blob: 58812eb37fc80b9ffc7d706306ad565fdda5e693 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
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 virtual ~SkRgnBuilder();
rmistry@google.comfbfcd562012-08-23 18:09:54 +000019
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 // returns true if it could allocate the working storage needed
21 bool init(int maxHeight, int maxTransitions);
22
23 void done() {
24 if (fCurrScanline != NULL) {
25 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
26 if (!this->collapsWithPrev()) { // flush the last line
27 fCurrScanline = fCurrScanline->nextScanline();
28 }
29 }
30 }
31
32 int computeRunCount() const;
33 void copyToRect(SkIRect*) const;
34 void copyToRgn(SkRegion::RunType runs[]) const;
35
36 virtual void blitH(int x, int y, int width);
37
38#ifdef SK_DEBUG
39 void dump() const {
40 SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
41 const Scanline* line = (Scanline*)fStorage;
42 while (line < fCurrScanline) {
43 SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
44 for (int i = 0; i < line->fXCount; i++) {
45 SkDebugf(" %d", line->firstX()[i]);
46 }
47 SkDebugf("\n");
48
49 line = line->nextScanline();
50 }
51 }
52#endif
53private:
reed@google.com9c36a762012-05-02 18:07:33 +000054 /*
55 * Scanline mimics a row in the region, nearly. A row in a region is:
56 * [Bottom IntervalCount [L R]... Sentinel]
57 * while a Scanline is
58 * [LastY XCount [L R]... uninitialized]
59 * The two are the same length (which is good), but we have to transmute
60 * the scanline a little when we convert it to a region-row.
61 *
62 * Potentially we could recode this to exactly match the row format, in
63 * which case copyToRgn() could be a single memcpy. Not sure that is worth
64 * the effort.
65 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 struct Scanline {
67 SkRegion::RunType fLastY;
68 SkRegion::RunType fXCount;
69
70 SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
71 Scanline* nextScanline() const {
reed@google.com9c36a762012-05-02 18:07:33 +000072 // add final +1 for the x-sentinel
73 return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 }
75 };
76 SkRegion::RunType* fStorage;
77 Scanline* fCurrScanline;
78 Scanline* fPrevScanline;
79 // points at next avialable x[] in fCurrScanline
80 SkRegion::RunType* fCurrXPtr;
81 SkRegion::RunType fTop; // first Y value
rmistry@google.comfbfcd562012-08-23 18:09:54 +000082
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 int fStorageCount;
84
85 bool collapsWithPrev() {
86 if (fPrevScanline != NULL &&
87 fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
88 fPrevScanline->fXCount == fCurrScanline->fXCount &&
89 !memcmp(fPrevScanline->firstX(),
90 fCurrScanline->firstX(),
91 fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
92 {
93 // update the height of fPrevScanline
94 fPrevScanline->fLastY = fCurrScanline->fLastY;
95 return true;
96 }
97 return false;
98 }
99};
100
101SkRgnBuilder::~SkRgnBuilder() {
102 sk_free(fStorage);
103}
104
105bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
106 if ((maxHeight | maxTransitions) < 0) {
107 return false;
108 }
109
110 Sk64 count, size;
111
112 // compute the count with +1 and +3 slop for the working buffer
113 count.setMul(maxHeight + 1, 3 + maxTransitions);
114 if (!count.is32() || count.isNeg()) {
115 return false;
116 }
117 fStorageCount = count.get32();
118
119 size.setMul(fStorageCount, sizeof(SkRegion::RunType));
120 if (!size.is32() || size.isNeg()) {
121 return false;
122 }
123
124 fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
125 if (NULL == fStorage) {
126 return false;
127 }
128
129 fCurrScanline = NULL; // signal empty collection
130 fPrevScanline = NULL; // signal first scanline
131 return true;
132}
133
134void SkRgnBuilder::blitH(int x, int y, int width) {
135 if (fCurrScanline == NULL) { // first time
136 fTop = (SkRegion::RunType)(y);
137 fCurrScanline = (Scanline*)fStorage;
138 fCurrScanline->fLastY = (SkRegion::RunType)(y);
139 fCurrXPtr = fCurrScanline->firstX();
140 } else {
141 SkASSERT(y >= fCurrScanline->fLastY);
142
143 if (y > fCurrScanline->fLastY) {
144 // if we get here, we're done with fCurrScanline
145 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
146
147 int prevLastY = fCurrScanline->fLastY;
148 if (!this->collapsWithPrev()) {
149 fPrevScanline = fCurrScanline;
150 fCurrScanline = fCurrScanline->nextScanline();
151
152 }
153 if (y - 1 > prevLastY) { // insert empty run
154 fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
155 fCurrScanline->fXCount = 0;
156 fCurrScanline = fCurrScanline->nextScanline();
157 }
158 // setup for the new curr line
159 fCurrScanline->fLastY = (SkRegion::RunType)(y);
160 fCurrXPtr = fCurrScanline->firstX();
161 }
162 }
163 // check if we should extend the current run, or add a new one
164 if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
165 fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
166 } else {
167 fCurrXPtr[0] = (SkRegion::RunType)(x);
168 fCurrXPtr[1] = (SkRegion::RunType)(x + width);
169 fCurrXPtr += 2;
170 }
171 SkASSERT(fCurrXPtr - fStorage < fStorageCount);
172}
173
174int SkRgnBuilder::computeRunCount() const {
175 if (fCurrScanline == NULL) {
176 return 0;
177 }
178
179 const SkRegion::RunType* line = fStorage;
180 const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline;
181
182 return 2 + (int)(stop - line);
183}
184
185void SkRgnBuilder::copyToRect(SkIRect* r) const {
186 SkASSERT(fCurrScanline != NULL);
reed@google.com9c36a762012-05-02 18:07:33 +0000187 // A rect's scanline is [bottom intervals left right sentinel] == 5
188 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 5);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
190 const Scanline* line = (const Scanline*)fStorage;
191 SkASSERT(line->fXCount == 2);
192
193 r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
194}
195
196void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
197 SkASSERT(fCurrScanline != NULL);
198 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
199
200 const Scanline* line = (const Scanline*)fStorage;
201 const Scanline* stop = fCurrScanline;
202
203 *runs++ = fTop;
204 do {
205 *runs++ = (SkRegion::RunType)(line->fLastY + 1);
206 int count = line->fXCount;
reed@google.com9c36a762012-05-02 18:07:33 +0000207 *runs++ = count >> 1; // intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 if (count) {
209 memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
210 runs += count;
211 }
212 *runs++ = SkRegion::kRunTypeSentinel;
213 line = line->nextScanline();
214 } while (line < stop);
215 SkASSERT(line == stop);
216 *runs = SkRegion::kRunTypeSentinel;
217}
218
219static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
220 static const uint8_t gPathVerbToInitialLastIndex[] = {
221 0, // kMove_Verb
222 1, // kLine_Verb
223 2, // kQuad_Verb
224 3, // kCubic_Verb
225 0, // kClose_Verb
226 0 // kDone_Verb
227 };
228
229 static const uint8_t gPathVerbToMaxEdges[] = {
230 0, // kMove_Verb
231 1, // kLine_Verb
232 2, // kQuad_VerbB
233 3, // kCubic_Verb
234 0, // kClose_Verb
235 0 // kDone_Verb
236 };
237
238 SkPath::Iter iter(path, true);
239 SkPoint pts[4];
240 SkPath::Verb verb;
241
242 int maxEdges = 0;
243 SkScalar top = SkIntToScalar(SK_MaxS16);
244 SkScalar bot = SkIntToScalar(SK_MinS16);
245
reed@google.com4a3b7142012-05-16 17:16:46 +0000246 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 maxEdges += gPathVerbToMaxEdges[verb];
248
249 int lastIndex = gPathVerbToInitialLastIndex[verb];
250 if (lastIndex > 0) {
251 for (int i = 1; i <= lastIndex; i++) {
252 if (top > pts[i].fY) {
253 top = pts[i].fY;
254 } else if (bot < pts[i].fY) {
255 bot = pts[i].fY;
256 }
257 }
258 } else if (SkPath::kMove_Verb == verb) {
259 if (top > pts[0].fY) {
260 top = pts[0].fY;
261 } else if (bot < pts[0].fY) {
262 bot = pts[0].fY;
263 }
264 }
265 }
266 SkASSERT(top <= bot);
267
268 *itop = SkScalarRound(top);
269 *ibot = SkScalarRound(bot);
270 return maxEdges;
271}
272
273bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
274 SkDEBUGCODE(this->validate();)
275
276 if (clip.isEmpty()) {
277 return this->setEmpty();
278 }
279
280 if (path.isEmpty()) {
281 if (path.isInverseFillType()) {
282 return this->set(clip);
283 } else {
284 return this->setEmpty();
285 }
286 }
287
288 // compute worst-case rgn-size for the path
289 int pathTop, pathBot;
290 int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
291 int clipTop, clipBot;
292 int clipTransitions;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
295
296 int top = SkMax32(pathTop, clipTop);
297 int bot = SkMin32(pathBot, clipBot);
298
299 if (top >= bot)
300 return this->setEmpty();
301
302 SkRgnBuilder builder;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
305 // can't allocate working space, so return false
306 return this->setEmpty();
307 }
308
309 SkScan::FillPath(path, clip, &builder);
310 builder.done();
311
312 int count = builder.computeRunCount();
313 if (count == 0) {
314 return this->setEmpty();
315 } else if (count == kRectRegionRuns) {
316 builder.copyToRect(&fBounds);
317 this->setRect(fBounds);
318 } else {
reed@google.com9c36a762012-05-02 18:07:33 +0000319 SkRegion tmp;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320
321 tmp.fRunHead = RunHead::Alloc(count);
322 builder.copyToRgn(tmp.fRunHead->writable_runs());
reed@google.com9c36a762012-05-02 18:07:33 +0000323 tmp.fRunHead->computeRunBounds(&tmp.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 this->swap(tmp);
325 }
326 SkDEBUGCODE(this->validate();)
327 return true;
328}
329
330/////////////////////////////////////////////////////////////////////////////////////////////////
331/////////////////////////////////////////////////////////////////////////////////////////////////
332
333struct Edge {
334 enum {
335 kY0Link = 0x01,
336 kY1Link = 0x02,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 kCompleteLink = (kY0Link | kY1Link)
339 };
340
341 SkRegion::RunType fX;
342 SkRegion::RunType fY0, fY1;
343 uint8_t fFlags;
344 Edge* fNext;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000345
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 void set(int x, int y0, int y1) {
347 SkASSERT(y0 != y1);
348
349 fX = (SkRegion::RunType)(x);
350 fY0 = (SkRegion::RunType)(y0);
351 fY1 = (SkRegion::RunType)(y1);
352 fFlags = 0;
353 SkDEBUGCODE(fNext = NULL;)
354 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000355
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 int top() const {
357 return SkFastMin32(fY0, fY1);
358 }
359};
360
361static void find_link(Edge* base, Edge* stop) {
362 SkASSERT(base < stop);
363
364 if (base->fFlags == Edge::kCompleteLink) {
365 SkASSERT(base->fNext);
366 return;
367 }
368
369 SkASSERT(base + 1 < stop);
370
371 int y0 = base->fY0;
372 int y1 = base->fY1;
373
374 Edge* e = base;
375 if ((base->fFlags & Edge::kY0Link) == 0) {
376 for (;;) {
377 e += 1;
378 if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
379 SkASSERT(NULL == e->fNext);
380 e->fNext = base;
381 e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
382 break;
383 }
384 }
385 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000386
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 e = base;
388 if ((base->fFlags & Edge::kY1Link) == 0) {
389 for (;;) {
390 e += 1;
391 if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
392 SkASSERT(NULL == base->fNext);
393 base->fNext = e;
394 e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
395 break;
396 }
397 }
398 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 base->fFlags = Edge::kCompleteLink;
401}
402
403static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
404 while (0 == edge->fFlags) {
405 edge++; // skip over "used" edges
406 }
407
408 SkASSERT(edge < stop);
409
410 Edge* base = edge;
411 Edge* prev = edge;
412 edge = edge->fNext;
413 SkASSERT(edge != base);
414
415 int count = 1;
416 path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
417 prev->fFlags = 0;
418 do {
419 if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
420 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
421 path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H
422 }
423 prev = edge;
424 edge = edge->fNext;
425 count += 1;
426 prev->fFlags = 0;
427 } while (edge != base);
428 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
429 path->close();
430 return count;
431}
432
433#include "SkTSearch.h"
434
435static int EdgeProc(const Edge* a, const Edge* b) {
436 return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
437}
438
439bool SkRegion::getBoundaryPath(SkPath* path) const {
tomhudson@google.comd5d9dad2012-01-03 14:52:37 +0000440 // path could safely be NULL if we're empty, but the caller shouldn't
441 // *know* that
442 SkASSERT(path);
443
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 if (this->isEmpty()) {
445 return false;
446 }
447
448 const SkIRect& bounds = this->getBounds();
449
450 if (this->isRect()) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000451 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 r.set(bounds); // this converts the ints to scalars
453 path->addRect(r);
454 return true;
455 }
456
457 SkRegion::Iterator iter(*this);
458 SkTDArray<Edge> edges;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
461 Edge* edge = edges.append(2);
462 edge[0].set(r.fLeft, r.fBottom, r.fTop);
463 edge[1].set(r.fRight, r.fTop, r.fBottom);
464 }
reed@google.comc7a67cb2012-05-07 14:52:12 +0000465 qsort(edges.begin(), edges.count(), sizeof(Edge), SkCastForQSort(EdgeProc));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 int count = edges.count();
468 Edge* start = edges.begin();
469 Edge* stop = start + count;
470 Edge* e;
471
472 for (e = start; e != stop; e++) {
473 find_link(e, stop);
474 }
475
476#ifdef SK_DEBUG
477 for (e = start; e != stop; e++) {
478 SkASSERT(e->fNext != NULL);
479 SkASSERT(e->fFlags == Edge::kCompleteLink);
480 }
481#endif
482
483 path->incReserve(count << 1);
484 do {
485 SkASSERT(count > 1);
486 count -= extract_path(start, stop, path);
487 } while (count > 0);
488
489 return true;
490}
491