caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2012 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 7 | #include "SkGeometry.h" |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 8 | #include "SkOpEdgeBuilder.h" |
| 9 | #include "SkReduceOrder.h" |
| 10 | |
| 11 | void SkOpEdgeBuilder::init() { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 12 | fCurrentContour = fContoursHead; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 13 | fOperand = false; |
| 14 | fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask |
| 15 | : kWinding_PathOpsMask; |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 16 | fUnparseable = false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 17 | fSecondHalf = preFetch(); |
| 18 | } |
| 19 | |
| 20 | void SkOpEdgeBuilder::addOperand(const SkPath& path) { |
| 21 | SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 22 | fPathVerbs.pop(); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 23 | fPath = &path; |
| 24 | fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask |
| 25 | : kWinding_PathOpsMask; |
| 26 | preFetch(); |
| 27 | } |
| 28 | |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 29 | int SkOpEdgeBuilder::count() const { |
| 30 | SkOpContour* contour = fContoursHead; |
| 31 | int count = 0; |
| 32 | while (contour) { |
| 33 | count += contour->count() > 0; |
| 34 | contour = contour->next(); |
| 35 | } |
| 36 | return count; |
| 37 | } |
| 38 | |
| 39 | bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) { |
| 40 | fOperand = false; |
| 41 | if (fUnparseable || !walk(allocator)) { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 42 | return false; |
| 43 | } |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 44 | complete(); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 45 | if (fCurrentContour && !fCurrentContour->count()) { |
| 46 | fContoursHead->remove(fCurrentContour); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 47 | } |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 48 | return true; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 49 | } |
| 50 | |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 51 | void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) { |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 52 | if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 53 | *fPathVerbs.append() = SkPath::kLine_Verb; |
| 54 | *fPathPts.append() = curveStart; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 55 | } else { |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 56 | fPathPts[fPathPts.count() - 1] = curveStart; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 57 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 58 | *fPathVerbs.append() = SkPath::kClose_Verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 59 | } |
| 60 | |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 61 | // very tiny points cause numerical instability : don't allow them |
| 62 | static void force_small_to_zero(SkPoint* pt) { |
| 63 | if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) { |
| 64 | pt->fX = 0; |
| 65 | } |
| 66 | if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) { |
| 67 | pt->fY = 0; |
| 68 | } |
| 69 | } |
| 70 | |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 71 | int SkOpEdgeBuilder::preFetch() { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 72 | if (!fPath->isFinite()) { |
| 73 | fUnparseable = true; |
| 74 | return 0; |
| 75 | } |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 76 | SkPath::RawIter iter(*fPath); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 77 | SkPoint curveStart; |
| 78 | SkPoint curve[4]; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 79 | SkPoint pts[4]; |
| 80 | SkPath::Verb verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 81 | bool lastCurve = false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 82 | do { |
| 83 | verb = iter.next(pts); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 84 | switch (verb) { |
| 85 | case SkPath::kMove_Verb: |
| 86 | if (!fAllowOpenContours && lastCurve) { |
| 87 | closeContour(curve[0], curveStart); |
| 88 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 89 | *fPathVerbs.append() = verb; |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 90 | force_small_to_zero(&pts[0]); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 91 | *fPathPts.append() = pts[0]; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 92 | curveStart = curve[0] = pts[0]; |
| 93 | lastCurve = false; |
| 94 | continue; |
| 95 | case SkPath::kLine_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 96 | force_small_to_zero(&pts[1]); |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 97 | if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 98 | uint8_t lastVerb = fPathVerbs.top(); |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 99 | if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 100 | fPathPts.top() = pts[1]; |
caryclark@google.com | 570863f | 2013-09-16 15:55:01 +0000 | [diff] [blame] | 101 | } |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 102 | continue; // skip degenerate points |
| 103 | } |
| 104 | break; |
| 105 | case SkPath::kQuad_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 106 | force_small_to_zero(&pts[1]); |
| 107 | force_small_to_zero(&pts[2]); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 108 | curve[1] = pts[1]; |
| 109 | curve[2] = pts[2]; |
| 110 | verb = SkReduceOrder::Quad(curve, pts); |
| 111 | if (verb == SkPath::kMove_Verb) { |
| 112 | continue; // skip degenerate points |
| 113 | } |
| 114 | break; |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 115 | case SkPath::kConic_Verb: |
| 116 | force_small_to_zero(&pts[1]); |
| 117 | force_small_to_zero(&pts[2]); |
| 118 | curve[1] = pts[1]; |
| 119 | curve[2] = pts[2]; |
| 120 | verb = SkReduceOrder::Conic(curve, iter.conicWeight(), pts); |
| 121 | if (verb == SkPath::kMove_Verb) { |
| 122 | continue; // skip degenerate points |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 123 | } |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 124 | break; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 125 | case SkPath::kCubic_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 126 | force_small_to_zero(&pts[1]); |
| 127 | force_small_to_zero(&pts[2]); |
| 128 | force_small_to_zero(&pts[3]); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 129 | curve[1] = pts[1]; |
| 130 | curve[2] = pts[2]; |
| 131 | curve[3] = pts[3]; |
| 132 | verb = SkReduceOrder::Cubic(curve, pts); |
| 133 | if (verb == SkPath::kMove_Verb) { |
| 134 | continue; // skip degenerate points |
| 135 | } |
| 136 | break; |
| 137 | case SkPath::kClose_Verb: |
| 138 | closeContour(curve[0], curveStart); |
| 139 | lastCurve = false; |
| 140 | continue; |
| 141 | case SkPath::kDone_Verb: |
| 142 | continue; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 143 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 144 | *fPathVerbs.append() = verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 145 | int ptCount = SkPathOpsVerbToPoints(verb); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 146 | fPathPts.append(ptCount, &pts[1]); |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 147 | if (verb == SkPath::kConic_Verb) { |
| 148 | *fWeights.append() = iter.conicWeight(); |
| 149 | } |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 150 | curve[0] = pts[ptCount]; |
| 151 | lastCurve = true; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 152 | } while (verb != SkPath::kDone_Verb); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 153 | if (!fAllowOpenContours && lastCurve) { |
| 154 | closeContour(curve[0], curveStart); |
| 155 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 156 | *fPathVerbs.append() = SkPath::kDone_Verb; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 157 | return fPathVerbs.count() - 1; |
| 158 | } |
| 159 | |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 160 | bool SkOpEdgeBuilder::close() { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 161 | complete(); |
| 162 | return true; |
| 163 | } |
| 164 | |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 165 | bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) { |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 166 | uint8_t* verbPtr = fPathVerbs.begin(); |
| 167 | uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 168 | SkPoint* pointsPtr = fPathPts.begin() - 1; |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 169 | SkScalar* weightPtr = fWeights.begin(); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 170 | SkPath::Verb verb; |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 171 | while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { |
| 172 | if (verbPtr == endOfFirstHalf) { |
| 173 | fOperand = true; |
| 174 | } |
| 175 | verbPtr++; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 176 | switch (verb) { |
| 177 | case SkPath::kMove_Verb: |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 178 | if (fCurrentContour && fCurrentContour->count()) { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 179 | if (fAllowOpenContours) { |
| 180 | complete(); |
| 181 | } else if (!close()) { |
| 182 | return false; |
| 183 | } |
| 184 | } |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 185 | if (!fCurrentContour) { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 186 | fCurrentContour = fContoursHead->appendContour(allocator); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 187 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 188 | fCurrentContour->init(fGlobalState, fOperand, |
| 189 | fXorMask[fOperand] == kEvenOdd_PathOpsMask); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 190 | pointsPtr += 1; |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 191 | continue; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 192 | case SkPath::kLine_Verb: |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 193 | fCurrentContour->addLine(pointsPtr, fAllocator); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 194 | break; |
| 195 | case SkPath::kQuad_Verb: |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 196 | fCurrentContour->addQuad(pointsPtr, fAllocator); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 197 | break; |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 198 | case SkPath::kConic_Verb: |
| 199 | fCurrentContour->addConic(pointsPtr, *weightPtr++, fAllocator); |
| 200 | break; |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 201 | case SkPath::kCubic_Verb: { |
| 202 | // split self-intersecting cubics in two before proceeding |
| 203 | // if the cubic is convex, it doesn't self intersect. |
| 204 | SkScalar loopT; |
caryclark | 6ff734b | 2015-09-04 05:00:15 -0700 | [diff] [blame] | 205 | if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) { |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 206 | SkPoint cubicPair[7]; |
| 207 | SkChopCubicAt(pointsPtr, cubicPair, loopT); |
caryclark | 1049f12 | 2015-04-20 08:31:59 -0700 | [diff] [blame] | 208 | if (!SkScalarsAreFinite(&cubicPair[0].fX, SK_ARRAY_COUNT(cubicPair) * 2)) { |
| 209 | return false; |
| 210 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 211 | SkPoint cStorage[2][4]; |
| 212 | SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]); |
| 213 | SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]); |
| 214 | if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) { |
| 215 | SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0]; |
| 216 | SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1]; |
| 217 | for (int index = 0; index < SkPathOpsVerbToPoints(v1); ++index) { |
| 218 | force_small_to_zero(&curve1[index]); |
| 219 | } |
| 220 | for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) { |
| 221 | force_small_to_zero(&curve2[index]); |
| 222 | } |
caryclark | 6ff734b | 2015-09-04 05:00:15 -0700 | [diff] [blame] | 223 | fCurrentContour->addCurve(v1, curve1, fAllocator); |
| 224 | fCurrentContour->addCurve(v2, curve2, fAllocator); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 225 | } else { |
| 226 | fCurrentContour->addCubic(pointsPtr, fAllocator); |
| 227 | } |
| 228 | } else { |
| 229 | fCurrentContour->addCubic(pointsPtr, fAllocator); |
| 230 | } |
| 231 | } break; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 232 | case SkPath::kClose_Verb: |
| 233 | SkASSERT(fCurrentContour); |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 234 | if (!close()) { |
| 235 | return false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 236 | } |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 237 | continue; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 238 | default: |
| 239 | SkDEBUGFAIL("bad verb"); |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 240 | return false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 241 | } |
reed | 0dc4dd6 | 2015-03-24 13:55:33 -0700 | [diff] [blame] | 242 | SkASSERT(fCurrentContour); |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 243 | fCurrentContour->debugValidate(); |
| 244 | pointsPtr += SkPathOpsVerbToPoints(verb); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 245 | } |
caryclark | 5435929 | 2015-03-26 07:52:43 -0700 | [diff] [blame] | 246 | if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 247 | return false; |
| 248 | } |
| 249 | return true; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 250 | } |