blob: 9289641c69d5eab494c37d78f5ba2726e13c1114 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkRegionPriv.h"
9#include "SkBlitter.h"
10#include "SkScan.h"
bungeman60e0fee2015-08-26 05:15:46 -070011#include "SkTSort.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkTDArray.h"
13#include "SkPath.h"
14
reedb8f07982014-06-20 13:59:15 -070015// The rgnbuilder caller *seems* to pass short counts, possible often seens early failure, so
16// we may not want to promote this to a "std" routine just yet.
17static bool sk_memeq32(const int32_t* SK_RESTRICT a, const int32_t* SK_RESTRICT b, int count) {
18 for (int i = 0; i < count; ++i) {
19 if (a[i] != b[i]) {
20 return false;
21 }
22 }
23 return true;
24}
25
reed@android.com8a1c16f2008-12-17 15:59:43 +000026class SkRgnBuilder : public SkBlitter {
27public:
commit-bot@chromium.org73be1fc2013-12-30 16:21:06 +000028 SkRgnBuilder();
reed@android.com8a1c16f2008-12-17 15:59:43 +000029 virtual ~SkRgnBuilder();
rmistry@google.comfbfcd562012-08-23 18:09:54 +000030
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 // returns true if it could allocate the working storage needed
reed@google.comb58ba892013-10-15 15:44:35 +000032 bool init(int maxHeight, int maxTransitions, bool pathIsInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
34 void done() {
halcanary96fcdcc2015-08-27 07:41:13 -070035 if (fCurrScanline != nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
37 if (!this->collapsWithPrev()) { // flush the last line
38 fCurrScanline = fCurrScanline->nextScanline();
39 }
40 }
41 }
42
43 int computeRunCount() const;
44 void copyToRect(SkIRect*) const;
45 void copyToRgn(SkRegion::RunType runs[]) const;
46
mtklein36352bf2015-03-25 18:17:31 -070047 void blitH(int x, int y, int width) override;
reed@android.com8a1c16f2008-12-17 15:59:43 +000048
49#ifdef SK_DEBUG
50 void dump() const {
51 SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
52 const Scanline* line = (Scanline*)fStorage;
53 while (line < fCurrScanline) {
54 SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
55 for (int i = 0; i < line->fXCount; i++) {
56 SkDebugf(" %d", line->firstX()[i]);
57 }
58 SkDebugf("\n");
59
60 line = line->nextScanline();
61 }
62 }
63#endif
64private:
reed@google.com9c36a762012-05-02 18:07:33 +000065 /*
66 * Scanline mimics a row in the region, nearly. A row in a region is:
67 * [Bottom IntervalCount [L R]... Sentinel]
68 * while a Scanline is
69 * [LastY XCount [L R]... uninitialized]
70 * The two are the same length (which is good), but we have to transmute
71 * the scanline a little when we convert it to a region-row.
72 *
73 * Potentially we could recode this to exactly match the row format, in
74 * which case copyToRgn() could be a single memcpy. Not sure that is worth
75 * the effort.
76 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 struct Scanline {
78 SkRegion::RunType fLastY;
79 SkRegion::RunType fXCount;
80
81 SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
82 Scanline* nextScanline() const {
reed@google.com9c36a762012-05-02 18:07:33 +000083 // add final +1 for the x-sentinel
84 return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 }
86 };
87 SkRegion::RunType* fStorage;
88 Scanline* fCurrScanline;
89 Scanline* fPrevScanline;
90 // points at next avialable x[] in fCurrScanline
91 SkRegion::RunType* fCurrXPtr;
92 SkRegion::RunType fTop; // first Y value
rmistry@google.comfbfcd562012-08-23 18:09:54 +000093
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 int fStorageCount;
95
96 bool collapsWithPrev() {
halcanary96fcdcc2015-08-27 07:41:13 -070097 if (fPrevScanline != nullptr &&
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
99 fPrevScanline->fXCount == fCurrScanline->fXCount &&
reedb8f07982014-06-20 13:59:15 -0700100 sk_memeq32(fPrevScanline->firstX(), fCurrScanline->firstX(), fCurrScanline->fXCount))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 {
102 // update the height of fPrevScanline
103 fPrevScanline->fLastY = fCurrScanline->fLastY;
104 return true;
105 }
106 return false;
107 }
108};
109
commit-bot@chromium.org73be1fc2013-12-30 16:21:06 +0000110SkRgnBuilder::SkRgnBuilder()
halcanary96fcdcc2015-08-27 07:41:13 -0700111 : fStorage(nullptr) {
commit-bot@chromium.org73be1fc2013-12-30 16:21:06 +0000112}
113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114SkRgnBuilder::~SkRgnBuilder() {
115 sk_free(fStorage);
116}
117
reed@google.comb58ba892013-10-15 15:44:35 +0000118bool SkRgnBuilder::init(int maxHeight, int maxTransitions, bool pathIsInverse) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 if ((maxHeight | maxTransitions) < 0) {
120 return false;
121 }
122
reed@google.comb58ba892013-10-15 15:44:35 +0000123 if (pathIsInverse) {
124 // allow for additional X transitions to "invert" each scanline
125 // [ L' ... normal transitions ... R' ]
126 //
127 maxTransitions += 2;
128 }
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 // compute the count with +1 and +3 slop for the working buffer
reed@google.com57212f92013-12-30 14:40:38 +0000131 int64_t count = sk_64_mul(maxHeight + 1, 3 + maxTransitions);
reed@google.comb58ba892013-10-15 15:44:35 +0000132
133 if (pathIsInverse) {
134 // allow for two "empty" rows for the top and bottom
135 // [ Y, 1, L, R, S] == 5 (*2 for top and bottom)
reed@google.com57212f92013-12-30 14:40:38 +0000136 count += 10;
reed@google.comb58ba892013-10-15 15:44:35 +0000137 }
138
reed@google.com57212f92013-12-30 14:40:38 +0000139 if (count < 0 || !sk_64_isS32(count)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 return false;
141 }
reed@google.com57212f92013-12-30 14:40:38 +0000142 fStorageCount = sk_64_asS32(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143
reed@google.com57212f92013-12-30 14:40:38 +0000144 int64_t size = sk_64_mul(fStorageCount, sizeof(SkRegion::RunType));
145 if (size < 0 || !sk_64_isS32(size)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 return false;
147 }
skia.committer@gmail.com47262912013-10-16 07:02:24 +0000148
reed@google.com57212f92013-12-30 14:40:38 +0000149 fStorage = (SkRegion::RunType*)sk_malloc_flags(sk_64_asS32(size), 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700150 if (nullptr == fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 return false;
152 }
153
halcanary96fcdcc2015-08-27 07:41:13 -0700154 fCurrScanline = nullptr; // signal empty collection
155 fPrevScanline = nullptr; // signal first scanline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 return true;
157}
158
159void SkRgnBuilder::blitH(int x, int y, int width) {
halcanary96fcdcc2015-08-27 07:41:13 -0700160 if (fCurrScanline == nullptr) { // first time
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 fTop = (SkRegion::RunType)(y);
162 fCurrScanline = (Scanline*)fStorage;
163 fCurrScanline->fLastY = (SkRegion::RunType)(y);
164 fCurrXPtr = fCurrScanline->firstX();
165 } else {
166 SkASSERT(y >= fCurrScanline->fLastY);
167
168 if (y > fCurrScanline->fLastY) {
169 // if we get here, we're done with fCurrScanline
170 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
171
172 int prevLastY = fCurrScanline->fLastY;
173 if (!this->collapsWithPrev()) {
174 fPrevScanline = fCurrScanline;
175 fCurrScanline = fCurrScanline->nextScanline();
176
177 }
178 if (y - 1 > prevLastY) { // insert empty run
179 fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
180 fCurrScanline->fXCount = 0;
181 fCurrScanline = fCurrScanline->nextScanline();
182 }
183 // setup for the new curr line
184 fCurrScanline->fLastY = (SkRegion::RunType)(y);
185 fCurrXPtr = fCurrScanline->firstX();
186 }
187 }
188 // check if we should extend the current run, or add a new one
189 if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
190 fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
191 } else {
192 fCurrXPtr[0] = (SkRegion::RunType)(x);
193 fCurrXPtr[1] = (SkRegion::RunType)(x + width);
194 fCurrXPtr += 2;
195 }
196 SkASSERT(fCurrXPtr - fStorage < fStorageCount);
197}
198
199int SkRgnBuilder::computeRunCount() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700200 if (fCurrScanline == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 return 0;
202 }
203
204 const SkRegion::RunType* line = fStorage;
205 const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline;
206
207 return 2 + (int)(stop - line);
208}
209
210void SkRgnBuilder::copyToRect(SkIRect* r) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700211 SkASSERT(fCurrScanline != nullptr);
reed@google.com9c36a762012-05-02 18:07:33 +0000212 // A rect's scanline is [bottom intervals left right sentinel] == 5
213 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 5);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214
215 const Scanline* line = (const Scanline*)fStorage;
216 SkASSERT(line->fXCount == 2);
217
218 r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
219}
220
221void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700222 SkASSERT(fCurrScanline != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
224
225 const Scanline* line = (const Scanline*)fStorage;
226 const Scanline* stop = fCurrScanline;
227
228 *runs++ = fTop;
229 do {
230 *runs++ = (SkRegion::RunType)(line->fLastY + 1);
231 int count = line->fXCount;
reed@google.com9c36a762012-05-02 18:07:33 +0000232 *runs++ = count >> 1; // intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 if (count) {
234 memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
235 runs += count;
236 }
237 *runs++ = SkRegion::kRunTypeSentinel;
238 line = line->nextScanline();
239 } while (line < stop);
240 SkASSERT(line == stop);
241 *runs = SkRegion::kRunTypeSentinel;
242}
243
reed@google.com277c3f82013-05-31 15:17:50 +0000244static unsigned verb_to_initial_last_index(unsigned verb) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 static const uint8_t gPathVerbToInitialLastIndex[] = {
246 0, // kMove_Verb
247 1, // kLine_Verb
248 2, // kQuad_Verb
reed@google.com277c3f82013-05-31 15:17:50 +0000249 2, // kConic_Verb
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 3, // kCubic_Verb
251 0, // kClose_Verb
252 0 // kDone_Verb
253 };
reed@google.com277c3f82013-05-31 15:17:50 +0000254 SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToInitialLastIndex));
255 return gPathVerbToInitialLastIndex[verb];
256}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257
reed@google.com277c3f82013-05-31 15:17:50 +0000258static unsigned verb_to_max_edges(unsigned verb) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 static const uint8_t gPathVerbToMaxEdges[] = {
260 0, // kMove_Verb
261 1, // kLine_Verb
262 2, // kQuad_VerbB
reed@google.com277c3f82013-05-31 15:17:50 +0000263 2, // kConic_VerbB
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 3, // kCubic_Verb
265 0, // kClose_Verb
266 0 // kDone_Verb
267 };
reed@google.com277c3f82013-05-31 15:17:50 +0000268 SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToMaxEdges));
269 return gPathVerbToMaxEdges[verb];
270}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271
reedc1b11f12015-03-13 08:48:26 -0700272// If returns 0, ignore itop and ibot
reed@google.com277c3f82013-05-31 15:17:50 +0000273static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 SkPath::Iter iter(path, true);
275 SkPoint pts[4];
276 SkPath::Verb verb;
277
278 int maxEdges = 0;
279 SkScalar top = SkIntToScalar(SK_MaxS16);
280 SkScalar bot = SkIntToScalar(SK_MinS16);
281
reed@google.com4a3b7142012-05-16 17:16:46 +0000282 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +0000283 maxEdges += verb_to_max_edges(verb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
reed@google.com277c3f82013-05-31 15:17:50 +0000285 int lastIndex = verb_to_initial_last_index(verb);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 if (lastIndex > 0) {
287 for (int i = 1; i <= lastIndex; i++) {
288 if (top > pts[i].fY) {
289 top = pts[i].fY;
290 } else if (bot < pts[i].fY) {
291 bot = pts[i].fY;
292 }
293 }
294 } else if (SkPath::kMove_Verb == verb) {
295 if (top > pts[0].fY) {
296 top = pts[0].fY;
297 } else if (bot < pts[0].fY) {
298 bot = pts[0].fY;
299 }
300 }
301 }
reedc1b11f12015-03-13 08:48:26 -0700302 if (0 == maxEdges) {
303 return 0; // we have only moves+closes
304 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
reedc1b11f12015-03-13 08:48:26 -0700306 SkASSERT(top <= bot);
reed@google.come1ca7052013-12-17 19:22:07 +0000307 *itop = SkScalarRoundToInt(top);
308 *ibot = SkScalarRoundToInt(bot);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 return maxEdges;
310}
311
reedc1b11f12015-03-13 08:48:26 -0700312static bool check_inverse_on_empty_return(SkRegion* dst, const SkPath& path, const SkRegion& clip) {
313 if (path.isInverseFillType()) {
314 return dst->set(clip);
315 } else {
316 return dst->setEmpty();
317 }
318}
319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
321 SkDEBUGCODE(this->validate();)
322
323 if (clip.isEmpty()) {
324 return this->setEmpty();
325 }
326
327 if (path.isEmpty()) {
reedc1b11f12015-03-13 08:48:26 -0700328 return check_inverse_on_empty_return(this, path, clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 }
330
331 // compute worst-case rgn-size for the path
332 int pathTop, pathBot;
333 int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
reedc1b11f12015-03-13 08:48:26 -0700334 if (0 == pathTransitions) {
335 return check_inverse_on_empty_return(this, path, clip);
336 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337
reedc1b11f12015-03-13 08:48:26 -0700338 int clipTop, clipBot;
339 int clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340
341 int top = SkMax32(pathTop, clipTop);
342 int bot = SkMin32(pathBot, clipBot);
reedc1b11f12015-03-13 08:48:26 -0700343 if (top >= bot) {
344 return check_inverse_on_empty_return(this, path, clip);
345 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
347 SkRgnBuilder builder;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000348
reed@google.comb58ba892013-10-15 15:44:35 +0000349 if (!builder.init(bot - top,
350 SkMax32(pathTransitions, clipTransitions),
351 path.isInverseFillType())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 // can't allocate working space, so return false
353 return this->setEmpty();
354 }
355
356 SkScan::FillPath(path, clip, &builder);
357 builder.done();
358
359 int count = builder.computeRunCount();
360 if (count == 0) {
361 return this->setEmpty();
362 } else if (count == kRectRegionRuns) {
363 builder.copyToRect(&fBounds);
364 this->setRect(fBounds);
365 } else {
reed@google.com9c36a762012-05-02 18:07:33 +0000366 SkRegion tmp;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367
368 tmp.fRunHead = RunHead::Alloc(count);
369 builder.copyToRgn(tmp.fRunHead->writable_runs());
reed@google.com9c36a762012-05-02 18:07:33 +0000370 tmp.fRunHead->computeRunBounds(&tmp.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 this->swap(tmp);
372 }
373 SkDEBUGCODE(this->validate();)
374 return true;
375}
376
377/////////////////////////////////////////////////////////////////////////////////////////////////
378/////////////////////////////////////////////////////////////////////////////////////////////////
379
380struct Edge {
381 enum {
382 kY0Link = 0x01,
383 kY1Link = 0x02,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 kCompleteLink = (kY0Link | kY1Link)
386 };
387
388 SkRegion::RunType fX;
389 SkRegion::RunType fY0, fY1;
390 uint8_t fFlags;
391 Edge* fNext;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 void set(int x, int y0, int y1) {
394 SkASSERT(y0 != y1);
395
396 fX = (SkRegion::RunType)(x);
397 fY0 = (SkRegion::RunType)(y0);
398 fY1 = (SkRegion::RunType)(y1);
399 fFlags = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700400 SkDEBUGCODE(fNext = nullptr;)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 int top() const {
404 return SkFastMin32(fY0, fY1);
405 }
406};
407
408static void find_link(Edge* base, Edge* stop) {
409 SkASSERT(base < stop);
410
411 if (base->fFlags == Edge::kCompleteLink) {
412 SkASSERT(base->fNext);
413 return;
414 }
415
416 SkASSERT(base + 1 < stop);
417
418 int y0 = base->fY0;
419 int y1 = base->fY1;
420
421 Edge* e = base;
422 if ((base->fFlags & Edge::kY0Link) == 0) {
423 for (;;) {
424 e += 1;
425 if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
halcanary96fcdcc2015-08-27 07:41:13 -0700426 SkASSERT(nullptr == e->fNext);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 e->fNext = base;
428 e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
429 break;
430 }
431 }
432 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 e = base;
435 if ((base->fFlags & Edge::kY1Link) == 0) {
436 for (;;) {
437 e += 1;
438 if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700439 SkASSERT(nullptr == base->fNext);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 base->fNext = e;
441 e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
442 break;
443 }
444 }
445 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 base->fFlags = Edge::kCompleteLink;
448}
449
450static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
451 while (0 == edge->fFlags) {
452 edge++; // skip over "used" edges
453 }
454
455 SkASSERT(edge < stop);
456
457 Edge* base = edge;
458 Edge* prev = edge;
459 edge = edge->fNext;
460 SkASSERT(edge != base);
461
462 int count = 1;
463 path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
464 prev->fFlags = 0;
465 do {
466 if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
467 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
468 path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H
469 }
470 prev = edge;
471 edge = edge->fNext;
472 count += 1;
473 prev->fFlags = 0;
474 } while (edge != base);
475 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
476 path->close();
477 return count;
478}
479
bungeman60e0fee2015-08-26 05:15:46 -0700480struct EdgeLT {
481 bool operator()(const Edge& a, const Edge& b) const {
482 return (a.fX == b.fX) ? a.top() < b.top() : a.fX < b.fX;
483 }
484};
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485
486bool SkRegion::getBoundaryPath(SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700487 // path could safely be nullptr if we're empty, but the caller shouldn't
tomhudson@google.comd5d9dad2012-01-03 14:52:37 +0000488 // *know* that
489 SkASSERT(path);
490
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 if (this->isEmpty()) {
492 return false;
493 }
494
495 const SkIRect& bounds = this->getBounds();
496
497 if (this->isRect()) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000498 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 r.set(bounds); // this converts the ints to scalars
500 path->addRect(r);
501 return true;
502 }
503
504 SkRegion::Iterator iter(*this);
505 SkTDArray<Edge> edges;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
508 Edge* edge = edges.append(2);
509 edge[0].set(r.fLeft, r.fBottom, r.fTop);
510 edge[1].set(r.fRight, r.fTop, r.fBottom);
511 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000512
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 int count = edges.count();
514 Edge* start = edges.begin();
515 Edge* stop = start + count;
bungeman60e0fee2015-08-26 05:15:46 -0700516 SkTQSort<Edge>(start, stop - 1, EdgeLT());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517
bungeman60e0fee2015-08-26 05:15:46 -0700518 Edge* e;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 for (e = start; e != stop; e++) {
520 find_link(e, stop);
521 }
522
523#ifdef SK_DEBUG
524 for (e = start; e != stop; e++) {
halcanary96fcdcc2015-08-27 07:41:13 -0700525 SkASSERT(e->fNext != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 SkASSERT(e->fFlags == Edge::kCompleteLink);
527 }
528#endif
529
530 path->incReserve(count << 1);
531 do {
532 SkASSERT(count > 1);
533 count -= extract_path(start, stop, path);
534 } while (count > 0);
535
536 return true;
537}