Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: src/pathops/SkPathOpsTSect.h

Issue 1394503003: fix some pathops bugs found in 1M skps (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: init to avoid warning Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pathops/SkPathOpsSimplify.cpp ('k') | src/pathops/SkPathOpsTypes.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2014 Google Inc. 2 * Copyright 2014 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkChunkAlloc.h" 8 #include "SkChunkAlloc.h"
9 #include "SkPathOpsBounds.h" 9 #include "SkPathOpsBounds.h"
10 #include "SkPathOpsRect.h" 10 #include "SkPathOpsRect.h"
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 fCoinEnd.init(); 80 fCoinEnd.init();
81 } 81 }
82 82
83 const SkTSect<OppCurve, TCurve>* debugOpp() const; 83 const SkTSect<OppCurve, TCurve>* debugOpp() const;
84 const SkTSpan* debugSpan(int ) const; 84 const SkTSpan* debugSpan(int ) const;
85 const SkTSpan* debugT(double t) const; 85 const SkTSpan* debugT(double t) const;
86 #ifdef SK_DEBUG 86 #ifdef SK_DEBUG
87 bool debugIsBefore(const SkTSpan* span) const; 87 bool debugIsBefore(const SkTSpan* span) const;
88 #endif 88 #endif
89 void dump() const; 89 void dump() const;
90 void dumpAll() const;
90 void dumpBounded(int id) const; 91 void dumpBounded(int id) const;
91 void dumpBounds() const; 92 void dumpBounds() const;
92 void dumpCoin() const; 93 void dumpCoin() const;
93 94
94 double endT() const { 95 double endT() const {
95 return fEndT; 96 return fEndT;
96 } 97 }
97 98
98 SkTSpan<OppCurve, TCurve>* findOppSpan(const SkTSpan<OppCurve, TCurve>* opp) const; 99 SkTSpan<OppCurve, TCurve>* findOppSpan(const SkTSpan<OppCurve, TCurve>* opp) const;
99 100
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 result->splitAt(span, t, &fHeap); 238 result->splitAt(span, t, &fHeap);
238 result->initBounds(fCurve); 239 result->initBounds(fCurve);
239 span->initBounds(fCurve); 240 span->initBounds(fCurve);
240 return result; 241 return result;
241 } 242 }
242 243
243 bool binarySearchCoin(SkTSect<OppCurve, TCurve>* , double tStart, double tSt ep, double* t, 244 bool binarySearchCoin(SkTSect<OppCurve, TCurve>* , double tStart, double tSt ep, double* t,
244 double* oppT); 245 double* oppT);
245 SkTSpan<TCurve, OppCurve>* boundsMax() const; 246 SkTSpan<TCurve, OppCurve>* boundsMax() const;
246 void coincidentCheck(SkTSect<OppCurve, TCurve>* sect2); 247 void coincidentCheck(SkTSect<OppCurve, TCurve>* sect2);
248 void coincidentForce(SkTSect<OppCurve, TCurve>* sect2, double start1s, doubl e start1e);
247 bool coincidentHasT(double t); 249 bool coincidentHasT(double t);
248 int collapsed() const; 250 int collapsed() const;
249 void computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2, SkTSpan<TCurve, OppCurve>* first, 251 void computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2, SkTSpan<TCurve, OppCurve>* first,
250 SkTSpan<TCurve, OppCurve>* last); 252 SkTSpan<TCurve, OppCurve>* last);
251 int countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first, 253 int countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first,
252 SkTSpan<TCurve, OppCurve>** last) const; 254 SkTSpan<TCurve, OppCurve>** last) const;
253 255
254 int debugID() const { 256 int debugID() const {
255 return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1); 257 return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
256 } 258 }
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
378 } 380 }
379 381
380 template<typename TCurve, typename OppCurve> 382 template<typename TCurve, typename OppCurve>
381 void SkTSect<TCurve, OppCurve>::addForPerp(SkTSpan<OppCurve, TCurve>* span, doub le t) { 383 void SkTSect<TCurve, OppCurve>::addForPerp(SkTSpan<OppCurve, TCurve>* span, doub le t) {
382 if (!span->hasOppT(t)) { 384 if (!span->hasOppT(t)) {
383 SkTSpan<TCurve, OppCurve>* priorSpan; 385 SkTSpan<TCurve, OppCurve>* priorSpan;
384 SkTSpan<TCurve, OppCurve>* opp = this->spanAtT(t, &priorSpan); 386 SkTSpan<TCurve, OppCurve>* opp = this->spanAtT(t, &priorSpan);
385 if (!opp) { 387 if (!opp) {
386 opp = this->addFollowing(priorSpan); 388 opp = this->addFollowing(priorSpan);
387 #if DEBUG_PERP 389 #if DEBUG_PERP
388 SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan ->debugID(), t, 390 SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan ?
389 opp->debugID()); 391 priorSpan->debugID() : -1, t, opp->debugID());
390 #endif 392 #endif
391 } 393 }
392 #if DEBUG_PERP 394 #if DEBUG_PERP
393 opp->dump(); SkDebugf("\n"); 395 opp->dump(); SkDebugf("\n");
394 SkDebugf("%s addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan->debu gID(), 396 SkDebugf("%s addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan ?
395 opp->debugID()); 397 priorSpan->debugID() : -1, opp->debugID());
396 #endif 398 #endif
397 opp->addBounded(span, &fHeap); 399 opp->addBounded(span, &fHeap);
398 span->addBounded(opp, &fHeap); 400 span->addBounded(opp, &fHeap);
399 } 401 }
400 this->validate(); 402 this->validate();
401 #if DEBUG_T_SECT 403 #if DEBUG_T_SECT
402 span->validatePerpT(t); 404 span->validatePerpT(t);
403 #endif 405 #endif
404 } 406 }
405 407
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after
785 testBounded = testBounded->fNext; 787 testBounded = testBounded->fNext;
786 } 788 }
787 #endif 789 #endif
788 } 790 }
789 791
790 template<typename TCurve, typename OppCurve> 792 template<typename TCurve, typename OppCurve>
791 void SkTSpan<TCurve, OppCurve>::validatePerpT(double oppT) const { 793 void SkTSpan<TCurve, OppCurve>::validatePerpT(double oppT) const {
792 const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded; 794 const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
793 while (testBounded) { 795 while (testBounded) {
794 const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded; 796 const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded;
795 if (between(overlap->fStartT, oppT, overlap->fEndT)) { 797 if (precisely_between(overlap->fStartT, oppT, overlap->fEndT)) {
796 return; 798 return;
797 } 799 }
798 testBounded = testBounded->fNext; 800 testBounded = testBounded->fNext;
799 } 801 }
800 SkASSERT(0); 802 SkASSERT(0);
801 } 803 }
802 804
803 template<typename TCurve, typename OppCurve> 805 template<typename TCurve, typename OppCurve>
804 void SkTSpan<TCurve, OppCurve>::validatePerpPt(double t, const SkDPoint& pt) con st { 806 void SkTSpan<TCurve, OppCurve>::validatePerpPt(double t, const SkDPoint& pt) con st {
805 SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt); 807 SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
937 sect2->validate(); 939 sect2->validate();
938 // check to see if a range of points are on the curve 940 // check to see if a range of points are on the curve
939 SkTSpan<TCurve, OppCurve>* coinStart = first; 941 SkTSpan<TCurve, OppCurve>* coinStart = first;
940 do { 942 do {
941 coinStart = this->extractCoincident(sect2, coinStart, last); 943 coinStart = this->extractCoincident(sect2, coinStart, last);
942 } while (coinStart && !last->fDeleted); 944 } while (coinStart && !last->fDeleted);
943 } while ((first = next)); 945 } while ((first = next));
944 } 946 }
945 947
946 template<typename TCurve, typename OppCurve> 948 template<typename TCurve, typename OppCurve>
949 void SkTSect<TCurve, OppCurve>::coincidentForce(SkTSect<OppCurve, TCurve>* sect2 ,
950 double start1s, double start1e) {
951 SkTSpan<TCurve, OppCurve>* first = fHead;
952 SkTSpan<TCurve, OppCurve>* last = this->tail();
953 SkTSpan<OppCurve, TCurve>* oppFirst = sect2->fHead;
954 SkTSpan<OppCurve, TCurve>* oppLast = sect2->tail();
955 bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
956 deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
957 this->removeSpanRange(first, last);
958 sect2->removeSpanRange(oppFirst, oppLast);
959 first->fStartT = start1s;
960 first->fEndT = start1e;
961 first->resetBounds(fCurve);
962 first->fCoinStart.setPerp(fCurve, start1s, fCurve[0], sect2->fCurve);
963 first->fCoinEnd.setPerp(fCurve, start1e, fCurve[TCurve::kPointLast], sect2-> fCurve);
964 bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
965 double oppStartT = SkTMax(0., first->fCoinStart.perpT());
966 double oppEndT = SkTMin(1., first->fCoinEnd.perpT());
967 if (!oppMatched) {
968 SkTSwap(oppStartT, oppEndT);
969 }
970 oppFirst->fStartT = oppStartT;
971 oppFirst->fEndT = oppEndT;
972 oppFirst->resetBounds(sect2->fCurve);
973 this->removeCoincident(first, false);
974 sect2->removeCoincident(oppFirst, true);
975 if (deleteEmptySpans) {
976 this->deleteEmptySpans();
977 sect2->deleteEmptySpans();
978 }
979 }
980
981 template<typename TCurve, typename OppCurve>
947 bool SkTSect<TCurve, OppCurve>::coincidentHasT(double t) { 982 bool SkTSect<TCurve, OppCurve>::coincidentHasT(double t) {
948 SkTSpan<TCurve, OppCurve>* test = fCoincident; 983 SkTSpan<TCurve, OppCurve>* test = fCoincident;
949 while (test) { 984 while (test) {
950 if (between(test->fStartT, t, test->fEndT)) { 985 if (between(test->fStartT, t, test->fEndT)) {
951 return true; 986 return true;
952 } 987 }
953 test = test->fNext; 988 test = test->fNext;
954 } 989 }
955 return false; 990 return false;
956 } 991 }
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
1219 *oppResult = 1; 1254 *oppResult = 1;
1220 } 1255 }
1221 } else { 1256 } else {
1222 *oppResult = 1; 1257 *oppResult = 1;
1223 } 1258 }
1224 return hullResult; 1259 return hullResult;
1225 } 1260 }
1226 if (span->fIsLine && oppSpan->fIsLine) { 1261 if (span->fIsLine && oppSpan->fIsLine) {
1227 SkIntersections i; 1262 SkIntersections i;
1228 int sects = this->linesIntersect(span, opp, oppSpan, &i); 1263 int sects = this->linesIntersect(span, opp, oppSpan, &i);
1264 if (sects == 2) {
1265 return *oppResult = 1;
1266 }
1229 if (!sects) { 1267 if (!sects) {
1230 return -1; 1268 return -1;
1231 } 1269 }
1232 span->fStartT = span->fEndT = i[0][0]; 1270 span->fStartT = span->fEndT = i[0][0];
1233 oppSpan->fStartT = oppSpan->fEndT = i[1][0]; 1271 oppSpan->fStartT = oppSpan->fEndT = i[1][0];
1234 return *oppResult = 2; 1272 return *oppResult = 2;
1235 } 1273 }
1236 if (span->fIsLinear || oppSpan->fIsLinear) { 1274 if (span->fIsLinear || oppSpan->fIsLinear) {
1237 return *oppResult = (int) span->linearsIntersect(oppSpan); 1275 return *oppResult = (int) span->linearsIntersect(oppSpan);
1238 } 1276 }
(...skipping 11 matching lines...) Expand all
1250 SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }}; 1288 SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
1251 SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[OppCurve::kPointLast] }}; 1289 SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[OppCurve::kPointLast] }};
1252 int loopCount = 0; 1290 int loopCount = 0;
1253 double bestDistSq = DBL_MAX; 1291 double bestDistSq = DBL_MAX;
1254 if (!thisRayI.intersectRay(opp->fCurve, thisLine)) { 1292 if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
1255 return 0; 1293 return 0;
1256 } 1294 }
1257 if (!oppRayI.intersectRay(this->fCurve, oppLine)) { 1295 if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
1258 return 0; 1296 return 0;
1259 } 1297 }
1298 // if the ends of each line intersect the opposite curve, the lines are coin cident
1299 if (thisRayI.used() > 1) {
1300 int ptMatches = 0;
1301 for (int tIndex = 0; tIndex < thisRayI.used(); ++tIndex) {
1302 for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(thisLine.fPts); + +lIndex) {
1303 ptMatches += thisRayI.pt(tIndex).approximatelyEqual(thisLine.fPt s[lIndex]);
1304 }
1305 }
1306 if (ptMatches == 2) {
1307 return 2;
1308 }
1309 }
1310 if (oppRayI.used() > 1) {
1311 int ptMatches = 0;
1312 for (int oIndex = 0; oIndex < oppRayI.used(); ++oIndex) {
1313 for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(thisLine.fPts); + +lIndex) {
1314 ptMatches += oppRayI.pt(oIndex).approximatelyEqual(oppLine.fPts[ lIndex]);
1315 }
1316 }
1317 if (ptMatches == 2) {
1318 return 2;
1319 }
1320 }
1260 do { 1321 do {
1261 // pick the closest pair of points 1322 // pick the closest pair of points
1262 double closest = DBL_MAX; 1323 double closest = DBL_MAX;
1263 int closeIndex SK_INIT_TO_AVOID_WARNING; 1324 int closeIndex SK_INIT_TO_AVOID_WARNING;
1264 int oppCloseIndex SK_INIT_TO_AVOID_WARNING; 1325 int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
1265 for (int index = 0; index < oppRayI.used(); ++index) { 1326 for (int index = 0; index < oppRayI.used(); ++index) {
1266 if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) { 1327 if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
1267 continue; 1328 continue;
1268 } 1329 }
1269 for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) { 1330 for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
(...skipping 644 matching lines...) Expand 10 before | Expand all | Expand 10 after
1914 // SkASSERT(between(0, sect, 2)); 1975 // SkASSERT(between(0, sect, 2));
1915 if (!sect) { 1976 if (!sect) {
1916 return; 1977 return;
1917 } 1978 }
1918 if (sect == 2 && oppSect == 2) { 1979 if (sect == 2 && oppSect == 2) {
1919 (void) EndsEqual(sect1, sect2, intersections); 1980 (void) EndsEqual(sect1, sect2, intersections);
1920 return; 1981 return;
1921 } 1982 }
1922 span1->addBounded(span2, &sect1->fHeap); 1983 span1->addBounded(span2, &sect1->fHeap);
1923 span2->addBounded(span1, &sect2->fHeap); 1984 span2->addBounded(span1, &sect2->fHeap);
1985 const int kMaxCoinLoopCount = 8;
1986 int coinLoopCount = kMaxCoinLoopCount;
1987 double start1s SK_INIT_TO_AVOID_WARNING;
1988 double start1e SK_INIT_TO_AVOID_WARNING;
1924 do { 1989 do {
1925 // find the largest bounds 1990 // find the largest bounds
1926 SkTSpan<TCurve, OppCurve>* largest1 = sect1->boundsMax(); 1991 SkTSpan<TCurve, OppCurve>* largest1 = sect1->boundsMax();
1927 if (!largest1) { 1992 if (!largest1) {
1928 break; 1993 break;
1929 } 1994 }
1930 SkTSpan<OppCurve, TCurve>* largest2 = sect2->boundsMax(); 1995 SkTSpan<OppCurve, TCurve>* largest2 = sect2->boundsMax();
1931 // split it 1996 // split it
1932 if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsM ax 1997 if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsM ax
1933 || (!largest1->fCollapsed && largest2->fCollapsed)))) { 1998 || (!largest1->fCollapsed && largest2->fCollapsed)))) {
(...skipping 14 matching lines...) Expand all
1948 // trim parts that don't intersect the opposite 2013 // trim parts that don't intersect the opposite
1949 SkTSpan<OppCurve, TCurve>* half2 = sect2->addOne(); 2014 SkTSpan<OppCurve, TCurve>* half2 = sect2->addOne();
1950 if (!half2->split(largest2, &sect2->fHeap)) { 2015 if (!half2->split(largest2, &sect2->fHeap)) {
1951 break; 2016 break;
1952 } 2017 }
1953 sect2->trim(largest2, sect1); 2018 sect2->trim(largest2, sect1);
1954 sect2->trim(half2, sect1); 2019 sect2->trim(half2, sect1);
1955 } 2020 }
1956 sect1->validate(); 2021 sect1->validate();
1957 sect2->validate(); 2022 sect2->validate();
2023 #if DEBUG_T_SECT_LOOP_COUNT
2024 intersections->debugBumpLoopCount(SkIntersections::kIterations_DebugLoop );
2025 #endif
1958 // if there are 9 or more continuous spans on both sects, suspect coinci dence 2026 // if there are 9 or more continuous spans on both sects, suspect coinci dence
1959 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT 2027 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
1960 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { 2028 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
2029 if (coinLoopCount == kMaxCoinLoopCount) {
2030 start1s = sect1->fHead->fStartT;
2031 start1e = sect1->tail()->fEndT;
2032 }
1961 sect1->coincidentCheck(sect2); 2033 sect1->coincidentCheck(sect2);
1962 sect1->validate(); 2034 sect1->validate();
1963 sect2->validate(); 2035 sect2->validate();
2036 #if DEBUG_T_SECT_LOOP_COUNT
2037 intersections->debugBumpLoopCount(SkIntersections::kCoinCheck_DebugL oop);
2038 #endif
2039 if (!--coinLoopCount) {
2040 /* All known working cases resolve in two tries. Sadly, cubicCon icTests[0]
2041 gets stuck in a loop. It adds an extension to allow a coincid ent end
2042 perpendicular to track its intersection in the opposite curve . However,
2043 the bounding box of the extension does not intersect the orig inal curve,
2044 so the extension is discarded, only to be added again the nex t time around. */
2045 sect1->coincidentForce(sect2, start1s, start1e);
2046 sect1->validate();
2047 sect2->validate();
2048 }
1964 } 2049 }
1965 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT 2050 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
1966 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { 2051 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
1967 sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail()); 2052 sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
1968 sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail()); 2053 sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
1969 sect1->removeByPerpendicular(sect2); 2054 sect1->removeByPerpendicular(sect2);
1970 sect1->validate(); 2055 sect1->validate();
1971 sect2->validate(); 2056 sect2->validate();
2057 #if DEBUG_T_SECT_LOOP_COUNT
2058 intersections->debugBumpLoopCount(SkIntersections::kComputePerp_Debu gLoop);
2059 #endif
1972 if (sect1->collapsed() > TCurve::kMaxIntersections) { 2060 if (sect1->collapsed() > TCurve::kMaxIntersections) {
1973 break; 2061 break;
1974 } 2062 }
1975 } 2063 }
1976 #if DEBUG_T_SECT_DUMP 2064 #if DEBUG_T_SECT_DUMP
1977 sect1->dumpBoth(sect2); 2065 sect1->dumpBoth(sect2);
1978 #endif 2066 #endif
1979 if (!sect1->fHead || !sect2->fHead) { 2067 if (!sect1->fHead || !sect2->fHead) {
1980 break; 2068 break;
1981 } 2069 }
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
2086 } else if (intersections->isCoincident(index + 1)) { 2174 } else if (intersections->isCoincident(index + 1)) {
2087 intersections->removeOne(index + 1); 2175 intersections->removeOne(index + 1);
2088 --last; 2176 --last;
2089 } else { 2177 } else {
2090 intersections->setCoincident(index++); 2178 intersections->setCoincident(index++);
2091 } 2179 }
2092 intersections->setCoincident(index); 2180 intersections->setCoincident(index);
2093 } 2181 }
2094 SkASSERT(intersections->used() <= TCurve::kMaxIntersections); 2182 SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
2095 } 2183 }
OLDNEW
« no previous file with comments | « src/pathops/SkPathOpsSimplify.cpp ('k') | src/pathops/SkPathOpsTypes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698