SkEdgeBuilder.cpp revision d36baa7a4a5ae3cc94cc4a45379f55658f80c0a6
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#include "SkEdgeBuilder.h"
8#include "SkPath.h"
9#include "SkEdge.h"
10#include "SkEdgeClipper.h"
11#include "SkLineClipper.h"
12#include "SkGeometry.h"
13
14template <typename T> static T* typedAllocThrow(SkChunkAlloc& alloc) {
15    return static_cast<T*>(alloc.allocThrow(sizeof(T)));
16}
17
18///////////////////////////////////////////////////////////////////////////////
19
20SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {
21    fEdgeList = nullptr;
22}
23
24SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(const SkEdge* edge, SkEdge* last) {
25    if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
26        return kNo_Combine;
27    }
28    if (edge->fWinding == last->fWinding) {
29        if (edge->fLastY + 1 == last->fFirstY) {
30            last->fFirstY = edge->fFirstY;
31            return kPartial_Combine;
32        }
33        if (edge->fFirstY == last->fLastY + 1) {
34            last->fLastY = edge->fLastY;
35            return kPartial_Combine;
36        }
37        return kNo_Combine;
38    }
39    if (edge->fFirstY == last->fFirstY) {
40        if (edge->fLastY == last->fLastY) {
41            return kTotal_Combine;
42        }
43        if (edge->fLastY < last->fLastY) {
44            last->fFirstY = edge->fLastY + 1;
45            return kPartial_Combine;
46        }
47        last->fFirstY = last->fLastY + 1;
48        last->fLastY = edge->fLastY;
49        last->fWinding = edge->fWinding;
50        return kPartial_Combine;
51    }
52    if (edge->fLastY == last->fLastY) {
53        if (edge->fFirstY > last->fFirstY) {
54            last->fLastY = edge->fFirstY - 1;
55            return kPartial_Combine;
56        }
57        last->fLastY = last->fFirstY - 1;
58        last->fFirstY = edge->fFirstY;
59        last->fWinding = edge->fWinding;
60        return kPartial_Combine;
61    }
62    return kNo_Combine;
63}
64
65static bool vertical_line(const SkEdge* edge) {
66    return !edge->fDX && !edge->fCurveCount;
67}
68
69void SkEdgeBuilder::addLine(const SkPoint pts[]) {
70    SkEdge* edge = typedAllocThrow<SkEdge>(fAlloc);
71    if (edge->setLine(pts[0], pts[1], fShiftUp)) {
72        if (vertical_line(edge) && fList.count()) {
73            Combine combine = CombineVertical(edge, *(fList.end() - 1));
74            if (kNo_Combine != combine) {
75                if (kTotal_Combine == combine) {
76                    fList.pop();
77                }
78                goto unallocate_edge;
79            }
80        }
81        fList.push(edge);
82    } else {
83unallocate_edge:
84        ;
85        // TODO: unallocate edge from storage...
86    }
87}
88
89void SkEdgeBuilder::addQuad(const SkPoint pts[]) {
90    SkQuadraticEdge* edge = typedAllocThrow<SkQuadraticEdge>(fAlloc);
91    if (edge->setQuadratic(pts, fShiftUp)) {
92        fList.push(edge);
93    } else {
94        // TODO: unallocate edge from storage...
95    }
96}
97
98void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
99    SkCubicEdge* edge = typedAllocThrow<SkCubicEdge>(fAlloc);
100    if (edge->setCubic(pts, fShiftUp)) {
101        fList.push(edge);
102    } else {
103        // TODO: unallocate edge from storage...
104    }
105}
106
107void SkEdgeBuilder::addClipper(SkEdgeClipper* clipper) {
108    SkPoint      pts[4];
109    SkPath::Verb verb;
110
111    while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
112        switch (verb) {
113            case SkPath::kLine_Verb:
114                this->addLine(pts);
115                break;
116            case SkPath::kQuad_Verb:
117                this->addQuad(pts);
118                break;
119            case SkPath::kCubic_Verb:
120                this->addCubic(pts);
121                break;
122            default:
123                break;
124        }
125    }
126}
127
128///////////////////////////////////////////////////////////////////////////////
129
130static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) {
131    dst->set(SkIntToScalar(src.fLeft >> shift),
132             SkIntToScalar(src.fTop >> shift),
133             SkIntToScalar(src.fRight >> shift),
134             SkIntToScalar(src.fBottom >> shift));
135}
136
137SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkEdge* edge, SkEdge** edgePtr) {
138    return !vertical_line(edge) || edgePtr <= fEdgeList ? kNo_Combine :
139            CombineVertical(edge, edgePtr[-1]);
140}
141
142int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shiftUp,
143                             bool canCullToTheRight) {
144    SkPath::Iter    iter(path, true);
145    SkPoint         pts[4];
146    SkPath::Verb    verb;
147
148    int maxEdgeCount = path.countPoints();
149    if (iclip) {
150        // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
151        // we turn portions that are clipped out on the left/right into vertical
152        // segments.
153        maxEdgeCount *= SkLineClipper::kMaxClippedLineSegments;
154    }
155    size_t maxEdgeSize = maxEdgeCount * sizeof(SkEdge);
156    size_t maxEdgePtrSize = maxEdgeCount * sizeof(SkEdge*);
157
158    // lets store the edges and their pointers in the same block
159    char* storage = (char*)fAlloc.allocThrow(maxEdgeSize + maxEdgePtrSize);
160    SkEdge* edge = reinterpret_cast<SkEdge*>(storage);
161    SkEdge** edgePtr = reinterpret_cast<SkEdge**>(storage + maxEdgeSize);
162    // Record the beginning of our pointers, so we can return them to the caller
163    fEdgeList = edgePtr;
164
165    if (iclip) {
166        SkRect clip;
167        setShiftedClip(&clip, *iclip, shiftUp);
168
169        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
170            switch (verb) {
171                case SkPath::kMove_Verb:
172                case SkPath::kClose_Verb:
173                    // we ignore these, and just get the whole segment from
174                    // the corresponding line/quad/cubic verbs
175                    break;
176                case SkPath::kLine_Verb: {
177                    SkPoint lines[SkLineClipper::kMaxPoints];
178                    int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
179                    SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
180                    for (int i = 0; i < lineCount; i++) {
181                        if (edge->setLine(lines[i], lines[i + 1], shiftUp)) {
182                            Combine combine = checkVertical(edge, edgePtr);
183                            if (kNo_Combine == combine) {
184                                *edgePtr++ = edge++;
185                            } else if (kTotal_Combine == combine) {
186                                --edgePtr;
187                            }
188                        }
189                    }
190                    break;
191                }
192                default:
193                    SkDEBUGFAIL("unexpected verb");
194                    break;
195            }
196        }
197    } else {
198        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
199            switch (verb) {
200                case SkPath::kMove_Verb:
201                case SkPath::kClose_Verb:
202                    // we ignore these, and just get the whole segment from
203                    // the corresponding line/quad/cubic verbs
204                    break;
205                case SkPath::kLine_Verb:
206                    if (edge->setLine(pts[0], pts[1], shiftUp)) {
207                        Combine combine = checkVertical(edge, edgePtr);
208                        if (kNo_Combine == combine) {
209                            *edgePtr++ = edge++;
210                        } else if (kTotal_Combine == combine) {
211                            --edgePtr;
212                        }
213                    }
214                    break;
215                default:
216                    SkDEBUGFAIL("unexpected verb");
217                    break;
218            }
219        }
220    }
221    SkASSERT((char*)edge <= (char*)fEdgeList);
222    SkASSERT(edgePtr - fEdgeList <= maxEdgeCount);
223    return SkToInt(edgePtr - fEdgeList);
224}
225
226static void handle_quad(SkEdgeBuilder* builder, const SkPoint pts[3]) {
227    SkPoint monoX[5];
228    int n = SkChopQuadAtYExtrema(pts, monoX);
229    for (int i = 0; i <= n; i++) {
230        builder->addQuad(&monoX[i * 2]);
231    }
232}
233
234int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, int shiftUp,
235                         bool canCullToTheRight) {
236    fAlloc.reset();
237    fList.reset();
238    fShiftUp = shiftUp;
239
240    if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
241        return this->buildPoly(path, iclip, shiftUp, canCullToTheRight);
242    }
243
244    SkAutoConicToQuads quadder;
245    const SkScalar conicTol = SK_Scalar1 / 4;
246
247    SkPath::Iter    iter(path, true);
248    SkPoint         pts[4];
249    SkPath::Verb    verb;
250
251    if (iclip) {
252        SkRect clip;
253        setShiftedClip(&clip, *iclip, shiftUp);
254        SkEdgeClipper clipper(canCullToTheRight);
255
256        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
257            switch (verb) {
258                case SkPath::kMove_Verb:
259                case SkPath::kClose_Verb:
260                    // we ignore these, and just get the whole segment from
261                    // the corresponding line/quad/cubic verbs
262                    break;
263                case SkPath::kLine_Verb: {
264                    SkPoint lines[SkLineClipper::kMaxPoints];
265                    int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
266                    for (int i = 0; i < lineCount; i++) {
267                        this->addLine(&lines[i]);
268                    }
269                    break;
270                }
271                case SkPath::kQuad_Verb:
272                    if (clipper.clipQuad(pts, clip)) {
273                        this->addClipper(&clipper);
274                    }
275                    break;
276                case SkPath::kConic_Verb: {
277                    const SkPoint* quadPts = quadder.computeQuads(
278                                          pts, iter.conicWeight(), conicTol);
279                    for (int i = 0; i < quadder.countQuads(); ++i) {
280                        if (clipper.clipQuad(quadPts, clip)) {
281                            this->addClipper(&clipper);
282                        }
283                        quadPts += 2;
284                    }
285                } break;
286                case SkPath::kCubic_Verb:
287                    if (clipper.clipCubic(pts, clip)) {
288                        this->addClipper(&clipper);
289                    }
290                    break;
291                default:
292                    SkDEBUGFAIL("unexpected verb");
293                    break;
294            }
295        }
296    } else {
297        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
298            switch (verb) {
299                case SkPath::kMove_Verb:
300                case SkPath::kClose_Verb:
301                    // we ignore these, and just get the whole segment from
302                    // the corresponding line/quad/cubic verbs
303                    break;
304                case SkPath::kLine_Verb:
305                    this->addLine(pts);
306                    break;
307                case SkPath::kQuad_Verb: {
308                    handle_quad(this, pts);
309                    break;
310                }
311                case SkPath::kConic_Verb: {
312                    const SkPoint* quadPts = quadder.computeQuads(
313                                          pts, iter.conicWeight(), conicTol);
314                    for (int i = 0; i < quadder.countQuads(); ++i) {
315                        handle_quad(this, quadPts);
316                        quadPts += 2;
317                    }
318                } break;
319                case SkPath::kCubic_Verb: {
320                    SkPoint monoY[10];
321                    int n = SkChopCubicAtYExtrema(pts, monoY);
322                    for (int i = 0; i <= n; i++) {
323                        this->addCubic(&monoY[i * 3]);
324                    }
325                    break;
326                }
327                default:
328                    SkDEBUGFAIL("unexpected verb");
329                    break;
330            }
331        }
332    }
333    fEdgeList = fList.begin();
334    return fList.count();
335}
336