blob: 803a5f4739e238a5426cc7c4fec513cca1faf879 [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() {
12 fCurrentContour = NULL;
13 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);
caryclark@google.comd892bd82013-06-17 14:10:36 +000022 fPathVerbs.pop_back();
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
caryclark@google.com66560ca2013-04-26 19:51:16 +000029bool SkOpEdgeBuilder::finish() {
30 if (fUnparseable || !walk()) {
31 return false;
32 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000033 complete();
34 if (fCurrentContour && !fCurrentContour->segments().count()) {
35 fContours.pop_back();
36 }
caryclark@google.com66560ca2013-04-26 19:51:16 +000037 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000038}
39
caryclark@google.com07e97fc2013-07-08 17:17:02 +000040void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000041 if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +000042 fPathVerbs.push_back(SkPath::kLine_Verb);
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +000043 fPathPts.push_back_n(1, &curveStart);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000044 } else {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000045 fPathPts[fPathPts.count() - 1] = curveStart;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000046 }
47 fPathVerbs.push_back(SkPath::kClose_Verb);
48}
49
caryclark65b427c2014-09-18 10:32:57 -070050// very tiny points cause numerical instability : don't allow them
51static void force_small_to_zero(SkPoint* pt) {
52 if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) {
53 pt->fX = 0;
54 }
55 if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) {
56 pt->fY = 0;
57 }
58}
59
60
caryclark@google.com07393ca2013-04-08 11:47:37 +000061int SkOpEdgeBuilder::preFetch() {
caryclark@google.com66560ca2013-04-26 19:51:16 +000062 if (!fPath->isFinite()) {
63 fUnparseable = true;
64 return 0;
65 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000066 SkAutoConicToQuads quadder;
67 const SkScalar quadderTol = SK_Scalar1 / 16;
caryclark@google.com6dc7df62013-04-25 11:51:54 +000068 SkPath::RawIter iter(*fPath);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000069 SkPoint curveStart;
70 SkPoint curve[4];
caryclark@google.com07393ca2013-04-08 11:47:37 +000071 SkPoint pts[4];
72 SkPath::Verb verb;
caryclark@google.com07e97fc2013-07-08 17:17:02 +000073 bool lastCurve = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 do {
75 verb = iter.next(pts);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000076 switch (verb) {
77 case SkPath::kMove_Verb:
78 if (!fAllowOpenContours && lastCurve) {
79 closeContour(curve[0], curveStart);
80 }
81 fPathVerbs.push_back(verb);
caryclark65b427c2014-09-18 10:32:57 -070082 force_small_to_zero(&pts[0]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000083 fPathPts.push_back(pts[0]);
84 curveStart = curve[0] = pts[0];
85 lastCurve = false;
86 continue;
87 case SkPath::kLine_Verb:
caryclark65b427c2014-09-18 10:32:57 -070088 force_small_to_zero(&pts[1]);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000089 if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
90 uint8_t lastVerb = fPathVerbs.back();
91 if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
caryclark@google.com570863f2013-09-16 15:55:01 +000092 fPathPts.back() = pts[1];
93 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000094 continue; // skip degenerate points
95 }
96 break;
97 case SkPath::kQuad_Verb:
caryclark65b427c2014-09-18 10:32:57 -070098 force_small_to_zero(&pts[1]);
99 force_small_to_zero(&pts[2]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000100 curve[1] = pts[1];
101 curve[2] = pts[2];
102 verb = SkReduceOrder::Quad(curve, pts);
103 if (verb == SkPath::kMove_Verb) {
104 continue; // skip degenerate points
105 }
106 break;
107 case SkPath::kConic_Verb: {
108 const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(),
109 quadderTol);
110 const int nQuads = quadder.countQuads();
111 for (int i = 0; i < nQuads; ++i) {
112 fPathVerbs.push_back(SkPath::kQuad_Verb);
113 }
reed3f4e0452015-01-06 07:44:21 -0800114 fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
115 curve[0] = pts[2];
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000116 lastCurve = true;
117 }
118 continue;
119 case SkPath::kCubic_Verb:
caryclark65b427c2014-09-18 10:32:57 -0700120 force_small_to_zero(&pts[1]);
121 force_small_to_zero(&pts[2]);
122 force_small_to_zero(&pts[3]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000123 curve[1] = pts[1];
124 curve[2] = pts[2];
125 curve[3] = pts[3];
126 verb = SkReduceOrder::Cubic(curve, pts);
127 if (verb == SkPath::kMove_Verb) {
128 continue; // skip degenerate points
129 }
130 break;
131 case SkPath::kClose_Verb:
132 closeContour(curve[0], curveStart);
133 lastCurve = false;
134 continue;
135 case SkPath::kDone_Verb:
136 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000138 fPathVerbs.push_back(verb);
139 int ptCount = SkPathOpsVerbToPoints(verb);
140 fPathPts.push_back_n(ptCount, &pts[1]);
141 curve[0] = pts[ptCount];
142 lastCurve = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000143 } while (verb != SkPath::kDone_Verb);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000144 if (!fAllowOpenContours && lastCurve) {
145 closeContour(curve[0], curveStart);
146 }
147 fPathVerbs.push_back(SkPath::kDone_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000148 return fPathVerbs.count() - 1;
149}
150
caryclark@google.com66560ca2013-04-26 19:51:16 +0000151bool SkOpEdgeBuilder::close() {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000152 complete();
153 return true;
154}
155
156bool SkOpEdgeBuilder::walk() {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 uint8_t* verbPtr = fPathVerbs.begin();
158 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000159 const SkPoint* pointsPtr = fPathPts.begin() - 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000160 SkPath::Verb verb;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000161 while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
162 if (verbPtr == endOfFirstHalf) {
163 fOperand = true;
164 }
165 verbPtr++;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000166 switch (verb) {
167 case SkPath::kMove_Verb:
caryclark@google.com66560ca2013-04-26 19:51:16 +0000168 if (fCurrentContour) {
169 if (fAllowOpenContours) {
170 complete();
171 } else if (!close()) {
172 return false;
173 }
174 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000175 if (!fCurrentContour) {
176 fCurrentContour = fContours.push_back_n(1);
177 fCurrentContour->setOperand(fOperand);
178 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000180 pointsPtr += 1;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000181 continue;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000182 case SkPath::kLine_Verb:
183 fCurrentContour->addLine(pointsPtr);
184 break;
185 case SkPath::kQuad_Verb:
186 fCurrentContour->addQuad(pointsPtr);
187 break;
188 case SkPath::kCubic_Verb:
189 fCurrentContour->addCubic(pointsPtr);
190 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191 case SkPath::kClose_Verb:
192 SkASSERT(fCurrentContour);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000193 if (!close()) {
194 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195 }
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000196 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000197 default:
198 SkDEBUGFAIL("bad verb");
caryclark@google.com66560ca2013-04-26 19:51:16 +0000199 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 }
reed@google.com277c3f82013-05-31 15:17:50 +0000201 pointsPtr += SkPathOpsVerbToPoints(verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000202 SkASSERT(fCurrentContour);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000204 if (fCurrentContour && !fAllowOpenContours && !close()) {
205 return false;
206 }
207 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208}