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 "SkBlitter.h"
9 #include "SkPath.h"
10 #include "SkRegionPriv.h"
11 #include "SkSafeMath.h"
12 #include "SkScan.h"
13 #include "SkTDArray.h"
14 #include "SkTSort.h"
15 #include "SkTo.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->set(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, false)) != 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()) {
326 return this->setEmpty();
327 }
328
329 if (path.isEmpty()) {
330 return check_inverse_on_empty_return(this, path, clip);
331 }
332
333 // Our builder is very fragile, and can't be called with spans/rects out of Y->X order.
334 // To ensure this, we only "fill" clipped to a rect (the clip's bounds), and if the
335 // clip is more complex than that, we just post-intersect the result with the clip.
336 if (clip.isComplex()) {
337 if (!this->setPath(path, SkRegion(clip.getBounds()))) {
338 return false;
339 }
340 return this->op(clip, kIntersect_Op);
341 }
342
343 // compute worst-case rgn-size for the path
344 int pathTop, pathBot;
345 int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
346 if (0 == pathTransitions) {
347 return check_inverse_on_empty_return(this, path, clip);
348 }
349
350 int clipTop, clipBot;
351 int clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
352
353 int top = SkMax32(pathTop, clipTop);
354 int bot = SkMin32(pathBot, clipBot);
355 if (top >= bot) {
356 return check_inverse_on_empty_return(this, path, clip);
357 }
358
359 SkRgnBuilder builder;
360
361 if (!builder.init(bot - top,
362 SkMax32(pathTransitions, clipTransitions),
363 path.isInverseFillType())) {
364 // can't allocate working space, so return false
365 return this->setEmpty();
366 }
367
368 SkScan::FillPath(path, clip, &builder);
369 builder.done();
370
371 int count = builder.computeRunCount();
372 if (count == 0) {
373 return this->setEmpty();
374 } else if (count == kRectRegionRuns) {
375 builder.copyToRect(&fBounds);
376 this->setRect(fBounds);
377 } else {
378 SkRegion tmp;
379
380 tmp.fRunHead = RunHead::Alloc(count);
381 builder.copyToRgn(tmp.fRunHead->writable_runs());
382 tmp.fRunHead->computeRunBounds(&tmp.fBounds);
383 this->swap(tmp);
384 }
385 SkDEBUGCODE(SkRegionPriv::Validate(*this));
386 return true;
387 }
388
389 /////////////////////////////////////////////////////////////////////////////////////////////////
390 /////////////////////////////////////////////////////////////////////////////////////////////////
391
392 struct Edge {
393 enum {
394 kY0Link = 0x01,
395 kY1Link = 0x02,
396
397 kCompleteLink = (kY0Link | kY1Link)
398 };
399
400 SkRegionPriv::RunType fX;
401 SkRegionPriv::RunType fY0, fY1;
402 uint8_t fFlags;
403 Edge* fNext;
404
setEdge405 void set(int x, int y0, int y1) {
406 SkASSERT(y0 != y1);
407
408 fX = (SkRegionPriv::RunType)(x);
409 fY0 = (SkRegionPriv::RunType)(y0);
410 fY1 = (SkRegionPriv::RunType)(y1);
411 fFlags = 0;
412 SkDEBUGCODE(fNext = nullptr;)
413 }
414
topEdge415 int top() const {
416 return SkMin32(fY0, fY1);
417 }
418 };
419
find_link(Edge * base,Edge * stop)420 static void find_link(Edge* base, Edge* stop) {
421 SkASSERT(base < stop);
422
423 if (base->fFlags == Edge::kCompleteLink) {
424 SkASSERT(base->fNext);
425 return;
426 }
427
428 SkASSERT(base + 1 < stop);
429
430 int y0 = base->fY0;
431 int y1 = base->fY1;
432
433 Edge* e = base;
434 if ((base->fFlags & Edge::kY0Link) == 0) {
435 for (;;) {
436 e += 1;
437 if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
438 SkASSERT(nullptr == e->fNext);
439 e->fNext = base;
440 e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
441 break;
442 }
443 }
444 }
445
446 e = base;
447 if ((base->fFlags & Edge::kY1Link) == 0) {
448 for (;;) {
449 e += 1;
450 if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
451 SkASSERT(nullptr == base->fNext);
452 base->fNext = e;
453 e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
454 break;
455 }
456 }
457 }
458
459 base->fFlags = Edge::kCompleteLink;
460 }
461
extract_path(Edge * edge,Edge * stop,SkPath * path)462 static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
463 while (0 == edge->fFlags) {
464 edge++; // skip over "used" edges
465 }
466
467 SkASSERT(edge < stop);
468
469 Edge* base = edge;
470 Edge* prev = edge;
471 edge = edge->fNext;
472 SkASSERT(edge != base);
473
474 int count = 1;
475 path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
476 prev->fFlags = 0;
477 do {
478 if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
479 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
480 path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H
481 }
482 prev = edge;
483 edge = edge->fNext;
484 count += 1;
485 prev->fFlags = 0;
486 } while (edge != base);
487 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
488 path->close();
489 return count;
490 }
491
492 struct EdgeLT {
operator ()EdgeLT493 bool operator()(const Edge& a, const Edge& b) const {
494 return (a.fX == b.fX) ? a.top() < b.top() : a.fX < b.fX;
495 }
496 };
497
getBoundaryPath(SkPath * path) const498 bool SkRegion::getBoundaryPath(SkPath* path) const {
499 // path could safely be nullptr if we're empty, but the caller shouldn't
500 // *know* that
501 SkASSERT(path);
502
503 if (this->isEmpty()) {
504 return false;
505 }
506
507 const SkIRect& bounds = this->getBounds();
508
509 if (this->isRect()) {
510 SkRect r;
511 r.set(bounds); // this converts the ints to scalars
512 path->addRect(r);
513 return true;
514 }
515
516 SkRegion::Iterator iter(*this);
517 SkTDArray<Edge> edges;
518
519 for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
520 Edge* edge = edges.append(2);
521 edge[0].set(r.fLeft, r.fBottom, r.fTop);
522 edge[1].set(r.fRight, r.fTop, r.fBottom);
523 }
524
525 int count = edges.count();
526 Edge* start = edges.begin();
527 Edge* stop = start + count;
528 SkTQSort<Edge>(start, stop - 1, EdgeLT());
529
530 Edge* e;
531 for (e = start; e != stop; e++) {
532 find_link(e, stop);
533 }
534
535 #ifdef SK_DEBUG
536 for (e = start; e != stop; e++) {
537 SkASSERT(e->fNext != nullptr);
538 SkASSERT(e->fFlags == Edge::kCompleteLink);
539 }
540 #endif
541
542 path->incReserve(count << 1);
543 do {
544 SkASSERT(count > 1);
545 count -= extract_path(start, stop, path);
546 } while (count > 0);
547
548 return true;
549 }
550