skia2/tests/PathOpsCubicReduceOrderTest.cpp
caryclark a35ab3e6e0 fix fuzzers
Many old pathops-related fuzz failures have built up while
the codebase was under a state a flux. Now that the code
is stable, address these failures.

Most of the CL plumbs the debug global state to downstream
routines so that, if the data is not trusted (ala fuzzed)
the function can safely exit without asserting.

TBR=reed@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2426173002

Review-Url: https://chromiumcodereview.appspot.com/2426173002
2016-10-20 08:32:18 -07:00

263 lines
11 KiB
C++

/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "PathOpsCubicIntersectionTestData.h"
#include "PathOpsQuadIntersectionTestData.h"
#include "PathOpsTestCommon.h"
#include "SkIntersections.h"
#include "SkPathOpsRect.h"
#include "SkReduceOrder.h"
#include "Test.h"
#if 0 // disable test until stroke reduction is supported
static bool controls_inside(const SkDCubic& cubic) {
return between(cubic[0].fX, cubic[1].fX, cubic[3].fX)
&& between(cubic[0].fX, cubic[2].fX, cubic[3].fX)
&& between(cubic[0].fY, cubic[1].fY, cubic[3].fY)
&& between(cubic[0].fY, cubic[2].fY, cubic[3].fY);
}
static bool tiny(const SkDCubic& cubic) {
int index, minX, maxX, minY, maxY;
minX = maxX = minY = maxY = 0;
for (index = 1; index < 4; ++index) {
if (cubic[minX].fX > cubic[index].fX) {
minX = index;
}
if (cubic[minY].fY > cubic[index].fY) {
minY = index;
}
if (cubic[maxX].fX < cubic[index].fX) {
maxX = index;
}
if (cubic[maxY].fY < cubic[index].fY) {
maxY = index;
}
}
return approximately_equal(cubic[maxX].fX, cubic[minX].fX)
&& approximately_equal(cubic[maxY].fY, cubic[minY].fY);
}
static void find_tight_bounds(const SkDCubic& cubic, SkDRect& bounds) {
SkDCubicPair cubicPair = cubic.chopAt(0.5);
if (!tiny(cubicPair.first()) && !controls_inside(cubicPair.first())) {
find_tight_bounds(cubicPair.first(), bounds);
} else {
bounds.add(cubicPair.first()[0]);
bounds.add(cubicPair.first()[3]);
}
if (!tiny(cubicPair.second()) && !controls_inside(cubicPair.second())) {
find_tight_bounds(cubicPair.second(), bounds);
} else {
bounds.add(cubicPair.second()[0]);
bounds.add(cubicPair.second()[3]);
}
}
#endif
DEF_TEST(PathOpsReduceOrderCubic, reporter) {
size_t index;
SkReduceOrder reducer;
int order;
enum {
RunAll,
RunPointDegenerates,
RunNotPointDegenerates,
RunLines,
RunNotLines,
RunModEpsilonLines,
RunLessEpsilonLines,
RunNegEpsilonLines,
RunQuadraticLines,
RunQuadraticPoints,
RunQuadraticModLines,
RunComputedLines,
RunNone
} run = RunAll;
int firstTestIndex = 0;
#if 0
run = RunComputedLines;
firstTestIndex = 18;
#endif
int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates
? firstTestIndex : SK_MaxS32;
int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates
? firstTestIndex : SK_MaxS32;
int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines
? firstTestIndex : SK_MaxS32;
int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines
? firstTestIndex : SK_MaxS32;
int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines
? firstTestIndex : SK_MaxS32;
int firstQuadraticPointTest = run == RunAll ? 0 : run == RunQuadraticPoints
? firstTestIndex : SK_MaxS32;
int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines
? firstTestIndex : SK_MaxS32;
int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines
? firstTestIndex : SK_MaxS32;
#if 0
int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines
? firstTestIndex : SK_MaxS32;
#endif
for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) {
const CubicPts& c = pointDegenerates[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 1) {
SkDebugf("[%d] pointDegenerates order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) {
const CubicPts& c = notPointDegenerates[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order == 1) {
SkDebugf("[%d] notPointDegenerates order=%d\n", static_cast<int>(index), order);
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstLinesTest; index < lines_count; ++index) {
const CubicPts& c = lines[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 2) {
SkDebugf("[%d] lines order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstNotLinesTest; index < notLines_count; ++index) {
const CubicPts& c = notLines[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order == 2) {
SkDebugf("[%d] notLines order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) {
const CubicPts& c = modEpsilonLines[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order == 2) {
SkDebugf("[%d] line mod by epsilon order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) {
const CubicPts& c = lessEpsilonLines[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 2) {
SkDebugf("[%d] line less by epsilon/2 order=%d\n", static_cast<int>(index), order);
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) {
const CubicPts& c = negEpsilonLines[index];
SkDCubic cubic;
cubic.debugSet(c.fPts);
SkASSERT(ValidCubic(cubic));
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 2) {
SkDebugf("[%d] line neg by epsilon/2 order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstQuadraticPointTest; index < quadraticPoints_count; ++index) {
const QuadPts& q = quadraticPoints[index];
SkDQuad quad;
quad.debugSet(q.fPts);
SkASSERT(ValidQuad(quad));
SkDCubic cubic = quad.debugToCubic();
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 1) {
SkDebugf("[%d] point quad order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
const QuadPts& q = quadraticLines[index];
SkDQuad quad;
quad.debugSet(q.fPts);
SkASSERT(ValidQuad(quad));
SkDCubic cubic = quad.debugToCubic();
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 2) {
SkDebugf("[%d] line quad order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
const QuadPts& q = quadraticModEpsilonLines[index];
SkDQuad quad;
quad.debugSet(q.fPts);
SkASSERT(ValidQuad(quad));
SkDCubic cubic = quad.debugToCubic();
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
if (order != 3) {
SkDebugf("[%d] line mod quad order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
#if 0 // disable test until stroke reduction is supported
// test if computed line end points are valid
for (index = firstComputedLinesTest; index < lines_count; ++index) {
const SkDCubic& cubic = lines[index];
SkASSERT(ValidCubic(cubic));
bool controlsInside = controls_inside(cubic);
order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics,
SkReduceOrder::kStroke_Style);
if (order == 2 && reducer.fLine[0] == reducer.fLine[1]) {
SkDebugf("[%d] line computed ends match order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
if (controlsInside) {
if ( (reducer.fLine[0].fX != cubic[0].fX && reducer.fLine[0].fX != cubic[3].fX)
|| (reducer.fLine[0].fY != cubic[0].fY && reducer.fLine[0].fY != cubic[3].fY)
|| (reducer.fLine[1].fX != cubic[0].fX && reducer.fLine[1].fX != cubic[3].fX)
|| (reducer.fLine[1].fY != cubic[0].fY && reducer.fLine[1].fY != cubic[3].fY)) {
SkDebugf("[%d] line computed ends order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
} else {
// binary search for extrema, compare against actual results
// while a control point is outside of bounding box formed by end points, split
SkDRect bounds = {DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX};
find_tight_bounds(cubic, bounds);
if ( (!AlmostEqualUlps(reducer.fLine[0].fX, bounds.fLeft)
&& !AlmostEqualUlps(reducer.fLine[0].fX, bounds.fRight))
|| (!AlmostEqualUlps(reducer.fLine[0].fY, bounds.fTop)
&& !AlmostEqualUlps(reducer.fLine[0].fY, bounds.fBottom))
|| (!AlmostEqualUlps(reducer.fLine[1].fX, bounds.fLeft)
&& !AlmostEqualUlps(reducer.fLine[1].fX, bounds.fRight))
|| (!AlmostEqualUlps(reducer.fLine[1].fY, bounds.fTop)
&& !AlmostEqualUlps(reducer.fLine[1].fY, bounds.fBottom))) {
SkDebugf("[%d] line computed tight bounds order=%d\n", static_cast<int>(index), order);
REPORTER_ASSERT(reporter, 0);
}
}
}
#endif
}