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