SkEdgeBuilder.cpp revision 451ceba3544fa1ff810378f1e9a47c7d20f48a9f
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 "SkAnalyticEdge.h"
11#include "SkEdgeClipper.h"
12#include "SkLineClipper.h"
13#include "SkGeometry.h"
14
15template <typename T> static T* typedAllocThrow(SkChunkAlloc& alloc) {
16    return static_cast<T*>(alloc.allocThrow(sizeof(T)));
17}
18
19///////////////////////////////////////////////////////////////////////////////
20
21SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {
22    fEdgeList = nullptr;
23}
24
25SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(const SkEdge* edge, SkEdge* last) {
26    if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
27        return kNo_Combine;
28    }
29    if (edge->fWinding == last->fWinding) {
30        if (edge->fLastY + 1 == last->fFirstY) {
31            last->fFirstY = edge->fFirstY;
32            return kPartial_Combine;
33        }
34        if (edge->fFirstY == last->fLastY + 1) {
35            last->fLastY = edge->fLastY;
36            return kPartial_Combine;
37        }
38        return kNo_Combine;
39    }
40    if (edge->fFirstY == last->fFirstY) {
41        if (edge->fLastY == last->fLastY) {
42            return kTotal_Combine;
43        }
44        if (edge->fLastY < last->fLastY) {
45            last->fFirstY = edge->fLastY + 1;
46            return kPartial_Combine;
47        }
48        last->fFirstY = last->fLastY + 1;
49        last->fLastY = edge->fLastY;
50        last->fWinding = edge->fWinding;
51        return kPartial_Combine;
52    }
53    if (edge->fLastY == last->fLastY) {
54        if (edge->fFirstY > last->fFirstY) {
55            last->fLastY = edge->fFirstY - 1;
56            return kPartial_Combine;
57        }
58        last->fLastY = last->fFirstY - 1;
59        last->fFirstY = edge->fFirstY;
60        last->fWinding = edge->fWinding;
61        return kPartial_Combine;
62    }
63    return kNo_Combine;
64}
65
66static inline bool approximatelyEqual(SkFixed a, SkFixed b) {
67    return SkAbs32(a - b) < 0x100;
68}
69
70SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(
71        const SkAnalyticEdge* edge, SkAnalyticEdge* last) {
72    SkASSERT(fAnalyticAA);
73    if (last->fCurveCount || last->fDX || edge->fX != last->fX) {
74        return kNo_Combine;
75    }
76    if (edge->fWinding == last->fWinding) {
77        if (edge->fLowerY == last->fUpperY) {
78            last->fUpperY = edge->fUpperY;
79            last->fY = last->fUpperY;
80            return kPartial_Combine;
81        }
82        if (approximatelyEqual(edge->fUpperY, last->fLowerY)) {
83            last->fLowerY = edge->fLowerY;
84            return kPartial_Combine;
85        }
86        return kNo_Combine;
87    }
88    if (approximatelyEqual(edge->fUpperY, last->fUpperY)) {
89        if (approximatelyEqual(edge->fLowerY, last->fLowerY)) {
90            return kTotal_Combine;
91        }
92        if (edge->fLowerY < last->fLowerY) {
93            last->fUpperY = edge->fLowerY;
94            last->fY = last->fUpperY;
95            return kPartial_Combine;
96        }
97        last->fUpperY = last->fLowerY;
98        last->fY = last->fUpperY;
99        last->fLowerY = edge->fLowerY;
100        last->fWinding = edge->fWinding;
101        return kPartial_Combine;
102    }
103    if (approximatelyEqual(edge->fLowerY, last->fLowerY)) {
104        if (edge->fUpperY > last->fUpperY) {
105            last->fLowerY = edge->fUpperY;
106            return kPartial_Combine;
107        }
108        last->fLowerY = last->fUpperY;
109        last->fUpperY = edge->fUpperY;
110        last->fY = last->fUpperY;
111        last->fWinding = edge->fWinding;
112        return kPartial_Combine;
113    }
114    return kNo_Combine;
115}
116
117bool SkEdgeBuilder::vertical_line(const SkEdge* edge) {
118    return !edge->fDX && !edge->fCurveCount;
119}
120
121bool SkEdgeBuilder::vertical_line(const SkAnalyticEdge* edge) {
122    SkASSERT(fAnalyticAA);
123    return !edge->fDX && !edge->fCurveCount;
124}
125
126void SkEdgeBuilder::addLine(const SkPoint pts[]) {
127    if (fAnalyticAA) {
128        SkAnalyticEdge* edge = typedAllocThrow<SkAnalyticEdge>(fAlloc);
129        if (edge->setLine(pts[0], pts[1])) {
130            if (vertical_line(edge) && fList.count()) {
131                Combine combine = CombineVertical(edge, (SkAnalyticEdge*)*(fList.end() - 1));
132                if (kNo_Combine != combine) {
133                    if (kTotal_Combine == combine) {
134                        fList.pop();
135                    }
136                    goto unallocate_analytic_edge;
137                }
138            }
139            fList.push(edge);
140        } else {
141unallocate_analytic_edge:
142            ;
143            // TODO: unallocate edge from storage...
144        }
145    } else {
146        SkEdge* edge = typedAllocThrow<SkEdge>(fAlloc);
147        if (edge->setLine(pts[0], pts[1], fShiftUp)) {
148            if (vertical_line(edge) && fList.count()) {
149                Combine combine = CombineVertical(edge, (SkEdge*)*(fList.end() - 1));
150                if (kNo_Combine != combine) {
151                    if (kTotal_Combine == combine) {
152                        fList.pop();
153                    }
154                    goto unallocate_edge;
155                }
156            }
157            fList.push(edge);
158        } else {
159unallocate_edge:
160            ;
161            // TODO: unallocate edge from storage...
162        }
163    }
164}
165
166void SkEdgeBuilder::addQuad(const SkPoint pts[]) {
167    if (fAnalyticAA) {
168        SkAnalyticQuadraticEdge* edge = typedAllocThrow<SkAnalyticQuadraticEdge>(fAlloc);
169        if (edge->setQuadratic(pts)) {
170            fList.push(edge);
171        } else {
172            // TODO: unallocate edge from storage...
173        }
174    } else {
175        SkQuadraticEdge* edge = typedAllocThrow<SkQuadraticEdge>(fAlloc);
176        if (edge->setQuadratic(pts, fShiftUp)) {
177            fList.push(edge);
178        } else {
179            // TODO: unallocate edge from storage...
180        }
181    }
182}
183
184void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
185    if (fAnalyticAA) {
186        SkAnalyticCubicEdge* edge = typedAllocThrow<SkAnalyticCubicEdge>(fAlloc);
187        if (edge->setCubic(pts)) {
188            fList.push(edge);
189        } else {
190            // TODO: unallocate edge from storage...
191        }
192    } else {
193        SkCubicEdge* edge = typedAllocThrow<SkCubicEdge>(fAlloc);
194        if (edge->setCubic(pts, fShiftUp)) {
195            fList.push(edge);
196        } else {
197            // TODO: unallocate edge from storage...
198        }
199    }
200}
201
202void SkEdgeBuilder::addClipper(SkEdgeClipper* clipper) {
203    SkPoint      pts[4];
204    SkPath::Verb verb;
205
206    while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
207        switch (verb) {
208            case SkPath::kLine_Verb:
209                this->addLine(pts);
210                break;
211            case SkPath::kQuad_Verb:
212                this->addQuad(pts);
213                break;
214            case SkPath::kCubic_Verb:
215                this->addCubic(pts);
216                break;
217            default:
218                break;
219        }
220    }
221}
222
223///////////////////////////////////////////////////////////////////////////////
224
225static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) {
226    dst->set(SkIntToScalar(src.fLeft >> shift),
227             SkIntToScalar(src.fTop >> shift),
228             SkIntToScalar(src.fRight >> shift),
229             SkIntToScalar(src.fBottom >> shift));
230}
231
232SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkEdge* edge, SkEdge** edgePtr) {
233    return !vertical_line(edge) || edgePtr <= (SkEdge**)fEdgeList ? kNo_Combine :
234            CombineVertical(edge, edgePtr[-1]);
235}
236
237SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkAnalyticEdge* edge,
238        SkAnalyticEdge** edgePtr) {
239    SkASSERT(fAnalyticAA);
240    return !vertical_line(edge) || edgePtr <= (SkAnalyticEdge**)fEdgeList ? kNo_Combine :
241            CombineVertical(edge, edgePtr[-1]);
242}
243
244int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shiftUp,
245                             bool canCullToTheRight) {
246    SkPath::Iter    iter(path, true);
247    SkPoint         pts[4];
248    SkPath::Verb    verb;
249
250    int maxEdgeCount = path.countPoints();
251    if (iclip) {
252        // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
253        // we turn portions that are clipped out on the left/right into vertical
254        // segments.
255        maxEdgeCount *= SkLineClipper::kMaxClippedLineSegments;
256    }
257    size_t edgeSize = fAnalyticAA ? sizeof(SkAnalyticEdge) : sizeof(SkEdge);
258    size_t maxEdgeSize = maxEdgeCount * edgeSize;
259    size_t maxEdgePtrSize = maxEdgeCount * sizeof(char*);
260
261    // lets store the edges and their pointers in the same block
262    char* storage = (char*)fAlloc.allocThrow(maxEdgeSize + maxEdgePtrSize);
263    char* edge = (char*)storage;
264    char** edgePtr = (char**)(storage + maxEdgeSize);
265    // Record the beginning of our pointers, so we can return them to the caller
266    fEdgeList = (void**)edgePtr;
267
268    if (iclip) {
269        SkRect clip;
270        setShiftedClip(&clip, *iclip, shiftUp);
271
272        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
273            switch (verb) {
274                case SkPath::kMove_Verb:
275                case SkPath::kClose_Verb:
276                    // we ignore these, and just get the whole segment from
277                    // the corresponding line/quad/cubic verbs
278                    break;
279                case SkPath::kLine_Verb: {
280                    SkPoint lines[SkLineClipper::kMaxPoints];
281                    int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
282                    SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
283                    for (int i = 0; i < lineCount; i++) {
284                        bool setLineResult = fAnalyticAA ?
285                                ((SkAnalyticEdge*)edge)->setLine(lines[i], lines[i + 1]) :
286                                ((SkEdge*)edge)->setLine(lines[i], lines[i + 1], shiftUp);
287                        if (setLineResult) {
288                            Combine combine = fAnalyticAA ?
289                                    checkVertical((SkAnalyticEdge*)edge, (SkAnalyticEdge**)edgePtr) :
290                                    checkVertical((SkEdge*)edge, (SkEdge**)edgePtr);
291                            if (kNo_Combine == combine) {
292                                *edgePtr++ = edge;
293                                edge += edgeSize;
294                            } else if (kTotal_Combine == combine) {
295                                --edgePtr;
296                            }
297                        }
298                    }
299                    break;
300                }
301                default:
302                    SkDEBUGFAIL("unexpected verb");
303                    break;
304            }
305        }
306    } else {
307        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
308            switch (verb) {
309                case SkPath::kMove_Verb:
310                case SkPath::kClose_Verb:
311                    // we ignore these, and just get the whole segment from
312                    // the corresponding line/quad/cubic verbs
313                    break;
314                case SkPath::kLine_Verb: {
315                    bool setLineResult = fAnalyticAA ?
316                            ((SkAnalyticEdge*)edge)->setLine(pts[0], pts[1]) :
317                            ((SkEdge*)edge)->setLine(pts[0], pts[1], shiftUp);
318                    if (setLineResult) {
319                        Combine combine = fAnalyticAA ?
320                                checkVertical((SkAnalyticEdge*)edge, (SkAnalyticEdge**)edgePtr) :
321                                checkVertical((SkEdge*)edge, (SkEdge**)edgePtr);
322                        if (kNo_Combine == combine) {
323                            *edgePtr++ = edge;
324                            edge += edgeSize;
325                        } else if (kTotal_Combine == combine) {
326                            --edgePtr;
327                        }
328                    }
329                    break;
330                }
331                default:
332                    SkDEBUGFAIL("unexpected verb");
333                    break;
334            }
335        }
336    }
337    SkASSERT((char*)edge <= (char*)fEdgeList);
338    SkASSERT(edgePtr - (char**)fEdgeList <= maxEdgeCount);
339    return SkToInt(edgePtr - (char**)fEdgeList);
340}
341
342static void handle_quad(SkEdgeBuilder* builder, const SkPoint pts[3]) {
343    SkPoint monoX[5];
344    int n = SkChopQuadAtYExtrema(pts, monoX);
345    for (int i = 0; i <= n; i++) {
346        builder->addQuad(&monoX[i * 2]);
347    }
348}
349
350int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, int shiftUp,
351                         bool canCullToTheRight, bool analyticAA) {
352    fAlloc.reset();
353    fList.reset();
354    fShiftUp = shiftUp;
355    fAnalyticAA = analyticAA;
356
357    if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
358        return this->buildPoly(path, iclip, shiftUp, canCullToTheRight);
359    }
360
361    SkAutoConicToQuads quadder;
362    const SkScalar conicTol = SK_Scalar1 / 4;
363
364    SkPath::Iter    iter(path, true);
365    SkPoint         pts[4];
366    SkPath::Verb    verb;
367
368    if (iclip) {
369        SkRect clip;
370        setShiftedClip(&clip, *iclip, shiftUp);
371        SkEdgeClipper clipper(canCullToTheRight);
372
373        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
374            switch (verb) {
375                case SkPath::kMove_Verb:
376                case SkPath::kClose_Verb:
377                    // we ignore these, and just get the whole segment from
378                    // the corresponding line/quad/cubic verbs
379                    break;
380                case SkPath::kLine_Verb: {
381                    SkPoint lines[SkLineClipper::kMaxPoints];
382                    int lineCount = SkLineClipper::ClipLine(pts, clip, lines, canCullToTheRight);
383                    for (int i = 0; i < lineCount; i++) {
384                        this->addLine(&lines[i]);
385                    }
386                    break;
387                }
388                case SkPath::kQuad_Verb:
389                    if (clipper.clipQuad(pts, clip)) {
390                        this->addClipper(&clipper);
391                    }
392                    break;
393                case SkPath::kConic_Verb: {
394                    const SkPoint* quadPts = quadder.computeQuads(
395                                          pts, iter.conicWeight(), conicTol);
396                    for (int i = 0; i < quadder.countQuads(); ++i) {
397                        if (clipper.clipQuad(quadPts, clip)) {
398                            this->addClipper(&clipper);
399                        }
400                        quadPts += 2;
401                    }
402                } break;
403                case SkPath::kCubic_Verb:
404                    if (clipper.clipCubic(pts, clip)) {
405                        this->addClipper(&clipper);
406                    }
407                    break;
408                default:
409                    SkDEBUGFAIL("unexpected verb");
410                    break;
411            }
412        }
413    } else {
414        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
415            switch (verb) {
416                case SkPath::kMove_Verb:
417                case SkPath::kClose_Verb:
418                    // we ignore these, and just get the whole segment from
419                    // the corresponding line/quad/cubic verbs
420                    break;
421                case SkPath::kLine_Verb:
422                    this->addLine(pts);
423                    break;
424                case SkPath::kQuad_Verb: {
425                    handle_quad(this, pts);
426                    break;
427                }
428                case SkPath::kConic_Verb: {
429                    const SkPoint* quadPts = quadder.computeQuads(
430                                          pts, iter.conicWeight(), conicTol);
431                    for (int i = 0; i < quadder.countQuads(); ++i) {
432                        handle_quad(this, quadPts);
433                        quadPts += 2;
434                    }
435                } break;
436                case SkPath::kCubic_Verb: {
437                    SkPoint monoY[10];
438                    int n = SkChopCubicAtYExtrema(pts, monoY);
439                    for (int i = 0; i <= n; i++) {
440                        this->addCubic(&monoY[i * 3]);
441                    }
442                    break;
443                }
444                default:
445                    SkDEBUGFAIL("unexpected verb");
446                    break;
447            }
448        }
449    }
450    fEdgeList = fList.begin();
451    return fList.count();
452}
453