SkRegion_path.cpp revision 73be1fc2b02757e3d98621d7cf735591aa6dffdb
1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkRegionPriv.h"
11#include "SkBlitter.h"
12#include "SkScan.h"
13#include "SkTDArray.h"
14#include "SkPath.h"
15
16class SkRgnBuilder : public SkBlitter {
17public:
18    SkRgnBuilder();
19    virtual ~SkRgnBuilder();
20
21    // returns true if it could allocate the working storage needed
22    bool init(int maxHeight, int maxTransitions, bool pathIsInverse);
23
24    void done() {
25        if (fCurrScanline != NULL) {
26            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
27            if (!this->collapsWithPrev()) { // flush the last line
28                fCurrScanline = fCurrScanline->nextScanline();
29            }
30        }
31    }
32
33    int     computeRunCount() const;
34    void    copyToRect(SkIRect*) const;
35    void    copyToRgn(SkRegion::RunType runs[]) const;
36
37    virtual void blitH(int x, int y, int width);
38
39#ifdef SK_DEBUG
40    void dump() const {
41        SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
42        const Scanline* line = (Scanline*)fStorage;
43        while (line < fCurrScanline) {
44            SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
45            for (int i = 0; i < line->fXCount; i++) {
46                SkDebugf(" %d", line->firstX()[i]);
47            }
48            SkDebugf("\n");
49
50            line = line->nextScanline();
51        }
52    }
53#endif
54private:
55    /*
56     *  Scanline mimics a row in the region, nearly. A row in a region is:
57     *      [Bottom IntervalCount [L R]... Sentinel]
58     *  while a Scanline is
59     *      [LastY XCount [L R]... uninitialized]
60     *  The two are the same length (which is good), but we have to transmute
61     *  the scanline a little when we convert it to a region-row.
62     *
63     *  Potentially we could recode this to exactly match the row format, in
64     *  which case copyToRgn() could be a single memcpy. Not sure that is worth
65     *  the effort.
66     */
67    struct Scanline {
68        SkRegion::RunType fLastY;
69        SkRegion::RunType fXCount;
70
71        SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
72        Scanline* nextScanline() const {
73            // add final +1 for the x-sentinel
74            return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
75        }
76    };
77    SkRegion::RunType*  fStorage;
78    Scanline*           fCurrScanline;
79    Scanline*           fPrevScanline;
80    //  points at next avialable x[] in fCurrScanline
81    SkRegion::RunType*  fCurrXPtr;
82    SkRegion::RunType   fTop;           // first Y value
83
84    int fStorageCount;
85
86    bool collapsWithPrev() {
87        if (fPrevScanline != NULL &&
88            fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
89            fPrevScanline->fXCount == fCurrScanline->fXCount &&
90            !memcmp(fPrevScanline->firstX(),
91                    fCurrScanline->firstX(),
92                    fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
93        {
94            // update the height of fPrevScanline
95            fPrevScanline->fLastY = fCurrScanline->fLastY;
96            return true;
97        }
98        return false;
99    }
100};
101
102SkRgnBuilder::SkRgnBuilder()
103    : fStorage(NULL) {
104}
105
106SkRgnBuilder::~SkRgnBuilder() {
107    sk_free(fStorage);
108}
109
110bool SkRgnBuilder::init(int maxHeight, int maxTransitions, bool pathIsInverse) {
111    if ((maxHeight | maxTransitions) < 0) {
112        return false;
113    }
114
115    if (pathIsInverse) {
116        // allow for additional X transitions to "invert" each scanline
117        // [ L' ... normal transitions ... R' ]
118        //
119        maxTransitions += 2;
120    }
121
122    // compute the count with +1 and +3 slop for the working buffer
123    int64_t count = sk_64_mul(maxHeight + 1, 3 + maxTransitions);
124
125    if (pathIsInverse) {
126        // allow for two "empty" rows for the top and bottom
127        //      [ Y, 1, L, R, S] == 5 (*2 for top and bottom)
128        count += 10;
129    }
130
131    if (count < 0 || !sk_64_isS32(count)) {
132        return false;
133    }
134    fStorageCount = sk_64_asS32(count);
135
136    int64_t size = sk_64_mul(fStorageCount, sizeof(SkRegion::RunType));
137    if (size < 0 || !sk_64_isS32(size)) {
138        return false;
139    }
140
141    fStorage = (SkRegion::RunType*)sk_malloc_flags(sk_64_asS32(size), 0);
142    if (NULL == fStorage) {
143        return false;
144    }
145
146    fCurrScanline = NULL;    // signal empty collection
147    fPrevScanline = NULL;    // signal first scanline
148    return true;
149}
150
151void SkRgnBuilder::blitH(int x, int y, int width) {
152    if (fCurrScanline == NULL) {  // first time
153        fTop = (SkRegion::RunType)(y);
154        fCurrScanline = (Scanline*)fStorage;
155        fCurrScanline->fLastY = (SkRegion::RunType)(y);
156        fCurrXPtr = fCurrScanline->firstX();
157    } else {
158        SkASSERT(y >= fCurrScanline->fLastY);
159
160        if (y > fCurrScanline->fLastY) {
161            // if we get here, we're done with fCurrScanline
162            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
163
164            int prevLastY = fCurrScanline->fLastY;
165            if (!this->collapsWithPrev()) {
166                fPrevScanline = fCurrScanline;
167                fCurrScanline = fCurrScanline->nextScanline();
168
169            }
170            if (y - 1 > prevLastY) {  // insert empty run
171                fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
172                fCurrScanline->fXCount = 0;
173                fCurrScanline = fCurrScanline->nextScanline();
174            }
175            // setup for the new curr line
176            fCurrScanline->fLastY = (SkRegion::RunType)(y);
177            fCurrXPtr = fCurrScanline->firstX();
178        }
179    }
180    //  check if we should extend the current run, or add a new one
181    if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
182        fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
183    } else {
184        fCurrXPtr[0] = (SkRegion::RunType)(x);
185        fCurrXPtr[1] = (SkRegion::RunType)(x + width);
186        fCurrXPtr += 2;
187    }
188    SkASSERT(fCurrXPtr - fStorage < fStorageCount);
189}
190
191int SkRgnBuilder::computeRunCount() const {
192    if (fCurrScanline == NULL) {
193        return 0;
194    }
195
196    const SkRegion::RunType*  line = fStorage;
197    const SkRegion::RunType*  stop = (const SkRegion::RunType*)fCurrScanline;
198
199    return 2 + (int)(stop - line);
200}
201
202void SkRgnBuilder::copyToRect(SkIRect* r) const {
203    SkASSERT(fCurrScanline != NULL);
204    // A rect's scanline is [bottom intervals left right sentinel] == 5
205    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 5);
206
207    const Scanline* line = (const Scanline*)fStorage;
208    SkASSERT(line->fXCount == 2);
209
210    r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
211}
212
213void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
214    SkASSERT(fCurrScanline != NULL);
215    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
216
217    const Scanline* line = (const Scanline*)fStorage;
218    const Scanline* stop = fCurrScanline;
219
220    *runs++ = fTop;
221    do {
222        *runs++ = (SkRegion::RunType)(line->fLastY + 1);
223        int count = line->fXCount;
224        *runs++ = count >> 1;   // intervalCount
225        if (count) {
226            memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
227            runs += count;
228        }
229        *runs++ = SkRegion::kRunTypeSentinel;
230        line = line->nextScanline();
231    } while (line < stop);
232    SkASSERT(line == stop);
233    *runs = SkRegion::kRunTypeSentinel;
234}
235
236static unsigned verb_to_initial_last_index(unsigned verb) {
237    static const uint8_t gPathVerbToInitialLastIndex[] = {
238        0,  //  kMove_Verb
239        1,  //  kLine_Verb
240        2,  //  kQuad_Verb
241        2,  //  kConic_Verb
242        3,  //  kCubic_Verb
243        0,  //  kClose_Verb
244        0   //  kDone_Verb
245    };
246    SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToInitialLastIndex));
247    return gPathVerbToInitialLastIndex[verb];
248}
249
250static unsigned verb_to_max_edges(unsigned verb) {
251    static const uint8_t gPathVerbToMaxEdges[] = {
252        0,  //  kMove_Verb
253        1,  //  kLine_Verb
254        2,  //  kQuad_VerbB
255        2,  //  kConic_VerbB
256        3,  //  kCubic_Verb
257        0,  //  kClose_Verb
258        0   //  kDone_Verb
259    };
260    SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToMaxEdges));
261    return gPathVerbToMaxEdges[verb];
262}
263
264
265static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
266    SkPath::Iter    iter(path, true);
267    SkPoint         pts[4];
268    SkPath::Verb    verb;
269
270    int maxEdges = 0;
271    SkScalar    top = SkIntToScalar(SK_MaxS16);
272    SkScalar    bot = SkIntToScalar(SK_MinS16);
273
274    while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
275        maxEdges += verb_to_max_edges(verb);
276
277        int lastIndex = verb_to_initial_last_index(verb);
278        if (lastIndex > 0) {
279            for (int i = 1; i <= lastIndex; i++) {
280                if (top > pts[i].fY) {
281                    top = pts[i].fY;
282                } else if (bot < pts[i].fY) {
283                    bot = pts[i].fY;
284                }
285            }
286        } else if (SkPath::kMove_Verb == verb) {
287            if (top > pts[0].fY) {
288                top = pts[0].fY;
289            } else if (bot < pts[0].fY) {
290                bot = pts[0].fY;
291            }
292        }
293    }
294    SkASSERT(top <= bot);
295
296    *itop = SkScalarRoundToInt(top);
297    *ibot = SkScalarRoundToInt(bot);
298    return maxEdges;
299}
300
301bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
302    SkDEBUGCODE(this->validate();)
303
304    if (clip.isEmpty()) {
305        return this->setEmpty();
306    }
307
308    if (path.isEmpty()) {
309        if (path.isInverseFillType()) {
310            return this->set(clip);
311        } else {
312            return this->setEmpty();
313        }
314    }
315
316    //  compute worst-case rgn-size for the path
317    int pathTop, pathBot;
318    int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
319    int clipTop, clipBot;
320    int clipTransitions;
321
322    clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
323
324    int top = SkMax32(pathTop, clipTop);
325    int bot = SkMin32(pathBot, clipBot);
326
327    if (top >= bot)
328        return this->setEmpty();
329
330    SkRgnBuilder builder;
331
332    if (!builder.init(bot - top,
333                      SkMax32(pathTransitions, clipTransitions),
334                      path.isInverseFillType())) {
335        // can't allocate working space, so return false
336        return this->setEmpty();
337    }
338
339    SkScan::FillPath(path, clip, &builder);
340    builder.done();
341
342    int count = builder.computeRunCount();
343    if (count == 0) {
344        return this->setEmpty();
345    } else if (count == kRectRegionRuns) {
346        builder.copyToRect(&fBounds);
347        this->setRect(fBounds);
348    } else {
349        SkRegion tmp;
350
351        tmp.fRunHead = RunHead::Alloc(count);
352        builder.copyToRgn(tmp.fRunHead->writable_runs());
353        tmp.fRunHead->computeRunBounds(&tmp.fBounds);
354        this->swap(tmp);
355    }
356    SkDEBUGCODE(this->validate();)
357    return true;
358}
359
360/////////////////////////////////////////////////////////////////////////////////////////////////
361/////////////////////////////////////////////////////////////////////////////////////////////////
362
363struct Edge {
364    enum {
365        kY0Link = 0x01,
366        kY1Link = 0x02,
367
368        kCompleteLink = (kY0Link | kY1Link)
369    };
370
371    SkRegion::RunType fX;
372    SkRegion::RunType fY0, fY1;
373    uint8_t fFlags;
374    Edge*   fNext;
375
376    void set(int x, int y0, int y1) {
377        SkASSERT(y0 != y1);
378
379        fX = (SkRegion::RunType)(x);
380        fY0 = (SkRegion::RunType)(y0);
381        fY1 = (SkRegion::RunType)(y1);
382        fFlags = 0;
383        SkDEBUGCODE(fNext = NULL;)
384    }
385
386    int top() const {
387        return SkFastMin32(fY0, fY1);
388    }
389};
390
391static void find_link(Edge* base, Edge* stop) {
392    SkASSERT(base < stop);
393
394    if (base->fFlags == Edge::kCompleteLink) {
395        SkASSERT(base->fNext);
396        return;
397    }
398
399    SkASSERT(base + 1 < stop);
400
401    int y0 = base->fY0;
402    int y1 = base->fY1;
403
404    Edge* e = base;
405    if ((base->fFlags & Edge::kY0Link) == 0) {
406        for (;;) {
407            e += 1;
408            if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
409                SkASSERT(NULL == e->fNext);
410                e->fNext = base;
411                e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
412                break;
413            }
414        }
415    }
416
417    e = base;
418    if ((base->fFlags & Edge::kY1Link) == 0) {
419        for (;;) {
420            e += 1;
421            if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
422                SkASSERT(NULL == base->fNext);
423                base->fNext = e;
424                e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
425                break;
426            }
427        }
428    }
429
430    base->fFlags = Edge::kCompleteLink;
431}
432
433static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
434    while (0 == edge->fFlags) {
435        edge++; // skip over "used" edges
436    }
437
438    SkASSERT(edge < stop);
439
440    Edge* base = edge;
441    Edge* prev = edge;
442    edge = edge->fNext;
443    SkASSERT(edge != base);
444
445    int count = 1;
446    path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
447    prev->fFlags = 0;
448    do {
449        if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
450            path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
451            path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0));    // H
452        }
453        prev = edge;
454        edge = edge->fNext;
455        count += 1;
456        prev->fFlags = 0;
457    } while (edge != base);
458    path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
459    path->close();
460    return count;
461}
462
463#include "SkTSearch.h"
464
465static int EdgeProc(const Edge* a, const Edge* b) {
466    return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
467}
468
469bool SkRegion::getBoundaryPath(SkPath* path) const {
470    // path could safely be NULL if we're empty, but the caller shouldn't
471    // *know* that
472    SkASSERT(path);
473
474    if (this->isEmpty()) {
475        return false;
476    }
477
478    const SkIRect& bounds = this->getBounds();
479
480    if (this->isRect()) {
481        SkRect  r;
482        r.set(bounds);      // this converts the ints to scalars
483        path->addRect(r);
484        return true;
485    }
486
487    SkRegion::Iterator  iter(*this);
488    SkTDArray<Edge>     edges;
489
490    for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
491        Edge* edge = edges.append(2);
492        edge[0].set(r.fLeft, r.fBottom, r.fTop);
493        edge[1].set(r.fRight, r.fTop, r.fBottom);
494    }
495    qsort(edges.begin(), edges.count(), sizeof(Edge), SkCastForQSort(EdgeProc));
496
497    int count = edges.count();
498    Edge* start = edges.begin();
499    Edge* stop = start + count;
500    Edge* e;
501
502    for (e = start; e != stop; e++) {
503        find_link(e, stop);
504    }
505
506#ifdef SK_DEBUG
507    for (e = start; e != stop; e++) {
508        SkASSERT(e->fNext != NULL);
509        SkASSERT(e->fFlags == Edge::kCompleteLink);
510    }
511#endif
512
513    path->incReserve(count << 1);
514    do {
515        SkASSERT(count > 1);
516        count -= extract_path(start, stop, path);
517    } while (count > 0);
518
519    return true;
520}
521