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