1 /*
2 * Copyright 2011 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 */
7
8 #include "SkAnalyticEdge.h"
9 #include "SkEdge.h"
10 #include "SkEdgeBuilder.h"
11 #include "SkEdgeClipper.h"
12 #include "SkGeometry.h"
13 #include "SkLineClipper.h"
14 #include "SkPath.h"
15 #include "SkPathPriv.h"
16 #include "SkSafeMath.h"
17 #include "SkTo.h"
18
combineVertical(const SkEdge * edge,SkEdge * last)19 SkEdgeBuilder::Combine SkBasicEdgeBuilder::combineVertical(const SkEdge* edge, SkEdge* last) {
20 if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
21 return kNo_Combine;
22 }
23 if (edge->fWinding == last->fWinding) {
24 if (edge->fLastY + 1 == last->fFirstY) {
25 last->fFirstY = edge->fFirstY;
26 return kPartial_Combine;
27 }
28 if (edge->fFirstY == last->fLastY + 1) {
29 last->fLastY = edge->fLastY;
30 return kPartial_Combine;
31 }
32 return kNo_Combine;
33 }
34 if (edge->fFirstY == last->fFirstY) {
35 if (edge->fLastY == last->fLastY) {
36 return kTotal_Combine;
37 }
38 if (edge->fLastY < last->fLastY) {
39 last->fFirstY = edge->fLastY + 1;
40 return kPartial_Combine;
41 }
42 last->fFirstY = last->fLastY + 1;
43 last->fLastY = edge->fLastY;
44 last->fWinding = edge->fWinding;
45 return kPartial_Combine;
46 }
47 if (edge->fLastY == last->fLastY) {
48 if (edge->fFirstY > last->fFirstY) {
49 last->fLastY = edge->fFirstY - 1;
50 return kPartial_Combine;
51 }
52 last->fLastY = last->fFirstY - 1;
53 last->fFirstY = edge->fFirstY;
54 last->fWinding = edge->fWinding;
55 return kPartial_Combine;
56 }
57 return kNo_Combine;
58 }
59
combineVertical(const SkAnalyticEdge * edge,SkAnalyticEdge * last)60 SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::combineVertical(const SkAnalyticEdge* edge,
61 SkAnalyticEdge* last) {
62 auto approximately_equal = [](SkFixed a, SkFixed b) {
63 return SkAbs32(a - b) < 0x100;
64 };
65
66 if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
67 return kNo_Combine;
68 }
69 if (edge->fWinding == last->fWinding) {
70 if (edge->fLowerY == last->fUpperY) {
71 last->fUpperY = edge->fUpperY;
72 last->fY = last->fUpperY;
73 return kPartial_Combine;
74 }
75 if (approximately_equal(edge->fUpperY, last->fLowerY)) {
76 last->fLowerY = edge->fLowerY;
77 return kPartial_Combine;
78 }
79 return kNo_Combine;
80 }
81 if (approximately_equal(edge->fUpperY, last->fUpperY)) {
82 if (approximately_equal(edge->fLowerY, last->fLowerY)) {
83 return kTotal_Combine;
84 }
85 if (edge->fLowerY < last->fLowerY) {
86 last->fUpperY = edge->fLowerY;
87 last->fY = last->fUpperY;
88 return kPartial_Combine;
89 }
90 last->fUpperY = last->fLowerY;
91 last->fY = last->fUpperY;
92 last->fLowerY = edge->fLowerY;
93 last->fWinding = edge->fWinding;
94 return kPartial_Combine;
95 }
96 if (approximately_equal(edge->fLowerY, last->fLowerY)) {
97 if (edge->fUpperY > last->fUpperY) {
98 last->fLowerY = edge->fUpperY;
99 return kPartial_Combine;
100 }
101 last->fLowerY = last->fUpperY;
102 last->fUpperY = edge->fUpperY;
103 last->fY = last->fUpperY;
104 last->fWinding = edge->fWinding;
105 return kPartial_Combine;
106 }
107 return kNo_Combine;
108 }
109
110 template <typename Edge>
is_vertical(const Edge * edge)111 static bool is_vertical(const Edge* edge) {
112 return edge->fDX == 0
113 && edge->fCurveCount == 0;
114 }
115
116 // TODO: we can deallocate the edge if edge->setFoo() fails
117 // or when we don't use it (kPartial_Combine or kTotal_Combine).
118
addLine(const SkPoint pts[])119 void SkBasicEdgeBuilder::addLine(const SkPoint pts[]) {
120 SkEdge* edge = fAlloc.make<SkEdge>();
121 if (edge->setLine(pts[0], pts[1], fClipShift)) {
122 Combine combine = is_vertical(edge) && !fList.empty()
123 ? this->combineVertical(edge, (SkEdge*)fList.top())
124 : kNo_Combine;
125
126 switch (combine) {
127 case kTotal_Combine: fList.pop(); break;
128 case kPartial_Combine: break;
129 case kNo_Combine: fList.push_back(edge); break;
130 }
131 }
132 }
addLine(const SkPoint pts[])133 void SkAnalyticEdgeBuilder::addLine(const SkPoint pts[]) {
134 SkAnalyticEdge* edge = fAlloc.make<SkAnalyticEdge>();
135 if (edge->setLine(pts[0], pts[1])) {
136
137 Combine combine = is_vertical(edge) && !fList.empty()
138 ? this->combineVertical(edge, (SkAnalyticEdge*)fList.top())
139 : kNo_Combine;
140
141 switch (combine) {
142 case kTotal_Combine: fList.pop(); break;
143 case kPartial_Combine: break;
144 case kNo_Combine: fList.push_back(edge); break;
145 }
146 }
147 }
addLine(const SkPoint pts[])148 void SkBezierEdgeBuilder::addLine(const SkPoint pts[]) {
149 SkLine* line = fAlloc.make<SkLine>();
150 if (line->set(pts)) {
151 fList.push_back(line);
152 }
153 }
154
addQuad(const SkPoint pts[])155 void SkBasicEdgeBuilder::addQuad(const SkPoint pts[]) {
156 SkQuadraticEdge* edge = fAlloc.make<SkQuadraticEdge>();
157 if (edge->setQuadratic(pts, fClipShift)) {
158 fList.push_back(edge);
159 }
160 }
addQuad(const SkPoint pts[])161 void SkAnalyticEdgeBuilder::addQuad(const SkPoint pts[]) {
162 SkAnalyticQuadraticEdge* edge = fAlloc.make<SkAnalyticQuadraticEdge>();
163 if (edge->setQuadratic(pts)) {
164 fList.push_back(edge);
165 }
166 }
addQuad(const SkPoint pts[])167 void SkBezierEdgeBuilder::addQuad(const SkPoint pts[]) {
168 SkQuad* quad = fAlloc.make<SkQuad>();
169 if (quad->set(pts)) {
170 fList.push_back(quad);
171 }
172 }
173
addCubic(const SkPoint pts[])174 void SkBasicEdgeBuilder::addCubic(const SkPoint pts[]) {
175 SkCubicEdge* edge = fAlloc.make<SkCubicEdge>();
176 if (edge->setCubic(pts, fClipShift)) {
177 fList.push_back(edge);
178 }
179 }
addCubic(const SkPoint pts[])180 void SkAnalyticEdgeBuilder::addCubic(const SkPoint pts[]) {
181 SkAnalyticCubicEdge* edge = fAlloc.make<SkAnalyticCubicEdge>();
182 if (edge->setCubic(pts)) {
183 fList.push_back(edge);
184 }
185 }
addCubic(const SkPoint pts[])186 void SkBezierEdgeBuilder::addCubic(const SkPoint pts[]) {
187 SkCubic* cubic = fAlloc.make<SkCubic>();
188 if (cubic->set(pts)) {
189 fList.push_back(cubic);
190 }
191 }
192
193 // TODO: merge addLine() and addPolyLine()?
194
addPolyLine(SkPoint pts[],char * arg_edge,char ** arg_edgePtr)195 SkEdgeBuilder::Combine SkBasicEdgeBuilder::addPolyLine(SkPoint pts[],
196 char* arg_edge, char** arg_edgePtr) {
197 auto edge = (SkEdge*) arg_edge;
198 auto edgePtr = (SkEdge**)arg_edgePtr;
199
200 if (edge->setLine(pts[0], pts[1], fClipShift)) {
201 return is_vertical(edge) && edgePtr > (SkEdge**)fEdgeList
202 ? this->combineVertical(edge, edgePtr[-1])
203 : kNo_Combine;
204 }
205 return SkEdgeBuilder::kPartial_Combine; // A convenient lie. Same do-nothing behavior.
206 }
addPolyLine(SkPoint pts[],char * arg_edge,char ** arg_edgePtr)207 SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::addPolyLine(SkPoint pts[],
208 char* arg_edge, char** arg_edgePtr) {
209 auto edge = (SkAnalyticEdge*) arg_edge;
210 auto edgePtr = (SkAnalyticEdge**)arg_edgePtr;
211
212 if (edge->setLine(pts[0], pts[1])) {
213 return is_vertical(edge) && edgePtr > (SkAnalyticEdge**)fEdgeList
214 ? this->combineVertical(edge, edgePtr[-1])
215 : kNo_Combine;
216 }
217 return SkEdgeBuilder::kPartial_Combine; // As above.
218 }
addPolyLine(SkPoint pts[],char * arg_edge,char ** arg_edgePtr)219 SkEdgeBuilder::Combine SkBezierEdgeBuilder::addPolyLine(SkPoint pts[],
220 char* arg_edge, char** arg_edgePtr) {
221 auto edge = (SkLine*)arg_edge;
222
223 if (edge->set(pts)) {
224 return kNo_Combine;
225 }
226 return SkEdgeBuilder::kPartial_Combine; // As above.
227 }
228
recoverClip(const SkIRect & src) const229 SkRect SkBasicEdgeBuilder::recoverClip(const SkIRect& src) const {
230 return { SkIntToScalar(src.fLeft >> fClipShift),
231 SkIntToScalar(src.fTop >> fClipShift),
232 SkIntToScalar(src.fRight >> fClipShift),
233 SkIntToScalar(src.fBottom >> fClipShift), };
234 }
recoverClip(const SkIRect & src) const235 SkRect SkAnalyticEdgeBuilder::recoverClip(const SkIRect& src) const {
236 return SkRect::Make(src);
237 }
recoverClip(const SkIRect & src) const238 SkRect SkBezierEdgeBuilder::recoverClip(const SkIRect& src) const {
239 return SkRect::Make(src);
240 }
241
allocEdges(size_t n,size_t * size)242 char* SkBasicEdgeBuilder::allocEdges(size_t n, size_t* size) {
243 *size = sizeof(SkEdge);
244 return (char*)fAlloc.makeArrayDefault<SkEdge>(n);
245 }
allocEdges(size_t n,size_t * size)246 char* SkAnalyticEdgeBuilder::allocEdges(size_t n, size_t* size) {
247 *size = sizeof(SkAnalyticEdge);
248 return (char*)fAlloc.makeArrayDefault<SkAnalyticEdge>(n);
249 }
allocEdges(size_t n,size_t * size)250 char* SkBezierEdgeBuilder::allocEdges(size_t n, size_t* size) {
251 *size = sizeof(SkLine);
252 return (char*)fAlloc.makeArrayDefault<SkLine>(n);
253 }
254
255 // TODO: maybe get rid of buildPoly() entirely?
buildPoly(const SkPath & path,const SkIRect * iclip,bool canCullToTheRight)256 int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) {
257 SkPath::Iter iter(path, true);
258 SkPoint pts[4];
259 SkPath::Verb verb;
260
261 size_t maxEdgeCount = path.countPoints();
262 if (iclip) {
263 // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
264 // we turn portions that are clipped out on the left/right into vertical
265 // segments.
266 SkSafeMath safe;
267 maxEdgeCount = safe.mul(maxEdgeCount, SkLineClipper::kMaxClippedLineSegments);
268 if (!safe) {
269 return 0;
270 }
271 }
272
273 size_t edgeSize;
274 char* edge = this->allocEdges(maxEdgeCount, &edgeSize);
275
276 SkDEBUGCODE(char* edgeStart = edge);
277 char** edgePtr = fAlloc.makeArrayDefault<char*>(maxEdgeCount);
278 fEdgeList = (void**)edgePtr;
279
280 if (iclip) {
281 SkRect clip = this->recoverClip(*iclip);
282
283 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
284 switch (verb) {
285 case SkPath::kMove_Verb:
286 case SkPath::kClose_Verb:
287 // we ignore these, and just get the whole segment from
288 // the corresponding line/quad/cubic verbs
289 break;
290 case SkPath::kLine_Verb: {
291 SkPoint lines[SkLineClipper::kMaxPoints];
292 int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
293 SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
294 for (int i = 0; i < lineCount; i++) {
295 switch( this->addPolyLine(lines + i, edge, edgePtr) ) {
296 case kTotal_Combine: edgePtr--; break;
297 case kPartial_Combine: break;
298 case kNo_Combine: *edgePtr++ = edge;
299 edge += edgeSize;
300 }
301 }
302 break;
303 }
304 default:
305 SkDEBUGFAIL("unexpected verb");
306 break;
307 }
308 }
309 } else {
310 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
311 switch (verb) {
312 case SkPath::kMove_Verb:
313 case SkPath::kClose_Verb:
314 // we ignore these, and just get the whole segment from
315 // the corresponding line/quad/cubic verbs
316 break;
317 case SkPath::kLine_Verb: {
318 switch( this->addPolyLine(pts, edge, edgePtr) ) {
319 case kTotal_Combine: edgePtr--; break;
320 case kPartial_Combine: break;
321 case kNo_Combine: *edgePtr++ = edge;
322 edge += edgeSize;
323 }
324 break;
325 }
326 default:
327 SkDEBUGFAIL("unexpected verb");
328 break;
329 }
330 }
331 }
332 SkASSERT((size_t)(edge - edgeStart) <= maxEdgeCount * edgeSize);
333 SkASSERT((size_t)(edgePtr - (char**)fEdgeList) <= maxEdgeCount);
334 return SkToInt(edgePtr - (char**)fEdgeList);
335 }
336
build(const SkPath & path,const SkIRect * iclip,bool canCullToTheRight)337 int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) {
338 SkAutoConicToQuads quadder;
339 const SkScalar conicTol = SK_Scalar1 / 4;
340
341 SkPath::Iter iter(path, true);
342 SkPoint pts[4];
343 SkPath::Verb verb;
344
345 bool is_finite = true;
346
347 if (iclip) {
348 SkRect clip = this->recoverClip(*iclip);
349 SkEdgeClipper clipper(canCullToTheRight);
350
351 auto apply_clipper = [this, &clipper, &is_finite] {
352 SkPoint pts[4];
353 SkPath::Verb verb;
354
355 while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
356 const int count = SkPathPriv::PtsInIter(verb);
357 if (!SkScalarsAreFinite(&pts[0].fX, count*2)) {
358 is_finite = false;
359 return;
360 }
361 switch (verb) {
362 case SkPath::kLine_Verb: this->addLine (pts); break;
363 case SkPath::kQuad_Verb: this->addQuad (pts); break;
364 case SkPath::kCubic_Verb: this->addCubic(pts); break;
365 default: break;
366 }
367 }
368 };
369
370 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
371 switch (verb) {
372 case SkPath::kMove_Verb:
373 case SkPath::kClose_Verb:
374 // we ignore these, and just get the whole segment from
375 // the corresponding line/quad/cubic verbs
376 break;
377 case SkPath::kLine_Verb:
378 if (clipper.clipLine(pts[0], pts[1], clip)) {
379 apply_clipper();
380 }
381 break;
382 case SkPath::kQuad_Verb:
383 if (clipper.clipQuad(pts, clip)) {
384 apply_clipper();
385 }
386 break;
387 case SkPath::kConic_Verb: {
388 const SkPoint* quadPts = quadder.computeQuads(
389 pts, iter.conicWeight(), conicTol);
390 for (int i = 0; i < quadder.countQuads(); ++i) {
391 if (clipper.clipQuad(quadPts, clip)) {
392 apply_clipper();
393 }
394 quadPts += 2;
395 }
396 } break;
397 case SkPath::kCubic_Verb:
398 if (clipper.clipCubic(pts, clip)) {
399 apply_clipper();
400 }
401 break;
402 default:
403 SkDEBUGFAIL("unexpected verb");
404 break;
405 }
406 }
407 } else {
408 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
409 auto handle_quad = [this](const SkPoint pts[3]) {
410 SkPoint monoX[5];
411 int n = SkChopQuadAtYExtrema(pts, monoX);
412 for (int i = 0; i <= n; i++) {
413 this->addQuad(&monoX[i * 2]);
414 }
415 };
416
417 switch (verb) {
418 case SkPath::kMove_Verb:
419 case SkPath::kClose_Verb:
420 // we ignore these, and just get the whole segment from
421 // the corresponding line/quad/cubic verbs
422 break;
423 case SkPath::kLine_Verb:
424 this->addLine(pts);
425 break;
426 case SkPath::kQuad_Verb: {
427 handle_quad(pts);
428 break;
429 }
430 case SkPath::kConic_Verb: {
431 const SkPoint* quadPts = quadder.computeQuads(
432 pts, iter.conicWeight(), conicTol);
433 for (int i = 0; i < quadder.countQuads(); ++i) {
434 handle_quad(quadPts);
435 quadPts += 2;
436 }
437 } break;
438 case SkPath::kCubic_Verb: {
439 if (!this->chopCubics()) {
440 this->addCubic(pts);
441 break;
442 }
443 SkPoint monoY[10];
444 int n = SkChopCubicAtYExtrema(pts, monoY);
445 for (int i = 0; i <= n; i++) {
446 this->addCubic(&monoY[i * 3]);
447 }
448 break;
449 }
450 default:
451 SkDEBUGFAIL("unexpected verb");
452 break;
453 }
454 }
455 }
456 fEdgeList = fList.begin();
457 return is_finite ? fList.count() : 0;
458 }
459
buildEdges(const SkPath & path,const SkIRect * shiftedClip)460 int SkEdgeBuilder::buildEdges(const SkPath& path,
461 const SkIRect* shiftedClip) {
462 // If we're convex, then we need both edges, even if the right edge is past the clip.
463 const bool canCullToTheRight = !path.isConvex();
464
465 // We can use our buildPoly() optimization if all the segments are lines.
466 // (Edges are homogenous and stored contiguously in memory, no need for indirection.)
467 const int count = SkPath::kLine_SegmentMask == path.getSegmentMasks()
468 ? this->buildPoly(path, shiftedClip, canCullToTheRight)
469 : this->build (path, shiftedClip, canCullToTheRight);
470
471 SkASSERT(count >= 0);
472
473 // If we can't cull to the right, we should have count > 1 (or 0),
474 // unless we're in DAA which doesn't need to chop edges at y extrema.
475 // For example, a single cubic edge with a valley shape \_/ is fine for DAA.
476 if (!canCullToTheRight && count == 1) {
477 SkASSERT(!this->chopCubics());
478 }
479
480 return count;
481 }
482