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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 76 | SkAutoConicToQuads quadder; |
| 77 | const SkScalar quadderTol = SK_Scalar1 / 16; |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 78 | SkPath::RawIter iter(*fPath); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 79 | SkPoint curveStart; |
| 80 | SkPoint curve[4]; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 81 | SkPoint pts[4]; |
| 82 | SkPath::Verb verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 83 | bool lastCurve = false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 84 | do { |
| 85 | verb = iter.next(pts); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 86 | switch (verb) { |
| 87 | case SkPath::kMove_Verb: |
| 88 | if (!fAllowOpenContours && lastCurve) { |
| 89 | closeContour(curve[0], curveStart); |
| 90 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 91 | *fPathVerbs.append() = verb; |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 92 | force_small_to_zero(&pts[0]); |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 93 | *fPathPts.append() = pts[0]; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 94 | curveStart = curve[0] = pts[0]; |
| 95 | lastCurve = false; |
| 96 | continue; |
| 97 | case SkPath::kLine_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 98 | force_small_to_zero(&pts[1]); |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 99 | if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) { |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 100 | uint8_t lastVerb = fPathVerbs.top(); |
caryclark@google.com | a2bbc6e | 2013-11-01 17:36:03 +0000 | [diff] [blame] | 101 | if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 102 | fPathPts.top() = pts[1]; |
caryclark@google.com | 570863f | 2013-09-16 15:55:01 +0000 | [diff] [blame] | 103 | } |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 104 | continue; // skip degenerate points |
| 105 | } |
| 106 | break; |
| 107 | case SkPath::kQuad_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 108 | force_small_to_zero(&pts[1]); |
| 109 | force_small_to_zero(&pts[2]); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 110 | curve[1] = pts[1]; |
| 111 | curve[2] = pts[2]; |
| 112 | verb = SkReduceOrder::Quad(curve, pts); |
| 113 | if (verb == SkPath::kMove_Verb) { |
| 114 | continue; // skip degenerate points |
| 115 | } |
| 116 | break; |
| 117 | case SkPath::kConic_Verb: { |
| 118 | const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), |
| 119 | quadderTol); |
| 120 | const int nQuads = quadder.countQuads(); |
| 121 | for (int i = 0; i < nQuads; ++i) { |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 122 | *fPathVerbs.append() = SkPath::kQuad_Verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 123 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 124 | fPathPts.append(nQuads * 2, &quadPts[1]); |
reed | 3f4e045 | 2015-01-06 07:44:21 -0800 | [diff] [blame] | 125 | curve[0] = pts[2]; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 126 | lastCurve = true; |
| 127 | } |
| 128 | continue; |
| 129 | case SkPath::kCubic_Verb: |
caryclark | 65b427c | 2014-09-18 10:32:57 -0700 | [diff] [blame] | 130 | force_small_to_zero(&pts[1]); |
| 131 | force_small_to_zero(&pts[2]); |
| 132 | force_small_to_zero(&pts[3]); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 133 | curve[1] = pts[1]; |
| 134 | curve[2] = pts[2]; |
| 135 | curve[3] = pts[3]; |
| 136 | verb = SkReduceOrder::Cubic(curve, pts); |
| 137 | if (verb == SkPath::kMove_Verb) { |
| 138 | continue; // skip degenerate points |
| 139 | } |
| 140 | break; |
| 141 | case SkPath::kClose_Verb: |
| 142 | closeContour(curve[0], curveStart); |
| 143 | lastCurve = false; |
| 144 | continue; |
| 145 | case SkPath::kDone_Verb: |
| 146 | continue; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 147 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 148 | *fPathVerbs.append() = verb; |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 149 | int ptCount = SkPathOpsVerbToPoints(verb); |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 150 | fPathPts.append(ptCount, &pts[1]); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 151 | curve[0] = pts[ptCount]; |
| 152 | lastCurve = true; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 153 | } while (verb != SkPath::kDone_Verb); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 154 | if (!fAllowOpenContours && lastCurve) { |
| 155 | closeContour(curve[0], curveStart); |
| 156 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 157 | *fPathVerbs.append() = SkPath::kDone_Verb; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 158 | return fPathVerbs.count() - 1; |
| 159 | } |
| 160 | |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 161 | bool SkOpEdgeBuilder::close() { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 162 | complete(); |
| 163 | return true; |
| 164 | } |
| 165 | |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 166 | bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) { |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 167 | uint8_t* verbPtr = fPathVerbs.begin(); |
| 168 | uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 169 | SkPoint* pointsPtr = fPathPts.begin() - 1; |
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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 186 | fCurrentContour = fContoursHead->appendContour(allocator); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 187 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -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 | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 196 | fCurrentContour->addQuad(pointsPtr, fAllocator); |
caryclark@google.com | 07e97fc | 2013-07-08 17:17:02 +0000 | [diff] [blame] | 197 | break; |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 198 | case SkPath::kCubic_Verb: { |
| 199 | // split self-intersecting cubics in two before proceeding |
| 200 | // if the cubic is convex, it doesn't self intersect. |
| 201 | SkScalar loopT; |
| 202 | if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) { |
| 203 | SkPoint cubicPair[7]; |
| 204 | SkChopCubicAt(pointsPtr, cubicPair, loopT); |
| 205 | SkPoint cStorage[2][4]; |
| 206 | SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]); |
| 207 | SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]); |
| 208 | if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) { |
| 209 | SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0]; |
| 210 | SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1]; |
| 211 | for (size_t index = 0; index < SK_ARRAY_COUNT(curve1); ++index) { |
| 212 | force_small_to_zero(&curve1[index]); |
| 213 | force_small_to_zero(&curve2[index]); |
| 214 | } |
| 215 | fCurrentContour->addCurve(v1, curve1, fAllocator); |
| 216 | fCurrentContour->addCurve(v2, curve2, fAllocator); |
| 217 | } else { |
| 218 | fCurrentContour->addCubic(pointsPtr, fAllocator); |
| 219 | } |
| 220 | } else { |
| 221 | fCurrentContour->addCubic(pointsPtr, fAllocator); |
| 222 | } |
| 223 | } break; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 224 | case SkPath::kClose_Verb: |
| 225 | SkASSERT(fCurrentContour); |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 226 | if (!close()) { |
| 227 | return false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 228 | } |
caryclark@google.com | 6dc7df6 | 2013-04-25 11:51:54 +0000 | [diff] [blame] | 229 | continue; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 230 | default: |
| 231 | SkDEBUGFAIL("bad verb"); |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 232 | return false; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 233 | } |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 234 | SkASSERT(fCurrentContour); |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 235 | fCurrentContour->debugValidate(); |
| 236 | pointsPtr += SkPathOpsVerbToPoints(verb); |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 237 | } |
caryclark | ccec0f9 | 2015-03-24 07:28:17 -0700 | [diff] [blame^] | 238 | if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) { |
caryclark@google.com | 66560ca | 2013-04-26 19:51:16 +0000 | [diff] [blame] | 239 | return false; |
| 240 | } |
| 241 | return true; |
caryclark@google.com | 07393ca | 2013-04-08 11:47:37 +0000 | [diff] [blame] | 242 | } |