From 27c449af06cd1d05db441593d08b84f3530fba52 Mon Sep 17 00:00:00 2001 From: "caryclark@google.com" Date: Fri, 27 Jul 2012 18:26:38 +0000 Subject: shape ops work in progress git-svn-id: http://skia.googlecode.com/svn/trunk@4815 2bbb7eff-a529-9590-31e7-b0007b416f81 --- experimental/Intersection/EdgeWalker_Test.h | 1 + experimental/Intersection/Simplify.cpp | 163 ++++++++++++--------- .../Intersection/SimplifyFindNext_Test.cpp | 17 ++- experimental/Intersection/SimplifyNew_Test.cpp | 72 ++++++++- experimental/Intersection/SimplifyRect4x4_Test.cpp | 25 +++- experimental/Intersection/op.htm | 52 +++++++ 6 files changed, 244 insertions(+), 86 deletions(-) (limited to 'experimental') diff --git a/experimental/Intersection/EdgeWalker_Test.h b/experimental/Intersection/EdgeWalker_Test.h index e07e708f46..6a316f613e 100644 --- a/experimental/Intersection/EdgeWalker_Test.h +++ b/experimental/Intersection/EdgeWalker_Test.h @@ -26,6 +26,7 @@ struct State4 { int b; int c; int d; + int testsRun; char filename[256]; pthread_t threadID; SkCanvas* canvas; diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp index 7f1e7c7866..c6236d513a 100644 --- a/experimental/Intersection/Simplify.cpp +++ b/experimental/Intersection/Simplify.cpp @@ -50,7 +50,7 @@ const bool gRunTestsInOneThread = true; #define DEBUG_ACTIVE_SPANS 1 #define DEBUG_ADD_INTERSECTING_TS 0 #define DEBUG_ADD_T_PAIR 0 -#define DEBUG_CONCIDENT 01 +#define DEBUG_CONCIDENT 0 #define DEBUG_CROSS 1 #define DEBUG_DUMP 1 #define DEBUG_MARK_DONE 1 @@ -656,7 +656,7 @@ struct Bounds : public SkRect { struct Span { Segment* fOther; - mutable SkPoint const* fPt; // lazily computed as needed + mutable SkPoint fPt; // lazily computed as needed double fT; double fOtherT; // value at fOther[fOtherIndex].fT int fOtherIndex; // can't be used during intersection @@ -792,10 +792,10 @@ public: #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 - 1].fT, xyAtT(tIndexStart - 1).fX, - xyAtT(tIndexStart - 1).fY); + fTs[tIndexStart].fT, xyAtT(tIndexStart).fX, + xyAtT(tIndexStart).fY); #endif - SkASSERT(0); // incomplete + addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT); } if (nextT < 1 && fTs[tIndex].fWindValue) { #if DEBUG_CONCIDENT @@ -812,10 +812,10 @@ public: #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 - 1].fT, other.xyAtT(oIndexStart - 1).fX, - other.xyAtT(oIndexStart - 1).fY); + other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX, + other.xyAtT(oIndexStart).fY); + other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT); #endif - SkASSERT(0); // incomplete } if (oNextT < 1 && other.fTs[oIndex].fWindValue) { #if DEBUG_CONCIDENT @@ -965,7 +965,7 @@ public: } span->fT = newT; span->fOther = other; - span->fPt = NULL; + span->fPt.fX = SK_ScalarNaN; span->fWindSum = SK_MinS32; span->fWindValue = 1; if ((span->fDone = newT == 1)) { @@ -1068,7 +1068,7 @@ public: do { if (transfer) { if (decrementOther) { - SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue); + SkASSERT(abs(end->fWindValue) <= gDebugMaxWindValue); ++(end->fWindValue); } else if (decrementSpan(end)) { TrackOutside(outsideTs, end->fT, oStartT); @@ -1085,7 +1085,7 @@ public: do { if (transfer) { if (!decrementOther) { - SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue); + SkASSERT(abs(oEnd->fWindValue) <= gDebugMaxWindValue); ++(oEnd->fWindValue); } else if (other.decrementSpan(oEnd)) { TrackOutside(oOutsideTs, oEnd->fT, startT); @@ -1320,28 +1320,21 @@ public: // it is guaranteed to have an end which describes a non-zero length (?) // winding -1 means ccw, 1 means cw // firstFind allows coincident edges to be treated differently - Segment* findNext(SkTDArray& chase, int winding, - int contourWinding, bool firstFind, bool active, + Segment* findNext(SkTDArray& chase, bool firstFind, bool active, const int startIndex, const int endIndex, int& nextStart, - int& nextEnd, int& spanWinding) { - - start here; - // winding is a mess - // try to simplify what we got - - int flipped = 1; + int& nextEnd, int& winding, int& spanWinding) { int sumWinding = winding + spanWinding; - if (sumWinding == 0 || (false && contourWinding && !firstFind)) { + if (sumWinding == 0) { sumWinding = spanWinding; } - bool insideContour = contourWinding && contourWinding * sumWinding < 0; - if (insideContour && (true || !firstFind)) { - sumWinding = contourWinding; + bool insideContour = active && winding && winding * sumWinding < 0; + if (insideContour) { + sumWinding = winding; } #if DEBUG_WINDING - SkDebugf("%s winding=%d contourWinding=%d spanWinding=%d sumWinding=%d\n", - __FUNCTION__, winding, contourWinding, spanWinding, sumWinding); + SkDebugf("%s winding=%d spanWinding=%d sumWinding=%d\n", + __FUNCTION__, winding, spanWinding, sumWinding); #endif SkASSERT(startIndex != endIndex); int count = fTs.count(); @@ -1378,7 +1371,7 @@ public: int firstIndex = findStartingEdge(sorted, startIndex, end); SkASSERT(firstIndex >= 0); #if DEBUG_SORT - debugShowSort(sorted, firstIndex, contourWinding, sumWinding); + debugShowSort(sorted, firstIndex, winding, sumWinding); #endif bool doBump = sorted[firstIndex]->firstBump(sumWinding); #if DEBUG_WINDING @@ -1396,8 +1389,9 @@ public: const Angle* foundAngle = NULL; bool foundDone = false; // iterate through the angle, and compute everyone's winding - bool firstEdge = true; - bool flopped = false; + bool toggleWinding = false; + bool flipFound = false; + int flipped = 1; Segment* nextSegment; do { if (nextIndex == angleCount) { @@ -1413,13 +1407,12 @@ public: maxWinding, sumWinding, nextAngle->sign()); #endif if (maxWinding * sumWinding < 0) { - flipped = -flipped; - flopped = true; + flipFound ^= true; #if DEBUG_WINDING - SkDebugf("flipped sign %d %d\n", maxWinding, sumWinding); + SkDebugf("flipFound maxWinding=%d sumWinding=%d\n", + maxWinding, sumWinding); #endif } - firstEdge = false; if (!sumWinding) { if (!active) { markDone(SkMin32(startIndex, endIndex), startWinding); @@ -1433,10 +1426,11 @@ public: if (!foundAngle || foundDone) { foundAngle = nextAngle; foundDone = nextSegment->done(*nextAngle); - if (!flopped && maxWinding * startWinding < 0) { + if (flipFound || (maxWinding * startWinding < 0)) { flipped = -flipped; #if DEBUG_WINDING - SkDebugf("flopped sign %d %d\n", maxWinding, startWinding); + SkDebugf("flipped flipFound=%d maxWinding=%d startWinding=%d\n", + flipFound, maxWinding, startWinding); #endif } } @@ -1444,10 +1438,20 @@ public: } if (!maxWinding && innerSwap && !foundAngle) { if (sumWinding * startWinding < 0 && flipped > 0) { - SkDebugf("%s flip?\n"); - // flipped = -flipped; + #if DEBUG_WINDING + SkDebugf("%s toggleWinding\n"); + #endif + toggleWinding = true; + } else if (startWinding != sumWinding) { + winding = sumWinding; } foundAngle = nextAngle; + if (flipFound) { + flipped = -1; + #if DEBUG_WINDING + SkDebugf("flipped flipFound=%d\n", flipFound); + #endif + } } if (nextSegment->done()) { continue; @@ -1481,9 +1485,23 @@ public: nextSegment = foundAngle->segment(); spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue( SkMin32(nextStart, nextEnd)); - #if DEBUG_WINDING - SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding); - #endif + if (toggleWinding) { + if (winding) { + winding = 0; + } else { + winding = -startWinding; + } + } + #if 0 + int min = SkMin32(nextStart, nextEnd); + int sign = foundAngle->sign(); + int windSum = nextSegment->windSum(min); + int windValue = nextSegment->windValue(min); + SkASSERT(winding + sign * windValue == windSum); + #endif + #if DEBUG_WINDING + SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding); + #endif return nextSegment; } @@ -2013,18 +2031,16 @@ public: } const SkPoint& xyAtT(const Span* span) const { - if (!span->fPt) { + if (SkScalarIsNaN(span->fPt.fX)) { if (span->fT == 0) { - span->fPt = &fPts[0]; + span->fPt = fPts[0]; } else if (span->fT == 1) { - span->fPt = &fPts[fVerb]; + span->fPt = fPts[fVerb]; } else { - SkPoint* pt = fIntersections.append(); - (*SegmentXYAtT[fVerb])(fPts, span->fT, pt); - span->fPt = pt; + (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt); } } - return *span->fPt; + return span->fPt; } SkScalar yAtT(int index) const { @@ -2153,10 +2169,6 @@ private: SkPath::Verb fVerb; Bounds fBounds; SkTDArray fTs; // two or more (always includes t=0 t=1) - // OPTIMIZATION:if intersections array is a pointer, the it could only - // be allocated as needed instead of always initialized -- though maybe - // the initialization is lightweight enough that it hardly matters - mutable SkTDArray fIntersections; int fDoneSpans; // used for quick check that segment is finished #if DEBUG_DUMP int fID; @@ -3250,6 +3262,11 @@ static void debugShowActiveSpans(SkTDArray& contourList) { } #endif +static bool windingIsActive(int winding, int spanWinding) { + return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding) + && (!winding || !spanWinding || winding == -spanWinding); +} + // Each segment may have an inside or an outside. Segments contained within // winding may have insides on either side, and form a contour that should be // ignored. Segments that are coincident with opposing direction segments may @@ -3286,21 +3303,26 @@ static void bridge(SkTDArray& contourList, SkPath& simple) { bool firstTime = true; int winding = contourWinding; int spanWinding = current->spanSign(index, endIndex); - // int firstWinding = contourWinding + spanWinding; + int spanWindSum = current->windSum(SkMin32(index, endIndex)); + if (spanWindSum != SK_MinS32) { + int calcWinding = spanWindSum; + if (spanWinding > 0) { + // calcWinding -= spanWinding; + } + SkDebugf("%s *** winding=%d calcWinding=%d\n", __FUNCTION__, + winding, calcWinding); + winding = calcWinding; + } // FIXME: needs work. While it works in limited situations, it does // not always compute winding correctly. Active should be removed and instead // the initial winding should be correctly passed in so that if the // inner contour is wound the same way, it never finds an accumulated // winding of zero. Inside 'find next', we need to look for transitions // other than zero when resolving sorted angles. + bool active = windingIsActive(winding, spanWinding); SkTDArray chaseArray; do { - bool active = winding * spanWinding <= 0 - && abs(winding) <= abs(spanWinding); #if DEBUG_WINDING - if (abs(winding) > abs(spanWinding) && winding * spanWinding < 0) { - SkDebugf("%s *** unexpected active?\n", __FUNCTION__); - } SkDebugf("%s active=%s winding=%d spanWinding=%d\n", __FUNCTION__, active ? "true" : "false", winding, spanWinding); @@ -3309,9 +3331,9 @@ static void bridge(SkTDArray& contourList, SkPath& simple) { do { SkASSERT(!current->done()); int nextStart, nextEnd; - Segment* next = current->findNext(chaseArray, winding, - contourWinding, firstTime, active, index, endIndex, - nextStart, nextEnd, spanWinding); + Segment* next = current->findNext(chaseArray, + firstTime, active, index, endIndex, + nextStart, nextEnd, winding, spanWinding); if (!next) { break; } @@ -3341,30 +3363,27 @@ static void bridge(SkTDArray& contourList, SkPath& simple) { spanWinding = current->windSum(lesser); int spanValue = current->windValue(lesser); SkASSERT(spanWinding != SK_MinS32); - int spanSign = current->spanSign(index, endIndex); #if DEBUG_WINDING - SkDebugf("%s spanWinding=%d spanSign=%d winding=%d spanValue=%d\n", - __FUNCTION__, spanWinding, spanSign, winding, spanValue); + SkDebugf("%s spanWinding=%d winding=%d spanValue=%d\n", + __FUNCTION__, spanWinding, winding, spanValue); #endif - if (spanWinding * spanSign < 0) { - #if DEBUG_WINDING - SkDebugf("%s spanWinding * spanSign < 0\n", __FUNCTION__); - #endif - // SkTSwap(index, endIndex); - } - if (abs(spanWinding) > spanValue) { + if (abs(spanWinding) != spanValue) { winding = spanWinding; spanWinding = spanValue * SkSign32(spanWinding); winding -= spanWinding; #if DEBUG_WINDING - SkDebugf("%s spanWinding=%d winding=%d\n", __FUNCTION__, + SkDebugf("%s != spanWinding=%d winding=%d\n", __FUNCTION__, spanWinding, winding); #endif - } else { + active = windingIsActive(winding, spanWinding); + } else if (winding) { #if DEBUG_WINDING SkDebugf("%s ->0 contourWinding=%d winding=%d\n", __FUNCTION__, contourWinding, winding); #endif + // start here; + // set active=false if it was false when chase was created + active = abs(winding) <= abs(spanWinding); winding = 0; } } while (true); diff --git a/experimental/Intersection/SimplifyFindNext_Test.cpp b/experimental/Intersection/SimplifyFindNext_Test.cpp index 7ea716d5e3..5fc6305294 100644 --- a/experimental/Intersection/SimplifyFindNext_Test.cpp +++ b/experimental/Intersection/SimplifyFindNext_Test.cpp @@ -18,7 +18,7 @@ namespace SimplifyFindNextTest { #include "Intersection_Tests.h" static const SimplifyFindNextTest::Segment* testCommon( - int winding, int startIndex, int endIndex, + int contourWinding, int spanWinding, int startIndex, int endIndex, SkTArray& contours) { SkTDArray contourList; makeContourList(contours, contourList); @@ -34,8 +34,9 @@ static const SimplifyFindNextTest::Segment* testCommon( pts[0] = segment.xyAtT(&segment.span(endIndex)); int nextStart, nextEnd; SkTDArray chaseArray; - SimplifyFindNextTest::Segment* next = segment.findNext(chaseArray, winding, - 0, true, true, startIndex, endIndex, nextStart, nextEnd, winding); + SimplifyFindNextTest::Segment* next = segment.findNext(chaseArray, + true, true, startIndex, endIndex, nextStart, nextEnd, + contourWinding, spanWinding); pts[1] = next->xyAtT(&next->span(nextStart)); SkASSERT(pts[0] == pts[1]); return next; @@ -44,17 +45,19 @@ static const SimplifyFindNextTest::Segment* testCommon( static void test(const SkPath& path) { SkTArray contours; SimplifyFindNextTest::EdgeBuilder builder(path, contours); - int winding = 0; + int contourWinding = 0; + int spanWinding = 1; int start = 0; int end = 1; - testCommon(winding, start, end, contours); + testCommon(contourWinding, spanWinding, start, end, contours); } static void test(const SkPath& path, int start, int end) { SkTArray contours; SimplifyFindNextTest::EdgeBuilder builder(path, contours); - int winding = 0; - testCommon(winding, start, end, contours); + int contourWinding = 0; + int spanWinding = 1; + testCommon(contourWinding, spanWinding, start, end, contours); } static void testLine1() { diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp index ce5934155c..aa475c7334 100644 --- a/experimental/Intersection/SimplifyNew_Test.cpp +++ b/experimental/Intersection/SimplifyNew_Test.cpp @@ -582,12 +582,78 @@ static void testLine59() { testSimplifyx(path); } -static void (*firstTest)() = 0; +static void testLine60() { + SkPath path, simple; + path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(6, 12, 18, 18, (SkPath::Direction) 1); + path.addRect(4, 12, 13, 13, (SkPath::Direction) 1); + testSimplifyx(path); +} + +static void testLine61() { + SkPath path, simple; + path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(12, 0, 24, 24, (SkPath::Direction) 1); + path.addRect(12, 0, 21, 21, (SkPath::Direction) 1); + testSimplifyx(path); +} + +static void testLine62() { + SkPath path, simple; + path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(0, 12, 12, 12, (SkPath::Direction) 0); + path.addRect(4, 12, 13, 13, (SkPath::Direction) 1); + testSimplifyx(path); +} + +static void testLine63() { + SkPath path, simple; + path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 10, 20, 20, (SkPath::Direction) 0); + path.addRect(0, 6, 12, 12, (SkPath::Direction) 1); + path.addRect(0, 32, 9, 36, (SkPath::Direction) 1); + testSimplifyx(path); +} + +static void testLine64() { + SkPath path, simple; + path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(10, 40, 30, 30, (SkPath::Direction) 0); + path.addRect(18, 6, 30, 30, (SkPath::Direction) 0); + testSimplifyx(path); +} + +static void testLine65() { + SkPath path, simple; + path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(10, 0, 30, 30, (SkPath::Direction) 0); + path.addRect(24, 0, 36, 36, (SkPath::Direction) 0); + path.addRect(32, 6, 36, 41, (SkPath::Direction) 1); + testSimplifyx(path); +} + +static void testLine66() { + SkPath path, simple; + path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 30, 20, 20, (SkPath::Direction) 0); + path.addRect(12, 20, 24, 30, (SkPath::Direction) 0); + testSimplifyx(path); +} + +static void (*firstTest)() = testLine66; static struct { void (*fun)(); const char* str; } tests[] = { + TEST(testLine66), + TEST(testLine65), + TEST(testLine64), + TEST(testLine63), + TEST(testLine62), + TEST(testLine61), + TEST(testLine60), TEST(testLine59), TEST(testLine58), TEST(testLine57), @@ -661,8 +727,8 @@ void SimplifyNew_Test() { return; } #ifdef SK_DEBUG - gDebugMaxWindSum = 3; - gDebugMaxWindValue = 3; + gDebugMaxWindSum = 4; + gDebugMaxWindValue = 4; #endif size_t index = testCount - 1; if (firstTest) { diff --git a/experimental/Intersection/SimplifyRect4x4_Test.cpp b/experimental/Intersection/SimplifyRect4x4_Test.cpp index 1b87f3f364..d97bdbd444 100644 --- a/experimental/Intersection/SimplifyRect4x4_Test.cpp +++ b/experimental/Intersection/SimplifyRect4x4_Test.cpp @@ -11,6 +11,7 @@ #include "SkCanvas.h" #include "SkStream.h" #include +#include #include #include #include @@ -29,7 +30,11 @@ static const char marker[] = "\n" "var testDivs = [\n"; static const char testLineStr[] = " testLine"; +#if 0 static const char filename[] = "../../experimental/Intersection/debugXX.txt"; +#else +static const char filename[] = "/flash/debug/XX.txt"; +#endif static int testNumber; static void* testSimplify4x4RectsMain(void* data) @@ -38,6 +43,7 @@ static void* testSimplify4x4RectsMain(void* data) bzero(pathStr, sizeof(pathStr)); SkASSERT(data); State4& state = *(State4*) data; + state.testsRun = 0; int aShape = state.a & 0x03; int aCW = state.a >> 2; int bShape = state.b & 0x03; @@ -179,6 +185,7 @@ static void* testSimplify4x4RectsMain(void* data) getcwd(pwd, sizeof(pwd)); SkDebugf("%s\n", pwd); #endif + #if 1 SkFILEWStream outFile(state.filename); if (!outFile.isValid()) { continue; @@ -211,8 +218,10 @@ static void* testSimplify4x4RectsMain(void* data) outFile.writeDecAsText(testNumber); outFile.writeText("),\n"); outFile.flush(); + #endif } testSimplifyx(path, out, state.bitmap, state.canvas); + state.testsRun++; } } } @@ -229,15 +238,16 @@ const int maxThreadsAllocated = 32; void Simplify4x4RectsThreaded_Test() { #ifdef SK_DEBUG - gDebugMaxWindSum = 3; - gDebugMaxWindValue = 3; + gDebugMaxWindSum = 4; + gDebugMaxWindValue = 4; #endif int maxThreads = 1; if (!gRunTestsInOneThread) { - size_t size; int threads = -1; + size_t size = sizeof(threads); sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0); - SkDebugf("%s size=%d processors=%d\n", __FUNCTION__, size, threads); + SkDebugf("%s errno=%d size=%d processors=%d\n", __FUNCTION__, + errno, size, threads); if (threads > 0) { maxThreads = threads; } else { @@ -272,6 +282,7 @@ void Simplify4x4RectsThreaded_Test() statePtr->filename[sizeof(filename) - 6] = '0' + threadIndex % 10; } threadIndex = 0; + int testsRun = 0; for (int a = 0; a < 8; ++a) { // outermost for (int b = a ; b < 8; ++b) { for (int c = b ; c < 8; ++c) { @@ -285,6 +296,9 @@ void Simplify4x4RectsThreaded_Test() createThread(statePtr, testSimplify4x4RectsMain); if (++threadIndex >= maxThreads) { waitForCompletion(threadState, threadIndex); + for (int index = 0; index < maxThreads; ++index) { + testsRun += threadState[index].testsRun; + } } } else { testSimplify4x4RectsMain(statePtr); @@ -298,6 +312,9 @@ void Simplify4x4RectsThreaded_Test() if (!gRunTestsInOneThread) SkDebugf("\n\n%d", a); } waitForCompletion(threadState, threadIndex); + for (int index = 0; index < maxThreads; ++index) { + testsRun += threadState[index].testsRun; + } #ifdef SK_DEBUG gDebugMaxWindSum = SK_MaxS32; gDebugMaxWindValue = SK_MaxS32; diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index 73448b41a8..531812e299 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -587,11 +587,63 @@ path.close(); path.addRect(4, 4, 13, 13, (SkPath::Direction) 1); +
+ path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(6, 12, 18, 18, (SkPath::Direction) 1); + path.addRect(4, 12, 13, 13, (SkPath::Direction) 1); +
+ +
+ path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(12, 0, 24, 24, (SkPath::Direction) 1); + path.addRect(12, 0, 21, 21, (SkPath::Direction) 1); +
+ +
+ path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 0, 20, 20, (SkPath::Direction) 0); + path.addRect(0, 12, 12, 12, (SkPath::Direction) 0); + path.addRect(4, 12, 13, 13, (SkPath::Direction) 1); +
+ +
+ path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 10, 20, 20, (SkPath::Direction) 0); + path.addRect(0, 6, 12, 12, (SkPath::Direction) 1); + path.addRect(0, 32, 9, 36, (SkPath::Direction) 1); +
+ +
+ path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(10, 40, 30, 30, (SkPath::Direction) 0); + path.addRect(18, 6, 30, 30, (SkPath::Direction) 0); +
+ +
+ path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(10, 0, 30, 30, (SkPath::Direction) 0); + path.addRect(24, 0, 36, 36, (SkPath::Direction) 0); + path.addRect(32, 6, 36, 41, (SkPath::Direction) 1); +
+ +
+ path.addRect(0, 0, 60, 60, (SkPath::Direction) 0); + path.addRect(0, 30, 20, 20, (SkPath::Direction) 0); + path.addRect(12, 20, 24, 30, (SkPath::Direction) 0); +
+