blob: d1d7af893177828e02ba764770303787dade8e87 [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 */
7#include "SkOpEdgeBuilder.h"
8#include "SkReduceOrder.h"
9
10void SkOpEdgeBuilder::init() {
11 fCurrentContour = NULL;
12 fOperand = false;
13 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
14 : kWinding_PathOpsMask;
15#if DEBUG_DUMP
16 gContourID = 0;
17 gSegmentID = 0;
18#endif
19 fSecondHalf = preFetch();
20}
21
22void SkOpEdgeBuilder::addOperand(const SkPath& path) {
23 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
24 fPathVerbs.pop();
25 fPath = &path;
26 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
27 : kWinding_PathOpsMask;
28 preFetch();
29}
30
31void SkOpEdgeBuilder::finish() {
32 walk();
33 complete();
34 if (fCurrentContour && !fCurrentContour->segments().count()) {
35 fContours.pop_back();
36 }
37 // correct pointers in contours since fReducePts may have moved as it grew
38 int cIndex = 0;
39 int extraCount = fExtra.count();
40 SkASSERT(extraCount == 0 || fExtra[0] == -1);
41 int eIndex = 0;
42 int rIndex = 0;
43 while (++eIndex < extraCount) {
44 int offset = fExtra[eIndex];
45 if (offset < 0) {
46 ++cIndex;
47 continue;
48 }
49 fCurrentContour = &fContours[cIndex];
50 rIndex += fCurrentContour->updateSegment(offset - 1,
51 &fReducePts[rIndex]);
52 }
53 fExtra.reset(); // we're done with this
54}
55
caryclark@google.com6dc7df62013-04-25 11:51:54 +000056// Note that copying the points here avoids copying the resulting path later.
57// To allow Op() to take one of the input paths as an output parameter, either the source data
58// must be copied (as implemented below) or the result must be copied.
59// OPTIMIZATION: This copies both sets of input points every time. If the input data was read
60// directly, the output path would only need to be copied if it was also one of the input paths.
caryclark@google.com07393ca2013-04-08 11:47:37 +000061int SkOpEdgeBuilder::preFetch() {
caryclark@google.com6dc7df62013-04-25 11:51:54 +000062 SkPath::RawIter iter(*fPath);
caryclark@google.com07393ca2013-04-08 11:47:37 +000063 SkPoint pts[4];
64 SkPath::Verb verb;
65 do {
66 verb = iter.next(pts);
67 *fPathVerbs.append() = verb;
68 if (verb == SkPath::kMove_Verb) {
69 *fPathPts.append() = pts[0];
70 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
71 fPathPts.append(verb, &pts[1]);
72 }
73 } while (verb != SkPath::kDone_Verb);
74 return fPathVerbs.count() - 1;
75}
76
77void SkOpEdgeBuilder::walk() {
78 SkPath::Verb reducedVerb;
79 uint8_t* verbPtr = fPathVerbs.begin();
80 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
81 const SkPoint* pointsPtr = fPathPts.begin();
82 const SkPoint* finalCurveStart = NULL;
83 const SkPoint* finalCurveEnd = NULL;
84 SkPath::Verb verb;
caryclark@google.com6dc7df62013-04-25 11:51:54 +000085 while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
86 if (verbPtr == endOfFirstHalf) {
87 fOperand = true;
88 }
89 verbPtr++;
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 switch (verb) {
91 case SkPath::kMove_Verb:
92 complete();
93 if (!fCurrentContour) {
94 fCurrentContour = fContours.push_back_n(1);
95 fCurrentContour->setOperand(fOperand);
96 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
97 *fExtra.append() = -1; // start new contour
98 }
99 finalCurveEnd = pointsPtr++;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000100 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 case SkPath::kLine_Verb:
102 // skip degenerate points
103 if (pointsPtr[-1].fX != pointsPtr[0].fX || pointsPtr[-1].fY != pointsPtr[0].fY) {
104 fCurrentContour->addLine(&pointsPtr[-1]);
105 }
106 break;
107 case SkPath::kQuad_Verb:
108 reducedVerb = SkReduceOrder::Quad(&pointsPtr[-1], &fReducePts);
109 if (reducedVerb == 0) {
110 break; // skip degenerate points
111 }
112 if (reducedVerb == 1) {
113 *fExtra.append() =
114 fCurrentContour->addLine(fReducePts.end() - 2);
115 break;
116 }
117 fCurrentContour->addQuad(&pointsPtr[-1]);
118 break;
119 case SkPath::kCubic_Verb:
120 reducedVerb = SkReduceOrder::Cubic(&pointsPtr[-1], &fReducePts);
121 if (reducedVerb == 0) {
122 break; // skip degenerate points
123 }
124 if (reducedVerb == 1) {
125 *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
126 break;
127 }
128 if (reducedVerb == 2) {
129 *fExtra.append() = fCurrentContour->addQuad(fReducePts.end() - 3);
130 break;
131 }
132 fCurrentContour->addCubic(&pointsPtr[-1]);
133 break;
134 case SkPath::kClose_Verb:
135 SkASSERT(fCurrentContour);
136 if (finalCurveStart && finalCurveEnd
137 && *finalCurveStart != *finalCurveEnd) {
138 *fReducePts.append() = *finalCurveStart;
139 *fReducePts.append() = *finalCurveEnd;
140 *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
141 }
142 complete();
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000143 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 default:
145 SkDEBUGFAIL("bad verb");
146 return;
147 }
148 finalCurveStart = &pointsPtr[verb - 1];
149 pointsPtr += verb;
150 SkASSERT(fCurrentContour);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000151 }
152}