blob: bd21d729b6ec1467c0c2b3fa2b9b445e2e1542fe [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
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.com07e97fc2013-07-08 17:17:02 +00007#include "SkGeometry.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00008#include "SkOpEdgeBuilder.h"
9#include "SkReduceOrder.h"
10
11void SkOpEdgeBuilder::init() {
caryclarkccec0f92015-03-24 07:28:17 -070012 fCurrentContour = fContoursHead;
caryclark@google.com07393ca2013-04-08 11:47:37 +000013 fOperand = false;
14 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
15 : kWinding_PathOpsMask;
caryclark@google.com66560ca2013-04-26 19:51:16 +000016 fUnparseable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000017 fSecondHalf = preFetch();
18}
19
20void SkOpEdgeBuilder::addOperand(const SkPath& path) {
21 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
caryclarkccec0f92015-03-24 07:28:17 -070022 fPathVerbs.pop();
caryclark@google.com07393ca2013-04-08 11:47:37 +000023 fPath = &path;
24 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
25 : kWinding_PathOpsMask;
26 preFetch();
27}
28
caryclarkccec0f92015-03-24 07:28:17 -070029int 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
39bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
40 fOperand = false;
41 if (fUnparseable || !walk(allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +000042 return false;
43 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000044 complete();
caryclarkccec0f92015-03-24 07:28:17 -070045 if (fCurrentContour && !fCurrentContour->count()) {
46 fContoursHead->remove(fCurrentContour);
caryclark@google.com07393ca2013-04-08 11:47:37 +000047 }
caryclark@google.com66560ca2013-04-26 19:51:16 +000048 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000049}
50
caryclark@google.com07e97fc2013-07-08 17:17:02 +000051void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000052 if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
caryclarkccec0f92015-03-24 07:28:17 -070053 *fPathVerbs.append() = SkPath::kLine_Verb;
54 *fPathPts.append() = curveStart;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000055 } else {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000056 fPathPts[fPathPts.count() - 1] = curveStart;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000057 }
caryclarkccec0f92015-03-24 07:28:17 -070058 *fPathVerbs.append() = SkPath::kClose_Verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000059}
60
caryclark65b427c2014-09-18 10:32:57 -070061// very tiny points cause numerical instability : don't allow them
62static 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.com07393ca2013-04-08 11:47:37 +000071int SkOpEdgeBuilder::preFetch() {
caryclark@google.com66560ca2013-04-26 19:51:16 +000072 if (!fPath->isFinite()) {
73 fUnparseable = true;
74 return 0;
75 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000076 SkAutoConicToQuads quadder;
77 const SkScalar quadderTol = SK_Scalar1 / 16;
caryclark@google.com6dc7df62013-04-25 11:51:54 +000078 SkPath::RawIter iter(*fPath);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000079 SkPoint curveStart;
80 SkPoint curve[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 SkPoint pts[4];
82 SkPath::Verb verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000083 bool lastCurve = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000084 do {
85 verb = iter.next(pts);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000086 switch (verb) {
87 case SkPath::kMove_Verb:
88 if (!fAllowOpenContours && lastCurve) {
89 closeContour(curve[0], curveStart);
90 }
caryclarkccec0f92015-03-24 07:28:17 -070091 *fPathVerbs.append() = verb;
caryclark65b427c2014-09-18 10:32:57 -070092 force_small_to_zero(&pts[0]);
caryclarkccec0f92015-03-24 07:28:17 -070093 *fPathPts.append() = pts[0];
caryclark@google.com07e97fc2013-07-08 17:17:02 +000094 curveStart = curve[0] = pts[0];
95 lastCurve = false;
96 continue;
97 case SkPath::kLine_Verb:
caryclark65b427c2014-09-18 10:32:57 -070098 force_small_to_zero(&pts[1]);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000099 if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
caryclarkccec0f92015-03-24 07:28:17 -0700100 uint8_t lastVerb = fPathVerbs.top();
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000101 if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
caryclarkccec0f92015-03-24 07:28:17 -0700102 fPathPts.top() = pts[1];
caryclark@google.com570863f2013-09-16 15:55:01 +0000103 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000104 continue; // skip degenerate points
105 }
106 break;
107 case SkPath::kQuad_Verb:
caryclark65b427c2014-09-18 10:32:57 -0700108 force_small_to_zero(&pts[1]);
109 force_small_to_zero(&pts[2]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000110 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) {
caryclarkccec0f92015-03-24 07:28:17 -0700122 *fPathVerbs.append() = SkPath::kQuad_Verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000123 }
caryclarkccec0f92015-03-24 07:28:17 -0700124 fPathPts.append(nQuads * 2, &quadPts[1]);
reed3f4e0452015-01-06 07:44:21 -0800125 curve[0] = pts[2];
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000126 lastCurve = true;
127 }
128 continue;
129 case SkPath::kCubic_Verb:
caryclark65b427c2014-09-18 10:32:57 -0700130 force_small_to_zero(&pts[1]);
131 force_small_to_zero(&pts[2]);
132 force_small_to_zero(&pts[3]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000133 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.com07393ca2013-04-08 11:47:37 +0000147 }
caryclarkccec0f92015-03-24 07:28:17 -0700148 *fPathVerbs.append() = verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000149 int ptCount = SkPathOpsVerbToPoints(verb);
caryclarkccec0f92015-03-24 07:28:17 -0700150 fPathPts.append(ptCount, &pts[1]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000151 curve[0] = pts[ptCount];
152 lastCurve = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153 } while (verb != SkPath::kDone_Verb);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000154 if (!fAllowOpenContours && lastCurve) {
155 closeContour(curve[0], curveStart);
156 }
caryclarkccec0f92015-03-24 07:28:17 -0700157 *fPathVerbs.append() = SkPath::kDone_Verb;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000158 return fPathVerbs.count() - 1;
159}
160
caryclark@google.com66560ca2013-04-26 19:51:16 +0000161bool SkOpEdgeBuilder::close() {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000162 complete();
163 return true;
164}
165
caryclarkccec0f92015-03-24 07:28:17 -0700166bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167 uint8_t* verbPtr = fPathVerbs.begin();
168 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclarkccec0f92015-03-24 07:28:17 -0700169 SkPoint* pointsPtr = fPathPts.begin() - 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000170 SkPath::Verb verb;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000171 while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
172 if (verbPtr == endOfFirstHalf) {
173 fOperand = true;
174 }
175 verbPtr++;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000176 switch (verb) {
177 case SkPath::kMove_Verb:
caryclarkccec0f92015-03-24 07:28:17 -0700178 if (fCurrentContour && fCurrentContour->count()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000179 if (fAllowOpenContours) {
180 complete();
181 } else if (!close()) {
182 return false;
183 }
184 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185 if (!fCurrentContour) {
caryclarkccec0f92015-03-24 07:28:17 -0700186 fCurrentContour = fContoursHead->appendContour(allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000187 }
caryclarkccec0f92015-03-24 07:28:17 -0700188 fCurrentContour->init(fGlobalState, fOperand,
189 fXorMask[fOperand] == kEvenOdd_PathOpsMask);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000190 pointsPtr += 1;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000191 continue;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000192 case SkPath::kLine_Verb:
caryclarkccec0f92015-03-24 07:28:17 -0700193 fCurrentContour->addLine(pointsPtr, fAllocator);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000194 break;
195 case SkPath::kQuad_Verb:
caryclarkccec0f92015-03-24 07:28:17 -0700196 fCurrentContour->addQuad(pointsPtr, fAllocator);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000197 break;
caryclarkccec0f92015-03-24 07:28:17 -0700198 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.com07393ca2013-04-08 11:47:37 +0000224 case SkPath::kClose_Verb:
225 SkASSERT(fCurrentContour);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000226 if (!close()) {
227 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000228 }
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000229 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 default:
231 SkDEBUGFAIL("bad verb");
caryclark@google.com66560ca2013-04-26 19:51:16 +0000232 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000234 SkASSERT(fCurrentContour);
caryclarkccec0f92015-03-24 07:28:17 -0700235 fCurrentContour->debugValidate();
236 pointsPtr += SkPathOpsVerbToPoints(verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000237 }
caryclarkccec0f92015-03-24 07:28:17 -0700238 if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000239 return false;
240 }
241 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000242}