| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 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 #include "SkIntersections.h" | 7 #include "SkIntersections.h" |
| 8 #include "SkOpAngle.h" | 8 #include "SkOpAngle.h" |
| 9 #include "SkOpSegment.h" |
| 9 #include "SkPathOpsCurve.h" | 10 #include "SkPathOpsCurve.h" |
| 10 #include "SkTSort.h" | 11 #include "SkTSort.h" |
| 11 | 12 |
| 12 #if DEBUG_SORT || DEBUG_SORT_SINGLE | |
| 13 #include "SkOpSegment.h" | |
| 14 #endif | |
| 15 | 13 |
| 16 // FIXME: this is bogus for quads and cubics | 14 // FIXME: this is bogus for quads and cubics |
| 17 // if the quads and cubics' line from end pt to ctrl pt are coincident, | 15 // if the quads and cubics' line from end pt to ctrl pt are coincident, |
| 18 // there's no obvious way to determine the curve ordering from the | 16 // there's no obvious way to determine the curve ordering from the |
| 19 // derivatives alone. In particular, if one quadratic's coincident tangent | 17 // derivatives alone. In particular, if one quadratic's coincident tangent |
| 20 // is longer than the other curve, the final control point can place the | 18 // is longer than the other curve, the final control point can place the |
| 21 // longer curve on either side of the shorter one. | 19 // longer curve on either side of the shorter one. |
| 22 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf | 20 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf |
| 23 // may provide some help, but nothing has been figured out yet. | 21 // may provide some help, but nothing has been figured out yet. |
| 24 | 22 |
| 25 /*( | 23 /*( |
| 26 for quads and cubics, set up a parameterized line (e.g. LineParameters ) | 24 for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
| 27 for points [0] to [1]. See if point [2] is on that line, or on one side | 25 for points [0] to [1]. See if point [2] is on that line, or on one side |
| 28 or the other. If it both quads' end points are on the same side, choose | 26 or the other. If it both quads' end points are on the same side, choose |
| 29 the shorter tangent. If the tangents are equal, choose the better second | 27 the shorter tangent. If the tangents are equal, choose the better second |
| 30 tangent angle | 28 tangent angle |
| 31 | 29 |
| 32 maybe I could set up LineParameters lazily | 30 maybe I could set up LineParameters lazily |
| 33 */ | 31 */ |
| 34 static int simple_compare(double x, double y, double rx, double ry) { | 32 static int simple_compare(double x, double y, double rx, double ry, double error
) { |
| 33 SkASSERT(error); |
| 35 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? | 34 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? |
| 36 return y < 0; | 35 return y < 0; |
| 37 } | 36 } |
| 38 if (y == 0 && ry == 0 && x * rx < 0) { | 37 if (y == 0 && ry == 0 && x * rx < 0) { |
| 39 return x < rx; | 38 return x < rx; |
| 40 } | 39 } |
| 41 double x_ry = x * ry; | 40 double x_ry = x * ry; |
| 42 double rx_y = rx * y; | 41 double rx_y = rx * y; |
| 43 double cmp = x_ry - rx_y; | 42 double cmp = (x_ry - rx_y) * error; |
| 44 if (!approximately_zero(cmp)) { | 43 if (!approximately_zero(cmp)) { |
| 45 return cmp < 0; | 44 return cmp < 0; |
| 46 } | 45 } |
| 47 if (approximately_zero(x_ry) && approximately_zero(rx_y) | 46 if (approximately_zero(x_ry) && approximately_zero(rx_y) |
| 48 && !approximately_zero_squared(cmp)) { | 47 && !approximately_zero_squared(cmp)) { |
| 49 return cmp < 0; | 48 return cmp < 0; |
| 50 } | 49 } |
| 51 return -1; | 50 return -1; |
| 52 } | 51 } |
| 53 | 52 |
| 53 static double gError[] = { |
| 54 0, 1, 1.0/2, 1.0/3 |
| 55 }; |
| 56 |
| 54 bool SkOpAngle::operator<(const SkOpAngle& rh) const { | 57 bool SkOpAngle::operator<(const SkOpAngle& rh) const { |
| 55 double x = dx(); | 58 double x = dx(); |
| 56 double y = dy(); | 59 double y = dy(); |
| 57 double rx = rh.dx(); | 60 double rx = rh.dx(); |
| 58 double ry = rh.dy(); | 61 double ry = rh.dy(); |
| 59 int simple = simple_compare(x, y, rx, ry); | 62 SkPath::Verb verb = fSegment->verb(); |
| 63 SkPath::Verb rVerb = rh.fSegment->verb(); |
| 64 int simple = simple_compare(x, y, rx, ry, gError[verb] * gError[rVerb]); |
| 60 if (simple >= 0) { | 65 if (simple >= 0) { |
| 61 return simple; | 66 return simple; |
| 62 } | 67 } |
| 63 // at this point, the initial tangent line is coincident | 68 // at this point, the initial tangent line is coincident |
| 64 // see if edges curl away from each other | 69 // see if edges curl away from each other |
| 65 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) | 70 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) |
| 66 || !approximately_zero(rh.fSide))) { | 71 || !approximately_zero(rh.fSide))) { |
| 67 // FIXME: running demo will trigger this assertion | 72 // FIXME: running demo will trigger this assertion |
| 68 // (don't know if commenting out will trigger further assertion or not) | 73 // (don't know if commenting out will trigger further assertion or not) |
| 69 // commenting it out allows demo to run in release, though | 74 // commenting it out allows demo to run in release, though |
| 70 return fSide < rh.fSide; | 75 return fSide < rh.fSide; |
| 71 } | 76 } |
| 72 // see if either curve can be lengthened and try the tangent compare again | 77 // see if either curve can be lengthened and try the tangent compare again |
| 73 if (/* cmp && */ (*fSpans)[fEnd].fOther != rh.fSegment // tangents not abso
lutely identical | 78 if (fSegment->other(fEnd) != rh.fSegment // tangents not absolutely identic
al |
| 74 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersect
ing | 79 && rh.fSegment->other(rh.fEnd) != fSegment) { // and not intersecti
ng |
| 75 SkOpAngle longer = *this; | 80 SkOpAngle longer = *this; |
| 76 SkOpAngle rhLonger = rh; | 81 SkOpAngle rhLonger = rh; |
| 77 if (longer.lengthen() | rhLonger.lengthen()) { | 82 if (longer.lengthen() | rhLonger.lengthen()) { |
| 78 return longer < rhLonger; | 83 return longer < rhLonger; |
| 79 } | 84 } |
| 80 } | 85 } |
| 81 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_z
ero(y)) | 86 if ((verb == SkPath::kLine_Verb && approximately_zero(x) && approximately_ze
ro(y)) |
| 82 || (rh.fVerb == SkPath::kLine_Verb | 87 || (rVerb == SkPath::kLine_Verb |
| 83 && approximately_zero(rx) && approximately_zero(ry))) { | 88 && approximately_zero(rx) && approximately_zero(ry))) { |
| 84 // See general unsortable comment below. This case can happen when | 89 // See general unsortable comment below. This case can happen when |
| 85 // one line has a non-zero change in t but no change in x and y. | 90 // one line has a non-zero change in t but no change in x and y. |
| 86 fUnsortable = true; | 91 fUnsortable = true; |
| 87 rh.fUnsortable = true; | 92 rh.fUnsortable = true; |
| 88 return this < &rh; // even with no solution, return a stable sort | 93 return this < &rh; // even with no solution, return a stable sort |
| 89 } | 94 } |
| 90 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny | 95 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) { |
| 91 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { | |
| 92 fUnsortable = true; | 96 fUnsortable = true; |
| 93 rh.fUnsortable = true; | 97 rh.fUnsortable = true; |
| 94 return this < &rh; // even with no solution, return a stable sort | 98 return this < &rh; // even with no solution, return a stable sort |
| 95 } | 99 } |
| 96 SkASSERT(fVerb >= SkPath::kQuad_Verb); | 100 SkASSERT(verb >= SkPath::kQuad_Verb); |
| 97 SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); | 101 SkASSERT(rVerb >= SkPath::kQuad_Verb); |
| 98 // FIXME: until I can think of something better, project a ray from the | 102 // FIXME: until I can think of something better, project a ray from the |
| 99 // end of the shorter tangent to midway between the end points | 103 // end of the shorter tangent to midway between the end points |
| 100 // through both curves and use the resulting angle to sort | 104 // through both curves and use the resulting angle to sort |
| 101 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive | 105 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive |
| 102 double len = fTangent1.normalSquared(); | 106 double len = fTangent1.normalSquared(); |
| 103 double rlen = rh.fTangent1.normalSquared(); | 107 double rlen = rh.fTangent1.normalSquared(); |
| 104 SkDLine ray; | 108 SkDLine ray; |
| 105 SkIntersections i, ri; | 109 SkIntersections i, ri; |
| 106 int roots, rroots; | 110 int roots, rroots; |
| 107 bool flip = false; | 111 bool flip = false; |
| 108 bool useThis; | 112 bool useThis; |
| 109 bool leftLessThanRight = fSide > 0; | 113 bool leftLessThanRight = fSide > 0; |
| 110 do { | 114 do { |
| 111 useThis = (len < rlen) ^ flip; | 115 useThis = (len < rlen) ^ flip; |
| 112 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; | 116 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; |
| 113 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; | 117 SkPath::Verb partVerb = useThis ? verb : rVerb; |
| 114 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? | 118 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? |
| 115 part[2] : part[1]; | 119 part[2] : part[1]; |
| 116 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; | 120 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; |
| 117 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; | 121 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; |
| 118 SkASSERT(ray[0] != ray[1]); | 122 SkASSERT(ray[0] != ray[1]); |
| 119 roots = (i.*CurveRay[fVerb])(fPts, ray); | 123 roots = (i.*CurveRay[verb])(fSegment->pts(), ray); |
| 120 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray); | 124 rroots = (ri.*CurveRay[rVerb])(rh.fSegment->pts(), ray); |
| 121 } while ((roots == 0 || rroots == 0) && (flip ^= true)); | 125 } while ((roots == 0 || rroots == 0) && (flip ^= true)); |
| 122 if (roots == 0 || rroots == 0) { | 126 if (roots == 0 || rroots == 0) { |
| 123 // FIXME: we don't have a solution in this case. The interim solution | 127 // FIXME: we don't have a solution in this case. The interim solution |
| 124 // is to mark the edges as unsortable, exclude them from this and | 128 // is to mark the edges as unsortable, exclude them from this and |
| 125 // future computations, and allow the returned path to be fragmented | 129 // future computations, and allow the returned path to be fragmented |
| 126 fUnsortable = true; | 130 fUnsortable = true; |
| 127 rh.fUnsortable = true; | 131 rh.fUnsortable = true; |
| 128 return this < &rh; // even with no solution, return a stable sort | 132 return this < &rh; // even with no solution, return a stable sort |
| 129 } | 133 } |
| 130 SkASSERT(fSide != 0 && rh.fSide != 0); | 134 SkASSERT(fSide != 0 && rh.fSide != 0); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 160 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); | 164 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); |
| 161 #endif | 165 #endif |
| 162 if (best > dist) { | 166 if (best > dist) { |
| 163 flip = true; | 167 flip = true; |
| 164 break; | 168 break; |
| 165 } | 169 } |
| 166 } | 170 } |
| 167 #if 0 | 171 #if 0 |
| 168 SkDVector lRay = lLoc - fCurvePart[0]; | 172 SkDVector lRay = lLoc - fCurvePart[0]; |
| 169 SkDVector rRay = rLoc - fCurvePart[0]; | 173 SkDVector rRay = rLoc - fCurvePart[0]; |
| 170 int rayDir = simple_compare(lRay.fX, lRay.fY, rRay.fX, rRay.fY); | 174 int rayDir = simple_compare(lRay.fX, lRay.fY, rRay.fX, rRay.fY, gError[verb]
* gError[rVerb]); |
| 171 SkASSERT(rayDir >= 0); | 175 SkASSERT(rayDir >= 0); |
| 172 if (rayDir < 0) { | 176 if (rayDir < 0) { |
| 173 fUnsortable = true; | 177 fUnsortable = true; |
| 174 rh.fUnsortable = true; | 178 rh.fUnsortable = true; |
| 175 return this < &rh; // even with no solution, return a stable sort | 179 return this < &rh; // even with no solution, return a stable sort |
| 176 } | 180 } |
| 177 #endif | 181 #endif |
| 178 if (flip) { | 182 if (flip) { |
| 179 leftLessThanRight = !leftLessThanRight; | 183 leftLessThanRight = !leftLessThanRight; |
| 180 // rayDir = !rayDir; | 184 // rayDir = !rayDir; |
| 181 } | 185 } |
| 182 #if 0 && (DEBUG_SORT || DEBUG_SORT_SINGLE) | 186 #if 0 && (DEBUG_SORT || DEBUG_SORT_SINGLE) |
| 183 SkDebugf("%d %c %d (fSide %c 0) loc={{%1.9g,%1.9g}, {%1.9g,%1.9g}} flip=%d r
ayDir=%d\n", | 187 SkDebugf("%d %c %d (fSide %c 0) loc={{%1.9g,%1.9g}, {%1.9g,%1.9g}} flip=%d r
ayDir=%d\n", |
| 184 fSegment->debugID(), "><"[leftLessThanRight], rh.fSegment->debug
ID(), | 188 fSegment->debugID(), "><"[leftLessThanRight], rh.fSegment->debug
ID(), |
| 185 "<>"[fSide > 0], lLoc.fX, lLoc.fY, rLoc.fX, rLoc.fY, flip, rayDi
r); | 189 "<>"[fSide > 0], lLoc.fX, lLoc.fY, rLoc.fX, rLoc.fY, flip, rayDi
r); |
| 186 #endif | 190 #endif |
| 187 // SkASSERT(leftLessThanRight == (bool) rayDir); | 191 // SkASSERT(leftLessThanRight == (bool) rayDir); |
| 188 return leftLessThanRight; | 192 return leftLessThanRight; |
| 189 } | 193 } |
| 190 | 194 |
| 195 bool SkOpAngle::isHorizontal() const { |
| 196 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb; |
| 197 } |
| 198 |
| 191 bool SkOpAngle::lengthen() { | 199 bool SkOpAngle::lengthen() { |
| 192 int newEnd = fEnd; | 200 int newEnd = fEnd; |
| 193 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { | 201 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) { |
| 194 fEnd = newEnd; | 202 fEnd = newEnd; |
| 195 setSpans(); | 203 setSpans(); |
| 196 return true; | 204 return true; |
| 197 } | 205 } |
| 198 return false; | 206 return false; |
| 199 } | 207 } |
| 200 | 208 |
| 201 bool SkOpAngle::reverseLengthen() { | 209 bool SkOpAngle::reverseLengthen() { |
| 202 if (fReversed) { | 210 if (fReversed) { |
| 203 return false; | 211 return false; |
| 204 } | 212 } |
| 205 int newEnd = fStart; | 213 int newEnd = fStart; |
| 206 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { | 214 if (fStart > fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) { |
| 207 fEnd = newEnd; | 215 fEnd = newEnd; |
| 208 fReversed = true; | 216 fReversed = true; |
| 209 setSpans(); | 217 setSpans(); |
| 210 return true; | 218 return true; |
| 211 } | 219 } |
| 212 return false; | 220 return false; |
| 213 } | 221 } |
| 214 | 222 |
| 215 void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* s
egment, | 223 void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* s
egment, |
| 216 int start, int end, const SkTDArray<SkOpSpan>& spans) { | 224 int start, int end, const SkTDArray<SkOpSpan>& spans) { |
| 217 fSegment = segment; | 225 fSegment = segment; |
| 218 fStart = start; | 226 fStart = start; |
| 219 fEnd = end; | 227 fEnd = end; |
| 220 fPts = orig; | 228 if (fSegment->isTiny(this) && (start == 0 || end == fSegment->count() - 1))
{ |
| 221 fVerb = verb; | 229 fUnsortable = true; |
| 222 fSpans = &spans; | 230 return; |
| 231 } |
| 223 fReversed = false; | 232 fReversed = false; |
| 224 fUnsortable = false; | 233 fUnsortable = false; |
| 225 setSpans(); | 234 setSpans(); |
| 226 } | 235 } |
| 227 | 236 |
| 228 | 237 |
| 229 void SkOpAngle::setSpans() { | 238 void SkOpAngle::setSpans() { |
| 230 double startT = (*fSpans)[fStart].fT; | 239 fSegment->subDivide(fStart, fEnd, &fCurvePart); |
| 231 double endT = (*fSpans)[fEnd].fT; | 240 switch (fSegment->verb()) { |
| 232 switch (fVerb) { | |
| 233 case SkPath::kLine_Verb: { | 241 case SkPath::kLine_Verb: { |
| 234 SkDLine l = SkDLine::SubDivide(fPts, startT, endT); | |
| 235 // OPTIMIZATION: for pure line compares, we never need fTangent1.c | 242 // OPTIMIZATION: for pure line compares, we never need fTangent1.c |
| 236 fTangent1.lineEndPoints(l); | 243 fTangent1.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart)); |
| 237 fSide = 0; | 244 fSide = 0; |
| 238 } break; | 245 } break; |
| 239 case SkPath::kQuad_Verb: { | 246 case SkPath::kQuad_Verb: { |
| 240 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); | 247 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); |
| 241 quad = SkDQuad::SubDivide(fPts, startT, endT); | |
| 242 fTangent1.quadEndPoints(quad, 0, 1); | 248 fTangent1.quadEndPoints(quad, 0, 1); |
| 243 if (dx() == 0 && dy() == 0) { | 249 if (dx() == 0 && dy() == 0) { |
| 244 fTangent1.quadEndPoints(quad); | 250 fTangent1.quadEndPoints(quad); |
| 245 } | 251 } |
| 246 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- c
ompare sign only | 252 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- c
ompare sign only |
| 247 } break; | 253 } break; |
| 248 case SkPath::kCubic_Verb: { | 254 case SkPath::kCubic_Verb: { |
| 249 // int nextC = 2; | 255 // int nextC = 2; |
| 250 fCurvePart = SkDCubic::SubDivide(fPts, startT, endT); | |
| 251 fTangent1.cubicEndPoints(fCurvePart, 0, 1); | 256 fTangent1.cubicEndPoints(fCurvePart, 0, 1); |
| 252 if (dx() == 0 && dy() == 0) { | 257 if (dx() == 0 && dy() == 0) { |
| 253 fTangent1.cubicEndPoints(fCurvePart, 0, 2); | 258 fTangent1.cubicEndPoints(fCurvePart, 0, 2); |
| 254 // nextC = 3; | 259 // nextC = 3; |
| 255 if (dx() == 0 && dy() == 0) { | 260 if (dx() == 0 && dy() == 0) { |
| 256 fTangent1.cubicEndPoints(fCurvePart, 0, 3); | 261 fTangent1.cubicEndPoints(fCurvePart, 0, 3); |
| 257 } | 262 } |
| 258 } | 263 } |
| 259 // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign on
ly | 264 // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign on
ly |
| 260 // if (nextC == 2 && approximately_zero(fSide)) { | 265 // if (nextC == 2 && approximately_zero(fSide)) { |
| 261 // fSide = -fTangent1.pointDistance(fCurvePart[3]); | 266 // fSide = -fTangent1.pointDistance(fCurvePart[3]); |
| 262 // } | 267 // } |
| 263 double testTs[4]; | 268 double testTs[4]; |
| 264 // OPTIMIZATION: keep inflections precomputed with cubic segment? | 269 // OPTIMIZATION: keep inflections precomputed with cubic segment? |
| 265 int testCount = SkDCubic::FindInflections(fPts, testTs); | 270 const SkPoint* pts = fSegment->pts(); |
| 271 int testCount = SkDCubic::FindInflections(pts, testTs); |
| 272 double startT = fSegment->t(fStart); |
| 273 double endT = fSegment->t(fEnd); |
| 266 double limitT = endT; | 274 double limitT = endT; |
| 267 int index; | 275 int index; |
| 268 for (index = 0; index < testCount; ++index) { | 276 for (index = 0; index < testCount; ++index) { |
| 269 if (!between(startT, testTs[index], limitT)) { | 277 if (!between(startT, testTs[index], limitT)) { |
| 270 testTs[index] = -1; | 278 testTs[index] = -1; |
| 271 } | 279 } |
| 272 } | 280 } |
| 273 testTs[testCount++] = startT; | 281 testTs[testCount++] = startT; |
| 274 testTs[testCount++] = endT; | 282 testTs[testCount++] = endT; |
| 275 SkTQSort<double>(testTs, &testTs[testCount - 1]); | 283 SkTQSort<double>(testTs, &testTs[testCount - 1]); |
| 276 double bestSide = 0; | 284 double bestSide = 0; |
| 277 int testCases = (testCount << 1) - 1; | 285 int testCases = (testCount << 1) - 1; |
| 278 index = 0; | 286 index = 0; |
| 279 while (testTs[index] < 0) { | 287 while (testTs[index] < 0) { |
| 280 ++index; | 288 ++index; |
| 281 } | 289 } |
| 282 index <<= 1; | 290 index <<= 1; |
| 283 for (; index < testCases; ++index) { | 291 for (; index < testCases; ++index) { |
| 284 int testIndex = index >> 1; | 292 int testIndex = index >> 1; |
| 285 double testT = testTs[testIndex]; | 293 double testT = testTs[testIndex]; |
| 286 if (index & 1) { | 294 if (index & 1) { |
| 287 testT = (testT + testTs[testIndex + 1]) / 2; | 295 testT = (testT + testTs[testIndex + 1]) / 2; |
| 288 } | 296 } |
| 289 // OPTIMIZE: could avoid call for t == startT, endT | 297 // OPTIMIZE: could avoid call for t == startT, endT |
| 290 SkDPoint pt = dcubic_xy_at_t(fPts, testT); | 298 SkDPoint pt = dcubic_xy_at_t(pts, testT); |
| 291 double testSide = fTangent1.pointDistance(pt); | 299 double testSide = fTangent1.pointDistance(pt); |
| 292 if (fabs(bestSide) < fabs(testSide)) { | 300 if (fabs(bestSide) < fabs(testSide)) { |
| 293 bestSide = testSide; | 301 bestSide = testSide; |
| 294 } | 302 } |
| 295 } | 303 } |
| 296 fSide = -bestSide; // compare sign only | 304 fSide = -bestSide; // compare sign only |
| 297 } break; | 305 } break; |
| 298 default: | 306 default: |
| 299 SkASSERT(0); | 307 SkASSERT(0); |
| 300 } | 308 } |
| 301 fUnsortable = dx() == 0 && dy() == 0; | 309 fUnsortable = dx() == 0 && dy() == 0; |
| 302 if (fUnsortable) { | 310 if (fUnsortable) { |
| 303 return; | 311 return; |
| 304 } | 312 } |
| 305 SkASSERT(fStart != fEnd); | 313 SkASSERT(fStart != fEnd); |
| 306 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 t
ype macro? | 314 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 t
ype macro? |
| 307 for (int index = fStart; index != fEnd; index += step) { | 315 for (int index = fStart; index != fEnd; index += step) { |
| 308 #if 1 | 316 #if 1 |
| 309 const SkOpSpan& thisSpan = (*fSpans)[index]; | 317 const SkOpSpan& thisSpan = fSegment->span(index); |
| 310 const SkOpSpan& nextSpan = (*fSpans)[index + step]; | 318 const SkOpSpan& nextSpan = fSegment->span(index + step); |
| 311 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { | 319 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { |
| 312 continue; | 320 continue; |
| 313 } | 321 } |
| 314 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortabl
eEnd; | 322 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortabl
eEnd; |
| 315 #if DEBUG_UNSORTABLE | 323 #if DEBUG_UNSORTABLE |
| 316 if (fUnsortable) { | 324 if (fUnsortable) { |
| 317 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT); | 325 SkPoint iPt = fSegment->xyAtT(index); |
| 318 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT); | 326 SkPoint ePt = fSegment->xyAtT(index + step); |
| 319 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __
FUNCTION__, | 327 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __
FUNCTION__, |
| 320 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | 328 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| 321 } | 329 } |
| 322 #endif | 330 #endif |
| 323 return; | 331 return; |
| 324 #else | 332 #else |
| 325 if ((*fSpans)[index].fUnsortableStart) { | 333 if ((*fSpans)[index].fUnsortableStart) { |
| 326 fUnsortable = true; | 334 fUnsortable = true; |
| 327 return; | 335 return; |
| 328 } | 336 } |
| 329 #endif | 337 #endif |
| 330 } | 338 } |
| 331 #if 1 | 339 #if 1 |
| 332 #if DEBUG_UNSORTABLE | 340 #if DEBUG_UNSORTABLE |
| 333 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT); | 341 SkPoint iPt = fSegment->xyAtT(fStart); |
| 334 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT); | 342 SkPoint ePt = fSegment->xyAtT(fEnd); |
| 335 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, | 343 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, |
| 336 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | 344 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| 337 #endif | 345 #endif |
| 338 fUnsortable = true; | 346 fUnsortable = true; |
| 339 #endif | 347 #endif |
| 340 } | 348 } |
| OLD | NEW |