pathops version two

R=reed@google.com

marked 'no commit' to attempt to get trybots to run

TBR=reed@google.com

Review URL: https://codereview.chromium.org/1002693002
This commit is contained in:
caryclark 2015-03-24 07:28:17 -07:00 committed by Commit bot
parent 62a320c8d4
commit ccec0f958f
95 changed files with 12522 additions and 13774 deletions

View File

@ -331,18 +331,19 @@
'<(skia_include_path)/pathops/SkPathOps.h',
'<(skia_src_path)/pathops/SkAddIntersections.cpp',
'<(skia_src_path)/pathops/SkDCubicIntersection.cpp',
'<(skia_src_path)/pathops/SkDCubicLineIntersection.cpp',
'<(skia_src_path)/pathops/SkDCubicToQuads.cpp',
'<(skia_src_path)/pathops/SkDLineIntersection.cpp',
'<(skia_src_path)/pathops/SkDQuadImplicit.cpp',
'<(skia_src_path)/pathops/SkDQuadIntersection.cpp',
'<(skia_src_path)/pathops/SkDQuadLineIntersection.cpp',
'<(skia_src_path)/pathops/SkIntersections.cpp',
'<(skia_src_path)/pathops/SkOpAngle.cpp',
'<(skia_src_path)/pathops/SkOpBuilder.cpp',
'<(skia_src_path)/pathops/SkOpCoincidence.cpp',
'<(skia_src_path)/pathops/SkOpContour.cpp',
'<(skia_src_path)/pathops/SkOpCubicHull.cpp',
'<(skia_src_path)/pathops/SkOpEdgeBuilder.cpp',
'<(skia_src_path)/pathops/SkOpSegment.cpp',
'<(skia_src_path)/pathops/SkOpSpan.cpp',
'<(skia_src_path)/pathops/SkPathOpsBounds.cpp',
'<(skia_src_path)/pathops/SkPathOpsCommon.cpp',
'<(skia_src_path)/pathops/SkPathOpsCubic.cpp',
@ -353,22 +354,24 @@
'<(skia_src_path)/pathops/SkPathOpsQuad.cpp',
'<(skia_src_path)/pathops/SkPathOpsRect.cpp',
'<(skia_src_path)/pathops/SkPathOpsSimplify.cpp',
'<(skia_src_path)/pathops/SkPathOpsTCubicSect.cpp',
'<(skia_src_path)/pathops/SkPathOpsTightBounds.cpp',
'<(skia_src_path)/pathops/SkPathOpsTriangle.cpp',
'<(skia_src_path)/pathops/SkPathOpsTQuadSect.cpp',
'<(skia_src_path)/pathops/SkPathOpsTypes.cpp',
'<(skia_src_path)/pathops/SkPathWriter.cpp',
'<(skia_src_path)/pathops/SkQuarticRoot.cpp',
'<(skia_src_path)/pathops/SkReduceOrder.cpp',
'<(skia_src_path)/pathops/SkAddIntersections.h',
'<(skia_src_path)/pathops/SkDQuadImplicit.h',
'<(skia_src_path)/pathops/SkIntersectionHelper.h',
'<(skia_src_path)/pathops/SkIntersections.h',
'<(skia_src_path)/pathops/SkLineParameters.h',
'<(skia_src_path)/pathops/SkOpAngle.h',
'<(skia_src_path)/pathops/SkOpCoincidence.h',
'<(skia_src_path)/pathops/SkOpContour.h',
'<(skia_src_path)/pathops/SkOpEdgeBuilder.h',
'<(skia_src_path)/pathops/SkOpSegment.h',
'<(skia_src_path)/pathops/SkOpSpan.h',
'<(skia_src_path)/pathops/SkOpTAllocator.h',
'<(skia_src_path)/pathops/SkPathOpsBounds.h',
'<(skia_src_path)/pathops/SkPathOpsCommon.h',
'<(skia_src_path)/pathops/SkPathOpsCubic.h',
@ -378,10 +381,9 @@
'<(skia_src_path)/pathops/SkPathOpsPoint.h',
'<(skia_src_path)/pathops/SkPathOpsQuad.h',
'<(skia_src_path)/pathops/SkPathOpsRect.h',
'<(skia_src_path)/pathops/SkPathOpsTriangle.h',
'<(skia_src_path)/pathops/SkPathOpsTSect.h',
'<(skia_src_path)/pathops/SkPathOpsTypes.h',
'<(skia_src_path)/pathops/SkPathWriter.h',
'<(skia_src_path)/pathops/SkQuarticRoot.h',
'<(skia_src_path)/pathops/SkReduceOrder.h',
],
}

View File

@ -1,61 +0,0 @@
{
'include_dirs' : [
'../include/pathops',
'../src/pathops',
],
'sources': [
'../include/pathops/SkPathOps.h',
'../src/pathops/SkAddIntersections.cpp',
'../src/pathops/SkDCubicIntersection.cpp',
'../src/pathops/SkDCubicLineIntersection.cpp',
'../src/pathops/SkDCubicToQuads.cpp',
'../src/pathops/SkDLineIntersection.cpp',
'../src/pathops/SkDQuadImplicit.cpp',
'../src/pathops/SkDQuadIntersection.cpp',
'../src/pathops/SkDQuadLineIntersection.cpp',
'../src/pathops/SkIntersections.cpp',
'../src/pathops/SkOpAngle.cpp',
'../src/pathops/SkOpContour.cpp',
'../src/pathops/SkOpEdgeBuilder.cpp',
'../src/pathops/SkOpSegment.cpp',
'../src/pathops/SkPathOpsBounds.cpp',
'../src/pathops/SkPathOpsCommon.cpp',
'../src/pathops/SkPathOpsCubic.cpp',
'../src/pathops/SkPathOpsDebug.cpp',
'../src/pathops/SkPathOpsLine.cpp',
'../src/pathops/SkPathOpsOp.cpp',
'../src/pathops/SkPathOpsPoint.cpp',
'../src/pathops/SkPathOpsQuad.cpp',
'../src/pathops/SkPathOpsRect.cpp',
'../src/pathops/SkPathOpsSimplify.cpp',
'../src/pathops/SkPathOpsTriangle.cpp',
'../src/pathops/SkPathOpsTypes.cpp',
'../src/pathops/SkPathWriter.cpp',
'../src/pathops/SkQuarticRoot.cpp',
'../src/pathops/SkReduceOrder.cpp',
'../src/pathops/SkAddIntersections.h',
'../src/pathops/SkDQuadImplicit.h',
'../src/pathops/SkIntersectionHelper.h',
'../src/pathops/SkIntersections.h',
'../src/pathops/SkLineParameters.h',
'../src/pathops/SkOpAngle.h',
'../src/pathops/SkOpContour.h',
'../src/pathops/SkOpEdgeBuilder.h',
'../src/pathops/SkOpSegment.h',
'../src/pathops/SkOpSpan.h',
'../src/pathops/SkPathOpsBounds.h',
'../src/pathops/SkPathOpsCommon.h',
'../src/pathops/SkPathOpsCubic.h',
'../src/pathops/SkPathOpsCurve.h',
'../src/pathops/SkPathOpsDebug.h',
'../src/pathops/SkPathOpsLine.h',
'../src/pathops/SkPathOpsPoint.h',
'../src/pathops/SkPathOpsQuad.h',
'../src/pathops/SkPathOpsRect.h',
'../src/pathops/SkPathOpsTriangle.h',
'../src/pathops/SkPathOpsTypes.h',
'../src/pathops/SkPathWriter.h',
'../src/pathops/SkQuarticRoot.h',
'../src/pathops/SkReduceOrder.h',
],
}

View File

@ -20,6 +20,7 @@
'../tests/PathOpsCubicLineIntersectionIdeas.cpp',
'../tests/PathOpsDebug.cpp',
'../tests/PathOpsOpLoopThreadedTest.cpp',
'../tests/PathOpsTSectDebug.h',
'../tests/skia_test.cpp',
],
'conditions': [

View File

@ -19,18 +19,17 @@
'../tests/PathOpsAngleTest.cpp',
'../tests/PathOpsBoundsTest.cpp',
'../tests/PathOpsBuilderTest.cpp',
'../tests/PathOpsBuildUseTest.cpp',
'../tests/PathOpsCubicIntersectionTest.cpp',
'../tests/PathOpsCubicIntersectionTestData.cpp',
'../tests/PathOpsCubicLineIntersectionTest.cpp',
'../tests/PathOpsCubicQuadIntersectionTest.cpp',
'../tests/PathOpsCubicReduceOrderTest.cpp',
'../tests/PathOpsCubicToQuadsTest.cpp',
'../tests/PathOpsDCubicTest.cpp',
'../tests/PathOpsDLineTest.cpp',
'../tests/PathOpsDPointTest.cpp',
'../tests/PathOpsDQuadTest.cpp',
'../tests/PathOpsDRectTest.cpp',
'../tests/PathOpsDTriangleTest.cpp',
'../tests/PathOpsDVectorTest.cpp',
'../tests/PathOpsExtendedTest.cpp',
'../tests/PathOpsFuzz763Test.cpp',
@ -44,7 +43,6 @@
'../tests/PathOpsQuadIntersectionTestData.cpp',
'../tests/PathOpsQuadLineIntersectionTest.cpp',
'../tests/PathOpsQuadLineIntersectionThreadedTest.cpp',
'../tests/PathOpsQuadParameterizationTest.cpp',
'../tests/PathOpsQuadReduceOrderTest.cpp',
'../tests/PathOpsSimplifyDegenerateThreadedTest.cpp',
'../tests/PathOpsSimplifyFailTest.cpp',
@ -56,11 +54,15 @@
'../tests/PathOpsSkpTest.cpp',
'../tests/PathOpsTestCommon.cpp',
'../tests/PathOpsThreadedCommon.cpp',
'../tests/PathOpsTightBoundsTest.cpp',
'../tests/PathOpsThreeWayTest.cpp',
'../tests/PathOpsTightBoundsTest.cpp',
'../tests/PathOpsTypesTest.cpp',
'../tests/PathOpsCubicIntersectionTestData.h',
'../tests/PathOpsExtendedTest.h',
'../tests/PathOpsQuadIntersectionTestData.h',
'../tests/PathOpsTestCommon.h',
'../tests/PathOpsThreadedCommon.h',
'../tests/PathOpsTSectDebug.h',
],
}

View File

@ -8,6 +8,8 @@
#define SkPathOps_DEFINED
#include "SkPreConfig.h"
#include "SkTArray.h"
#include "SkTDArray.h"
class SkPath;
struct SkRect;
@ -35,9 +37,10 @@ enum SkPathOp {
@param one The first operand (for difference, the minuend)
@param two The second operand (for difference, the subtrahend)
@param op The operator to apply.
@param result The product of the operands. The result may be one of the
inputs.
@return True if operation succeeded.
@return True if the operation succeeded.
*/
bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result);
@ -63,4 +66,31 @@ bool SK_API Simplify(const SkPath& path, SkPath* result);
*/
bool SK_API TightBounds(const SkPath& path, SkRect* result);
/** Perform a series of path operations, optimized for unioning many paths together.
*/
class SK_API SkOpBuilder {
public:
/** Add one or more paths and their operand. The builder is empty before the first
path is added, so the result of a single add is (emptyPath OP path).
@param path The second operand.
@param op The operator to apply to the existing and supplied paths.
*/
void add(const SkPath& path, SkPathOp op);
/** Computes the sum of all paths and operands, and resets the builder to its
initial state.
@param result The product of the operands.
@return True if the operation succeeded.
*/
bool resolve(SkPath* result);
private:
SkTArray<SkPath> fPathRefs;
SkTDArray<SkPathOp> fOps;
void reset();
};
#endif

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
#include "SkOpCoincidence.h"
#include "SkPathOpsBounds.h"
#if DEBUG_ADD_INTERSECTING_TS
@ -130,20 +131,6 @@ static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
SkDebugf("\n");
}
static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
const SkIntersections& i) {
SkASSERT(i.used() == pts);
if (!pts) {
SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__,
CUBIC_DEBUG_DATA(wt.pts()));
return;
}
SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0));
SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]);
SkDebugf("\n");
}
#else
static void debugShowLineIntersection(int , const SkIntersectionHelper& ,
const SkIntersectionHelper& , const SkIntersections& ) {
@ -168,13 +155,10 @@ static void debugShowCubicQuadIntersection(int , const SkIntersectionHelper& ,
static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
const SkIntersectionHelper& , const SkIntersections& ) {
}
static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
const SkIntersections& ) {
}
#endif
bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
SkChunkAlloc* allocator) {
if (test != next) {
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
return false;
@ -186,10 +170,11 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
}
SkIntersectionHelper wt;
wt.init(test);
bool foundCommonContour = test == next;
do {
SkIntersectionHelper wn;
wn.init(next);
test->debugValidate();
next->debugValidate();
if (test == next && !wn.startAfter(wt)) {
continue;
}
@ -306,14 +291,22 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
break;
}
case SkIntersectionHelper::kQuad_Segment: {
pts = ts.quadQuad(wt.pts(), wn.pts());
ts.alignQuadPts(wt.pts(), wn.pts());
SkDQuad quad1;
quad1.set(wt.pts());
SkDQuad quad2;
quad2.set(wn.pts());
pts = ts.intersect(quad1, quad2);
debugShowQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
swap = true;
pts = ts.cubicQuad(wn.pts(), wt.pts());
SkDQuad quad1;
quad1.set(wt.pts());
SkDCubic cubic1 = quad1.toCubic();
SkDCubic cubic2;
cubic2.set(wn.pts());
pts = ts.intersect(cubic2, cubic1);
debugShowCubicQuadIntersection(pts, wn, wt, ts);
break;
}
@ -339,12 +332,21 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
break;
}
case SkIntersectionHelper::kQuad_Segment: {
pts = ts.cubicQuad(wt.pts(), wn.pts());
SkDCubic cubic1;
cubic1.set(wt.pts());
SkDQuad quad2;
quad2.set(wn.pts());
SkDCubic cubic2 = quad2.toCubic();
pts = ts.intersect(cubic1, cubic2);
debugShowCubicQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
pts = ts.cubicCubic(wt.pts(), wn.pts());
SkDCubic cubic1;
cubic1.set(wt.pts());
SkDCubic cubic2;
cubic2.set(wn.pts());
pts = ts.intersect(cubic1, cubic2);
debugShowCubicIntersection(pts, wt, wn, ts);
break;
}
@ -355,102 +357,53 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
default:
SkASSERT(0);
}
if (!foundCommonContour && pts > 0) {
test->addCross(next);
next->addCross(test);
foundCommonContour = true;
}
// in addition to recording T values, record matching segment
if (pts == 2) {
if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment
&& wt.segmentType() <= SkIntersectionHelper::kLine_Segment) {
if (wt.addCoincident(wn, ts, swap)) {
continue;
}
pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
} else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
&& wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
&& ts.isCoincident(0)) {
SkASSERT(ts.coincidentUsed() == 2);
if (wt.addCoincident(wn, ts, swap)) {
continue;
}
pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
}
}
if (pts >= 2) {
for (int pt = 0; pt < pts - 1; ++pt) {
const SkDPoint& point = ts.pt(pt);
const SkDPoint& next = ts.pt(pt + 1);
if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next)
&& wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) {
if (!wt.addPartialCoincident(wn, ts, pt, swap)) {
// remove extra point if two map to same float values
pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
}
}
}
}
int coinIndex = -1;
SkOpPtT* coinPtT[2];
for (int pt = 0; pt < pts; ++pt) {
SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
SkPoint point = ts.pt(pt).asSkPoint();
wt.alignTPt(wn, swap, pt, &ts, &point);
int testTAt = wt.addT(wn, point, ts[swap][pt]);
int nextTAt = wn.addT(wt, point, ts[!swap][pt]);
wt.addOtherT(testTAt, ts[!swap][pt], nextTAt);
wn.addOtherT(nextTAt, ts[swap][pt], testTAt);
wt.segment()->debugValidate();
SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
allocator);
wn.segment()->debugValidate();
SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
allocator);
testTAt->addOpp(nextTAt);
if (testTAt->fPt != nextTAt->fPt) {
testTAt->span()->unaligned();
nextTAt->span()->unaligned();
}
wt.segment()->debugValidate();
wn.segment()->debugValidate();
if (!ts.isCoincident(pt)) {
continue;
}
if (coinIndex < 0) {
coinPtT[0] = testTAt;
coinPtT[1] = nextTAt;
coinIndex = pt;
continue;
}
if (coinPtT[0]->span() == testTAt->span()) {
coinIndex = -1;
continue;
}
if (coinPtT[1]->span() == nextTAt->span()) {
coinIndex = -1; // coincidence span collapsed
continue;
}
if (swap) {
SkTSwap(coinPtT[0], coinPtT[1]);
SkTSwap(testTAt, nextTAt);
}
SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
wt.segment()->debugValidate();
wn.segment()->debugValidate();
coinIndex = -1;
}
SkASSERT(coinIndex < 0); // expect coincidence to be paired
} while (wn.advance());
} while (wt.advance());
return true;
}
void AddSelfIntersectTs(SkOpContour* test) {
SkIntersectionHelper wt;
wt.init(test);
do {
if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) {
continue;
}
SkIntersections ts;
int pts = ts.cubic(wt.pts());
debugShowCubicIntersection(pts, wt, ts);
if (!pts) {
continue;
}
SkASSERT(pts == 1);
SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1);
SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1);
SkPoint point = ts.pt(0).asSkPoint();
int testTAt = wt.addSelfT(point, ts[0][0]);
int nextTAt = wt.addSelfT(point, ts[1][0]);
wt.addOtherT(testTAt, ts[1][0], nextTAt);
wt.addOtherT(nextTAt, ts[0][0], testTAt);
} while (wt.advance());
}
// resolve any coincident pairs found while intersecting, and
// see if coincidence is formed by clipping non-concident segments
bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
int contourCount = (*contourList).count();
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = (*contourList)[cIndex];
contour->resolveNearCoincidence();
}
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = (*contourList)[cIndex];
contour->addCoincidentPoints();
}
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = (*contourList)[cIndex];
if (!contour->calcCoincidentWinding()) {
return false;
}
}
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = (*contourList)[cIndex];
contour->calcPartialCoincidentWinding();
}
return true;
}

View File

@ -9,10 +9,10 @@
#include "SkIntersectionHelper.h"
#include "SkIntersections.h"
#include "SkTArray.h"
bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
void AddSelfIntersectTs(SkOpContour* test);
bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
class SkOpCoincidence;
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
SkChunkAlloc* allocator);
#endif

View File

@ -1,704 +0,0 @@
/*
* 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 "SkIntersections.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsPoint.h"
#include "SkPathOpsQuad.h"
#include "SkPathOpsRect.h"
#include "SkReduceOrder.h"
#include "SkTSort.h"
#if ONE_OFF_DEBUG
static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
#endif
#define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
#define DEBUG_QUAD_PART_SHOW_SIMPLE DEBUG_QUAD_PART && 0
#define SWAP_TOP_DEBUG 0
static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) {
SkDCubic part = cubic.subDivide(tStart, tEnd);
SkDQuad quad = part.toQuad();
// FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
// extremely shallow quadratic?
int order = reducer->reduce(quad);
#if DEBUG_QUAD_PART
SkDebugf("%s cubic=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
" t=(%1.9g,%1.9g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY,
cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
cubic[3].fX, cubic[3].fY, tStart, tEnd);
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n"
" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY,
part[3].fX, part[3].fY, quad[0].fX, quad[0].fY,
quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
#if DEBUG_QUAD_PART_SHOW_SIMPLE
SkDebugf("%s simple=(%1.9g,%1.9g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY);
if (order > 1) {
SkDebugf(" %1.9g,%1.9g", reducer->fQuad[1].fX, reducer->fQuad[1].fY);
}
if (order > 2) {
SkDebugf(" %1.9g,%1.9g", reducer->fQuad[2].fX, reducer->fQuad[2].fY);
}
SkDebugf(")\n");
SkASSERT(order < 4 && order > 0);
#endif
#endif
return order;
}
static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
int order2, SkIntersections& i) {
if (order1 == 3 && order2 == 3) {
i.intersect(simple1, simple2);
} else if (order1 <= 2 && order2 <= 2) {
i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
} else if (order1 == 3 && order2 <= 2) {
i.intersect(simple1, (const SkDLine&) simple2);
} else {
SkASSERT(order1 <= 2 && order2 == 3);
i.intersect(simple2, (const SkDLine&) simple1);
i.swapPts();
}
}
// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
// chase intersections near quadratic ends, requiring odd hacks to find them.
static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2,
double t2s, double t2e, double precisionScale, SkIntersections& i) {
i.upDepth();
SkDCubic c1 = cubic1.subDivide(t1s, t1e);
SkDCubic c2 = cubic2.subDivide(t2s, t2e);
SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1;
// OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1);
SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2;
c2.toQuadraticTs(c2.calcPrecision() * precisionScale, &ts2);
double t1Start = t1s;
int ts1Count = ts1.count();
for (int i1 = 0; i1 <= ts1Count; ++i1) {
const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
const double t1 = t1s + (t1e - t1s) * tEnd1;
SkReduceOrder s1;
int o1 = quadPart(cubic1, t1Start, t1, &s1);
double t2Start = t2s;
int ts2Count = ts2.count();
for (int i2 = 0; i2 <= ts2Count; ++i2) {
const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
const double t2 = t2s + (t2e - t2s) * tEnd2;
if (&cubic1 == &cubic2 && t1Start >= t2Start) {
t2Start = t2;
continue;
}
SkReduceOrder s2;
int o2 = quadPart(cubic2, t2Start, t2, &s2);
#if ONE_OFF_DEBUG
char tab[] = " ";
if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
&& tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab,
__FUNCTION__, t1Start, t1, t2Start, t2);
SkIntersections xlocals;
xlocals.allowNear(false);
xlocals.allowFlatMeasure(true);
intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals);
SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
}
#endif
SkIntersections locals;
locals.allowNear(false);
locals.allowFlatMeasure(true);
intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals);
int tCount = locals.used();
for (int tIdx = 0; tIdx < tCount; ++tIdx) {
double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx];
double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx];
// if the computed t is not sufficiently precise, iterate
SkDPoint p1 = cubic1.ptAtT(to1);
SkDPoint p2 = cubic2.ptAtT(to2);
if (p1.approximatelyEqual(p2)) {
// FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
// SkASSERT(!locals.isCoincident(tIdx));
if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
if (i.swapped()) { // FIXME: insert should respect swap
i.insert(to2, to1, p1);
} else {
i.insert(to1, to2, p1);
}
}
} else {
/*for random cubics, 16 below catches 99.997% of the intersections. To test for the remaining 0.003%
look for nearly coincident curves. and check each 1/16th section.
*/
double offset = precisionScale / 16; // FIXME: const is arbitrary: test, refine
double c1Bottom = tIdx == 0 ? 0 :
(t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2;
double c1Min = SkTMax(c1Bottom, to1 - offset);
double c1Top = tIdx == tCount - 1 ? 1 :
(t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2;
double c1Max = SkTMin(c1Top, to1 + offset);
double c2Min = SkTMax(0., to2 - offset);
double c2Max = SkTMin(1., to2 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
__FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
c1Max, c2Min, c2Max);
#endif
intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
#endif
if (tCount > 1) {
c1Min = SkTMax(0., to1 - offset);
c1Max = SkTMin(1., to1 + offset);
double c2Bottom = tIdx == 0 ? to2 :
(t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2;
double c2Top = tIdx == tCount - 1 ? to2 :
(t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2;
if (c2Bottom > c2Top) {
SkTSwap(c2Bottom, c2Top);
}
if (c2Bottom == to2) {
c2Bottom = 0;
}
if (c2Top == to2) {
c2Top = 1;
}
c2Min = SkTMax(c2Bottom, to2 - offset);
c2Max = SkTMin(c2Top, to2 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
__FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
c1Max, c2Min, c2Max);
#endif
intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
#endif
c1Min = SkTMax(c1Bottom, to1 - offset);
c1Max = SkTMin(c1Top, to1 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
__FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
c1Max, c2Min, c2Max);
#endif
intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
#endif
}
// intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
// FIXME: if no intersection is found, either quadratics intersected where
// cubics did not, or the intersection was missed. In the former case, expect
// the quadratics to be nearly parallel at the point of intersection, and check
// for that.
}
}
t2Start = t2;
}
t1Start = t1;
}
i.downDepth();
}
// if two ends intersect, check middle for coincidence
bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
if (fUsed < 2) {
return false;
}
int last = fUsed - 1;
double tRange1 = fT[0][last] - fT[0][0];
double tRange2 = fT[1][last] - fT[1][0];
for (int index = 1; index < 5; ++index) {
double testT1 = fT[0][0] + tRange1 * index / 5;
double testT2 = fT[1][0] + tRange2 * index / 5;
SkDPoint testPt1 = c1.ptAtT(testT1);
SkDPoint testPt2 = c2.ptAtT(testT2);
if (!testPt1.approximatelyEqual(testPt2)) {
return false;
}
}
if (fUsed > 2) {
fPt[1] = fPt[last];
fT[0][1] = fT[0][last];
fT[1][1] = fT[1][last];
fUsed = 2;
}
fIsCoincident[0] = fIsCoincident[1] = 0x03;
return true;
}
#define LINE_FRACTION 0.1
// intersect the end of the cubic with the other. Try lines from the end to control and opposite
// end to determine range of t on opposite cubic.
bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
int t1Index = start ? 0 : 3;
double testT = (double) !start;
bool swap = swapped();
// quad/quad at this point checks to see if exact matches have already been found
// cubic/cubic can't reject so easily since cubics can intersect same point more than once
SkDLine tmpLine;
tmpLine[0] = tmpLine[1] = cubic2[t1Index];
tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
SkIntersections impTs;
impTs.allowNear(false);
impTs.allowFlatMeasure(true);
impTs.intersectRay(cubic1, tmpLine);
for (int index = 0; index < impTs.used(); ++index) {
SkDPoint realPt = impTs.pt(index);
if (!tmpLine[0].approximatelyEqual(realPt)) {
continue;
}
if (swap) {
cubicInsert(testT, impTs[0][index], tmpLine[0], cubic2, cubic1);
} else {
cubicInsert(impTs[0][index], testT, tmpLine[0], cubic1, cubic2);
}
return true;
}
return false;
}
void SkIntersections::cubicInsert(double one, double two, const SkDPoint& pt,
const SkDCubic& cubic1, const SkDCubic& cubic2) {
for (int index = 0; index < fUsed; ++index) {
if (fT[0][index] == one) {
double oldTwo = fT[1][index];
if (oldTwo == two) {
return;
}
SkDPoint mid = cubic2.ptAtT((oldTwo + two) / 2);
if (mid.approximatelyEqual(fPt[index])) {
return;
}
}
if (fT[1][index] == two) {
SkDPoint mid = cubic1.ptAtT((fT[0][index] + two) / 2);
if (mid.approximatelyEqual(fPt[index])) {
return;
}
}
}
insert(one, two, pt);
}
void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
const SkDRect& bounds2) {
SkDLine line;
int t1Index = start ? 0 : 3;
double testT = (double) !start;
// don't bother if the two cubics are connnected
static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
static const int kMaxLineCubicIntersections = 3;
SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
line[0] = cubic1[t1Index];
// this variant looks for intersections with the end point and lines parallel to other points
for (int index = 0; index < kPointsInCubic; ++index) {
if (index == t1Index) {
continue;
}
SkDVector dxy1 = cubic1[index] - line[0];
dxy1 /= SkDCubic::gPrecisionUnit;
line[1] = line[0] + dxy1;
SkDRect lineBounds;
lineBounds.setBounds(line);
if (!bounds2.intersects(&lineBounds)) {
continue;
}
SkIntersections local;
if (!local.intersect(cubic2, line)) {
continue;
}
for (int idx2 = 0; idx2 < local.used(); ++idx2) {
double foundT = local[0][idx2];
if (approximately_less_than_zero(foundT)
|| approximately_greater_than_one(foundT)) {
continue;
}
if (local.pt(idx2).approximatelyEqual(line[0])) {
if (swapped()) { // FIXME: insert should respect swap
insert(foundT, testT, line[0]);
} else {
insert(testT, foundT, line[0]);
}
} else {
tVals.push_back(foundT);
}
}
}
if (tVals.count() == 0) {
return;
}
SkTQSort<double>(tVals.begin(), tVals.end() - 1);
double tMin1 = start ? 0 : 1 - LINE_FRACTION;
double tMax1 = start ? LINE_FRACTION : 1;
int tIdx = 0;
do {
int tLast = tIdx;
while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
++tLast;
}
double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
int lastUsed = used();
if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
}
if (lastUsed == used()) {
tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
}
}
tIdx = tLast + 1;
} while (tIdx < tVals.count());
return;
}
const double CLOSE_ENOUGH = 0.001;
static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) {
return false;
}
pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2);
return true;
}
static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
int last = i.used() - 1;
if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
return false;
}
pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2);
return true;
}
static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) {
// the idea here is to see at minimum do a quick reject by rotating all points
// to either side of the line formed by connecting the endpoints
// if the opposite curves points are on the line or on the other side, the
// curves at most intersect at the endpoints
for (int oddMan = 0; oddMan < 4; ++oddMan) {
const SkDPoint* endPt[3];
for (int opp = 1; opp < 4; ++opp) {
int end = oddMan ^ opp; // choose a value not equal to oddMan
endPt[opp - 1] = &c1[end];
}
for (int triTest = 0; triTest < 3; ++triTest) {
double origX = endPt[triTest]->fX;
double origY = endPt[triTest]->fY;
int oppTest = triTest + 1;
if (3 == oppTest) {
oppTest = 0;
}
double adj = endPt[oppTest]->fX - origX;
double opp = endPt[oppTest]->fY - origY;
if (adj == 0 && opp == 0) { // if the other point equals the test point, ignore it
continue;
}
double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp;
if (approximately_zero(sign)) {
goto tryNextHalfPlane;
}
for (int n = 0; n < 4; ++n) {
double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
if (test * sign > 0 && !precisely_zero(test)) {
goto tryNextHalfPlane;
}
}
}
return true;
tryNextHalfPlane:
;
}
return false;
}
int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
if (fMax == 0) {
fMax = 9;
}
bool selfIntersect = &c1 == &c2;
if (selfIntersect) {
if (c1[0].approximatelyEqual(c1[3])) {
insert(0, 1, c1[0]);
return fUsed;
}
} else {
// OPTIMIZATION: set exact end bits here to avoid cubic exact end later
for (int i1 = 0; i1 < 4; i1 += 3) {
for (int i2 = 0; i2 < 4; i2 += 3) {
if (c1[i1].approximatelyEqual(c2[i2])) {
insert(i1 >> 1, i2 >> 1, c1[i1]);
}
}
}
}
SkASSERT(fUsed < 4);
if (!selfIntersect) {
if (only_end_pts_in_common(c1, c2)) {
return fUsed;
}
if (only_end_pts_in_common(c2, c1)) {
return fUsed;
}
}
// quad/quad does linear test here -- cubic does not
// cubics which are really lines should have been detected in reduce step earlier
int exactEndBits = 0;
if (selfIntersect) {
if (fUsed) {
return fUsed;
}
} else {
exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
swap();
exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
swap();
}
if (cubicCheckCoincidence(c1, c2)) {
SkASSERT(!selfIntersect);
return fUsed;
}
// FIXME: pass in cached bounds from caller
SkDRect c2Bounds;
c2Bounds.setBounds(c2);
if (!(exactEndBits & 4)) {
cubicNearEnd(c1, false, c2, c2Bounds);
}
if (!(exactEndBits & 8)) {
if (selfIntersect && fUsed) {
return fUsed;
}
cubicNearEnd(c1, true, c2, c2Bounds);
if (selfIntersect && fUsed && ((approximately_less_than_zero(fT[0][0])
&& approximately_less_than_zero(fT[1][0]))
|| (approximately_greater_than_one(fT[0][0])
&& approximately_greater_than_one(fT[1][0])))) {
SkASSERT(fUsed == 1);
fUsed = 0;
return fUsed;
}
}
if (!selfIntersect) {
SkDRect c1Bounds;
c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
swap();
if (!(exactEndBits & 1)) {
cubicNearEnd(c2, false, c1, c1Bounds);
}
if (!(exactEndBits & 2)) {
cubicNearEnd(c2, true, c1, c1Bounds);
}
swap();
}
if (cubicCheckCoincidence(c1, c2)) {
SkASSERT(!selfIntersect);
return fUsed;
}
SkIntersections i;
i.fAllowNear = false;
i.fFlatMeasure = true;
i.fMax = 9;
::intersect(c1, 0, 1, c2, 0, 1, 1, i);
int compCount = i.used();
if (compCount) {
int exactCount = used();
if (exactCount == 0) {
*this = i;
} else {
// at least one is exact or near, and at least one was computed. Eliminate duplicates
for (int exIdx = 0; exIdx < exactCount; ++exIdx) {
for (int cpIdx = 0; cpIdx < compCount; ) {
if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) {
i.removeOne(cpIdx);
--compCount;
continue;
}
double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2;
SkDPoint pt = c1.ptAtT(tAvg);
if (!pt.approximatelyEqual(fPt[exIdx])) {
++cpIdx;
continue;
}
tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2;
pt = c2.ptAtT(tAvg);
if (!pt.approximatelyEqual(fPt[exIdx])) {
++cpIdx;
continue;
}
i.removeOne(cpIdx);
--compCount;
}
}
// if mid t evaluates to nearly the same point, skip the t
for (int cpIdx = 0; cpIdx < compCount - 1; ) {
double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2;
SkDPoint pt = c1.ptAtT(tAvg);
if (!pt.approximatelyEqual(fPt[cpIdx])) {
++cpIdx;
continue;
}
tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2;
pt = c2.ptAtT(tAvg);
if (!pt.approximatelyEqual(fPt[cpIdx])) {
++cpIdx;
continue;
}
i.removeOne(cpIdx);
--compCount;
}
// in addition to adding below missing function, think about how to say
append(i);
}
}
// If an end point and a second point very close to the end is returned, the second
// point may have been detected because the approximate quads
// intersected at the end and close to it. Verify that the second point is valid.
if (fUsed <= 1) {
return fUsed;
}
SkDPoint pt[2];
if (closeStart(c1, 0, *this, pt[0]) && closeStart(c2, 1, *this, pt[1])
&& pt[0].approximatelyEqual(pt[1])) {
removeOne(1);
}
if (closeEnd(c1, 0, *this, pt[0]) && closeEnd(c2, 1, *this, pt[1])
&& pt[0].approximatelyEqual(pt[1])) {
removeOne(used() - 2);
}
// vet the pairs of t values to see if the mid value is also on the curve. If so, mark
// the span as coincident
if (fUsed >= 2 && !coincidentUsed()) {
int last = fUsed - 1;
int match = 0;
for (int index = 0; index < last; ++index) {
double mid1 = (fT[0][index] + fT[0][index + 1]) / 2;
double mid2 = (fT[1][index] + fT[1][index + 1]) / 2;
pt[0] = c1.ptAtT(mid1);
pt[1] = c2.ptAtT(mid2);
if (pt[0].approximatelyEqual(pt[1])) {
match |= 1 << index;
}
}
if (match) {
#if DEBUG_CONCIDENT
if (((match + 1) & match) != 0) {
SkDebugf("%s coincident hole\n", __FUNCTION__);
}
#endif
// for now, assume that everything from start to finish is coincident
if (fUsed > 2) {
fPt[1] = fPt[last];
fT[0][1] = fT[0][last];
fT[1][1] = fT[1][last];
fIsCoincident[0] = 0x03;
fIsCoincident[1] = 0x03;
fUsed = 2;
}
}
}
return fUsed;
}
// Up promote the quad to a cubic.
// OPTIMIZATION If this is a common use case, optimize by duplicating
// the intersect 3 loop to avoid the promotion / demotion code
int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
fMax = 7;
SkDCubic up = quad.toCubic();
(void) intersect(cubic, up);
return used();
}
/* http://www.ag.jku.at/compass/compasssample.pdf
( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
SINTEF Applied Mathematics http://www.sintef.no )
describes a method to find the self intersection of a cubic by taking the gradient of the implicit
form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
int SkIntersections::intersect(const SkDCubic& c) {
fMax = 1;
// check to see if x or y end points are the extrema. Are other quick rejects possible?
if (c.endsAreExtremaInXOrY()) {
return false;
}
// OPTIMIZATION: could quick reject if neither end point tangent ray intersected the line
// segment formed by the opposite end point to the control point
(void) intersect(c, c);
if (used() > 1) {
fUsed = 0;
} else if (used() > 0) {
if (approximately_equal_double(fT[0][0], fT[1][0])) {
fUsed = 0;
} else {
SkASSERT(used() == 1);
if (fT[0][0] > fT[1][0]) {
swapPts();
}
}
}
return used();
}

View File

@ -93,6 +93,29 @@ public:
fAllowNear = allow;
}
void checkCoincident() {
int last = fIntersections->used() - 1;
for (int index = 0; index < last; ) {
double cubicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
double t = fLine.nearPoint(cubicMidPt, NULL);
if (t < 0) {
++index;
continue;
}
if (fIntersections->isCoincident(index)) {
fIntersections->removeOne(index);
--last;
} else if (fIntersections->isCoincident(index + 1)) {
fIntersections->removeOne(index + 1);
--last;
} else {
fIntersections->setCoincident(index++);
}
fIntersections->setCoincident(index);
}
}
// see parallel routine in line quadratic intersections
int intersectRay(double roots[3]) {
double adj = fLine[1].fX - fLine[0].fX;
@ -131,32 +154,11 @@ public:
double cubicT = rootVals[index];
double lineT = findLineT(cubicT);
SkDPoint pt;
if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized)) {
#if ONE_OFF_DEBUG
SkDPoint cPt = fCubic.ptAtT(cubicT);
SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
cPt.fX, cPt.fY);
#endif
for (int inner = 0; inner < fIntersections->used(); ++inner) {
if (fIntersections->pt(inner) != pt) {
continue;
}
double existingCubicT = (*fIntersections)[0][inner];
if (cubicT == existingCubicT) {
goto skipInsert;
}
// check if midway on cubic is also same point. If so, discard this
double cubicMidT = (existingCubicT + cubicT) / 2;
SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
if (cubicMidPt.approximatelyEqual(pt)) {
goto skipInsert;
}
}
if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
fIntersections->insert(cubicT, lineT, pt);
skipInsert:
;
}
}
checkCoincident();
return fIntersections->used();
}
@ -186,20 +188,43 @@ public:
int count = HorizontalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
SkDPoint pt;
pt.fX = fCubic.ptAtT(cubicT).fX;
pt.fY = axisIntercept;
SkDPoint pt = { fCubic.ptAtT(cubicT).fX, axisIntercept };
double lineT = (pt.fX - left) / (right - left);
if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
fIntersections->insert(cubicT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
checkCoincident();
return fIntersections->used();
}
bool uniqueAnswer(double cubicT, const SkDPoint& pt) {
for (int inner = 0; inner < fIntersections->used(); ++inner) {
if (fIntersections->pt(inner) != pt) {
continue;
}
double existingCubicT = (*fIntersections)[0][inner];
if (cubicT == existingCubicT) {
return false;
}
// check if midway on cubic is also same point. If so, discard this
double cubicMidT = (existingCubicT + cubicT) / 2;
SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
if (cubicMidPt.approximatelyEqual(pt)) {
return false;
}
}
#if ONE_OFF_DEBUG
SkDPoint cPt = fCubic.ptAtT(cubicT);
SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
cPt.fX, cPt.fY);
#endif
return true;
}
static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) {
double A, B, C, D;
SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D);
@ -226,17 +251,16 @@ public:
int count = VerticalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
SkDPoint pt;
pt.fX = axisIntercept;
pt.fY = fCubic.ptAtT(cubicT).fY;
SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
double lineT = (pt.fY - top) / (bottom - top);
if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
fIntersections->insert(cubicT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
checkCoincident();
return fIntersections->used();
}
@ -342,7 +366,7 @@ public:
double lT = *lineT = SkPinT(*lineT);
SkDPoint lPt = fLine.ptAtT(lT);
SkDPoint cPt = fCubic.ptAtT(cT);
if (!lPt.moreRoughlyEqual(cPt)) {
if (!lPt.roughlyEqual(cPt)) {
return false;
}
// FIXME: if points are roughly equal but not approximately equal, need to do

View File

@ -19,62 +19,10 @@ If this is a degree-elevated cubic, then both equations will give the same answe
it's likely not, your best bet is to average them. So,
P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
SkDCubic defined by: P1/2 - anchor points, C1/C2 control points
|x| is the euclidean norm of x
mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the
control point at C = (3·C2 - P2 + 3·C1 - P1)/4
Algorithm
pick an absolute precision (prec)
Compute the Tdiv as the root of (cubic) equation
sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a
quadratic, with a defect less than prec, by the mid-point approximation.
Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point
approximation
Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
confirmed by (maybe stolen from)
http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
// maybe in turn derived from http://www.cccg.ca/proceedings/2004/36.pdf
// also stored at http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/bezier%20cccg04%20paper.pdf
*/
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
#include "SkReduceOrder.h"
#include "SkTArray.h"
#include "SkTSort.h"
#define USE_CUBIC_END_POINTS 1
static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
const double adjust = sqrt(3.) / 36;
SkDCubic sub;
const SkDCubic* cPtr;
if (start == 0) {
cPtr = &cubic;
} else {
// OPTIMIZE: special-case half-split ?
sub = cubic.subDivide(start, 1);
cPtr = &sub;
}
const SkDCubic& c = *cPtr;
double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
double dist = sqrt(dx * dx + dy * dy);
double tDiv3 = precision / (adjust * dist);
double t = SkDCubeRoot(tDiv3);
if (start > 0) {
t = start + (1 - start) * t;
}
return t;
}
SkDQuad SkDCubic::toQuad() const {
SkDQuad quad;
@ -86,101 +34,3 @@ SkDQuad SkDCubic::toQuad() const {
quad[2] = fPts[3];
return quad;
}
static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
double tDiv = calc_t_div(cubic, precision, 0);
if (tDiv >= 1) {
return true;
}
if (tDiv >= 0.5) {
ts->push_back(0.5);
return true;
}
return false;
}
static void addTs(const SkDCubic& cubic, double precision, double start, double end,
SkTArray<double, true>* ts) {
double tDiv = calc_t_div(cubic, precision, 0);
double parts = ceil(1.0 / tDiv);
for (double index = 0; index < parts; ++index) {
double newT = start + (index / parts) * (end - start);
if (newT > 0 && newT < 1) {
ts->push_back(newT);
}
}
}
// flavor that returns T values only, deferring computing the quads until they are needed
// FIXME: when called from recursive intersect 2, this could take the original cubic
// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
// it would still take the prechopped cubic for reduce order and find cubic inflections
void SkDCubic::toQuadraticTs(double precision, SkTArray<double, true>* ts) const {
SkReduceOrder reducer;
int order = reducer.reduce(*this, SkReduceOrder::kAllow_Quadratics);
if (order < 3) {
return;
}
double inflectT[5];
int inflections = findInflections(inflectT);
SkASSERT(inflections <= 2);
if (!endsAreExtremaInXOrY()) {
inflections += findMaxCurvature(&inflectT[inflections]);
SkASSERT(inflections <= 5);
}
SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
// OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
// own subroutine?
while (inflections && approximately_less_than_zero(inflectT[0])) {
memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
}
int start = 0;
int next = 1;
while (next < inflections) {
if (!approximately_equal(inflectT[start], inflectT[next])) {
++start;
++next;
continue;
}
memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
}
while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
--inflections;
}
SkDCubicPair pair;
if (inflections == 1) {
pair = chopAt(inflectT[0]);
int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
if (orderP1 < 2) {
--inflections;
} else {
int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
if (orderP2 < 2) {
--inflections;
}
}
}
if (inflections == 0 && add_simple_ts(*this, precision, ts)) {
return;
}
if (inflections == 1) {
pair = chopAt(inflectT[0]);
addTs(pair.first(), precision, 0, inflectT[0], ts);
addTs(pair.second(), precision, inflectT[0], 1, ts);
return;
}
if (inflections > 1) {
SkDCubic part = subDivide(0, inflectT[0]);
addTs(part, precision, 0, inflectT[0], ts);
int last = inflections - 1;
for (int idx = 0; idx < last; ++idx) {
part = subDivide(inflectT[idx], inflectT[idx + 1]);
addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
}
part = subDivide(inflectT[last], 1);
addTs(part, precision, inflectT[last], 1, ts);
return;
}
addTs(*this, precision, 0, 1, ts);
}

View File

@ -7,45 +7,6 @@
#include "SkIntersections.h"
#include "SkPathOpsLine.h"
/* Determine the intersection point of two lines. This assumes the lines are not parallel,
and that that the lines are infinite.
From http://en.wikipedia.org/wiki/Line-line_intersection
*/
SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
double axLen = a[1].fX - a[0].fX;
double ayLen = a[1].fY - a[0].fY;
double bxLen = b[1].fX - b[0].fX;
double byLen = b[1].fY - b[0].fY;
double denom = byLen * axLen - ayLen * bxLen;
SkASSERT(denom);
double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
SkDPoint p;
p.fX = (term1 * bxLen - axLen * term2) / denom;
p.fY = (term1 * byLen - ayLen * term2) / denom;
return p;
}
int SkIntersections::cleanUpCoincidence() {
do {
int last = fUsed - 1;
for (int index = 0; index < last; ++index) {
if (fT[0][index] == fT[0][index + 1]) {
removeOne(index + (int) (fT[1][index] == 0 || fT[1][index] == 1));
goto tryAgain;
}
}
for (int index = 0; index < last; ++index) {
if (fT[1][index] == fT[1][index + 1]) {
removeOne(index + (int) (fT[0][index] == 0 || fT[0][index] == 1));
goto tryAgain;
}
}
return fUsed;
tryAgain: ;
} while (true);
}
void SkIntersections::cleanUpParallelLines(bool parallel) {
while (fUsed > 2) {
removeOne(1);
@ -58,6 +19,9 @@ void SkIntersections::cleanUpParallelLines(bool parallel) {
removeOne(endMatch);
}
}
if (fUsed == 2) {
fIsCoincident[0] = fIsCoincident[1] = 0x03;
}
}
void SkIntersections::computePoints(const SkDLine& line, int used) {
@ -81,12 +45,6 @@ int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
SkDVector ab0 = a[0] - b[0];
double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
#if 0
if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
fUsed = 0;
return 0;
}
#endif
numerA /= denom;
numerB /= denom;
int used;
@ -190,7 +148,6 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
}
SkASSERT(a[iA] != b[nearer]);
SkASSERT(iA == (bNearA[nearer] > 0.5));
fNearlySame[iA] = true;
insertNear(iA, nearer, a[iA], b[nearer]);
aNearB[iA] = -1;
bNearA[nearer] = -1;
@ -235,18 +192,6 @@ static double horizontal_intercept(const SkDLine& line, double y) {
return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
}
int SkIntersections::horizontal(const SkDLine& line, double y) {
fMax = 2;
int horizontalType = horizontal_coincident(line, y);
if (horizontalType == 1) {
fT[0][0] = horizontal_intercept(line, y);
} else if (horizontalType == 2) {
fT[0][0] = 0;
fT[0][1] = 1;
}
return fUsed = horizontalType;
}
int SkIntersections::horizontal(const SkDLine& line, double left, double right,
double y, bool flipped) {
fMax = 3; // clean up parallel at the end will limit the result to 2 at the most
@ -323,18 +268,6 @@ static double vertical_intercept(const SkDLine& line, double x) {
return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
}
int SkIntersections::vertical(const SkDLine& line, double x) {
fMax = 2;
int verticalType = vertical_coincident(line, x);
if (verticalType == 1) {
fT[0][0] = vertical_intercept(line, x);
} else if (verticalType == 2) {
fT[0][0] = 0;
fT[0][1] = 1;
}
return fUsed = verticalType;
}
int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
double x, bool flipped) {
fMax = 3; // cleanup parallel lines will bring this back line
@ -393,14 +326,3 @@ int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
return fUsed;
}
// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
// 4 subs, 2 muls, 1 cmp
static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
}
// 16 subs, 8 muls, 6 cmps
bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
&& ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
}

View File

@ -1,117 +0,0 @@
/*
* 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 "SkDQuadImplicit.h"
/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
*
* This paper proves that Syvester's method can compute the implicit form of
* the quadratic from the parameterized form.
*
* Given x = a*t*t + b*t + c (the parameterized form)
* y = d*t*t + e*t + f
*
* we want to find an equation of the implicit form:
*
* A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
*
* The implicit form can be expressed as a 4x4 determinant, as shown.
*
* The resultant obtained by Syvester's method is
*
* | a b (c - x) 0 |
* | 0 a b (c - x) |
* | d e (f - y) 0 |
* | 0 d e (f - y) |
*
* which expands to
*
* d*d*x*x + -2*a*d*x*y + a*a*y*y
* + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
* + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
* +
* | a b c 0 |
* | 0 a b c | == 0.
* | d e f 0 |
* | 0 d e f |
*
* Expanding the constant determinant results in
*
* | a b c | | b c 0 |
* a*| e f 0 | + d*| a b c | ==
* | d e f | | d e f |
*
* a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
*
*/
// use the tricky arithmetic path, but leave the original to compare just in case
static bool straight_forward = false;
SkDQuadImplicit::SkDQuadImplicit(const SkDQuad& q) {
double a, b, c;
SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
double d, e, f;
SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
// compute the implicit coefficients
if (straight_forward) { // 42 muls, 13 adds
fP[kXx_Coeff] = d * d;
fP[kXy_Coeff] = -2 * a * d;
fP[kYy_Coeff] = a * a;
fP[kX_Coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
fP[kY_Coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
fP[kC_Coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
+ d*(b*b*f + c*c*d - c*a*f - c*e*b);
} else { // 26 muls, 11 adds
double aa = a * a;
double ad = a * d;
double dd = d * d;
fP[kXx_Coeff] = dd;
fP[kXy_Coeff] = -2 * ad;
fP[kYy_Coeff] = aa;
double be = b * e;
double bde = be * d;
double cdd = c * dd;
double ee = e * e;
fP[kX_Coeff] = -2*cdd + bde - a*ee + 2*ad*f;
double aaf = aa * f;
double abe = a * be;
double ac = a * c;
double bb_2ac = b*b - 2*ac;
fP[kY_Coeff] = -2*aaf + abe - d*bb_2ac;
fP[kC_Coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
}
}
/* Given a pair of quadratics, determine their parametric coefficients.
* If the scaled coefficients are nearly equal, then the part of the quadratics
* may be coincident.
* OPTIMIZATION -- since comparison short-circuits on no match,
* lazily compute the coefficients, comparing the easiest to compute first.
* xx and yy first; then xy; and so on.
*/
bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const {
int first = 0;
for (int index = 0; index <= kC_Coeff; ++index) {
if (approximately_zero(fP[index]) && approximately_zero(p2.fP[index])) {
first += first == index;
continue;
}
if (first == index) {
continue;
}
if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
return false;
}
}
return true;
}
bool SkDQuadImplicit::Match(const SkDQuad& quad1, const SkDQuad& quad2) {
SkDQuadImplicit i1(quad1); // a'xx , b'xy , c'yy , d'x , e'y , f
SkDQuadImplicit i2(quad2);
return i1.match(i2);
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDQuadImplicit_DEFINED
#define SkDQuadImplicit_DEFINED
#include "SkPathOpsQuad.h"
class SkDQuadImplicit {
public:
explicit SkDQuadImplicit(const SkDQuad& q);
bool match(const SkDQuadImplicit& two) const;
static bool Match(const SkDQuad& quad1, const SkDQuad& quad2);
double x2() const { return fP[kXx_Coeff]; }
double xy() const { return fP[kXy_Coeff]; }
double y2() const { return fP[kYy_Coeff]; }
double x() const { return fP[kX_Coeff]; }
double y() const { return fP[kY_Coeff]; }
double c() const { return fP[kC_Coeff]; }
private:
enum Coeffs {
kXx_Coeff,
kXy_Coeff,
kYy_Coeff,
kX_Coeff,
kY_Coeff,
kC_Coeff,
};
double fP[kC_Coeff + 1];
};
#endif

View File

@ -1,617 +0,0 @@
// Another approach is to start with the implicit form of one curve and solve
// (seek implicit coefficients in QuadraticParameter.cpp
// by substituting in the parametric form of the other.
// The downside of this approach is that early rejects are difficult to come by.
// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
#include "SkDQuadImplicit.h"
#include "SkIntersections.h"
#include "SkPathOpsLine.h"
#include "SkQuarticRoot.h"
#include "SkTArray.h"
#include "SkTSort.h"
/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
* and given x = at^2 + bt + c (the parameterized form)
* y = dt^2 + et + f
* then
* 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
*/
static int findRoots(const SkDQuadImplicit& i, const SkDQuad& quad, double roots[4],
bool oneHint, bool flip, int firstCubicRoot) {
SkDQuad flipped;
const SkDQuad& q = flip ? (flipped = quad.flip()) : quad;
double a, b, c;
SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
double d, e, f;
SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
const double t4 = i.x2() * a * a
+ i.xy() * a * d
+ i.y2() * d * d;
const double t3 = 2 * i.x2() * a * b
+ i.xy() * (a * e + b * d)
+ 2 * i.y2() * d * e;
const double t2 = i.x2() * (b * b + 2 * a * c)
+ i.xy() * (c * d + b * e + a * f)
+ i.y2() * (e * e + 2 * d * f)
+ i.x() * a
+ i.y() * d;
const double t1 = 2 * i.x2() * b * c
+ i.xy() * (c * e + b * f)
+ 2 * i.y2() * e * f
+ i.x() * b
+ i.y() * e;
const double t0 = i.x2() * c * c
+ i.xy() * c * f
+ i.y2() * f * f
+ i.x() * c
+ i.y() * f
+ i.c();
int rootCount = SkReducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
if (rootCount < 0) {
rootCount = SkQuarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
}
if (flip) {
for (int index = 0; index < rootCount; ++index) {
roots[index] = 1 - roots[index];
}
}
return rootCount;
}
static int addValidRoots(const double roots[4], const int count, double valid[4]) {
int result = 0;
int index;
for (index = 0; index < count; ++index) {
if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
continue;
}
double t = 1 - roots[index];
if (approximately_less_than_zero(t)) {
t = 0;
} else if (approximately_greater_than_one(t)) {
t = 1;
}
SkASSERT(t >= 0 && t <= 1);
valid[result++] = t;
}
return result;
}
static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
// the idea here is to see at minimum do a quick reject by rotating all points
// to either side of the line formed by connecting the endpoints
// if the opposite curves points are on the line or on the other side, the
// curves at most intersect at the endpoints
for (int oddMan = 0; oddMan < 3; ++oddMan) {
const SkDPoint* endPt[2];
for (int opp = 1; opp < 3; ++opp) {
int end = oddMan ^ opp; // choose a value not equal to oddMan
if (3 == end) { // and correct so that largest value is 1 or 2
end = opp;
}
endPt[opp - 1] = &q1[end];
}
double origX = endPt[0]->fX;
double origY = endPt[0]->fY;
double adj = endPt[1]->fX - origX;
double opp = endPt[1]->fY - origY;
double sign = (q1[oddMan].fY - origY) * adj - (q1[oddMan].fX - origX) * opp;
if (approximately_zero(sign)) {
goto tryNextHalfPlane;
}
for (int n = 0; n < 3; ++n) {
double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
if (test * sign > 0 && !precisely_zero(test)) {
goto tryNextHalfPlane;
}
}
return true;
tryNextHalfPlane:
;
}
return false;
}
// returns false if there's more than one intercept or the intercept doesn't match the point
// returns true if the intercept was successfully added or if the
// original quads need to be subdivided
static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
SkIntersections* i, bool* subDivide) {
double tMid = (tMin + tMax) / 2;
SkDPoint mid = q2.ptAtT(tMid);
SkDLine line;
line[0] = line[1] = mid;
SkDVector dxdy = q2.dxdyAtT(tMid);
line[0] -= dxdy;
line[1] += dxdy;
SkIntersections rootTs;
rootTs.allowNear(false);
int roots = rootTs.intersect(q1, line);
if (roots == 0) {
if (subDivide) {
*subDivide = true;
}
return true;
}
if (roots == 2) {
return false;
}
SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
if (!pt2.approximatelyEqual(mid)) {
return false;
}
i->insertSwap(rootTs[0][0], tMid, pt2);
return true;
}
static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
double t2s, double t2e, SkIntersections* i, bool* subDivide) {
SkDQuad hull = q1.subDivide(t1s, t1e);
SkDLine line = {{hull[2], hull[0]}};
const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
const size_t kTestCount = SK_ARRAY_COUNT(testLines);
SkSTArray<kTestCount * 2, double, true> tsFound;
for (size_t index = 0; index < kTestCount; ++index) {
SkIntersections rootTs;
rootTs.allowNear(false);
int roots = rootTs.intersect(q2, *testLines[index]);
for (int idx2 = 0; idx2 < roots; ++idx2) {
double t = rootTs[0][idx2];
#if 0 // def SK_DEBUG // FIXME : accurate for error = 16, error of 17.5 seen
// {{{136.08723965397621, 1648.2814535211637}, {593.49031197259478, 1190.8784277439891}, {593.49031197259478, 544.0128173828125}}}
// {{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
SkDPoint qPt = q2.ptAtT(t);
SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
SkASSERT(qPt.approximatelyDEqual(lPt));
#endif
if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
continue;
}
tsFound.push_back(rootTs[0][idx2]);
}
}
int tCount = tsFound.count();
if (tCount <= 0) {
return true;
}
double tMin, tMax;
if (tCount == 1) {
tMin = tMax = tsFound[0];
} else {
SkASSERT(tCount > 1);
SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
tMin = tsFound[0];
tMax = tsFound[tsFound.count() - 1];
}
SkDPoint end = q2.ptAtT(t2s);
bool startInTriangle = hull.pointInHull(end);
if (startInTriangle) {
tMin = t2s;
}
end = q2.ptAtT(t2e);
bool endInTriangle = hull.pointInHull(end);
if (endInTriangle) {
tMax = t2e;
}
int split = 0;
SkDVector dxy1, dxy2;
if (tMin != tMax || tCount > 2) {
dxy2 = q2.dxdyAtT(tMin);
for (int index = 1; index < tCount; ++index) {
dxy1 = dxy2;
dxy2 = q2.dxdyAtT(tsFound[index]);
double dot = dxy1.dot(dxy2);
if (dot < 0) {
split = index - 1;
break;
}
}
}
if (split == 0) { // there's one point
if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
return true;
}
i->swap();
return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
}
// At this point, we have two ranges of t values -- treat each separately at the split
bool result;
if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
result = true;
} else {
i->swap();
result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
}
if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
result = true;
} else {
i->swap();
result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
}
return result;
}
static double flat_measure(const SkDQuad& q) {
SkDVector mid = q[1] - q[0];
SkDVector dxy = q[2] - q[0];
double length = dxy.length(); // OPTIMIZE: get rid of sqrt
return fabs(mid.cross(dxy) / length);
}
// FIXME ? should this measure both and then use the quad that is the flattest as the line?
static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
if (i->flatMeasure()) {
// for backward compatibility, use the old method when called from cubics
// FIXME: figure out how to fix cubics when it calls the new path
double measure = flat_measure(q1);
// OPTIMIZE: (get rid of sqrt) use approximately_zero
if (!approximately_zero_sqrt(measure)) { // approximately_zero_sqrt
return false;
}
} else {
if (!q1.isLinear(0, 2)) {
return false;
}
}
return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
}
// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
// avoid imprecision incurred with chopAt
static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2,
double s2, double e2, SkIntersections* i) {
double m1 = flat_measure(*q1);
double m2 = flat_measure(*q2);
i->reset();
const SkDQuad* rounder, *flatter;
double sf, midf, ef, sr, er;
if (m2 < m1) {
rounder = q1;
sr = s1;
er = e1;
flatter = q2;
sf = s2;
midf = (s2 + e2) / 2;
ef = e2;
} else {
rounder = q2;
sr = s2;
er = e2;
flatter = q1;
sf = s1;
midf = (s1 + e1) / 2;
ef = e1;
}
bool subDivide = false;
is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
if (subDivide) {
relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
}
if (m2 < m1) {
i->swapPts();
}
}
// each time through the loop, this computes values it had from the last loop
// if i == j == 1, the center values are still good
// otherwise, for i != 1 or j != 1, four of the values are still good
// and if i == 1 ^ j == 1, an additional value is good
static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1Seed,
double* t2Seed, SkDPoint* pt) {
double tStep = ROUGH_EPSILON;
SkDPoint t1[3], t2[3];
int calcMask = ~0;
do {
if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed);
if (t1[1].approximatelyEqual(t2[1])) {
*pt = t1[1];
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__,
t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY);
#endif
if (*t1Seed < 0) {
*t1Seed = 0;
} else if (*t1Seed > 1) {
*t1Seed = 1;
}
if (*t2Seed < 0) {
*t2Seed = 0;
} else if (*t2Seed > 1) {
*t2Seed = 1;
}
return true;
}
if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep));
if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep));
if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep));
if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *t2Seed + tStep));
double dist[3][3];
// OPTIMIZE: using calcMask value permits skipping some distance calcuations
// if prior loop's results are moved to correct slot for reuse
dist[1][1] = t1[1].distanceSquared(t2[1]);
int best_i = 1, best_j = 1;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (i == 1 && j == 1) {
continue;
}
dist[i][j] = t1[i].distanceSquared(t2[j]);
if (dist[best_i][best_j] > dist[i][j]) {
best_i = i;
best_j = j;
}
}
}
if (best_i == 1 && best_j == 1) {
tStep /= 2;
if (tStep < FLT_EPSILON_HALF) {
break;
}
calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5);
continue;
}
if (best_i == 0) {
*t1Seed -= tStep;
t1[2] = t1[1];
t1[1] = t1[0];
calcMask = 1 << 0;
} else if (best_i == 2) {
*t1Seed += tStep;
t1[0] = t1[1];
t1[1] = t1[2];
calcMask = 1 << 2;
} else {
calcMask = 0;
}
if (best_j == 0) {
*t2Seed -= tStep;
t2[2] = t2[1];
t2[1] = t2[0];
calcMask |= 1 << 3;
} else if (best_j == 2) {
*t2Seed += tStep;
t2[0] = t2[1];
t2[1] = t2[2];
calcMask |= 1 << 5;
}
} while (true);
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__,
t1Seed, t2Seed, t1[1].fX, t1[1].fY, t1[2].fX, t1[2].fY);
#endif
return false;
}
static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
const SkIntersections& orig, bool swap, SkIntersections* i) {
if (orig.used() == 1 && orig[!swap][0] == testT) {
return;
}
if (orig.used() == 2 && orig[!swap][1] == testT) {
return;
}
SkDLine tmpLine;
int testTIndex = testT << 1;
tmpLine[0] = tmpLine[1] = q2[testTIndex];
tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY;
tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX;
SkIntersections impTs;
impTs.intersectRay(q1, tmpLine);
for (int index = 0; index < impTs.used(); ++index) {
SkDPoint realPt = impTs.pt(index);
if (!tmpLine[0].approximatelyPEqual(realPt)) {
continue;
}
if (swap) {
i->insert(testT, impTs[0][index], tmpLine[0]);
} else {
i->insert(impTs[0][index], testT, tmpLine[0]);
}
}
}
int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
fMax = 4;
bool exactMatch = false;
// if the quads share an end point, check to see if they overlap
for (int i1 = 0; i1 < 3; i1 += 2) {
for (int i2 = 0; i2 < 3; i2 += 2) {
if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
insert(i1 >> 1, i2 >> 1, q1[i1]);
exactMatch = true;
}
}
}
SkASSERT(fUsed < 3);
if (only_end_pts_in_common(q1, q2)) {
return fUsed;
}
if (only_end_pts_in_common(q2, q1)) {
return fUsed;
}
// see if either quad is really a line
// FIXME: figure out why reduce step didn't find this earlier
if (is_linear(q1, q2, this)) {
return fUsed;
}
SkIntersections swapped;
swapped.setMax(fMax);
if (is_linear(q2, q1, &swapped)) {
swapped.swapPts();
*this = swapped;
return fUsed;
}
SkIntersections copyI(*this);
lookNearEnd(q1, q2, 0, *this, false, &copyI);
lookNearEnd(q1, q2, 1, *this, false, &copyI);
lookNearEnd(q2, q1, 0, *this, true, &copyI);
lookNearEnd(q2, q1, 1, *this, true, &copyI);
int innerEqual = 0;
if (copyI.fUsed >= 2) {
SkASSERT(copyI.fUsed <= 4);
double width = copyI[0][1] - copyI[0][0];
int midEnd = 1;
for (int index = 2; index < copyI.fUsed; ++index) {
double testWidth = copyI[0][index] - copyI[0][index - 1];
if (testWidth <= width) {
continue;
}
midEnd = index;
}
for (int index = 0; index < 2; ++index) {
double testT = (copyI[0][midEnd] * (index + 1)
+ copyI[0][midEnd - 1] * (2 - index)) / 3;
SkDPoint testPt1 = q1.ptAtT(testT);
testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3;
SkDPoint testPt2 = q2.ptAtT(testT);
innerEqual += testPt1.approximatelyEqual(testPt2);
}
}
bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2;
if (expectCoincident) {
reset();
insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]);
int last = copyI.fUsed - 1;
insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]);
return fUsed;
}
SkDQuadImplicit i1(q1);
SkDQuadImplicit i2(q2);
int index;
bool flip1 = q1[2] == q2[0];
bool flip2 = q1[0] == q2[2];
bool useCubic = q1[0] == q2[0];
double roots1[4];
int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
// OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
double roots1Copy[4];
SkDEBUGCODE(sk_bzero(roots1Copy, sizeof(roots1Copy)));
int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
SkDPoint pts1[4];
for (index = 0; index < r1Count; ++index) {
pts1[index] = q1.ptAtT(roots1Copy[index]);
}
double roots2[4];
int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
double roots2Copy[4];
int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
SkDPoint pts2[4];
for (index = 0; index < r2Count; ++index) {
pts2[index] = q2.ptAtT(roots2Copy[index]);
}
bool triedBinary = false;
if (r1Count == r2Count && r1Count <= 1) {
if (r1Count == 1 && used() == 0) {
if (pts1[0].approximatelyEqual(pts2[0])) {
insert(roots1Copy[0], roots2Copy[0], pts1[0]);
} else {
// find intersection by chasing t
triedBinary = true;
if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
insert(roots1Copy[0], roots2Copy[0], pts1[0]);
}
}
}
return fUsed;
}
int closest[4];
double dist[4];
bool foundSomething = false;
for (index = 0; index < r1Count; ++index) {
dist[index] = DBL_MAX;
closest[index] = -1;
for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
continue;
}
double dx = pts2[ndex2].fX - pts1[index].fX;
double dy = pts2[ndex2].fY - pts1[index].fY;
double distance = dx * dx + dy * dy;
if (dist[index] <= distance) {
continue;
}
for (int outer = 0; outer < index; ++outer) {
if (closest[outer] != ndex2) {
continue;
}
if (dist[outer] < distance) {
goto next;
}
closest[outer] = -1;
}
dist[index] = distance;
closest[index] = ndex2;
foundSomething = true;
next:
;
}
}
if (r1Count && r2Count && !foundSomething) {
if (exactMatch) {
SkASSERT(fUsed > 0);
return fUsed;
}
relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
if (fUsed) {
return fUsed;
}
// maybe the curves are nearly coincident
if (!triedBinary && binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
insert(roots1Copy[0], roots2Copy[0], pts1[0]);
}
return fUsed;
}
int used = 0;
do {
double lowest = DBL_MAX;
int lowestIndex = -1;
for (index = 0; index < r1Count; ++index) {
if (closest[index] < 0) {
continue;
}
if (roots1Copy[index] < lowest) {
lowestIndex = index;
lowest = roots1Copy[index];
}
}
if (lowestIndex < 0) {
break;
}
insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
pts1[lowestIndex]);
closest[lowestIndex] = -1;
} while (++used < r1Count);
return fUsed;
}
void SkIntersections::alignQuadPts(const SkPoint q1[3], const SkPoint q2[3]) {
for (int index = 0; index < used(); ++index) {
const SkPoint result = pt(index).asSkPoint();
if (q1[0] == result || q1[2] == result || q2[0] == result || q2[2] == result) {
continue;
}
if (SkDPoint::ApproximatelyEqual(q1[0], result)) {
fPt[index].set(q1[0]);
// SkASSERT(way_roughly_zero(fT[0][index])); // this value can be bigger than way rough
fT[0][index] = 0;
} else if (SkDPoint::ApproximatelyEqual(q1[2], result)) {
fPt[index].set(q1[2]);
// SkASSERT(way_roughly_equal(fT[0][index], 1));
fT[0][index] = 1;
}
if (SkDPoint::ApproximatelyEqual(q2[0], result)) {
fPt[index].set(q2[0]);
// SkASSERT(way_roughly_zero(fT[1][index]));
fT[1][index] = 0;
} else if (SkDPoint::ApproximatelyEqual(q2[2], result)) {
fPt[index].set(q2[2]);
// SkASSERT(way_roughly_equal(fT[1][index], 1));
fT[1][index] = 1;
}
}
}

View File

@ -105,6 +105,29 @@ public:
fAllowNear = allow;
}
void checkCoincident() {
int last = fIntersections->used() - 1;
for (int index = 0; index < last; ) {
double quadMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
double t = fLine.nearPoint(quadMidPt, NULL);
if (t < 0) {
++index;
continue;
}
if (fIntersections->isCoincident(index)) {
fIntersections->removeOne(index);
--last;
} else if (fIntersections->isCoincident(index + 1)) {
fIntersections->removeOne(index + 1);
--last;
} else {
fIntersections->setCoincident(index++);
}
fIntersections->setCoincident(index);
}
}
int intersectRay(double roots[2]) {
/*
solve by rotating line+quad so line is horizontal, then finding the roots
@ -140,20 +163,17 @@ public:
if (fAllowNear) {
addNearEndPoints();
}
if (fIntersections->used() == 2) {
// FIXME : need sharable code that turns spans into coincident if middle point is on
} else {
double rootVals[2];
int roots = intersectRay(rootVals);
for (int index = 0; index < roots; ++index) {
double quadT = rootVals[index];
double lineT = findLineT(quadT);
SkDPoint pt;
if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) {
fIntersections->insert(quadT, lineT, pt);
}
double rootVals[2];
int roots = intersectRay(rootVals);
for (int index = 0; index < roots; ++index) {
double quadT = rootVals[index];
double lineT = findLineT(quadT);
SkDPoint pt;
if (pinTs(&quadT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(quadT, pt)) {
fIntersections->insert(quadT, lineT, pt);
}
}
checkCoincident();
return fIntersections->used();
}
@ -178,16 +198,41 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fX - left) / (right - left);
if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
fIntersections->insert(quadT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
checkCoincident();
return fIntersections->used();
}
bool uniqueAnswer(double quadT, const SkDPoint& pt) {
for (int inner = 0; inner < fIntersections->used(); ++inner) {
if (fIntersections->pt(inner) != pt) {
continue;
}
double existingQuadT = (*fIntersections)[0][inner];
if (quadT == existingQuadT) {
return false;
}
// check if midway on quad is also same point. If so, discard this
double quadMidT = (existingQuadT + quadT) / 2;
SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
if (quadMidPt.approximatelyEqual(pt)) {
return false;
}
}
#if ONE_OFF_DEBUG
SkDPoint qPt = fQuad.ptAtT(quadT);
SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
qPt.fX, qPt.fY);
#endif
return true;
}
int verticalIntersect(double axisIntercept, double roots[2]) {
double D = fQuad[2].fX; // f
double E = fQuad[1].fX; // e
@ -209,13 +254,14 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fY - top) / (bottom - top);
if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
fIntersections->insert(quadT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
checkCoincident();
return fIntersections->used();
}

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPath.h"
#ifdef SK_DEBUG
@ -21,42 +22,9 @@ public:
kCubic_Segment = SkPath::kCubic_Verb,
};
bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
}
// FIXME: does it make sense to write otherIndex now if we're going to
// fix it up later?
void addOtherT(int index, double otherT, int otherIndex) {
fContour->addOtherT(fIndex, index, otherT, otherIndex);
}
bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
bool swap) {
return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
swap);
}
// Avoid collapsing t values that are close to the same since
// we walk ts to describe consecutive intersections. Since a pair of ts can
// be nearly equal, any problems caused by this should be taken care
// of later.
// On the edge or out of range values are negative; add 2 to get end
int addT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT);
}
int addSelfT(const SkPoint& pt, double newT) {
return fContour->addSelfT(fIndex, pt, newT);
}
bool advance() {
return ++fIndex < fLast;
}
void alignTPt(SkIntersectionHelper& other, bool swap, int index,
SkIntersections* ts, SkPoint* point) {
fContour->alignTPt(fIndex, other.fContour, other.fIndex, swap, index, ts, point);
fSegment = fSegment->next();
return fSegment != NULL;
}
SkScalar bottom() const {
@ -64,30 +32,15 @@ public:
}
const SkPathOpsBounds& bounds() const {
return fContour->segments()[fIndex].bounds();
return fSegment->bounds();
}
SkOpContour* contour() const {
return fSegment->contour();
}
void init(SkOpContour* contour) {
fContour = contour;
fIndex = 0;
fLast = contour->segments().count();
}
bool isAdjacent(const SkIntersectionHelper& next) {
return fContour == next.fContour && fIndex + 1 == next.fIndex;
}
bool isFirstLast(const SkIntersectionHelper& next) {
return fContour == next.fContour && fIndex == 0
&& next.fIndex == fLast - 1;
}
bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const {
const SkOpSegment& segment = fContour->segments()[fIndex];
double mid = (t1 + t2) / 2;
SkDPoint midPtByT = segment.dPtAtT(mid);
SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
return midPtByT.approximatelyPEqual(midPtByAvg);
fSegment = contour->first();
}
SkScalar left() const {
@ -95,41 +48,40 @@ public:
}
const SkPoint* pts() const {
return fContour->segments()[fIndex].pts();
return fSegment->pts();
}
SkScalar right() const {
return bounds().fRight;
}
SkOpSegment* segment() const {
return fSegment;
}
SegmentType segmentType() const {
const SkOpSegment& segment = fContour->segments()[fIndex];
SegmentType type = (SegmentType) segment.verb();
SegmentType type = (SegmentType) fSegment->verb();
if (type != kLine_Segment) {
return type;
}
if (segment.isHorizontal()) {
if (fSegment->isHorizontal()) {
return kHorizontalLine_Segment;
}
if (segment.isVertical()) {
if (fSegment->isVertical()) {
return kVerticalLine_Segment;
}
return kLine_Segment;
}
bool startAfter(const SkIntersectionHelper& after) {
fIndex = after.fIndex;
return advance();
fSegment = after.fSegment->next();
return fSegment != NULL;
}
SkScalar top() const {
return bounds().fTop;
}
SkPath::Verb verb() const {
return fContour->segments()[fIndex].verb();
}
SkScalar x() const {
return bounds().fLeft;
}
@ -147,10 +99,5 @@ public:
}
private:
// utility callable by the user from the debugger when the implementation code is linked in
void dump() const;
SkOpContour* fContour;
int fIndex;
int fLast;
SkOpSegment* fSegment;
};

View File

@ -7,26 +7,25 @@
#include "SkIntersections.h"
void SkIntersections::append(const SkIntersections& i) {
for (int index = 0; index < i.fUsed; ++index) {
insert(i[0][index], i[1][index], i.pt(index));
int SkIntersections::closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt,
double* closestDist) const {
int closest = -1;
*closestDist = SK_ScalarMax;
for (int index = 0; index < fUsed; ++index) {
if (!between(rangeStart, fT[0][index], rangeEnd)) {
continue;
}
const SkDPoint& iPt = fPt[index];
double dist = testPt.distanceSquared(iPt);
if (*closestDist > dist) {
*closestDist = dist;
closest = index;
}
}
return closest;
}
int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
NULL,
&SkIntersections::verticalLine,
&SkIntersections::verticalQuad,
&SkIntersections::verticalCubic
};
int ( SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine&) = {
NULL,
&SkIntersections::lineRay,
&SkIntersections::quadRay,
&SkIntersections::cubicRay
};
// called only by test code
int SkIntersections::coincidentUsed() const {
if (!fIsCoincident[0]) {
SkASSERT(!fIsCoincident[1]);
@ -48,12 +47,12 @@ int SkIntersections::coincidentUsed() const {
return count;
}
int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
SkDCubic cubic;
cubic.set(pts);
fMax = 3;
return intersectRay(cubic, line);
}
int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
NULL,
&SkIntersections::verticalLine,
&SkIntersections::verticalQuad,
&SkIntersections::verticalCubic
};
void SkIntersections::flip() {
for (int index = 0; index < fUsed; ++index) {
@ -105,7 +104,6 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
int remaining = fUsed - index;
if (remaining > 0) {
memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
memmove(&fPt2[index + 1], &fPt2[index], sizeof(fPt2[0]) * remaining);
memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
int clearMask = ~((1 << index) - 1);
@ -125,39 +123,53 @@ void SkIntersections::insertNear(double one, double two, const SkDPoint& pt1, co
SkASSERT(one == 0 || one == 1);
SkASSERT(two == 0 || two == 1);
SkASSERT(pt1 != pt2);
SkASSERT(fNearlySame[(int) one]);
fNearlySame[one ? 1 : 0] = true;
(void) insert(one, two, pt1);
fPt2[one ? fUsed - 1 : 0] = pt2;
fPt2[one ? 1 : 0] = pt2;
}
void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
int SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
int index = insertSwap(one, two, pt);
if (index >= 0) {
setCoincident(index);
}
return index;
}
void SkIntersections::setCoincident(int index) {
SkASSERT(index >= 0);
int bit = 1 << index;
fIsCoincident[0] |= bit;
fIsCoincident[1] |= bit;
}
int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
SkDLine l;
l.set(pts);
fMax = 2;
return intersectRay(l, line);
void SkIntersections::merge(const SkIntersections& a, int aIndex, const SkIntersections& b,
int bIndex) {
this->reset();
fT[0][0] = a.fT[0][aIndex];
fT[1][0] = b.fT[0][bIndex];
fPt[0] = a.fPt[aIndex];
fPt2[0] = b.fPt[bIndex];
fUsed = 1;
}
void SkIntersections::offset(int base, double start, double end) {
for (int index = base; index < fUsed; ++index) {
double val = fT[fSwap][index];
val *= end - start;
val += start;
fT[fSwap][index] = val;
int SkIntersections::mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const {
int result = -1;
for (int index = 0; index < fUsed; ++index) {
if (!between(rangeStart, fT[0][index], rangeEnd)) {
continue;
}
if (result < 0) {
result = index;
continue;
}
SkDVector best = fPt[result] - origin;
SkDVector test = fPt[index] - origin;
if (test.crossCheck(best) < 0) {
result = index;
}
}
}
int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
SkDQuad quad;
quad.set(pts);
fMax = 2;
return intersectRay(quad, line);
return result;
}
void SkIntersections::quickRemoveOne(int index, int replace) {
@ -172,7 +184,6 @@ void SkIntersections::removeOne(int index) {
return;
}
memmove(&fPt[index], &fPt[index + 1], sizeof(fPt[0]) * remaining);
memmove(&fPt2[index], &fPt2[index + 1], sizeof(fPt2[0]) * remaining);
memmove(&fT[0][index], &fT[0][index + 1], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index], &fT[1][index + 1], sizeof(fT[1][0]) * remaining);
// SkASSERT(fIsCoincident[0] == 0);
@ -182,13 +193,6 @@ void SkIntersections::removeOne(int index) {
fIsCoincident[1] -= ((fIsCoincident[1] >> 1) & ~((1 << index) - 1)) + coBit;
}
void SkIntersections::swapPts() {
int index;
for (index = 0; index < fUsed; ++index) {
SkTSwap(fT[0][index], fT[1][index]);
}
}
int SkIntersections::verticalLine(const SkPoint a[2], SkScalar top, SkScalar bottom,
SkScalar x, bool flipped) {
SkDLine line;

View File

@ -16,7 +16,6 @@ class SkIntersections {
public:
SkIntersections()
: fSwap(0)
, fFlatMeasure(false)
#ifdef SK_DEBUG
, fDepth(0)
#endif
@ -24,7 +23,6 @@ public:
sk_bzero(fPt, sizeof(fPt));
sk_bzero(fPt2, sizeof(fPt2));
sk_bzero(fT, sizeof(fT));
sk_bzero(fIsCoincident, sizeof(fIsCoincident));
sk_bzero(fNearlySame, sizeof(fNearlySame));
reset();
fMax = 0; // require that the caller set the max
@ -32,7 +30,7 @@ public:
class TArray {
public:
explicit TArray(const double ts[9]) : fTArray(ts) {}
explicit TArray(const double ts[10]) : fTArray(ts) {}
double operator[](int n) const {
return fTArray[n];
}
@ -40,28 +38,15 @@ public:
};
TArray operator[](int n) const { return TArray(fT[n]); }
void allowFlatMeasure(bool flatAllowed) {
fFlatMeasure = flatAllowed;
}
void allowNear(bool nearAllowed) {
fAllowNear = nearAllowed;
}
int cubic(const SkPoint a[4]) {
SkDCubic cubic;
cubic.set(a);
fMax = 1; // self intersect
return intersect(cubic);
}
int cubicCubic(const SkPoint a[4], const SkPoint b[4]) {
SkDCubic aCubic;
aCubic.set(a);
SkDCubic bCubic;
bCubic.set(b);
fMax = 9;
return intersect(aCubic, bCubic);
void clearCoincidence(int index) {
SkASSERT(index >= 0);
int bit = 1 << index;
fIsCoincident[0] &= ~bit;
fIsCoincident[1] &= ~bit;
}
int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@ -88,19 +73,6 @@ public:
return intersect(cubic, line);
}
int cubicQuad(const SkPoint a[4], const SkPoint b[3]) {
SkDCubic cubic;
cubic.set(a);
SkDQuad quad;
quad.set(b);
fMax = 7;
return intersect(cubic, quad);
}
bool flatMeasure() const {
return fFlatMeasure;
}
bool hasT(double t) const {
SkASSERT(t == 0 || t == 1);
return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
@ -178,19 +150,11 @@ public:
return intersect(quad, line);
}
int quadQuad(const SkPoint a[3], const SkPoint b[3]) {
SkDQuad aQuad;
aQuad.set(a);
SkDQuad bQuad;
bQuad.set(b);
fMax = 4;
return intersect(aQuad, bQuad);
}
// leaves swap, max alone
void reset() {
fAllowNear = true;
fUsed = 0;
sk_bzero(fIsCoincident, sizeof(fIsCoincident));
}
void set(bool swap, int tIndex, double t) {
@ -205,8 +169,6 @@ public:
fSwap ^= true;
}
void swapPts();
bool swapped() const {
return fSwap;
}
@ -219,19 +181,27 @@ public:
SkASSERT(--fDepth >= 0);
}
bool unBumpT(int index) {
SkASSERT(fUsed == 1);
fT[0][index] = fT[0][index] * (1 + BUMP_EPSILON * 2) - BUMP_EPSILON;
if (!between(0, fT[0][index], 1)) {
fUsed = 0;
return false;
}
return true;
}
void upDepth() {
SkASSERT(++fDepth < 16);
}
void alignQuadPts(const SkPoint a[3], const SkPoint b[3]);
void append(const SkIntersections& );
int cleanUpCoincidence();
int closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt, double* dist) const;
int coincidentUsed() const;
void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1,
const SkDCubic& c2);
int cubicRay(const SkPoint pts[4], const SkDLine& line);
void flip();
int horizontal(const SkDLine&, double y);
int horizontal(const SkDLine&, double left, double right, double y, bool flipped);
int horizontal(const SkDQuad&, double left, double right, double y, bool flipped);
int horizontal(const SkDQuad&, double left, double right, double y, double tRange[2]);
@ -242,25 +212,20 @@ public:
int insert(double one, double two, const SkDPoint& pt);
void insertNear(double one, double two, const SkDPoint& pt1, const SkDPoint& pt2);
// start if index == 0 : end if index == 1
void insertCoincident(double one, double two, const SkDPoint& pt);
int insertCoincident(double one, double two, const SkDPoint& pt);
int intersect(const SkDLine&, const SkDLine&);
int intersect(const SkDQuad&, const SkDLine&);
int intersect(const SkDQuad&, const SkDQuad&);
int intersect(const SkDCubic&); // return true if cubic self-intersects
int intersect(const SkDCubic&, const SkDLine&);
int intersect(const SkDCubic&, const SkDQuad&);
int intersect(const SkDCubic&, const SkDCubic&);
int intersectRay(const SkDLine&, const SkDLine&);
int intersectRay(const SkDQuad&, const SkDLine&);
int intersectRay(const SkDCubic&, const SkDLine&);
static SkDPoint Line(const SkDLine&, const SkDLine&);
int lineRay(const SkPoint pts[2], const SkDLine& line);
void offset(int base, double start, double end);
void merge(const SkIntersections& , int , const SkIntersections& , int );
int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
void quickRemoveOne(int index, int replace);
int quadRay(const SkPoint pts[3], const SkDLine& line);
void removeOne(int index);
static bool Test(const SkDLine& , const SkDLine&);
int vertical(const SkDLine&, double x);
void setCoincident(int index);
int vertical(const SkDLine&, double top, double bottom, double x, bool flipped);
int vertical(const SkDQuad&, double top, double bottom, double x, bool flipped);
int vertical(const SkDCubic&, double top, double bottom, double x, bool flipped);
@ -276,6 +241,8 @@ public:
#endif
}
void dump() const; // implemented for testing only
private:
bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2);
bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2);
@ -283,22 +250,20 @@ private:
void cleanUpParallelLines(bool parallel);
void computePoints(const SkDLine& line, int used);
SkDPoint fPt[9]; // FIXME: since scans store points as SkPoint, this should also
SkDPoint fPt2[9]; // used by nearly same to store alternate intersection point
double fT[2][9];
SkDPoint fPt[10]; // FIXME: since scans store points as SkPoint, this should also
SkDPoint fPt2[2]; // used by nearly same to store alternate intersection point
double fT[2][10];
uint16_t fIsCoincident[2]; // bit set for each curve's coincident T
bool fNearlySame[2]; // true if end points nearly match
unsigned char fUsed;
unsigned char fMax;
bool fAllowNear;
bool fSwap;
bool fFlatMeasure; // backwards-compatibility when cubics uses quad intersection
#ifdef SK_DEBUG
int fDepth;
#endif
};
extern int (SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine& );
extern int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar top, SkScalar bottom,
SkScalar x, bool flipped);

File diff suppressed because it is too large Load Diff

View File

@ -7,17 +7,18 @@
#ifndef SkOpAngle_DEFINED
#define SkOpAngle_DEFINED
#include "SkChunkAlloc.h"
#include "SkLineParameters.h"
#if DEBUG_ANGLE
#include "SkString.h"
#endif
class SkOpContour;
class SkOpPtT;
class SkOpSegment;
struct SkOpSpan;
class SkOpSpanBase;
class SkOpSpan;
// sorting angles
// given angles of {dx dy ddx ddy dddx dddy} sort them
class SkOpAngle {
public:
enum { kStackBasedCount = 8 }; // FIXME: determine what this should be
struct SkOpAngle {
enum IncludeType {
kUnaryWinding,
kUnaryXor,
@ -25,29 +26,66 @@ public:
kBinaryOpp,
};
bool after(SkOpAngle* test);
int allOnOneSide(const SkOpAngle* test);
bool checkCrossesZero() const;
void checkNearCoincidence();
bool checkParallel(SkOpAngle* );
bool computeSector();
int convexHullOverlaps(const SkOpAngle* ) const;
int end() const {
const SkOpAngle* debugAngle(int id) const;
SkOpContour* debugContour(int id);
int debugID() const {
return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
#if DEBUG_SORT
void debugLoop() const;
#endif
#if DEBUG_ANGLE
SkString debugPart() const;
#endif
const SkOpPtT* debugPtT(int id) const;
const SkOpSegment* debugSegment(int id) const;
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
void debugValidateNext() const; // in debug builds, verify that angle loop is uncorrupted
double distEndRatio(double dist) const;
// available to testing only
void dump() const;
void dumpCurves() const;
void dumpLoop() const;
void dumpOne(bool functionHeader) const;
void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
void dumpTest() const;
SkOpSpanBase* end() const {
return fEnd;
}
const SkOpAngle* findFirst() const;
bool inLoop() const {
return !!fNext;
}
bool endsIntersect(SkOpAngle* );
bool endToSide(const SkOpAngle* rh, bool* inside) const;
SkOpAngle* findFirst();
int findSector(SkPath::Verb verb, double x, double y) const;
SkOpGlobalState* globalState() const;
void insert(SkOpAngle* );
bool isHorizontal() const;
SkOpSpan* lastMarked() const;
bool loopContains(const SkOpAngle& ) const;
SkOpSpanBase* lastMarked() const;
bool loopContains(const SkOpAngle* ) const;
int loopCount() const;
void markStops();
bool merge(SkOpAngle* );
double midT() const;
bool midToSide(const SkOpAngle* rh, bool* inside) const;
SkOpAngle* next() const {
return fNext;
}
bool oppositePlanes(const SkOpAngle* rh) const;
bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
SkOpAngle* previous() const;
int sectorEnd() const {
@ -58,120 +96,57 @@ public:
return fSectorStart;
}
void set(const SkOpSegment* segment, int start, int end);
SkOpSegment* segment() const;
void setLastMarked(SkOpSpan* marked) {
void set(SkOpSpanBase* start, SkOpSpanBase* end);
void setCurveHullSweep();
void setID(int id) {
PATH_OPS_DEBUG_CODE(fID = id);
}
void setLastMarked(SkOpSpanBase* marked) {
fLastMarked = marked;
}
SkOpSegment* segment() const {
return const_cast<SkOpSegment*>(fSegment);
}
void setSector();
void setSpans();
int sign() const;
int sign() const {
return SkSign32(fStart - fEnd);
}
bool small() const;
int start() const {
SkOpSpanBase* start() const {
return fStart;
}
SkOpSpan* starter();
bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
bool unorderable() const {
return fUnorderable;
}
// available to testing only
#if DEBUG_SORT
void debugLoop() const; // called by code during run
#endif
#if DEBUG_ANGLE
void debugSameAs(const SkOpAngle* compare) const;
#endif
void dump() const;
void dumpLoop() const;
void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
#if DEBUG_ANGLE
int debugID() const { return fID; }
void setID(int id) {
fID = id;
}
#else
int debugID() const { return 0; }
#endif
#if DEBUG_VALIDATE
void debugValidateLoop() const;
#endif
private:
bool after(const SkOpAngle* test) const;
int allOnOneSide(const SkOpAngle& test) const;
bool calcSlop(double x, double y, double rx, double ry, bool* result) const;
bool checkCrossesZero() const;
bool checkParallel(const SkOpAngle& ) const;
bool computeSector();
int convexHullOverlaps(const SkOpAngle& ) const;
double distEndRatio(double dist) const;
int findSector(SkPath::Verb verb, double x, double y) const;
bool endsIntersect(const SkOpAngle& ) const;
double midT() const;
bool oppositePlanes(const SkOpAngle& rh) const;
bool orderable(const SkOpAngle& rh) const; // false == this < rh ; true == this > rh
bool overlap(const SkOpAngle& test) const;
void setCurveHullSweep();
void setSector();
void setSpans();
bool tangentsDiverge(const SkOpAngle& rh, double s0xt0) const;
SkDCubic fCurvePart; // the curve from start to end
SkDCubic fCurvePart; // the curve from start to end
double fSide;
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
const SkOpSegment* fSegment;
SkOpAngle* fNext;
SkOpSpan* fLastMarked;
SkOpSpanBase* fLastMarked;
SkDVector fSweep[2];
int fStart;
int fEnd;
int fComputedEnd;
SkOpSpanBase* fStart;
SkOpSpanBase* fEnd;
SkOpSpanBase* fComputedEnd;
int fSectorMask;
int8_t fSectorStart; // in 32nds of a circle
int8_t fSectorEnd;
bool fIsCurve;
bool fStop; // set if ordered angle is greater than the previous
mutable bool fUnorderable; // this is editable by orderable()
bool fStop; // set if ordered angle is greater than the previous
bool fUnorderable;
bool fUnorderedSweep; // set when a cubic's first control point between the sweep vectors
bool fComputeSector;
bool fComputedSector;
bool fCheckCoincidence;
PATH_OPS_DEBUG_CODE(int fID);
#if DEBUG_ANGLE
int fID;
#endif
#if DEBUG_VALIDATE
void debugValidateNext() const; // in debug builds, verify that angle loop is uncorrupted
#else
void debugValidateNext() const {}
#endif
void dumpOne(bool showFunc) const; // available to testing only
void dumpPartials() const; // utility to be called by user from debugger
friend class PathOpsAngleTester;
};
class SkOpAngleSet {
public:
SkOpAngleSet();
~SkOpAngleSet();
SkOpAngle& push_back();
void reset();
private:
void dump() const; // utility to be called by user from debugger
SkChunkAlloc* fAngles;
#if DEBUG_ANGLE
int fCount;
#endif
};
#endif

388
src/pathops/SkOpCoincidence.cpp Executable file
View File

@ -0,0 +1,388 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkOpCoincidence.h"
#include "SkOpSegment.h"
#include "SkPathOpsTSect.h"
void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
bool flipped = oppPtTStart->fT > oppPtTEnd->fT;
SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
coinRec->fNext = this->fHead;
coinRec->fCoinPtTStart = coinPtTStart;
coinRec->fCoinPtTEnd = coinPtTEnd;
coinRec->fOppPtTStart = oppPtTStart;
coinRec->fOppPtTEnd = oppPtTEnd;
coinRec->fFlipped = flipped;
this->fHead = coinRec;
}
static void tRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
double denom = overE->fT - overS->fT;
double start = 0 < denom ? tStart : tEnd;
double end = 0 < denom ? tEnd : tStart;
double sRatio = (start - overS->fT) / denom;
double eRatio = (end - overS->fT) / denom;
*coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
*coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
}
bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
double coinTs, coinTe, oppTs, oppTe;
tRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
tRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
SkOpSegment* coinSeg = coinPtTStart->segment();
SkOpSegment* oppSeg = oppPtTStart->segment();
SkASSERT(coinSeg != oppSeg);
SkCoincidentSpans* check = this->fHead;
do {
const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
continue;
}
const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
continue;
}
int cTs = coinTs;
int cTe = coinTe;
int oTs = oppTs;
int oTe = oppTe;
if (checkCoinSeg != coinSeg) {
SkASSERT(checkOppSeg != oppSeg);
SkTSwap(cTs, oTs);
SkTSwap(cTe, oTe);
}
int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
+ (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
+ (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
+ (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
// SkASSERT(tweenCount == 0 || tweenCount == 4);
if (tweenCount) {
return true;
}
} while ((check = check->fNext));
if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
SkTSwap(oppTs, oppTe);
}
if (coinTs > coinTe) {
SkTSwap(coinTs, coinTe);
SkTSwap(oppTs, oppTe);
}
SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
if (cs == ce) {
return false;
}
SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator);
SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator);
SkASSERT(os != oe);
cs->addOpp(os);
ce->addOpp(oe);
this->add(cs, ce, os, oe, allocator);
return true;
}
bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
SkCoincidentSpans* outer = this->fHead;
if (!outer) {
return true;
}
do {
SkCoincidentSpans* inner = outer;
while ((inner = inner->fNext)) {
double overS, overE;
if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
return false;
}
} else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
return false;
}
} else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
return false;
}
} else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
return false;
}
}
}
} while ((outer = outer->fNext));
return true;
}
bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, bool flipped) {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return false;
}
do {
if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd
&& coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
&& coin->fFlipped == flipped) {
return true;
}
} while ((coin = coin->fNext));
return false;
}
// walk span sets in parallel, moving winding from one to the other
bool SkOpCoincidence::apply() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return true;
}
do {
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
SkASSERT(start == start->starter(end));
bool flipped = coin->fFlipped;
SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
SkASSERT(oStart == oStart->starter(oEnd));
SkOpSegment* segment = start->segment();
SkOpSegment* oSegment = oStart->segment();
bool operandSwap = segment->operand() != oSegment->operand();
if (flipped) {
do {
SkOpSpanBase* oNext = oStart->next();
if (oNext == oEnd) {
break;
}
oStart = oNext->upCast();
} while (true);
}
bool isXor = segment->isXor();
bool oppXor = oSegment->isXor();
do {
int windValue = start->windValue();
int oWindValue = oStart->windValue();
int oppValue = start->oppValue();
int oOppValue = oStart->oppValue();
// winding values are added or subtracted depending on direction and wind type
// same or opposite values are summed depending on the operand value
if (windValue >= oWindValue) {
if (operandSwap) {
SkTSwap(oWindValue, oOppValue);
}
if (flipped) {
windValue -= oWindValue;
oppValue -= oOppValue;
} else {
windValue += oWindValue;
oppValue += oOppValue;
}
if (isXor) {
windValue &= 1;
}
if (oppXor) {
oppValue &= 1;
}
oWindValue = oOppValue = 0;
} else {
if (operandSwap) {
SkTSwap(windValue, oppValue);
}
if (flipped) {
oWindValue -= windValue;
oOppValue -= oppValue;
} else {
oWindValue += windValue;
oOppValue += oppValue;
}
if (isXor) {
oOppValue &= 1;
}
if (oppXor) {
oWindValue &= 1;
}
windValue = oppValue = 0;
}
start->setWindValue(windValue);
start->setOppValue(oppValue);
oStart->setWindValue(oWindValue);
oStart->setOppValue(oOppValue);
if (!windValue && !oppValue) {
segment->markDone(start);
}
if (!oWindValue && !oOppValue) {
oSegment->markDone(oStart);
}
SkOpSpanBase* next = start->next();
SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
if (next == end) {
break;
}
start = next->upCast();
if (!oNext) {
return false;
}
if (!oNext->upCastable()) {
return false;
}
oStart = oNext->upCast();
} while (true);
} while ((coin = coin->fNext));
return true;
}
void SkOpCoincidence::detach(SkCoincidentSpans* remove) {
SkCoincidentSpans* coin = fHead;
SkCoincidentSpans* prev = NULL;
SkCoincidentSpans* next;
do {
next = coin->fNext;
if (coin == remove) {
if (prev) {
prev->fNext = next;
} else {
fHead = next;
}
break;
}
prev = coin;
} while ((coin = next));
SkASSERT(coin);
}
void SkOpCoincidence::expand() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
SkOpSegment* segment = coin->fCoinPtTStart->segment();
SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
SkOpSpan* prev = start->prev();
SkOpPtT* oppPtT;
if (prev && (oppPtT = prev->contains(oppSegment))) {
double midT = (prev->t() + start->t()) / 2;
if (segment->isClose(midT, oppSegment)) {
coin->fCoinPtTStart = prev->ptT();
coin->fOppPtTStart = oppPtT;
}
}
SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next();
if (next && (oppPtT = next->contains(oppSegment))) {
double midT = (end->t() + next->t()) / 2;
if (segment->isClose(midT, oppSegment)) {
coin->fCoinPtTEnd = next->ptT();
coin->fOppPtTEnd = oppPtT;
}
}
} while ((coin = coin->fNext));
}
void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
if (coin->fCoinPtTStart == deleted) {
if (coin->fCoinPtTEnd->span() == kept->span()) {
return this->detach(coin);
}
coin->fCoinPtTStart = kept;
}
if (coin->fCoinPtTEnd == deleted) {
if (coin->fCoinPtTStart->span() == kept->span()) {
return this->detach(coin);
}
coin->fCoinPtTEnd = kept;
}
if (coin->fOppPtTStart == deleted) {
if (coin->fOppPtTEnd->span() == kept->span()) {
return this->detach(coin);
}
coin->fOppPtTStart = kept;
}
if (coin->fOppPtTEnd == deleted) {
if (coin->fOppPtTStart->span() == kept->span()) {
return this->detach(coin);
}
coin->fOppPtTEnd = kept;
}
} while ((coin = coin->fNext));
}
void SkOpCoincidence::mark() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
SkOpSpanBase* oldEnd = end;
SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
SkOpSpanBase* oOldEnd = oEnd;
SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
if (flipped) {
SkTSwap(oStart, oEnd);
}
SkOpSpanBase* next = start;
SkOpSpanBase* oNext = oStart;
// check to see if coincident span could be bigger
do {
next = next->upCast()->next();
oNext = flipped ? oNext->prev() : oNext->upCast()->next();
if (next == end || oNext == oEnd) {
break;
}
if (!next->containsCoinEnd(oNext)) {
next->insertCoinEnd(oNext);
}
SkOpSpan* nextSpan = next->upCast();
SkOpSpan* oNextSpan = oNext->upCast();
if (!nextSpan->containsCoincidence(oNextSpan)) {
nextSpan->insertCoincidence(oNextSpan);
}
} while (true);
} while ((coin = coin->fNext));
}
bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
if (coin1s->segment() != coin2s->segment()) {
return false;
}
*overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
*overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
return *overS < *overE;
}

View File

@ -19,6 +19,8 @@ struct SkCoincidentSpans {
SkOpPtT* fOppPtTStart;
SkOpPtT* fOppPtTEnd;
bool fFlipped;
void dump() const;
};
class SkOpCoincidence {
@ -28,13 +30,27 @@ public:
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
void apply();
SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
bool addMissing(SkChunkAlloc* allocator);
bool apply();
bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, bool flipped);
void detach(SkCoincidentSpans* );
void dump() const;
void expand();
void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
void mark();
private:
bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
SkChunkAlloc* allocator);
bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
double* overS, double* overE) const;
SkCoincidentSpans* fHead;
};

View File

@ -4,42 +4,35 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkIntersections.h"
#include "SkOpContour.h"
#include "SkOpTAllocator.h"
#include "SkPathWriter.h"
#include "SkReduceOrder.h"
#include "SkTSort.h"
bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, bool swap) {
SkPoint pt0 = ts.pt(0).asSkPoint();
SkPoint pt1 = ts.pt(1).asSkPoint();
if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
// FIXME: one could imagine a case where it would be incorrect to ignore this
// suppose two self-intersecting cubics overlap to be coincident --
// this needs to check that by some measure the t values are far enough apart
// or needs to check to see if the self-intersection bit was set on the cubic segment
return false;
void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator) {
switch (verb) {
case SkPath::kLine_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
appendSegment(allocator).addLine(ptStorage, this);
} break;
case SkPath::kQuad_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
appendSegment(allocator).addQuad(ptStorage, this);
} break;
case SkPath::kCubic_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
appendSegment(allocator).addCubic(ptStorage, this);
} break;
default:
SkASSERT(0);
}
SkCoincidence& coincidence = fCoincidences.push_back();
coincidence.fOther = other;
coincidence.fSegments[0] = index;
coincidence.fSegments[1] = otherIndex;
coincidence.fTs[swap][0] = ts[0][0];
coincidence.fTs[swap][1] = ts[0][1];
coincidence.fTs[!swap][0] = ts[1][0];
coincidence.fTs[!swap][1] = ts[1][1];
coincidence.fPts[swap][0] = pt0;
coincidence.fPts[swap][1] = pt1;
bool nearStart = ts.nearlySame(0);
bool nearEnd = ts.nearlySame(1);
coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
coincidence.fNearly[0] = nearStart;
coincidence.fNearly[1] = nearEnd;
return true;
}
SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@ -47,627 +40,27 @@ SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
if (testSegment->done()) {
continue;
}
*start = *end = 0;
while (testSegment->nextCandidate(start, end)) {
if (!testSegment->isVertical(*start, *end)) {
SkOpSpanBase* span = testSegment->head();
SkOpSpanBase* testS, * testE;
while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
if (!testSegment->isVertical(testS, testE)) {
*start = testS;
*end = testE;
return testSegment;
}
span = span->upCast()->next();
}
}
return NULL;
}
// if one is very large the smaller may have collapsed to nothing
static void bump_out_close_span(double* startTPtr, double* endTPtr) {
double startT = *startTPtr;
double endT = *endTPtr;
if (approximately_negative(endT - startT)) {
if (endT <= 1 - FLT_EPSILON) {
*endTPtr += FLT_EPSILON;
SkASSERT(*endTPtr <= 1);
} else {
*startTPtr -= FLT_EPSILON;
SkASSERT(*startTPtr >= 0);
}
}
}
// first pass, add missing T values
// second pass, determine winding values of overlaps
void SkOpContour::addCoincidentPoints() {
int count = fCoincidences.count();
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fCoincidences[index];
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
// OPTIMIZATION: remove from array
continue;
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs("-");
other.debugShowTs("o");
#endif
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
bool startSwapped, oStartSwapped, cancelers;
if ((cancelers = startSwapped = startT > endT)) {
SkTSwap(startT, endT);
}
bump_out_close_span(&startT, &endT);
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if ((oStartSwapped = oStartT > oEndT)) {
SkTSwap(oStartT, oEndT);
cancelers ^= true;
}
bump_out_close_span(&oStartT, &oEndT);
SkASSERT(!approximately_negative(oEndT - oStartT));
const SkPoint& startPt = coincidence.fPts[0][startSwapped];
if (cancelers) {
// make sure startT and endT have t entries
if (startT > 0 || oEndT < 1
|| thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
thisOne.addTPair(startT, &other, oEndT, true, startPt,
coincidence.fPts[1][startSwapped]);
}
const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
if (oStartT > 0 || endT < 1
|| thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
coincidence.fPts[0][oStartSwapped]);
}
} else {
if (startT > 0 || oStartT > 0
|| thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
thisOne.addTPair(startT, &other, oStartT, true, startPt,
coincidence.fPts[1][startSwapped]);
}
const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
if (endT < 1 || oEndT < 1
|| thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
coincidence.fPts[0][!oStartSwapped]);
}
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs("+");
other.debugShowTs("o");
#endif
}
// if there are multiple pairs of coincidence that share an edge, see if the opposite
// are also coincident
for (int index = 0; index < count - 1; ++index) {
const SkCoincidence& coincidence = fCoincidences[index];
int thisIndex = coincidence.fSegments[0];
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
for (int idx2 = 1; idx2 < count; ++idx2) {
const SkCoincidence& innerCoin = fCoincidences[idx2];
int innerThisIndex = innerCoin.fSegments[0];
if (thisIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
}
if (this == otherContour && otherIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
}
SkOpContour* innerOtherContour = innerCoin.fOther;
innerThisIndex = innerCoin.fSegments[1];
if (this == innerOtherContour && thisIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
}
if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
}
}
}
}
bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, int ptIndex, bool swap) {
SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
// FIXME: one could imagine a case where it would be incorrect to ignore this
// suppose two self-intersecting cubics overlap to form a partial coincidence --
// although it isn't clear why the regular coincidence could wouldn't pick this up
// this is exceptional enough to ignore for now
return false;
}
SkCoincidence& coincidence = fPartialCoincidences.push_back();
coincidence.fOther = other;
coincidence.fSegments[0] = index;
coincidence.fSegments[1] = otherIndex;
coincidence.fTs[swap][0] = ts[0][ptIndex];
coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
coincidence.fTs[!swap][0] = ts[1][ptIndex];
coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
coincidence.fNearly[0] = 0;
coincidence.fNearly[1] = 0;
return true;
}
void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
SkCoincidence* coincidence) {
for (int idx2 = 0; idx2 < 2; ++idx2) {
if (coincidence->fPts[0][idx2] == aligned.fOldPt
&& coincidence->fTs[swap][idx2] == aligned.fOldT) {
SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
coincidence->fPts[0][idx2] = aligned.fPt;
SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
coincidence->fTs[swap][idx2] = aligned.fT;
}
}
}
void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
SkTArray<SkCoincidence, true>* coincidences) {
int count = coincidences->count();
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = (*coincidences)[index];
int thisIndex = coincidence.fSegments[0];
const SkOpSegment* thisOne = &fSegments[thisIndex];
const SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
const SkOpSegment* other = &otherContour->fSegments[otherIndex];
if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
align(aligned, false, &coincidence);
} else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
align(aligned, true, &coincidence);
}
}
}
void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
int zeroPt;
if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
alignPt(segmentIndex, point, zeroPt);
}
if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
other->alignPt(otherIndex, point, zeroPt);
}
}
void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
const SkOpSegment& segment = fSegments[index];
if (0 == zeroPt) {
*point = segment.pts()[0];
} else {
*point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
}
}
int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
double tVal = (*ts)[swap][tIndex];
if (tVal != 0 && precisely_zero(tVal)) {
ts->set(swap, tIndex, 0);
return 0;
}
if (tVal != 1 && precisely_equal(tVal, 1)) {
ts->set(swap, tIndex, 1);
return 1;
}
return -1;
}
bool SkOpContour::calcAngles() {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
if (!fSegments[test].calcAngles()) {
return false;
}
}
return true;
}
bool SkOpContour::calcCoincidentWinding() {
int count = fCoincidences.count();
#if DEBUG_CONCIDENT
if (count > 0) {
SkDebugf("%s count=%d\n", __FUNCTION__, count);
}
#endif
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fCoincidences[index];
if (!calcCommonCoincidentWinding(coincidence)) {
return false;
}
}
return true;
}
void SkOpContour::calcPartialCoincidentWinding() {
int count = fPartialCoincidences.count();
#if DEBUG_CONCIDENT
if (count > 0) {
SkDebugf("%s count=%d\n", __FUNCTION__, count);
}
#endif
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fPartialCoincidences[index];
calcCommonCoincidentWinding(coincidence);
}
// if there are multiple pairs of partial coincidence that share an edge, see if the opposite
// are also coincident
for (int index = 0; index < count - 1; ++index) {
const SkCoincidence& coincidence = fPartialCoincidences[index];
int thisIndex = coincidence.fSegments[0];
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
for (int idx2 = 1; idx2 < count; ++idx2) {
const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
int innerThisIndex = innerCoin.fSegments[0];
if (thisIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
}
if (this == otherContour && otherIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
}
SkOpContour* innerOtherContour = innerCoin.fOther;
innerThisIndex = innerCoin.fSegments[1];
if (this == innerOtherContour && thisIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
}
if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
}
}
}
}
void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
const SkCoincidence& twoCoin, int twoIdx, bool partial) {
SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
// look for common overlap
double min = SK_ScalarMax;
double max = SK_ScalarMin;
double min1 = oneCoin.fTs[!oneIdx][0];
double max1 = oneCoin.fTs[!oneIdx][1];
double min2 = twoCoin.fTs[!twoIdx][0];
double max2 = twoCoin.fTs[!twoIdx][1];
bool cancelers = (min1 < max1) != (min2 < max2);
if (min1 > max1) {
SkTSwap(min1, max1);
}
if (min2 > max2) {
SkTSwap(min2, max2);
}
if (between(min1, min2, max1)) {
min = min2;
}
if (between(min1, max2, max1)) {
max = max2;
}
if (between(min2, min1, max2)) {
min = SkTMin(min, min1);
}
if (between(min2, max1, max2)) {
max = SkTMax(max, max1);
}
if (min >= max) {
return; // no overlap
}
// look to see if opposite are different segments
int seg1Index = oneCoin.fSegments[oneIdx];
int seg2Index = twoCoin.fSegments[twoIdx];
if (seg1Index == seg2Index) {
return;
}
SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
// find opposite t value ranges corresponding to reference min/max range
const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
const int refSegIndex = oneCoin.fSegments[!oneIdx];
const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
int seg1Start = segment1->findOtherT(min, refSegment);
int seg1End = segment1->findOtherT(max, refSegment);
int seg2Start = segment2->findOtherT(min, refSegment);
int seg2End = segment2->findOtherT(max, refSegment);
// if the opposite pairs already contain min/max, we're done
if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
return;
}
double loEnd = SkTMin(min1, min2);
double hiEnd = SkTMax(max1, max2);
// insert the missing coincident point(s)
double missingT1 = -1;
double otherT1 = -1;
if (seg1Start < 0) {
if (seg2Start < 0) {
return;
}
missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
segment2, seg1End);
if (missingT1 < 0) {
return;
}
const SkOpSpan* missingSpan = &segment2->span(seg2Start);
otherT1 = missingSpan->fT;
} else if (seg2Start < 0) {
SkASSERT(seg1Start >= 0);
missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
segment1, seg2End);
if (missingT1 < 0) {
return;
}
const SkOpSpan* missingSpan = &segment1->span(seg1Start);
otherT1 = missingSpan->fT;
}
SkPoint missingPt1;
SkOpSegment* addTo1 = NULL;
SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
int minTIndex = refSegment->findExactT(min, addOther1);
SkASSERT(minTIndex >= 0);
if (missingT1 >= 0) {
missingPt1 = refSegment->span(minTIndex).fPt;
addTo1 = seg1Start < 0 ? segment1 : segment2;
}
double missingT2 = -1;
double otherT2 = -1;
if (seg1End < 0) {
if (seg2End < 0) {
return;
}
missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
segment2, seg1Start);
if (missingT2 < 0) {
return;
}
const SkOpSpan* missingSpan = &segment2->span(seg2End);
otherT2 = missingSpan->fT;
} else if (seg2End < 0) {
SkASSERT(seg1End >= 0);
missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
segment1, seg2Start);
if (missingT2 < 0) {
return;
}
const SkOpSpan* missingSpan = &segment1->span(seg1End);
otherT2 = missingSpan->fT;
}
SkPoint missingPt2;
SkOpSegment* addTo2 = NULL;
SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
int maxTIndex = refSegment->findExactT(max, addOther2);
SkASSERT(maxTIndex >= 0);
if (missingT2 >= 0) {
missingPt2 = refSegment->span(maxTIndex).fPt;
addTo2 = seg1End < 0 ? segment1 : segment2;
}
if (missingT1 >= 0) {
addTo1->pinT(missingPt1, &missingT1);
addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
} else {
SkASSERT(minTIndex >= 0);
missingPt1 = refSegment->span(minTIndex).fPt;
}
if (missingT2 >= 0) {
addTo2->pinT(missingPt2, &missingT2);
addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
} else {
SkASSERT(minTIndex >= 0);
missingPt2 = refSegment->span(maxTIndex).fPt;
}
if (!partial) {
return;
}
if (cancelers) {
if (missingT1 >= 0) {
if (addTo1->reversePoints(missingPt1, missingPt2)) {
SkTSwap(missingPt1, missingPt2);
}
addTo1->addTCancel(missingPt1, missingPt2, addOther1);
} else {
if (addTo2->reversePoints(missingPt1, missingPt2)) {
SkTSwap(missingPt1, missingPt2);
}
addTo2->addTCancel(missingPt1, missingPt2, addOther2);
}
} else if (missingT1 >= 0) {
SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
} else {
SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
}
}
void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
int count = coincidences.count();
#if DEBUG_CONCIDENT
if (count > 0) {
SkDebugf("%s count=%d\n", __FUNCTION__, count);
}
#endif
// look for a lineup where the partial implies another adjoining coincidence
for (int index = 0; index < count; ++index) {
const SkCoincidence& coincidence = coincidences[index];
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
if (thisOne.done()) {
continue;
}
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if (other.done()) {
continue;
}
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
if (startT == endT) { // this can happen in very large compares
continue;
}
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if (oStartT == oEndT) {
continue;
}
bool swapStart = startT > endT;
bool swapOther = oStartT > oEndT;
const SkPoint* startPt = &coincidence.fPts[0][0];
const SkPoint* endPt = &coincidence.fPts[0][1];
if (swapStart) {
SkTSwap(startT, endT);
SkTSwap(oStartT, oEndT);
SkTSwap(startPt, endPt);
}
bool cancel = swapOther != swapStart;
int step = swapStart ? -1 : 1;
int oStep = swapOther ? -1 : 1;
double oMatchStart = cancel ? oEndT : oStartT;
if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
bool added = false;
if (oMatchStart != 0) {
const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
}
if (!cancel && startT != 0 && !added) {
(void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
}
}
double oMatchEnd = cancel ? oStartT : oEndT;
if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
bool added = false;
if (cancel && endT != 1 && !added) {
(void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
}
}
}
}
bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
return true;
}
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
if (thisOne.done()) {
return true;
}
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if (other.done()) {
return true;
}
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
const SkPoint* startPt = &coincidence.fPts[0][0];
const SkPoint* endPt = &coincidence.fPts[0][1];
bool cancelers;
if ((cancelers = startT > endT)) {
SkTSwap<double>(startT, endT);
SkTSwap<const SkPoint*>(startPt, endPt);
}
bump_out_close_span(&startT, &endT);
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if (oStartT > oEndT) {
SkTSwap<double>(oStartT, oEndT);
cancelers ^= true;
}
bump_out_close_span(&oStartT, &oEndT);
SkASSERT(!approximately_negative(oEndT - oStartT));
bool success = true;
if (cancelers) {
thisOne.addTCancel(*startPt, *endPt, &other);
} else {
success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs("p");
other.debugShowTs("o");
#endif
return success;
}
void SkOpContour::resolveNearCoincidence() {
int count = fCoincidences.count();
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fCoincidences[index];
if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
continue;
}
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
SkOpContour* otherContour = coincidence.fOther;
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
// OPTIMIZATION: remove from coincidence array
continue;
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs("-");
other.debugShowTs("o");
#endif
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
bool cancelers;
if ((cancelers = startT > endT)) {
SkTSwap<double>(startT, endT);
}
if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
if (endT <= 1 - FLT_EPSILON) {
endT += FLT_EPSILON;
SkASSERT(endT <= 1);
} else {
startT -= FLT_EPSILON;
SkASSERT(startT >= 0);
}
}
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if (oStartT > oEndT) {
SkTSwap<double>(oStartT, oEndT);
cancelers ^= true;
}
SkASSERT(!approximately_negative(oEndT - oStartT));
if (cancelers) {
thisOne.blindCancel(coincidence, &other);
} else {
thisOne.blindCoincident(coincidence, &other);
}
}
}
void SkOpContour::sortAngles() {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
fSegments[test].sortAngles();
}
}
void SkOpContour::sortSegments() {
int segmentCount = fSegments.count();
fSortedSegments.push_back_n(segmentCount);
for (int test = 0; test < segmentCount; ++test) {
fSortedSegments[test] = &fSegments[test];
}
SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
fFirstSorted = 0;
}
void SkOpContour::toPath(SkPathWriter* path) const {
int segmentCount = fSegments.count();
const SkPoint& pt = fSegments.front().pts()[0];
const SkPoint& pt = fHead.pts()[0];
path->deferredMove(pt);
for (int test = 0; test < segmentCount; ++test) {
fSegments[test].addCurveTo(0, 1, path, true);
}
const SkOpSegment* segment = &fHead;
do {
segment->addCurveTo(segment->head(), segment->tail(), path, true);
} while ((segment = segment->next()));
path->close();
}
@ -706,57 +99,14 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
}
}
SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
SkOpSegment* testSegment = &fSegments[test];
if (testSegment->done()) {
SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
SkOpSegment* segment = &fHead;
do {
if (segment->done()) {
continue;
}
testSegment->undoneSpan(start, end);
return testSegment;
}
segment->undoneSpan(startPtr, endPtr);
return segment;
} while ((segment = segment->next()));
return NULL;
}
#if DEBUG_SHOW_WINDING
int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
int count = fSegments.count();
int sum = 0;
for (int index = 0; index < count; ++index) {
sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
}
// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
return sum;
}
void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
int ofInterest = 1 << 5 | 1 << 8;
int total = 0;
int index;
for (index = 0; index < contourList.count(); ++index) {
total += contourList[index]->segments().count();
}
int sum = 0;
for (index = 0; index < contourList.count(); ++index) {
sum += contourList[index]->debugShowWindingValues(total, ofInterest);
}
// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
}
#endif
void SkOpContour::setBounds() {
int count = fSegments.count();
if (count == 0) {
SkDebugf("%s empty contour\n", __FUNCTION__);
SkASSERT(0);
// FIXME: delete empty contour?
return;
}
fBounds = fSegments.front().bounds();
for (int index = 1; index < count; ++index) {
fBounds.add(fSegments[index].bounds());
}
}

View File

@ -8,31 +8,16 @@
#define SkOpContour_DEFINED
#include "SkOpSegment.h"
#include "SkTArray.h"
#include "SkTDArray.h"
#include "SkTSort.h"
#if defined(SK_DEBUG) || !FORCE_RELEASE
#include "SkThread.h"
#endif
class SkIntersections;
class SkOpContour;
class SkChunkAlloc;
class SkPathWriter;
struct SkCoincidence {
SkOpContour* fOther;
int fSegments[2];
double fTs[2][2];
SkPoint fPts[2][2];
int fNearly[2];
};
class SkOpContour {
public:
SkOpContour() {
reset();
#if defined(SK_DEBUG) || !FORCE_RELEASE
fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
#endif
}
bool operator<(const SkOpContour& rh) const {
@ -41,211 +26,255 @@ public:
: fBounds.fTop < rh.fBounds.fTop;
}
bool addCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, bool swap);
void addCoincidentPoints();
void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
appendSegment(allocator).addCubic(pts, this);
}
void addCross(const SkOpContour* crosser) {
#ifdef DEBUG_CROSS
for (int index = 0; index < fCrosses.count(); ++index) {
SkASSERT(fCrosses[index] != crosser);
void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
appendSegment(allocator).addLine(pts, this);
}
void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
appendSegment(allocator).addQuad(pts, this);
}
void align() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
segment->align();
} while ((segment = segment->next()));
}
SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
SkOpSegment* result = fCount++
? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
result->setPrev(fTail);
if (fTail) {
fTail->setNext(result);
}
#endif
fCrosses.push_back(crosser);
fTail = result;
return *result;
}
void addCubic(const SkPoint pts[4]) {
fSegments.push_back().addCubic(pts, fOperand, fXor);
fContainsCurves = fContainsCubics = true;
}
int addLine(const SkPoint pts[2]) {
fSegments.push_back().addLine(pts, fOperand, fXor);
return fSegments.count();
}
void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
}
bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, int ptIndex, bool swap);
int addQuad(const SkPoint pts[3]) {
fSegments.push_back().addQuad(pts, fOperand, fXor);
fContainsCurves = true;
return fSegments.count();
}
int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
setContainsIntercepts();
return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
}
int addSelfT(int segIndex, const SkPoint& pt, double newT) {
setContainsIntercepts();
return fSegments[segIndex].addSelfT(pt, newT);
}
void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
SkTArray<SkCoincidence, true>* coincidences);
void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
alignCoincidence(aligned, &fCoincidences);
alignCoincidence(aligned, &fPartialCoincidences);
}
void alignMultiples(SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment& segment = fSegments[sIndex];
if (segment.hasMultiples()) {
segment.alignMultiples(aligned);
}
SkOpContour* appendContour(SkChunkAlloc* allocator) {
SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
SkOpContour* prev = this;
SkOpContour* next;
while ((next = prev->next())) {
prev = next;
}
prev->setNext(contour);
return contour;
}
void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
const SkPathOpsBounds& bounds() const {
return fBounds;
}
bool calcAngles();
bool calcCoincidentWinding();
void calcPartialCoincidentWinding();
void checkDuplicates() {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment& segment = fSegments[sIndex];
if (segment.count() > 2) {
segment.checkDuplicates();
}
}
}
bool checkEnds() {
if (!fContainsCurves) {
return true;
}
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment* segment = &fSegments[sIndex];
if (segment->verb() == SkPath::kLine_Verb) {
continue;
}
if (segment->done()) {
continue; // likely coincident, nothing to do
}
if (!segment->checkEnds()) {
return false;
}
}
return true;
}
void checkMultiples() {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment& segment = fSegments[sIndex];
if (segment.count() > 2) {
segment.checkMultiples();
fMultiples |= segment.hasMultiples();
}
}
}
void checkSmall() {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment& segment = fSegments[sIndex];
// OPTIMIZATION : skip segments that are done?
if (segment.hasSmall()) {
segment.checkSmall();
}
}
}
// if same point has different T values, choose a common T
void checkTiny() {
int segmentCount = fSegments.count();
if (segmentCount <= 2) {
return;
}
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
SkOpSegment& segment = fSegments[sIndex];
if (segment.hasTiny()) {
segment.checkTiny();
}
}
void calcAngles(SkChunkAlloc* allocator) {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
segment->calcAngles(allocator);
} while ((segment = segment->next()));
}
void complete() {
setBounds();
fContainsIntercepts = false;
}
bool containsCubics() const {
return fContainsCubics;
int count() const {
return fCount;
}
bool crosses(const SkOpContour* crosser) const {
for (int index = 0; index < fCrosses.count(); ++index) {
if (fCrosses[index] == crosser) {
return true;
}
}
return false;
int debugID() const {
return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
int debugIndent() const {
return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
}
#if DEBUG_ACTIVE_SPANS
void debugShowActiveSpans() {
SkOpSegment* segment = &fHead;
do {
segment->debugShowActiveSpans();
} while ((segment = segment->next()));
}
#endif
const SkOpAngle* debugAngle(int id) const {
return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
}
SkOpContour* debugContour(int id) {
return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
}
const SkOpPtT* debugPtT(int id) const {
return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
}
const SkOpSegment* debugSegment(int id) const {
return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
}
const SkOpSpanBase* debugSpan(int id) const {
return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
}
SkOpGlobalState* globalState() const {
return fState;
}
void debugValidate() const {
#if DEBUG_VALIDATE
const SkOpSegment* segment = &fHead;
const SkOpSegment* prior = NULL;
do {
segment->debugValidate();
SkASSERT(segment->prev() == prior);
prior = segment;
} while ((segment = segment->next()));
SkASSERT(prior == fTail);
#endif
}
bool done() const {
return fDone;
}
void dump();
void dumpAll();
void dumpAngles() const;
void dumpPt(int ) const;
void dumpPts() const;
void dumpPtsX() const;
void dumpSegment(int ) const;
void dumpSegments(SkPathOp op) const;
void dumpSpan(int ) const;
void dumpSpans() const;
const SkPoint& end() const {
const SkOpSegment& segment = fSegments.back();
return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
}
void fixOtherTIndex() {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
fSegments[sIndex].fixOtherTIndex();
}
SkOpSegment* first() {
SkASSERT(fCount > 0);
return &fHead;
}
bool hasMultiples() const {
return fMultiples;
const SkOpSegment* first() const {
SkASSERT(fCount > 0);
return &fHead;
}
void joinCoincidence() {
joinCoincidence(fCoincidences, false);
joinCoincidence(fPartialCoincidences, true);
void indentDump() {
PATH_OPS_DEBUG_CODE(fIndent += 2);
}
SkOpSegment* nonVerticalSegment(int* start, int* end);
void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
fState = globalState;
fOperand = operand;
fXor = isXor;
}
bool isXor() const {
return fXor;
}
void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
if (fState->angleCoincidence()) {
segment->checkAngleCoin(coincidences, allocator);
} else {
segment->missingCoincidence(coincidences, allocator);
}
} while ((segment = segment->next()));
}
bool moveNearby() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
if (!segment->moveNearby()) {
return false;
}
} while ((segment = segment->next()));
return true;
}
SkOpContour* next() {
return fNext;
}
const SkOpContour* next() const {
return fNext;
}
SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
bool operand() const {
return fOperand;
}
bool oppXor() const {
return fOppXor;
}
void outdentDump() {
PATH_OPS_DEBUG_CODE(fIndent -= 2);
}
void remove(SkOpContour* contour) {
if (contour == this) {
SkASSERT(fCount == 0);
return;
}
SkASSERT(contour->fNext == NULL);
SkOpContour* prev = this;
SkOpContour* next;
while ((next = prev->next()) != contour) {
SkASSERT(next);
prev = next;
}
SkASSERT(prev);
prev->setNext(NULL);
}
void reset() {
fSegments.reset();
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
fTail = NULL;
fNext = NULL;
fCount = 0;
fDone = false;
SkDEBUGCODE(fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin));
SkDEBUGCODE(fFirstSorted = -1);
PATH_OPS_DEBUG_CODE(fIndent = 0);
}
void resolveNearCoincidence();
SkTArray<SkOpSegment>& segments() {
return fSegments;
void setBounds() {
SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
fBounds = segment->bounds();
while ((segment = segment->next())) {
fBounds.add(segment->bounds());
}
}
void setContainsIntercepts() {
fContainsIntercepts = true;
void setGlobalState(SkOpGlobalState* state) {
fState = state;
}
void setNext(SkOpContour* contour) {
SkASSERT(!fNext == !!contour);
fNext = contour;
}
void setOperand(bool isOp) {
@ -254,107 +283,68 @@ public:
void setOppXor(bool isOppXor) {
fOppXor = isOppXor;
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
fSegments[test].setOppXor(isOppXor);
}
}
void setXor(bool isXor) {
fXor = isXor;
}
void sortAngles();
void sortSegments();
SkPath::Verb simplifyCubic(SkPoint pts[4]);
const SkPoint& start() const {
return fSegments.front().pts()[0];
void sortAngles() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
segment->sortAngles();
} while ((segment = segment->next()));
}
void toPath(SkPathWriter* path) const;
void sortSegments() {
SkOpSegment* segment = &fHead;
do {
*fSortedSegments.append() = segment;
} while ((segment = segment->next()));
SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
fFirstSorted = 0;
}
const SkPoint& start() const {
return fHead.pts()[0];
}
void toPartialBackward(SkPathWriter* path) const {
int segmentCount = fSegments.count();
for (int test = segmentCount - 1; test >= 0; --test) {
fSegments[test].addCurveTo(1, 0, path, true);
}
const SkOpSegment* segment = fTail;
do {
segment->addCurveTo(segment->tail(), segment->head(), path, true);
} while ((segment = segment->prev()));
}
void toPartialForward(SkPathWriter* path) const {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
fSegments[test].addCurveTo(0, 1, path, true);
}
const SkOpSegment* segment = &fHead;
do {
segment->addCurveTo(segment->head(), segment->tail(), path, true);
} while ((segment = segment->next()));
}
void toPath(SkPathWriter* path) const;
void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
SkOpSegment* undoneSegment(int* start, int* end);
int updateSegment(int index, const SkPoint* pts) {
SkOpSegment& segment = fSegments[index];
segment.updatePts(pts);
return SkPathOpsVerbToPoints(segment.verb()) + 1;
}
#if DEBUG_TEST
SkTArray<SkOpSegment>& debugSegments() {
return fSegments;
}
#endif
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void debugShowActiveSpans() {
for (int index = 0; index < fSegments.count(); ++index) {
fSegments[index].debugShowActiveSpans();
}
}
#endif
#if DEBUG_SHOW_WINDING
int debugShowWindingValues(int totalSegments, int ofInterest);
static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
#endif
// available to test routines only
void dump() const;
void dumpAngles() const;
void dumpCoincidence(const SkCoincidence& ) const;
void dumpCoincidences() const;
void dumpPt(int ) const;
void dumpPts() const;
void dumpSpan(int ) const;
void dumpSpans() const;
SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
private:
void alignPt(int index, SkPoint* point, int zeroPt) const;
int alignT(bool swap, int tIndex, SkIntersections* ts) const;
bool calcCommonCoincidentWinding(const SkCoincidence& );
void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
const SkCoincidence& twoCoin, int twoIdx, bool partial);
void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
void setBounds();
SkTArray<SkOpSegment> fSegments;
SkTArray<SkOpSegment*, true> fSortedSegments;
int fFirstSorted;
SkTArray<SkCoincidence, true> fCoincidences;
SkTArray<SkCoincidence, true> fPartialCoincidences;
SkTArray<const SkOpContour*, true> fCrosses;
SkOpGlobalState* fState;
SkOpSegment fHead;
SkOpSegment* fTail;
SkOpContour* fNext;
SkTDArray<SkOpSegment*> fSortedSegments; // set by find top segment
SkPathOpsBounds fBounds;
bool fContainsIntercepts; // FIXME: is this used by anybody?
bool fContainsCubics;
bool fContainsCurves;
bool fDone;
bool fMultiples; // set if some segment has multiple identical intersections with other curves
int fCount;
int fFirstSorted;
bool fDone; // set by find top segment
bool fOperand; // true for the second argument to a binary operator
bool fXor;
bool fOppXor;
#if defined(SK_DEBUG) || !FORCE_RELEASE
int debugID() const { return fID; }
int fID;
#else
int debugID() const { return -1; }
#endif
bool fXor; // set if original path had even-odd fill
bool fOppXor; // set if opposite path had even-odd fill
PATH_OPS_DEBUG_CODE(int fID);
PATH_OPS_DEBUG_CODE(int fIndent);
};
#endif

View File

@ -9,7 +9,7 @@
#include "SkReduceOrder.h"
void SkOpEdgeBuilder::init() {
fCurrentContour = NULL;
fCurrentContour = fContoursHead;
fOperand = false;
fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
@ -19,32 +19,43 @@ void SkOpEdgeBuilder::init() {
void SkOpEdgeBuilder::addOperand(const SkPath& path) {
SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
fPathVerbs.pop_back();
fPathVerbs.pop();
fPath = &path;
fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
preFetch();
}
bool SkOpEdgeBuilder::finish() {
if (fUnparseable || !walk()) {
int SkOpEdgeBuilder::count() const {
SkOpContour* contour = fContoursHead;
int count = 0;
while (contour) {
count += contour->count() > 0;
contour = contour->next();
}
return count;
}
bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
fOperand = false;
if (fUnparseable || !walk(allocator)) {
return false;
}
complete();
if (fCurrentContour && !fCurrentContour->segments().count()) {
fContours.pop_back();
if (fCurrentContour && !fCurrentContour->count()) {
fContoursHead->remove(fCurrentContour);
}
return true;
}
void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
fPathVerbs.push_back(SkPath::kLine_Verb);
fPathPts.push_back_n(1, &curveStart);
*fPathVerbs.append() = SkPath::kLine_Verb;
*fPathPts.append() = curveStart;
} else {
fPathPts[fPathPts.count() - 1] = curveStart;
}
fPathVerbs.push_back(SkPath::kClose_Verb);
*fPathVerbs.append() = SkPath::kClose_Verb;
}
// very tiny points cause numerical instability : don't allow them
@ -57,7 +68,6 @@ static void force_small_to_zero(SkPoint* pt) {
}
}
int SkOpEdgeBuilder::preFetch() {
if (!fPath->isFinite()) {
fUnparseable = true;
@ -78,18 +88,18 @@ int SkOpEdgeBuilder::preFetch() {
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
fPathVerbs.push_back(verb);
*fPathVerbs.append() = verb;
force_small_to_zero(&pts[0]);
fPathPts.push_back(pts[0]);
*fPathPts.append() = pts[0];
curveStart = curve[0] = pts[0];
lastCurve = false;
continue;
case SkPath::kLine_Verb:
force_small_to_zero(&pts[1]);
if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
uint8_t lastVerb = fPathVerbs.back();
uint8_t lastVerb = fPathVerbs.top();
if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
fPathPts.back() = pts[1];
fPathPts.top() = pts[1];
}
continue; // skip degenerate points
}
@ -109,9 +119,9 @@ int SkOpEdgeBuilder::preFetch() {
quadderTol);
const int nQuads = quadder.countQuads();
for (int i = 0; i < nQuads; ++i) {
fPathVerbs.push_back(SkPath::kQuad_Verb);
*fPathVerbs.append() = SkPath::kQuad_Verb;
}
fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
fPathPts.append(nQuads * 2, &quadPts[1]);
curve[0] = pts[2];
lastCurve = true;
}
@ -135,16 +145,16 @@ int SkOpEdgeBuilder::preFetch() {
case SkPath::kDone_Verb:
continue;
}
fPathVerbs.push_back(verb);
*fPathVerbs.append() = verb;
int ptCount = SkPathOpsVerbToPoints(verb);
fPathPts.push_back_n(ptCount, &pts[1]);
fPathPts.append(ptCount, &pts[1]);
curve[0] = pts[ptCount];
lastCurve = true;
} while (verb != SkPath::kDone_Verb);
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
fPathVerbs.push_back(SkPath::kDone_Verb);
*fPathVerbs.append() = SkPath::kDone_Verb;
return fPathVerbs.count() - 1;
}
@ -153,10 +163,10 @@ bool SkOpEdgeBuilder::close() {
return true;
}
bool SkOpEdgeBuilder::walk() {
bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
uint8_t* verbPtr = fPathVerbs.begin();
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
const SkPoint* pointsPtr = fPathPts.begin() - 1;
SkPoint* pointsPtr = fPathPts.begin() - 1;
SkPath::Verb verb;
while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
if (verbPtr == endOfFirstHalf) {
@ -165,7 +175,7 @@ bool SkOpEdgeBuilder::walk() {
verbPtr++;
switch (verb) {
case SkPath::kMove_Verb:
if (fCurrentContour) {
if (fCurrentContour && fCurrentContour->count()) {
if (fAllowOpenContours) {
complete();
} else if (!close()) {
@ -173,21 +183,44 @@ bool SkOpEdgeBuilder::walk() {
}
}
if (!fCurrentContour) {
fCurrentContour = fContours.push_back_n(1);
fCurrentContour->setOperand(fOperand);
fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
fCurrentContour = fContoursHead->appendContour(allocator);
}
fCurrentContour->init(fGlobalState, fOperand,
fXorMask[fOperand] == kEvenOdd_PathOpsMask);
pointsPtr += 1;
continue;
case SkPath::kLine_Verb:
fCurrentContour->addLine(pointsPtr);
fCurrentContour->addLine(pointsPtr, fAllocator);
break;
case SkPath::kQuad_Verb:
fCurrentContour->addQuad(pointsPtr);
break;
case SkPath::kCubic_Verb:
fCurrentContour->addCubic(pointsPtr);
fCurrentContour->addQuad(pointsPtr, fAllocator);
break;
case SkPath::kCubic_Verb: {
// split self-intersecting cubics in two before proceeding
// if the cubic is convex, it doesn't self intersect.
SkScalar loopT;
if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) {
SkPoint cubicPair[7];
SkChopCubicAt(pointsPtr, cubicPair, loopT);
SkPoint cStorage[2][4];
SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]);
SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]);
if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) {
SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0];
SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1];
for (size_t index = 0; index < SK_ARRAY_COUNT(curve1); ++index) {
force_small_to_zero(&curve1[index]);
force_small_to_zero(&curve2[index]);
}
fCurrentContour->addCurve(v1, curve1, fAllocator);
fCurrentContour->addCurve(v2, curve2, fAllocator);
} else {
fCurrentContour->addCubic(pointsPtr, fAllocator);
}
} else {
fCurrentContour->addCubic(pointsPtr, fAllocator);
}
} break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentContour);
if (!close()) {
@ -198,10 +231,11 @@ bool SkOpEdgeBuilder::walk() {
SkDEBUGFAIL("bad verb");
return false;
}
pointsPtr += SkPathOpsVerbToPoints(verb);
SkASSERT(fCurrentContour);
fCurrentContour->debugValidate();
pointsPtr += SkPathOpsVerbToPoints(verb);
}
if (fCurrentContour && !fAllowOpenContours && !close()) {
if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
return false;
}
return true;

View File

@ -9,20 +9,25 @@
#include "SkOpContour.h"
#include "SkPathWriter.h"
#include "SkTArray.h"
class SkOpEdgeBuilder {
public:
SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
: fPath(path.nativePath())
, fContours(contours)
SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
SkOpGlobalState* globalState)
: fAllocator(allocator) // FIXME: replace with const, tune this
, fGlobalState(globalState)
, fPath(path.nativePath())
, fContoursHead(contours2)
, fAllowOpenContours(true) {
init();
}
SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
: fPath(&path)
, fContours(contours)
SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
SkOpGlobalState* globalState)
: fAllocator(allocator)
, fGlobalState(globalState)
, fPath(&path)
, fContoursHead(contours2)
, fAllowOpenContours(false) {
init();
}
@ -30,13 +35,19 @@ public:
void addOperand(const SkPath& path);
void complete() {
if (fCurrentContour && fCurrentContour->segments().count()) {
if (fCurrentContour && fCurrentContour->count()) {
fCurrentContour->complete();
fCurrentContour = NULL;
}
}
bool finish();
int count() const;
bool finish(SkChunkAlloc* );
const SkOpContour* head() const {
return fContoursHead;
}
void init();
bool unparseable() const { return fUnparseable; }
SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@ -45,13 +56,15 @@ private:
void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
bool close();
int preFetch();
bool walk();
bool walk(SkChunkAlloc* );
SkChunkAlloc* fAllocator;
SkOpGlobalState* fGlobalState;
const SkPath* fPath;
SkTArray<SkPoint, true> fPathPts;
SkTArray<uint8_t, true> fPathVerbs;
SkTDArray<SkPoint> fPathPts;
SkTDArray<uint8_t> fPathVerbs;
SkOpContour* fCurrentContour;
SkTArray<SkOpContour>& fContours;
SkOpContour* fContoursHead;
SkPathOpsMask fXorMask[2];
int fSecondHalf;
bool fOperand;

File diff suppressed because it is too large Load Diff

View File

@ -9,160 +9,262 @@
#include "SkOpAngle.h"
#include "SkOpSpan.h"
#include "SkOpTAllocator.h"
#include "SkPathOpsBounds.h"
#include "SkPathOpsCurve.h"
#include "SkTArray.h"
#include "SkTDArray.h"
#if defined(SK_DEBUG) || !FORCE_RELEASE
#include "SkThread.h"
#endif
struct SkCoincidence;
class SkOpCoincidence;
class SkOpContour;
class SkPathWriter;
class SkOpSegment {
public:
SkOpSegment() {
#if defined(SK_DEBUG) || !FORCE_RELEASE
fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
#endif
}
enum AllowAlias {
kAllowAlias,
kNoAlias
};
bool operator<(const SkOpSegment& rh) const {
return fBounds.fTop < rh.fBounds.fTop;
}
struct AlignedSpan {
double fOldT;
double fT;
SkPoint fOldPt;
SkPoint fPt;
const SkOpSegment* fSegment;
const SkOpSegment* fOther1;
const SkOpSegment* fOther2;
};
SkOpAngle* activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
bool* done, bool* sortable);
SkOpAngle* activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr, bool* done, bool* sortable);
SkOpAngle* activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr, bool* done, bool* sortable);
bool activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
SkPathOp op);
bool activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end, SkPathOp op,
int* sumMiWinding, int* sumSuWinding);
SkPoint activeLeftTop(SkOpSpanBase** firstT);
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
void addCubic(SkPoint pts[4], SkOpContour* parent) {
init(pts, parent, SkPath::kCubic_Verb);
fBounds.setCubicBounds(pts);
}
void addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path,
bool active) const;
SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
angle->set(&fTail, fTail.prev());
fTail.setFromAngle(angle);
return angle;
}
void addLine(SkPoint pts[2], SkOpContour* parent) {
init(pts, parent, SkPath::kLine_Verb);
fBounds.set(pts, 2);
}
SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
SkOpAngle* addSingletonAngles(int step, SkChunkAlloc* );
SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
angle->set(&fHead, fHead.next());
fHead.setToAngle(angle);
return angle;
}
void addQuad(SkPoint pts[3], SkOpContour* parent) {
init(pts, parent, SkPath::kQuad_Verb);
fBounds.setQuadBounds(pts);
}
SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
void align();
static bool BetweenTs(const SkOpSpanBase* lesser, double testT, const SkOpSpanBase* greater);
const SkPathOpsBounds& bounds() const {
return fBounds;
}
// OPTIMIZE
// when the edges are initially walked, they don't automatically get the prior and next
// edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
// and would additionally remove the need for similar checks in condition edges. It would
// also allow intersection code to assume end of segment intersections (maybe?)
bool complete() const {
int count = fTs.count();
return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
void bumpCount() {
++fCount;
}
void calcAngles(SkChunkAlloc*);
void checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
void checkNearCoincidence(SkOpAngle* );
bool clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const;
static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
SkOpContour* contour() const {
return fContour;
}
int count() const {
return fTs.count();
return fCount;
}
SkOpSpan* crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical);
void debugAddAngle(double startT, double endT, SkChunkAlloc*);
const SkOpAngle* debugAngle(int id) const;
SkOpContour* debugContour(int id);
int debugID() const {
return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
#if DEBUG_SWAP_TOP
int debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
#endif
SkOpAngle* debugLastAngle();
const SkOpPtT* debugPtT(int id) const;
void debugReset();
const SkOpSegment* debugSegment(int id) const;
#if DEBUG_ACTIVE_SPANS
void debugShowActiveSpans() const;
#endif
#if DEBUG_MARK_DONE
void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding);
void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, int oppWinding);
#endif
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
void detach(const SkOpSpan* );
double distSq(double t, SkOpAngle* opp);
bool done() const {
SkASSERT(fDoneSpans <= fTs.count());
return fDoneSpans == fTs.count();
}
bool done(int min) const {
return fTs[min].fDone;
SkASSERT(fDoneCount <= fCount);
return fDoneCount == fCount;
}
bool done(const SkOpAngle* angle) const {
return done(SkMin32(angle->start(), angle->end()));
return angle->start()->starter(angle->end())->done();
}
SkDPoint dPtAtT(double mid) const {
return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
SkVector dxdy(int index) const {
return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
SkDVector dSlopeAtT(double mid) const {
return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
SkScalar dy(int index) const {
return dxdy(index).fY;
void dump() const;
void dumpAll() const;
void dumpAngles() const;
void dumpCoin() const;
void dumpPts() const;
SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
int xorMiMask, int xorSuMask);
SkOpSegment* findNextWinding(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
SkOpSpanBase** nextEnd, bool* unsortable);
SkOpSegment* findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable);
SkOpSegment* findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
bool* unsortable, SkChunkAlloc* );
SkOpGlobalState* globalState() const;
const SkOpSpan* head() const {
return &fHead;
}
bool hasMultiples() const {
return fMultiples;
SkOpSpan* head() {
return &fHead;
}
bool hasSmall() const {
return fSmall;
void init(SkPoint pts[], SkOpContour* parent, SkPath::Verb verb);
void initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
SkOpAngle::IncludeType angleIncludeType);
bool initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit, int winding,
SkScalar hitDx, int oppWind, SkScalar hitOppDx);
SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
SkOpSpanBase* next = prev->next();
result->setPrev(prev);
prev->setNext(result);
SkDEBUGCODE(result->ptT()->fT = 0);
result->setNext(next);
if (next) {
next->setPrev(result);
}
return result;
}
bool hasTiny() const {
return fTiny;
}
bool intersected() const {
return fTs.count() > 0;
}
bool isCanceled(int tIndex) const {
return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
}
bool isConnected(int startIndex, int endIndex) const {
return fTs[startIndex].fWindSum != SK_MinS32 || fTs[endIndex].fWindSum != SK_MinS32;
}
bool isClose(double t, const SkOpSegment* opp) const;
bool isHorizontal() const {
return fBounds.fTop == fBounds.fBottom;
}
SkOpSegment* isSimple(SkOpSpanBase** end, int* step) {
return nextChase(end, step, NULL, NULL);
}
bool isVertical() const {
return fBounds.fLeft == fBounds.fRight;
}
bool isVertical(int start, int end) const {
return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start->t(), end->t());
}
bool operand() const {
return fOperand;
bool isXor() const;
const SkPoint& lastPt() const {
return fPts[SkPathOpsVerbToPoints(fVerb)];
}
int oppSign(const SkOpAngle* angle) const {
SkASSERT(angle->segment() == this);
return oppSign(angle->start(), angle->end());
SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
SkOpSpanBase** lastPtr);
bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
int oppWinding, SkOpSpanBase** lastPtr);
SkOpSpanBase* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
SkOpSpanBase* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
const SkOpAngle* angle);
void markDone(SkOpSpan* );
bool markWinding(SkOpSpan* , int winding);
bool markWinding(SkOpSpan* , int winding, int oppWinding);
bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
bool monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
bool moveNearby();
SkOpSegment* next() const {
return fNext;
}
int oppSign(int startIndex, int endIndex) const {
int result = startIndex < endIndex ? -fTs[startIndex].fOppValue : fTs[endIndex].fOppValue;
#if DEBUG_WIND_BUMP
SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
#endif
static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
bool operand() const;
static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
int result = start->t() < end->t() ? -start->upCast()->oppValue()
: end->upCast()->oppValue();
return result;
}
int oppSum(int tIndex) const {
return fTs[tIndex].fOppSum;
}
bool oppXor() const;
int oppSum(const SkOpAngle* angle) const {
int lesser = SkMin32(angle->start(), angle->end());
return fTs[lesser].fOppSum;
const SkOpSegment* prev() const {
return fPrev;
}
int oppValue(int tIndex) const {
return fTs[tIndex].fOppValue;
}
int oppValue(const SkOpAngle* angle) const {
int lesser = SkMin32(angle->start(), angle->end());
return fTs[lesser].fOppValue;
}
#if DEBUG_VALIDATE
bool oppXor() const {
return fOppXor;
}
#endif
SkPoint ptAtT(double mid) const {
return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
@ -171,399 +273,113 @@ public:
return fPts;
}
void reset() {
init(NULL, (SkPath::Verb) -1, false, false);
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
fTs.reset();
bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
}
bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
void setOppXor(bool isOppXor) {
fOppXor = isOppXor;
bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
return ptsDisjoint(span.fT, span.fPt, t, pt);
}
void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
int deltaSum = spanSign(index, endIndex);
bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
void resetVisited() {
fVisited = false;
}
void setContour(SkOpContour* contour) {
fContour = contour;
}
void setNext(SkOpSegment* next) {
fNext = next;
}
void setPrev(SkOpSegment* prev) {
fPrev = prev;
}
bool setVisited() {
if (fVisited) {
return false;
}
return (fVisited = true);
}
void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
int deltaSum = SpanSign(start, end);
*maxWinding = *sumWinding;
*sumWinding -= deltaSum;
}
const SkOpSpan& span(int tIndex) const {
return fTs[tIndex];
}
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
int* maxWinding, int* sumWinding);
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
void sortAngles();
const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
SkASSERT(tStart != tEnd);
const SkOpSpan& span = fTs[tStart];
return tStart < tEnd ? span.fToAngle : span.fFromAngle;
}
// FIXME: create some sort of macro or template that avoids casting
SkOpAngle* spanToAngle(int tStart, int tEnd) {
const SkOpAngle* cAngle = (const_cast<const SkOpSegment*>(this))->spanToAngle(tStart, tEnd);
return const_cast<SkOpAngle*>(cAngle);
}
int spanSign(const SkOpAngle* angle) const {
SkASSERT(angle->segment() == this);
return spanSign(angle->start(), angle->end());
}
int spanSign(int startIndex, int endIndex) const {
int result = startIndex < endIndex ? -fTs[startIndex].fWindValue : fTs[endIndex].fWindValue;
#if DEBUG_WIND_BUMP
SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
#endif
static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
int result = start->t() < end->t() ? -start->upCast()->windValue()
: end->upCast()->windValue();
return result;
}
double t(int tIndex) const {
return fTs[tIndex].fT;
SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
SkASSERT(start != end);
return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
}
double tAtMid(int start, int end, double mid) const {
return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPoint edge[4]) const;
bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkDCubic* result) const;
void subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathOpsBounds* bounds) const;
const SkOpSpanBase* tail() const {
return &fTail;
}
void updatePts(const SkPoint pts[]) {
fPts = pts;
SkOpSpanBase* tail() {
return &fTail;
}
static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
return start->t() * (1 - mid) + end->t() * mid;
}
void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateOppWinding(const SkOpAngle* angle) const;
int updateOppWindingReverse(const SkOpAngle* angle) const;
int updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateWinding(const SkOpAngle* angle) const;
int updateWindingReverse(const SkOpAngle* angle) const;
static bool UseInnerWinding(int outerWinding, int innerWinding);
SkPath::Verb verb() const {
return fVerb;
}
int windSum(int tIndex) const {
return fTs[tIndex].fWindSum;
}
int windValue(int tIndex) const {
return fTs[tIndex].fWindValue;
}
#if defined(SK_DEBUG) || DEBUG_WINDING
SkScalar xAtT(int index) const {
return xAtT(&fTs[index]);
}
#endif
#if DEBUG_VALIDATE
bool _xor() const { // FIXME: used only by SkOpAngle::debugValidateLoop()
return fXor;
}
#endif
const SkPoint& xyAtT(const SkOpSpan* span) const {
return span->fPt;
}
const SkPoint& xyAtT(int index) const {
return xyAtT(&fTs[index]);
}
#if defined(SK_DEBUG) || DEBUG_WINDING
SkScalar yAtT(int index) const {
return yAtT(&fTs[index]);
}
#endif
const SkOpAngle* activeAngle(int index, int* start, int* end, bool* done,
bool* sortable) const;
SkPoint activeLeftTop(int* firstT) const;
bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op);
bool activeWinding(int index, int endIndex);
void addCubic(const SkPoint pts[4], bool operand, bool evenOdd);
void addCurveTo(int start, int end, SkPathWriter* path, bool active) const;
void addEndSpan(int endIndex);
void addLine(const SkPoint pts[2], bool operand, bool evenOdd);
void addOtherT(int index, double otherT, int otherIndex);
void addQuad(const SkPoint pts[3], bool operand, bool evenOdd);
void addSimpleAngle(int endIndex);
int addSelfT(const SkPoint& pt, double newT);
void addStartSpan(int endIndex);
int addT(SkOpSegment* other, const SkPoint& pt, double newT);
void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
bool addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
SkOpSegment* other);
const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
const SkPoint& pt);
const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
const SkPoint& pt, const SkPoint& oPt);
void alignMultiples(SkTDArray<AlignedSpan>* aligned);
bool alignSpan(int index, double thisT, const SkPoint& thisPt);
void alignSpanState(int start, int end);
bool betweenTs(int lesser, double testT, int greater) const;
void blindCancel(const SkCoincidence& coincidence, SkOpSegment* other);
void blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other);
bool calcAngles();
double calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
double hiEnd, const SkOpSegment* other, int thisEnd);
double calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
double hiEnd, const SkOpSegment* other, int thisEnd);
void checkDuplicates();
bool checkEnds();
void checkMultiples();
void checkSmall();
bool checkSmall(int index) const;
void checkTiny();
int computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType);
bool containsPt(const SkPoint& , int index, int endIndex) const;
int crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, bool* hitSomething,
double mid, bool opp, bool current) const;
bool findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, int oEnd,
int step, SkPoint* startPt, SkPoint* endPt, double* endT) const;
SkOpSegment* findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask);
SkOpSegment* findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
bool* unsortable);
SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable);
int findExactT(double t, const SkOpSegment* ) const;
int findOtherT(double t, const SkOpSegment* ) const;
int findT(double t, const SkPoint& , const SkOpSegment* ) const;
SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool firstPass);
void fixOtherTIndex();
bool inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
const SkOpAngle* angle) const;
void initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType);
bool initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
SkScalar hitOppDx);
bool isMissing(double startT, const SkPoint& pt) const;
bool isTiny(const SkOpAngle* angle) const;
bool joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt, int step,
bool cancel);
SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
bool markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
SkOpSpan** lastPtr);
SkOpSpan* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
const SkOpAngle* angle);
void markDone(int index, int winding);
void markDoneBinary(int index);
void markDoneFinal(int index);
void markDoneUnary(int index);
bool nextCandidate(int* start, int* end) const;
int nextSpan(int from, int step) const;
void pinT(const SkPoint& pt, double* t);
void setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
void sortAngles();
bool subDivide(int start, int end, SkPoint edge[4]) const;
bool subDivide(int start, int end, SkDCubic* result) const;
void undoneSpan(int* start, int* end);
int updateOppWindingReverse(const SkOpAngle* angle) const;
int updateWindingReverse(const SkOpAngle* angle) const;
static bool UseInnerWinding(int outerWinding, int innerWinding);
static bool UseInnerWindingReverse(int outerWinding, int innerWinding);
int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const;
int windingAtT(double tHit, const SkOpSpan* span, bool crossOpp, SkScalar* dx) const;
int windSum(const SkOpAngle* angle) const;
// available for testing only
#if defined(SK_DEBUG) || !FORCE_RELEASE
int debugID() const {
return fID;
SkPoint* writablePt(bool end) {
return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
}
#else
int debugID() const {
return -1;
}
#endif
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void debugShowActiveSpans() const;
#endif
#if DEBUG_CONCIDENT
void debugShowTs(const char* prefix) const;
#endif
#if DEBUG_SHOW_WINDING
int debugShowWindingValues(int slotCount, int ofInterest) const;
#endif
const SkTDArray<SkOpSpan>& debugSpans() const;
void debugValidate() const;
// available to testing only
const SkOpAngle* debugLastAngle() const;
void dumpAngles() const;
void dumpContour(int firstID, int lastID) const;
void dumpPts() const;
void dumpSpans() const;
private:
struct MissingSpan {
double fT;
double fEndT;
SkOpSegment* fSegment;
SkOpSegment* fOther;
double fOtherT;
SkPoint fPt;
};
const SkOpAngle* activeAngleInner(int index, int* start, int* end, bool* done,
bool* sortable) const;
const SkOpAngle* activeAngleOther(int index, int* start, int* end, bool* done,
bool* sortable) const;
bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
int* sumMiWinding, int* sumSuWinding);
bool activeWinding(int index, int endIndex, int* sumWinding);
void addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
void addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** );
SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** );
SkOpAngle* addSingletonAngles(int step);
void alignRange(int lower, int upper, const SkOpSegment* other, int oLower, int oUpper);
void alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other, double otherT,
const SkOpSegment* other2, SkOpSpan* oSpan, SkTDArray<AlignedSpan>* );
bool betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const;
void bumpCoincidentBlind(bool binary, int index, int last);
bool bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* index,
SkTArray<SkPoint, true>* outsideTs);
void bumpCoincidentOBlind(int index, int last);
bool bumpCoincidentOther(const SkOpSpan& oTest, int* index,
SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt);
bool bumpSpan(SkOpSpan* span, int windDelta, int oppDelta);
bool calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts);
bool checkForSmall(const SkOpSpan* span, const SkPoint& pt, double newT,
int* less, int* more) const;
void checkLinks(const SkOpSpan* ,
SkTArray<MissingSpan, true>* missingSpans) const;
static void CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
const SkOpSpan* oFirst, const SkOpSpan* oLast,
const SkOpSpan** missingPtr,
SkTArray<MissingSpan, true>* missingSpans);
int checkSetAngle(int tIndex) const;
void checkSmallCoincidence(const SkOpSpan& span, SkTArray<MissingSpan, true>* );
bool coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const;
bool clockwise(int tStart, int tEnd, bool* swap) const;
static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
bool containsT(double t, const SkOpSegment* other, double otherT) const;
bool decrementSpan(SkOpSpan* span);
int findEndSpan(int endIndex) const;
int findStartSpan(int startIndex) const;
int firstActive(int tIndex) const;
const SkOpSpan& firstSpan(const SkOpSpan& thisSpan) const;
void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd);
bool inCoincidentSpan(double t, const SkOpSegment* other) const;
bool inconsistentWinding(const SkOpAngle* , int maxWinding, int oppMaxWinding) const;
bool inconsistentWinding(int min, int maxWinding, int oppMaxWinding) const;
bool inconsistentWinding(const char* funName, int tIndex, int winding, int oppWinding) const;
bool inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const;
#if OLD_CHASE
bool isSimple(int end) const;
#else
SkOpSegment* isSimple(int* end, int* step);
#endif
bool isTiny(int index) const;
const SkOpSpan& lastSpan(const SkOpSpan& thisSpan) const;
void matchWindingValue(int tIndex, double t, bool borrowWind);
SkOpSpan* markAndChaseDone(int index, int endIndex, int winding);
SkOpSpan* markAndChaseDoneBinary(const SkOpAngle* angle, int winding, int oppWinding);
bool markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr);
bool markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr);
bool markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
SkOpSpan** lastPtr);
SkOpSpan* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
void markDoneBinary(int index, int winding, int oppWinding);
SkOpSpan* markAndChaseDoneUnary(const SkOpAngle* angle, int winding);
void markOneDone(const char* funName, int tIndex, int winding);
void markOneDoneBinary(const char* funName, int tIndex);
void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding);
void markOneDoneFinal(const char* funName, int tIndex);
void markOneDoneUnary(const char* funName, int tIndex);
bool markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr);
bool markOneWinding(const char* funName, int tIndex, int winding, int oppWinding,
SkOpSpan** lastPtr);
bool markWinding(int index, int winding);
bool markWinding(int index, int winding, int oppWinding);
bool monotonicInY(int tStart, int tEnd) const;
bool multipleEnds() const { return fTs[count() - 2].fT == 1; }
bool multipleStarts() const { return fTs[1].fT == 0; }
SkOpSegment* nextChase(int* index, int* step, int* min, SkOpSpan** last) const;
int nextExactSpan(int from, int step) const;
void resetSpanFlags();
bool serpentine(int tStart, int tEnd) const;
void setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
void setFromAngle(int endIndex, SkOpAngle* );
void setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span);
void setToAngle(int endIndex, SkOpAngle* );
void setUpWindings(int index, int endIndex, int* sumMiWinding,
int* maxWinding, int* sumWinding);
void subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const;
static void TrackOutsidePair(SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt,
const SkPoint& startPt);
static void TrackOutside(SkTArray<SkPoint, true>* outsideTs, const SkPoint& startPt);
int updateOppWinding(int index, int endIndex) const;
int updateOppWinding(const SkOpAngle* angle) const;
int updateWinding(int index, int endIndex) const;
int updateWinding(const SkOpAngle* angle) const;
int updateWindingReverse(int index, int endIndex) const;
SkOpSpan* verifyOneWinding(const char* funName, int tIndex);
SkOpSpan* verifyOneWindingU(const char* funName, int tIndex);
SkScalar xAtT(const SkOpSpan* span) const {
return xyAtT(span).fX;
}
SkScalar yAtT(const SkOpSpan* span) const {
return xyAtT(span).fY;
}
void zeroSpan(SkOpSpan* span);
#if DEBUG_SWAP_TOP
bool controlsContainedByEnds(int tStart, int tEnd) const;
#endif
void debugAddAngle(int start, int end);
#if DEBUG_CONCIDENT
void debugAddTPair(double t, const SkOpSegment& other, double otherT) const;
#endif
#if DEBUG_ANGLE
void debugCheckPointsEqualish(int tStart, int tEnd) const;
#endif
#if DEBUG_SWAP_TOP
int debugInflections(int index, int endIndex) const;
#endif
#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);
#endif
#if DEBUG_WINDING
static char as_digit(int value) {
return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
}
#endif
// available to testing only
void debugConstruct();
void debugConstructCubic(SkPoint shortQuad[4]);
void debugConstructLine(SkPoint shortQuad[2]);
void debugConstructQuad(SkPoint shortQuad[3]);
void debugReset();
void dumpDPts() const;
void dumpHexPts() const;
void dumpSpan(int index) const;
const SkPoint* fPts;
SkPathOpsBounds fBounds;
// FIXME: can't convert to SkTArray because it uses insert
SkTDArray<SkOpSpan> fTs; // 2+ (always includes t=0 t=1) -- at least (number of spans) + 1
SkOpAngleSet fAngles; // empty or 2+ -- (number of non-zero spans) * 2
// OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
int fDoneSpans; // quick check that segment is finished
// OPTIMIZATION: force the following to be byte-sized
SkOpSpan fHead; // the head span always has its t set to zero
SkOpSpanBase fTail; // the tail span always has its t set to one
SkOpContour* fContour;
SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments
const SkOpSegment* fPrev;
SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked
SkPathOpsBounds fBounds; // tight bounds
int fCount; // number of spans (one for a non-intersecting segment)
int fDoneCount; // number of processed spans (zero initially)
SkPath::Verb fVerb;
bool fLoop; // set if cubic intersects itself
bool fMultiples; // set if curve intersects multiple other curves at one interior point
bool fOperand;
bool fXor; // set if original contour had even-odd fill
bool fOppXor; // set if opposite operand had even-odd fill
bool fSmall; // set if some span is small
bool fTiny; // set if some span is tiny
#if defined(SK_DEBUG) || !FORCE_RELEASE
int fID;
#endif
friend class PathOpsSegmentTester;
bool fVisited; // used by missing coincidence check
PATH_OPS_DEBUG_CODE(int fID);
};
#endif

355
src/pathops/SkOpSpan.cpp Executable file
View File

@ -0,0 +1,355 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkOpCoincidence.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
bool SkOpPtT::alias() const {
return this->span()->ptT() != this;
}
SkOpContour* SkOpPtT::contour() const {
return segment()->contour();
}
SkOpGlobalState* SkOpPtT::globalState() const {
return contour()->globalState();
}
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
fT = t;
fPt = pt;
fSpan = span;
fNext = this;
fDuplicatePt = duplicate;
fDeleted = false;
PATH_OPS_DEBUG_CODE(fID = span->globalState()->nextPtTID());
}
bool SkOpPtT::onEnd() const {
const SkOpSpanBase* span = this->span();
if (span->ptT() != this) {
return false;
}
const SkOpSegment* segment = this->segment();
return span == segment->head() || span == segment->tail();
}
SkOpPtT* SkOpPtT::prev() {
SkOpPtT* result = this;
SkOpPtT* next = this;
while ((next = next->fNext) != this) {
result = next;
}
SkASSERT(result->fNext == this);
return result;
}
SkOpPtT* SkOpPtT::remove() {
SkOpPtT* prev = this;
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
prev->removeNext(this);
SkASSERT(prev->fNext != prev);
fDeleted = true;
return prev;
}
prev = next;
} while (prev != this);
SkASSERT(0);
return NULL;
}
void SkOpPtT::removeNext(SkOpPtT* kept) {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
SkASSERT(this != next->fNext);
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
next->setDeleted();
if (span->ptT() == next) {
span->upCast()->detach(kept);
}
}
const SkOpSegment* SkOpPtT::segment() const {
return span()->segment();
}
SkOpSegment* SkOpPtT::segment() {
return span()->segment();
}
// find the starting or ending span with an existing loop of angles
// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
// FIXME? assert that only one other span has a valid windValue or oppValue
void SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) {
SkOpAngle* angle;
if (checkFrom) {
SkASSERT(this->final());
if (this->fromAngle()) {
SkASSERT(this->fromAngle()->loopCount() == 2);
return;
}
angle = this->segment()->addEndSpan(allocator);
} else {
SkASSERT(this->t() == 0);
SkOpSpan* span = this->upCast();
if (span->toAngle()) {
SkASSERT(span->toAngle()->loopCount() == 2);
SkASSERT(!span->fromAngle());
span->setFromAngle(span->toAngle()->next());
return;
}
angle = this->segment()->addStartSpan(allocator);
}
SkOpPtT* ptT = this->ptT();
SkOpSpanBase* oSpanBase;
SkOpSpan* oSpan;
SkOpSegment* other;
do {
ptT = ptT->next();
oSpanBase = ptT->span();
oSpan = oSpanBase->upCastable();
other = oSpanBase->segment();
if (oSpan && oSpan->windValue()) {
break;
}
if (oSpanBase->t() == 0) {
continue;
}
SkOpSpan* oFromSpan = oSpanBase->prev();
SkASSERT(oFromSpan->t() < 1);
if (oFromSpan->windValue()) {
break;
}
} while (ptT != this->ptT());
SkOpAngle* oAngle;
if (checkFrom) {
oAngle = other->addStartSpan(allocator);
SkASSERT(oSpan && !oSpan->final());
SkASSERT(oAngle == oSpan->toAngle());
} else {
oAngle = other->addEndSpan(allocator);
SkASSERT(oAngle == oSpanBase->fromAngle());
}
angle->insert(oAngle);
}
void SkOpSpanBase::align() {
if (this->fAligned) {
return;
}
SkASSERT(!zero_or_one(this->fPtT.fT));
SkASSERT(this->fPtT.next());
// if a linked pt/t pair has a t of zero or one, use it as the base for alignment
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
while ((ptT = ptT->next()) != stopPtT) {
if (zero_or_one(ptT->fT)) {
SkOpSegment* segment = ptT->segment();
SkASSERT(this->segment() != segment);
SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
if (ptT->fT) {
segment->tail()->alignEnd(1, segment->lastPt());
} else {
segment->head()->alignEnd(0, segment->pts()[0]);
}
return;
}
}
alignInner();
this->fAligned = true;
}
// FIXME: delete spans that collapse
// delete segments that collapse
// delete contours that collapse
void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
SkASSERT(zero_or_one(t));
SkOpSegment* segment = this->segment();
SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
alignInner();
*segment->writablePt(!!t) = pt;
SkOpPtT* ptT = &this->fPtT;
SkASSERT(t == ptT->fT);
SkASSERT(pt == ptT->fPt);
SkOpPtT* test = ptT, * stopPtT = ptT;
while ((test = test->next()) != stopPtT) {
SkOpSegment* other = test->segment();
if (other == this->segment()) {
continue;
}
if (!zero_or_one(test->fT)) {
continue;
}
*other->writablePt(!!test->fT) = pt;
}
this->fAligned = true;
}
void SkOpSpanBase::alignInner() {
// force the spans to share points and t values
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
const SkPoint& pt = ptT->fPt;
do {
ptT->fPt = pt;
const SkOpSpanBase* span = ptT->span();
SkOpPtT* test = ptT;
do {
SkOpPtT* prev = test;
if ((test = test->next()) == stopPtT) {
break;
}
if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
// omit aliases that alignment makes redundant
if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
SkASSERT(test->alias());
prev->removeNext(ptT);
test = prev;
} else {
SkASSERT(ptT->alias());
stopPtT = ptT = ptT->remove();
break;
}
}
} while (true);
} while ((ptT = ptT->next()) != stopPtT);
}
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
const SkOpPtT* start = &fPtT;
const SkOpPtT* check = &span->fPtT;
SkASSERT(start != check);
const SkOpPtT* walk = start;
while ((walk = walk->next()) != start) {
if (walk == check) {
return true;
}
}
return false;
}
SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
SkOpPtT* start = &fPtT;
SkOpPtT* walk = start;
while ((walk = walk->next()) != start) {
if (walk->segment() == segment) {
return walk;
}
}
return NULL;
}
bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
SkASSERT(this->segment() != segment);
const SkOpSpanBase* next = this;
while ((next = next->fCoinEnd) != this) {
if (next->segment() == segment) {
return true;
}
}
return false;
}
SkOpContour* SkOpSpanBase::contour() const {
return segment()->contour();
}
SkOpGlobalState* SkOpSpanBase::globalState() const {
return contour()->globalState();
}
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
fSegment = segment;
fPtT.init(this, t, pt, false);
fCoinEnd = this;
fFromAngle = NULL;
fPrev = prev;
fAligned = true;
fChased = false;
PATH_OPS_DEBUG_CODE(fCount = 1);
PATH_OPS_DEBUG_CODE(fID = globalState()->nextSpanID());
}
// this pair of spans share a common t value or point; merge them and eliminate duplicates
// this does not compute the best t or pt value; this merely moves all data into a single list
void SkOpSpanBase::merge(SkOpSpan* span) {
SkOpPtT* spanPtT = span->ptT();
SkASSERT(this->t() != spanPtT->fT);
SkASSERT(!zero_or_one(spanPtT->fT));
span->detach(this->ptT());
SkOpPtT* remainder = spanPtT->next();
ptT()->insert(spanPtT);
while (remainder != spanPtT) {
SkOpPtT* next = remainder->next();
SkOpPtT* compare = spanPtT->next();
while (compare != spanPtT) {
SkOpPtT* nextC = compare->next();
if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
goto tryNextRemainder;
}
compare = nextC;
}
spanPtT->insert(remainder);
tryNextRemainder:
remainder = next;
}
}
void SkOpSpan::applyCoincidence(SkOpSpan* opp) {
SkASSERT(!final());
SkASSERT(0); // incomplete
}
bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
SkASSERT(this->segment() != segment);
const SkOpSpan* next = fCoincident;
do {
if (next->segment() == segment) {
return true;
}
} while ((next = next->fCoincident) != this);
return false;
}
void SkOpSpan::detach(SkOpPtT* kept) {
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
SkOpSpanBase* next = this->next();
SkASSERT(next);
prev->setNext(next);
next->setPrev(prev);
this->segment()->detach(this);
this->globalState()->coincidence()->fixUp(this->ptT(), kept);
this->ptT()->setDeleted();
}
void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
SkASSERT(t != 1);
initBase(segment, prev, t, pt);
fCoincident = this;
fToAngle = NULL;
fWindSum = fOppSum = SK_MinS32;
fWindValue = 1;
fOppValue = 0;
fChased = fDone = false;
segment->bumpCount();
}
void SkOpSpan::setOppSum(int oppSum) {
SkASSERT(!final());
if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
this->globalState()->setWindingFailed();
return;
}
SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
fOppSum = oppSum;
}

View File

@ -7,36 +7,460 @@
#ifndef SkOpSpan_DEFINED
#define SkOpSpan_DEFINED
#include "SkPathOpsDebug.h"
#include "SkPoint.h"
class SkOpAngle;
class SkChunkAlloc;
struct SkOpAngle;
class SkOpContour;
class SkOpGlobalState;
class SkOpSegment;
class SkOpSpanBase;
class SkOpSpan;
struct SkOpSpan {
SkPoint fPt; // computed when the curves are intersected
double fT;
double fOtherT; // value at fOther[fOtherIndex].fT
SkOpSegment* fOther;
SkOpAngle* fFromAngle; // (if t > 0) index into segment's angle array going negative in t
SkOpAngle* fToAngle; // (if t < 1) index into segment's angle array going positive in t
int fOtherIndex; // can't be used during intersection
// subset of op span used by terminal span (when t is equal to one)
class SkOpPtT {
public:
enum {
kIsAlias = 1,
kIsDuplicate = 1
};
void addOpp(SkOpPtT* opp) {
// find the fOpp ptr to opp
SkOpPtT* oppPrev = opp->fNext;
if (oppPrev == this) {
return;
}
while (oppPrev->fNext != opp) {
oppPrev = oppPrev->fNext;
if (oppPrev == this) {
return;
}
}
SkOpPtT* oldNext = this->fNext;
SkASSERT(this != opp);
this->fNext = opp;
SkASSERT(oppPrev != oldNext);
oppPrev->fNext = oldNext;
}
bool alias() const;
SkOpContour* contour() const;
int debugID() const {
return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
const SkOpAngle* debugAngle(int id) const;
SkOpContour* debugContour(int id);
int debugLoopLimit(bool report) const;
bool debugMatchID(int id) const;
const SkOpPtT* debugPtT(int id) const;
const SkOpSegment* debugSegment(int id) const;
const SkOpSpanBase* debugSpan(int id) const;
SkOpGlobalState* globalState() const;
void debugValidate() const;
bool deleted() const {
return fDeleted;
}
bool duplicate() const {
return fDuplicatePt;
}
void dump() const; // available to testing only
void dumpAll() const;
void dumpBase() const;
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
void insert(SkOpPtT* span) {
SkASSERT(span != this);
span->fNext = fNext;
fNext = span;
}
const SkOpPtT* next() const {
return fNext;
}
SkOpPtT* next() {
return fNext;
}
bool onEnd() const;
SkOpPtT* prev();
SkOpPtT* remove();
void removeNext(SkOpPtT* kept);
const SkOpSegment* segment() const;
SkOpSegment* segment();
void setDeleted() {
SkASSERT(!fDeleted);
fDeleted = true;
}
const SkOpSpanBase* span() const {
return fSpan;
}
SkOpSpanBase* span() {
return fSpan;
}
double fT;
SkPoint fPt; // cache of point value at this t
protected:
SkOpSpanBase* fSpan; // contains winding data
SkOpPtT* fNext; // intersection on opposite curve or alias on this curve
bool fDeleted; // set if removed from span list
bool fDuplicatePt; // set if identical pt is somewhere in the next loop
PATH_OPS_DEBUG_CODE(int fID);
};
class SkOpSpanBase {
public:
void addSimpleAngle(bool checkFrom , SkChunkAlloc* );
void align();
bool aligned() const {
return fAligned;
}
void alignEnd(double t, const SkPoint& pt);
bool chased() const {
return fChased;
}
void clearCoinEnd() {
SkASSERT(fCoinEnd != this);
fCoinEnd = this;
}
const SkOpSpanBase* coinEnd() const {
return fCoinEnd;
}
bool contains(const SkOpSpanBase* ) const;
SkOpPtT* contains(const SkOpSegment* );
bool containsCoinEnd(const SkOpSpanBase* coin) const {
SkASSERT(this != coin);
const SkOpSpanBase* next = this;
while ((next = next->fCoinEnd) != this) {
if (next == coin) {
return true;
}
}
return false;
}
bool containsCoinEnd(const SkOpSegment* ) const;
SkOpContour* contour() const;
int debugBumpCount() {
return PATH_OPS_DEBUG_RELEASE(++fCount, -1);
}
int debugID() const {
return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
const SkOpAngle* debugAngle(int id) const;
bool debugCoinEndLoopCheck() const;
SkOpContour* debugContour(int id);
const SkOpPtT* debugPtT(int id) const;
const SkOpSegment* debugSegment(int id) const;
const SkOpSpanBase* debugSpan(int id) const;
SkOpGlobalState* globalState() const;
void debugValidate() const;
bool deleted() const {
return fPtT.deleted();
}
void dump() const; // available to testing only
void dumpCoin() const;
void dumpAll() const;
void dumpBase() const;
bool final() const {
return fPtT.fT == 1;
}
SkOpAngle* fromAngle() const {
return fFromAngle;
}
void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
void insertCoinEnd(SkOpSpanBase* coin) {
if (containsCoinEnd(coin)) {
SkASSERT(coin->containsCoinEnd(this));
return;
}
debugValidate();
SkASSERT(this != coin);
SkOpSpanBase* coinNext = coin->fCoinEnd;
coin->fCoinEnd = this->fCoinEnd;
this->fCoinEnd = coinNext;
debugValidate();
}
void merge(SkOpSpan* span);
SkOpSpan* prev() const {
return fPrev;
}
const SkPoint& pt() const {
return fPtT.fPt;
}
const SkOpPtT* ptT() const {
return &fPtT;
}
SkOpPtT* ptT() {
return &fPtT;
}
SkOpSegment* segment() const {
return fSegment;
}
void setChased(bool chased) {
fChased = chased;
}
SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
void setFromAngle(SkOpAngle* angle) {
fFromAngle = angle;
}
void setPrev(SkOpSpan* prev) {
fPrev = prev;
}
bool simple() const {
fPtT.debugValidate();
return fPtT.next()->next() == &fPtT;
}
const SkOpSpan* starter(const SkOpSpanBase* end) const {
const SkOpSpanBase* result = t() < end->t() ? this : end;
return result->upCast();
}
SkOpSpan* starter(SkOpSpanBase* end) {
SkASSERT(this->segment() == end->segment());
SkOpSpanBase* result = t() < end->t() ? this : end;
return result->upCast();
}
SkOpSpan* starter(SkOpSpanBase** endPtr) {
SkOpSpanBase* end = *endPtr;
SkASSERT(this->segment() == end->segment());
SkOpSpanBase* result;
if (t() < end->t()) {
result = this;
} else {
result = end;
*endPtr = this;
}
return result->upCast();
}
int step(const SkOpSpanBase* end) const {
return t() < end->t() ? 1 : -1;
}
double t() const {
return fPtT.fT;
}
void unaligned() {
fAligned = false;
}
SkOpSpan* upCast() {
SkASSERT(!final());
return (SkOpSpan*) this;
}
const SkOpSpan* upCast() const {
SkASSERT(!final());
return (const SkOpSpan*) this;
}
SkOpSpan* upCastable() {
return final() ? NULL : upCast();
}
const SkOpSpan* upCastable() const {
return final() ? NULL : upCast();
}
private:
void alignInner();
protected: // no direct access to internals to avoid treating a span base as a span
SkOpPtT fPtT; // list of points and t values associated with the start of this span
SkOpSegment* fSegment; // segment that contains this span
SkOpSpanBase* fCoinEnd; // linked list of coincident spans that end here (may point to itself)
SkOpAngle* fFromAngle; // points to next angle from span start to end
SkOpSpan* fPrev; // previous intersection point
bool fAligned;
bool fChased; // set after span has been added to chase array
PATH_OPS_DEBUG_CODE(int fCount); // number of pt/t pairs added
PATH_OPS_DEBUG_CODE(int fID);
};
class SkOpSpan : public SkOpSpanBase {
public:
void applyCoincidence(SkOpSpan* opp);
bool clearCoincident() {
SkASSERT(!final());
if (fCoincident == this) {
return false;
}
fCoincident = this;
return true;
}
bool containsCoincidence(const SkOpSegment* ) const;
bool containsCoincidence(const SkOpSpan* coin) const {
SkASSERT(this != coin);
const SkOpSpan* next = this;
while ((next = next->fCoincident) != this) {
if (next == coin) {
return true;
}
}
return false;
}
bool debugCoinLoopCheck() const;
void detach(SkOpPtT* );
bool done() const {
SkASSERT(!final());
return fDone;
}
void dumpCoin() const;
bool dumpSpan() const;
void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
void insertCoincidence(SkOpSpan* coin) {
if (containsCoincidence(coin)) {
SkASSERT(coin->containsCoincidence(this));
return;
}
debugValidate();
SkASSERT(this != coin);
SkOpSpan* coinNext = coin->fCoincident;
coin->fCoincident = this->fCoincident;
this->fCoincident = coinNext;
debugValidate();
}
bool isCanceled() const {
SkASSERT(!final());
return fWindValue == 0 && fOppValue == 0;
}
bool isCoincident() const {
SkASSERT(!final());
return fCoincident != this;
}
SkOpSpanBase* next() const {
SkASSERT(!final());
return fNext;
}
int oppSum() const {
SkASSERT(!final());
return fOppSum;
}
int oppValue() const {
SkASSERT(!final());
return fOppValue;
}
SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
void setDone(bool done) {
SkASSERT(!final());
fDone = done;
}
void setNext(SkOpSpanBase* nextT) {
SkASSERT(!final());
fNext = nextT;
}
void setOppSum(int oppSum);
void setOppValue(int oppValue) {
SkASSERT(!final());
SkASSERT(fOppSum == SK_MinS32);
fOppValue = oppValue;
}
void setToAngle(SkOpAngle* angle) {
SkASSERT(!final());
fToAngle = angle;
}
void setWindSum(int windSum) {
SkASSERT(!final());
SkASSERT(fWindSum == SK_MinS32 || fWindSum == windSum);
SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(windSum) <= DEBUG_LIMIT_WIND_SUM);
fWindSum = windSum;
}
void setWindValue(int windValue) {
SkASSERT(!final());
SkASSERT(windValue >= 0);
SkASSERT(fWindSum == SK_MinS32);
fWindValue = windValue;
}
SkOpAngle* toAngle() const {
SkASSERT(!final());
return fToAngle;
}
int windSum() const {
SkASSERT(!final());
return fWindSum;
}
int windValue() const {
SkASSERT(!final());
return fWindValue;
}
private: // no direct access to internals to avoid treating a span base as a span
SkOpSpan* fCoincident; // linked list of spans coincident with this one (may point to itself)
SkOpAngle* fToAngle; // points to next angle from span start to end
SkOpSpanBase* fNext; // next intersection point
int fWindSum; // accumulated from contours surrounding this one.
int fOppSum; // for binary operators: the opposite winding sum
int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
bool fChased; // set after span has been added to chase array
bool fCoincident; // set if span is bumped -- if set additional points aren't inserted
bool fDone; // if set, this span to next higher T has been processed
bool fLoop; // set when a cubic loops back to this point
bool fMultiple; // set if this is one of mutiple spans with identical t and pt values
bool fNear; // set if opposite end point is near but not equal to this one
bool fSmall; // if set, consecutive points are almost equal
bool fTiny; // if set, consecutive points are equal but consecutive ts are not precisely equal
// available to testing only
const SkOpSegment* debugToSegment(ptrdiff_t* ) const;
void dump() const;
void dumpOne() const;
};
#endif

View File

@ -19,6 +19,12 @@ public:
return record;
}
static T* AllocateArray(SkChunkAlloc* allocator, int count) {
void* ptr = allocator->allocThrow(sizeof(T) * count);
T* record = (T*) ptr;
return record;
}
static T* New(SkChunkAlloc* allocator) {
return new (Allocate(allocator)) T();
}

View File

@ -5,47 +5,25 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
#include "SkTSort.h"
static void alignMultiples(SkTArray<SkOpContour*, true>* contourList,
SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
if (contour->hasMultiples()) {
contour->alignMultiples(aligned);
}
}
}
static void alignCoincidence(SkTArray<SkOpContour*, true>* contourList,
const SkTDArray<SkOpSegment::AlignedSpan>& aligned) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
int count = aligned.count();
for (int index = 0; index < count; ++index) {
contour->alignCoincidence(aligned[index]);
}
}
}
static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
bool* tryAgain, double* midPtr, bool opp) {
const int index = *indexPtr;
const int endIndex = *endIndexPtr;
static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) {
SkOpSpanBase* start = *startPtr;
SkOpSpanBase* end = *endPtr;
const double mid = *midPtr;
const SkOpSegment* current = *currentPtr;
double tAtMid = current->tAtMid(index, endIndex, mid);
double tAtMid = SkOpSegment::TAtMid(start, end, mid);
SkPoint basePt = current->ptAtT(tAtMid);
int contourCount = contourList.count();
SkScalar bestY = SK_ScalarMin;
SkOpSegment* bestSeg = NULL;
int bestTIndex = 0;
SkOpSpan* bestTSpan = NULL;
bool bestOpp;
bool hitSomething = false;
for (int cTest = 0; cTest < contourCount; ++cTest) {
@ -57,37 +35,38 @@ static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, S
if (bestY > contour->bounds().fBottom) {
continue;
}
int segmentCount = contour->segments().count();
for (int test = 0; test < segmentCount; ++test) {
SkOpSegment* testSeg = &contour->segments()[test];
SkOpSegment* testSeg = contour->first();
SkASSERT(testSeg);
do {
SkScalar testY = bestY;
double testHit;
int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
testOpp, testSeg == current);
if (testTIndex < 0) {
if (testTIndex == SK_MinS32) {
bool vertical;
SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
testSeg == current, &testY, &testHit, &hitSomething, &vertical);
if (!testTSpan) {
if (vertical) {
hitSomething = true;
bestSeg = NULL;
goto abortContours; // vertical encountered, return and try different point
}
continue;
}
if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
double baseT = current->t(index);
double endT = current->t(endIndex);
if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
double baseT = start->t();
double endT = end->t();
double newMid = (testHit - baseT) / (endT - baseT);
#if DEBUG_WINDING
double midT = current->tAtMid(index, endIndex, mid);
SkPoint midXY = current->xyAtT(midT);
double newMidT = current->tAtMid(index, endIndex, newMid);
SkPoint newXY = current->xyAtT(newMidT);
double midT = SkOpSegment::TAtMid(start, end, mid);
SkPoint midXY = current->ptAtT(midT);
double newMidT = SkOpSegment::TAtMid(start, end, newMid);
SkPoint newXY = current->ptAtT(newMidT);
SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
" n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), mid, newMid,
baseT, current->xAtT(index), current->yAtT(index),
baseT, start->pt().fX, start->pt().fY,
baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
endT, current->xAtT(endIndex), current->yAtT(endIndex));
endT, end->pt().fX, end->pt().fY);
#endif
*midPtr = newMid * 2; // calling loop with divide by 2 before continuing
return SK_MinS32;
@ -95,38 +74,39 @@ static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, S
bestSeg = testSeg;
*bestHit = testHit;
bestOpp = testOpp;
bestTIndex = testTIndex;
bestTSpan = testTSpan;
bestY = testY;
}
} while ((testSeg = testSeg->next()));
}
abortContours:
int result;
if (!bestSeg) {
result = hitSomething ? SK_MinS32 : 0;
} else {
if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
if (bestTSpan->windSum() == SK_MinS32) {
*currentPtr = bestSeg;
*indexPtr = bestTIndex;
*endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
*startPtr = bestTSpan;
*endPtr = bestTSpan->next();
SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
*tryAgain = true;
return 0;
}
result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
SkASSERT(result == SK_MinS32 || *bestDx);
}
double baseT = current->t(index);
double endT = current->t(endIndex);
double baseT = (*startPtr)->t();
double endT = (*endPtr)->t();
*bestHit = baseT + mid * (endT - baseT);
return result;
}
SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr) {
int contourCount = contourList.count();
SkOpSegment* result;
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = contourList[cIndex];
result = contour->undoneSegment(start, end);
result = contour->undoneSegment(startPtr, endPtr);
if (result) {
return result;
}
@ -134,20 +114,23 @@ SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, i
return NULL;
}
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr) {
while (chase->count()) {
SkOpSpan* span;
SkOpSpanBase* span;
chase->pop(&span);
const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
SkOpSegment* segment = backPtr.fOther;
*tIndex = backPtr.fOtherIndex;
SkOpSegment* segment = span->segment();
*startPtr = span->ptT()->next()->span();
bool sortable = true;
bool done = true;
*endIndex = -1;
if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
*endPtr = NULL;
if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
&sortable)) {
*tIndex = last->start();
*endIndex = last->end();
if (last->unorderable()) {
continue;
}
*startPtr = last->start();
*endPtr = last->end();
#if TRY_ROTATE
*chase->insert(0) = span;
#else
@ -162,65 +145,58 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex)
continue;
}
// find first angle, initialize winding to computed wind sum
const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
const SkOpAngle* firstAngle;
SkDEBUGCODE(firstAngle = angle);
SkDEBUGCODE(bool loop = false);
int winding;
const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
if (!angle) {
continue;
}
const SkOpAngle* firstAngle = angle;
bool loop = false;
int winding = SK_MinS32;
do {
angle = angle->next();
SkASSERT(angle != firstAngle || !loop);
SkDEBUGCODE(loop |= angle == firstAngle);
if (angle == firstAngle && loop) {
break; // if we get here, there's no winding, loop is unorderable
}
loop |= angle == firstAngle;
segment = angle->segment();
winding = segment->windSum(angle);
} while (winding == SK_MinS32);
int spanWinding = segment->spanSign(angle->start(), angle->end());
#if DEBUG_WINDING
SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding);
#endif
// turn span winding into contour winding
if (spanWinding * winding < 0) {
winding += spanWinding;
if (winding == SK_MinS32) {
continue;
}
// we care about first sign and whether wind sum indicates this
// edge is inside or outside. Maybe need to pass span winding
// or first winding or something into this function?
// advance to first undone angle, then return it and winding
// (to set whether edges are active or not)
int sumWinding = segment->updateWindingReverse(angle);
SkOpSegment* first = NULL;
firstAngle = angle;
winding -= firstAngle->segment()->spanSign(firstAngle);
while ((angle = angle->next()) != firstAngle) {
segment = angle->segment();
int maxWinding = winding;
winding -= segment->spanSign(angle);
#if DEBUG_SORT
SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
segment->debugID(), maxWinding, winding, angle->sign());
#endif
*tIndex = angle->start();
*endIndex = angle->end();
int lesser = SkMin32(*tIndex, *endIndex);
const SkOpSpan& nextSpan = segment->span(lesser);
if (!nextSpan.fDone) {
// FIXME: this be wrong? assign startWinding if edge is in
// same direction. If the direction is opposite, winding to
// assign is flipped sign or +/- 1?
if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
maxWinding = winding;
SkOpSpanBase* start = angle->start();
SkOpSpanBase* end = angle->end();
int maxWinding;
segment->setUpWinding(start, end, &maxWinding, &sumWinding);
if (!segment->done(angle)) {
if (!first) {
first = segment;
*startPtr = start;
*endPtr = end;
}
// allowed to do nothing
(void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
break;
// OPTIMIZATION: should this also add to the chase?
(void) segment->markAngle(maxWinding, sumWinding, angle);
}
}
*chase->insert(0) = span;
return segment;
if (first) {
#if TRY_ROTATE
*chase->insert(0) = span;
#else
*chase->append() = span;
#endif
return first;
}
}
return NULL;
}
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
#if DEBUG_ACTIVE_SPANS
void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
int index;
for (index = 0; index < contourList.count(); ++ index) {
contourList[index]->debugShowActiveSpans();
@ -228,11 +204,12 @@ void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
}
#endif
static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
bool* unsortable, bool* done, SkChunkAlloc* allocator) {
SkOpSegment* result;
const SkOpSegment* lastTopStart = NULL;
int lastIndex = -1, lastEndIndex = -1;
SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
do {
SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
int contourCount = contourList.count();
@ -261,27 +238,27 @@ static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourLi
return NULL;
}
*topLeft = bestXY;
result = topStart->findTop(index, endIndex, unsortable, firstPass);
result = topStart->findTop(firstPass, start, end, unsortable, allocator);
if (!result) {
if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
*done = true;
return NULL;
}
lastTopStart = topStart;
lastIndex = *index;
lastEndIndex = *endIndex;
lastStart = *start;
lastEnd = *end;
}
} while (!result);
return result;
}
static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
double test = 0.9;
int contourWinding;
do {
contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
tHit, hitDx, tryAgain, &test, opp);
if (contourWinding != SK_MinS32 || *tryAgain) {
return contourWinding;
@ -296,9 +273,9 @@ static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
return contourWinding;
}
static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
SkOpSegment** current, int* index, int* endIndex) {
if (!(*current)->isVertical(*index, *endIndex)) {
static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
if (!(*current)->isVertical(*start, *end)) {
return;
}
int contourCount = contourList.count();
@ -307,7 +284,7 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
if (contour->done()) {
continue;
}
SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
if (nonVertical) {
*current = nonVertical;
return;
@ -316,41 +293,41 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
return;
}
struct SortableTop { // error if local in pre-C++11
SkOpSegment* fSegment;
int fIndex;
int fEndIndex;
struct SortableTop2 { // error if local in pre-C++11
SkOpSpanBase* fStart;
SkOpSpanBase* fEnd;
};
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
bool firstPass) {
SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
done, firstPass);
SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass,
SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
SkChunkAlloc* allocator) {
SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft,
unsortable, done, allocator);
if (!current) {
return NULL;
}
const int startIndex = *indexPtr;
const int endIndex = *endIndexPtr;
SkOpSpanBase* start = *startPtr;
SkOpSpanBase* end = *endPtr;
SkASSERT(current == start->segment());
if (*firstContour) {
current->initWinding(startIndex, endIndex, angleIncludeType);
current->initWinding(start, end, angleIncludeType);
*firstContour = false;
return current;
}
int minIndex = SkMin32(startIndex, endIndex);
int sumWinding = current->windSum(minIndex);
SkOpSpan* minSpan = start->starter(end);
int sumWinding = minSpan->windSum();
if (sumWinding == SK_MinS32) {
int index = endIndex;
int oIndex = startIndex;
do {
const SkOpSpan& span = current->span(index);
if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
current->addSimpleAngle(index);
SkOpSpanBase* iSpan = end;
SkOpSpanBase* oSpan = start;
do {
bool checkFrom = oSpan->t() < iSpan->t();
if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) {
iSpan->addSimpleAngle(checkFrom, allocator);
}
sumWinding = current->computeSum(oIndex, index, angleIncludeType);
SkTSwap(index, oIndex);
} while (sumWinding == SK_MinS32 && index == startIndex);
sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
SkTSwap(iSpan, oSpan);
} while (sumWinding == SK_MinS32 && iSpan == start);
}
if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
return current;
@ -364,26 +341,28 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
SkScalar hitDx = 0;
SkScalar hitOppDx = 0;
// keep track of subsequent returns to detect infinite loops
SkTDArray<SortableTop> sortableTops;
SkTDArray<SortableTop2> sortableTops;
do {
// if current is vertical, find another candidate which is not
// if only remaining candidates are vertical, then they can be marked done
SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
skipVertical(contourList, &current, indexPtr, endIndexPtr);
SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
SkASSERT(current == (*startPtr)->segment());
skipVertical(contourList, &current, startPtr, endPtr);
SkASSERT(current); // FIXME: if null, all remaining are vertical
SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
SkASSERT(current == (*startPtr)->segment());
tryAgain = false;
contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
&hitDx, &tryAgain, onlyVertical, false);
SkASSERT(current == (*startPtr)->segment());
if (tryAgain) {
bool giveUp = false;
int count = sortableTops.count();
for (int index = 0; index < count; ++index) {
const SortableTop& prev = sortableTops[index];
const SortableTop2& prev = sortableTops[index];
if (giveUp) {
prev.fSegment->markDoneFinal(prev.fIndex);
} else if (prev.fSegment == current
&& (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
} else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
// remaining edges are non-vertical and cannot have their winding computed
// mark them as done and return, and hope that assembly can fill the holes
giveUp = true;
@ -395,14 +374,13 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
return NULL;
}
}
SortableTop* sortableTop = sortableTops.append();
sortableTop->fSegment = current;
sortableTop->fIndex = *indexPtr;
sortableTop->fEndIndex = *endIndexPtr;
SortableTop2* sortableTop = sortableTops.append();
sortableTop->fStart = *startPtr;
sortableTop->fEnd = *endPtr;
#if DEBUG_SORT
SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
__FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
*onlyVertical);
__FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
tHit, hitDx, tryAgain, *onlyVertical);
#endif
if (*onlyVertical) {
return current;
@ -413,127 +391,35 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
if (angleIncludeType < SkOpAngle::kBinarySingle) {
break;
}
oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
&hitOppDx, &tryAgain, NULL, true);
SkASSERT(current == (*startPtr)->segment());
} while (tryAgain);
bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
oppContourWinding, hitOppDx);
if (current->done()) {
return NULL;
} else if (!success) { // check if the span has a valid winding
int min = SkTMin(*indexPtr, *endIndexPtr);
const SkOpSpan& span = current->span(min);
if (span.fWindSum == SK_MinS32) {
SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
: (*endPtr)->upCast();
if (minSpan->windSum() == SK_MinS32) {
return NULL;
}
}
return current;
}
static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
if (!contour->calcAngles()) {
return false;
}
}
return true;
}
static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->checkDuplicates();
}
}
static bool checkEnds(SkTArray<SkOpContour*, true>* contourList) {
// it's hard to determine if the end of a cubic or conic nearly intersects another curve.
// instead, look to see if the connecting curve intersected at that same end.
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
if (!contour->checkEnds()) {
return false;
}
}
return true;
}
static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
bool hasMultiples = false;
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->checkMultiples();
hasMultiples |= contour->hasMultiples();
}
return hasMultiples;
}
// A small interval of a pair of curves may collapse to lines for each, triggering coincidence
static void checkSmall(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->checkSmall();
}
}
// A tiny interval may indicate an undiscovered coincidence. Find and fix.
static void checkTiny(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->checkTiny();
}
}
static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->fixOtherTIndex();
}
}
static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->joinCoincidence();
}
}
static void sortAngles(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->sortAngles();
}
}
static void sortSegments(SkTArray<SkOpContour*, true>* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->sortSegments();
}
}
void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
bool evenOdd, bool oppEvenOdd) {
int count = contours.count();
if (count == 0) {
do {
if (contour->count()) {
contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
*list.append() = contour;
}
} while ((contour = contour->next()));
if (list.count() < 2) {
return;
}
for (int index = 0; index < count; ++index) {
SkOpContour& contour = contours[index];
contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
list.push_back(&contour);
}
SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
}
@ -554,19 +440,22 @@ public:
reassemble contour pieces into new path
*/
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
SkOpContour contour;
SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
SkTArray<SkOpContour> contours;
SkOpEdgeBuilder builder(path, contours);
builder.finish();
int count = contours.count();
int outer;
SkTArray<int, true> runs(count); // indices of partial contours
for (outer = 0; outer < count; ++outer) {
const SkOpContour& eContour = contours[outer];
const SkPoint& eStart = eContour.start();
const SkPoint& eEnd = eContour.end();
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
builder.finish(&allocator);
SkTDArray<const SkOpContour* > runs; // indices of partial contours
const SkOpContour* eContour = builder.head();
do {
if (!eContour->count()) {
continue;
}
const SkPoint& eStart = eContour->start();
const SkPoint& eEnd = eContour->end();
#if DEBUG_ASSEMBLE
SkDebugf("%s contour", __FUNCTION__);
if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@ -578,44 +467,42 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
#endif
if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
eContour.toPath(simple);
eContour->toPath(simple);
continue;
}
runs.push_back(outer);
}
count = runs.count();
*runs.append() = eContour;
} while ((eContour = eContour->next()));
int count = runs.count();
if (count == 0) {
return;
}
SkTArray<int, true> sLink, eLink;
sLink.push_back_n(count);
eLink.push_back_n(count);
SkTDArray<int> sLink, eLink;
sLink.append(count);
eLink.append(count);
int rIndex, iIndex;
for (rIndex = 0; rIndex < count; ++rIndex) {
sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
}
const int ends = count * 2; // all starts and ends
const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
SkTArray<double, true> distances;
distances.push_back_n(entries);
SkTDArray<double> distances;
distances.append(entries);
for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
outer = runs[rIndex >> 1];
const SkOpContour& oContour = contours[outer];
const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
const SkOpContour* oContour = runs[rIndex >> 1];
const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
* ends - rIndex - 1;
for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
int inner = runs[iIndex >> 1];
const SkOpContour& iContour = contours[inner];
const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
const SkOpContour* iContour = runs[iIndex >> 1];
const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
double dx = iPt.fX - oPt.fX;
double dy = iPt.fY - oPt.fY;
double dist = dx * dx + dy * dy;
distances[row + iIndex] = dist; // oStart distance from iStart
}
}
SkTArray<int, true> sortedDist;
sortedDist.push_back_n(entries);
SkTDArray<int> sortedDist;
sortedDist.append(entries);
for (rIndex = 0; rIndex < entries; ++rIndex) {
sortedDist[rIndex] = rIndex;
}
@ -678,17 +565,16 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
eIndex < 0 ? ~eIndex : eIndex);
#endif
do {
outer = runs[rIndex];
const SkOpContour& contour = contours[outer];
const SkOpContour* contour = runs[rIndex];
if (first) {
first = false;
const SkPoint* startPtr = &contour.start();
const SkPoint* startPtr = &contour->start();
simple->deferredMove(startPtr[0]);
}
if (forward) {
contour.toPartialForward(simple);
contour->toPartialForward(simple);
} else {
contour.toPartialBackward(simple);
contour->toPartialBackward(simple);
}
#if DEBUG_ASSEMBLE
SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
@ -742,36 +628,88 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#endif
}
bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
#if DEBUG_SHOW_WINDING
SkOpContour::debugShowWindingValues(contourList);
#endif
if (!CoincidenceCheck(contourList, total)) {
static void align(SkTDArray<SkOpContour* >* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->align();
}
}
static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->calcAngles(allocator);
}
}
static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->missingCoincidence(coincidence, allocator);
}
}
static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
if (!contour->moveNearby()) {
return false;
}
}
return true;
}
static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->sortAngles();
}
}
static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
contour->sortSegments();
}
}
bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
// move t values and points together to eliminate small/tiny gaps
if (!moveNearby(contourList)) {
return false;
}
#if DEBUG_SHOW_WINDING
SkOpContour::debugShowWindingValues(contourList);
align(contourList); // give all span members common values
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kIntersecting);
#endif
fixOtherTIndex(contourList);
if (!checkEnds(contourList)) { // check if connecting curve intersected at the same end
coincidence->addMissing(allocator);
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kWalking);
#endif
coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
coincidence->mark(); // mark spans of coincident segments as coincident
missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
return false;
}
bool hasM = checkMultiples(contourList); // check if intersections agree on t and point values
SkTDArray<SkOpSegment::AlignedSpan> aligned;
if (hasM) {
alignMultiples(contourList, &aligned); // align pairs of identical points
alignCoincidence(contourList, aligned);
}
checkDuplicates(contourList); // check if spans have the same number on the other end
checkTiny(contourList); // if pair have the same end points, mark them as parallel
checkSmall(contourList); // a pair of curves with a small span may turn into coincident lines
joinCoincidence(contourList); // join curves that connect to a coincident pair
sortSegments(contourList);
if (!calcAngles(contourList)) {
return false;
}
calcAngles(contourList, allocator);
sortAngles(contourList);
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
if (globalState->angleCoincidence()) {
missingCoincidence(contourList, coincidence, allocator);
if (!coincidence->apply()) {
return false;
}
}
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(*contourList);
#endif
return true;

View File

@ -8,24 +8,28 @@
#define SkPathOpsCommon_DEFINED
#include "SkOpAngle.h"
#include "SkOpContour.h"
#include "SkTDArray.h"
class SkOpCoincidence;
class SkOpContour;
class SkPathWriter;
void Assemble(const SkPathWriter& path, SkPathWriter* simple);
// FIXME: find chase uses insert, so it can't be converted to SkTArray yet
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex);
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& , SkOpAngle::IncludeType ,
bool* firstContour, int* index, int* endIndex, SkPoint* topLeft,
bool* unsortable, bool* done, bool* onlyVertical, bool firstPass);
SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end);
void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour*>& , bool firstPass,
SkOpAngle::IncludeType , bool* firstContour, SkOpSpanBase** index,
SkOpSpanBase** endIndex, SkPoint* topLeft, bool* unsortable,
bool* done, bool* onlyVertical, SkChunkAlloc* );
SkOpSegment* FindUndone(SkTDArray<SkOpContour*>& contourList, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
void MakeContourList(SkOpContour* , SkTDArray<SkOpContour*>& list,
bool evenOdd, bool oppEvenOdd);
bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
bool HandleCoincidence(SkTDArray<SkOpContour*>* , SkOpCoincidence* , SkChunkAlloc* ,
SkOpGlobalState* );
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
#if DEBUG_ACTIVE_SPANS
void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList);
#endif
#endif

View File

@ -4,6 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkGeometry.h"
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
@ -26,8 +27,8 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
double priorT = t - step;
SkASSERT(priorT >= min);
SkDPoint lessPt = ptAtT(priorT);
if (approximately_equal(lessPt.fX, cubicAtT.fX)
&& approximately_equal(lessPt.fY, cubicAtT.fY)) {
if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
&& approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@ -41,10 +42,12 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
t = priorT;
} else {
double nextT = t + lastStep;
SkASSERT(nextT <= max);
if (nextT > max) {
return -1;
}
SkDPoint morePt = ptAtT(nextT);
if (approximately_equal(morePt.fX, cubicAtT.fX)
&& approximately_equal(morePt.fY, cubicAtT.fY)) {
if (approximately_equal_half(morePt.fX, cubicAtT.fX)
&& approximately_equal_half(morePt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@ -88,35 +91,6 @@ void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
*C -= 3 * *D; // C = -3*a + 3*b
}
bool SkDCubic::controlsContainedByEnds() const {
SkDVector startTan = fPts[1] - fPts[0];
if (startTan.fX == 0 && startTan.fY == 0) {
startTan = fPts[2] - fPts[0];
}
SkDVector endTan = fPts[2] - fPts[3];
if (endTan.fX == 0 && endTan.fY == 0) {
endTan = fPts[1] - fPts[3];
}
if (startTan.dot(endTan) >= 0) {
return false;
}
SkDLine startEdge = {{fPts[0], fPts[0]}};
startEdge[1].fX -= startTan.fY;
startEdge[1].fY += startTan.fX;
SkDLine endEdge = {{fPts[3], fPts[3]}};
endEdge[1].fX -= endTan.fY;
endEdge[1].fY += endTan.fX;
double leftStart1 = startEdge.isLeft(fPts[1]);
if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
return false;
}
double leftEnd1 = endEdge.isLeft(fPts[1]);
if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
return false;
}
return leftStart1 * leftEnd1 >= 0;
}
bool SkDCubic::endsAreExtremaInXOrY() const {
return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
&& between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
@ -124,17 +98,120 @@ bool SkDCubic::endsAreExtremaInXOrY() const {
&& between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
}
// Do a quick reject by rotating all points relative to a line formed by
// a pair of one cubic's points. If the 2nd cubic's points
// are on the line or on the opposite side from the 1st cubic's 'odd man', the
// curves at most intersect at the endpoints.
/* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
if returning false, check contains true if the the cubic pair have only the end point in common
*/
bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
bool linear = true;
char hullOrder[4];
int hullCount = convexHull(hullOrder);
int end1 = hullOrder[0];
int hullIndex = 0;
const SkDPoint* endPt[2];
endPt[0] = &fPts[end1];
do {
hullIndex = (hullIndex + 1) % hullCount;
int end2 = hullOrder[hullIndex];
endPt[1] = &fPts[end2];
double origX = endPt[0]->fX;
double origY = endPt[0]->fY;
double adj = endPt[1]->fX - origX;
double opp = endPt[1]->fY - origY;
int oddManMask = other_two(end1, end2);
int oddMan = end1 ^ oddManMask;
double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
int oddMan2 = end2 ^ oddManMask;
double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
if (sign * sign2 < 0) {
continue;
}
if (approximately_zero(sign)) {
sign = sign2;
if (approximately_zero(sign)) {
continue;
}
}
linear = false;
bool foundOutlier = false;
for (int n = 0; n < kPointCount; ++n) {
double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
if (test * sign > 0 && !precisely_zero(test)) {
foundOutlier = true;
break;
}
}
if (!foundOutlier) {
return false;
}
endPt[0] = endPt[1];
end1 = end2;
} while (hullIndex);
*isLinear = linear;
return true;
}
bool SkDCubic::isLinear(int startIndex, int endIndex) const {
SkLineParameters lineParameters;
lineParameters.cubicEndPoints(*this, startIndex, endIndex);
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
largest = SkTMax(largest, -tiniest);
double distance = lineParameters.controlPtDistance(*this, 1);
if (!approximately_zero(distance)) {
if (!approximately_zero_when_compared_to(distance, largest)) {
return false;
}
distance = lineParameters.controlPtDistance(*this, 2);
return approximately_zero(distance);
return approximately_zero_when_compared_to(distance, largest);
}
bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
SkScalar d[3];
SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
if (cubicType == kLoop_SkCubicType) {
// crib code from gpu path utils that finds t values where loop self-intersects
// use it to find mid of t values which should be a friendly place to chop
SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
SkScalar ls = d[1] - tempSqrt;
SkScalar lt = 2.f * d[0];
SkScalar ms = d[1] + tempSqrt;
SkScalar mt = 2.f * d[0];
if (between(0, ls, lt) || between(0, ms, mt)) {
ls = ls / lt;
ms = ms / mt;
SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
*t = (smaller + larger) / 2;
return *t > 0 && *t < 1;
}
} else if (cubicType == kSerpentine_SkCubicType) {
SkDCubic cubic;
cubic.set(pointsPtr);
double inflectionTs[2];
int infTCount = cubic.findInflections(inflectionTs);
if (infTCount == 2) {
double maxCurvature[3];
int roots = cubic.findMaxCurvature(maxCurvature);
for (int index = 0; index < roots; ++index) {
if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
*t = maxCurvature[index];
return true;
}
}
} else if (infTCount == 1) {
*t = inflectionTs[0];
return *t > 0 && *t < 1;
}
return false;
}
return false;
}
bool SkDCubic::monotonicInY() const {
@ -142,6 +219,13 @@ bool SkDCubic::monotonicInY() const {
&& between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
}
void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
int offset = (int) !SkToBool(index);
o1Pts[0] = &fPts[offset];
o1Pts[1] = &fPts[++offset];
o1Pts[2] = &fPts[++offset];
}
int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
SearchAxis xAxis, double* validRoots) const {
extrema += findInflections(&extremeTs[extrema]);
@ -163,26 +247,6 @@ int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
return validCount;
}
bool SkDCubic::serpentine() const {
#if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicOp53d
double tValues[2];
// OPTIMIZATION : another case where caching the present of cubic inflections would be useful
return findInflections(tValues) > 1;
#endif
if (!controlsContainedByEnds()) {
return false;
}
double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY);
for (int idx = 0; idx < 2; ++idx) {
wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
}
double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY);
for (int idx = 1; idx < 3; ++idx) {
waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
}
return wiggle * waggle < 0;
}
// cubic roots
static const double PI = 3.141592653589793;
@ -505,25 +569,10 @@ void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
double t1, double t2, SkDPoint dst[2]) const {
SkASSERT(t1 != t2);
#if 0
double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3);
double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3);
double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3);
double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3);
double mx = ex * 27 - a.fX * 8 - d.fX;
double my = ey * 27 - a.fY * 8 - d.fY;
double nx = fx * 27 - a.fX - d.fX * 8;
double ny = fy * 27 - a.fY - d.fY * 8;
/* bx = */ dst[0].fX = (mx * 2 - nx) / 18;
/* by = */ dst[0].fY = (my * 2 - ny) / 18;
/* cx = */ dst[1].fX = (nx * 2 - mx) / 18;
/* cy = */ dst[1].fY = (ny * 2 - my) / 18;
#else
// this approach assumes that the control points computed directly are accurate enough
SkDCubic sub = subDivide(t1, t2);
dst[0] = sub[1] + (a - sub[0]);
dst[1] = sub[2] + (d - sub[3]);
#endif
if (t1 == 0 || t2 == 0) {
align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
}

View File

@ -10,7 +10,6 @@
#include "SkPath.h"
#include "SkPathOpsPoint.h"
#include "SkTArray.h"
struct SkDCubicPair {
const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@ -19,13 +18,33 @@ struct SkDCubicPair {
};
struct SkDCubic {
static const int kPointCount = 4;
static const int kPointLast = kPointCount - 1;
static const int kMaxIntersections = 9;
enum SearchAxis {
kXAxis,
kYAxis
};
const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
bool collapsed() const {
return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2])
&& fPts[0].approximatelyEqual(fPts[3]);
}
bool controlsInside() const {
SkDVector v01 = fPts[0] - fPts[1];
SkDVector v02 = fPts[0] - fPts[2];
SkDVector v03 = fPts[0] - fPts[3];
SkDVector v13 = fPts[1] - fPts[3];
SkDVector v23 = fPts[2] - fPts[3];
return v03.dot(v01) > 0 && v03.dot(v02) > 0 && v03.dot(v13) > 0 && v03.dot(v23) > 0;
}
static bool IsCubic() { return true; }
const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
@ -33,30 +52,35 @@ struct SkDCubic {
SkDCubicPair chopAt(double t) const;
bool clockwise() const;
static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
bool controlsContainedByEnds() const;
static bool ComplexBreak(const SkPoint pts[4], SkScalar* t);
int convexHull(char order[kPointCount]) const;
void dump() const; // callable from the debugger when the implementation code is linked in
void dumpID(int id) const;
void dumpInner() const;
SkDVector dxdyAtT(double t) const;
bool endsAreExtremaInXOrY() const;
static int FindExtrema(double a, double b, double c, double d, double tValue[2]);
int findInflections(double tValues[2]) const;
static int FindInflections(const SkPoint a[4], double tValues[2]) {
static int FindInflections(const SkPoint a[kPointCount], double tValues[2]) {
SkDCubic cubic;
cubic.set(a);
return cubic.findInflections(tValues);
}
int findMaxCurvature(double tValues[]) const;
bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
bool isLinear(int startIndex, int endIndex) const;
bool monotonicInY() const;
void otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const;
SkDPoint ptAtT(double t) const;
static int RootsReal(double A, double B, double C, double D, double t[3]);
static int RootsValidT(const double A, const double B, const double C, double D, double s[3]);
int searchRoots(double extremes[6], int extrema, double axisIntercept,
SearchAxis xAxis, double* validRoots) const;
bool serpentine() const;
void set(const SkPoint pts[4]) {
void set(const SkPoint pts[kPointCount]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
@ -65,7 +89,7 @@ struct SkDCubic {
SkDCubic subDivide(double t1, double t2) const;
static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
SkDCubic cubic;
cubic.set(a);
return cubic.subDivide(t1, t2);
@ -73,7 +97,7 @@ struct SkDCubic {
void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
static void SubDivide(const SkPoint pts[4], const SkDPoint& a, const SkDPoint& d, double t1,
static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
double t2, SkDPoint p[2]) {
SkDCubic cubic;
cubic.set(pts);
@ -81,16 +105,29 @@ struct SkDCubic {
}
SkDPoint top(double startT, double endT) const;
void toQuadraticTs(double precision, SkTArray<double, true>* ts) const;
SkDQuad toQuad() const;
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
void dumpNumber() const;
static const int gPrecisionUnit;
SkDPoint fPts[4];
SkDPoint fPts[kPointCount];
};
/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
that computes the other two. Note that:
one ^ two == 3 for (0, 3), (1, 2)
one ^ two < 3 for (0, 1), (0, 2), (1, 3), (2, 3)
3 - (one ^ two) is either 0, 1, or 2
1 >> (3 - (one ^ two)) is either 0 or 1
thus:
returned == 2 for (0, 3), (1, 2)
returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
given that:
(0, 3) ^ 2 -> (2, 1) (1, 2) ^ 2 -> (3, 0)
(0, 1) ^ 3 -> (3, 2) (0, 2) ^ 3 -> (3, 1) (1, 3) ^ 3 -> (2, 0) (2, 3) ^ 3 -> (1, 0)
*/
inline int other_two(int one, int two) {
return 1 >> (3 - (one ^ two)) ^ 3;
}
#endif

View File

@ -1,175 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkCubicSpan_DEFINE
#define SkCubicSpan_DEFINE
#include "SkChunkAlloc.h"
#include "SkPathOpsRect.h"
#include "SkPathOpsCubic.h"
#include "SkTArray.h"
class SkIntersections;
class SkCubicCoincident {
public:
bool isCoincident() const {
return fCoincident;
}
void init() {
fCoincident = false;
SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
SkDEBUGCODE(fPerpT = SK_ScalarNaN);
}
void markCoincident() {
if (!fCoincident) {
fPerpT = -1;
}
fCoincident = true;
}
const SkDPoint& perpPt() const {
return fPerpPt;
}
double perpT() const {
return fPerpT;
}
void setPerp(const SkDCubic& cubic1, double t, const SkDPoint& qPt, const SkDCubic& cubic2);
private:
SkDPoint fPerpPt;
double fPerpT; // perpendicular intersection on opposite Cubic
bool fCoincident;
};
class SkCubicSect; // used only by debug id
class SkCubicSpan {
public:
void init(const SkDCubic& Cubic);
void initBounds(const SkDCubic& Cubic);
bool contains(double t) const {
return !! const_cast<SkCubicSpan*>(this)->innerFind(t);
}
bool contains(const SkCubicSpan* span) const;
SkCubicSpan* find(double t) {
SkCubicSpan* result = innerFind(t);
SkASSERT(result);
return result;
}
bool intersects(const SkCubicSpan* span) const;
const SkCubicSpan* next() const {
return fNext;
}
void reset() {
fBounded.reset();
}
bool split(SkCubicSpan* work) {
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
}
bool splitAt(SkCubicSpan* work, double t);
bool tightBoundsIntersects(const SkCubicSpan* span) const;
// implementation is for testing only
void dump() const;
private:
bool hullIntersects(const SkDCubic& ) const;
SkCubicSpan* innerFind(double t);
bool linearIntersects(const SkDCubic& ) const;
// implementation is for testing only
#if DEBUG_BINARY_CUBIC
int debugID(const SkCubicSect* ) const { return fDebugID; }
#else
int debugID(const SkCubicSect* ) const;
#endif
void dump(const SkCubicSect* ) const;
void dumpID(const SkCubicSect* ) const;
#if DEBUG_BINARY_CUBIC
void validate() const;
#endif
SkDCubic fPart;
SkCubicCoincident fCoinStart;
SkCubicCoincident fCoinEnd;
SkSTArray<4, SkCubicSpan*, true> fBounded;
SkCubicSpan* fPrev;
SkCubicSpan* fNext;
SkDRect fBounds;
double fStartT;
double fEndT;
double fBoundsMax;
bool fCollapsed;
bool fHasPerp;
mutable bool fIsLinear;
#if DEBUG_BINARY_CUBIC
int fDebugID;
bool fDebugDeleted;
#endif
friend class SkCubicSect;
};
class SkCubicSect {
public:
SkCubicSect(const SkDCubic& Cubic PATH_OPS_DEBUG_PARAMS(int id));
static void BinarySearch(SkCubicSect* sect1, SkCubicSect* sect2, SkIntersections* intersections);
// for testing only
void dumpCubics() const;
private:
SkCubicSpan* addOne();
bool binarySearchCoin(const SkCubicSect& , double tStart, double tStep, double* t,
double* oppT);
SkCubicSpan* boundsMax() const;
void coincidentCheck(SkCubicSect* sect2);
bool intersects(const SkCubicSpan* span, const SkCubicSect* opp, const SkCubicSpan* oppSpan) const;
void onCurveCheck(SkCubicSect* sect2, SkCubicSpan* first, SkCubicSpan* last);
void recoverCollapsed();
void removeSpan(SkCubicSpan* span);
void removeOne(const SkCubicSpan* test, SkCubicSpan* span);
void removeSpans(SkCubicSpan* span, SkCubicSect* opp);
void setPerp(const SkDCubic& opp, SkCubicSpan* first, SkCubicSpan* last);
void trim(SkCubicSpan* span, SkCubicSect* opp);
// for testing only
void dump() const;
void dumpBoth(const SkCubicSect& opp) const;
void dumpBoth(const SkCubicSect* opp) const;
#if DEBUG_BINARY_CUBIC
int debugID() const { return fDebugID; }
void validate() const;
#else
int debugID() const { return 0; }
#endif
const SkDCubic& fCubic;
SkChunkAlloc fHeap;
SkCubicSpan* fHead;
SkCubicSpan* fDeleted;
int fActiveCount;
#if DEBUG_BINARY_CUBIC
int fDebugID;
int fDebugCount;
int fDebugAllocatedCount;
#endif
friend class SkCubicSpan; // only used by debug id
};
#endif

View File

@ -7,6 +7,13 @@
#include "SkPathOpsDebug.h"
#include "SkPath.h"
#if DEBUG_ANGLE
#include "SkString.h"
#endif
#if DEBUG_VALIDATE
extern bool FLAGS_runFail;
#endif
#if defined SK_DEBUG || !FORCE_RELEASE
@ -26,10 +33,10 @@ int SkPathOpsDebug::gSortCount;
const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
#endif
bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
const SkOpSpan* span) {
bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
const SkOpSpanBase* span) {
for (int index = 0; index < chaseArray.count(); ++index) {
const SkOpSpan* entry = chaseArray[index];
const SkOpSpanBase* entry = chaseArray[index];
if (entry == span) {
return true;
}
@ -65,6 +72,8 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
SkDebugf("%d", wind);
}
}
#endif // defined SK_DEBUG || !FORCE_RELEASE
#if DEBUG_SHOW_TEST_NAME
void* SkPathOpsDebug::CreateNameStr() {
@ -97,11 +106,160 @@ void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op,
}
#endif
#endif // defined SK_DEBUG || !FORCE_RELEASE
#include "SkOpAngle.h"
#include "SkOpSegment.h"
#if DEBUG_SWAP_TOP
int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
double inflections[2];
return dst.findInflections(inflections);
}
#endif
SkOpAngle* SkOpSegment::debugLastAngle() {
SkOpAngle* result = NULL;
SkOpSpan* span = this->head();
do {
if (span->toAngle()) {
SkASSERT(!result);
result = span->toAngle();
}
} while ((span = span->next()->upCastable()));
SkASSERT(result);
return result;
}
void SkOpSegment::debugReset() {
this->init(this->fPts, this->contour(), this->verb());
}
#if DEBUG_ACTIVE_SPANS
void SkOpSegment::debugShowActiveSpans() const {
debugValidate();
if (done()) {
return;
}
int lastId = -1;
double lastT = -1;
const SkOpSpan* span = &fHead;
do {
if (span->done()) {
continue;
}
if (lastId == fID && lastT == span->t()) {
continue;
}
lastId = fID;
lastT = span->t();
SkDebugf("%s id=%d", __FUNCTION__, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
const SkOpPtT* ptT = span->ptT();
SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
SkDebugf(" tEnd=%1.9g", span->next()->t());
SkDebugf(" windSum=");
if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->windSum());
}
SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
SkDebugf("\n");
} while ((span = span->next()->upCastable()));
}
#endif
#if DEBUG_MARK_DONE
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
const SkPoint& pt = span->ptT()->fPt;
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
if (winding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", winding);
}
SkDebugf(" windSum=");
if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->windSum());
}
SkDebugf(" windValue=%d\n", span->windValue());
}
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
int oppWinding) {
const SkPoint& pt = span->ptT()->fPt;
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
if (winding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", winding);
}
SkDebugf(" newOppSum=");
if (oppWinding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", oppWinding);
}
SkDebugf(" oppSum=");
if (span->oppSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->oppSum());
}
SkDebugf(" windSum=");
if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->windSum());
}
SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
}
#endif
#if DEBUG_ANGLE
SkString SkOpAngle::debugPart() const {
SkString result;
switch (this->segment()->verb()) {
case SkPath::kLine_Verb:
result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
this->segment()->debugID());
break;
case SkPath::kQuad_Verb:
result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
this->segment()->debugID());
break;
case SkPath::kCubic_Verb:
result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
this->segment()->debugID());
break;
default:
SkASSERT(0);
}
return result;
}
#endif
#if DEBUG_SORT
void SkOpAngle::debugLoop() const {
const SkOpAngle* first = this;
@ -111,25 +269,59 @@ void SkOpAngle::debugLoop() const {
SkDebugf("\n");
next = next->fNext;
} while (next && next != first);
next = first;
do {
next->debugValidate();
next = next->fNext;
} while (next && next != first);
}
#endif
#if DEBUG_ANGLE
void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
SK_ALWAYSBREAK(fSegment == compare->fSegment);
const SkOpSpan& startSpan = fSegment->span(fStart);
const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
const SkOpSpan& endSpan = fSegment->span(fEnd);
const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
}
#endif
void SkOpAngle::debugValidate() const {
#if DEBUG_VALIDATE
const SkOpAngle* first = this;
const SkOpAngle* next = this;
int wind = 0;
int opp = 0;
int lastXor = -1;
int lastOppXor = -1;
do {
if (next->unorderable()) {
return;
}
const SkOpSpan* minSpan = next->start()->starter(next->end());
if (minSpan->windValue() == SK_MinS32) {
return;
}
bool op = next->segment()->operand();
bool isXor = next->segment()->isXor();
bool oppXor = next->segment()->oppXor();
SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
SkASSERT(!DEBUG_LIMIT_WIND_SUM
|| between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
bool useXor = op ? oppXor : isXor;
SkASSERT(lastXor == -1 || lastXor == (int) useXor);
lastXor = (int) useXor;
wind += next->sign() * (op ? minSpan->oppValue() : minSpan->windValue());
if (useXor) {
wind &= 1;
}
useXor = op ? isXor : oppXor;
SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
lastOppXor = (int) useXor;
opp += next->sign() * (op ? minSpan->windValue() : minSpan->oppValue());
if (useXor) {
opp &= 1;
}
next = next->fNext;
} while (next && next != first);
SkASSERT(wind == 0);
SkASSERT(opp == 0 || !FLAGS_runFail);
#endif
}
void SkOpAngle::debugValidateNext() const {
#if !FORCE_RELEASE
const SkOpAngle* first = this;
const SkOpAngle* next = first;
SkTDArray<const SkOpAngle*>(angles);
@ -145,422 +337,137 @@ void SkOpAngle::debugValidateNext() const {
return;
}
} while (true);
}
void SkOpAngle::debugValidateLoop() const {
const SkOpAngle* first = this;
const SkOpAngle* next = first;
SK_ALWAYSBREAK(first->next() != first);
int signSum = 0;
int oppSum = 0;
bool firstOperand = fSegment->operand();
bool unorderable = false;
do {
unorderable |= next->fUnorderable;
const SkOpSegment* segment = next->fSegment;
bool operandsMatch = firstOperand == segment->operand();
signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
if (segment->_xor()) {
// SK_ALWAYSBREAK(span.fWindValue == 1);
// SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
}
if (segment->oppXor()) {
SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
// SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
}
next = next->next();
if (!next) {
return;
}
} while (next != first);
if (unorderable) {
return;
}
SK_ALWAYSBREAK(!signSum || fSegment->_xor());
SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
int lastWinding;
int lastOppWinding;
int winding;
int oppWinding;
do {
const SkOpSegment* segment = next->fSegment;
const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
winding = span.fWindSum;
if (winding != SK_MinS32) {
// SK_ALWAYSBREAK(winding != 0);
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
lastWinding = winding;
int diffWinding = segment->spanSign(next);
if (!segment->_xor()) {
SK_ALWAYSBREAK(diffWinding != 0);
bool sameSign = (winding > 0) == (diffWinding > 0);
winding -= sameSign ? diffWinding : -diffWinding;
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
if (!sameSign) {
SkTSwap(winding, lastWinding);
}
}
lastOppWinding = oppWinding = span.fOppSum;
if (oppWinding != SK_MinS32 && !segment->oppXor()) {
int oppDiffWinding = segment->oppSign(next);
// SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
if (oppDiffWinding) {
bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
if (!oppSameSign) {
SkTSwap(oppWinding, lastOppWinding);
}
}
}
firstOperand = segment->operand();
break;
}
SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
next = next->next();
} while (next != first);
if (winding == SK_MinS32) {
return;
}
SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
first = next;
next = next->next();
do {
const SkOpSegment* segment = next->fSegment;
lastWinding = winding;
lastOppWinding = oppWinding;
bool operandsMatch = firstOperand == segment->operand();
if (operandsMatch) {
if (!segment->_xor()) {
winding -= segment->spanSign(next);
SK_ALWAYSBREAK(winding != lastWinding);
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
}
if (!segment->oppXor()) {
int oppDiffWinding = segment->oppSign(next);
if (oppWinding != SK_MinS32) {
oppWinding -= oppDiffWinding;
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
} else {
SK_ALWAYSBREAK(oppDiffWinding == 0);
}
}
} else {
if (!segment->oppXor()) {
winding -= segment->oppSign(next);
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
}
if (!segment->_xor()) {
oppWinding -= segment->spanSign(next);
SK_ALWAYSBREAK(oppWinding != lastOppWinding);
SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
}
}
bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
int sumWinding = useInner ? winding : lastWinding;
bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
if (!operandsMatch) {
SkTSwap(useInner, oppUseInner);
SkTSwap(sumWinding, oppSumWinding);
}
const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
if (winding == -lastWinding) {
if (span.fWindSum != SK_MinS32) {
SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
__FUNCTION__,
useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
}
}
if (oppWinding != SK_MinS32) {
if (span.fOppSum != SK_MinS32) {
SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
}
} else {
SK_ALWAYSBREAK(!firstOperand);
SK_ALWAYSBREAK(!segment->operand());
SK_ALWAYSBREAK(!span.fOppValue);
}
next = next->next();
} while (next != first);
}
#endif
#if DEBUG_SWAP_TOP
bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.controlsContainedByEnds();
}
#endif
#if DEBUG_CONCIDENT
// SK_ALWAYSBREAK if pair has not already been added
void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
for (int i = 0; i < fTs.count(); ++i) {
if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
return;
}
}
SK_ALWAYSBREAK(0);
}
#endif
#if DEBUG_ANGLE
void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
const SkPoint& basePt = fTs[tStart].fPt;
while (++tStart < tEnd) {
const SkPoint& cmpPt = fTs[tStart].fPt;
SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
}
}
#endif
#if DEBUG_SWAP_TOP
int SkOpSegment::debugInflections(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
double inflections[2];
return dst.findInflections(inflections);
}
#endif
const SkOpAngle* SkOpSegment::debugLastAngle() const {
const SkOpAngle* result = NULL;
for (int index = 0; index < count(); ++index) {
const SkOpSpan& span = this->span(index);
if (span.fToAngle) {
SkASSERT(!result);
result = span.fToAngle;
}
}
SkASSERT(result);
return result;
}
void SkOpSegment::debugReset() {
fTs.reset();
fAngles.reset();
}
#if DEBUG_CONCIDENT
void SkOpSegment::debugShowTs(const char* prefix) const {
SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
int lastWind = -1;
int lastOpp = -1;
double lastT = -1;
int i;
for (i = 0; i < fTs.count(); ++i) {
bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
|| lastOpp != fTs[i].fOppValue;
if (change && lastWind >= 0) {
SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
}
if (change) {
SkDebugf(" [o=%d", fTs[i].fOther->fID);
lastWind = fTs[i].fWindValue;
lastOpp = fTs[i].fOppValue;
lastT = fTs[i].fT;
} else {
SkDebugf(",%d", fTs[i].fOther->fID);
}
}
if (i <= 0) {
return;
}
SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
if (fOperand) {
SkDebugf(" operand");
}
if (done()) {
SkDebugf(" done");
}
SkDebugf("\n");
}
#endif
#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void SkOpSegment::debugShowActiveSpans() const {
debugValidate();
if (done()) {
return;
}
#if DEBUG_ACTIVE_SPANS_SHORT_FORM
int lastId = -1;
double lastT = -1;
#endif
for (int i = 0; i < fTs.count(); ++i) {
if (fTs[i].fDone) {
continue;
}
SK_ALWAYSBREAK(i < fTs.count() - 1);
#if DEBUG_ACTIVE_SPANS_SHORT_FORM
if (lastId == fID && lastT == fTs[i].fT) {
continue;
}
lastId = fID;
lastT = fTs[i].fT;
#endif
SkDebugf("%s id=%d", __FUNCTION__, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
const SkOpSpan* span = &fTs[i];
SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
int iEnd = i + 1;
while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
++iEnd;
}
SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
const SkOpSegment* other = fTs[i].fOther;
SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
if (fTs[i].fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", fTs[i].fWindSum);
}
SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
}
}
#endif
#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
(&span)[1].fT, winding);
if (span.fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span.fWindSum);
}
SkDebugf(" windValue=%d\n", span.fWindValue);
}
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
int oppWinding) {
const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
(&span)[1].fT, winding, oppWinding);
if (span.fOppSum == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span.fOppSum);
}
SkDebugf(" windSum=");
if (span.fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span.fWindSum);
}
SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
}
#endif
#if DEBUG_SHOW_WINDING
int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
if (!(1 << fID & ofInterest)) {
return 0;
}
int sum = 0;
SkTArray<char, true> slots(slotCount * 2);
memset(slots.begin(), ' ', slotCount * 2);
for (int i = 0; i < fTs.count(); ++i) {
// if (!(1 << fTs[i].fOther->fID & ofInterest)) {
// continue;
// }
sum += fTs[i].fWindValue;
slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
sum += fTs[i].fOppValue;
slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
}
SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
slots.begin() + slotCount);
return sum;
}
#endif
void SkOpSegment::debugValidate() const {
#if DEBUG_VALIDATE
int count = fTs.count();
SK_ALWAYSBREAK(count >= 2);
SK_ALWAYSBREAK(fTs[0].fT == 0);
SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
const SkOpSpanBase* span = &fHead;
double lastT = -1;
const SkOpSpanBase* prev = NULL;
int count = 0;
int done = 0;
double t = -1;
const SkOpSpan* last = NULL;
bool tinyTFound = false;
bool hasLoop = false;
for (int i = 0; i < count; ++i) {
const SkOpSpan& span = fTs[i];
SK_ALWAYSBREAK(t <= span.fT);
t = span.fT;
int otherIndex = span.fOtherIndex;
const SkOpSegment* other = span.fOther;
SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
const SkOpSpan& otherSpan = other->fTs[otherIndex];
SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
SK_ALWAYSBREAK(otherSpan.fOtherT == t);
SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
done += span.fDone;
if (last) {
SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
bool tsEqual = last->fT == span.fT;
bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
bool pointsEqual = last->fPt == span.fPt;
bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
#if 0 // bufferOverflow test triggers this
SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
#endif
// SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
SK_ALWAYSBREAK(!last->fTiny || last->fDone);
SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
SK_ALWAYSBREAK(!last->fSmall || last->fDone);
// SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
// SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
if (last->fTiny) {
tinyTFound |= !tsPreciselyEqual;
} else {
tinyTFound = false;
}
do {
if (!span->final()) {
++count;
done += span->upCast()->done() ? 1 : 0;
}
last = &span;
hasLoop |= last->fLoop;
}
SK_ALWAYSBREAK(done == fDoneSpans);
// if (fAngles.count() ) {
// fAngles.begin()->debugValidateLoop();
// }
SkASSERT(span->segment() == this);
SkASSERT(!prev || prev->upCast()->next() == span);
SkASSERT(!prev || prev == span->prev());
prev = span;
double t = span->ptT()->fT;
SkASSERT(lastT < t);
lastT = t;
span->debugValidate();
} while (!span->final() && (span = span->upCast()->next()));
SkASSERT(count == fCount);
SkASSERT(done == fDoneCount);
SkASSERT(span->final());
span->debugValidate();
#endif
}
bool SkOpSpanBase::debugCoinEndLoopCheck() const {
int loop = 0;
const SkOpSpanBase* next = this;
SkOpSpanBase* nextCoin;
do {
nextCoin = next->fCoinEnd;
SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
for (int check = 1; check < loop - 1; ++check) {
const SkOpSpanBase* checkCoin = this->fCoinEnd;
const SkOpSpanBase* innerCoin = checkCoin;
for (int inner = check + 1; inner < loop; ++inner) {
innerCoin = innerCoin->fCoinEnd;
if (checkCoin == innerCoin) {
SkDebugf("*** bad coincident end loop ***\n");
return false;
}
}
}
++loop;
} while ((next = nextCoin) && next != this);
return true;
}
void SkOpSpanBase::debugValidate() const {
#if DEBUG_VALIDATE
const SkOpPtT* ptT = &fPtT;
SkASSERT(ptT->span() == this);
do {
// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
ptT->debugValidate();
ptT = ptT->next();
} while (ptT != &fPtT);
SkASSERT(this->debugCoinEndLoopCheck());
if (!this->final()) {
SkASSERT(this->upCast()->debugCoinLoopCheck());
}
if (fFromAngle) {
fFromAngle->debugValidate();
}
if (!this->final() && this->upCast()->toAngle()) {
this->upCast()->toAngle()->debugValidate();
}
#endif
}
bool SkOpSpan::debugCoinLoopCheck() const {
int loop = 0;
const SkOpSpan* next = this;
SkOpSpan* nextCoin;
do {
nextCoin = next->fCoincident;
SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
for (int check = 1; check < loop - 1; ++check) {
const SkOpSpan* checkCoin = this->fCoincident;
const SkOpSpan* innerCoin = checkCoin;
for (int inner = check + 1; inner < loop; ++inner) {
innerCoin = innerCoin->fCoincident;
if (checkCoin == innerCoin) {
SkDebugf("*** bad coincident loop ***\n");
return false;
}
}
}
++loop;
} while ((next = nextCoin) && next != this);
return true;
}
#include "SkOpContour.h"
int SkOpPtT::debugLoopLimit(bool report) const {
int loop = 0;
const SkOpPtT* next = this;
do {
for (int check = 1; check < loop - 1; ++check) {
const SkOpPtT* checkPtT = this->fNext;
const SkOpPtT* innerPtT = checkPtT;
for (int inner = check + 1; inner < loop; ++inner) {
innerPtT = innerPtT->fNext;
if (checkPtT == innerPtT) {
if (report) {
SkDebugf("*** bad ptT loop ***\n");
}
return loop;
}
}
}
++loop;
} while ((next = next->fNext) && next != this);
return 0;
}
void SkOpPtT::debugValidate() const {
#if DEBUG_VALIDATE
if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
return;
}
SkASSERT(fNext);
SkASSERT(fNext != this);
SkASSERT(fNext->fNext);
SkASSERT(debugLoopLimit(false) == 0);
#endif
}

View File

@ -39,35 +39,22 @@
#define DEBUG_ACTIVE_OP 0
#define DEBUG_ACTIVE_SPANS 0
#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_ADD_T_PAIR 0
#define DEBUG_ADD_T 0
#define DEBUG_ANGLE 0
#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 0
#define DEBUG_CHECK_ALIGN 0
#define DEBUG_CHECK_TINY 0
#define DEBUG_CONCIDENT 0
#define DEBUG_CROSS 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_DUPLICATES 0
#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 0
#define DEBUG_LIMIT_WIND_SUM 0
#define DEBUG_MARK_DONE 0
#define DEBUG_PATH_CONSTRUCTION 0
#define DEBUG_PERP 0
#define DEBUG_SHOW_TEST_NAME 0
#define DEBUG_SHOW_TEST_PROGRESS 0
#define DEBUG_SHOW_WINDING 0
#define DEBUG_SORT 0
#define DEBUG_SORT_COMPACT 0
#define DEBUG_SORT_RAW 0
#define DEBUG_SORT_SINGLE 0
#define DEBUG_SWAP_TOP 0
#define DEBUG_UNSORTABLE 0
#define DEBUG_T_SECT 0
#define DEBUG_T_SECT_DUMP 0
#define DEBUG_VALIDATE 0
#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 0
#define DEBUG_WINDING_AT_T 0
@ -75,51 +62,56 @@
#define DEBUG_ACTIVE_OP 1
#define DEBUG_ACTIVE_SPANS 1
#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
#define DEBUG_ADD_INTERSECTING_TS 1
#define DEBUG_ADD_T_PAIR 1
#define DEBUG_ADD_T 1
#define DEBUG_ANGLE 1
#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 1
#define DEBUG_CHECK_ALIGN 1
#define DEBUG_CHECK_TINY 1
#define DEBUG_CONCIDENT 1
#define DEBUG_CROSS 01
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_DUPLICATES 1
#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 1
#define DEBUG_LIMIT_WIND_SUM 4
#define DEBUG_LIMIT_WIND_SUM 5
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_PERP 0
#define DEBUG_SHOW_TEST_NAME 1
#define DEBUG_SHOW_TEST_PROGRESS 1
#define DEBUG_SHOW_WINDING 0
#define DEBUG_SORT 1
#define DEBUG_SORT_COMPACT 0
#define DEBUG_SORT_RAW 0
#define DEBUG_SORT_SINGLE 0
#define DEBUG_SWAP_TOP 1
#define DEBUG_UNSORTABLE 1
#define DEBUG_VALIDATE 0
#define DEBUG_WIND_BUMP 0
#define DEBUG_T_SECT 1
#define DEBUG_T_SECT_DUMP 02
#define DEBUG_VALIDATE 1
#define DEBUG_WINDING 1
#define DEBUG_WINDING_AT_T 1
#endif
#if DEBUG_AS_C_CODE
#define CUBIC_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
#define QUAD_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
#define LINE_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}}"
#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
#ifdef SK_RELEASE
#define PATH_OPS_DEBUG_RELEASE(a, b) b
#define PATH_OPS_DEBUG_CODE(...)
#define PATH_OPS_DEBUG_PARAMS(...)
#else
#define CUBIC_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
#define QUAD_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
#define LINE_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g)"
#define PT_DEBUG_STR "(%1.9g,%1.9g)"
#define PATH_OPS_DEBUG_RELEASE(a, b) a
#define PATH_OPS_DEBUG_CODE(...) __VA_ARGS__
#define PATH_OPS_DEBUG_PARAMS(...) , __VA_ARGS__
#endif
#if DEBUG_T_SECT == 0
#define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b
#define PATH_OPS_DEBUG_T_SECT_PARAMS(...)
#define PATH_OPS_DEBUG_T_SECT_CODE(...)
#else
#define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) a
#define PATH_OPS_DEBUG_T_SECT_PARAMS(...) , __VA_ARGS__
#define PATH_OPS_DEBUG_T_SECT_CODE(...) __VA_ARGS__
#endif
#if DEBUG_T_SECT_DUMP > 1
extern int gDumpTSectNum;
#endif
#define CUBIC_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
#define QUAD_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
#define LINE_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
#define T_DEBUG_STR(t, n) #t "[" #n "]=%1.9g"
#define TX_DEBUG_STR(t) #t "[%d]=%1.9g"
#define CUBIC_DEBUG_DATA(c) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY
@ -135,7 +127,6 @@
#include "SkTLS.h"
#endif
#include "SkTArray.h"
#include "SkTDArray.h"
class SkPathOpsDebug {
@ -156,7 +147,6 @@ public:
static const char* kPathOpStr[];
#endif
static bool ChaseContains(const SkTDArray<struct SkOpSpan *>& , const struct SkOpSpan * );
static void MathematicaIze(char* str, size_t bufferSize);
static bool ValidWind(int winding);
static void WindingPrintf(int winding);
@ -171,66 +161,96 @@ public:
#endif
static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
static void DumpCoincidence(const SkTArray<class SkOpContour, true>& contours);
static void DumpCoincidence(const SkTArray<class SkOpContour* , true>& contours);
static void DumpContours(const SkTArray<class SkOpContour, true>& contours);
static void DumpContours(const SkTArray<class SkOpContour* , true>& contours);
static void DumpContourAngles(const SkTArray<class SkOpContour, true>& contours);
static void DumpContourAngles(const SkTArray<class SkOpContour* , true>& contours);
static void DumpContourPt(const SkTArray<class SkOpContour, true>& contours, int id);
static void DumpContourPt(const SkTArray<class SkOpContour* , true>& contours, int id);
static void DumpContourPts(const SkTArray<class SkOpContour, true>& contours);
static void DumpContourPts(const SkTArray<class SkOpContour* , true>& contours);
static void DumpContourSpan(const SkTArray<class SkOpContour, true>& contours, int id);
static void DumpContourSpan(const SkTArray<class SkOpContour* , true>& contours, int id);
static void DumpContourSpans(const SkTArray<class SkOpContour, true>& contours);
static void DumpContourSpans(const SkTArray<class SkOpContour* , true>& contours);
static void DumpSpans(const SkTDArray<struct SkOpSpan *>& );
static void DumpSpans(const SkTDArray<struct SkOpSpan *>* );
static bool ChaseContains(const SkTDArray<class SkOpSpanBase*>& , const class SkOpSpanBase* );
static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
static const class SkOpSpanBase* DebugSpanSpan(const class SkOpSpanBase*, int id);
static void DumpContours(SkTDArray<class SkOpContour* >* contours);
static void DumpContoursAll(SkTDArray<class SkOpContour* >* contours);
static void DumpContoursAngles(const SkTDArray<class SkOpContour* >* contours);
static void DumpContoursPt(const SkTDArray<class SkOpContour* >* contours, int id);
static void DumpContoursPts(const SkTDArray<class SkOpContour* >* contours);
static void DumpContoursSegment(const SkTDArray<class SkOpContour* >* contours, int id);
static void DumpContoursSpan(const SkTDArray<class SkOpContour* >* contours, int id);
static void DumpContoursSpans(const SkTDArray<class SkOpContour* >* contours);
};
// shorthand for calling from debugger
void Dump(const SkTArray<class SkOpContour, true>& contours);
void Dump(const SkTArray<class SkOpContour* , true>& contours);
void Dump(const SkTArray<class SkOpContour, true>* contours);
void Dump(const SkTArray<class SkOpContour* , true>* contours);
template<typename TCurve> class SkTSect;
template<typename TCurve> class SkTSpan;
void Dump(const SkTDArray<SkOpSpan* >& chase);
void Dump(const SkTDArray<SkOpSpan* >* chase);
struct SkDQuad;
struct SkDCubic;
void DumpAngles(const SkTArray<class SkOpContour, true>& contours);
void DumpAngles(const SkTArray<class SkOpContour* , true>& contours);
void DumpAngles(const SkTArray<class SkOpContour, true>* contours);
void DumpAngles(const SkTArray<class SkOpContour* , true>* contours);
const SkTSpan<SkDCubic>* DebugSpan(const SkTSect<SkDCubic>* , int id);
const SkTSpan<SkDQuad>* DebugSpan(const SkTSect<SkDQuad>* , int id);
const SkTSpan<SkDCubic>* DebugT(const SkTSect<SkDCubic>* , double t);
const SkTSpan<SkDQuad>* DebugT(const SkTSect<SkDQuad>* , double t);
void DumpCoin(const SkTArray<class SkOpContour, true>& contours);
void DumpCoin(const SkTArray<class SkOpContour* , true>& contours);
void DumpCoin(const SkTArray<class SkOpContour, true>* contours);
void DumpCoin(const SkTArray<class SkOpContour* , true>* contours);
const SkTSpan<SkDCubic>* DebugSpan(const SkTSpan<SkDCubic>* , int id);
const SkTSpan<SkDQuad>* DebugSpan(const SkTSpan<SkDQuad>* , int id);
const SkTSpan<SkDCubic>* DebugT(const SkTSpan<SkDCubic>* , double t);
const SkTSpan<SkDQuad>* DebugT(const SkTSpan<SkDQuad>* , double t);
void DumpPts(const SkTArray<class SkOpContour, true>& contours);
void DumpPts(const SkTArray<class SkOpContour* , true>& contours);
void DumpPts(const SkTArray<class SkOpContour, true>* contours);
void DumpPts(const SkTArray<class SkOpContour* , true>* contours);
void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID);
void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID);
void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
void DumpSpans(const SkTArray<class SkOpContour, true>& contours);
void DumpSpans(const SkTArray<class SkOpContour* , true>& contours);
void DumpSpans(const SkTArray<class SkOpContour, true>* contours);
void DumpSpans(const SkTArray<class SkOpContour* , true>* contours);
void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID);
void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID);
void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
void Dump(const SkTSect<SkDCubic>* );
void Dump(const SkTSect<SkDQuad>* );
void Dump(const SkTSpan<SkDCubic>* , const SkTSect<SkDCubic>* = NULL);
void Dump(const SkTSpan<SkDQuad>* , const SkTSect<SkDQuad>* = NULL);
void DumpBoth(SkTSect<SkDCubic>* sect1, SkTSect<SkDCubic>* sect2);
void DumpBoth(SkTSect<SkDQuad>* sect1, SkTSect<SkDQuad>* sect2);
void DumpCoin(SkTSect<SkDCubic>* sect1);
void DumpCoin(SkTSect<SkDQuad>* sect1);
void DumpCoinCurves(SkTSect<SkDCubic>* sect1);
void DumpCoinCurves(SkTSect<SkDQuad>* sect1);
void DumpCurves(const SkTSpan<SkDCubic>* );
void DumpCurves(const SkTSpan<SkDQuad>* );
// generates tools/path_sorter.htm and path_visualizer.htm compatible data
void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
void DumpT(const SkDQuad& quad, double t);
void DumpT(const struct SkDQuad& quad, double t);
const struct SkOpAngle* DebugAngle(const SkTDArray<class SkOpContour* >* contours, int id);
class SkOpContour* DebugContour(const SkTDArray<class SkOpContour* >* contours, int id);
const class SkOpPtT* DebugPtT(const SkTDArray<class SkOpContour* >* contours, int id);
const class SkOpSegment* DebugSegment(const SkTDArray<class SkOpContour* >* contours, int id);
const class SkOpSpanBase* DebugSpan(const SkTDArray<class SkOpContour* >* contours, int id);
void Dump(const SkTDArray<class SkOpContour* >* contours);
void DumpAll(SkTDArray<class SkOpContour* >* contours);
void DumpAngles(const SkTDArray<class SkOpContour* >* contours);
void DumpCoin(const SkTDArray<class SkOpContour* >* contours);
void DumpPt(const SkTDArray<class SkOpContour* >* contours, int segmentID);
void DumpPts(const SkTDArray<class SkOpContour* >* contours);
void DumpSegment(const SkTDArray<class SkOpContour* >* contours, int segmentID);
void DumpSpan(const SkTDArray<class SkOpContour* >* contours, int spanID);
void DumpSpans(const SkTDArray<class SkOpContour* >* contours);
#endif

View File

@ -6,14 +6,6 @@
*/
#include "SkPathOpsLine.h"
SkDLine SkDLine::subDivide(double t1, double t2) const {
SkDVector delta = tangent();
SkDLine dst = {{{
fPts[0].fX - t1 * delta.fX, fPts[0].fY - t1 * delta.fY}, {
fPts[0].fX - t2 * delta.fX, fPts[0].fY - t2 * delta.fY}}};
return dst;
}
// may have this below somewhere else already:
// copying here because I thought it was clever
@ -28,6 +20,7 @@ SkDLine SkDLine::subDivide(double t1, double t2) const {
// Point with coordinates {float x, y;}
//===================================================================
// (only used by testing)
// isLeft(): tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and P1
@ -110,19 +103,6 @@ bool SkDLine::nearRay(const SkDPoint& xy) const {
return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2)
// OPTIMIZE: a specialty routine could speed this up -- may not be called very often though
bool SkDLine::NearRay(double x1, double y1, double x2, double y2) {
double denom1 = x1 * x1 + y1 * y1;
double denom2 = x2 * x2 + y2 * y2;
SkDLine line = {{{0, 0}, {x1, y1}}};
SkDPoint pt = {x2, y2};
if (denom2 > denom1) {
SkTSwap(line[1], pt);
}
return line.nearRay(pt);
}
double SkDLine::ExactPointH(const SkDPoint& xy, double left, double right, double y) {
if (xy.fY == y) {
if (xy.fX == left) {

View File

@ -20,27 +20,20 @@ struct SkDLine {
fPts[1] = pts[1];
}
static SkDLine SubDivide(const SkPoint a[2], double t1, double t2) {
SkDLine line;
line.set(a);
return line.subDivide(t1, t2);
}
double exactPoint(const SkDPoint& xy) const;
static double ExactPointH(const SkDPoint& xy, double left, double right, double y);
static double ExactPointV(const SkDPoint& xy, double top, double bottom, double x);
// only used by testing
double isLeft(const SkDPoint& pt) const;
double nearPoint(const SkDPoint& xy, bool* unequal) const;
bool nearRay(const SkDPoint& xy) const;
static double NearPointH(const SkDPoint& xy, double left, double right, double y);
static double NearPointV(const SkDPoint& xy, double top, double bottom, double x);
static bool NearRay(double dx1, double dy1, double dx2, double dy2);
SkDPoint ptAtT(double t) const;
SkDLine subDivide(double t1, double t2) const;
void dump() const;
private:
SkDVector tangent() const { return fPts[0] - fPts[1]; }
};
#endif

View File

@ -5,27 +5,29 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr) {
while (chase.count()) {
SkOpSpan* span;
SkOpSpanBase* span;
chase.pop(&span);
const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
SkOpSegment* segment = backPtr.fOther;
*tIndex = backPtr.fOtherIndex;
// OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
*startPtr = span->ptT()->prev()->span();
SkOpSegment* segment = (*startPtr)->segment();
bool sortable = true;
bool done = true;
*endIndex = -1;
if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
*endPtr = NULL;
if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
&sortable)) {
if (last->unorderable()) {
continue;
}
*tIndex = last->start();
*endIndex = last->end();
*startPtr = last->start();
*endPtr = last->end();
#if TRY_ROTATE
*chase.insert(0) = span;
#else
@ -40,7 +42,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
continue;
}
// find first angle, initialize winding to computed fWindSum
const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
if (!angle) {
continue;
}
@ -65,33 +67,25 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
SkOpSegment* first = NULL;
bool badData = false;
while ((angle = angle->next()) != firstAngle && !badData) {
firstAngle = angle;
while ((angle = angle->next()) != firstAngle) {
segment = angle->segment();
int start = angle->start();
int end = angle->end();
SkOpSpanBase* start = angle->start();
SkOpSpanBase* end = angle->end();
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
if (!segment->done(angle)) {
if (!first) {
first = segment;
*tIndex = start;
*endIndex = end;
}
if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
oppSumWinding, angle)) {
badData = true;
break;
*startPtr = start;
*endPtr = end;
}
// OPTIMIZATION: should this also add to the chase?
(void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
oppSumWinding, angle);
}
}
if (badData) {
continue;
}
if (first) {
#if TRY_ROTATE
*chase.insert(0) = span;
@ -104,36 +98,8 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
return NULL;
}
/*
static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
bool windingIsOp, PathOp op) {
bool active = windingIsActive(winding, spanWinding);
if (!active) {
return false;
}
if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
switch (op) {
case kIntersect_Op:
case kUnion_Op:
return true;
case kDifference_Op: {
int absSpan = abs(spanWinding);
int absOpp = abs(oppSpanWinding);
return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
}
case kXor_Op:
return spanWinding != oppSpanWinding;
default:
SkASSERT(0);
}
}
bool opActive = oppWinding != 0;
return gOpLookup[op][opActive][windingIsOp];
}
*/
static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
const int xorMask, const int xorOpMask, SkPathWriter* simple) {
static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@ -141,12 +107,14 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
int index, endIndex;
SkOpSpanBase* start;
SkOpSpanBase* end;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
&index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kBinarySingle,
&firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
allocator);
if (!current) {
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
@ -165,69 +133,65 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
SkTDArray<SkOpSpan*> chase;
SkTDArray<SkOpSpanBase*> chase;
do {
if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
int nextStart = index;
int nextEnd = endIndex;
SkOpSpanBase* nextStart = start;
SkOpSpanBase* nextEnd = end;
SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
&unsortable, op, xorMask, xorOpMask);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
current->addCurveTo(index, endIndex, simple, true);
current->addCurveTo(start, end, simple, true);
#if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
DebugShowActiveSpans(contourList);
}
#endif
// SkASSERT(simple->isClosed());
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), start->pt().fX, start->pt().fY,
end->pt().fX, end->pt().fY);
#endif
current->addCurveTo(index, endIndex, simple, true);
current->addCurveTo(start, end, simple, true);
current = next;
index = nextStart;
endIndex = nextEnd;
} while (!simple->isClosed() && (!unsortable
|| !current->done(SkMin32(index, endIndex))));
if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
// FIXME : add to simplify, xor cpaths
int min = SkMin32(index, endIndex);
if (!unsortable && !simple->isEmpty()) {
unsortable = current->checkSmall(min);
}
if (!current->done(min)) {
current->addCurveTo(index, endIndex, simple, true);
current->markDoneBinary(min);
start = nextStart;
end = nextEnd;
} while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
if (current->activeWinding(start, end) && !simple->isClosed()) {
SkOpSpan* spanStart = start->starter(end);
if (!spanStart->done()) {
current->addCurveTo(start, end, simple, true);
current->markDone(spanStart);
}
}
simple->close();
} else {
SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
if (last && !last->fChased && !last->fLoop) {
last->fChased = true;
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
last->setChased(true);
SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
*chase.append() = last;
#if DEBUG_WINDING
SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
last->fSmall);
SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
if (!last->final()) {
SkDebugf(" windSum=%d", last->upCast()->windSum());
}
SkDebugf("\n");
#endif
}
}
current = findChaseOp(chase, &index, &endIndex);
current = findChaseOp(chase, &start, &end);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@ -291,16 +255,19 @@ static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
dump_path(file, two, false, true);
fprintf(file, " SkPath path2(path);\n");
fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
fprintf(file, "}\n");
fprintf(file, "}\n");
fclose(file);
}
#endif
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
SkOpContour contour;
SkOpCoincidence coincidence;
SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
#endif
#if DEBUG_SHOW_TEST_NAME
#endif
#if 0 && DEBUG_SHOW_TEST_NAME
char* debugName = DEBUG_FILENAME_STRING;
if (debugName && debugName[0]) {
SkPathOpsDebug::BumpTestName(debugName);
@ -321,53 +288,54 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
SkTArray<SkOpContour> contours;
// FIXME: add self-intersecting cubics' T values to segment
SkOpEdgeBuilder builder(*minuend, contours);
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
if (builder.unparseable()) {
return false;
}
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
if (!builder.finish()) {
if (!builder.finish(&allocator)) {
return false;
}
#if !FORCE_RELEASE
contour.dumpSegments(op);
#endif
result->reset();
result->setFillType(fillType);
const int xorOpMask = builder.xorMask();
SkTArray<SkOpContour*, true> contourList;
MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
SkTDArray<SkOpContour* > contourList;
MakeContourList(&contour, contourList, xorMask == kEvenOdd_PathOpsMask,
xorOpMask == kEvenOdd_PathOpsMask);
SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
return true;
}
if ((*currentPtr)->count() == 0) {
SkASSERT((*currentPtr)->next() == NULL);
return true;
}
SkOpContour** listEnd = contourList.end();
// find all intersections between segments
do {
SkOpContour** nextPtr = currentPtr;
SkOpContour* current = *currentPtr++;
if (current->containsCubics()) {
AddSelfIntersectTs(current);
}
SkOpContour* next;
do {
next = *nextPtr++;
} while (AddIntersectTs(current, next) && nextPtr != listEnd);
} while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd);
} while (currentPtr != listEnd);
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
// eat through coincident edges
int total = 0;
int index;
for (index = 0; index < contourList.count(); ++index) {
total += contourList[index]->segments().count();
}
if (!HandleCoincidence(&contourList, total)) {
if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
return false;
}
// construct closed contours
SkPathWriter wrapper(*result);
bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);

View File

@ -25,21 +25,25 @@ struct SkDVector {
friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);
// only used by testing
void operator+=(const SkDVector& v) {
fX += v.fX;
fY += v.fY;
}
// only called by nearestT, which is currently only used by testing
void operator-=(const SkDVector& v) {
fX -= v.fX;
fY -= v.fY;
}
// only used by testing
void operator/=(const double s) {
fX /= s;
fY /= s;
}
// only used by testing
void operator*=(const double s) {
fX *= s;
fY *= s;
@ -50,6 +54,7 @@ struct SkDVector {
return v;
}
// only used by testing
double cross(const SkDVector& a) const {
return fX * a.fY - fY * a.fX;
}
@ -98,11 +103,13 @@ struct SkDPoint {
fY = pt.fY;
}
// only used by testing
void operator+=(const SkDVector& v) {
fX += v.fX;
fY += v.fY;
}
// only used by testing
void operator-=(const SkDVector& v) {
fX -= v.fX;
fY -= v.fY;
@ -122,7 +129,7 @@ struct SkDPoint {
double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
largest = SkTMax(largest, -tiniest);
return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
bool approximatelyEqual(const SkPoint& a) const {
@ -145,44 +152,10 @@ struct SkDPoint {
float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
largest = SkTMax(largest, -tiniest);
return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
return true;
}
return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
}
bool approximatelyPEqual(const SkDPoint& a) const {
if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
return true;
}
if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
return false;
}
double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
largest = SkTMax(largest, -tiniest);
return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
bool approximatelyDEqual(const SkDPoint& a) const {
if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
return true;
}
if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
return false;
}
double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
largest = SkTMax(largest, -tiniest);
return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
// only used by testing
bool approximatelyZero() const {
return approximately_zero(fX) && approximately_zero(fY);
}
@ -209,7 +182,7 @@ struct SkDPoint {
return result;
}
bool moreRoughlyEqual(const SkDPoint& a) const {
bool roughlyEqual(const SkDPoint& a) const {
if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
return true;
}
@ -220,10 +193,6 @@ struct SkDPoint {
return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
bool roughlyEqual(const SkDPoint& a) const {
return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
}
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
static void Dump(const SkPoint& pt);

27
src/pathops/SkPathOpsPostSect.cpp Normal file → Executable file
View File

@ -17,8 +17,8 @@ SkOpContour* SkOpPtT::contour() const {
return segment()->contour();
}
SkOpDebugState* SkOpPtT::debugState() const {
return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
SkOpGlobalState* SkOpPtT::globalState() const {
return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL);
}
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@ -28,7 +28,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
fNext = this;
fDuplicatePt = duplicate;
fDeleted = false;
PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
}
bool SkOpPtT::onEnd() const {
@ -45,7 +45,7 @@ SkOpPtT* SkOpPtT::remove() {
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
prev->removeNext();
prev->removeNext(this);
fDeleted = true;
return prev;
}
@ -55,14 +55,14 @@ SkOpPtT* SkOpPtT::remove() {
return NULL;
}
void SkOpPtT::removeNext() {
void SkOpPtT::removeNext(SkOpPtT* kept) {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
next->setDeleted();
if (span->ptT() == next) {
span->upCast()->detach();
span->upCast()->detach(kept);
}
}
@ -199,7 +199,7 @@ void SkOpSpanBase::alignInner() {
// omit aliases that alignment makes redundant
if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
SkASSERT(test->alias());
prev->removeNext();
prev->removeNext(ptT);
test = prev;
} else {
SkASSERT(ptT->alias());
@ -239,8 +239,8 @@ SkOpContour* SkOpSpanBase::contour() const {
return segment()->contour();
}
SkOpDebugState* SkOpSpanBase::debugState() const {
return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
SkOpGlobalState* SkOpSpanBase::globalState() const {
return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL);
}
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@ -252,7 +252,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
fAligned = true;
fChased = false;
PATH_OPS_DEBUG_CODE(fCount = 1);
PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
PATH_OPS_DEBUG_CODE(fID = ++globalState()->fSpanID);
}
// this pair of spans share a common t value or point; merge them and eliminate duplicates
@ -261,7 +261,7 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
SkOpPtT* spanPtT = span->ptT();
SkASSERT(this->t() != spanPtT->fT);
SkASSERT(!zero_or_one(spanPtT->fT));
span->detach();
span->detach(this->ptT());
SkOpPtT* remainder = spanPtT->next();
ptT()->insert(spanPtT);
while (remainder != spanPtT) {
@ -304,7 +304,7 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
return false;
}
void SkOpSpan::detach() {
void SkOpSpan::detach(SkOpPtT* kept) {
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@ -313,6 +313,9 @@ void SkOpSpan::detach() {
prev->setNext(next);
next->setPrev(prev);
this->segment()->detach(this);
if (this->coincident()) {
this->globalState()->fCoincidence->fixUp(this->ptT(), kept);
}
this->ptT()->setDeleted();
}

View File

@ -8,7 +8,61 @@
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
#include "SkPathOpsTriangle.h"
/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
// Do a quick reject by rotating all points relative to a line formed by
// a pair of one quad's points. If the 2nd quad's points
// are on the line or on the opposite side from the 1st quad's 'odd man', the
// curves at most intersect at the endpoints.
/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
if returning false, check contains true if the the quad pair have only the end point in common
*/
bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
bool linear = true;
for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
const SkDPoint* endPt[2];
this->otherPts(oddMan, endPt);
double origX = endPt[0]->fX;
double origY = endPt[0]->fY;
double adj = endPt[1]->fX - origX;
double opp = endPt[1]->fY - origY;
double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
if (approximately_zero(sign)) {
continue;
}
linear = false;
bool foundOutlier = false;
for (int n = 0; n < kPointCount; ++n) {
double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
if (test * sign > 0 && !precisely_zero(test)) {
foundOutlier = true;
break;
}
}
if (!foundOutlier) {
return false;
}
}
*isLinear = linear;
return true;
}
/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
oddMan opp x=oddMan^opp x=x-oddMan m=x>>2 x&~m
0 1 1 1 0 1
2 2 2 0 2
1 1 0 -1 -1 0
2 3 2 0 2
2 1 3 1 0 1
2 0 -2 -1 0
*/
void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
for (int opp = 1; opp < kPointCount; ++opp) {
int end = (oddMan ^ opp) - oddMan; // choose a value not equal to oddMan
end &= ~(end >> 2); // if the value went negative, set it to zero
endPt[opp - 1] = &fPts[end];
}
}
// from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
// (currently only used by testing)
@ -43,10 +97,6 @@ double SkDQuad::nearestT(const SkDPoint& pt) const {
return d0 < d2 ? 0 : 1;
}
bool SkDQuad::pointInHull(const SkDPoint& pt) const {
return ((const SkDTriangle&) fPts).contains(pt);
}
SkDPoint SkDQuad::top(double startT, double endT) const {
SkDQuad sub = subDivide(startT, endT);
SkDPoint topPt = sub[0];
@ -140,7 +190,12 @@ bool SkDQuad::isLinear(int startIndex, int endIndex) const {
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
double distance = lineParameters.controlPtDistance(*this);
return approximately_zero(distance);
double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
largest = SkTMax(largest, -tiniest);
return approximately_zero_when_compared_to(distance, largest);
}
SkDCubic SkDQuad::toCubic() const {
@ -240,13 +295,6 @@ void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
SkASSERT(t1 != t2);
SkDPoint b;
#if 0
// this approach assumes that the control point computed directly is accurate enough
double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
b.fX = 2 * dx - (a.fX + c.fX) / 2;
b.fY = 2 * dy - (a.fY + c.fY) / 2;
#else
SkDQuad sub = subDivide(t1, t2);
SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
@ -258,7 +306,6 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
SkASSERT(i.used() <= 2);
b = SkDPoint::Mid(b0[1], b1[1]);
}
#endif
if (t1 == 0 || t2 == 0) {
align(0, &b);
}

View File

@ -17,43 +17,61 @@ struct SkDQuadPair {
};
struct SkDQuad {
SkDPoint fPts[3];
static const int kPointCount = 3;
static const int kPointLast = kPointCount - 1;
static const int kMaxIntersections = 4;
SkDPoint fPts[kPointCount];
bool collapsed() const {
return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2]);
}
bool controlsInside() const {
SkDVector v01 = fPts[0] - fPts[1];
SkDVector v02 = fPts[0] - fPts[2];
SkDVector v12 = fPts[1] - fPts[2];
return v02.dot(v01) > 0 && v02.dot(v12) > 0;
}
SkDQuad flip() const {
SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
return result;
}
void set(const SkPoint pts[3]) {
static bool IsCubic() { return false; }
void set(const SkPoint pts[kPointCount]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
}
const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
static int AddValidTs(double s[], int realRoots, double* t);
void align(int endIndex, SkDPoint* dstPt) const;
SkDQuadPair chopAt(double t) const;
SkDVector dxdyAtT(double t) const;
static int FindExtrema(double a, double b, double c, double tValue[1]);
bool hullIntersects(const SkDQuad& , bool* isLinear) const;
bool isLinear(int startIndex, int endIndex) const;
bool monotonicInY() const;
double nearestT(const SkDPoint&) const;
bool pointInHull(const SkDPoint&) const;
void otherPts(int oddMan, const SkDPoint* endPt[2]) const;
SkDPoint ptAtT(double t) const;
static int RootsReal(double A, double B, double C, double t[2]);
static int RootsValidT(const double A, const double B, const double C, double s[2]);
static void SetABC(const double* quad, double* a, double* b, double* c);
SkDQuad subDivide(double t1, double t2) const;
static SkDQuad SubDivide(const SkPoint a[3], double t1, double t2) {
static SkDQuad SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
SkDQuad quad;
quad.set(a);
return quad.subDivide(t1, t2);
}
SkDPoint subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const;
static SkDPoint SubDivide(const SkPoint pts[3], const SkDPoint& a, const SkDPoint& c,
static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
double t1, double t2) {
SkDQuad quad;
quad.set(pts);
@ -64,7 +82,8 @@ struct SkDQuad {
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
void dumpComma(const char*) const;
void dumpID(int id) const;
void dumpInner() const;
private:
// static double Tangent(const double* quadratic, double t); // uncalled

View File

@ -1,175 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkQuadSpan_DEFINE
#define SkQuadSpan_DEFINE
#include "SkChunkAlloc.h"
#include "SkPathOpsRect.h"
#include "SkPathOpsQuad.h"
#include "SkTArray.h"
class SkIntersections;
class SkQuadCoincident {
public:
bool isCoincident() const {
return fCoincident;
}
void init() {
fCoincident = false;
SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
SkDEBUGCODE(fPerpT = SK_ScalarNaN);
}
void markCoincident() {
if (!fCoincident) {
fPerpT = -1;
}
fCoincident = true;
}
const SkDPoint& perpPt() const {
return fPerpPt;
}
double perpT() const {
return fPerpT;
}
void setPerp(const SkDQuad& quad1, double t, const SkDPoint& qPt, const SkDQuad& quad2);
private:
SkDPoint fPerpPt;
double fPerpT; // perpendicular intersection on opposite quad
bool fCoincident;
};
class SkQuadSect; // used only by debug id
class SkQuadSpan {
public:
void init(const SkDQuad& quad);
void initBounds(const SkDQuad& quad);
bool contains(double t) const {
return !! const_cast<SkQuadSpan*>(this)->innerFind(t);
}
bool contains(const SkQuadSpan* span) const;
SkQuadSpan* find(double t) {
SkQuadSpan* result = innerFind(t);
SkASSERT(result);
return result;
}
bool intersects(const SkQuadSpan* span) const;
const SkQuadSpan* next() const {
return fNext;
}
void reset() {
fBounded.reset();
}
bool split(SkQuadSpan* work) {
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
}
bool splitAt(SkQuadSpan* work, double t);
bool tightBoundsIntersects(const SkQuadSpan* span) const;
// implementation is for testing only
void dump() const;
private:
bool hullIntersects(const SkDQuad& q2) const;
SkQuadSpan* innerFind(double t);
bool linearIntersects(const SkDQuad& q2) const;
// implementation is for testing only
#if DEBUG_BINARY_QUAD
int debugID(const SkQuadSect* ) const { return fDebugID; }
#else
int debugID(const SkQuadSect* ) const;
#endif
void dump(const SkQuadSect* ) const;
void dumpID(const SkQuadSect* ) const;
#if DEBUG_BINARY_QUAD
void validate() const;
#endif
SkDQuad fPart;
SkQuadCoincident fCoinStart;
SkQuadCoincident fCoinEnd;
SkSTArray<4, SkQuadSpan*, true> fBounded;
SkQuadSpan* fPrev;
SkQuadSpan* fNext;
SkDRect fBounds;
double fStartT;
double fEndT;
double fBoundsMax;
bool fCollapsed;
bool fHasPerp;
mutable bool fIsLinear;
#if DEBUG_BINARY_QUAD
int fDebugID;
bool fDebugDeleted;
#endif
friend class SkQuadSect;
};
class SkQuadSect {
public:
SkQuadSect(const SkDQuad& quad PATH_OPS_DEBUG_PARAMS(int id));
static void BinarySearch(SkQuadSect* sect1, SkQuadSect* sect2, SkIntersections* intersections);
// for testing only
void dumpQuads() const;
private:
SkQuadSpan* addOne();
bool binarySearchCoin(const SkQuadSect& , double tStart, double tStep, double* t, double* oppT);
SkQuadSpan* boundsMax() const;
void coincidentCheck(SkQuadSect* sect2);
bool intersects(const SkQuadSpan* span, const SkQuadSect* opp, const SkQuadSpan* oppSpan) const;
void onCurveCheck(SkQuadSect* sect2, SkQuadSpan* first, SkQuadSpan* last);
void recoverCollapsed();
void removeSpan(SkQuadSpan* span);
void removeOne(const SkQuadSpan* test, SkQuadSpan* span);
void removeSpans(SkQuadSpan* span, SkQuadSect* opp);
void setPerp(const SkDQuad& opp, SkQuadSpan* first, SkQuadSpan* last);
const SkQuadSpan* tail() const;
void trim(SkQuadSpan* span, SkQuadSect* opp);
// for testing only
void dump() const;
void dumpBoth(const SkQuadSect& opp) const;
void dumpBoth(const SkQuadSect* opp) const;
#if DEBUG_BINARY_QUAD
int debugID() const { return fDebugID; }
void validate() const;
#else
int debugID() const { return 0; }
#endif
const SkDQuad& fQuad;
SkChunkAlloc fHeap;
SkQuadSpan* fHead;
SkQuadSpan* fDeleted;
int fActiveCount;
#if DEBUG_BINARY_QUAD
int fDebugID;
int fDebugCount;
int fDebugAllocatedCount;
#endif
friend class SkQuadSpan; // only used by debug id
};
#endif

View File

@ -9,11 +9,6 @@
#include "SkPathOpsQuad.h"
#include "SkPathOpsRect.h"
void SkDRect::setBounds(const SkDLine& line) {
set(line[0]);
add(line[1]);
}
void SkDRect::setBounds(const SkDQuad& quad) {
set(quad[0]);
add(quad[2]);
@ -30,13 +25,6 @@ void SkDRect::setBounds(const SkDQuad& quad) {
}
}
void SkDRect::setRawBounds(const SkDQuad& quad) {
set(quad[0]);
for (int x = 1; x < 3; ++x) {
add(quad[x]);
}
}
static bool is_bounded_by_end_points(double a, double b, double c, double d) {
return between(a, b, d) && between(a, c, d);
}
@ -56,10 +44,3 @@ void SkDRect::setBounds(const SkDCubic& c) {
add(c.ptAtT(tValues[x]));
}
}
void SkDRect::setRawBounds(const SkDCubic& cubic) {
set(cubic[0]);
for (int x = 1; x < 4; ++x) {
add(cubic[x]);
}
}

View File

@ -13,18 +13,10 @@ struct SkDRect {
double fLeft, fTop, fRight, fBottom;
void add(const SkDPoint& pt) {
if (fLeft > pt.fX) {
fLeft = pt.fX;
}
if (fTop > pt.fY) {
fTop = pt.fY;
}
if (fRight < pt.fX) {
fRight = pt.fX;
}
if (fBottom < pt.fY) {
fBottom = pt.fY;
}
fLeft = SkTMin(fLeft, pt.fX);
fTop = SkTMin(fTop, pt.fY);
fRight = SkTMax(fRight, pt.fX);
fBottom = SkTMax(fBottom, pt.fY);
}
bool contains(const SkDPoint& pt) const {
@ -32,12 +24,15 @@ struct SkDRect {
&& approximately_between(fTop, pt.fY, fBottom);
}
bool intersects(SkDRect* r) const {
bool intersects(const SkDRect& r) const {
if (fLeft > fRight) {
SkDebugf("!");
}
SkASSERT(fLeft <= fRight);
SkASSERT(fTop <= fBottom);
SkASSERT(r->fLeft <= r->fRight);
SkASSERT(r->fTop <= r->fBottom);
return r->fLeft <= fRight && fLeft <= r->fRight && r->fTop <= fBottom && fTop <= r->fBottom;
SkASSERT(r.fLeft <= r.fRight);
SkASSERT(r.fTop <= r.fBottom);
return r.fLeft <= fRight && fLeft <= r.fRight && r.fTop <= fBottom && fTop <= r.fBottom;
}
void set(const SkDPoint& pt) {
@ -53,11 +48,8 @@ struct SkDRect {
return fBottom - fTop;
}
void setBounds(const SkDLine&);
void setBounds(const SkDCubic&);
void setBounds(const SkDQuad&);
void setRawBounds(const SkDCubic&);
void setRawBounds(const SkDQuad&);
};
#endif

View File

@ -5,11 +5,13 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
SkChunkAlloc* allocator) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@ -17,15 +19,24 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
int index, endIndex;
SkOpSpanBase* start;
SkOpSpanBase* end;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
&index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
&firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
allocator);
if (!current) {
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
if (firstPass) {
firstPass = false;
} else {
break;
}
}
topLeft.fX = topLeft.fY = SK_ScalarMin;
continue;
}
@ -34,62 +45,66 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
SkTDArray<SkOpSpan*> chase;
SkTDArray<SkOpSpanBase*> chase;
do {
if (current->activeWinding(index, endIndex)) {
if (current->activeWinding(start, end)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
int nextStart = index;
int nextEnd = endIndex;
SkOpSpanBase* nextStart = start;
SkOpSpanBase* nextEnd = end;
SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
&unsortable);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
current->addCurveTo(index, endIndex, simple, true);
SkASSERT(simple->isClosed());
current->addCurveTo(start, end, simple, true);
#if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
DebugShowActiveSpans(contourList);
}
#endif
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
current->debugID(), start->pt().fX, start->pt().fY,
end->pt().fX, end->pt().fY);
#endif
current->addCurveTo(index, endIndex, simple, true);
current->addCurveTo(start, end, simple, true);
current = next;
index = nextStart;
endIndex = nextEnd;
} while (!simple->isClosed() && (!unsortable
|| !current->done(SkMin32(index, endIndex))));
if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
// SkASSERT(unsortable || simple->isEmpty());
int min = SkMin32(index, endIndex);
if (!current->done(min)) {
current->addCurveTo(index, endIndex, simple, true);
current->markDoneUnary(min);
start = nextStart;
end = nextEnd;
} while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
if (current->activeWinding(start, end) && !simple->isClosed()) {
SkOpSpan* spanStart = start->starter(end);
if (!spanStart->done()) {
current->addCurveTo(start, end, simple, true);
current->markDone(spanStart);
}
}
simple->close();
} else {
SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
if (last && !last->fChased && !last->fLoop) {
last->fChased = true;
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
last->setChased(true);
SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
// assert that last isn't already in array
*chase.append() = last;
#if DEBUG_WINDING
SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
last->fSmall);
SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
if (!last->final()) {
SkDebugf(" windSum=%d", last->upCast()->windSum());
}
SkDebugf("\n");
#endif
}
}
current = FindChase(&chase, &index, &endIndex);
current = FindChase(&chase, &start, &end);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@ -102,9 +117,11 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
}
// returns true if all edges were processed
static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
SkChunkAlloc* allocator) {
SkOpSegment* current;
int start, end;
SkOpSpanBase* start;
SkOpSpanBase* end;
bool unsortable = false;
bool closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
@ -115,34 +132,38 @@ static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* s
}
#endif
SkASSERT(unsortable || !current->done());
int nextStart = start;
int nextEnd = end;
SkOpSpanBase* nextStart = start;
SkOpSpanBase* nextEnd = end;
SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
current->addCurveTo(start, end, simple, true);
SkASSERT(simple->isClosed());
#if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
DebugShowActiveSpans(contourList);
}
#endif
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
current->xyAtT(end).fX, current->xyAtT(end).fY);
current->debugID(), start->pt().fX, start->pt().fY,
end->pt().fX, end->pt().fY);
#endif
current->addCurveTo(start, end, simple, true);
current = next;
start = nextStart;
end = nextEnd;
} while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
} while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
if (!simple->isClosed()) {
SkASSERT(unsortable);
int min = SkMin32(start, end);
if (!current->done(min)) {
SkOpSpan* spanStart = start->starter(end);
if (!spanStart->done()) {
current->addCurveTo(start, end, simple, true);
current->markDone(min, 1);
current->markDone(spanStart);
}
closable = false;
}
@ -156,52 +177,68 @@ static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* s
// FIXME : add this as a member of SkPath
bool Simplify(const SkPath& path, SkPath* result) {
#if DEBUG_SORT || DEBUG_SWAP_TOP
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
if (path.isConvex()) {
if (result != &path) {
*result = path;
}
result->setFillType(fillType);
return true;
}
// turn path into list of segments
SkTArray<SkOpContour> contours;
SkOpEdgeBuilder builder(path, contours);
if (!builder.finish()) {
SkOpCoincidence coincidence;
SkOpContour contour;
SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUG_SORT || DEBUG_SWAP_TOP
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
if (!builder.finish(&allocator)) {
return false;
}
SkTArray<SkOpContour*, true> contourList;
MakeContourList(contours, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
#if !FORCE_RELEASE
contour.dumpSegments((SkPathOp) -1);
#endif
result->reset();
result->setFillType(fillType);
SkTDArray<SkOpContour* > contourList;
MakeContourList(&contour, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
return true;
}
SkOpContour** listEnd = contourList.end();
if ((*currentPtr)->count() == 0) {
SkASSERT((*currentPtr)->next() == NULL);
return true;
}
SkOpContour** listEnd2 = contourList.end();
// find all intersections between segments
do {
SkOpContour** nextPtr = currentPtr;
SkOpContour* current = *currentPtr++;
if (current->containsCubics()) {
AddSelfIntersectTs(current);
}
SkOpContour* next;
do {
next = *nextPtr++;
} while (AddIntersectTs(current, next) && nextPtr != listEnd);
} while (currentPtr != listEnd);
if (!HandleCoincidence(&contourList, 0)) {
} while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd2);
} while (currentPtr != listEnd2);
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
return false;
}
// construct closed contours
SkPathWriter simple(*result);
if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
: !bridgeXor(contourList, &simple))
SkPathWriter wrapper(*result);
if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
: !bridgeXor(contourList, &wrapper, &allocator))
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);
SkPathWriter assembled(temp);
Assemble(simple, &assembled);
Assemble(wrapper, &assembled);
*result = *assembled.nativePath();
result->setFillType(fillType);
}

View File

@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
int SkIntersections::intersectB(const SkDCubic& cubic1, const SkDCubic& cubic2) {
SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_PARAMS(1));
SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_PARAMS(2));
int SkIntersections::intersect(const SkDCubic& cubic1, const SkDCubic& cubic2) {
SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
return used();
}

View File

@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
int SkIntersections::intersectB(const SkDQuad& quad1, const SkDQuad& quad2) {
SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_PARAMS(1));
SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_PARAMS(2));
int SkIntersections::intersect(const SkDQuad& quad1, const SkDQuad& quad2) {
SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
return used();
}

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,16 @@
#include "SkPathOpsCommon.h"
bool TightBounds(const SkPath& path, SkRect* result) {
SkOpContour contour;
SkOpGlobalState globalState( NULL PATH_OPS_DEBUG_PARAMS(&contour));
// turn path into list of segments
SkTArray<SkOpContour> contours;
SkOpEdgeBuilder builder(path, contours);
if (!builder.finish()) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
if (!builder.finish(&allocator)) {
return false;
}
SkTArray<SkOpContour*, true> contourList;
MakeContourList(contours, contourList, false, false);
SkTDArray<SkOpContour* > contourList;
MakeContourList(&contour, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
result->setEmpty();
if (!currentPtr) {

View File

@ -1,51 +0,0 @@
/*
* 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 "SkPathOpsTriangle.h"
// http://www.blackpawn.com/texts/pointinpoly/default.html
// return true if pt is inside triangle; false if outside or on the line
bool SkDTriangle::contains(const SkDPoint& pt) const {
// Compute vectors
SkDVector v0 = fPts[2] - fPts[0];
SkDVector v1 = fPts[1] - fPts[0];
SkDVector v2 = pt - fPts[0];
// Compute dot products
double dot00 = v0.dot(v0);
double dot01 = v0.dot(v1);
double dot02 = v0.dot(v2);
double dot11 = v1.dot(v1);
double dot12 = v1.dot(v2);
// original code doesn't handle degenerate input; isn't symmetric with inclusion of corner pts;
// introduces error with divide; doesn't short circuit on early answer
#if 0
// Compute barycentric coordinates
double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
return (u >= 0) && (v >= 0) && (u + v <= 1);
#else
double w = dot00 * dot11 - dot01 * dot01;
if (w == 0) {
return false;
}
double wSign = w < 0 ? -1 : 1;
double u = (dot11 * dot02 - dot01 * dot12) * wSign;
if (u <= 0) {
return false;
}
double v = (dot00 * dot12 - dot01 * dot02) * wSign;
if (v <= 0) {
return false;
}
return u + v < w * wSign;
#endif
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPathOpsTriangle_DEFINED
#define SkPathOpsTriangle_DEFINED
#include "SkPathOpsPoint.h"
struct SkDTriangle {
SkDPoint fPts[3];
bool contains(const SkDPoint& pt) const;
};
#endif

View File

@ -22,6 +22,111 @@ enum SkPathOpsMask {
kEvenOdd_PathOpsMask = 1
};
class SkOpCoincidence;
class SkOpContour;
class SkOpGlobalState {
public:
SkOpGlobalState(SkOpCoincidence* coincidence PATH_OPS_DEBUG_PARAMS(SkOpContour* head))
: fCoincidence(coincidence)
, fWindingFailed(false)
, fAngleCoincidence(false)
#if DEBUG_VALIDATE
, fPhase(kIntersecting)
#endif
PATH_OPS_DEBUG_PARAMS(fHead(head))
PATH_OPS_DEBUG_PARAMS(fAngleID(0))
PATH_OPS_DEBUG_PARAMS(fContourID(0))
PATH_OPS_DEBUG_PARAMS(fPtTID(0))
PATH_OPS_DEBUG_PARAMS(fSegmentID(0))
PATH_OPS_DEBUG_PARAMS(fSpanID(0)) {
}
#if DEBUG_VALIDATE
enum Phase {
kIntersecting,
kWalking
};
#endif
bool angleCoincidence() {
return fAngleCoincidence;
}
SkOpCoincidence* coincidence() {
return fCoincidence;
}
#ifdef SK_DEBUG
const struct SkOpAngle* debugAngle(int id) const;
SkOpContour* debugContour(int id);
const class SkOpPtT* debugPtT(int id) const;
const class SkOpSegment* debugSegment(int id) const;
const class SkOpSpanBase* debugSpan(int id) const;
int nextAngleID() {
return ++fAngleID;
}
int nextContourID() {
return ++fContourID;
}
int nextPtTID() {
return ++fPtTID;
}
int nextSegmentID() {
return ++fSegmentID;
}
int nextSpanID() {
return ++fSpanID;
}
#endif
#if DEBUG_VALIDATE
Phase phase() const {
return fPhase;
}
#endif
void setAngleCoincidence() {
fAngleCoincidence = true;
}
#if DEBUG_VALIDATE
void setPhase(Phase phase) {
SkASSERT(fPhase != phase);
fPhase = phase;
}
#endif
// called in very rare cases where angles are sorted incorrectly -- signfies op will fail
void setWindingFailed() {
fWindingFailed = true;
}
bool windingFailed() const {
return fWindingFailed;
}
private:
SkOpCoincidence* fCoincidence;
bool fWindingFailed;
bool fAngleCoincidence;
#if DEBUG_VALIDATE
Phase fPhase;
#endif
#ifdef SK_DEBUG
SkOpContour* fHead;
int fAngleID;
int fContourID;
int fPtTID;
int fSegmentID;
int fSpanID;
#endif
};
// Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
bool AlmostEqualUlps(float a, float b);
inline bool AlmostEqualUlps(double a, double b) {
@ -92,6 +197,7 @@ const double DBL_EPSILON_SUBDIVIDE_ERR = DBL_EPSILON * 16;
const double ROUGH_EPSILON = FLT_EPSILON * 64;
const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
const double BUMP_EPSILON = FLT_EPSILON * 4096;
inline bool zero_or_one(double x) {
return x == 0 || x == 1;
@ -141,12 +247,6 @@ inline bool roughly_zero(double x) {
return fabs(x) < ROUGH_EPSILON;
}
#if 0 // unused for now
inline bool way_roughly_zero(double x) {
return fabs(x) < WAY_ROUGH_EPSILON;
}
#endif
inline bool approximately_zero_inverse(double x) {
return fabs(x) > FLT_EPSILON_INVERSE;
}
@ -156,6 +256,10 @@ inline bool approximately_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
}
inline bool precisely_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
}
// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
// AlmostEqualUlps instead.
inline bool approximately_equal(double x, double y) {
@ -304,7 +408,8 @@ inline bool precisely_between(double a, double b, double c) {
// returns true if (a <= b <= c) || (a >= b >= c)
inline bool between(double a, double b, double c) {
SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
|| (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
return (a - b) * (c - b) <= 0;
}
@ -312,6 +417,15 @@ inline bool roughly_equal(double x, double y) {
return fabs(x - y) < ROUGH_EPSILON;
}
inline bool roughly_negative(double x) {
return x < ROUGH_EPSILON;
}
inline bool roughly_between(double a, double b, double c) {
return a <= c ? roughly_negative(a - b) && roughly_negative(b - c)
: roughly_negative(b - a) && roughly_negative(c - b);
}
inline bool more_roughly_equal(double x, double y) {
return fabs(x - y) < MORE_ROUGH_EPSILON;
}
@ -324,7 +438,6 @@ struct SkDPoint;
struct SkDVector;
struct SkDLine;
struct SkDQuad;
struct SkDTriangle;
struct SkDCubic;
struct SkDRect;

View File

@ -1,168 +0,0 @@
// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
/*
* Roots3And4.c
*
* Utility functions to find cubic and quartic roots,
* coefficients are passed like this:
*
* c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
*
* The functions return the number of non-complex roots and
* put the values into the s array.
*
* Author: Jochen Schwarze (schwarze@isa.de)
*
* Jan 26, 1990 Version for Graphics Gems
* Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
* (reported by Mark Podlipec),
* Old-style function definitions,
* IsZero() as a macro
* Nov 23, 1990 Some systems do not declare acos() and cbrt() in
* <math.h>, though the functions exist in the library.
* If large coefficients are used, EQN_EPS should be
* reduced considerably (e.g. to 1E-30), results will be
* correct but multiple roots might be reported more
* than once.
*/
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
#include "SkQuarticRoot.h"
int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
const double t0, const bool oneHint, double roots[4]) {
#ifdef SK_DEBUG
// create a string mathematica understands
// GDB set print repe 15 # if repeated digits is a bother
// set print elements 400 # if line doesn't fit
char str[1024];
sk_bzero(str, sizeof(str));
SK_SNPRINTF(str, sizeof(str),
"Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
t4, t3, t2, t1, t0);
SkPathOpsDebug::MathematicaIze(str, sizeof(str));
#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
SkDebugf("%s\n", str);
#endif
#endif
if (approximately_zero_when_compared_to(t4, t0) // 0 is one root
&& approximately_zero_when_compared_to(t4, t1)
&& approximately_zero_when_compared_to(t4, t2)) {
if (approximately_zero_when_compared_to(t3, t0)
&& approximately_zero_when_compared_to(t3, t1)
&& approximately_zero_when_compared_to(t3, t2)) {
return SkDQuad::RootsReal(t2, t1, t0, roots);
}
if (approximately_zero_when_compared_to(t4, t3)) {
return SkDCubic::RootsReal(t3, t2, t1, t0, roots);
}
}
if ((approximately_zero_when_compared_to(t0, t1) || approximately_zero(t1)) // 0 is one root
// && approximately_zero_when_compared_to(t0, t2)
&& approximately_zero_when_compared_to(t0, t3)
&& approximately_zero_when_compared_to(t0, t4)) {
int num = SkDCubic::RootsReal(t4, t3, t2, t1, roots);
for (int i = 0; i < num; ++i) {
if (approximately_zero(roots[i])) {
return num;
}
}
roots[num++] = 0;
return num;
}
if (oneHint) {
SkASSERT(approximately_zero_double(t4 + t3 + t2 + t1 + t0) ||
approximately_zero_when_compared_to(t4 + t3 + t2 + t1 + t0, // 1 is one root
SkTMax(fabs(t4), SkTMax(fabs(t3), SkTMax(fabs(t2), SkTMax(fabs(t1), fabs(t0)))))));
// note that -C == A + B + D + E
int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots);
for (int i = 0; i < num; ++i) {
if (approximately_equal(roots[i], 1)) {
return num;
}
}
roots[num++] = 1;
return num;
}
return -1;
}
int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
const double D, const double E, double s[4]) {
double u, v;
/* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
const double invA = 1 / A;
const double a = B * invA;
const double b = C * invA;
const double c = D * invA;
const double d = E * invA;
/* substitute x = y - a/4 to eliminate cubic term:
x^4 + px^2 + qx + r = 0 */
const double a2 = a * a;
const double p = -3 * a2 / 8 + b;
const double q = a2 * a / 8 - a * b / 2 + c;
const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
int num;
double largest = SkTMax(fabs(p), fabs(q));
if (approximately_zero_when_compared_to(r, largest)) {
/* no absolute term: y(y^3 + py + q) = 0 */
num = SkDCubic::RootsReal(1, 0, p, q, s);
s[num++] = 0;
} else {
/* solve the resolvent cubic ... */
double cubicRoots[3];
int roots = SkDCubic::RootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
int index;
/* ... and take one real solution ... */
double z;
num = 0;
int num2 = 0;
for (index = firstCubicRoot; index < roots; ++index) {
z = cubicRoots[index];
/* ... to build two quadric equations */
u = z * z - r;
v = 2 * z - p;
if (approximately_zero_squared(u)) {
u = 0;
} else if (u > 0) {
u = sqrt(u);
} else {
continue;
}
if (approximately_zero_squared(v)) {
v = 0;
} else if (v > 0) {
v = sqrt(v);
} else {
continue;
}
num = SkDQuad::RootsReal(1, q < 0 ? -v : v, z - u, s);
num2 = SkDQuad::RootsReal(1, q < 0 ? v : -v, z + u, s + num);
if (!((num | num2) & 1)) {
break; // prefer solutions without single quad roots
}
}
num += num2;
if (!num) {
return 0; // no valid cubic root
}
}
/* resubstitute */
const double sub = a / 4;
for (int i = 0; i < num; ++i) {
s[i] -= sub;
}
// eliminate duplicates
for (int i = 0; i < num - 1; ++i) {
for (int j = i + 1; j < num; ) {
if (AlmostDequalUlps(s[i], s[j])) {
if (j < --num) {
s[j] = s[num];
}
} else {
++j;
}
}
}
return num;
}

View File

@ -1,16 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDQuarticRoot_DEFINED
#define SkDQuarticRoot_DEFINED
int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
const double t0, const bool oneHint, double s[4]);
int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
const double D, const double E, double s[4]);
#endif

View File

@ -272,6 +272,11 @@ SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
}
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
&& SkDPoint::ApproximatelyEqual(a[0], a[3])) {
reducePts[0] = a[0];
return SkPath::kMove_Verb;
}
SkDCubic cubic;
cubic.set(a);
SkReduceOrder reducer;

View File

@ -7,11 +7,9 @@
#ifndef SkReduceOrder_DEFINED
#define SkReduceOrder_DEFINED
#include "SkPath.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
#include "SkTArray.h"
union SkReduceOrder {
enum Quadratics {

View File

@ -6,8 +6,8 @@
*/
#include "PathOpsTestCommon.h"
#include "SkIntersections.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathOpsTriangle.h"
#include "SkRandom.h"
#include "SkTArray.h"
#include "SkTSort.h"
@ -18,12 +18,12 @@ static bool gPathOpsAngleIdeasEnableBruteCheck = false;
class PathOpsAngleTester {
public:
static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
return lh.convexHullOverlaps(rh);
static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
return lh.convexHullOverlaps(&rh);
}
static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
return lh.endsIntersect(rh);
static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
return lh.endsIntersect(&rh);
}
};
@ -406,28 +406,29 @@ static bool bruteForceCheck(skiatest::Reporter* reporter, const SkDQuad& quad1,
return ccw == upperRange.ccw;
}
class PathOpsSegmentTester {
public:
static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
segment->debugConstructQuad(shortQuad);
}
};
static void makeSegment(const SkDQuad& quad, SkPoint shortQuad[3], SkOpSegment* result) {
static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3],
SkChunkAlloc* allocator) {
shortQuad[0] = quad[0].asSkPoint();
shortQuad[1] = quad[1].asSkPoint();
shortQuad[2] = quad[2].asSkPoint();
PathOpsSegmentTester::ConstructQuad(result, shortQuad);
contour->addQuad(shortQuad, allocator);
}
static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
int testNo) {
int testNo, SkChunkAlloc* allocator) {
SkPoint shortQuads[2][3];
SkOpSegment seg[2];
makeSegment(quad1, shortQuads[0], &seg[0]);
makeSegment(quad2, shortQuads[1], &seg[1]);
int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg[0].debugLastAngle(),
*seg[1].debugLastAngle());
SkOpContour contour;
SkOpGlobalState state(NULL PATH_OPS_DEBUG_PARAMS(&contour));
contour.init(&state, false, false);
makeSegment(&contour, quad1, shortQuads[0], allocator);
makeSegment(&contour, quad1, shortQuads[1], allocator);
SkOpSegment* seg1 = contour.first();
seg1->debugAddAngle(0, 1, allocator);
SkOpSegment* seg2 = seg1->next();
seg2->debugAddAngle(0, 1, allocator);
int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
*seg2->debugLastAngle());
const SkDPoint& origin = quad1[0];
REPORTER_ASSERT(reporter, origin == quad2[0]);
double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX);
@ -545,25 +546,27 @@ static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, c
}
if (overlap < 0) {
SkDEBUGCODE(int realEnds =)
PathOpsAngleTester::EndsIntersect(*seg[0].debugLastAngle(),
*seg[1].debugLastAngle());
PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(),
*seg2->debugLastAngle());
SkASSERT(realEnds == (firstInside ? 1 : 0));
}
bruteForce(reporter, quad1, quad2, firstInside);
}
DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
SkChunkAlloc allocator(4096);
// gPathOpsAngleIdeasVerbose = true;
const SkDQuad quads[] = {
{{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
{{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
};
for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) {
testQuadAngles(reporter, quads[index], quads[index + 1], 0);
testQuadAngles(reporter, quads[index], quads[index + 1], 0, &allocator);
}
}
DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
SkChunkAlloc allocator(4096);
if (!gPathOpsAngleIdeasVerbose) { // takes a while to run -- so exclude it by default
return;
}
@ -587,7 +590,7 @@ DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
if (i.used() > 1) {
continue;
}
testQuadAngles(reporter, quad1, quad2, index);
testQuadAngles(reporter, quad1, quad2, index, &allocator);
}
}

View File

@ -6,10 +6,9 @@
*/
#include "PathOpsTestCommon.h"
#include "SkIntersections.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathOpsTriangle.h"
#include "SkRandom.h"
#include "SkTArray.h"
#include "SkTSort.h"
#include "Test.h"
@ -191,20 +190,20 @@ DEF_TEST(PathOpsAngleFindSlop, reporter) {
class PathOpsAngleTester {
public:
static int After(const SkOpAngle& lh, const SkOpAngle& rh) {
static int After(SkOpAngle& lh, SkOpAngle& rh) {
return lh.after(&rh);
}
static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
return lh.convexHullOverlaps(rh);
static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
return lh.convexHullOverlaps(&rh);
}
static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) {
return lh.orderable(rh);
static int Orderable(SkOpAngle& lh, SkOpAngle& rh) {
return lh.orderable(&rh);
}
static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
return lh.endsIntersect(rh);
static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
return lh.endsIntersect(&rh);
}
static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
@ -214,18 +213,6 @@ public:
class PathOpsSegmentTester {
public:
static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) {
segment->debugConstructCubic(shortCubic);
}
static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) {
segment->debugConstructLine(shortLine);
}
static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
segment->debugConstructQuad(shortQuad);
}
static void DebugReset(SkOpSegment* segment) {
segment->debugReset();
}
@ -246,7 +233,10 @@ static CircleData circleDataSet[] = {
static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
DEF_TEST(PathOpsAngleCircle, reporter) {
SkOpSegment segment[2];
SkOpContour contour;
SkOpGlobalState state(NULL PATH_OPS_DEBUG_PARAMS(&contour));
contour.init(&state, false, false);
SkChunkAlloc allocator(4096);
for (int index = 0; index < circleDataSetSize; ++index) {
CircleData& data = circleDataSet[index];
for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
@ -254,17 +244,21 @@ DEF_TEST(PathOpsAngleCircle, reporter) {
}
switch (data.fPtCount) {
case 2:
PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts);
contour.addLine(data.fShortPts, &allocator);
break;
case 3:
PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts);
contour.addQuad(data.fShortPts, &allocator);
break;
case 4:
PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts);
contour.addCubic(data.fShortPts, &allocator);
break;
}
}
PathOpsAngleTester::Orderable(*segment[0].debugLastAngle(), *segment[1].debugLastAngle());
SkOpSegment* first = contour.first();
first->debugAddAngle(0, 1, &allocator);
SkOpSegment* next = first->next();
next->debugAddAngle(0, 1, &allocator);
PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
}
struct IntersectData {
@ -379,11 +373,39 @@ static IntersectData intersectDataSet16[] = { // pathops_visualizer.htm:7419
{ {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
}; //
// from skpi_gino_com_16
static IntersectData intersectDataSet17[] = {
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
, 3, 0.74590454, 0.547660352, {} },
{ /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
, 4, 0.12052623, 0, {} },
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
, 3, 0.74590454, 1, {} },
};
static IntersectData intersectDataSet18[] = {
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
, 3, 0.74590454, 1, {} },
{ /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
, 4, 0.12052623, 0.217351928, {} },
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
, 3, 0.74590454, 0.547660352, {} },
};
static IntersectData intersectDataSet19[] = {
{ /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
, 4, 0.135148995, 0.134791946, {} },
{ /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}}
, 4, 0.956740456, 0.894913214, {} },
{ /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
, 4, 0.135148995, 0.551812363, {} },
};
#define I(x) intersectDataSet##x
static IntersectData* intersectDataSets[] = {
I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
I(11), I(12), I(13), I(14), I(15), I(16),
I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
};
#undef I
@ -391,56 +413,55 @@ static IntersectData* intersectDataSets[] = {
static const int intersectDataSetSizes[] = {
I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
I(11), I(12), I(13), I(14), I(15), I(16),
I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
};
#undef I
static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
struct FourPoints {
SkPoint pts[4];
};
DEF_TEST(PathOpsAngleAfter, reporter) {
SkChunkAlloc allocator(4096);
SkOpContour contour;
SkOpGlobalState state(NULL PATH_OPS_DEBUG_PARAMS(&contour));
contour.init(&state, false, false);
for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
IntersectData* dataArray = intersectDataSets[index];
const int dataSize = intersectDataSetSizes[index];
SkOpSegment segment[3];
for (int index2 = 0; index2 < dataSize - 2; ++index2) {
for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) {
PathOpsSegmentTester::DebugReset(&segment[temp]);
}
for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) {
allocator.reset();
contour.reset();
for (int index3 = 0; index3 < 3; ++index3) {
IntersectData& data = dataArray[index2 + index3];
SkPoint temp[4];
SkPoint* temp = (SkPoint*) SkOpTAllocator<FourPoints>::Allocate(&allocator);
for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
}
switch (data.fPtCount) {
case 2: {
SkDLine seg = SkDLine::SubDivide(temp, data.fTStart,
data.fTStart < data.fTEnd ? 1 : 0);
data.fShortPts[0] = seg[0].asSkPoint();
data.fShortPts[1] = seg[1].asSkPoint();
PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts);
contour.addLine(temp, &allocator);
} break;
case 3: {
SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd);
data.fShortPts[0] = seg[0].asSkPoint();
data.fShortPts[1] = seg[1].asSkPoint();
data.fShortPts[2] = seg[2].asSkPoint();
PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts);
contour.addQuad(temp, &allocator);
} break;
case 4: {
SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd);
data.fShortPts[0] = seg[0].asSkPoint();
data.fShortPts[1] = seg[1].asSkPoint();
data.fShortPts[2] = seg[2].asSkPoint();
data.fShortPts[3] = seg[3].asSkPoint();
PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts);
contour.addCubic(temp, &allocator);
} break;
}
}
SkOpAngle& angle1 = *const_cast<SkOpAngle*>(segment[0].debugLastAngle());
SkOpAngle& angle2 = *const_cast<SkOpAngle*>(segment[1].debugLastAngle());
SkOpAngle& angle3 = *const_cast<SkOpAngle*>(segment[2].debugLastAngle());
SkOpSegment* seg1 = contour.first();
seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd, &allocator);
SkOpSegment* seg2 = seg1->next();
seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd, &allocator);
SkOpSegment* seg3 = seg2->next();
seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd, &allocator);
SkOpAngle& angle1 = *seg1->debugLastAngle();
SkOpAngle& angle2 = *seg2->debugLastAngle();
SkOpAngle& angle3 = *seg3->debugLastAngle();
PathOpsAngleTester::SetNext(angle1, angle3);
// These data sets are seeded when the set itself fails, so likely the dataset does not
// match the expected result. The tests above return 1 when first added, but
@ -451,35 +472,26 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
}
}
void SkOpSegment::debugConstruct() {
addStartSpan(1);
addEndSpan(1);
debugAddAngle(0, 1);
}
void SkOpSegment::debugAddAngle(int start, int end) {
SkASSERT(start != end);
SkOpAngle& angle = fAngles.push_back();
angle.set(this, start, end);
}
void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) {
addCubic(shortQuad, false, false);
addT(NULL, shortQuad[0], 0);
addT(NULL, shortQuad[3], 1);
debugConstruct();
}
void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) {
addLine(shortQuad, false, false);
addT(NULL, shortQuad[0], 0);
addT(NULL, shortQuad[1], 1);
debugConstruct();
}
void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) {
addQuad(shortQuad, false, false);
addT(NULL, shortQuad[0], 0);
addT(NULL, shortQuad[2], 1);
debugConstruct();
void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* allocator) {
SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
: this->addT(startT, kNoAlias, allocator);
SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
: this->addT(endT, kNoAlias, allocator);
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
SkOpSpanBase* startSpan = &fHead;
while (startSpan->ptT() != startPtT) {
startSpan = startSpan->upCast()->next();
}
SkOpSpanBase* endSpan = &fHead;
while (endSpan->ptT() != endPtT) {
endSpan = endSpan->upCast()->next();
}
angle->set(startSpan, endSpan);
if (startT < endT) {
startSpan->upCast()->setToAngle(angle);
endSpan->setFromAngle(angle);
} else {
endSpan->upCast()->setToAngle(angle);
startSpan->setFromAngle(angle);
}
}

View File

@ -59,7 +59,6 @@ path2.lineTo(SkBits2Float(0x422c58d6), SkBits2Float(0x422705c1));
path2.cubicTo(SkBits2Float(0x42383446), SkBits2Float(0x421ac98f), SkBits2Float(0x4242b98a), SkBits2Float(0x420d5308), SkBits2Float(0x424bbb17), SkBits2Float(0x41fdb8ee));
path2.lineTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6));
path2.close();
// SkOpSegment.cpp:3488: failed assertion "other->fTs[min].fWindSum == oppWinding"
testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
}
@ -1296,7 +1295,7 @@ path.lineTo(SkBits2Float(0x4285e672), SkBits2Float(0xc2443b5f));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -1512,7 +1511,7 @@ path.lineTo(SkBits2Float(0x42a3a81d), SkBits2Float(0xc15e595e));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -1754,7 +1753,7 @@ path.lineTo(SkBits2Float(0x4039d102), SkBits2Float(0xc2a5e5fe));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -1884,11 +1883,7 @@ path.lineTo(SkBits2Float(0x3ee8b040), SkBits2Float(0xc2a5ff5d));
path.close();
SkPath path2(path);
if (FLAGS_runFail) {
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
} else {
testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -3982,7 +3977,7 @@ path.lineTo(SkBits2Float(0x42a38b52), SkBits2Float(0xc1639578));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -4092,7 +4087,7 @@ path.lineTo(SkBits2Float(0x42a5fe22), SkBits2Float(0x3f4744a1));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -4240,7 +4235,7 @@ path.lineTo(SkBits2Float(0x429c4e4c), SkBits2Float(0x41df969b));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -4414,7 +4409,7 @@ path.lineTo(SkBits2Float(0x428cfdb5), SkBits2Float(0x422f3e36));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -4836,7 +4831,7 @@ path.lineTo(SkBits2Float(0x425b4ae0), SkBits2Float(0x427944c0));
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
// op end success 1
@ -4956,7 +4951,7 @@ path.lineTo(SkBits2Float(0x424f88ba), SkBits2Float(0x428191f0));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -5367,7 +5362,7 @@ path.lineTo(SkBits2Float(0x3fc9081a), SkBits2Float(0xc2a5f864));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -5443,7 +5438,7 @@ path.lineTo(SkBits2Float(0x40848cae), SkBits2Float(0xc2a5cb0c));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -6297,7 +6292,7 @@ path.lineTo(SkBits2Float(0x429ff91f), SkBits2Float(0xc1b14b8a));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -7054,7 +7049,7 @@ path.lineTo(SkBits2Float(0x4273ad4f), SkBits2Float(0x42617d52));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -7443,7 +7438,7 @@ path.lineTo(SkBits2Float(0x427e3109), SkBits2Float(0x42559108));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -7560,7 +7555,7 @@ path.lineTo(SkBits2Float(0x4279eebd), SkBits2Float(0x425a890e));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -7863,7 +7858,7 @@ path.lineTo(SkBits2Float(0x42759f2b), SkBits2Float(0x425f5e9b));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// op end success 1
@ -10686,7 +10681,7 @@ path.close();
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp68;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp1394;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
@ -11128,5 +11123,5 @@ DEF_TEST(PathOpsBattle, reporter) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
RunTestSet(reporter, tests, testCount, firstTest, NULL, stopTest, runReverse);
}

View File

@ -4,7 +4,10 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "PathOpsExtendedTest.h"
#include "PathOpsTestCommon.h"
#include "SkBitmap.h"
#include "Test.h"
DEF_TEST(PathOpsBuilder, reporter) {
@ -22,6 +25,7 @@ DEF_TEST(PathOpsBuilder, reporter) {
REPORTER_ASSERT(reporter, result.isEmpty());
SkPath rectPath;
rectPath.setFillType(SkPath::kEvenOdd_FillType);
rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
builder.add(rectPath, kUnion_PathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
@ -33,13 +37,14 @@ DEF_TEST(PathOpsBuilder, reporter) {
REPORTER_ASSERT(reporter, rectPath == result);
rectPath.reset();
rectPath.setFillType(SkPath::kEvenOdd_FillType);
rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
builder.add(rectPath, kUnion_PathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
REPORTER_ASSERT(reporter, closed);
REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
REPORTER_ASSERT(reporter, rectPath == result);
REPORTER_ASSERT(reporter, rectPath == result);
builder.add(rectPath, kDifference_PathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
@ -69,5 +74,7 @@ DEF_TEST(PathOpsBuilder, reporter) {
builder.add(circle2, kUnion_PathOp);
builder.add(circle3, kDifference_PathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
REPORTER_ASSERT(reporter, opCompare == result);
SkBitmap bitmap;
int pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result, bitmap);
REPORTER_ASSERT(reporter, pixelDiff == 0);
}

View File

@ -6,6 +6,7 @@
*/
#include "PathOpsCubicIntersectionTestData.h"
#include "PathOpsTestCommon.h"
#include "SkGeometry.h"
#include "SkIntersections.h"
#include "SkPathOpsRect.h"
#include "SkReduceOrder.h"
@ -162,6 +163,60 @@ static const SkDCubic testSet[] = {
const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
static const SkDCubic newTestSet[] = {
{{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27576}, {980.025879,1481.27527}}},
{{{980.025879,1481.27527}, {980.025452,1481.27222}, {980.023743,1481.26038}, {980.02179,1481.24072}}},
{{{1.80943513,3.07782435}, {1.66686702,2.16806936}, {1.68301272,0}, {3,0}}},
{{{0,1}, {0,3}, {3,2}, {5,2}}},
{{{3.4386673,2.66977954}, {4.06668949,2.17046738}, {4.78887367,1.59629118}, {6,2}}},
{{{1.71985495,3.49467373}, {2.11620402,2.7201426}, {2.91897964,1.15138781}, {6,3}}},
{{{0,1}, {0.392703831,1.78540766}, {0.219947904,2.05676103}, {0.218561709,2.05630541}}},
{{{0.218561709,2.05630541}, {0.216418028,2.05560064}, {0.624105453,1.40486407}, {4.16666651,1.00000012}}},
{{{0, 1}, {3, 5}, {2, 1}, {3, 1}}},
{{{1.01366711f, 2.21379328f}, {1.09074128f, 2.23241305f}, {1.60246587f, 0.451849401f}, {5, 3}}},
{{{0, 1}, {0.541499972f, 3.16599989f}, {1.08299994f, 2.69299984f}, {2.10083938f, 1.80391729f}}},
{{{0.806384504f, 2.85426903f}, {1.52740121f, 1.99355423f}, {2.81689167f, 0.454222918f}, {5, 1}}},
{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
{{{2, 3}, {2.36602545f, 3.36602545f}, {2.330127f, 3.06217766f}, {2.28460979f, 2.67691422f}}},
{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
{{{2.28460979f, 2.67691422f}, {2.20577145f, 2.00961876f}, {2.09807634f, 1.09807622f}, {4, 3}}},
{{{0, 1}, {0.8211091160774231, 2.0948121547698975}, {0.91805583238601685, 2.515404224395752}, {0.91621249914169312, 2.5146586894989014}}},
{{{0.91621249914169312, 2.5146586894989014}, {0.91132104396820068, 2.5126807689666748}, {0.21079301834106445, -0.45617169141769409}, {10.5, -1.6666665077209473}}},
{{{42.6237564,68.9841232}, {32.449646,81.963089}, {14.7713947,103.565269}, {12.6310005,105.247002}}},
{{{37.2640038,95.3540039}, {37.2640038,95.3540039}, {11.3710003,83.7339935}, {-25.0779991,124.912003}}},
{{{0,1}, {4,5}, {6,0}, {1,0}}},
{{{0,6}, {0,1}, {1,0}, {5,4}}},
{{{0,1}, {4,6}, {5,1}, {6,2}}},
{{{1,5}, {2,6}, {1,0}, {6,4}}},
{{{322, 896.04803466796875}, {314.09201049804687, 833.4376220703125}, {260.24713134765625, 785}, {195, 785}}},
{{{195, 785}, {265.14016723632812, 785}, {322, 842.30755615234375}, {322, 913}}},
{{{1, 4}, {4, 5}, {3, 2}, {6, 3}}},
{{{2, 3}, {3, 6}, {4, 1}, {5, 4}}},
{{{67, 913}, {67, 917.388916015625}, {67.224380493164063, 921.72576904296875}, {67.662384033203125, 926}}},
{{{194, 1041}, {123.85984039306641, 1041}, {67, 983.69244384765625}, {67, 913}}},
{{{1,4}, {1,5}, {6,0}, {5,1}}},
{{{0,6}, {1,5}, {4,1}, {5,1}}},
{{{0,1}, {4,5}, {6,0}, {1,0}}},
{{{0,6}, {0,1}, {1,0}, {5,4}}},
{{{0,1}, {4,6}, {2,0}, {2,0}}},
{{{0,2}, {0,2}, {1,0}, {6,4}}},
{{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}},
{{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}},
@ -306,7 +361,6 @@ static const SkDCubic newTestSet[] = {
};
const int newTestSetCount = (int) SK_ARRAY_COUNT(newTestSet);
static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
bool coin) {
SkASSERT(ValidCubic(cubic1));
@ -320,28 +374,22 @@ static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const S
cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
#endif
SkTArray<SkDQuad, true> quads1;
CubicToQuads(cubic1, cubic1.calcPrecision(), quads1);
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 1\n");
for (int index = 0; index < quads1.count(); ++index) {
const SkDQuad& q = quads1[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
q[1].fX, q[1].fY, q[2].fX, q[2].fY);
}
#endif
SkTArray<SkDQuad, true> quads2;
CubicToQuads(cubic2, cubic2.calcPrecision(), quads2);
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 2\n");
for (int index = 0; index < quads2.count(); ++index) {
const SkDQuad& q = quads2[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
q[1].fX, q[1].fY, q[2].fX, q[2].fY);
}
#if DEBUG_T_SECT_DUMP > 1
gDumpTSectNum = 0;
#endif
SkIntersections intersections;
intersections.intersect(cubic1, cubic2);
#if DEBUG_T_SECT_DUMP == 3
SkDebugf("</div>\n\n");
SkDebugf("<script type=\"text/javascript\">\n\n");
SkDebugf("var testDivs = [\n");
for (int index = 1; index <= gDumpTSectNum; ++index) {
SkDebugf("sect%d,\n", index);
}
#endif
if (coin && intersections.used() != 2) {
SkDebugf("");
}
REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
double tt1, tt2;
SkDPoint xy1, xy2;
@ -558,37 +606,30 @@ int selfSetCount = (int) SK_ARRAY_COUNT(selfSet);
static void selfOneOff(skiatest::Reporter* reporter, int index) {
const SkDCubic& cubic = selfSet[index];
#if ONE_OFF_DEBUG
int idx2;
double max[3];
int ts = cubic.findMaxCurvature(max);
for (idx2 = 0; idx2 < ts; ++idx2) {
SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY);
SkPoint c[4];
for (int i = 0; i < 4; ++i) {
c[i] = cubic[i].asSkPoint();
}
SkTArray<double, true> ts1;
SkTArray<SkDQuad, true> quads1;
cubic.toQuadraticTs(cubic.calcPrecision(), &ts1);
for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
SkScalar loopT;
SkScalar d[3];
SkCubicType cubicType = SkClassifyCubic(c, d);
if (SkDCubic::ComplexBreak(c, &loopT) && cubicType == SkCubicType::kLoop_SkCubicType) {
SkIntersections i;
SkPoint twoCubics[7];
SkChopCubicAt(c, twoCubics, loopT);
SkDCubic chopped[2];
chopped[0].set(&twoCubics[0]);
chopped[1].set(&twoCubics[3]);
int result = i.intersect(chopped[0], chopped[1]);
REPORTER_ASSERT(reporter, result == 2);
REPORTER_ASSERT(reporter, i.used() == 2);
for (int index = 0; index < result; ++index) {
SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
reporter->bumpTestCount();
}
}
CubicToQuads(cubic, cubic.calcPrecision(), quads1);
for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
const SkDQuad& q = quads1[idx2];
SkDebugf(" {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n",
q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY);
}
SkDebugf("\n");
#endif
SkIntersections i;
int result = i.intersect(cubic);
REPORTER_ASSERT(reporter, result == 1);
REPORTER_ASSERT(reporter, i.used() == 1);
REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0]));
SkDPoint pt1 = cubic.ptAtT(i[0][0]);
SkDPoint pt2 = cubic.ptAtT(i[1][0]);
REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
reporter->bumpTestCount();
}
static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
@ -599,12 +640,12 @@ static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
}
static const SkDCubic coinSet[] = {
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
{{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}},
{{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125},
{324.71566772460937, 714.62060546875}, {325, 714.9990234375}}},
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
};
static int coinSetCount = (int) SK_ARRAY_COUNT(coinSet);
@ -642,3 +683,72 @@ DEF_TEST(PathOpsCubicIntersection, reporter) {
if (false) CubicIntersection_IntersectionFinder();
if (false) CubicIntersection_RandTest(reporter);
}
static void binaryTest(const SkDCubic& cubic1, const SkDCubic& cubic2,
skiatest::Reporter* reporter) {
SkASSERT(ValidCubic(cubic1));
SkASSERT(ValidCubic(cubic2));
SkIntersections intersections;
SkReduceOrder reduce1, reduce2;
int order1 = reduce1.reduce(cubic1, SkReduceOrder::kNo_Quadratics);
int order2 = reduce2.reduce(cubic2, SkReduceOrder::kNo_Quadratics);
if (order1 == 4 && order2 == 4) {
intersections.intersect(cubic1, cubic2);
} else {
intersections.reset();
}
SkIntersections intersections2;
(void) intersections2.intersect(cubic1, cubic2);
REPORTER_ASSERT(reporter, intersections.used() <= intersections2.used()
|| intersections[0][0] + 0.01 > intersections[0][1]);
for (int index = 0; index < intersections2.used(); ++index) {
// SkASSERT(intersections.pt(index).approximatelyEqual(intersections2.pt(index)));
double tt1 = intersections2[0][index];
SkDPoint xy1 = cubic1.ptAtT(tt1);
double tt2 = intersections2[1][index];
SkDPoint xy2 = cubic2.ptAtT(tt2);
REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
}
}
DEF_TEST(PathOpsCubicBinaryTest, reporter) {
int outer = 0;
int inner = outer + 1;
do {
const SkDCubic& cubic1 = testSet[outer];
const SkDCubic& cubic2 = testSet[inner];
binaryTest(cubic1, cubic2, reporter);
inner += 2;
outer += 2;
} while (outer < (int) testSetCount);
}
DEF_TEST(PathOpsCubicBinaryNew, reporter) {
int outer = 62;
int inner = outer + 1;
do {
const SkDCubic& cubic1 = newTestSet[outer];
const SkDCubic& cubic2 = newTestSet[inner];
binaryTest(cubic1, cubic2, reporter);
inner += 2;
outer += 2;
} while (outer < (int) newTestSetCount);
}
DEF_TEST(PathOpsCubicBinaryStd, reporter) {
const int firstTest = 0;
for (size_t index = firstTest; index < tests_count; ++index) {
const SkDCubic& cubic1 = tests[index][0];
const SkDCubic& cubic2 = tests[index][1];
binaryTest(cubic1, cubic2, reporter);
}
}
DEF_TEST(PathOpsCubicBinaryCoin, reporter) {
int firstFail = 0;
for (int index = firstFail; index < coinSetCount; index += 2) {
const SkDCubic& cubic1 = coinSet[index];
const SkDCubic& cubic2 = coinSet[index + 1];
binaryTest(cubic1, cubic2, reporter);
}
}

View File

@ -46,8 +46,8 @@ const SkDCubic pointDegenerates[] = {
const size_t pointDegenerates_count = SK_ARRAY_COUNT(pointDegenerates);
const SkDCubic notPointDegenerates[] = {
{{{1 + FLT_EPSILON * 2, 1}, {1, FLT_EPSILON * 2}, {1, 1}, {1, 1}}},
{{{1 + FLT_EPSILON * 2, 1}, {1 - FLT_EPSILON * 2, 1}, {1, 1}, {1, 1}}}
{{{1 + FLT_EPSILON * 8, 1}, {1, FLT_EPSILON * 8}, {1, 1}, {1, 1}}},
{{{1 + FLT_EPSILON * 8, 1}, {1 - FLT_EPSILON * 8, 1}, {1, 1}, {1, 1}}}
};
const size_t notPointDegenerates_count =
@ -156,8 +156,8 @@ const SkDCubic notLines[] = {
const size_t notLines_count = SK_ARRAY_COUNT(notLines);
static const double E = FLT_EPSILON * 2;
static const double F = FLT_EPSILON * 3;
static const double E = FLT_EPSILON * 8;
static const double F = FLT_EPSILON * 8;
const SkDCubic modEpsilonLines[] = {
{{{0, E}, {0, 0}, {0, 0}, {1, 0}}}, // horizontal
@ -187,8 +187,8 @@ const SkDCubic modEpsilonLines[] = {
{{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}}},
{{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}}, // first-middle middle-last coincident
{{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}}, // no coincident
{{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}}, // INVESTIGATE: why the epsilon is bigger
{{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}}, // INVESTIGATE: why the epsilon is bigger
{{{1, 1}, {3, 3}, {2, 2}, {4, 4+F+F}}}, // INVESTIGATE: why the epsilon is bigger
{{{1, 1+F+F}, {2, 2}, {4, 4}, {3, 3}}}, // INVESTIGATE: why the epsilon is bigger
{{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}}},
{{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}}},
{{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}}},

View File

@ -49,6 +49,9 @@ static void testFail(skiatest::Reporter* reporter, int iIndex) {
}
static lineCubic lineCubicTests[] = {
{{{{0.468027353,4}, {1.06734705,1.33333337}, {1.36700678,0}, {3,0}}},
{{{2,1}, {0,1}}}},
{{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875},
{-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}},
{{{-287.9506133720805678, -557.1376476615772617},

View File

@ -15,44 +15,32 @@
static struct quadCubic {
SkDCubic cubic;
SkDQuad quad;
int answerCount;
SkDPoint answers[2];
} quadCubicTests[] = {
#if 0 // FIXME : this should not fail (root problem behind skpcarrot_is24 )
{{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
{{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1,
{{1019.421, 662.449}}},
#endif
{{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}},
{{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
{{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
{{778, 14089}, {776.82855609581270,14091.828250841330}}},
{{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
{{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
{{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2,
{{1110, 817}, {1110.70715f, 817.292908f}}},
{{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}},
{{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
{{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2,
{{1110.70715f, 817.292908f}, {1111, 818}}},
{{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}},
{{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}},
{{{55, 207}, {52.929431915283203, 206.99949645996094},
{51.464466094970703, 205.53553771972656}}}, 2,
{{55, 207}, {51.464466094970703, 205.53553771972656}}},
{51.464466094970703, 205.53553771972656}}}},
{{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}},
{{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688},
{34.355339050292969, 82.355339050292969}}}, 2,
{{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}},
{34.355339050292969, 82.355339050292969}}}},
{{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
{{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
{{18,226}, {0,0}}},
{{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}},
{{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
{{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
{{10,234}, {0,0}}},
{{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}},
};
static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests);
@ -75,9 +63,9 @@ static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
SkDebugf("[%d] quad order=%d\n", iIndex, order2);
REPORTER_ASSERT(reporter, 0);
}
SkDCubic quadToCubic = quad.toCubic();
SkIntersections i;
int roots = i.intersect(cubic, quad);
SkASSERT(roots == quadCubicTests[index].answerCount);
int roots = i.intersect(cubic, quadToCubic);
for (int pt = 0; pt < roots; ++pt) {
double tt1 = i[0][pt];
SkDPoint xy1 = cubic.ptAtT(tt1);
@ -88,15 +76,6 @@ static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
__FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
}
REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
bool found = false;
for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
}
if (!found) {
SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
__FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
}
REPORTER_ASSERT(reporter, found);
}
reporter->bumpTestCount();
}
@ -111,195 +90,3 @@ DEF_TEST(PathOpsCubicQuadIntersection, reporter) {
DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) {
cubicQuadIntersection(reporter, 0);
}
static bool gPathOpCubicQuadSlopVerbose = false;
static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
// determine that slop required after quad/quad finds a candidate intersection
// use the cross of the tangents plus the distance from 1 or 0 as knobs
DEF_TEST(PathOpsCubicQuadSlop, reporter) {
// create a random non-selfintersecting cubic
// break it into quadratics
// offset the quadratic, measuring the slop required to find the intersection
if (!gPathOpCubicQuadSlopVerbose) { // takes a while to run -- so exclude it by default
return;
}
int results[101];
sk_bzero(results, sizeof(results));
double minCross[101];
sk_bzero(minCross, sizeof(minCross));
double maxCross[101];
sk_bzero(maxCross, sizeof(maxCross));
double sumCross[101];
sk_bzero(sumCross, sizeof(sumCross));
int foundOne = 0;
int slopCount = 1;
SkRandom ran;
for (int index = 0; index < 10000000; ++index) {
if (index % 1000 == 999) SkDebugf(".");
SkDCubic cubic = {{
{ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
{ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
{ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
{ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
}};
SkIntersections i;
if (i.intersect(cubic)) {
continue;
}
SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
double tStart = 0;
int tsCount = ts.count();
for (int i1 = 0; i1 <= tsCount; ++i1) {
const double tEnd = i1 < tsCount ? ts[i1] : 1;
SkDCubic part = cubic.subDivide(tStart, tEnd);
SkDQuad quad = part.toQuad();
SkReduceOrder reducer;
int order = reducer.reduce(quad);
if (order != 3) {
continue;
}
for (int i2 = 0; i2 < 100; ++i2) {
SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
SkDQuad nearby = {{
{quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
{quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
{quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
}};
order = reducer.reduce(nearby);
if (order != 3) {
continue;
}
SkIntersections locals;
locals.allowNear(false);
locals.intersect(quad, nearby);
if (locals.used() != 1) {
continue;
}
// brute force find actual intersection
SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
SkIntersections liner;
int i3;
int found = -1;
int foundErr = true;
for (i3 = 1; i3 <= 1000; ++i3) {
cubicLine[0] = cubicLine[1];
cubicLine[1] = cubic.ptAtT(i3 / 1000.);
liner.reset();
liner.allowNear(false);
liner.intersect(nearby, cubicLine);
if (liner.used() == 0) {
continue;
}
if (liner.used() > 1) {
foundErr = true;
break;
}
if (found > 0) {
foundErr = true;
break;
}
foundErr = false;
found = i3;
}
if (foundErr) {
continue;
}
SkDVector dist = liner.pt(0) - locals.pt(0);
SkDVector qV = nearby.dxdyAtT(locals[0][0]);
double cubicT = (found - 1 + liner[1][0]) / 1000.;
SkDVector cV = cubic.dxdyAtT(cubicT);
double qxc = qV.crossCheck(cV);
double qvLen = qV.length();
double cvLen = cV.length();
double maxLen = SkTMax(qvLen, cvLen);
qxc /= maxLen;
double quadT = tStart + (tEnd - tStart) * locals[0][0];
double diffT = fabs(cubicT - quadT);
int diffIdx = (int) (diffT * 100);
results[diffIdx]++;
double absQxc = fabs(qxc);
if (sumCross[diffIdx] == 0) {
minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
} else {
minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
sumCross[diffIdx] += absQxc;
}
if (diffIdx >= 20) {
#if 01
SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
" quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
" {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
" qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
nearby[2].fX, nearby[2].fY,
liner.pt(0).fX, liner.pt(0).fY,
locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
#else
SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
quadT, cubicT, dist.length(), qxc);
SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
"{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
"{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
nearby[2].fX, nearby[2].fY,
liner.pt(0).fX, liner.pt(0).fY,
locals.pt(0).fX, locals.pt(0).fY);
SkDebugf("</div>\n\n");
#endif
}
++foundOne;
}
tStart = tEnd;
}
if (++foundOne >= 100000) {
break;
}
}
#if 01
SkDebugf("slopCount=%d\n", slopCount);
int max = 100;
while (results[max] == 0) {
--max;
}
for (int i = 0; i <= max; ++i) {
if (i > 0 && i % 10 == 0) {
SkDebugf("\n");
}
SkDebugf("%d ", results[i]);
}
SkDebugf("min\n");
for (int i = 0; i <= max; ++i) {
if (i > 0 && i % 10 == 0) {
SkDebugf("\n");
}
SkDebugf("%1.9g ", minCross[i]);
}
SkDebugf("max\n");
for (int i = 0; i <= max; ++i) {
if (i > 0 && i % 10 == 0) {
SkDebugf("\n");
}
SkDebugf("%1.9g ", maxCross[i]);
}
SkDebugf("avg\n");
for (int i = 0; i <= max; ++i) {
if (i > 0 && i % 10 == 0) {
SkDebugf("\n");
}
SkDebugf("%1.9g ", sumCross[i] / results[i]);
}
#else
for (int i = 1; i < slopCount; ++i) {
SkDebugf(" slop%d,\n", i);
}
#endif
SkDebugf("\n");
}

View File

@ -1,198 +0,0 @@
/*
* 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 "SkGeometry.h"
#include "SkIntersections.h"
#include "SkPathOpsRect.h"
#include "SkReduceOrder.h"
#include "Test.h"
static void test(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
int firstTest, size_t testCount) {
for (size_t index = firstTest; index < testCount; ++index) {
const SkDCubic& cubic = cubics[index];
SkASSERT(ValidCubic(cubic));
double precision = cubic.calcPrecision();
SkTArray<SkDQuad, true> quads;
CubicToQuads(cubic, precision, quads);
if (quads.count() != 1 && quads.count() != 2) {
SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
quads.count());
}
REPORTER_ASSERT(reporter, quads.count() == 1);
}
}
static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name,
int firstTest, size_t testCount) {
for (size_t index = firstTest; index < testCount; ++index) {
const SkDQuad& quad = quadTests[index];
SkASSERT(ValidQuad(quad));
SkDCubic cubic = quad.toCubic();
double precision = cubic.calcPrecision();
SkTArray<SkDQuad, true> quads;
CubicToQuads(cubic, precision, quads);
if (quads.count() != 1 && quads.count() != 2) {
SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
quads.count());
}
REPORTER_ASSERT(reporter, quads.count() <= 2);
}
}
static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
int firstTest, size_t testCount) {
// test if computed line end points are valid
for (size_t index = firstTest; index < testCount; ++index) {
const SkDCubic& cubic = cubics[index];
SkASSERT(ValidCubic(cubic));
double precision = cubic.calcPrecision();
SkTArray<SkDQuad, true> quads;
CubicToQuads(cubic, precision, quads);
if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
|| !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
SkDebugf("[%d] unmatched start\n", static_cast<int>(index));
REPORTER_ASSERT(reporter, 0);
}
int last = quads.count() - 1;
if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
|| !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
SkDebugf("[%d] unmatched end\n", static_cast<int>(index));
REPORTER_ASSERT(reporter, 0);
}
}
}
static void testC(skiatest::Reporter* reporter, const SkDCubic(* cubics)[2], const char* name,
int firstTest, size_t testCount) {
for (size_t index = firstTest; index < testCount; ++index) {
for (int idx2 = 0; idx2 < 2; ++idx2) {
const SkDCubic& cubic = cubics[index][idx2];
SkASSERT(ValidCubic(cubic));
double precision = cubic.calcPrecision();
SkTArray<SkDQuad, true> quads;
CubicToQuads(cubic, precision, quads);
if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
|| !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
SkDebugf("[%d][%d] unmatched start\n", static_cast<int>(index), idx2);
REPORTER_ASSERT(reporter, 0);
}
int last = quads.count() - 1;
if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
|| !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
SkDebugf("[%d][%d] unmatched end\n", static_cast<int>(index), idx2);
REPORTER_ASSERT(reporter, 0);
}
}
}
}
DEF_TEST(CubicToQuads, reporter) {
enum {
RunAll,
RunPointDegenerates,
RunNotPointDegenerates,
RunLines,
RunNotLines,
RunModEpsilonLines,
RunLessEpsilonLines,
RunNegEpsilonLines,
RunQuadraticLines,
RunQuadraticModLines,
RunComputedLines,
RunComputedTests,
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 firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines
? firstTestIndex : SK_MaxS32;
int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines
? firstTestIndex : SK_MaxS32;
int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines
? firstTestIndex : SK_MaxS32;
int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests
? firstTestIndex : SK_MaxS32;
test(reporter, pointDegenerates, "pointDegenerates", firstPointDegeneratesTest,
pointDegenerates_count);
testC(reporter, notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest,
notPointDegenerates_count);
test(reporter, lines, "lines", firstLinesTest, lines_count);
testC(reporter, notLines, "notLines", firstNotLinesTest, notLines_count);
testC(reporter, modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
test(reporter, lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest,
lessEpsilonLines_count);
test(reporter, negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
test(reporter, quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
test(reporter, quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
quadraticModEpsilonLines_count);
testC(reporter, lines, "computed lines", firstComputedLinesTest, lines_count);
testC(reporter, tests, "computed tests", firstComputedCubicsTest, tests_count);
}
static SkDCubic locals[] = {
{{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002},
{0.0017190297609673323, 0.99828097023903239},
{0.0053709083094631276, 0.99505672974365911}}},
{{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139},
{8.03767257, 89.1628526}}},
{{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441},
{80.392774, 61.3533852}}},
{{{60.776536520932126, 71.249307306133829}, {87.107894191103014, 22.377669868235323},
{1.4974754310666936, 68.069569937917208}, {45.261946574441133, 17.536076632112298}}},
};
static size_t localsCount = SK_ARRAY_COUNT(locals);
#define DEBUG_CRASH 0
#define TEST_AVERAGE_END_POINTS 0 // must take const off to test
extern const bool AVERAGE_END_POINTS;
static void oneOff(skiatest::Reporter* reporter, size_t x) {
const SkDCubic& cubic = locals[x];
SkASSERT(ValidCubic(cubic));
const SkPoint skcubic[4] = {
{static_cast<float>(cubic[0].fX), static_cast<float>(cubic[0].fY)},
{static_cast<float>(cubic[1].fX), static_cast<float>(cubic[1].fY)},
{static_cast<float>(cubic[2].fX), static_cast<float>(cubic[2].fY)},
{static_cast<float>(cubic[3].fX), static_cast<float>(cubic[3].fY)}};
SkScalar skinflect[2];
int skin = SkFindCubicInflections(skcubic, skinflect);
if (false) SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
SkTArray<SkDQuad, true> quads;
double precision = cubic.calcPrecision();
CubicToQuads(cubic, precision, quads);
if (false) SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
}
DEF_TEST(CubicsToQuadratics_OneOff_Loop, reporter) {
for (size_t x = 0; x < localsCount; ++x) {
oneOff(reporter, x);
}
}
DEF_TEST(CubicsToQuadratics_OneOff_Single, reporter) {
oneOff(reporter, 0);
}

View File

@ -43,10 +43,6 @@ DEF_TEST(PathOpsLineUtilities, reporter) {
SkDebugf("%s [%d] expected left\n", __FUNCTION__, index);
REPORTER_ASSERT(reporter, 0);
}
line2 = line.subDivide(1, 0);
REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
line2 = SkDLine::SubDivide(pts, 1, 0);
REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
SkDPoint mid = line.ptAtT(.5);
REPORTER_ASSERT(reporter, approximately_equal((line[0].fX + line[1].fX) / 2, mid.fX));
REPORTER_ASSERT(reporter, approximately_equal((line[0].fY + line[1].fY) / 2, mid.fY));

View File

@ -38,7 +38,6 @@ DEF_TEST(PathOpsDPoint, reporter) {
REPORTER_ASSERT(reporter, p == pt);
REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt));
REPORTER_ASSERT(reporter, p.roughlyEqual(pt));
REPORTER_ASSERT(reporter, p.moreRoughlyEqual(pt));
p.fX = p.fY = 0;
REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0);
REPORTER_ASSERT(reporter, p.approximatelyZero());

View File

@ -1,63 +0,0 @@
/*
* 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 "PathOpsTestCommon.h"
#include "SkPath.h"
#include "SkPathOpsQuad.h"
#include "SkRRect.h"
#include "Test.h"
static const SkDQuad tests[] = {
{{{1, 1}, {2, 1}, {0, 2}}},
{{{0, 0}, {1, 1}, {3, 1}}},
{{{2, 0}, {1, 1}, {2, 2}}},
{{{4, 0}, {0, 1}, {4, 2}}},
{{{0, 0}, {0, 1}, {1, 1}}},
};
static const SkDPoint inPoint[]= {
{1, 1.2},
{1, 0.8},
{1.8, 1},
{1.5, 1},
{0.4999, 0.5}, // was 0.5, 0.5; points on the hull are considered outside
};
static const SkDPoint outPoint[]= {
{1, 1.6},
{1, 1.5},
{2.2, 1},
{1.5, 1.5},
{1.1, 0.5},
};
static const size_t tests_count = SK_ARRAY_COUNT(tests);
DEF_TEST(PathOpsDQuad, reporter) {
for (size_t index = 0; index < tests_count; ++index) {
const SkDQuad& quad = tests[index];
SkASSERT(ValidQuad(quad));
bool result = quad.pointInHull(inPoint[index]);
if (!result) {
SkDebugf("%s [%d] expected in hull\n", __FUNCTION__, index);
REPORTER_ASSERT(reporter, 0);
}
result = quad.pointInHull(outPoint[index]);
if (result) {
SkDebugf("%s [%d] expected outside hull\n", __FUNCTION__, index);
REPORTER_ASSERT(reporter, 0);
}
}
}
DEF_TEST(PathOpsRRect, reporter) {
SkPath path;
SkRRect rRect;
SkRect rect = {135, 143, 250, 177};
SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}};
rRect.setRectRadii(rect, radii);
path.addRRect(rRect);
}

View File

@ -11,15 +11,6 @@
#include "SkPathOpsRect.h"
#include "Test.h"
static const SkDLine lineTests[] = {
{{{2, 1}, {2, 1}}},
{{{2, 1}, {1, 1}}},
{{{2, 1}, {2, 2}}},
{{{1, 1}, {2, 2}}},
{{{3, 0}, {2, 1}}},
{{{3, 2}, {1, 1}}},
};
static const SkDQuad quadTests[] = {
{{{1, 1}, {2, 1}, {0, 2}}},
{{{0, 0}, {1, 1}, {3, 1}}},
@ -34,44 +25,31 @@ static const SkDCubic cubicTests[] = {
{{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
};
static const size_t lineTests_count = SK_ARRAY_COUNT(lineTests);
static const size_t quadTests_count = SK_ARRAY_COUNT(quadTests);
static const size_t cubicTests_count = SK_ARRAY_COUNT(cubicTests);
static void setRawBounds(const SkDQuad& quad, SkDRect* rect) {
rect->set(quad[0]);
rect->add(quad[1]);
rect->add(quad[2]);
}
static void setRawBounds(const SkDCubic& cubic, SkDRect* rect) {
rect->set(cubic[0]);
rect->add(cubic[1]);
rect->add(cubic[2]);
rect->add(cubic[3]);
}
DEF_TEST(PathOpsDRect, reporter) {
size_t index;
SkDRect rect, rect2;
for (index = 0; index < lineTests_count; ++index) {
const SkDLine& line = lineTests[index];
SkASSERT(ValidLine(line));
rect.setBounds(line);
REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX));
REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY));
REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX));
REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY));
rect2.set(line[0]);
rect2.add(line[1]);
REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX));
REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY));
REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX));
REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY));
REPORTER_ASSERT(reporter, rect.contains(line[0]));
REPORTER_ASSERT(reporter, rect.intersects(&rect2));
}
for (index = 0; index < quadTests_count; ++index) {
const SkDQuad& quad = quadTests[index];
SkASSERT(ValidQuad(quad));
rect.setRawBounds(quad);
REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX,
SkTMin(quad[1].fX, quad[2].fX)));
REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY,
SkTMin(quad[1].fY, quad[2].fY)));
REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX,
SkTMax(quad[1].fX, quad[2].fX)));
REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY,
SkTMax(quad[1].fY, quad[2].fY)));
setRawBounds(quad, &rect);
rect2.setBounds(quad);
REPORTER_ASSERT(reporter, rect.intersects(&rect2));
REPORTER_ASSERT(reporter, rect.intersects(rect2));
// FIXME: add a recursive box subdivision method to verify that tight bounds is correct
SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
REPORTER_ASSERT(reporter, rect.contains(leftTop));
@ -81,17 +59,9 @@ DEF_TEST(PathOpsDRect, reporter) {
for (index = 0; index < cubicTests_count; ++index) {
const SkDCubic& cubic = cubicTests[index];
SkASSERT(ValidCubic(cubic));
rect.setRawBounds(cubic);
REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX,
SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX))));
REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY,
SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY))));
REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX,
SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX))));
REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY,
SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY))));
setRawBounds(cubic, &rect);
rect2.setBounds(cubic);
REPORTER_ASSERT(reporter, rect.intersects(&rect2));
REPORTER_ASSERT(reporter, rect.intersects(rect2));
// FIXME: add a recursive box subdivision method to verify that tight bounds is correct
SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
REPORTER_ASSERT(reporter, rect.contains(leftTop));

View File

@ -1,70 +0,0 @@
/*
* 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 "PathOpsTestCommon.h"
#include "SkPathOpsTriangle.h"
#include "Test.h"
static const SkDTriangle tests[] = {
{{{2, 0}, {3, 1}, {2, 2}}},
{{{3, 1}, {2, 2}, {1, 1}}},
{{{3, 0}, {2, 1}, {3, 2}}},
};
static const SkDPoint inPoint[] = {
{2.5, 1},
{2, 1.5},
{2.5, 1},
};
static const SkDPoint outPoint[] = {
{3, 0},
{2.5, 2},
{2.5, 2},
};
static const size_t tests_count = SK_ARRAY_COUNT(tests);
DEF_TEST(PathOpsTriangleUtilities, reporter) {
for (size_t index = 0; index < tests_count; ++index) {
const SkDTriangle& triangle = tests[index];
SkASSERT(ValidTriangle(triangle));
bool result = triangle.contains(inPoint[index]);
if (!result) {
SkDebugf("%s [%d] expected point in triangle\n", __FUNCTION__, index);
REPORTER_ASSERT(reporter, 0);
}
result = triangle.contains(outPoint[index]);
if (result) {
SkDebugf("%s [%d] expected point outside triangle\n", __FUNCTION__, index);
REPORTER_ASSERT(reporter, 0);
}
}
}
static const SkDTriangle oneOff[] = {
{{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753},
{279.25839233398438, 7.7416000366210938}}},
{{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877},
{279.25839233398438, 7.7416000366210938}}}
};
static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff);
DEF_TEST(PathOpsTriangleOneOff, reporter) {
for (size_t index = 0; index < oneOff_count; ++index) {
const SkDTriangle& triangle = oneOff[index];
SkASSERT(ValidTriangle(triangle));
for (int inner = 0; inner < 3; ++inner) {
bool result = triangle.contains(triangle.fPts[inner]);
if (result) {
SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner);
REPORTER_ASSERT(reporter, 0);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@
__SK_FORCE_IMAGE_DECODER_LINKING;
DEFINE_bool2(runFail, f, false, "run tests known to fail.");
DEFINE_bool2(runBinary, f, false, "run tests known to fail binary sect.");
static const char marker[] =
"</div>\n"
@ -47,10 +48,6 @@ static const char* opSuffixes[] = {
"o",
};
static bool gShowPath = false;
static bool gComparePathsAssert = true;
static bool gPathStrAssert = true;
#if DEBUG_SHOW_TEST_NAME
static void showPathData(const SkPath& path) {
SkPath::RawIter iter(path);
@ -82,6 +79,13 @@ static void showPathData(const SkPath& path) {
lastPt = pts[2];
lastPtSet = true;
break;
case SkPath::kConic_Verb:
SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}, //weight=%1.9g\n",
pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
iter.conicWeight());
lastPt = pts[2];
lastPtSet = true;
break;
case SkPath::kCubic_Verb:
SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
@ -273,7 +277,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
return true;
}
static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
const SkPath& two, SkBitmap& bitmap) {
int errors2x2;
SkPath scaledOne, scaledTwo;
@ -282,7 +286,6 @@ static int comparePaths(skiatest::Reporter* reporter, const char* filename, cons
return 0;
}
const int MAX_ERRORS = 9;
REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
}
@ -303,7 +306,7 @@ static void showPathOpPath(const char* testName, const SkPath& one, const SkPath
*gTestOp.append() = shapeOp;
++gTestNo;
SkDebugf(" SkPath path, pathB;\n");
#if DEBUG_SHOW_TEST_NAME
#if 0 && DEBUG_SHOW_TEST_NAME
SkPathOpsDebug::ShowOnePath(a, "path", false);
SkPathOpsDebug::ShowOnePath(b, "pathB", false);
#endif
@ -334,17 +337,14 @@ static int comparePaths(skiatest::Reporter* reporter, const char* testName, cons
return 0;
}
if (errors2x2 == 0) {
if (gShowPath) {
showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
}
return 0;
}
if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
if (errors2x2 > MAX_ERRORS) {
SkAutoMutexAcquire autoM(compareDebugOut3);
SkDebugf("\n*** this test fails ***\n");
showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
REPORTER_ASSERT(reporter, 0);
} else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
} else if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
SkAutoMutexAcquire autoM(compareDebugOut4);
showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
}
@ -367,7 +367,7 @@ static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
#if 0
outFile.writeText("<div id=\"");
outFile.writeText("\n<div id=\"");
writeTestName(nameSuffix, outFile);
outFile.writeText("\">\n");
if (pathPrefix) {
@ -412,15 +412,12 @@ static void outputToStream(const char* pathStr, const char* pathPrefix, const ch
}
SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
const char* pathStr) {
SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
path.setFillType(fillType);
#if DEBUG_SHOW_TEST_NAME
if (gShowPath) {
SkPathOpsDebug::ShowOnePath(path, "path", false);
}
#endif
state.fReporter->bumpTestCount();
if (!Simplify(path, &out)) {
SkDebugf("%s did not expect failure\n", __FUNCTION__);
REPORTER_ASSERT(state.fReporter, 0);
@ -430,7 +427,7 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
return true;
}
int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
if (result && gPathStrAssert) {
if (result) {
SkAutoMutexAcquire autoM(simplifyDebugOut);
char temp[8192];
sk_bzero(temp, sizeof(temp));
@ -450,23 +447,39 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
return result == 0;
}
bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
#if DEBUG_SHOW_TEST_NAME
static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
bool checkFail) {
#if 0 && DEBUG_SHOW_TEST_NAME
showPathData(path);
#endif
SkPath out;
if (!Simplify(path, &out)) {
SkDebugf("%s did not expect failure\n", __FUNCTION__);
SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
REPORTER_ASSERT(reporter, 0);
return false;
}
SkBitmap bitmap;
int result = comparePaths(reporter, filename, path, out, bitmap);
if (result && gPathStrAssert) {
int errors = comparePaths(reporter, filename, path, out, bitmap);
if (!checkFail) {
if (!errors) {
SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
REPORTER_ASSERT(reporter, 0);
return false;
}
} else if (errors) {
REPORTER_ASSERT(reporter, 0);
}
reporter->bumpTestCount();
return result == 0;
return errors == 0;
}
bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
return inner_simplify(reporter, path, filename, true);
}
bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
bool checkFail) {
return inner_simplify(reporter, path, filename, checkFail);
}
#if DEBUG_SHOW_TEST_NAME
@ -480,7 +493,7 @@ static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName, bool threaded, bool expectSuccess) {
#if DEBUG_SHOW_TEST_NAME
#if 0 && DEBUG_SHOW_TEST_NAME
showName(a, b, shapeOp);
#endif
SkPath out;
@ -489,7 +502,7 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
REPORTER_ASSERT(reporter, 0);
return false;
}
if (threaded && !reporter->verbose()) {
if (!reporter->verbose()) {
return true;
}
SkPath pathOut, scaledPathOut;
@ -518,7 +531,7 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
scaledOut.setFillType(out.getFillType());
int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
a, b, shapeOp, scale, expectSuccess);
if (result && gPathStrAssert) {
if (result) {
REPORTER_ASSERT(reporter, 0);
}
reporter->bumpTestCount();
@ -604,6 +617,7 @@ void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
void (*firstTest)(skiatest::Reporter* , const char* filename),
void (*skipTest)(skiatest::Reporter* , const char* filename),
void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
size_t index;
if (firstTest) {
@ -612,8 +626,7 @@ void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
--index;
}
#if DEBUG_SHOW_TEST_NAME
SkDebugf("<div id=\"%s\">\n", tests[index].str);
SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
#endif
(*tests[index].fun)(reporter, tests[index].str);
if (tests[index].fun == stopTest) {
@ -622,11 +635,14 @@ void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
}
index = reverse ? count - 1 : 0;
size_t last = reverse ? 0 : count - 1;
bool foundSkip = !skipTest;
do {
if (tests[index].fun != firstTest) {
if (tests[index].fun == skipTest) {
foundSkip = true;
}
if (foundSkip && tests[index].fun != firstTest) {
#if DEBUG_SHOW_TEST_NAME
SkDebugf("<div id=\"%s\">\n", tests[index].str);
SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
#endif
(*tests[index].fun)(reporter, tests[index].str);
}

View File

@ -17,6 +17,7 @@
#include "Test.h"
DECLARE_bool(runFail);
DECLARE_bool(runBinary);
struct PathOpsThreadState;
@ -26,7 +27,8 @@ struct TestDesc {
};
//extern int comparePaths(const SkPath& one, const SkPath& two);
extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
extern int comparePaths(skiatest::Reporter* reporter, const char* filename,
const SkPath& one, const SkPath& two, SkBitmap& bitmap);
extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths);
extern void showOp(const SkPathOp op);
extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
@ -40,6 +42,8 @@ extern bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, co
extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
const char* pathStr);
extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
const char* filename, bool checkFail);
void initializeTests(skiatest::Reporter* reporter, const char* testName);
void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
@ -47,6 +51,7 @@ void outputProgress(char* ramStr, const char* pathStr, SkPathOp op);
void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
void (*firstTest)(skiatest::Reporter* , const char* filename),
void (*skipTest)(skiatest::Reporter* , const char* filename),
void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse);
void ShowTestArray();
void ShowTestName(PathOpsThreadState* data, int a, int b, int c, int d);

View File

@ -121,11 +121,6 @@ path.close();
}
static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) {
#ifdef SK_BUILD_FOR_ANDROID
if (!FLAGS_runFail) {
return; // fails on nexus 9 in release, possibly related to fused multiply-add
}
#endif
SkPath path;
path.setFillType((SkPath::FillType) 1);
path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
@ -219,11 +214,6 @@ path.close();
}
static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) {
#ifdef SK_BUILD_FOR_ANDROID
if (!FLAGS_runFail) {
return; // fails on nexus 9 in release, possibly related to fused multiply-add
}
#endif
SkPath path;
path.setFillType((SkPath::FillType) 1);
path.moveTo(-47.1494f, 4.35143f);
@ -243,7 +233,7 @@ path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc
path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
@ -264,7 +254,7 @@ static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
path.quadTo(-39.8065f, 18.9507f, -43.0072f, 19.8086f);
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) {
@ -505,7 +495,7 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
@ -595,7 +585,7 @@ path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) {
@ -630,7 +620,7 @@ path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) {
@ -719,7 +709,7 @@ path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) {
@ -942,7 +932,7 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
@ -1141,7 +1131,6 @@ path.close();
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
//SkOpSegment.cpp:3475: failed assertion "firstAngle"
static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType((SkPath::FillType) 1);
@ -1447,7 +1436,7 @@ path.quadTo(SkBits2Float(0x421fbff7), SkBits2Float(0x41f8ceed), SkBits2Float(0x4
path.close();
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) {
@ -1542,10 +1531,10 @@ path.close();
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
// SkOpSegment.cpp:4010: failed assertion "span->fOppSum == -0x7FFFFFFF || span->fOppSum == oppWinding
static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType((SkPath::FillType) 1);
#if 00
path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e));
path.quadTo(SkBits2Float(0x418ad549), SkBits2Float(0xc24fca02), SkBits2Float(0x41981613), SkBits2Float(0xc2444f40));
@ -1556,6 +1545,8 @@ path.quadTo(SkBits2Float(0x40d9eeca), SkBits2Float(0xc218d592), SkBits2Float(0x4
path.quadTo(SkBits2Float(0x405fd0f0), SkBits2Float(0xc22fcb17), SkBits2Float(0x408b5c58), SkBits2Float(0xc23c98a3));
path.quadTo(SkBits2Float(0x40a6d038), SkBits2Float(0xc249662f), SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
path.close();
#endif
#if 000
path.moveTo(SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
path.quadTo(SkBits2Float(0xc2113c71), SkBits2Float(0xc2240440), SkBits2Float(0xc203fb34), SkBits2Float(0xc22403dc));
path.quadTo(SkBits2Float(0xc1ed73ee), SkBits2Float(0xc2240379), SkBits2Float(0xc1dab5b7), SkBits2Float(0xc21aa3d1));
@ -1566,6 +1557,8 @@ path.quadTo(SkBits2Float(0xc2113e50), SkBits2Float(0xc1c8087f), SkBits2Float(0xc
path.quadTo(SkBits2Float(0xc223fc87), SkBits2Float(0xc1ed871e), SkBits2Float(0xc223fc24), SkBits2Float(0xc20404cc));
path.quadTo(SkBits2Float(0xc223fbc0), SkBits2Float(0xc2114609), SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
path.close();
#endif
#if 00
path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
@ -1582,11 +1575,15 @@ path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc
path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
path.close();
#endif
#if 01
path.moveTo(SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
path.lineTo(SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
path.lineTo(SkBits2Float(0xc2533af7), SkBits2Float(0x41624f68));
path.quadTo(SkBits2Float(0xc2533a8e), SkBits2Float(0x41625591), SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
path.close();
#endif
#if 0
path.moveTo(SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
path.quadTo(SkBits2Float(0x41ed82ea), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
@ -1602,6 +1599,8 @@ path.quadTo(SkBits2Float(0x41dab5bf), SkBits2Float(0x41dac7c8), SkBits2Float(0x4
path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
path.quadTo(SkBits2Float(0x41dac293), SkBits2Float(0x41dabaf3), SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
path.close();
#endif
#if 00001
path.moveTo(SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c));
path.quadTo(SkBits2Float(0xc218dd36), SkBits2Float(0x40d9a0b8), SkBits2Float(0xc2156c96), SkBits2Float(0x411fdb9a));
@ -1622,10 +1621,12 @@ path.lineTo(SkBits2Float(0xc2533b22), SkBits2Float(0x41624cea));
path.quadTo(SkBits2Float(0xc256a842), SkBits2Float(0x412f19c8), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
path.close();
#endif
SkPath path1(path);
path.reset();
path.setFillType((SkPath::FillType) 0);
#if 01
path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
path.quadTo(SkBits2Float(0xc256a8ae), SkBits2Float(0x412f1cb2), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
@ -1636,9 +1637,9 @@ path.quadTo(SkBits2Float(0xc211faaa), SkBits2Float(0x41534d02), SkBits2Float(0xc
path.quadTo(SkBits2Float(0xc21f3c59), SkBits2Float(0x41979082), SkBits2Float(0xc22c0a07), SkBits2Float(0x419e6c7a));
path.quadTo(SkBits2Float(0xc238d7b6), SkBits2Float(0x41a54872), SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
path.close();
#endif
SkPath path2(path);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) {
@ -2197,13 +2198,10 @@ path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x4
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) {
if (!FLAGS_runFail) { // FIXME: asserts in alignSpanState
return;
}
SkPath path;
path.setFillType((SkPath::FillType) 1);
path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
@ -2396,6 +2394,7 @@ path.close();
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_2674194;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
@ -2440,5 +2439,5 @@ DEF_TEST(PathOpsFuzz763, reporter) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
}

View File

@ -11,6 +11,9 @@
// FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
static const SkDLine tests[][2] = {
{{{{0.00010360032320022583, 1.0172703415155411}, {0.00014114845544099808, 1.0200891587883234}}},
{{{0.00010259449481964111, 1.017270140349865}, {0.00018215179443359375, 1.022890567779541}}}},
#if 0
// these do intersect at a pair of points, but not close enough for check results liking
{{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
@ -82,10 +85,13 @@ static const SkDLine coincidentTests[][2] = {
static const size_t coincidentTests_count = SK_ARRAY_COUNT(coincidentTests);
static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
const SkIntersections& ts) {
const SkIntersections& ts, bool nearAllowed) {
for (int i = 0; i < ts.used(); ++i) {
SkDPoint result1 = line1.ptAtT(ts[0][i]);
SkDPoint result2 = line2.ptAtT(ts[1][i]);
if (nearAllowed && result1.roughlyEqual(result2)) {
continue;
}
if (!result1.approximatelyEqual(result2) && !ts.nearlySame(i)) {
REPORTER_ASSERT(reporter, ts.used() != 1);
result2 = line2.ptAtT(ts[1][i ^ 1]);
@ -98,14 +104,16 @@ static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, co
}
}
static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) {
static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
bool nearAllowed) {
SkASSERT(ValidLine(line1));
SkASSERT(ValidLine(line2));
SkIntersections i;
i.allowNear(nearAllowed);
int pts = i.intersect(line1, line2);
REPORTER_ASSERT(reporter, pts);
REPORTER_ASSERT(reporter, pts == i.used());
check_results(reporter, line1, line2, i);
check_results(reporter, line1, line2, i, nearAllowed);
if (line1[0] == line1[1] || line2[0] == line2[1]) {
return;
}
@ -114,28 +122,28 @@ static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const Sk
double right = SkTMax(line1[0].fX, line1[1].fX);
SkIntersections ts;
ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
check_results(reporter, line2, line1, ts);
check_results(reporter, line2, line1, ts, nearAllowed);
}
if (line2[0].fY == line2[1].fY) {
double left = SkTMin(line2[0].fX, line2[1].fX);
double right = SkTMax(line2[0].fX, line2[1].fX);
SkIntersections ts;
ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
check_results(reporter, line1, line2, ts);
check_results(reporter, line1, line2, ts, nearAllowed);
}
if (line1[0].fX == line1[1].fX) {
double top = SkTMin(line1[0].fY, line1[1].fY);
double bottom = SkTMax(line1[0].fY, line1[1].fY);
SkIntersections ts;
ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
check_results(reporter, line2, line1, ts);
check_results(reporter, line2, line1, ts, nearAllowed);
}
if (line2[0].fX == line2[1].fX) {
double top = SkTMin(line2[0].fY, line2[1].fY);
double bottom = SkTMax(line2[0].fY, line2[1].fY);
SkIntersections ts;
ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
check_results(reporter, line1, line2, ts);
check_results(reporter, line1, line2, ts, nearAllowed);
}
reporter->bumpTestCount();
}
@ -148,7 +156,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
int pts = ts.intersect(line1, line2);
REPORTER_ASSERT(reporter, pts == 2);
REPORTER_ASSERT(reporter, pts == ts.used());
check_results(reporter, line1, line2, ts);
check_results(reporter, line1, line2, ts, false);
if (line1[0] == line1[1] || line2[0] == line2[1]) {
return;
}
@ -159,7 +167,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
REPORTER_ASSERT(reporter, pts == 2);
REPORTER_ASSERT(reporter, pts == ts.used());
check_results(reporter, line2, line1, ts);
check_results(reporter, line2, line1, ts, false);
}
if (line2[0].fY == line2[1].fY) {
double left = SkTMin(line2[0].fX, line2[1].fX);
@ -168,7 +176,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
REPORTER_ASSERT(reporter, pts == 2);
REPORTER_ASSERT(reporter, pts == ts.used());
check_results(reporter, line1, line2, ts);
check_results(reporter, line1, line2, ts, false);
}
if (line1[0].fX == line1[1].fX) {
double top = SkTMin(line1[0].fY, line1[1].fY);
@ -177,7 +185,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
REPORTER_ASSERT(reporter, pts == 2);
REPORTER_ASSERT(reporter, pts == ts.used());
check_results(reporter, line2, line1, ts);
check_results(reporter, line2, line1, ts, false);
}
if (line2[0].fX == line2[1].fX) {
double top = SkTMin(line2[0].fY, line2[1].fY);
@ -186,7 +194,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
REPORTER_ASSERT(reporter, pts == 2);
REPORTER_ASSERT(reporter, pts == ts.used());
check_results(reporter, line1, line2, ts);
check_results(reporter, line1, line2, ts, false);
}
reporter->bumpTestCount();
}
@ -201,7 +209,7 @@ DEF_TEST(PathOpsLineIntersection, reporter) {
for (index = 0; index < tests_count; ++index) {
const SkDLine& line1 = tests[index][0];
const SkDLine& line2 = tests[index][1];
testOne(reporter, line1, line2);
testOne(reporter, line1, line2, true);
}
for (index = 0; index < noIntersect_count; ++index) {
const SkDLine& line1 = noIntersect[index][0];
@ -217,8 +225,13 @@ DEF_TEST(PathOpsLineIntersection, reporter) {
DEF_TEST(PathOpsLineIntersectionOneOff, reporter) {
int index = 0;
SkASSERT(index < (int) tests_count);
testOne(reporter, tests[index][0], tests[index][1]);
testOne(reporter, tests[1][0], tests[1][1]);
testOne(reporter, tests[index][0], tests[index][1], true);
}
DEF_TEST(PathOpsLineIntersectionExactOneOff, reporter) {
int index = 0;
SkASSERT(index < (int) tests_count);
testOne(reporter, tests[index][0], tests[index][1], false);
}
DEF_TEST(PathOpsLineIntersectionOneCoincident, reporter) {

View File

@ -27,6 +27,10 @@ static void testOpCubicsMain(PathOpsThreadState* data) {
SkPath pathA, pathB;
if (progress) {
char* str = pathStr;
const int loopNo = 129;
str += sprintf(str, "static void cubicOp%d(skiatest::Reporter* reporter,"
" const char* filename) {\n", loopNo);
str += sprintf(str, " SkPath path, pathB;\n");
str += sprintf(str, " path.setFillType(SkPath::k%s_FillType);\n",
e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
? "EvenOdd" : "?UNDEFINED");
@ -41,6 +45,9 @@ static void testOpCubicsMain(PathOpsThreadState* data) {
str += sprintf(str, " pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
state.fB, state.fA, state.fD, state.fC);
str += sprintf(str, " pathB.close();\n");
str += sprintf(str, " testPathOp(reporter, path, pathB, kDifference_PathOp,"
" filename);\n");
str += sprintf(str, "}\n");
}
pathA.setFillType((SkPath::FillType) e);
pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));

View File

@ -7,6 +7,24 @@
#include "PathOpsExtendedTest.h"
#include "PathOpsThreadedCommon.h"
static int add_point(char* str, SkScalar x, SkScalar y) {
int result;
int asInt = SkScalarRoundToInt(x);
if (SkIntToScalar(asInt) == x) {
result = sprintf(str, "%d", asInt);
} else {
result = sprintf(str, "%1.9gf", x);
}
result += sprintf(str + result, ",");
asInt = SkScalarRoundToInt(y);
if (SkIntToScalar(asInt) == y) {
result += sprintf(str + result, "%d", asInt);
} else {
result += sprintf(str + result, "%1.9gf", y);
}
return result;
}
static void testOpLoopsMain(PathOpsThreadState* data) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
@ -35,14 +53,27 @@ static void testOpLoopsMain(PathOpsThreadState* data) {
SkPath pathA, pathB;
if (progress) {
char* str = pathStr;
const int loopNo = 7;
str += sprintf(str, "static void loop%d(skiatest::Reporter* reporter,"
" const char* filename) {\n", loopNo);
str += sprintf(str, " SkPath path, pathB;\n");
str += sprintf(str, " path.moveTo(%d,%d);\n", a, b);
str += sprintf(str, " path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
c, d, endC.fX, endC.fY, endD.fX, endD.fY);
str += sprintf(str, " path.cubicTo(%d,%d, ", c, d);
str += add_point(str, endC.fX, endC.fY);
str += sprintf(str, ", ");
str += add_point(str, endD.fX, endD.fY);
str += sprintf(str, ");\n");
str += sprintf(str, " path.close();\n");
str += sprintf(str, " pathB.moveTo(%d,%d);\n", c, d);
str += sprintf(str, " pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n",
endC.fX, endC.fY, endD.fX, endD.fY, a, b);
str += sprintf(str, " pathB.cubicTo(");
str += add_point(str, endC.fX, endC.fY);
str += sprintf(str, ", ");
str += add_point(str, endD.fX, endD.fY);
str += sprintf(str, ", %d,%d);\n", a, b);
str += sprintf(str, " pathB.close();\n");
str += sprintf(str, " testPathOp(reporter, path, pathB, kIntersect_PathOp,"
" filename);\n");
str += sprintf(str, "}\n");
}
pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b));
pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY);
@ -62,9 +93,6 @@ static void testOpLoopsMain(PathOpsThreadState* data) {
}
DEF_TEST(PathOpsOpLoopsThreaded, reporter) {
if (!FLAGS_runFail) {
return;
}
initializeTests(reporter, "cubicOp");
PathOpsThreadedTestRunner testRunner(reporter);
for (int a = 0; a < 6; ++a) { // outermost
@ -84,9 +112,6 @@ finish:
}
DEF_TEST(PathOpsOpLoops, reporter) {
if (!FLAGS_runFail) {
return;
}
initializeTests(reporter, "cubicOp");
PathOpsThreadState state;
state.fReporter = reporter;

View File

@ -713,11 +713,6 @@ static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// this fails to detect a cubic/cubic intersection
// the slight overlap is missed when the cubics are approximated by quadratics
// and the subsequent line/cubic intersection also (correctly) misses the intersection
// if the line/cubic was a matching line/approx.quadratic then the missing intersection
// could have been detected
static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
@ -1795,9 +1790,6 @@ static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// this fails because the pair of nearly coincident cubics intersect at the ends
// but the line connected to one of the cubics at the same point does not intersect
// the other
static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1934,7 +1926,7 @@ static void issue1417(skiatest::Reporter* reporter, const char* filename) {
path2.lineTo(113.232177734375f, 173.5789947509765625f);
path2.lineTo(113.232177734375f, 173.5789947509765625f);
path2.close();
// FIXME : difficult data, circle back later
testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
}
@ -2056,9 +2048,6 @@ static void rectOp3x(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
}
// this fails to generate two interior line segments
// an earlier pathops succeeded, but still failed to generate one interior line segment
// (but was saved by assemble, which works around a single line missing segment)
static void issue1435(skiatest::Reporter* reporter, const char* filename) {
SkPath path1;
path1.moveTo(160, 60);
@ -2256,7 +2245,7 @@ static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
}
static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) { // add t cancel
static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(-1.24344979e-014f, 348);
@ -2277,7 +2266,7 @@ static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filenam
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) { // add t cancel
static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(1.99840144e-015f, 494);
@ -2300,7 +2289,7 @@ static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) { // partial coincidence
static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(670.537415f, 285);
@ -2326,7 +2315,7 @@ static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filena
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpact_com43(skiatest::Reporter* reporter, const char* filename) { // bridge op
static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(1.45716772e-016f, 924.336121f);
@ -2351,7 +2340,7 @@ static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) { // zero span
static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(320.097229f, 628.573669f);
@ -2375,7 +2364,7 @@ static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) { // find chase op
static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(0, 926);
@ -2394,7 +2383,7 @@ static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { // calc common
static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(205.605804f, 142.334625f);
@ -2418,7 +2407,7 @@ static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) { // mark and chase winding
static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(-4.4408921e-016f, 682.5f);
@ -2439,7 +2428,7 @@ static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename)
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) { // cubic/cubic intersect
static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(324.071075f, 845.071045f);
@ -2466,7 +2455,7 @@ static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filena
pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711);
pathB.lineTo(317, 711);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
}
static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) {
@ -2724,7 +2713,6 @@ static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filen
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// this fails because cubic/quad misses an intersection (failure is isolated in c/q int test)
static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2748,7 +2736,7 @@ static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f);
pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f);
pathB.close();
testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) {
@ -3247,7 +3235,6 @@ static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// triggers addSimpleAngle with non-zero argument
static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
@ -3282,7 +3269,7 @@ static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
pathB.moveTo(1, 3);
pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
pathB.close();
testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) {
@ -3464,7 +3451,7 @@ static void issue2753(skiatest::Reporter* reporter, const char* filename) {
path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f);
path2.close();
testPathOpCheck(reporter, path1, path2, kUnion_PathOp, filename, FLAGS_runFail);
testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
}
static void issue2808(skiatest::Reporter* reporter, const char* filename) {
@ -3509,12 +3496,335 @@ static void cubicOp115(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void testRect1(skiatest::Reporter* reporter, const char* filename) {
SkPath path, path2;
path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
// path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
testPathOp(reporter, path, path2, kUnion_PathOp, filename);
}
static void testRect2(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
path.addRect(4, 4, 5, 5, SkPath::kCW_Direction);
pathB.setFillType(SkPath::kEvenOdd_FillType);
pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp116(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(4,6, 2,0, 2,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,2);
pathB.cubicTo(0,2, 1,0, 6,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp117(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(4,5, 6,0, 1,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(0,1, 1,0, 5,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp118(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(4,6, 5,1, 6,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(2,6, 1,0, 6,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void loop1(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,1);
path.cubicTo(1,5, -5.66666651f,3.33333349f, 8.83333302f,2.33333349f);
path.close();
pathB.moveTo(1,5);
pathB.cubicTo(-5.66666651f,3.33333349f, 8.83333302f,2.33333349f, 0,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
#include "SkPathOpsCubic.h"
static void loop1asQuad(skiatest::Reporter* reporter, const char* filename) {
SkDCubic c1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}};
SkDCubic c2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}};
double c1InflectionTs[2], c2InflectionTs[2];
SkDEBUGCODE(int c1InfTCount =) c1.findInflections(c1InflectionTs);
SkASSERT(c1InfTCount == 2);
SkDEBUGCODE(int c2InfTCount =) c2.findInflections(c2InflectionTs);
SkASSERT(c2InfTCount == 1);
SkASSERT(c1InflectionTs[0] > c1InflectionTs[1]);
SkDCubicPair c1pair = c1.chopAt(c1InflectionTs[0]);
SkDCubicPair c1apair = c1pair.first().chopAt(c1InflectionTs[1]);
SkDCubicPair c2pair = c2.chopAt(c2InflectionTs[0]);
SkDQuad q1[2] = { c1pair.first().toQuad(), c1pair.second().toQuad() };
SkDQuad q1a[2] = { c1apair.first().toQuad(), c1apair.second().toQuad() };
SkDQuad q2[2] = { c2pair.first().toQuad(), c2pair.second().toQuad() };
SkPath path, pathB;
path.moveTo(q1a[0].fPts[0].asSkPoint());
path.quadTo(q1a[0].fPts[1].asSkPoint(), q1a[0].fPts[2].asSkPoint());
path.quadTo(q1a[1].fPts[1].asSkPoint(), q1a[1].fPts[2].asSkPoint());
path.quadTo(q1[1].fPts[1].asSkPoint(), q1[1].fPts[2].asSkPoint());
path.close();
pathB.moveTo(q2[0].fPts[0].asSkPoint());
pathB.quadTo(q2[0].fPts[1].asSkPoint(), q2[0].fPts[2].asSkPoint());
pathB.quadTo(q2[1].fPts[1].asSkPoint(), q2[1].fPts[2].asSkPoint());
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void loop2(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,1);
path.cubicTo(3,4, 3.f,4.f, 4.5f,1.5f);
path.close();
pathB.moveTo(3,4);
pathB.cubicTo(3.f,4.f, 4.5f,1.5f, 0,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void loop3(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,1);
path.cubicTo(3,5, -3.66666651f,0, 10.5f,-1.66666651f);
path.close();
pathB.moveTo(3,5);
pathB.cubicTo(-3.66666651f,0, 10.5f,-1.66666651f, 0,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void loop4(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,5);
path.cubicTo(1,5, 1,4, 0.833333313f,3);
path.close();
pathB.moveTo(1,5);
pathB.cubicTo(1,4, 0.833333313f,3, 0,5);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
#include "SkParsePath.h"
static void issue3517(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
const char str[] = "M31.35 57.75L31.35 57.75C31.9 57.7486 32.45 57.7948 33 57.7413C33.55 57.6878 34.1 57.5014 34.65 57.4291C35.2 57.3569 35.75 57.3223 36.3 57.3079C36.85 57.2935 37.4 57.3143 37.95 57.3428C38.5 57.3712 39.05 57.4112 39.6 57.4786C40.15 57.546 40.7 57.7029 41.25 57.7472C41.8 57.7916 42.35 57.7962 42.9 57.7445C43.45 57.6928 44 57.5345 44.55 57.4373C45.1 57.34 45.65 57.2115 46.2 57.1611C46.75 57.1107 47.3 57.1371 47.85 57.1349C48.4 57.1327 48.95 57.144 49.5 57.1478C50.05 57.1516 50.6 57.1553 51.15 57.1579C51.7 57.1605 52.25 57.1601 52.8 57.1634C53.35 57.1667 53.9 57.1731 54.45 57.1776C55 57.182 55.55 57.1916 56.1 57.19C56.65 57.1884 57.2 57.178 57.75 57.168C58.3 57.158 58.85 57.1355 59.4 57.1299C59.95 57.1243 60.5 57.1338 61.05 57.1345C61.6 57.1352 62.15 57.124 62.7 57.134C63.25 57.1441 63.8 57.1731 64.35 57.195C64.9 57.2169 65.45 57.2532 66 57.2655C66.55 57.2778 67.1 57.2647 67.65 57.2687C68.2 57.2728 68.75 57.267 69.3 57.2896C69.85 57.3122 70.4 57.371 70.95 57.4044C71.5 57.4377 72.05 57.4668 72.6 57.4896C73.15 57.5123 73.7 57.545 74.25 57.5408C74.8 57.5365 75.35 57.5068 75.9 57.4641C76.45 57.4213 77 57.3244 77.55 57.2842C78.1 57.244 78.65 57.2163 79.2 57.2228C79.75 57.2293 80.3 57.29 80.85 57.3232C81.4 57.3563 81.95 57.396 82.5 57.4219C83.05 57.4478 83.6 57.4637 84.15 57.4787C84.7 57.4937 85.25 57.5011 85.8 57.5121C86.35 57.523 86.9 57.5411 87.45 57.5444C88 57.5477 88.55 57.5663 89.1 57.5318C89.65 57.4972 90.2 57.3126 90.75 57.337C91.3 57.3613 91.85 57.6088 92.4 57.6776C92.95 57.7465 93.5 57.7379 94.05 57.75C94.6 57.7621 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
SkParsePath::FromSVGString(str, &path);
const char strB[] = "M31.35 57.75L31.35 57.75C31.9 57.7514 32.45 57.7052 33 57.7587C33.55 57.8122 34.1 57.9986 34.65 58.0709C35.2 58.1431 35.75 58.1777 36.3 58.1921C36.85 58.2065 37.4 58.1857 37.95 58.1572C38.5 58.1288 39.05 58.0888 39.6 58.0214C40.15 57.954 40.7 57.7971 41.25 57.7528C41.8 57.7084 42.35 57.7038 42.9 57.7555C43.45 57.8072 44 57.9655 44.55 58.0627C45.1 58.16 45.65 58.2885 46.2 58.3389C46.75 58.3893 47.3 58.3629 47.85 58.3651C48.4 58.3673 48.95 58.356 49.5 58.3522C50.05 58.3484 50.6 58.3447 51.15 58.3421C51.7 58.3395 52.25 58.3399 52.8 58.3366C53.35 58.3333 53.9 58.3269 54.45 58.3224C55 58.318 55.55 58.3084 56.1 58.31C56.65 58.3116 57.2 58.322 57.75 58.332C58.3 58.342 58.85 58.3645 59.4 58.3701C59.95 58.3757 60.5 58.3662 61.05 58.3655C61.6 58.3648 62.15 58.376 62.7 58.366C63.25 58.3559 63.8 58.3269 64.35 58.305C64.9 58.2831 65.45 58.2468 66 58.2345C66.55 58.2222 67.1 58.2353 67.65 58.2313C68.2 58.2272 68.75 58.233 69.3 58.2104C69.85 58.1878 70.4 58.129 70.95 58.0956C71.5 58.0623 72.05 58.0332 72.6 58.0104C73.15 57.9877 73.7 57.955 74.25 57.9592C74.8 57.9635 75.35 57.9932 75.9 58.0359C76.45 58.0787 77 58.1756 77.55 58.2158C78.1 58.256 78.65 58.2837 79.2 58.2772C79.75 58.2707 80.3 58.21 80.85 58.1768C81.4 58.1437 81.95 58.104 82.5 58.0781C83.05 58.0522 83.6 58.0363 84.15 58.0213C84.7 58.0063 85.25 57.9989 85.8 57.9879C86.35 57.977 86.9 57.9589 87.45 57.9556C88 57.9523 88.55 57.9337 89.1 57.9682C89.65 58.0028 90.2 58.1874 90.75 58.163C91.3 58.1387 91.85 57.8912 92.4 57.8224C92.95 57.7535 93.5 57.7621 94.05 57.75C94.6 57.7379 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
SkParsePath::FromSVGString(strB, &pathB);
testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
}
static void cubicOp119(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(3,5, 2,1, 3,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,2);
pathB.cubicTo(1,3, 1,0, 5,3);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void cubicOp120(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,4, 2,1, 4,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,2);
pathB.cubicTo(0,4, 1,0, 4,2);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp121(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(3,4, 3,2, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(3,4, 1,0, 4,3);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// FIXME : haven't debugged this failure yet
static void cubicOp122(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(3,5, 4,1, 4,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,4);
pathB.cubicTo(0,4, 1,0, 5,3);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp123(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,5, 2,0, 6,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,2);
pathB.cubicTo(0,6, 1,0, 5,1);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void loop5(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,2);
path.cubicTo(1,2, 1,1.66666663f, 0.833333313f,1.33333325f);
path.close();
pathB.moveTo(1,2);
pathB.cubicTo(1,1.66666663f, 0.833333313f,1.33333325f, 0,2);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void loop6(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0,1);
path.cubicTo(1,3, -1.66666675f,1.66666663f, 4.16666651f,1.00000012f);
path.close();
pathB.moveTo(1,3);
pathB.cubicTo(-1.66666675f,1.66666663f, 4.16666651f,1.00000012f, 0,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void cubicOp124(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,5, 6,0, 3,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(0,3, 1,0, 5,1);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp125(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(3,6, 3,1, 6,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,3);
pathB.cubicTo(2,6, 1,0, 6,3);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp126(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,3, 6,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(1,2, 1,0, 3,0);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void cubicOp127(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,5, 6,0, 3,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(0,3, 1,0, 5,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void cubicOp128(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,3, 3,2, 5,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(2,5, 1,0, 3,0);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
TEST(cubicOp128),
TEST(cubicOp127),
TEST(cubicOp126),
TEST(cubicOp125),
TEST(cubicOp124),
TEST(loop6),
TEST(loop5),
TEST(cubicOp123),
TEST(cubicOp122),
TEST(cubicOp121),
TEST(cubicOp120),
TEST(cubicOp119),
TEST(loop4),
TEST(loop3),
TEST(loop2),
TEST(loop1asQuad),
TEST(loop1),
TEST(issue3517),
TEST(cubicOp118),
TEST(cubicOp117),
TEST(cubicOp116),
TEST(testRect2),
TEST(testRect1),
TEST(cubicOp115),
TEST(issue2753), // FIXME: pair of cubics miss intersection
TEST(issue2753),
TEST(cubicOp114), // FIXME: curve with inflection is ordered the wrong way
TEST(issue2808),
TEST(cubicOp114asQuad),
@ -3527,8 +3837,6 @@ static struct TestDesc tests[] = {
TEST(kari1),
TEST(quadOp10i),
TEST(cubicOp113),
// fails because a cubic/quadratic intersection is missed
// the internal quad/quad is far enough away from the real cubic/quad that it is rejected
TEST(skpcarrot_is24),
TEST(issue1417),
TEST(cubicOp112),
@ -3555,7 +3863,7 @@ static struct TestDesc tests[] = {
TEST(issue1435),
TEST(cubicOp98x),
TEST(cubicOp97x),
TEST(skpcarpetplanet_ru22), // cubic/cubic intersect detects unwanted coincidence
TEST(skpcarpetplanet_ru22),
TEST(cubicOp96d),
TEST(cubicOp95u),
TEST(skpadbox_lt15),
@ -3747,11 +4055,11 @@ DEF_TEST(PathOpsOp, reporter) {
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
if (runSubTests && runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
}
RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
if (runSubTests && !runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
}
}
@ -3760,7 +4068,7 @@ static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
SkPath pathB;
pathB.addRect(0,0, 300,16);
testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename);
testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
}
// m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
@ -3780,7 +4088,7 @@ static void fuzz433(skiatest::Reporter* reporter, const char* filename) {
path2.lineTo(-170 + 20,11000000000.0f + 20);
path2.close();
testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename);
testPathOpCheck(reporter, path1, path2, kIntersect_PathOp, filename, FLAGS_runFail);
}
static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
@ -3803,7 +4111,7 @@ static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
path2.lineTo(190, 60);
path2.close();
testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
testPathOpCheck(reporter, path1, path2, kUnion_PathOp, filename, FLAGS_runFail);
}
static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
@ -3849,7 +4157,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
path.close();
SkPath path2(path);
testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
@ -3895,7 +4203,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
path.close();
SkPath path2(path);
testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
}
static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
@ -3962,5 +4270,5 @@ DEF_TEST(PathOpsFailOp, reporter) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
RunTestSet(reporter, failTests, failTestCount, 0, 0, false);
RunTestSet(reporter, failTests, failTestCount, NULL, NULL, NULL, false);
}

View File

@ -53,6 +53,36 @@ static void standardTestCases(skiatest::Reporter* reporter) {
}
static const SkDQuad testSet[] = {
{{{4981.9990234375, 1590}, {4981.9990234375, 1617.7523193359375}, {4962.375, 1637.3760986328125}}},
{{{4962.3759765625, 1637.3760986328125}, {4982, 1617.7523193359375}, {4982, 1590}}},
{{{48.7416f, 7.74160004f}, {96.4831848f, -40}, {164, -40}}},
{{{56.9671326f, 0}, {52.7835083f, 3.69968891f}, {48.7416f, 7.74160004f}}},
{{{138, 80}, {147.15692138671875, 80}, {155.12803649902344, 82.86279296875}}},
{{{155.12803649902344, 82.86279296875}, {153.14971923828125, 82.152290344238281}, {151.09841918945312, 81.618133544921875}}},
{{{88, 130}, {88, 131.54483032226562}, {88.081489562988281, 133.0560302734375}}},
{{{88.081489562988281, 133.0560302734375}, {88, 131.54483032226562}, {88, 130}}},
{{{0.59987992,2.14448452}, {0.775417507,1.95606446}, {1.00564098,1.79310346}}},
{{{1.00564098,1.79310346}, {1.25936198,1.615623}, {1.35901463,1.46834028}}},
{{{3,0}, {0,1}, {3,2}}},
{{{2,0}, {1,1}, {2,2}}},
{{{38.656852722167969, 38.656852722167969}, {38.651023864746094, 38.662681579589844}, {38.644744873046875, 38.668937683105469}}},
{{{38.656852722167969, 38.656852722167969}, {36.313709259033203, 41}, {33, 41}}},
{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
{{{4867.623046875, 1637.3760986328125}, {4847.9990234375, 1617.7523193359375}, {4847.9990234375, 1590}}},
{{{4848, 1590}, {4848, 1617.7523193359375}, {4867.6240234375, 1637.3760986328125}}},
{{{102.64466094970703, 165.3553466796875}, {110.79246520996094, 173.50314331054687}, {120.81797790527344, 177.11778259277344}}},
{{{113.232177734375, 173.57899475097656}, {116.88026428222656, 175.69805908203125}, {120.81797790527344, 177.11778259277344}}},
{{{-37.3484879,10.0192947}, {-36.4966316,13.2140198}, {-38.1506348,16.0788383}}},
{{{-38.1462746,16.08918}, {-36.4904327,13.2193804}, {-37.3484879,10.0192947}}},
@ -320,6 +350,8 @@ static void oneOffTests(skiatest::Reporter* reporter) {
}
static const SkDQuad coincidentTestSet[] = {
{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
#if 0
{{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}},
{{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}},
@ -339,9 +371,9 @@ static void coincidentTestOne(skiatest::Reporter* reporter, int test1, int test2
SkASSERT(ValidQuad(quad2));
SkIntersections intersections2;
intersections2.intersect(quad1, quad2);
REPORTER_ASSERT(reporter, intersections2.coincidentUsed() == 2);
REPORTER_ASSERT(reporter, intersections2.used() == 2);
for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
REPORTER_ASSERT(reporter, intersections2.coincidentUsed() >= 2);
REPORTER_ASSERT(reporter, intersections2.used() >= 2);
for (int pt = 0; pt < intersections2.coincidentUsed(); pt += 2) {
double tt1 = intersections2[0][pt];
double tt2 = intersections2[1][pt];
SkDPoint pt1 = quad1.ptAtT(tt1);
@ -390,9 +422,8 @@ static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) {
left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]);
SkDLine diag = {{q1[0], q1[2]}};
left[2] = diag.isLeft(q2[index]);
SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
floatSign(left[1]), floatSign(left[2]),
q1.pointInHull(q2[index]) ? "true" : "false");
SkDebugf("%s left=(%d, %d, %d)\n", __FUNCTION__, floatSign(left[0]),
floatSign(left[1]), floatSign(left[2]));
}
SkDebugf("\n");
}
@ -519,6 +550,14 @@ static void QuadraticIntersection_IntersectionFinder() {
intersectionFinder(0, 1);
}
DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
oneOffTest1(reporter, 10, 11);
}
DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
coincidentTestOne(reporter, 0, 1);
}
DEF_TEST(PathOpsQuadIntersection, reporter) {
oneOffTests(reporter);
coincidentTest(reporter);
@ -527,10 +566,31 @@ DEF_TEST(PathOpsQuadIntersection, reporter) {
if (false) QuadraticIntersection_PointFinder();
}
DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
coincidentTestOne(reporter, 0, 1);
}
#include "SkCommonFlags.h"
DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
oneOffTest1(reporter, 0, 1);
DEF_TEST(PathOpsQuadBinaryProfile, reporter) {
if (!FLAGS_veryVerbose) {
return;
}
SkIntersections intersections;
for (int x = 0; x < 100; ++x) {
int outer = 0;
int inner = outer + 1;
do {
const SkDQuad& quad1 = testSet[outer];
const SkDQuad& quad2 = testSet[inner];
(void) intersections.intersect(quad1, quad2);
REPORTER_ASSERT(reporter, intersections.used() >= 0); // make sure code isn't tossed
inner += 2;
outer += 2;
} while (outer < (int) testSetCount);
}
for (int x = 0; x < 100; ++x) {
for (size_t test = 0; test < quadraticTests_count; ++test) {
const SkDQuad& quad1 = quadraticTests[test][0];
const SkDQuad& quad2 = quadraticTests[test][1];
(void) intersections.intersect(quad1, quad2);
REPORTER_ASSERT(reporter, intersections.used() >= 0); // make sure code isn't tossed
}
}
}

View File

@ -44,10 +44,10 @@ const SkDQuad quadraticLines[] = {
const size_t quadraticLines_count = SK_ARRAY_COUNT(quadraticLines);
static const double F = FLT_EPSILON * 3;
static const double H = FLT_EPSILON * 4;
static const double J = FLT_EPSILON * 5;
static const double K = FLT_EPSILON * 8; // INVESTIGATE: why are larger multiples necessary?
static const double F = FLT_EPSILON * 32;
static const double H = FLT_EPSILON * 32;
static const double J = FLT_EPSILON * 32;
static const double K = FLT_EPSILON * 32; // INVESTIGATE: why are larger multiples necessary?
const SkDQuad quadraticModEpsilonLines[] = {
{{{0, F}, {0, 0}, {1, 0}}},
@ -64,7 +64,7 @@ const SkDQuad quadraticModEpsilonLines[] = {
{{{1, 1+J}, {2, 2}, {3, 3}}},
{{{1, 1}, {3, 3}, {3+F, 3}}},
{{{1, 1}, {1+F, 1}, {2, 2}}},
{{{1, 1}, {2, 2}, {1, 1+F}}},
{{{1, 1}, {2, 2}, {1, 1+K}}},
{{{1, 1}, {1, 1+F}, {3, 3}}},
{{{1+H, 1}, {2, 2}, {4, 4}}}, // no coincident
{{{1, 1+K}, {3, 3}, {4, 4}}},

View File

@ -1,50 +0,0 @@
/*
* 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 "SkDQuadImplicit.h"
#include "SkPathOpsQuad.h"
#include "Test.h"
static bool point_on_parameterized_curve(const SkDQuad& quad, const SkDPoint& point) {
SkDQuadImplicit q(quad);
double xx = q.x2() * point.fX * point.fX;
double xy = q.xy() * point.fX * point.fY;
double yy = q.y2() * point.fY * point.fY;
double x = q.x() * point.fX;
double y = q.y() * point.fY;
double c = q.c();
double sum = xx + xy + yy + x + y + c;
return approximately_zero(sum);
}
static const SkDQuad quadratics[] = {
{{{0, 0}, {1, 0}, {1, 1}}},
};
static const int quadratics_count = (int) SK_ARRAY_COUNT(quadratics);
DEF_TEST(PathOpsQuadImplicit, reporter) {
// split large quadratic
// compare original, parts, to see if the are coincident
for (int index = 0; index < quadratics_count; ++index) {
const SkDQuad& test = quadratics[index];
SkDQuadPair split = test.chopAt(0.5);
SkDQuad midThird = test.subDivide(1.0/3, 2.0/3);
const SkDQuad* quads[] = {
&test, &midThird, &split.first(), &split.second()
};
int quadsCount = (int) SK_ARRAY_COUNT(quads);
for (int one = 0; one < quadsCount; ++one) {
for (int two = 0; two < quadsCount; ++two) {
for (int inner = 0; inner < 3; inner += 2) {
REPORTER_ASSERT(reporter, point_on_parameterized_curve(*quads[one],
(*quads[two])[inner]));
}
REPORTER_ASSERT(reporter, SkDQuadImplicit::Match(*quads[one], *quads[two]));
}
}
}
}

View File

@ -86,10 +86,7 @@ static void dontFailOne(skiatest::Reporter* reporter, int index) {
SkPath result;
result.setFillType(SkPath::kWinding_FillType);
bool success = Simplify(path, &result);
// linux 32 debug fails test 13 because the quad is not treated as linear
// there's no error in the math that I can find -- it looks like a processor
// or compiler bug -- so for now, allow either to work
REPORTER_ASSERT(reporter, success || index == 13);
REPORTER_ASSERT(reporter, success);
REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
reporter->bumpTestCount();
}

View File

@ -3649,7 +3649,7 @@ static void testTriangles2(skiatest::Reporter* reporter, const char* filename) {
testSimplify(reporter, path, filename);
}
// A test this for this case:
// A test for this case:
// contourA has two segments that are coincident
// contourB has two segments that are coincident in the same place
// each ends up with +2/0 pairs for winding count
@ -4506,8 +4506,6 @@ static void testQuads47(skiatest::Reporter* reporter, const char* filename) {
testSimplify(reporter, path, filename);
}
// this fails because there is a short unorderable segment and the unordered state isn't handled
// correctly later on.
static void testQuads46x(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -4679,7 +4677,9 @@ static void testRect3(skiatest::Reporter* reporter, const char* filename) {
testSimplify(reporter, path, filename);
}
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = testCubic2;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static TestDesc tests[] = {
TEST(testRect3),
@ -4738,7 +4738,7 @@ static TestDesc tests[] = {
TEST(testQuadralateral3),
TEST(testDegenerate5),
TEST(testQuad12),
TEST(testQuadratic51), // has unorderable angles
TEST(testQuadratic51),
TEST(testQuad8),
TEST(testQuad11),
TEST(testQuad10),
@ -5111,14 +5111,13 @@ static void (*firstSubTest)(skiatest::Reporter* , const char* filename) = 0;
static bool runSubTests = false;
static bool runSubTestsFirst = false;
static bool runReverse = false;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
DEF_TEST(PathOpsSimplify, reporter) {
if (runSubTests && runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
}
RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
if (runSubTests && !runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
}
}

View File

@ -896,19 +896,6 @@ static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filen
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* this cubic/quad pair
c = 430,280 430,278.895416 473.876068,278 528,278
q = 430,280 430.009796,277.101196 458.703552,275.050262
only intersect at the shared point (430,280)
they sort backwards because the tangent from pt[0] to control pt[1]
c' = (0.00000000000000000, -1.1045837402343750)
q' = (0.0097961425781250000, -2.8988037109375000)
suggests that the quad is counterclockwise of the cubic, when the reverse is true
the angle code is fooled because the control pt[1] of both the quad and cubic
is far away from cubic cntl [2] and quad pt [2].
Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent
Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl?
*/
static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -941,11 +928,6 @@ static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename)
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* didn't investigate thoroughly, but looks to be missorting quad and cubic
{{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}}
{{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}}
decision maker is case 14 leftLessThanRight
*/
static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1079,7 +1061,7 @@ static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) {
pathB.lineTo(149, 675);
pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
}
static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) {
@ -1105,10 +1087,12 @@ static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* fi
pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202);
pathB.lineTo(50, 183);
pathB.close();
// FIXME: this generates quads and cubics that are (correctly) not coincident unlike the old code
// however, somewhere the angles are sorted incorrectly and the winding is computed to be -1/-2
// but I can't find the error
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* cubic ends just above opp line */
static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1128,7 +1112,6 @@ static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filenam
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// pair of lines are not quite coincident, so sorting line/cubic fails (i think)
static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1161,9 +1144,6 @@ static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char*
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// pair of curves have nearly the same initial tangent but are sorting by
// that alone sorts them incorrectly. Need to detect that tangents are nearly
// identical and not reliable by themselves
static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1190,7 +1170,6 @@ static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// nearly identical to lavoixdunord -- to not-quite-coincident lines
static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1223,7 +1202,6 @@ static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* file
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// visually looks like lavoixdunord and www_booking_com
static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1583,11 +1561,6 @@ static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* Three edges are partially coincident. Only one of the three knows about the other two.
Subsequently, when the angle loop is created, it misses one of the edges.
After coincident edges are processed, probably need a check-and-correct that makes sure the
coincidences are all self-consistent.
*/
static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1606,13 +1579,6 @@ static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename)
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* this generates a pair of lines that are essentially coincident; but the next line at a right
angle is not treated as if it intersects at the same point.
There are several of options:
move the intersection of the right angle line to the coincident point (should 'near' do this?
construct another coincident pair from the right angle line to the coincident point
treat the intersection as simple and not coincident
*/
static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1631,7 +1597,6 @@ static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filen
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// fails on angle insert -- haven't investigated yet
static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1657,7 +1622,6 @@ static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* fil
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
/* asserts in alignSpanState looks like a coincident related bug */
static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1694,7 +1658,6 @@ static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* file
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// fails on angle insert
static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1721,7 +1684,6 @@ static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// fails in intersections insert
static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1766,7 +1728,6 @@ static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char*
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// /SkOpContour.cpp:278: failed assertion "!approximately_negative(oEndT - oStartT)
static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1787,7 +1748,6 @@ static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1825,7 +1785,6 @@ static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char*
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1844,7 +1803,6 @@ static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// debugValidateLoop loop sum fails
static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1863,7 +1821,6 @@ static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// debugValidateLoop loop sum fails
static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1882,7 +1839,6 @@ static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkIntersections::lineVertical fUsed >= fMax
static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1904,7 +1860,6 @@ static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* file
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpContour::calcPartialCoincidentWinding SkASSERT(!approximately_negative(endT - startT));
static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1945,7 +1900,6 @@ static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const ch
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// can't find winding of remaining vertical edges
static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -1993,7 +1947,6 @@ static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter,
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// asserts in bridgeOp simple->isClosed()
static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2134,7 +2087,6 @@ static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* f
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// !simple->isClosed()
static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2158,7 +2110,6 @@ static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, con
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// line quad intersection SkIntersections::assert
static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2180,7 +2131,6 @@ static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2303,7 +2253,6 @@ static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* file
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// joinCoincidence / findT / assert
static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2332,7 +2281,6 @@ static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter,
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// joinCoincidence / findT / assert
static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2361,7 +2309,6 @@ static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* f
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// cubic-cubic intersection reduce checkLinear assert
static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2660,7 +2607,6 @@ static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* fil
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2684,7 +2630,6 @@ static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// joinCoincidence / findT / assert
static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2721,7 +2666,6 @@ static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filen
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// !simple->isClosed()
static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2786,7 +2730,6 @@ static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, con
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// !simple->isClosed()
static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2870,7 +2813,6 @@ static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// hangs
static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -2889,7 +2831,6 @@ static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// hangs
static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -3613,12 +3554,7 @@ static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* fi
pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f);
pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590);
pathB.close();
if (FLAGS_runFail) {
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
} else {
// INVESTIGATE : why this normal test takes fail case (test has never worked)
testPathFailOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
@ -3707,7 +3643,6 @@ static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const ch
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// addSimpleAngle: failed assertion "index == count() - 2"
static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -3727,7 +3662,6 @@ static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* fi
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// addTCoincident oPeek = &other->fTs[++oPeekIndex];
static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
@ -3781,11 +3715,7 @@ static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* f
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
// SkOpSegment.cpp:4398: failed assertion "!span->fDone"
static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) {
if (/* 0 && */ !FLAGS_runFail) { // has never worked MUST BE FIXED BEFORE NEXT CHECKIN
return;
}
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(78.6429825f, 3150.97632f);
@ -3814,12 +3744,36 @@ static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filena
pathB.lineTo(77.6666718f, 3153.3335f);
pathB.cubicTo(77.6666718f, 3151.49268f, 79.15905f, 3150, 81, 3150);
pathB.close();
testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(941, 1494);
path.lineTo(941, 1464);
path.lineTo(985, 1464);
path.lineTo(985, 1494);
path.lineTo(941, 1494);
path.close();
SkPath pathB;
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(984.546021f, 1478.31494f);
pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f);
pathB.cubicTo(984.419006f, 1478.63477f, 983.044373f, 1481.90405f, 980.026001f, 1481.276f);
pathB.cubicTo(980.026001f, 1481.276f, 980.02594f, 1481.27576f, 980.025879f, 1481.27527f);
pathB.cubicTo(980.018494f, 1481.22131f, 979.602478f, 1478.38831f, 984.546021f, 1478.31494f);
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
TEST(skpwww_educationalcraft_com_4a),
TEST(skpwww_lptemp_com_3),
TEST(skpwww_shinydemos_com_5),
TEST(skpwww_lptemp_com_5),
@ -3939,11 +3893,10 @@ static struct TestDesc tests[] = {
static const size_t testCount = SK_ARRAY_COUNT(tests);
static bool runReverse = false;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
DEF_TEST(PathOpsSkp, reporter) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
}

View File

@ -8,77 +8,158 @@
#include "SkPathOpsTSect.h"
template<typename TCurve>
void SkTSect<TCurve>::dump() const {
SkDebugf("id=%d", debugID());
const SkTSpan<TCurve>* SkTSect<TCurve>::debugSpan(int id) const {
const SkTSpan<TCurve>* test = fHead;
do {
if (test->debugID() == id) {
return test;
}
} while ((test = test->next()));
#ifndef SK_RELEASE
test = fOppSect->fHead;
do {
if (test->debugID() == id) {
return test;
}
} while ((test = test->next()));
#endif
return NULL;
}
template<typename TCurve>
const SkTSpan<TCurve>* SkTSect<TCurve>::debugT(double t) const {
const SkTSpan<TCurve>* test = fHead;
const SkTSpan<TCurve>* closest = NULL;
double bestDist = DBL_MAX;
do {
if (between(test->fStartT, t, test->fEndT)) {
return test;
}
double testDist = SkTMin(fabs(test->fStartT - t), fabs(test->fEndT - t));
if (bestDist > testDist) {
bestDist = testDist;
closest = test;
}
} while ((test = test->next()));
SkASSERT(closest);
return closest;
}
template<typename TCurve>
void SkTSect<TCurve>::dump() const {
dumpCommon(fHead);
}
extern int gDumpTSectNum;
template<typename TCurve>
void SkTSect<TCurve>::dumpBoth(SkTSect* opp) const {
#if DEBUG_T_SECT_DUMP <= 2
#if DEBUG_T_SECT_DUMP == 2
SkDebugf("%d ", ++gDumpTSectNum);
#endif
this->dump();
SkDebugf(" ");
opp->dump();
SkDebugf("\n");
#elif DEBUG_T_SECT_DUMP == 3
SkDebugf("<div id=\"sect%d\">\n", ++gDumpTSectNum);
if (this->fHead) {
this->dumpCurves();
}
if (opp->fHead) {
PATH_OPS_DEBUG_CODE(opp->dumpCurves());
}
SkDebugf("</div>\n\n");
#endif
}
template<typename TCurve>
void SkTSect<TCurve>::dumpBounds(int id) const {
const SkTSpan<TCurve>* bounded = debugSpan(id);
if (!bounded) {
SkDebugf("no span matches %d\n", id);
return;
}
const SkTSpan<TCurve>* test = bounded->debugOpp()->fHead;
do {
if (test->findOppSpan(bounded)) {
test->dump();
}
} while ((test = test->next()));
}
template<typename TCurve>
void SkTSect<TCurve>::dumpCoin() const {
dumpCommon(fCoincident);
}
template<typename TCurve>
void SkTSect<TCurve>::dumpCoinCurves() const {
dumpCommonCurves(fCoincident);
}
template<typename TCurve>
void SkTSect<TCurve>::dumpCommon(const SkTSpan<TCurve>* test) const {
SkDebugf("id=%d", debugID());
if (!test) {
SkDebugf(" (empty)");
return;
}
do {
SkDebugf(" ");
test->dump(this);
test->dump();
} while ((test = test->next()));
}
template<typename TCurve>
void SkTSect<TCurve>::dumpBoth(const SkTSect& opp) const {
dump();
SkDebugf(" ");
opp.dump();
SkDebugf("\n");
}
template<typename TCurve>
void SkTSect<TCurve>::dumpBoth(const SkTSect* opp) const {
dumpBoth(*opp);
void SkTSect<TCurve>::dumpCommonCurves(const SkTSpan<TCurve>* test) const {
do {
test->fPart.dumpID(test->debugID());
} while ((test = test->next()));
}
template<typename TCurve>
void SkTSect<TCurve>::dumpCurves() const {
const SkTSpan<TCurve>* test = fHead;
do {
test->fPart.dump();
} while ((test = test->next()));
}
#if !DEBUG_T_SECT
template<typename TCurve>
int SkTSpan<TCurve>::debugID(const SkTSect<TCurve>* sect) const {
if (!sect) {
return -1;
}
int id = 1;
const SkTSpan* test = sect->fHead;
while (test && test != this) {
++id;
test = test->fNext;
}
return id;
}
#endif
template<typename TCurve>
void SkTSpan<TCurve>::dumpID(const SkTSect<TCurve>* sect) const {
if (fCoinStart.isCoincident()) {
SkDebugf("%c", '*');
}
SkDebugf("%d", debugID(sect));
if (fCoinEnd.isCoincident()) {
SkDebugf("%c", '*');
}
dumpCommonCurves(fHead);
}
template<typename TCurve>
void SkTSpan<TCurve>::dump(const SkTSect<TCurve>* sect) const {
dumpID(sect);
const SkTSpan<TCurve>* SkTSpan<TCurve>::debugSpan(int id) const {
return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugSpan(id), NULL);
}
template<typename TCurve>
const SkTSpan<TCurve>* SkTSpan<TCurve>::debugT(double t) const {
return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugT(t), NULL);
}
template<typename TCurve>
void SkTSpan<TCurve>::dump() const {
dumpID();
SkDebugf("=(%g,%g) [", fStartT, fEndT);
for (int index = 0; index < fBounded.count(); ++index) {
SkTSpan* span = fBounded[index];
span->dumpID(sect);
span->dumpID();
if (index < fBounded.count() - 1) {
SkDebugf(",");
}
}
SkDebugf("]");
}
template<typename TCurve>
void SkTSpan<TCurve>::dumpBounds(int id) const {
PATH_OPS_DEBUG_CODE(fDebugSect->dumpBounds(id));
}
template<typename TCurve>
void SkTSpan<TCurve>::dumpID() const {
if (fCoinStart.isCoincident()) {
SkDebugf("%c", '*');
}
SkDebugf("%d", debugID());
if (fCoinEnd.isCoincident()) {
SkDebugf("%c", '*');
}
}

View File

@ -9,11 +9,129 @@
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
#include "SkPathOpsTriangle.h"
#include "SkReduceOrder.h"
#include "SkTSort.h"
static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
const double adjust = sqrt(3.) / 36;
SkDCubic sub;
const SkDCubic* cPtr;
if (start == 0) {
cPtr = &cubic;
} else {
// OPTIMIZE: special-case half-split ?
sub = cubic.subDivide(start, 1);
cPtr = &sub;
}
const SkDCubic& c = *cPtr;
double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
double dist = sqrt(dx * dx + dy * dy);
double tDiv3 = precision / (adjust * dist);
double t = SkDCubeRoot(tDiv3);
if (start > 0) {
t = start + (1 - start) * t;
}
return t;
}
static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
double tDiv = calc_t_div(cubic, precision, 0);
if (tDiv >= 1) {
return true;
}
if (tDiv >= 0.5) {
ts->push_back(0.5);
return true;
}
return false;
}
static void addTs(const SkDCubic& cubic, double precision, double start, double end,
SkTArray<double, true>* ts) {
double tDiv = calc_t_div(cubic, precision, 0);
double parts = ceil(1.0 / tDiv);
for (double index = 0; index < parts; ++index) {
double newT = start + (index / parts) * (end - start);
if (newT > 0 && newT < 1) {
ts->push_back(newT);
}
}
}
static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
SkReduceOrder reducer;
int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
if (order < 3) {
return;
}
double inflectT[5];
int inflections = cubic->findInflections(inflectT);
SkASSERT(inflections <= 2);
if (!cubic->endsAreExtremaInXOrY()) {
inflections += cubic->findMaxCurvature(&inflectT[inflections]);
SkASSERT(inflections <= 5);
}
SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
// OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
// own subroutine?
while (inflections && approximately_less_than_zero(inflectT[0])) {
memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
}
int start = 0;
int next = 1;
while (next < inflections) {
if (!approximately_equal(inflectT[start], inflectT[next])) {
++start;
++next;
continue;
}
memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
}
while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
--inflections;
}
SkDCubicPair pair;
if (inflections == 1) {
pair = cubic->chopAt(inflectT[0]);
int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
if (orderP1 < 2) {
--inflections;
} else {
int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
if (orderP2 < 2) {
--inflections;
}
}
}
if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
return;
}
if (inflections == 1) {
pair = cubic->chopAt(inflectT[0]);
addTs(pair.first(), precision, 0, inflectT[0], ts);
addTs(pair.second(), precision, inflectT[0], 1, ts);
return;
}
if (inflections > 1) {
SkDCubic part = cubic->subDivide(0, inflectT[0]);
addTs(part, precision, 0, inflectT[0], ts);
int last = inflections - 1;
for (int idx = 0; idx < last; ++idx) {
part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
}
part = cubic->subDivide(inflectT[last], 1);
addTs(part, precision, inflectT[last], 1, ts);
return;
}
addTs(*cubic, precision, 0, 1, ts);
}
void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
SkTArray<double, true> ts;
cubic.toQuadraticTs(precision, &ts);
toQuadraticTs(&cubic, precision, &ts);
if (ts.count() <= 0) {
SkDQuad quad = cubic.toQuad();
quads.push_back(quad);
@ -180,15 +298,6 @@ bool ValidQuad(const SkDQuad& quad) {
return true;
}
bool ValidTriangle(const SkDTriangle& triangle) {
for (int index = 0; index < 3; ++index) {
if (!ValidPoint(triangle.fPts[index])) {
return false;
}
}
return true;
}
bool ValidVector(const SkDVector& v) {
if (SkDoubleIsNaN(v.fX)) {
return false;

View File

@ -21,7 +21,6 @@ bool ValidLine(const SkDLine& line);
bool ValidPoint(const SkDPoint& pt);
bool ValidPoints(const SkPoint* pts, int count);
bool ValidQuad(const SkDQuad& quad);
bool ValidTriangle(const SkDTriangle& triangle);
bool ValidVector(const SkDVector& v);
#endif

View File

@ -49,14 +49,16 @@ static void testSetTest(skiatest::Reporter* reporter, int index) {
const Curve& iTest = testSet.tests[inner];
SkIntersections* i = combos.append();
sk_bzero(i, sizeof(SkIntersections));
SkDLine oLine = {{ oTest.curve[0], oTest.curve[1] }};
SkDLine iLine = {{ iTest.curve[0], iTest.curve[1] }};
if (oTest.ptCount == 1 && iTest.ptCount == 1) {
i->intersect(*(const SkDLine*) &oTest.curve, *(const SkDLine*) &iTest.curve);
i->intersect(oLine, iLine);
} else if (oTest.ptCount == 1 && iTest.ptCount == 4) {
i->intersect(iTest.curve, *(const SkDLine*) &oTest.curve);
i->intersect(iTest.curve, oLine);
} else if (oTest.ptCount == 4 && iTest.ptCount == 1) {
i->intersect(oTest.curve, *(const SkDLine*) &oTest.curve);
i->intersect(oTest.curve, iLine);
} else if (oTest.ptCount == 4 && iTest.ptCount == 4) {
i->intersectB(oTest.curve, iTest.curve);
i->intersect(oTest.curve, iTest.curve);
} else {
SkASSERT(0);
}

View File

@ -8,7 +8,6 @@
#include "PathOpsThreadedCommon.h"
#include "SkCanvas.h"
#include "SkRandom.h"
#include "SkTArray.h"
#include "SkTSort.h"
#include "Test.h"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff