| /* |
| * 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 "Simplify.h" |
| |
| #undef SkASSERT |
| #define SkASSERT(cond) while (!(cond)) { sk_throw(); } |
| |
| // Terminology: |
| // A Path contains one of more Contours |
| // A Contour is made up of Segment array |
| // A Segment is described by a Verb and a Point array with 2, 3, or 4 points |
| // A Verb is one of Line, Quad(ratic), or Cubic |
| // A Segment contains a Span array |
| // A Span is describes a portion of a Segment using starting and ending T |
| // T values range from 0 to 1, where 0 is the first Point in the Segment |
| // An Edge is a Segment generated from a Span |
| |
| // FIXME: remove once debugging is complete |
| #ifdef SK_DEBUG |
| int gDebugMaxWindSum = SK_MaxS32; |
| int gDebugMaxWindValue = SK_MaxS32; |
| #endif |
| |
| #define PIN_ADD_T 0 |
| #define TRY_ROTATE 1 |
| #define ONE_PASS_COINCIDENCE_CHECK 0 |
| #define APPROXIMATE_CUBICS 1 |
| #define COMPACT_DEBUG_SORT 0 |
| |
| #define DEBUG_UNUSED 0 // set to expose unused functions |
| |
| #if FORCE_RELEASE || defined SK_RELEASE |
| |
| const bool gRunTestsInOneThread = false; |
| |
| #define DEBUG_ACTIVE_OP 0 |
| #define DEBUG_ACTIVE_SPANS 0 |
| #define DEBUG_ACTIVE_SPANS_SHORT_FORM 0 |
| #define DEBUG_ADD_INTERSECTING_TS 0 |
| #define DEBUG_ADD_T_PAIR 0 |
| #define DEBUG_ANGLE 0 |
| #define DEBUG_AS_C_CODE 1 |
| #define DEBUG_ASSEMBLE 0 |
| #define DEBUG_CONCIDENT 0 |
| #define DEBUG_CROSS 0 |
| #define DEBUG_FLOW 0 |
| #define DEBUG_MARK_DONE 0 |
| #define DEBUG_PATH_CONSTRUCTION 0 |
| #define DEBUG_SHOW_WINDING 0 |
| #define DEBUG_SORT 0 |
| #define DEBUG_SWAP_TOP 0 |
| #define DEBUG_UNSORTABLE 0 |
| #define DEBUG_WIND_BUMP 0 |
| #define DEBUG_WINDING 0 |
| #define DEBUG_WINDING_AT_T 0 |
| |
| #else |
| |
| const bool gRunTestsInOneThread = true; |
| |
| #define DEBUG_ACTIVE_OP 1 |
| #define DEBUG_ACTIVE_SPANS 1 |
| #define DEBUG_ACTIVE_SPANS_SHORT_FORM 0 |
| #define DEBUG_ADD_INTERSECTING_TS 1 |
| #define DEBUG_ADD_T_PAIR 1 |
| #define DEBUG_ANGLE 1 |
| #define DEBUG_AS_C_CODE 1 |
| #define DEBUG_ASSEMBLE 1 |
| #define DEBUG_CONCIDENT 1 |
| #define DEBUG_CROSS 0 |
| #define DEBUG_FLOW 1 |
| #define DEBUG_MARK_DONE 1 |
| #define DEBUG_PATH_CONSTRUCTION 1 |
| #define DEBUG_SHOW_WINDING 0 |
| #define DEBUG_SORT 1 |
| #define DEBUG_SWAP_TOP 1 |
| #define DEBUG_UNSORTABLE 1 |
| #define DEBUG_WIND_BUMP 0 |
| #define DEBUG_WINDING 1 |
| #define DEBUG_WINDING_AT_T 1 |
| |
| #endif |
| |
| #define DEBUG_DUMP (DEBUG_ACTIVE_OP | DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | \ |
| DEBUG_PATH_CONSTRUCTION) |
| |
| #if DEBUG_AS_C_CODE |
| #define CUBIC_DEBUG_STR "{{%1.17g,%1.17g}, {%1.17g,%1.17g}, {%1.17g,%1.17g}, {%1.17g,%1.17g}}" |
| #define QUAD_DEBUG_STR "{{%1.17g,%1.17g}, {%1.17g,%1.17g}, {%1.17g,%1.17g}}" |
| #define LINE_DEBUG_STR "{{%1.17g,%1.17g}, {%1.17g,%1.17g}}" |
| #define PT_DEBUG_STR "{{%1.17g,%1.17g}}" |
| #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)" |
| #endif |
| #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 |
| #define QUAD_DEBUG_DATA(q) q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY |
| #define LINE_DEBUG_DATA(l) l[0].fX, l[0].fY, l[1].fX, l[1].fY |
| #define PT_DEBUG_DATA(i, n) i.fPt[n].x, i.fPt[n].y |
| |
| #if DEBUG_DUMP |
| static const char* kLVerbStr[] = {"", "line", "quad", "cubic"}; |
| // static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"}; |
| static int gContourID; |
| static int gSegmentID; |
| #endif |
| |
| #if DEBUG_SORT || DEBUG_SWAP_TOP |
| static int gDebugSortCountDefault = SK_MaxS32; |
| static int gDebugSortCount; |
| #endif |
| |
| #if DEBUG_ACTIVE_OP |
| static const char* kShapeOpStr[] = {"diff", "sect", "union", "xor"}; |
| #endif |
| |
| #ifndef DEBUG_TEST |
| #define DEBUG_TEST 0 |
| #endif |
| |
| #define MAKE_CONST_LINE(line, pts) \ |
| const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}} |
| #define MAKE_CONST_QUAD(quad, pts) \ |
| const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \ |
| {pts[2].fX, pts[2].fY}} |
| #define MAKE_CONST_CUBIC(cubic, pts) \ |
| const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \ |
| {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}} |
| |
| static int LineIntersect(const SkPoint a[2], const SkPoint b[2], |
| Intersections& intersections) { |
| MAKE_CONST_LINE(aLine, a); |
| MAKE_CONST_LINE(bLine, b); |
| return intersect(aLine, bLine, intersections); |
| } |
| |
| static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2], |
| Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| MAKE_CONST_LINE(bLine, b); |
| return intersect(aQuad, bLine, intersections); |
| } |
| |
| static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2], |
| Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| MAKE_CONST_LINE(bLine, b); |
| return intersect(aCubic, bLine, intersections); |
| } |
| |
| static int QuadIntersect(const SkPoint a[3], const SkPoint b[3], |
| Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| MAKE_CONST_QUAD(bQuad, b); |
| #define TRY_QUARTIC_SOLUTION 1 |
| #if TRY_QUARTIC_SOLUTION |
| intersect2(aQuad, bQuad, intersections); |
| #else |
| intersect(aQuad, bQuad, intersections); |
| #endif |
| return intersections.fUsed; |
| } |
| |
| #if APPROXIMATE_CUBICS |
| static int CubicQuadIntersect(const SkPoint a[4], const SkPoint b[3], |
| Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| MAKE_CONST_QUAD(bQuad, b); |
| return intersect(aCubic, bQuad, intersections); |
| } |
| #endif |
| |
| static int CubicIntersect(const SkPoint a[4], const SkPoint b[4], Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| MAKE_CONST_CUBIC(bCubic, b); |
| #if APPROXIMATE_CUBICS |
| intersect3(aCubic, bCubic, intersections); |
| #else |
| intersect(aCubic, bCubic, intersections); |
| #endif |
| return intersections.fUsed; |
| } |
| |
| static int CubicIntersect(const SkPoint a[4], Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return intersect(aCubic, intersections); |
| } |
| |
| static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right, |
| SkScalar y, bool flipped, Intersections& intersections) { |
| MAKE_CONST_LINE(aLine, a); |
| return horizontalIntersect(aLine, left, right, y, flipped, intersections); |
| } |
| |
| static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right, |
| SkScalar y, bool flipped, Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| return horizontalIntersect(aQuad, left, right, y, flipped, intersections); |
| } |
| |
| static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right, |
| SkScalar y, bool flipped, Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return horizontalIntersect(aCubic, left, right, y, flipped, intersections); |
| } |
| |
| static int (* const HSegmentIntersect[])(const SkPoint [], SkScalar , |
| SkScalar , SkScalar , bool , Intersections& ) = { |
| NULL, |
| HLineIntersect, |
| HQuadIntersect, |
| HCubicIntersect |
| }; |
| |
| static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom, |
| SkScalar x, bool flipped, Intersections& intersections) { |
| MAKE_CONST_LINE(aLine, a); |
| return verticalIntersect(aLine, top, bottom, x, flipped, intersections); |
| } |
| |
| static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom, |
| SkScalar x, bool flipped, Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| return verticalIntersect(aQuad, top, bottom, x, flipped, intersections); |
| } |
| |
| static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom, |
| SkScalar x, bool flipped, Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return verticalIntersect(aCubic, top, bottom, x, flipped, intersections); |
| } |
| |
| static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar , |
| SkScalar , SkScalar , bool , Intersections& ) = { |
| NULL, |
| VLineIntersect, |
| VQuadIntersect, |
| VCubicIntersect |
| }; |
| |
| static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) { |
| MAKE_CONST_LINE(line, a); |
| double x, y; |
| xy_at_t(line, t, x, y); |
| out->fX = SkDoubleToScalar(x); |
| out->fY = SkDoubleToScalar(y); |
| } |
| |
| static void LineXYAtT(const SkPoint a[2], double t, _Point* out) { |
| MAKE_CONST_LINE(line, a); |
| xy_at_t(line, t, out->x, out->y); |
| } |
| |
| static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) { |
| MAKE_CONST_QUAD(quad, a); |
| double x, y; |
| xy_at_t(quad, t, x, y); |
| out->fX = SkDoubleToScalar(x); |
| out->fY = SkDoubleToScalar(y); |
| } |
| |
| static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) { |
| MAKE_CONST_QUAD(quad, a); |
| xy_at_t(quad, t, out->x, out->y); |
| } |
| |
| static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) { |
| MAKE_CONST_CUBIC(cubic, a); |
| double x, y; |
| xy_at_t(cubic, t, x, y); |
| out->fX = SkDoubleToScalar(x); |
| out->fY = SkDoubleToScalar(y); |
| } |
| |
| static void CubicXYAtT(const SkPoint a[4], double t, _Point* out) { |
| MAKE_CONST_CUBIC(cubic, a); |
| xy_at_t(cubic, t, out->x, out->y); |
| } |
| |
| static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = { |
| NULL, |
| LineXYAtT, |
| QuadXYAtT, |
| CubicXYAtT |
| }; |
| |
| static void (* const SegmentXYAtT2[])(const SkPoint [], double , _Point* ) = { |
| NULL, |
| LineXYAtT, |
| QuadXYAtT, |
| CubicXYAtT |
| }; |
| |
| static SkScalar LineXAtT(const SkPoint a[2], double t) { |
| MAKE_CONST_LINE(aLine, a); |
| double x; |
| xy_at_t(aLine, t, x, *(double*) 0); |
| return SkDoubleToScalar(x); |
| } |
| |
| static SkScalar QuadXAtT(const SkPoint a[3], double t) { |
| MAKE_CONST_QUAD(quad, a); |
| double x; |
| xy_at_t(quad, t, x, *(double*) 0); |
| return SkDoubleToScalar(x); |
| } |
| |
| static SkScalar CubicXAtT(const SkPoint a[4], double t) { |
| MAKE_CONST_CUBIC(cubic, a); |
| double x; |
| xy_at_t(cubic, t, x, *(double*) 0); |
| return SkDoubleToScalar(x); |
| } |
| |
| static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = { |
| NULL, |
| LineXAtT, |
| QuadXAtT, |
| CubicXAtT |
| }; |
| |
| static SkScalar LineYAtT(const SkPoint a[2], double t) { |
| MAKE_CONST_LINE(aLine, a); |
| double y; |
| xy_at_t(aLine, t, *(double*) 0, y); |
| return SkDoubleToScalar(y); |
| } |
| |
| static SkScalar QuadYAtT(const SkPoint a[3], double t) { |
| MAKE_CONST_QUAD(quad, a); |
| double y; |
| xy_at_t(quad, t, *(double*) 0, y); |
| return SkDoubleToScalar(y); |
| } |
| |
| static SkScalar CubicYAtT(const SkPoint a[4], double t) { |
| MAKE_CONST_CUBIC(cubic, a); |
| double y; |
| xy_at_t(cubic, t, *(double*) 0, y); |
| return SkDoubleToScalar(y); |
| } |
| |
| static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = { |
| NULL, |
| LineYAtT, |
| QuadYAtT, |
| CubicYAtT |
| }; |
| |
| static SkScalar LineDXAtT(const SkPoint a[2], double ) { |
| return a[1].fX - a[0].fX; |
| } |
| |
| static SkScalar QuadDXAtT(const SkPoint a[3], double t) { |
| MAKE_CONST_QUAD(quad, a); |
| double x = dx_at_t(quad, t); |
| return SkDoubleToScalar(x); |
| } |
| |
| static SkScalar CubicDXAtT(const SkPoint a[4], double t) { |
| MAKE_CONST_CUBIC(cubic, a); |
| double x = dx_at_t(cubic, t); |
| return SkDoubleToScalar(x); |
| } |
| |
| static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = { |
| NULL, |
| LineDXAtT, |
| QuadDXAtT, |
| CubicDXAtT |
| }; |
| |
| static SkScalar LineDYAtT(const SkPoint a[2], double ) { |
| return a[1].fY - a[0].fY; |
| } |
| |
| static SkScalar QuadDYAtT(const SkPoint a[3], double t) { |
| MAKE_CONST_QUAD(quad, a); |
| double y = dy_at_t(quad, t); |
| return SkDoubleToScalar(y); |
| } |
| |
| static SkScalar CubicDYAtT(const SkPoint a[4], double t) { |
| MAKE_CONST_CUBIC(cubic, a); |
| double y = dy_at_t(cubic, t); |
| return SkDoubleToScalar(y); |
| } |
| |
| static SkScalar (* const SegmentDYAtT[])(const SkPoint [], double ) = { |
| NULL, |
| LineDYAtT, |
| QuadDYAtT, |
| CubicDYAtT |
| }; |
| |
| static SkVector LineDXDYAtT(const SkPoint a[2], double ) { |
| return a[1] - a[0]; |
| } |
| |
| static SkVector QuadDXDYAtT(const SkPoint a[3], double t) { |
| MAKE_CONST_QUAD(quad, a); |
| _Vector v = dxdy_at_t(quad, t); |
| return v.asSkVector(); |
| } |
| |
| static SkVector CubicDXDYAtT(const SkPoint a[4], double t) { |
| MAKE_CONST_CUBIC(cubic, a); |
| _Vector v = dxdy_at_t(cubic, t); |
| return v.asSkVector(); |
| } |
| |
| static SkVector (* const SegmentDXDYAtT[])(const SkPoint [], double ) = { |
| NULL, |
| LineDXDYAtT, |
| QuadDXDYAtT, |
| CubicDXDYAtT |
| }; |
| |
| static void LineSubDivide(const SkPoint a[2], double startT, double endT, |
| SkPoint sub[2]) { |
| MAKE_CONST_LINE(aLine, a); |
| _Line dst; |
| sub_divide(aLine, startT, endT, dst); |
| sub[0].fX = SkDoubleToScalar(dst[0].x); |
| sub[0].fY = SkDoubleToScalar(dst[0].y); |
| sub[1].fX = SkDoubleToScalar(dst[1].x); |
| sub[1].fY = SkDoubleToScalar(dst[1].y); |
| } |
| |
| static void QuadSubDivide(const SkPoint a[3], double startT, double endT, |
| SkPoint sub[3]) { |
| MAKE_CONST_QUAD(aQuad, a); |
| Quadratic dst; |
| sub_divide(aQuad, startT, endT, dst); |
| sub[0].fX = SkDoubleToScalar(dst[0].x); |
| sub[0].fY = SkDoubleToScalar(dst[0].y); |
| sub[1].fX = SkDoubleToScalar(dst[1].x); |
| sub[1].fY = SkDoubleToScalar(dst[1].y); |
| sub[2].fX = SkDoubleToScalar(dst[2].x); |
| sub[2].fY = SkDoubleToScalar(dst[2].y); |
| } |
| |
| static void CubicSubDivide(const SkPoint a[4], double startT, double endT, |
| SkPoint sub[4]) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| Cubic dst; |
| sub_divide(aCubic, startT, endT, dst); |
| sub[0].fX = SkDoubleToScalar(dst[0].x); |
| sub[0].fY = SkDoubleToScalar(dst[0].y); |
| sub[1].fX = SkDoubleToScalar(dst[1].x); |
| sub[1].fY = SkDoubleToScalar(dst[1].y); |
| sub[2].fX = SkDoubleToScalar(dst[2].x); |
| sub[2].fY = SkDoubleToScalar(dst[2].y); |
| sub[3].fX = SkDoubleToScalar(dst[3].x); |
| sub[3].fY = SkDoubleToScalar(dst[3].y); |
| } |
| |
| static void (* const SegmentSubDivide[])(const SkPoint [], double , double , |
| SkPoint []) = { |
| NULL, |
| LineSubDivide, |
| QuadSubDivide, |
| CubicSubDivide |
| }; |
| |
| static void LineSubDivideHD(const SkPoint a[2], double startT, double endT, _Line& dst) { |
| MAKE_CONST_LINE(aLine, a); |
| sub_divide(aLine, startT, endT, dst); |
| } |
| |
| static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT, Quadratic& dst) { |
| MAKE_CONST_QUAD(aQuad, a); |
| sub_divide(aQuad, startT, endT, dst); |
| } |
| |
| static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT, Cubic& dst) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| sub_divide(aCubic, startT, endT, dst); |
| } |
| |
| static SkPoint QuadTop(const SkPoint a[3], double startT, double endT) { |
| MAKE_CONST_QUAD(quad, a); |
| _Point topPt = top(quad, startT, endT); |
| return topPt.asSkPoint(); |
| } |
| |
| static SkPoint CubicTop(const SkPoint a[3], double startT, double endT) { |
| MAKE_CONST_CUBIC(cubic, a); |
| _Point topPt = top(cubic, startT, endT); |
| return topPt.asSkPoint(); |
| } |
| |
| static SkPoint (* SegmentTop[])(const SkPoint[], double , double ) = { |
| NULL, |
| NULL, |
| QuadTop, |
| CubicTop |
| }; |
| |
| #if DEBUG_UNUSED |
| static void QuadSubBounds(const SkPoint a[3], double startT, double endT, |
| SkRect& bounds) { |
| SkPoint dst[3]; |
| QuadSubDivide(a, startT, endT, dst); |
| bounds.fLeft = bounds.fRight = dst[0].fX; |
| bounds.fTop = bounds.fBottom = dst[0].fY; |
| for (int index = 1; index < 3; ++index) { |
| bounds.growToInclude(dst[index].fX, dst[index].fY); |
| } |
| } |
| |
| static void CubicSubBounds(const SkPoint a[4], double startT, double endT, |
| SkRect& bounds) { |
| SkPoint dst[4]; |
| CubicSubDivide(a, startT, endT, dst); |
| bounds.fLeft = bounds.fRight = dst[0].fX; |
| bounds.fTop = bounds.fBottom = dst[0].fY; |
| for (int index = 1; index < 4; ++index) { |
| bounds.growToInclude(dst[index].fX, dst[index].fY); |
| } |
| } |
| #endif |
| |
| static SkPath::Verb QuadReduceOrder(const SkPoint a[3], |
| SkTDArray<SkPoint>& reducePts) { |
| MAKE_CONST_QUAD(aQuad, a); |
| Quadratic dst; |
| int order = reduceOrder(aQuad, dst, kReduceOrder_TreatAsFill); |
| if (order == 2) { // quad became line |
| for (int index = 0; index < order; ++index) { |
| SkPoint* pt = reducePts.append(); |
| pt->fX = SkDoubleToScalar(dst[index].x); |
| pt->fY = SkDoubleToScalar(dst[index].y); |
| } |
| } |
| return (SkPath::Verb) (order - 1); |
| } |
| |
| static SkPath::Verb CubicReduceOrder(const SkPoint a[4], |
| SkTDArray<SkPoint>& reducePts) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| Cubic dst; |
| int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed, kReduceOrder_TreatAsFill); |
| if (order == 2 || order == 3) { // cubic became line or quad |
| for (int index = 0; index < order; ++index) { |
| SkPoint* pt = reducePts.append(); |
| pt->fX = SkDoubleToScalar(dst[index].x); |
| pt->fY = SkDoubleToScalar(dst[index].y); |
| } |
| } |
| return (SkPath::Verb) (order - 1); |
| } |
| |
| static bool QuadIsLinear(const SkPoint a[3]) { |
| MAKE_CONST_QUAD(aQuad, a); |
| return isLinear(aQuad, 0, 2); |
| } |
| |
| static bool CubicIsLinear(const SkPoint a[4]) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return isLinear(aCubic, 0, 3); |
| } |
| |
| static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) { |
| MAKE_CONST_LINE(aLine, a); |
| double x[2]; |
| xy_at_t(aLine, startT, x[0], *(double*) 0); |
| xy_at_t(aLine, endT, x[1], *(double*) 0); |
| return SkMinScalar((float) x[0], (float) x[1]); |
| } |
| |
| static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) { |
| MAKE_CONST_QUAD(aQuad, a); |
| return (float) leftMostT(aQuad, startT, endT); |
| } |
| |
| static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return (float) leftMostT(aCubic, startT, endT); |
| } |
| |
| static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = { |
| NULL, |
| LineLeftMost, |
| QuadLeftMost, |
| CubicLeftMost |
| }; |
| |
| #if 0 // currently unused |
| static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2], |
| Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| MAKE_CONST_LINE(bLine, b); |
| return intersectRay(aQuad, bLine, intersections); |
| } |
| #endif |
| |
| static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine, Intersections& intersections) { |
| MAKE_CONST_QUAD(aQuad, a); |
| return intersectRay(aQuad, bLine, intersections); |
| } |
| |
| static int CubicRayIntersect(const SkPoint a[3], const _Line& bLine, Intersections& intersections) { |
| MAKE_CONST_CUBIC(aCubic, a); |
| return intersectRay(aCubic, bLine, intersections); |
| } |
| |
| static int (* const SegmentRayIntersect[])(const SkPoint [], const _Line& , Intersections&) = { |
| NULL, |
| NULL, |
| QuadRayIntersect, |
| CubicRayIntersect |
| }; |
| |
| |
| |
| static bool LineVertical(const SkPoint a[2], double startT, double endT) { |
| MAKE_CONST_LINE(aLine, a); |
| double x[2]; |
| xy_at_t(aLine, startT, x[0], *(double*) 0); |
| xy_at_t(aLine, endT, x[1], *(double*) 0); |
| return AlmostEqualUlps((float) x[0], (float) x[1]); |
| } |
| |
| static bool QuadVertical(const SkPoint a[3], double startT, double endT) { |
| SkPoint dst[3]; |
| QuadSubDivide(a, startT, endT, dst); |
| return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX); |
| } |
| |
| static bool CubicVertical(const SkPoint a[4], double startT, double endT) { |
| SkPoint dst[4]; |
| CubicSubDivide(a, startT, endT, dst); |
| return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX) |
| && AlmostEqualUlps(dst[2].fX, dst[3].fX); |
| } |
| |
| static bool (* const SegmentVertical[])(const SkPoint [], double , double) = { |
| NULL, |
| LineVertical, |
| QuadVertical, |
| CubicVertical |
| }; |
| |
| class Segment; |
| |
| struct Span { |
| Segment* fOther; |
| mutable SkPoint fPt; // lazily computed as needed |
| double fT; |
| double fOtherT; // value at fOther[fOtherIndex].fT |
| int fOtherIndex; // can't be used during intersection |
| 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 fDone; // if set, this span to next higher T has been processed |
| bool fUnsortableStart; // set when start is part of an unsortable pair |
| bool fUnsortableEnd; // set when end is part of an unsortable pair |
| bool fTiny; // if set, span may still be considered once for edge following |
| bool fLoop; // set when a cubic loops back to this point |
| }; |
| |
| // sorting angles |
| // given angles of {dx dy ddx ddy dddx dddy} sort them |
| class Angle { |
| public: |
| // FIXME: this is bogus for quads and cubics |
| // if the quads and cubics' line from end pt to ctrl pt are coincident, |
| // there's no obvious way to determine the curve ordering from the |
| // derivatives alone. In particular, if one quadratic's coincident tangent |
| // is longer than the other curve, the final control point can place the |
| // longer curve on either side of the shorter one. |
| // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf |
| // may provide some help, but nothing has been figured out yet. |
| |
| /*( |
| for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
| for points [0] to [1]. See if point [2] is on that line, or on one side |
| or the other. If it both quads' end points are on the same side, choose |
| the shorter tangent. If the tangents are equal, choose the better second |
| tangent angle |
| |
| maybe I could set up LineParameters lazily |
| */ |
| bool operator<(const Angle& rh) const { |
| double y = dy(); |
| double ry = rh.dy(); |
| if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? |
| return y < 0; |
| } |
| double x = dx(); |
| double rx = rh.dx(); |
| if (y == 0 && ry == 0 && x * rx < 0) { |
| return x < rx; |
| } |
| double x_ry = x * ry; |
| double rx_y = rx * y; |
| double cmp = x_ry - rx_y; |
| if (!approximately_zero(cmp)) { |
| return cmp < 0; |
| } |
| if (approximately_zero(x_ry) && approximately_zero(rx_y) |
| && !approximately_zero_squared(cmp)) { |
| return cmp < 0; |
| } |
| // at this point, the initial tangent line is coincident |
| // see if edges curl away from each other |
| if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) |
| || !approximately_zero(rh.fSide))) { |
| // FIXME: running demo will trigger this assertion |
| // (don't know if commenting out will trigger further assertion or not) |
| // commenting it out allows demo to run in release, though |
| // SkASSERT(fSide != rh.fSide); |
| return fSide < rh.fSide; |
| } |
| // see if either curve can be lengthened and try the tangent compare again |
| if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical |
| && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting |
| Angle longer = *this; |
| Angle rhLonger = rh; |
| if (longer.lengthen() | rhLonger.lengthen()) { |
| return longer < rhLonger; |
| } |
| #if 0 |
| // what if we extend in the other direction? |
| longer = *this; |
| rhLonger = rh; |
| if (longer.reverseLengthen() | rhLonger.reverseLengthen()) { |
| return longer < rhLonger; |
| } |
| #endif |
| } |
| if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y)) |
| || (rh.fVerb == SkPath::kLine_Verb |
| && approximately_zero(rx) && approximately_zero(ry))) { |
| // See general unsortable comment below. This case can happen when |
| // one line has a non-zero change in t but no change in x and y. |
| fUnsortable = true; |
| rh.fUnsortable = true; |
| return this < &rh; // even with no solution, return a stable sort |
| } |
| if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny |
| || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { |
| fUnsortable = true; |
| rh.fUnsortable = true; |
| return this < &rh; // even with no solution, return a stable sort |
| } |
| SkASSERT(fVerb >= SkPath::kQuad_Verb); |
| SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); |
| // FIXME: until I can think of something better, project a ray from the |
| // end of the shorter tangent to midway between the end points |
| // through both curves and use the resulting angle to sort |
| // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive |
| double len = fTangent1.normalSquared(); |
| double rlen = rh.fTangent1.normalSquared(); |
| _Line ray; |
| Intersections i, ri; |
| int roots, rroots; |
| bool flip = false; |
| do { |
| bool useThis = (len < rlen) ^ flip; |
| const Cubic& part = useThis ? fCurvePart : rh.fCurvePart; |
| SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; |
| ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ? |
| part[2] : part[1]; |
| ray[1].x = (part[0].x + part[partVerb].x) / 2; |
| ray[1].y = (part[0].y + part[partVerb].y) / 2; |
| SkASSERT(ray[0] != ray[1]); |
| roots = (*SegmentRayIntersect[fVerb])(fPts, ray, i); |
| rroots = (*SegmentRayIntersect[rh.fVerb])(rh.fPts, ray, ri); |
| } while ((roots == 0 || rroots == 0) && (flip ^= true)); |
| if (roots == 0 || rroots == 0) { |
| // FIXME: we don't have a solution in this case. The interim solution |
| // is to mark the edges as unsortable, exclude them from this and |
| // future computations, and allow the returned path to be fragmented |
| fUnsortable = true; |
| rh.fUnsortable = true; |
| return this < &rh; // even with no solution, return a stable sort |
| } |
| _Point loc; |
| double best = SK_ScalarInfinity; |
| double dx, dy, dist; |
| int index; |
| for (index = 0; index < roots; ++index) { |
| (*SegmentXYAtT2[fVerb])(fPts, i.fT[0][index], &loc); |
| dx = loc.x - ray[0].x; |
| dy = loc.y - ray[0].y; |
| dist = dx * dx + dy * dy; |
| if (best > dist) { |
| best = dist; |
| } |
| } |
| for (index = 0; index < rroots; ++index) { |
| (*SegmentXYAtT2[rh.fVerb])(rh.fPts, ri.fT[0][index], &loc); |
| dx = loc.x - ray[0].x; |
| dy = loc.y - ray[0].y; |
| dist = dx * dx + dy * dy; |
| if (best > dist) { |
| return fSide < 0; |
| } |
| } |
| return fSide > 0; |
| } |
| |
| double dx() const { |
| return fTangent1.dx(); |
| } |
| |
| double dy() const { |
| return fTangent1.dy(); |
| } |
| |
| int end() const { |
| return fEnd; |
| } |
| |
| bool isHorizontal() const { |
| return dy() == 0 && fVerb == SkPath::kLine_Verb; |
| } |
| |
| bool lengthen() { |
| int newEnd = fEnd; |
| if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
| fEnd = newEnd; |
| setSpans(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool reverseLengthen() { |
| if (fReversed) { |
| return false; |
| } |
| int newEnd = fStart; |
| if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
| fEnd = newEnd; |
| fReversed = true; |
| setSpans(); |
| return true; |
| } |
| return false; |
| } |
| |
| void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment, |
| int start, int end, const SkTDArray<Span>& spans) { |
| fSegment = segment; |
| fStart = start; |
| fEnd = end; |
| fPts = orig; |
| fVerb = verb; |
| fSpans = &spans; |
| fReversed = false; |
| fUnsortable = false; |
| setSpans(); |
| } |
| |
| |
| void setSpans() { |
| double startT = (*fSpans)[fStart].fT; |
| double endT = (*fSpans)[fEnd].fT; |
| switch (fVerb) { |
| case SkPath::kLine_Verb: |
| _Line l; |
| LineSubDivideHD(fPts, startT, endT, l); |
| // OPTIMIZATION: for pure line compares, we never need fTangent1.c |
| fTangent1.lineEndPoints(l); |
| fSide = 0; |
| break; |
| case SkPath::kQuad_Verb: { |
| Quadratic& quad = (Quadratic&)fCurvePart; |
| QuadSubDivideHD(fPts, startT, endT, quad); |
| fTangent1.quadEndPoints(quad, 0, 1); |
| if (dx() == 0 && dy() == 0) { |
| fTangent1.quadEndPoints(quad); |
| } |
| fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only |
| } break; |
| case SkPath::kCubic_Verb: { |
| int nextC = 2; |
| CubicSubDivideHD(fPts, startT, endT, fCurvePart); |
| fTangent1.cubicEndPoints(fCurvePart, 0, 1); |
| if (dx() == 0 && dy() == 0) { |
| fTangent1.cubicEndPoints(fCurvePart, 0, 2); |
| nextC = 3; |
| if (dx() == 0 && dy() == 0) { |
| fTangent1.cubicEndPoints(fCurvePart, 0, 3); |
| } |
| } |
| fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only |
| if (nextC == 2 && approximately_zero(fSide)) { |
| fSide = -fTangent1.pointDistance(fCurvePart[3]); |
| } |
| } break; |
| default: |
| SkASSERT(0); |
| } |
| fUnsortable = dx() == 0 && dy() == 0; |
| if (fUnsortable) { |
| return; |
| } |
| SkASSERT(fStart != fEnd); |
| int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro? |
| for (int index = fStart; index != fEnd; index += step) { |
| #if 1 |
| const Span& thisSpan = (*fSpans)[index]; |
| const Span& nextSpan = (*fSpans)[index + step]; |
| if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { |
| continue; |
| } |
| fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd; |
| #if DEBUG_UNSORTABLE |
| if (fUnsortable) { |
| SkPoint iPt, ePt; |
| (*SegmentXYAtT[fVerb])(fPts, thisSpan.fT, &iPt); |
| (*SegmentXYAtT[fVerb])(fPts, nextSpan.fT, &ePt); |
| SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, |
| index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| } |
| #endif |
| return; |
| #else |
| if ((*fSpans)[index].fUnsortableStart) { |
| fUnsortable = true; |
| return; |
| } |
| #endif |
| } |
| #if 1 |
| #if DEBUG_UNSORTABLE |
| SkPoint iPt, ePt; |
| (*SegmentXYAtT[fVerb])(fPts, startT, &iPt); |
| (*SegmentXYAtT[fVerb])(fPts, endT, &ePt); |
| SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, |
| fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| #endif |
| fUnsortable = true; |
| #endif |
| } |
| |
| Segment* segment() const { |
| return const_cast<Segment*>(fSegment); |
| } |
| |
| int sign() const { |
| return SkSign32(fStart - fEnd); |
| } |
| |
| const SkTDArray<Span>* spans() const { |
| return fSpans; |
| } |
| |
| int start() const { |
| return fStart; |
| } |
| |
| bool unsortable() const { |
| return fUnsortable; |
| } |
| |
| #if DEBUG_ANGLE |
| const SkPoint* pts() const { |
| return fPts; |
| } |
| |
| SkPath::Verb verb() const { |
| return fVerb; |
| } |
| |
| void debugShow(const SkPoint& a) const { |
| SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide); |
| } |
| #endif |
| |
| private: |
| const SkPoint* fPts; |
| Cubic fCurvePart; |
| SkPath::Verb fVerb; |
| double fSide; |
| LineParameters fTangent1; |
| const SkTDArray<Span>* fSpans; |
| const Segment* fSegment; |
| int fStart; |
| int fEnd; |
| bool fReversed; |
| mutable bool fUnsortable; // this alone is editable by the less than operator |
| }; |
| |
| // Bounds, unlike Rect, does not consider a line to be empty. |
| struct Bounds : public SkRect { |
| static bool Intersects(const Bounds& a, const Bounds& b) { |
| return a.fLeft <= b.fRight && b.fLeft <= a.fRight && |
| a.fTop <= b.fBottom && b.fTop <= a.fBottom; |
| } |
| |
| void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { |
| if (left < fLeft) { |
| fLeft = left; |
| } |
| if (top < fTop) { |
| fTop = top; |
| } |
| if (right > fRight) { |
| fRight = right; |
| } |
| if (bottom > fBottom) { |
| fBottom = bottom; |
| } |
| } |
| |
| void add(const Bounds& toAdd) { |
| add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom); |
| } |
| |
| void add(const SkPoint& pt) { |
| if (pt.fX < fLeft) fLeft = pt.fX; |
| if (pt.fY < fTop) fTop = pt.fY; |
| if (pt.fX > fRight) fRight = pt.fX; |
| if (pt.fY > fBottom) fBottom = pt.fY; |
| } |
| |
| bool isEmpty() { |
| return fLeft > fRight || fTop > fBottom |
| || (fLeft == fRight && fTop == fBottom) |
| || sk_double_isnan(fLeft) || sk_double_isnan(fRight) |
| || sk_double_isnan(fTop) || sk_double_isnan(fBottom); |
| } |
| |
| void setCubicBounds(const SkPoint a[4]) { |
| _Rect dRect; |
| MAKE_CONST_CUBIC(cubic, a); |
| dRect.setBounds(cubic); |
| set((float) dRect.left, (float) dRect.top, (float) dRect.right, |
| (float) dRect.bottom); |
| } |
| |
| void setLineBounds(const SkPoint a[2]) { |
| setPoint(a[0]); |
| add(a[1]); |
| } |
| |
| void setQuadBounds(const SkPoint a[3]) { |
| MAKE_CONST_QUAD(quad, a); |
| _Rect dRect; |
| dRect.setBounds(quad); |
| set((float) dRect.left, (float) dRect.top, (float) dRect.right, |
| (float) dRect.bottom); |
| } |
| |
| void setPoint(const SkPoint& pt) { |
| fLeft = fRight = pt.fX; |
| fTop = fBottom = pt.fY; |
| } |
| }; |
| |
| static void (Bounds::*setSegmentBounds[])(const SkPoint[]) = { |
| NULL, |
| &Bounds::setLineBounds, |
| &Bounds::setQuadBounds, |
| &Bounds::setCubicBounds |
| }; |
| |
| // OPTIMIZATION: does the following also work, and is it any faster? |
| // return outerWinding * innerWinding > 0 |
| // || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0))) |
| static bool useInnerWinding(int outerWinding, int innerWinding) { |
| SkASSERT(outerWinding != SK_MaxS32); |
| SkASSERT(innerWinding != SK_MaxS32); |
| int absOut = abs(outerWinding); |
| int absIn = abs(innerWinding); |
| bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn; |
| #if 0 && DEBUG_WINDING |
| if (outerWinding * innerWinding < 0) { |
| SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__, |
| outerWinding, innerWinding, result ? "true" : "false"); |
| } |
| #endif |
| return result; |
| } |
| |
| #define F (false) // discard the edge |
| #define T (true) // keep the edge |
| |
| static const bool gUnaryActiveEdge[2][2] = { |
| // from=0 from=1 |
| // to=0,1 to=0,1 |
| {F, T}, {T, F}, |
| }; |
| |
| static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = { |
| // miFrom=0 miFrom=1 |
| // miTo=0 miTo=1 miTo=0 miTo=1 |
| // suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1 |
| // suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 |
| {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su |
| {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su |
| {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su |
| {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su |
| }; |
| |
| #undef F |
| #undef T |
| |
| // wrap path to keep track of whether the contour is initialized and non-empty |
| class PathWrapper { |
| public: |
| PathWrapper(SkPath& path) |
| : fPathPtr(&path) |
| , fCloses(0) |
| , fMoves(0) |
| { |
| init(); |
| } |
| |
| void close() { |
| if (!fHasMove) { |
| return; |
| } |
| bool callClose = isClosed(); |
| lineTo(); |
| if (fEmpty) { |
| return; |
| } |
| if (callClose) { |
| #if DEBUG_PATH_CONSTRUCTION |
| SkDebugf("path.close();\n"); |
| #endif |
| fPathPtr->close(); |
| fCloses++; |
| } |
| init(); |
| } |
| |
| void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) { |
| lineTo(); |
| moveTo(); |
| fDefer[1] = pt3; |
| nudge(); |
| fDefer[0] = fDefer[1]; |
| #if DEBUG_PATH_CONSTRUCTION |
| SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n", |
| pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY); |
| #endif |
| fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY); |
| fEmpty = false; |
| } |
| |
| void deferredLine(const SkPoint& pt) { |
| if (pt == fDefer[1]) { |
| return; |
| } |
| if (changedSlopes(pt)) { |
| lineTo(); |
| fDefer[0] = fDefer[1]; |
| } |
| fDefer[1] = pt; |
| } |
| |
| void deferredMove(const SkPoint& pt) { |
| fMoved = true; |
| fHasMove = true; |
| fEmpty = true; |
| fDefer[0] = fDefer[1] = pt; |
| } |
| |
| void deferredMoveLine(const SkPoint& pt) { |
| if (!fHasMove) { |
| deferredMove(pt); |
| } |
| deferredLine(pt); |
| } |
| |
| bool hasMove() const { |
| return fHasMove; |
| } |
| |
| void init() { |
| fEmpty = true; |
| fHasMove = false; |
| fMoved = false; |
| } |
| |
| bool isClosed() const { |
| return !fEmpty && fFirstPt == fDefer[1]; |
| } |
| |
| void lineTo() { |
| if (fDefer[0] == fDefer[1]) { |
| return; |
| } |
| moveTo(); |
| nudge(); |
| fEmpty = false; |
| #if DEBUG_PATH_CONSTRUCTION |
| SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY); |
| #endif |
| fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY); |
| fDefer[0] = fDefer[1]; |
| } |
| |
| const SkPath* nativePath() const { |
| return fPathPtr; |
| } |
| |
| void nudge() { |
| if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX) |
| || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) { |
| return; |
| } |
| fDefer[1] = fFirstPt; |
| } |
| |
| void quadTo(const SkPoint& pt1, const SkPoint& pt2) { |
| lineTo(); |
| moveTo(); |
| fDefer[1] = pt2; |
| nudge(); |
| fDefer[0] = fDefer[1]; |
| #if DEBUG_PATH_CONSTRUCTION |
| SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n", |
| pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY); |
| #endif |
| fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY); |
| fEmpty = false; |
| } |
| |
| bool someAssemblyRequired() const { |
| return fCloses < fMoves; |
| } |
| |
| protected: |
| bool changedSlopes(const SkPoint& pt) const { |
| if (fDefer[0] == fDefer[1]) { |
| return false; |
| } |
| SkScalar deferDx = fDefer[1].fX - fDefer[0].fX; |
| SkScalar deferDy = fDefer[1].fY - fDefer[0].fY; |
| SkScalar lineDx = pt.fX - fDefer[1].fX; |
| SkScalar lineDy = pt.fY - fDefer[1].fY; |
| return deferDx * lineDy != deferDy * lineDx; |
| } |
| |
| void moveTo() { |
| if (!fMoved) { |
| return; |
| } |
| fFirstPt = fDefer[0]; |
| #if DEBUG_PATH_CONSTRUCTION |
| SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY); |
| #endif |
| fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY); |
| fMoved = false; |
| fMoves++; |
| } |
| |
| private: |
| SkPath* fPathPtr; |
| SkPoint fDefer[2]; |
| SkPoint fFirstPt; |
| int fCloses; |
| int fMoves; |
| bool fEmpty; |
| bool fHasMove; |
| bool fMoved; |
| }; |
| |
| class Segment { |
| public: |
| Segment() { |
| #if DEBUG_DUMP |
| fID = ++gSegmentID; |
| #endif |
| } |
| |
| bool operator<(const Segment& rh) const { |
| return fBounds.fTop < rh.fBounds.fTop; |
| } |
| |
| bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) { |
| if (activeAngleInner(index, done, angles)) { |
| return true; |
| } |
| int lesser = index; |
| while (--lesser >= 0 && equalPoints(index, lesser)) { |
| if (activeAngleOther(lesser, done, angles)) { |
| return true; |
| } |
| } |
| lesser = index; |
| do { |
| if (activeAngleOther(index, done, angles)) { |
| return true; |
| } |
| } while (++index < fTs.count() && equalPoints(index, lesser)); |
| return false; |
| } |
| |
| bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) { |
| Span* span = &fTs[index]; |
| Segment* other = span->fOther; |
| int oIndex = span->fOtherIndex; |
| return other->activeAngleInner(oIndex, done, angles); |
| } |
| |
| bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) { |
| int next = nextExactSpan(index, 1); |
| if (next > 0) { |
| Span& upSpan = fTs[index]; |
| if (upSpan.fWindValue || upSpan.fOppValue) { |
| addAngle(angles, index, next); |
| if (upSpan.fDone || upSpan.fUnsortableEnd) { |
| done++; |
| } else if (upSpan.fWindSum != SK_MinS32) { |
| return true; |
| } |
| } else if (!upSpan.fDone) { |
| upSpan.fDone = true; |
| fDoneSpans++; |
| } |
| } |
| int prev = nextExactSpan(index, -1); |
| // edge leading into junction |
| if (prev >= 0) { |
| Span& downSpan = fTs[prev]; |
| if (downSpan.fWindValue || downSpan.fOppValue) { |
| addAngle(angles, index, prev); |
| if (downSpan.fDone) { |
| done++; |
| } else if (downSpan.fWindSum != SK_MinS32) { |
| return true; |
| } |
| } else if (!downSpan.fDone) { |
| downSpan.fDone = true; |
| fDoneSpans++; |
| } |
| } |
| return false; |
| } |
| |
| SkPoint activeLeftTop(bool onlySortable, int* firstT) const { |
| SkASSERT(!done()); |
| SkPoint topPt = {SK_ScalarMax, SK_ScalarMax}; |
| int count = fTs.count(); |
| // see if either end is not done since we want smaller Y of the pair |
| bool lastDone = true; |
| bool lastUnsortable = false; |
| double lastT = -1; |
| for (int index = 0; index < count; ++index) { |
| const Span& span = fTs[index]; |
| if (onlySortable && (span.fUnsortableStart || lastUnsortable)) { |
| goto next; |
| } |
| if (span.fDone && lastDone) { |
| goto next; |
| } |
| if (approximately_negative(span.fT - lastT)) { |
| goto next; |
| } |
| { |
| const SkPoint& xy = xyAtT(&span); |
| if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) { |
| topPt = xy; |
| if (firstT) { |
| *firstT = index; |
| } |
| } |
| if (fVerb != SkPath::kLine_Verb && !lastDone) { |
| SkPoint curveTop = (*SegmentTop[fVerb])(fPts, lastT, span.fT); |
| if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY |
| && topPt.fX > curveTop.fX)) { |
| topPt = curveTop; |
| if (firstT) { |
| *firstT = index; |
| } |
| } |
| } |
| lastT = span.fT; |
| } |
| next: |
| lastDone = span.fDone; |
| lastUnsortable = span.fUnsortableEnd; |
| } |
| return topPt; |
| } |
| |
| bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) { |
| int sumMiWinding = updateWinding(endIndex, index); |
| int sumSuWinding = updateOppWinding(endIndex, index); |
| if (fOperand) { |
| SkTSwap<int>(sumMiWinding, sumSuWinding); |
| } |
| int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
| return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding, |
| maxWinding, sumWinding, oppMaxWinding, oppSumWinding); |
| } |
| |
| bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op, |
| int& sumMiWinding, int& sumSuWinding, |
| int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) { |
| setUpWindings(index, endIndex, sumMiWinding, sumSuWinding, |
| maxWinding, sumWinding, oppMaxWinding, oppSumWinding); |
| bool miFrom; |
| bool miTo; |
| bool suFrom; |
| bool suTo; |
| if (operand()) { |
| miFrom = (oppMaxWinding & xorMiMask) != 0; |
| miTo = (oppSumWinding & xorMiMask) != 0; |
| suFrom = (maxWinding & xorSuMask) != 0; |
| suTo = (sumWinding & xorSuMask) != 0; |
| } else { |
| miFrom = (maxWinding & xorMiMask) != 0; |
| miTo = (sumWinding & xorMiMask) != 0; |
| suFrom = (oppMaxWinding & xorSuMask) != 0; |
| suTo = (oppSumWinding & xorSuMask) != 0; |
| } |
| bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo]; |
| #if DEBUG_ACTIVE_OP |
| SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__, |
| kShapeOpStr[op], miFrom, miTo, suFrom, suTo, result); |
| #endif |
| SkASSERT(result != -1); |
| return result; |
| } |
| |
| bool activeWinding(int index, int endIndex) { |
| int sumWinding = updateWinding(endIndex, index); |
| int maxWinding; |
| return activeWinding(index, endIndex, maxWinding, sumWinding); |
| } |
| |
| bool activeWinding(int index, int endIndex, int& maxWinding, int& sumWinding) { |
| setUpWinding(index, endIndex, maxWinding, sumWinding); |
| bool from = maxWinding != 0; |
| bool to = sumWinding != 0; |
| bool result = gUnaryActiveEdge[from][to]; |
| SkASSERT(result != -1); |
| return result; |
| } |
| |
| void addAngle(SkTDArray<Angle>& angles, int start, int end) const { |
| SkASSERT(start != end); |
| Angle* angle = angles.append(); |
| #if DEBUG_ANGLE |
| if (angles.count() > 1 && !fTs[start].fTiny) { |
| SkPoint angle0Pt, newPt; |
| (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(), |
| (*angles[0].spans())[angles[0].start()].fT, &angle0Pt); |
| (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt); |
| SkASSERT(AlmostEqualUlps(angle0Pt.fX, newPt.fX)); |
| SkASSERT(AlmostEqualUlps(angle0Pt.fY, newPt.fY)); |
| } |
| #endif |
| angle->set(fPts, fVerb, this, start, end, fTs); |
| } |
| |
| void addCancelOutsides(double tStart, double oStart, Segment& other, |
| double oEnd) { |
| int tIndex = -1; |
| int tCount = fTs.count(); |
| int oIndex = -1; |
| int oCount = other.fTs.count(); |
| do { |
| ++tIndex; |
| } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount); |
| int tIndexStart = tIndex; |
| do { |
| ++oIndex; |
| } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount); |
| int oIndexStart = oIndex; |
| double nextT; |
| do { |
| nextT = fTs[++tIndex].fT; |
| } while (nextT < 1 && approximately_negative(nextT - tStart)); |
| double oNextT; |
| do { |
| oNextT = other.fTs[++oIndex].fT; |
| } while (oNextT < 1 && approximately_negative(oNextT - oStart)); |
| // at this point, spans before and after are at: |
| // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex] |
| // if tIndexStart == 0, no prior span |
| // if nextT == 1, no following span |
| |
| // advance the span with zero winding |
| // if the following span exists (not past the end, non-zero winding) |
| // connect the two edges |
| if (!fTs[tIndexStart].fWindValue) { |
| if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) { |
| #if DEBUG_CONCIDENT |
| SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n", |
| __FUNCTION__, fID, other.fID, tIndexStart - 1, |
| fTs[tIndexStart].fT, xyAtT(tIndexStart).fX, |
| xyAtT(tIndexStart).fY); |
| #endif |
| addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false, |
| fTs[tIndexStart].fPt); |
| } |
| if (nextT < 1 && fTs[tIndex].fWindValue) { |
| #if DEBUG_CONCIDENT |
| SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n", |
| __FUNCTION__, fID, other.fID, tIndex, |
| fTs[tIndex].fT, xyAtT(tIndex).fX, |
| xyAtT(tIndex).fY); |
| #endif |
| addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false, fTs[tIndex].fPt); |
| } |
| } else { |
| SkASSERT(!other.fTs[oIndexStart].fWindValue); |
| if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) { |
| #if DEBUG_CONCIDENT |
| SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n", |
| __FUNCTION__, fID, other.fID, oIndexStart - 1, |
| other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX, |
| other.xyAtT(oIndexStart).fY); |
| other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT); |
| #endif |
| } |
| if (oNextT < 1 && other.fTs[oIndex].fWindValue) { |
| #if DEBUG_CONCIDENT |
| SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n", |
| __FUNCTION__, fID, other.fID, oIndex, |
| other.fTs[oIndex].fT, other.xyAtT(oIndex).fX, |
| other.xyAtT(oIndex).fY); |
| other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT); |
| #endif |
| } |
| } |
| } |
| |
| void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other, |
| double oEnd) { |
| // walk this to outsideTs[0] |
| // walk other to outsideTs[1] |
| // if either is > 0, add a pointer to the other, copying adjacent winding |
| int tIndex = -1; |
| int oIndex = -1; |
| double tStart = outsideTs[0]; |
| double oStart = outsideTs[1]; |
| do { |
| ++tIndex; |
| } while (!approximately_negative(tStart - fTs[tIndex].fT)); |
| SkPoint ptStart = fTs[tIndex].fPt; |
| do { |
| ++oIndex; |
| } while (!approximately_negative(oStart - other.fTs[oIndex].fT)); |
| if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) { |
| addTPair(tStart, other, oStart, false, ptStart); |
| } |
| tStart = fTs[tIndex].fT; |
| oStart = other.fTs[oIndex].fT; |
| do { |
| double nextT; |
| do { |
| nextT = fTs[++tIndex].fT; |
| } while (approximately_negative(nextT - tStart)); |
| tStart = nextT; |
| ptStart = fTs[tIndex].fPt; |
| do { |
| nextT = other.fTs[++oIndex].fT; |
| } while (approximately_negative(nextT - oStart)); |
| oStart = nextT; |
| if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) { |
| break; |
| } |
| addTPair(tStart, other, oStart, false, ptStart); |
| } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart)); |
| } |
| |
| void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) { |
| init(pts, SkPath::kCubic_Verb, operand, evenOdd); |
| fBounds.setCubicBounds(pts); |
| } |
| |
| /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const { |
| SkPoint edge[4]; |
| const SkPoint* ePtr; |
| int lastT = fTs.count() - 1; |
| if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) { |
| ePtr = fPts; |
| } else { |
| // OPTIMIZE? if not active, skip remainder and return xy_at_t(end) |
| subDivide(start, end, edge); |
| ePtr = edge; |
| } |
| if (active) { |
| bool reverse = ePtr == fPts && start != 0; |
| if (reverse) { |
| path.deferredMoveLine(ePtr[fVerb]); |
| switch (fVerb) { |
| case SkPath::kLine_Verb: |
| path.deferredLine(ePtr[0]); |
| break; |
| case SkPath::kQuad_Verb: |
| path.quadTo(ePtr[1], ePtr[0]); |
| break; |
| case SkPath::kCubic_Verb: |
| path.cubicTo(ePtr[2], ePtr[1], ePtr[0]); |
| break; |
| default: |
| SkASSERT(0); |
| } |
| // return ePtr[0]; |
| } else { |
| path.deferredMoveLine(ePtr[0]); |
| switch (fVerb) { |
| case SkPath::kLine_Verb: |
| path.deferredLine(ePtr[1]); |
| break; |
| case SkPath::kQuad_Verb: |
| path.quadTo(ePtr[1], ePtr[2]); |
| break; |
| case SkPath::kCubic_Verb: |
| path.cubicTo(ePtr[1], ePtr[2], ePtr[3]); |
| break; |
| default: |
| SkASSERT(0); |
| } |
| } |
| } |
| // return ePtr[fVerb]; |
| } |
| |
| void addLine(const SkPoint pts[2], bool operand, bool evenOdd) { |
| init(pts, SkPath::kLine_Verb, operand, evenOdd); |
| fBounds.set(pts, 2); |
| } |
| |
| #if 0 |
| const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const { |
| const SkPoint& pt = xyAtT(tIndex); |
| if (active) { |
| path.deferredMove(pt); |
| } |
| return pt; |
| } |
| #endif |
| |
| // add 2 to edge or out of range values to get T extremes |
| void addOtherT(int index, double otherT, int otherIndex) { |
| Span& span = fTs[index]; |
| #if PIN_ADD_T |
| if (precisely_less_than_zero(otherT)) { |
| otherT = 0; |
| } else if (precisely_greater_than_one(otherT)) { |
| otherT = 1; |
| } |
| #endif |
| span.fOtherT = otherT; |
| span.fOtherIndex = otherIndex; |
| } |
| |
| void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) { |
| init(pts, SkPath::kQuad_Verb, operand, evenOdd); |
| fBounds.setQuadBounds(pts); |
| } |
| |
| // Defer all coincident edge processing until |
| // after normal intersections have been computed |
| |
| // no need to be tricky; insert in normal T order |
| // resolve overlapping ts when considering coincidence later |
| |
| // add non-coincident intersection. Resulting edges are sorted in T. |
| int addT(Segment* other, const SkPoint& pt, double& newT) { |
| // FIXME: in the pathological case where there is a ton of intercepts, |
| // binary search? |
| int insertedAt = -1; |
| size_t tCount = fTs.count(); |
| #if PIN_ADD_T |
| // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect) |
| if (precisely_less_than_zero(newT)) { |
| newT = 0; |
| } else if (precisely_greater_than_one(newT)) { |
| newT = 1; |
| } |
| #endif |
| for (size_t index = 0; index < tCount; ++index) { |
| // OPTIMIZATION: if there are three or more identical Ts, then |
| // the fourth and following could be further insertion-sorted so |
| // that all the edges are clockwise or counterclockwise. |
| // This could later limit segment tests to the two adjacent |
| // neighbors, although it doesn't help with determining which |
| // circular direction to go in. |
| if (newT < fTs[index].fT) { |
| insertedAt = index; |
| break; |
| } |
| } |
| Span* span; |
| if (insertedAt >= 0) { |
| span = fTs.insert(insertedAt); |
| } else { |
| insertedAt = tCount; |
| span = fTs.append(); |
| } |
| span->fT = newT; |
| span->fOther = other; |
| span->fPt = pt; |
| span->fWindSum = SK_MinS32; |
| span->fOppSum = SK_MinS32; |
| span->fWindValue = 1; |
| span->fOppValue = 0; |
| span->fTiny = false; |
| span->fLoop = false; |
| if ((span->fDone = newT == 1)) { |
| ++fDoneSpans; |
| } |
| span->fUnsortableStart = false; |
| span->fUnsortableEnd = false; |
| int less = -1; |
| while (&span[less + 1] - fTs.begin() > 0 && xyAtT(&span[less]) == xyAtT(span)) { |
| #if 1 |
| if (span[less].fDone) { |
| break; |
| } |
| double tInterval = newT - span[less].fT; |
| if (precisely_negative(tInterval)) { |
| break; |
| } |
| if (fVerb == SkPath::kCubic_Verb) { |
| double tMid = newT - tInterval / 2; |
| _Point midPt; |
| CubicXYAtT(fPts, tMid, &midPt); |
| if (!midPt.approximatelyEqual(xyAtT(span))) { |
| break; |
| } |
| } |
| span[less].fTiny = true; |
| span[less].fDone = true; |
| if (approximately_negative(newT - span[less].fT)) { |
| if (approximately_greater_than_one(newT)) { |
| span[less].fUnsortableStart = true; |
| span[less - 1].fUnsortableEnd = true; |
| } |
| if (approximately_less_than_zero(span[less].fT)) { |
| span[less + 1].fUnsortableStart = true; |
| span[less].fUnsortableEnd = true; |
| } |
| } |
| ++fDoneSpans; |
| #else |
| double tInterval = newT - span[less].fT; |
| if (precisely_negative(tInterval)) { |
| break; |
| } |
| if (fVerb == SkPath::kCubic_Verb) { |
| double tMid = newT - tInterval / 2; |
| _Point midPt; |
| CubicXYAtT(fPts, tMid, &midPt); |
| if (!midPt.approximatelyEqual(xyAtT(span))) { |
| break; |
| } |
| } |
| SkASSERT(span[less].fDone == span->fDone); |
| if (span[less].fT == 0) { |
| span->fT = newT = 0; |
| } else { |
| setSpanT(less, newT); |
| } |
| #endif |
| --less; |
| } |
| int more = 1; |
| while (fTs.end() - &span[more - 1] > 1 && xyAtT(&span[more]) == xyAtT(span)) { |
| #if 1 |
| if (span[more - 1].fDone) { |
| break; |
| } |
| double tEndInterval = span[more].fT - newT; |
| if (precisely_negative(tEndInterval)) { |
| break; |
| } |
| if (fVerb == SkPath::kCubic_Verb) { |
| double tMid = newT - tEndInterval / 2; |
| _Point midEndPt; |
| CubicXYAtT(fPts, tMid, &midEndPt); |
| if (!midEndPt.approximatelyEqual(xyAtT(span))) { |
| break; |
| } |
| } |
| span[more - 1].fTiny = true; |
| span[more - 1].fDone = true; |
| if (approximately_negative(span[more].fT - newT)) { |
| if (approximately_greater_than_one(span[more].fT)) { |
| span[more + 1].fUnsortableStart = true; |
| span[more].fUnsortableEnd = true; |
| } |
| if (approximately_less_than_zero(newT)) { |
| span[more].fUnsortableStart = true; |
| span[more - 1].fUnsortableEnd = true; |
| } |
| } |
| ++fDoneSpans; |
| #else |
| double tEndInterval = span[more].fT - newT; |
| if (precisely_negative(tEndInterval)) { |
| break; |
| } |
| if (fVerb == SkPath::kCubic_Verb) { |
| double tMid = newT - tEndInterval / 2; |
| _Point midEndPt; |
| CubicXYAtT(fPts, tMid, &midEndPt); |
| if (!midEndPt.approximatelyEqual(xyAtT(span))) { |
| break; |
| } |
| } |
| SkASSERT(span[more - 1].fDone == span[more].fDone); |
| if (newT == 0) { |
| setSpanT(more, 0); |
| } else { |
| span->fT = newT = span[more].fT; |
| } |
| #endif |
| ++more; |
| } |
| return insertedAt; |
| } |
| |
| // set spans from start to end to decrement by one |
| // note this walks other backwards |
| // FIMXE: there's probably an edge case that can be constructed where |
| // two span in one segment are separated by float epsilon on one span but |
| // not the other, if one segment is very small. For this |
| // case the counts asserted below may or may not be enough to separate the |
| // spans. Even if the counts work out, what if the spans aren't correctly |
| // sorted? It feels better in such a case to match the span's other span |
| // pointer since both coincident segments must contain the same spans. |
| void addTCancel(double startT, double endT, Segment& other, |
| double oStartT, double oEndT) { |
| SkASSERT(!approximately_negative(endT - startT)); |
| SkASSERT(!approximately_negative(oEndT - oStartT)); |
| bool binary = fOperand != other.fOperand; |
| int index = 0; |
| while (!approximately_negative(startT - fTs[index].fT)) { |
| ++index; |
| } |
| int oIndex = other.fTs.count(); |
| while (approximately_positive(other.fTs[--oIndex].fT - oEndT)) |
| ; |
| double tRatio = (oEndT - oStartT) / (endT - startT); |
| Span* test = &fTs[index]; |
| Span* oTest = &other.fTs[oIndex]; |
| SkTDArray<double> outsideTs; |
| SkTDArray<double> oOutsideTs; |
| do { |
| bool decrement = test->fWindValue && oTest->fWindValue && !binary; |
| bool track = test->fWindValue || oTest->fWindValue; |
| double testT = test->fT; |
| double oTestT = oTest->fT; |
| Span* span = test; |
| do { |
| if (decrement) { |
| decrementSpan(span); |
| } else if (track && span->fT < 1 && oTestT < 1) { |
| TrackOutside(outsideTs, span->fT, oTestT); |
| } |
| span = &fTs[++index]; |
| } while (approximately_negative(span->fT - testT)); |
| Span* oSpan = oTest; |
| double otherTMatchStart = oEndT - (span->fT - startT) * tRatio; |
| double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio; |
| SkDEBUGCODE(int originalWindValue = oSpan->fWindValue); |
| while (approximately_negative(otherTMatchStart - oSpan->fT) |
| && !approximately_negative(otherTMatchEnd - oSpan->fT)) { |
| #ifdef SK_DEBUG |
| SkASSERT(originalWindValue == oSpan->fWindValue); |
| #endif |
| if (decrement) { |
| other.decrementSpan(oSpan); |
| } else if (track && oSpan->fT < 1 && testT < 1) { |
| TrackOutside(oOutsideTs, oSpan->fT, testT); |
| } |
| if (!oIndex) { |
| break; |
| } |
| oSpan = &other.fTs[--oIndex]; |
| } |
| test = span; |
| oTest = oSpan; |
| } while (!approximately_negative(endT - test->fT)); |
| SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT)); |
| // FIXME: determine if canceled edges need outside ts added |
| if (!done() && outsideTs.count()) { |
| double tStart = outsideTs[0]; |
| double oStart = outsideTs[1]; |
| addCancelOutsides(tStart, oStart, other, oEndT); |
| int count = outsideTs.count(); |
| if (count > 2) { |
| double tStart = outsideTs[count - 2]; |
| double oStart = outsideTs[count - 1]; |
| addCancelOutsides(tStart, oStart, other, oEndT); |
| } |
| } |
| if (!other.done() && oOutsideTs.count()) { |
| double tStart = oOutsideTs[0]; |
| double oStart = oOutsideTs[1]; |
| other.addCancelOutsides(tStart, oStart, *this, endT); |
| } |
| } |
| |
| int addSelfT(Segment* other, const SkPoint& pt, double& newT) { |
| int result = addT(other, pt, newT); |
| Span* span = &fTs[result]; |
| span->fLoop = true; |
| return result; |
| } |
| |
| int addUnsortableT(Segment* other, bool start, const SkPoint& pt, double& newT) { |
| int result = addT(other, pt, newT); |
| Span* span = &fTs[result]; |
| if (start) { |
| if (result > 0) { |
| span[result - 1].fUnsortableEnd = true; |
| } |
| span[result].fUnsortableStart = true; |
| } else { |
| span[result].fUnsortableEnd = true; |
| if (result + 1 < fTs.count()) { |
| span[result + 1].fUnsortableStart = true; |
| } |
| } |
| return result; |
| } |
| |
| int bumpCoincidentThis(const Span* oTest, bool opp, int index, |
| SkTDArray<double>& outsideTs) { |
| int oWindValue = oTest->fWindValue; |
| int oOppValue = oTest->fOppValue; |
| if (opp) { |
| SkTSwap<int>(oWindValue, oOppValue); |
| } |
| Span* const test = &fTs[index]; |
| Span* end = test; |
| const double oStartT = oTest->fT; |
| do { |
| if (bumpSpan(end, oWindValue, oOppValue)) { |
| TrackOutside(outsideTs, end->fT, oStartT); |
| } |
| end = &fTs[++index]; |
| } while (approximately_negative(end->fT - test->fT)); |
| return index; |
| } |
| |
| // because of the order in which coincidences are resolved, this and other |
| // may not have the same intermediate points. Compute the corresponding |
| // intermediate T values (using this as the master, other as the follower) |
| // and walk other conditionally -- hoping that it catches up in the end |
| int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex, |
| SkTDArray<double>& oOutsideTs) { |
| Span* const oTest = &fTs[oIndex]; |
| Span* oEnd = oTest; |
| const double startT = test->fT; |
| const double oStartT = oTest->fT; |
| while (!approximately_negative(oEndT - oEnd->fT) |
| && approximately_negative(oEnd->fT - oStartT)) { |
| zeroSpan(oEnd); |
| TrackOutside(oOutsideTs, oEnd->fT, startT); |
| oEnd = &fTs[++oIndex]; |
| } |
| return oIndex; |
| } |
| |
| // FIXME: need to test 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 |
| // since logic below doesn't transfer count (only increments/decrements) can this be |
| // resolved to +4/0 ? |
| |
| // set spans from start to end to increment the greater by one and decrement |
| // the lesser |
| void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) { |
| SkASSERT(!approximately_negative(endT - startT)); |
| SkASSERT(!approximately_negative(oEndT - oStartT)); |
| bool opp = fOperand ^ other.fOperand; |
| int index = 0; |
| while (!approximately_negative(startT - fTs[index].fT)) { |
| ++index; |
| } |
| int oIndex = 0; |
| while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) { |
| ++oIndex; |
| } |
| Span* test = &fTs[index]; |
| Span* oTest = &other.fTs[oIndex]; |
| SkTDArray<double> outsideTs; |
| SkTDArray<double> oOutsideTs; |
| do { |
| // if either span has an opposite value and the operands don't match, resolve first |
| // SkASSERT(!test->fDone || !oTest->fDone); |
| if (test->fDone || oTest->fDone) { |
| index = advanceCoincidentThis(oTest, opp, index); |
| oIndex = other.advanceCoincidentOther(test, oEndT, oIndex); |
| } else { |
| index = bumpCoincidentThis(oTest, opp, index, outsideTs); |
| oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs); |
| } |
| test = &fTs[index]; |
| oTest = &other.fTs[oIndex]; |
| } while (!approximately_negative(endT - test->fT)); |
| SkASSERT(approximately_negative(oTest->fT - oEndT)); |
| SkASSERT(approximately_negative(oEndT - oTest->fT)); |
| if (!done() && outsideTs.count()) { |
| addCoinOutsides(outsideTs, other, oEndT); |
| } |
| if (!other.done() && oOutsideTs.count()) { |
| other.addCoinOutsides(oOutsideTs, *this, endT); |
| } |
| } |
| |
| // FIXME: this doesn't prevent the same span from being added twice |
| // fix in caller, SkASSERT here? |
| void addTPair(double t, Segment& other, double otherT, bool borrowWind, const SkPoint& pt) { |
| int tCount = fTs.count(); |
| for (int tIndex = 0; tIndex < tCount; ++tIndex) { |
| const Span& span = fTs[tIndex]; |
| if (!approximately_negative(span.fT - t)) { |
| break; |
| } |
| if (approximately_negative(span.fT - t) && span.fOther == &other |
| && approximately_equal(span.fOtherT, otherT)) { |
| #if DEBUG_ADD_T_PAIR |
| SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n", |
| __FUNCTION__, fID, t, other.fID, otherT); |
| #endif |
| return; |
| } |
| } |
| #if DEBUG_ADD_T_PAIR |
| SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n", |
| __FUNCTION__, fID, t, other.fID, otherT); |
| #endif |
| int insertedAt = addT(&other, pt, t); |
| int otherInsertedAt = other.addT(this, pt, otherT); |
| addOtherT(insertedAt, otherT, otherInsertedAt); |
| other.addOtherT(otherInsertedAt, t, insertedAt); |
| matchWindingValue(insertedAt, t, borrowWind); |
| other.matchWindingValue(otherInsertedAt, otherT, borrowWind); |
| } |
| |
| void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const { |
| // add edge leading into junction |
| int min = SkMin32(end, start); |
| if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) { |
| addAngle(angles, end, start); |
| } |
| // add edge leading away from junction |
| int step = SkSign32(end - start); |
| int tIndex = nextExactSpan(end, step); |
| min = SkMin32(end, tIndex); |
| if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) { |
| addAngle(angles, end, tIndex); |
| } |
| } |
| |
| int advanceCoincidentThis(const Span* oTest, bool opp, int index) { |
| Span* const test = &fTs[index]; |
| Span* end = test; |
| do { |
| end = &fTs[++index]; |
| } while (approximately_negative(end->fT - test->fT)); |
| return index; |
| } |
| |
| int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) { |
| Span* const oTest = &fTs[oIndex]; |
| Span* oEnd = oTest; |
| const double oStartT = oTest->fT; |
| while (!approximately_negative(oEndT - oEnd->fT) |
| && approximately_negative(oEnd->fT - oStartT)) { |
| oEnd = &fTs[++oIndex]; |
| } |
| return oIndex; |
| } |
| |
| bool betweenTs(int lesser, double testT, int greater) { |
| if (lesser > greater) { |
| SkTSwap<int>(lesser, greater); |
| } |
| return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT); |
| } |
| |
| const Bounds& bounds() const { |
| return fBounds; |
| } |
| |
| void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const { |
| double referenceT = fTs[index].fT; |
| int lesser = index; |
| while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand) |
| && precisely_negative(referenceT - fTs[lesser].fT)) { |
| buildAnglesInner(lesser, angles); |
| } |
| do { |
| buildAnglesInner(index, angles); |
| } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand) |
| && precisely_negative(fTs[index].fT - referenceT)); |
| } |
| |
| void buildAnglesInner(int index, SkTDArray<Angle>& angles) const { |
| const Span* span = &fTs[index]; |
| Segment* other = span->fOther; |
| // if there is only one live crossing, and no coincidence, continue |
| // in the same direction |
| // if there is coincidence, the only choice may be to reverse direction |
| // find edge on either side of intersection |
| int oIndex = span->fOtherIndex; |
| // if done == -1, prior span has already been processed |
| int step = 1; |
| int next = other->nextExactSpan(oIndex, step); |
| if (next < 0) { |
| step = -step; |
| next = other->nextExactSpan(oIndex, step); |
| } |
| // add candidate into and away from junction |
| other->addTwoAngles(next, oIndex, angles); |
| } |
| |
| int computeSum(int startIndex, int endIndex, bool binary) { |
| SkTDArray<Angle> angles; |
| addTwoAngles(startIndex, endIndex, angles); |
| buildAngles(endIndex, angles, false); |
| // OPTIMIZATION: check all angles to see if any have computed wind sum |
| // before sorting (early exit if none) |
| SkTDArray<Angle*> sorted; |
| bool sortable = SortAngles(angles, sorted); |
| #if DEBUG_SORT |
| sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0); |
| #endif |
| if (!sortable) { |
| return SK_MinS32; |
| } |
| int angleCount = angles.count(); |
| const Angle* angle; |
| const Segment* base; |
| int winding; |
| int oWinding; |
| int firstIndex = 0; |
| do { |
| angle = sorted[firstIndex]; |
| base = angle->segment(); |
| winding = base->windSum(angle); |
| if (winding != SK_MinS32) { |
| oWinding = base->oppSum(angle); |
| break; |
| } |
| if (++firstIndex == angleCount) { |
| return SK_MinS32; |
| } |
| } while (true); |
| // turn winding into contourWinding |
| int spanWinding = base->spanSign(angle); |
| bool inner = useInnerWinding(winding + spanWinding, winding); |
| #if DEBUG_WINDING |
| SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__, |
| spanWinding, winding, angle->sign(), inner, |
| inner ? winding + spanWinding : winding); |
| #endif |
| if (inner) { |
| winding += spanWinding; |
| } |
| #if DEBUG_SORT |
| base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding); |
| #endif |
| int nextIndex = firstIndex + 1; |
| int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
| winding -= base->spanSign(angle); |
| oWinding -= base->oppSign(angle); |
| do { |
| if (nextIndex == angleCount) { |
| nextIndex = 0; |
| } |
| angle = sorted[nextIndex]; |
| Segment* segment = angle->segment(); |
| bool opp = base->fOperand ^ segment->fOperand; |
| int maxWinding, oMaxWinding; |
| int spanSign = segment->spanSign(angle); |
| int oppoSign = segment->oppSign(angle); |
| if (opp) { |
| oMaxWinding = oWinding; |
| oWinding -= spanSign; |
| maxWinding = winding; |
| if (oppoSign) { |
| winding -= oppoSign; |
| } |
| } else { |
| maxWinding = winding; |
| winding -= spanSign; |
| oMaxWinding = oWinding; |
| if (oppoSign) { |
| oWinding -= oppoSign; |
| } |
| } |
| if (segment->windSum(angle) == SK_MinS32) { |
| if (opp) { |
| if (useInnerWinding(oMaxWinding, oWinding)) { |
| oMaxWinding = oWinding; |
| } |
| if (oppoSign && useInnerWinding(maxWinding, winding)) { |
| maxWinding = winding; |
| } |
| (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding); |
| } else { |
| if (useInnerWinding(maxWinding, winding)) { |
| maxWinding = winding; |
| } |
| if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) { |
| oMaxWinding = oWinding; |
| } |
| (void) segment->markAndChaseWinding(angle, maxWinding, |
| binary ? oMaxWinding : 0); |
| } |
| } |
| } while (++nextIndex != lastIndex); |
| int minIndex = SkMin32(startIndex, endIndex); |
| return windSum(minIndex); |
| } |
| |
| int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething, |
| double mid, bool opp, bool current) const { |
| SkScalar bottom = fBounds.fBottom; |
| int bestTIndex = -1; |
| if (bottom <= bestY) { |
| return bestTIndex; |
| } |
| SkScalar top = fBounds.fTop; |
| if (top >= basePt.fY) { |
| return bestTIndex; |
| } |
| if (fBounds.fLeft > basePt.fX) { |
| return bestTIndex; |
| } |
| if (fBounds.fRight < basePt.fX) { |
| return bestTIndex; |
| } |
| if (fBounds.fLeft == fBounds.fRight) { |
| // if vertical, and directly above test point, wait for another one |
| return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex; |
| } |
| // intersect ray starting at basePt with edge |
| Intersections intersections; |
| // OPTIMIZE: use specialty function that intersects ray with curve, |
| // returning t values only for curve (we don't care about t on ray) |
| int pts = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX, false, intersections); |
| if (pts == 0 || (current && pts == 1)) { |
| return bestTIndex; |
| } |
| if (current) { |
| SkASSERT(pts > 1); |
| int closestIdx = 0; |
| double closest = fabs(intersections.fT[0][0] - mid); |
| for (int idx = 1; idx < pts; ++idx) { |
| double test = fabs(intersections.fT[0][idx] - mid); |
| if (closest > test) { |
| closestIdx = idx; |
| closest = test; |
| } |
| } |
| if (closestIdx < pts - 1) { |
| intersections.fT[0][closestIdx] = intersections.fT[0][pts - 1]; |
| } |
| --pts; |
| } |
| double bestT = -1; |
| for (int index = 0; index < pts; ++index) { |
| double foundT = intersections.fT[0][index]; |
| if (approximately_less_than_zero(foundT) |
| || approximately_greater_than_one(foundT)) { |
| continue; |
| } |
| SkScalar testY = (*SegmentYAtT[fVerb])(fPts, foundT); |
| if (approximately_negative(testY - bestY) |
| || approximately_negative(basePt.fY - testY)) { |
| continue; |
| } |
| if (pts > 1 && fVerb == SkPath::kLine_Verb) { |
| return SK_MinS32; // if the intersection is edge on, wait for another one |
| } |
| if (fVerb > SkPath::kLine_Verb) { |
| SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, foundT); |
| if (approximately_zero(dx)) { |
| return SK_MinS32; // hit vertical, wait for another one |
| } |
| } |
| bestY = testY; |
| bestT = foundT; |
| } |
| if (bestT < 0) { |
| return bestTIndex; |
| } |
| SkASSERT(bestT >= 0); |
| SkASSERT(bestT <= 1); |
| int start; |
| int end = 0; |
| do { |
| start = end; |
| end = nextSpan(start, 1); |
| } while (fTs[end].fT < bestT); |
| // FIXME: see next candidate for a better pattern to find the next start/end pair |
| while (start + 1 < end && fTs[start].fDone) { |
| ++start; |
| } |
| if (!isCanceled(start)) { |
| hitT = bestT; |
| bestTIndex = start; |
| hitSomething = true; |
| } |
| return bestTIndex; |
| } |
| |
| void decrementSpan(Span* span) { |
| SkASSERT(span->fWindValue > 0); |
| if (--(span->fWindValue) == 0) { |
| if (!span->fOppValue && !span->fDone) { |
| span->fDone = true; |
| ++fDoneSpans; |
| } |
| } |
| } |
| |
| bool bumpSpan(Span* span, int windDelta, int oppDelta) { |
| SkASSERT(!span->fDone); |
| span->fWindValue += windDelta; |
| SkASSERT(span->fWindValue >= 0); |
| span->fOppValue += oppDelta; |
| SkASSERT(span->fOppValue >= 0); |
| if (fXor) { |
| span->fWindValue &= 1; |
| } |
| if (fOppXor) { |
| span->fOppValue &= 1; |
| } |
| if (!span->fWindValue && !span->fOppValue) { |
| span->fDone = true; |
| ++fDoneSpans; |
| return true; |
| } |
| return false; |
| } |
| |
| // 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; |
| } |
| |
| bool done() const { |
| SkASSERT(fDoneSpans <= fTs.count()); |
| return fDoneSpans == fTs.count(); |
| } |
| |
| bool done(int min) const { |
| return fTs[min].fDone; |
| } |
| |
| bool done(const Angle* angle) const { |
| return done(SkMin32(angle->start(), angle->end())); |
| } |
| |
| SkVector dxdy(int index) const { |
| return (*SegmentDXDYAtT[fVerb])(fPts, fTs[index].fT); |
| } |
| |
| SkScalar dy(int index) const { |
| return (*SegmentDYAtT[fVerb])(fPts, fTs[index].fT); |
| } |
| |
| bool equalPoints(int greaterTIndex, int lesserTIndex) { |
| SkASSERT(greaterTIndex >= lesserTIndex); |
| double greaterT = fTs[greaterTIndex].fT; |
| double lesserT = fTs[lesserTIndex].fT; |
| if (greaterT == lesserT) { |
| return true; |
| } |
| if (!approximately_negative(greaterT - lesserT)) { |
| return false; |
| } |
| return xyAtT(greaterTIndex) == xyAtT(lesserTIndex); |
| } |
| |
| /* |
| The M and S variable name parts stand for the operators. |
| Mi stands for Minuend (see wiki subtraction, analogous to difference) |
| Su stands for Subtrahend |
| The Opp variable name part designates that the value is for the Opposite operator. |
| Opposite values result from combining coincident spans. |
| */ |
| |
| Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd, |
| bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) { |
| const int startIndex = nextStart; |
| const int endIndex = nextEnd; |
| SkASSERT(startIndex != endIndex); |
| const int count = fTs.count(); |
| SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0); |
| const int step = SkSign32(endIndex - startIndex); |
| const int end = nextExactSpan(startIndex, step); |
| SkASSERT(end >= 0); |
| Span* endSpan = &fTs[end]; |
| Segment* other; |
| if (isSimple(end)) { |
| // mark the smaller of startIndex, endIndex done, and all adjacent |
| // spans with the same T value (but not 'other' spans) |
| #if DEBUG_WINDING |
| SkDebugf("%s simple\n", __FUNCTION__); |
| #endif |
| int min = SkMin32(startIndex, endIndex); |
| if (fTs[min].fDone) { |
| return NULL; |
| } |
| markDoneBinary(min); |
| other = endSpan->fOther; |
| nextStart = endSpan->fOtherIndex; |
| double startT = other->fTs[nextStart].fT; |
| nextEnd = nextStart; |
| do { |
| nextEnd += step; |
| } |
| while (precisely_zero(startT - other->fTs[nextEnd].fT)); |
| SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count()); |
| return other; |
| } |
| // more than one viable candidate -- measure angles to find best |
| SkTDArray<Angle> angles; |
| SkASSERT(startIndex - endIndex != 0); |
| SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
| addTwoAngles(startIndex, end, angles); |
| buildAngles(end, angles, true); |
| SkTDArray<Angle*> sorted; |
| bool sortable = SortAngles(angles, sorted); |
| int angleCount = angles.count(); |
| int firstIndex = findStartingEdge(sorted, startIndex, end); |
| SkASSERT(firstIndex >= 0); |
| #if DEBUG_SORT |
| debugShowSort(__FUNCTION__, sorted, firstIndex); |
| #endif |
| if (!sortable) { |
| unsortable = true; |
| return NULL; |
| } |
| SkASSERT(sorted[firstIndex]->segment() == this); |
| #if DEBUG_WINDING |
| SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex, |
| sorted[firstIndex]->sign()); |
| #endif |
| int sumMiWinding = updateWinding(endIndex, startIndex); |
| int sumSuWinding = updateOppWinding(endIndex, startIndex); |
| if (operand()) { |
| SkTSwap<int>(sumMiWinding, sumSuWinding); |
| } |
| int nextIndex = firstIndex + 1; |
| int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
| const Angle* foundAngle = NULL; |
| bool foundDone = false; |
| // iterate through the angle, and compute everyone's winding |
| Segment* nextSegment; |
| int activeCount = 0; |
| do { |
| SkASSERT(nextIndex != firstIndex); |
| if (nextIndex == angleCount) { |
| nextIndex = 0; |
| } |
| const Angle* nextAngle = sorted[nextIndex]; |
| nextSegment = nextAngle->segment(); |
| int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
| bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(), |
| nextAngle->end(), op, sumMiWinding, sumSuWinding, |
| maxWinding, sumWinding, oppMaxWinding, oppSumWinding); |
| if (activeAngle) { |
| ++activeCount; |
| if (!foundAngle || (foundDone && activeCount & 1)) { |
| if (nextSegment->tiny(nextAngle)) { |
| unsortable = true; |
| return NULL; |
| } |
| foundAngle = nextAngle; |
| foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle); |
| } |
| } |
| if (nextSegment->done()) { |
| continue; |
| } |
| if (nextSegment->windSum(nextAngle) != SK_MinS32) { |
| continue; |
| } |
| Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, |
| oppSumWinding, activeAngle, nextAngle); |
| if (last) { |
| *chase.append() = last; |
| #if DEBUG_WINDING |
| SkDebugf("%s chase.append id=%d\n", __FUNCTION__, |
| last->fOther->fTs[last->fOtherIndex].fOther->debugID()); |
| #endif |
| } |
| } while (++nextIndex != lastIndex); |
| markDoneBinary(SkMin32(startIndex, endIndex)); |
| if (!foundAngle) { |
| return NULL; |
| } |
| nextStart = foundAngle->start(); |
| nextEnd = foundAngle->end(); |
| nextSegment = foundAngle->segment(); |
| |
| #if DEBUG_WINDING |
| SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n", |
| __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd); |
| #endif |
| return nextSegment; |
| } |
| |
| Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd, |
| bool& unsortable) { |
| const int startIndex = nextStart; |
| const int endIndex = nextEnd; |
| SkASSERT(startIndex != endIndex); |
| const int count = fTs.count(); |
| SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0); |
| const int step = SkSign32(endIndex - startIndex); |
| const int end = nextExactSpan(startIndex, step); |
| SkASSERT(end >= 0); |
| Span* endSpan = &fTs[end]; |
| Segment* other; |
| if (isSimple(end)) { |
| // mark the smaller of startIndex, endIndex done, and all adjacent |
| // spans with the same T value (but not 'other' spans) |
| #if DEBUG_WINDING |
| SkDebugf("%s simple\n", __FUNCTION__); |
| #endif |
| int min = SkMin32(startIndex, endIndex); |
| if (fTs[min].fDone) { |
| return NULL; |
| } |
| markDoneUnary(min); |
| other = endSpan->fOther; |
| nextStart = endSpan->fOtherIndex; |
| double startT = other->fTs[nextStart].fT; |
| nextEnd = nextStart; |
| do { |
| nextEnd += step; |
| } |
| while (precisely_zero(startT - other->fTs[nextEnd].fT)); |
| SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count()); |
| return other; |
| } |
| // more than one viable candidate -- measure angles to find best |
| SkTDArray<Angle> angles; |
| SkASSERT(startIndex - endIndex != 0); |
| SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
| addTwoAngles(startIndex, end, angles); |
| buildAngles(end, angles, true); |
| SkTDArray<Angle*> sorted; |
| bool sortable = SortAngles(angles, sorted); |
| int angleCount = angles.count(); |
| int firstIndex = findStartingEdge(sorted, startIndex, end); |
| SkASSERT(firstIndex >= 0); |
| #if DEBUG_SORT |
| debugShowSort(__FUNCTION__, sorted, firstIndex); |
| #endif |
| if (!sortable) { |
| unsortable = true; |
| return NULL; |
| } |
| SkASSERT(sorted[firstIndex]->segment() == this); |
| #if DEBUG_WINDING |
| SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex, |
| sorted[firstIndex]->sign()); |
| #endif |
| int sumWinding = updateWinding(endIndex, startIndex); |
| int nextIndex = firstIndex + 1; |
| int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
| const Angle* foundAngle = NULL; |
| bool foundDone = false; |
| // iterate through the angle, and compute everyone's winding |
| Segment* nextSegment; |
| int activeCount = 0; |
| do { |
| SkASSERT(nextIndex != firstIndex); |
| if (nextIndex == angleCount) { |
| nextIndex = 0; |
| } |
|