blob: ca120236640df2b96b05fb9f779c1a56982272a5 [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 "SkTemplates.h"
12#include "SkThread.h"
reed@google.com9c36a762012-05-02 18:07:33 +000013#include "SkUtils.h"
14
15/* Region Layout
16 *
17 * TOP
18 *
19 * [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
20 * ...
21 *
22 * Y-Sentinel
23 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
25SkDEBUGCODE(int32_t gRgnAllocCounter;)
26
27/////////////////////////////////////////////////////////////////////////////////////////////////
28
reed@google.com9c36a762012-05-02 18:07:33 +000029/* Pass in the beginning with the intervals.
30 * We back up 1 to read the interval-count.
31 * Return the beginning of the next scanline (i.e. the next Y-value)
32 */
33static SkRegion::RunType* skip_intervals(const SkRegion::RunType runs[]) {
34 int intervals = runs[-1];
35#ifdef SK_DEBUG
36 if (intervals > 0) {
37 SkASSERT(runs[0] < runs[1]);
38 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
39 } else {
40 SkASSERT(0 == intervals);
41 SkASSERT(SkRegion::kRunTypeSentinel == runs[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 }
reed@google.com9c36a762012-05-02 18:07:33 +000043#endif
44 runs += intervals * 2 + 1;
45 return const_cast<SkRegion::RunType*>(runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +000046}
47
reed@google.com9c36a762012-05-02 18:07:33 +000048bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
49 SkIRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 assert_sentinel(runs[0], false); // top
reed@google.com9c36a762012-05-02 18:07:33 +000051 SkASSERT(count >= kRectRegionRuns);
reed@android.com8a1c16f2008-12-17 15:59:43 +000052
reed@google.comc34f53d2012-04-30 15:10:13 +000053 if (count == kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +000055 SkASSERT(1 == runs[2]);
56 assert_sentinel(runs[3], false); // left
57 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 assert_sentinel(runs[5], true);
reed@google.com9c36a762012-05-02 18:07:33 +000059 assert_sentinel(runs[6], true);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000060
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 SkASSERT(runs[0] < runs[1]); // valid height
reed@google.com9c36a762012-05-02 18:07:33 +000062 SkASSERT(runs[3] < runs[4]); // valid width
rmistry@google.comfbfcd562012-08-23 18:09:54 +000063
reed@google.com9c36a762012-05-02 18:07:33 +000064 bounds->set(runs[3], runs[0], runs[4], runs[1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 return true;
66 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 return false;
68}
69
70//////////////////////////////////////////////////////////////////////////
71
reed@google.com0a0a2362011-03-23 13:51:55 +000072SkRegion::SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 fBounds.set(0, 0, 0, 0);
74 fRunHead = SkRegion_gEmptyRunHeadPtr;
75}
76
reed@google.com0a0a2362011-03-23 13:51:55 +000077SkRegion::SkRegion(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
79 this->setRegion(src);
80}
81
reed@google.com0a0a2362011-03-23 13:51:55 +000082SkRegion::SkRegion(const SkIRect& rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
84 this->setRect(rect);
85}
86
reed@google.com0a0a2362011-03-23 13:51:55 +000087SkRegion::~SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 this->freeRuns();
89}
90
reed@google.com0a0a2362011-03-23 13:51:55 +000091void SkRegion::freeRuns() {
bungeman@google.comb77b69f2013-01-24 16:38:23 +000092 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 SkASSERT(fRunHead->fRefCnt >= 1);
reed@google.com0a0a2362011-03-23 13:51:55 +000094 if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 //SkASSERT(gRgnAllocCounter > 0);
96 //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
97 //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
98 sk_free(fRunHead);
99 }
100 }
101}
102
reed@google.comaf7e6942012-05-01 14:43:22 +0000103void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
104 fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
105}
106
reed@google.com9c36a762012-05-02 18:07:33 +0000107void SkRegion::allocateRuns(int count) {
108 fRunHead = RunHead::Alloc(count);
109}
110
reed@google.comaf7e6942012-05-01 14:43:22 +0000111void SkRegion::allocateRuns(const RunHead& head) {
112 fRunHead = RunHead::Alloc(head.fRunCount,
113 head.getYSpanCount(),
114 head.getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115}
116
reed@google.com0a0a2362011-03-23 13:51:55 +0000117SkRegion& SkRegion::operator=(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 (void)this->setRegion(src);
119 return *this;
120}
121
reed@google.com0a0a2362011-03-23 13:51:55 +0000122void SkRegion::swap(SkRegion& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 SkTSwap<SkIRect>(fBounds, other.fBounds);
124 SkTSwap<RunHead*>(fRunHead, other.fRunHead);
125}
126
commit-bot@chromium.org5dd567c2013-07-17 21:39:28 +0000127int SkRegion::computeRegionComplexity() const {
128 if (this->isEmpty()) {
129 return 0;
130 } else if (this->isRect()) {
131 return 1;
132 }
133 return fRunHead->getIntervalCount();
134}
skia.committer@gmail.comf7d01ce2013-07-18 07:00:56 +0000135
reed@google.com0a0a2362011-03-23 13:51:55 +0000136bool SkRegion::setEmpty() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 this->freeRuns();
138 fBounds.set(0, 0, 0, 0);
139 fRunHead = SkRegion_gEmptyRunHeadPtr;
140 return false;
141}
142
reed@google.com0a0a2362011-03-23 13:51:55 +0000143bool SkRegion::setRect(int32_t left, int32_t top,
144 int32_t right, int32_t bottom) {
145 if (left >= right || top >= bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 return this->setEmpty();
reed@google.com0a0a2362011-03-23 13:51:55 +0000147 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 this->freeRuns();
149 fBounds.set(left, top, right, bottom);
150 fRunHead = SkRegion_gRectRunHeadPtr;
151 return true;
152}
153
reed@google.com0a0a2362011-03-23 13:51:55 +0000154bool SkRegion::setRect(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
156}
157
reed@google.com0a0a2362011-03-23 13:51:55 +0000158bool SkRegion::setRegion(const SkRegion& src) {
159 if (this != &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 this->freeRuns();
161
162 fBounds = src.fBounds;
163 fRunHead = src.fRunHead;
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000164 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 sk_atomic_inc(&fRunHead->fRefCnt);
reed@google.com0a0a2362011-03-23 13:51:55 +0000166 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 }
168 return fRunHead != SkRegion_gEmptyRunHeadPtr;
169}
170
reed@google.com0a0a2362011-03-23 13:51:55 +0000171bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 SkRegion tmp(rect);
173
174 return this->op(tmp, rgn, op);
175}
176
reed@google.com0a0a2362011-03-23 13:51:55 +0000177bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 SkRegion tmp(rect);
179
180 return this->op(rgn, tmp, op);
181}
182
reed@google.com0a0a2362011-03-23 13:51:55 +0000183///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
djsollen@google.com56c69772011-11-08 19:00:26 +0000185#ifdef SK_BUILD_FOR_ANDROID
reed@google.comc34f53d2012-04-30 15:10:13 +0000186char* SkRegion::toString() {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000187 Iterator iter(*this);
188 int count = 0;
189 while (!iter.done()) {
190 count++;
191 iter.next();
192 }
193 // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
194 const int max = (count*((11*4)+5))+11+1;
195 char* result = (char*)malloc(max);
196 if (result == NULL) {
197 return NULL;
198 }
199 count = sprintf(result, "SkRegion(");
200 iter.reset(*this);
201 while (!iter.done()) {
202 const SkIRect& r = iter.rect();
203 count += sprintf(result+count, "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
204 iter.next();
205 }
206 count += sprintf(result+count, ")");
207 return result;
208}
209#endif
210
reed@google.comc34f53d2012-04-30 15:10:13 +0000211///////////////////////////////////////////////////////////////////////////////
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000212
reed@google.comc34f53d2012-04-30 15:10:13 +0000213int SkRegion::count_runtype_values(int* itop, int* ibot) const {
214 if (this == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 *itop = SK_MinS32;
216 *ibot = SK_MaxS32;
217 return 0;
218 }
219
220 int maxT;
221
reed@google.comc34f53d2012-04-30 15:10:13 +0000222 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 maxT = 2;
reed@google.comc34f53d2012-04-30 15:10:13 +0000224 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 SkASSERT(this->isComplex());
reed@google.comaf7e6942012-05-01 14:43:22 +0000226 maxT = fRunHead->getIntervalCount() * 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 }
228 *itop = fBounds.fTop;
229 *ibot = fBounds.fBottom;
230 return maxT;
231}
232
reed@google.com7d4aee32012-04-30 13:29:02 +0000233static bool isRunCountEmpty(int count) {
234 return count <= 2;
235}
236
reed@google.comc34f53d2012-04-30 15:10:13 +0000237bool SkRegion::setRuns(RunType runs[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 SkDEBUGCODE(this->validate();)
239 SkASSERT(count > 0);
240
reed@google.comc34f53d2012-04-30 15:10:13 +0000241 if (isRunCountEmpty(count)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 // SkDEBUGF(("setRuns: empty\n"));
243 assert_sentinel(runs[count-1], true);
244 return this->setEmpty();
245 }
246
247 // trim off any empty spans from the top and bottom
248 // weird I should need this, perhaps op() could be smarter...
reed@google.comc34f53d2012-04-30 15:10:13 +0000249 if (count > kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 RunType* stop = runs + count;
251 assert_sentinel(runs[0], false); // top
252 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +0000253 // runs[2] is uncomputed intervalCount
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000254
reed@google.com9c36a762012-05-02 18:07:33 +0000255 if (runs[3] == SkRegion::kRunTypeSentinel) { // should be first left...
256 runs += 3; // skip empty initial span
257 runs[0] = runs[-2]; // set new top to prev bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
reed@google.com9c36a762012-05-02 18:07:33 +0000259 assert_sentinel(runs[2], false); // intervalcount
260 assert_sentinel(runs[3], false); // left
261 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 }
263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 assert_sentinel(stop[-1], true);
265 assert_sentinel(stop[-2], true);
reed@google.com9c36a762012-05-02 18:07:33 +0000266
267 // now check for a trailing empty span
268 if (stop[-5] == SkRegion::kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
269 stop[-4] = SkRegion::kRunTypeSentinel; // kill empty last span
270 stop -= 3;
271 assert_sentinel(stop[-1], true); // last y-sentinel
272 assert_sentinel(stop[-2], true); // last x-sentinel
273 assert_sentinel(stop[-3], false); // last right
274 assert_sentinel(stop[-4], false); // last left
275 assert_sentinel(stop[-5], false); // last interval-count
276 assert_sentinel(stop[-6], false); // last bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 }
278 count = (int)(stop - runs);
279 }
280
281 SkASSERT(count >= kRectRegionRuns);
282
reed@google.com9c36a762012-05-02 18:07:33 +0000283 if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 return this->setRect(fBounds);
285 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 // if we get here, we need to become a complex region
288
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000289 if (!this->isComplex() || fRunHead->fRunCount != count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 this->freeRuns();
reed@google.com9c36a762012-05-02 18:07:33 +0000291 this->allocateRuns(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 // must call this before we can write directly into runs()
295 // in case we are sharing the buffer with another region (copy on write)
296 fRunHead = fRunHead->ensureWritable();
297 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
reed@google.com9c36a762012-05-02 18:07:33 +0000298 fRunHead->computeRunBounds(&fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299
300 SkDEBUGCODE(this->validate();)
301
302 return true;
303}
304
305void SkRegion::BuildRectRuns(const SkIRect& bounds,
reed@google.comc34f53d2012-04-30 15:10:13 +0000306 RunType runs[kRectRegionRuns]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 runs[0] = bounds.fTop;
308 runs[1] = bounds.fBottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000309 runs[2] = 1; // 1 interval for this scanline
310 runs[3] = bounds.fLeft;
311 runs[4] = bounds.fRight;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 runs[5] = kRunTypeSentinel;
reed@google.com9c36a762012-05-02 18:07:33 +0000313 runs[6] = kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314}
315
reed@google.comc34f53d2012-04-30 15:10:13 +0000316bool SkRegion::contains(int32_t x, int32_t y) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000317 SkDEBUGCODE(this->validate();)
318
reed@google.comc34f53d2012-04-30 15:10:13 +0000319 if (!fBounds.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000321 }
322 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000326
reed@google.com9c36a762012-05-02 18:07:33 +0000327 const RunType* runs = fRunHead->findScanline(y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328
reed@google.com9c36a762012-05-02 18:07:33 +0000329 // Skip the Bottom and IntervalCount
330 runs += 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000331
reed@google.com9c36a762012-05-02 18:07:33 +0000332 // Just walk this scanline, checking each interval. The X-sentinel will
333 // appear as a left-inteval (runs[0]) and should abort the search.
334 //
335 // We could do a bsearch, using interval-count (runs[1]), but need to time
336 // when that would be worthwhile.
337 //
338 for (;;) {
339 if (x < runs[0]) {
340 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 }
reed@google.com9c36a762012-05-02 18:07:33 +0000342 if (x < runs[1]) {
343 return true;
344 }
345 runs += 2;
346 }
347 return false;
348}
349
mike@reedtribe.org796a1752012-11-07 03:39:46 +0000350static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
351 return runs[0];
352}
353
reed@google.com9c36a762012-05-02 18:07:33 +0000354static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
355 // skip [B N [L R]... S]
356 return runs + 2 + runs[1] * 2 + 1;
357}
358
359static bool scanline_contains(const SkRegion::RunType runs[],
360 SkRegion::RunType L, SkRegion::RunType R) {
361 runs += 2; // skip Bottom and IntervalCount
362 for (;;) {
363 if (L < runs[0]) {
364 break;
365 }
366 if (R <= runs[1]) {
367 return true;
368 }
369 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 }
371 return false;
372}
373
reed@google.comc34f53d2012-04-30 15:10:13 +0000374bool SkRegion::contains(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000375 SkDEBUGCODE(this->validate();)
376
377 if (!fBounds.contains(r)) {
378 return false;
379 }
380 if (this->isRect()) {
381 return true;
382 }
reed@google.com9c36a762012-05-02 18:07:33 +0000383 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000384
reed@google.com9c36a762012-05-02 18:07:33 +0000385 const RunType* scanline = fRunHead->findScanline(r.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000386 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000387 if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
388 return false;
389 }
reed@google.combb094b92012-11-07 14:23:42 +0000390 if (r.fBottom <= scanline_bottom(scanline)) {
391 break;
392 }
reed@google.com9c36a762012-05-02 18:07:33 +0000393 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000394 }
reed@google.com9c36a762012-05-02 18:07:33 +0000395 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396}
397
reed@google.comc34f53d2012-04-30 15:10:13 +0000398bool SkRegion::contains(const SkRegion& rgn) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000399 SkDEBUGCODE(this->validate();)
400 SkDEBUGCODE(rgn.validate();)
401
reed@google.comc34f53d2012-04-30 15:10:13 +0000402 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000404 }
405 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000407 }
reed@google.com9c36a762012-05-02 18:07:33 +0000408 if (rgn.isRect()) {
409 return this->contains(rgn.getBounds());
410 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411
reed@google.com7d4aee32012-04-30 13:29:02 +0000412 /*
413 * A contains B is equivalent to
414 * B - A == 0
415 */
416 return !Oper(rgn, *this, kDifference_Op, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417}
418
reed@google.comc34f53d2012-04-30 15:10:13 +0000419const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
reed@google.comaf7e6942012-05-01 14:43:22 +0000420 int* intervals) const {
421 SkASSERT(tmpStorage && intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422 const RunType* runs = tmpStorage;
423
reed@google.comc34f53d2012-04-30 15:10:13 +0000424 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 tmpStorage[0] = kRunTypeSentinel;
reed@google.comaf7e6942012-05-01 14:43:22 +0000426 *intervals = 0;
reed@google.comc34f53d2012-04-30 15:10:13 +0000427 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 BuildRectRuns(fBounds, tmpStorage);
reed@google.comaf7e6942012-05-01 14:43:22 +0000429 *intervals = 1;
reed@google.comc34f53d2012-04-30 15:10:13 +0000430 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 runs = fRunHead->readonly_runs();
reed@google.comaf7e6942012-05-01 14:43:22 +0000432 *intervals = fRunHead->getIntervalCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 }
434 return runs;
435}
436
reed@google.comc34f53d2012-04-30 15:10:13 +0000437///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438
reed@google.com9c36a762012-05-02 18:07:33 +0000439static bool scanline_intersects(const SkRegion::RunType runs[],
440 SkRegion::RunType L, SkRegion::RunType R) {
441 runs += 2; // skip Bottom and IntervalCount
442 for (;;) {
443 if (R <= runs[0]) {
444 break;
445 }
446 if (L < runs[1]) {
447 return true;
448 }
449 runs += 2;
450 }
451 return false;
452}
453
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454bool SkRegion::intersects(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000455 SkDEBUGCODE(this->validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000456
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 if (this->isEmpty() || r.isEmpty()) {
458 return false;
459 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000460
reed@google.com9c36a762012-05-02 18:07:33 +0000461 SkIRect sect;
462 if (!sect.intersect(fBounds, r)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 return false;
464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 if (this->isRect()) {
466 return true;
467 }
reed@google.combb094b92012-11-07 14:23:42 +0000468 SkASSERT(this->isComplex());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000469
reed@google.com9c36a762012-05-02 18:07:33 +0000470 const RunType* scanline = fRunHead->findScanline(sect.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000471 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000472 if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
473 return true;
474 }
reed@google.combb094b92012-11-07 14:23:42 +0000475 if (sect.fBottom <= scanline_bottom(scanline)) {
476 break;
477 }
reed@google.com9c36a762012-05-02 18:07:33 +0000478 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000479 }
reed@google.com9c36a762012-05-02 18:07:33 +0000480 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481}
482
483bool SkRegion::intersects(const SkRegion& rgn) const {
484 if (this->isEmpty() || rgn.isEmpty()) {
485 return false;
486 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
489 return false;
490 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000491
reed@google.com9c36a762012-05-02 18:07:33 +0000492 bool weAreARect = this->isRect();
493 bool theyAreARect = rgn.isRect();
494
495 if (weAreARect && theyAreARect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 return true;
497 }
reed@google.com9c36a762012-05-02 18:07:33 +0000498 if (weAreARect) {
499 return rgn.intersects(this->getBounds());
500 }
501 if (theyAreARect) {
502 return this->intersects(rgn.getBounds());
503 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000504
reed@google.com9c36a762012-05-02 18:07:33 +0000505 // both of us are complex
reed@google.com7d4aee32012-04-30 13:29:02 +0000506 return Oper(*this, rgn, kIntersect_Op, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507}
508
reed@google.comc34f53d2012-04-30 15:10:13 +0000509///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
reed@google.com6d428d32012-01-25 21:53:53 +0000511bool SkRegion::operator==(const SkRegion& b) const {
512 SkDEBUGCODE(validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 SkDEBUGCODE(b.validate();)
514
reed@google.com6d428d32012-01-25 21:53:53 +0000515 if (this == &b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000517 }
reed@google.com6d428d32012-01-25 21:53:53 +0000518 if (fBounds != b.fBounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000520 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000521
reed@google.com6d428d32012-01-25 21:53:53 +0000522 const SkRegion::RunHead* ah = fRunHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 const SkRegion::RunHead* bh = b.fRunHead;
524
525 // this catches empties and rects being equal
reed@google.com97fa34c2011-03-18 14:44:42 +0000526 if (ah == bh) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000528 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 // now we insist that both are complex (but different ptrs)
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000530 if (!this->isComplex() || !b.isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000532 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 return ah->fRunCount == bh->fRunCount &&
534 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
535 ah->fRunCount * sizeof(SkRegion::RunType));
536}
537
reed@google.com97fa34c2011-03-18 14:44:42 +0000538void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 SkDEBUGCODE(this->validate();)
540
reed@google.com97fa34c2011-03-18 14:44:42 +0000541 if (NULL == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 return;
reed@google.com97fa34c2011-03-18 14:44:42 +0000543 }
544 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 dst->setEmpty();
reed@google.com97fa34c2011-03-18 14:44:42 +0000546 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
548 fBounds.fRight + dx, fBounds.fBottom + dy);
reed@google.com97fa34c2011-03-18 14:44:42 +0000549 } else {
550 if (this == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 dst->fRunHead = dst->fRunHead->ensureWritable();
reed@google.com97fa34c2011-03-18 14:44:42 +0000552 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkRegion tmp;
reed@google.comaf7e6942012-05-01 14:43:22 +0000554 tmp.allocateRuns(*fRunHead);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 tmp.fBounds = fBounds;
556 dst->swap(tmp);
557 }
558
559 dst->fBounds.offset(dx, dy);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 const RunType* sruns = fRunHead->readonly_runs();
562 RunType* druns = dst->fRunHead->writable_runs();
563
564 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
reed@google.com97fa34c2011-03-18 14:44:42 +0000565 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 int bottom = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000567 if (bottom == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000569 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000571 *druns++ = *sruns++; // copy intervalCount;
reed@google.com97fa34c2011-03-18 14:44:42 +0000572 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 int x = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000574 if (x == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000576 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 *druns++ = (SkRegion::RunType)(x + dx);
578 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
579 }
580 *druns++ = kRunTypeSentinel; // x sentinel
581 }
582 *druns++ = kRunTypeSentinel; // y sentinel
583
584 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
585 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
586 }
587
588 SkDEBUGCODE(this->validate();)
589}
590
reed@android.com097a3512010-07-13 18:35:14 +0000591///////////////////////////////////////////////////////////////////////////////
592
reed@android.com097a3512010-07-13 18:35:14 +0000593bool SkRegion::setRects(const SkIRect rects[], int count) {
594 if (0 == count) {
reed@android.comcb342352010-07-22 18:27:53 +0000595 this->setEmpty();
596 } else {
597 this->setRect(rects[0]);
598 for (int i = 1; i < count; i++) {
599 this->op(rects[i], kUnion_Op);
reed@android.com097a3512010-07-13 18:35:14 +0000600 }
601 }
reed@android.comcb342352010-07-22 18:27:53 +0000602 return !this->isEmpty();
reed@android.com097a3512010-07-13 18:35:14 +0000603}
604
605///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606
607#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized
608#pragma warning ( push )
609#pragma warning ( disable : 4701 )
610#endif
611
612#ifdef SK_DEBUG
613static void assert_valid_pair(int left, int rite)
614{
615 SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
616}
617#else
618 #define assert_valid_pair(left, rite)
619#endif
620
621struct spanRec {
622 const SkRegion::RunType* fA_runs;
623 const SkRegion::RunType* fB_runs;
624 int fA_left, fA_rite, fB_left, fB_rite;
625 int fLeft, fRite, fInside;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000626
reed@google.comc34f53d2012-04-30 15:10:13 +0000627 void init(const SkRegion::RunType a_runs[],
628 const SkRegion::RunType b_runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 fA_left = *a_runs++;
630 fA_rite = *a_runs++;
631 fB_left = *b_runs++;
632 fB_rite = *b_runs++;
633
634 fA_runs = a_runs;
635 fB_runs = b_runs;
636 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000637
reed@google.comc34f53d2012-04-30 15:10:13 +0000638 bool done() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
640 SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
reed@google.comc34f53d2012-04-30 15:10:13 +0000641 return fA_left == SkRegion::kRunTypeSentinel &&
642 fB_left == SkRegion::kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 }
644
reed@google.comc34f53d2012-04-30 15:10:13 +0000645 void next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 assert_valid_pair(fA_left, fA_rite);
647 assert_valid_pair(fB_left, fB_rite);
648
649 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
650 bool a_flush = false;
651 bool b_flush = false;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000652
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 int a_left = fA_left;
654 int a_rite = fA_rite;
655 int b_left = fB_left;
656 int b_rite = fB_rite;
657
reed@google.comc34f53d2012-04-30 15:10:13 +0000658 if (a_left < b_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 inside = 1;
660 left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000661 if (a_rite <= b_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 rite = a_rite;
663 a_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000664 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 rite = a_left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000666 }
667 } else if (b_left < a_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 inside = 2;
669 left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000670 if (b_rite <= a_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 rite = b_rite;
672 b_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000673 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 rite = b_left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000675 }
676 } else { // a_left == b_left
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 inside = 3;
678 left = a_left; // or b_left
reed@google.comc34f53d2012-04-30 15:10:13 +0000679 if (a_rite <= b_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 rite = b_left = a_rite;
681 a_flush = true;
682 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000683 if (b_rite <= a_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 rite = a_left = b_rite;
685 b_flush = true;
686 }
687 }
688
reed@google.comc34f53d2012-04-30 15:10:13 +0000689 if (a_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 a_left = *fA_runs++;
691 a_rite = *fA_runs++;
692 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000693 if (b_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 b_left = *fB_runs++;
695 b_rite = *fB_runs++;
696 }
697
698 SkASSERT(left <= rite);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000699
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 // now update our state
701 fA_left = a_left;
702 fA_rite = a_rite;
703 fB_left = b_left;
704 fB_rite = b_rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 fLeft = left;
707 fRite = rite;
708 fInside = inside;
709 }
710};
711
712static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
713 const SkRegion::RunType b_runs[],
714 SkRegion::RunType dst[],
reed@google.comc34f53d2012-04-30 15:10:13 +0000715 int min, int max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 spanRec rec;
717 bool firstInterval = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 rec.init(a_runs, b_runs);
720
reed@google.comc34f53d2012-04-30 15:10:13 +0000721 while (!rec.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 rec.next();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000723
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 int left = rec.fLeft;
725 int rite = rec.fRite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 // add left,rite to our dst buffer (checking for coincidence
728 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
reed@google.comc34f53d2012-04-30 15:10:13 +0000729 left < rite) { // skip if equal
730 if (firstInterval || dst[-1] < left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 *dst++ = (SkRegion::RunType)(left);
732 *dst++ = (SkRegion::RunType)(rite);
733 firstInterval = false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000734 } else {
735 // update the right edge
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 dst[-1] = (SkRegion::RunType)(rite);
reed@google.comc34f53d2012-04-30 15:10:13 +0000737 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 }
739 }
740
741 *dst++ = SkRegion::kRunTypeSentinel;
742 return dst;
743}
744
745#if defined _WIN32 && _MSC_VER >= 1300
746#pragma warning ( pop )
747#endif
748
749static const struct {
750 uint8_t fMin;
751 uint8_t fMax;
752} gOpMinMax[] = {
753 { 1, 1 }, // Difference
754 { 3, 3 }, // Intersection
755 { 1, 3 }, // Union
756 { 1, 2 } // XOR
757};
758
759class RgnOper {
760public:
reed@google.comc34f53d2012-04-30 15:10:13 +0000761 RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 // need to ensure that the op enum lines up with our minmax array
763 SkASSERT(SkRegion::kDifference_Op == 0);
764 SkASSERT(SkRegion::kIntersect_Op == 1);
765 SkASSERT(SkRegion::kUnion_Op == 2);
766 SkASSERT(SkRegion::kXOR_Op == 3);
767 SkASSERT((unsigned)op <= 3);
768
769 fStartDst = dst;
770 fPrevDst = dst + 1;
771 fPrevLen = 0; // will never match a length from operate_on_span
772 fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
773
774 fMin = gOpMinMax[op].fMin;
775 fMax = gOpMinMax[op].fMax;
776 }
777
reed@google.comc34f53d2012-04-30 15:10:13 +0000778 void addSpan(int bottom, const SkRegion::RunType a_runs[],
779 const SkRegion::RunType b_runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +0000780 // skip X values and slots for the next Y+intervalCount
781 SkRegion::RunType* start = fPrevDst + fPrevLen + 2;
782 // start points to beginning of dst interval
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
784 size_t len = stop - start;
reed@google.com9c36a762012-05-02 18:07:33 +0000785 SkASSERT(len >= 1 && (len & 1) == 1);
786 SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787
reed@google.comc34f53d2012-04-30 15:10:13 +0000788 if (fPrevLen == len &&
reed@google.com9c36a762012-05-02 18:07:33 +0000789 (1 == len || !memcmp(fPrevDst, start,
790 (len - 1) * sizeof(SkRegion::RunType)))) {
reed@google.comc34f53d2012-04-30 15:10:13 +0000791 // update Y value
reed@google.com9c36a762012-05-02 18:07:33 +0000792 fPrevDst[-2] = (SkRegion::RunType)(bottom);
reed@google.comc34f53d2012-04-30 15:10:13 +0000793 } else { // accept the new span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 if (len == 1 && fPrevLen == 0) {
795 fTop = (SkRegion::RunType)(bottom); // just update our bottom
796 } else {
reed@google.com9c36a762012-05-02 18:07:33 +0000797 start[-2] = (SkRegion::RunType)(bottom);
798 start[-1] = len >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 fPrevDst = start;
800 fPrevLen = len;
801 }
802 }
803 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000804
reed@google.comc34f53d2012-04-30 15:10:13 +0000805 int flush() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 fStartDst[0] = fTop;
807 fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
808 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
809 }
810
reed@google.com7d4aee32012-04-30 13:29:02 +0000811 bool isEmpty() const { return 0 == fPrevLen; }
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 uint8_t fMin, fMax;
814
815private:
816 SkRegion::RunType* fStartDst;
817 SkRegion::RunType* fPrevDst;
818 size_t fPrevLen;
819 SkRegion::RunType fTop;
820};
821
reed@google.com7d4aee32012-04-30 13:29:02 +0000822// want a unique value to signal that we exited due to quickExit
823#define QUICK_EXIT_TRUE_COUNT (-1)
824
reed@google.com1deaab52011-10-06 13:11:46 +0000825static int operate(const SkRegion::RunType a_runs[],
826 const SkRegion::RunType b_runs[],
827 SkRegion::RunType dst[],
reed@google.com7d4aee32012-04-30 13:29:02 +0000828 SkRegion::Op op,
829 bool quickExit) {
reed@google.com9c36a762012-05-02 18:07:33 +0000830 const SkRegion::RunType gEmptyScanline[] = {
831 0, // dummy bottom value
832 0, // zero intervals
reed@google.com5f7c8a52012-05-03 16:17:38 +0000833 SkRegion::kRunTypeSentinel,
834 // just need a 2nd value, since spanRec.init() reads 2 values, even
835 // though if the first value is the sentinel, it ignores the 2nd value.
836 // w/o the 2nd value here, we might read uninitialized memory.
837 // This happens when we are using gSentinel, which is pointing at
838 // our sentinel value.
839 0
reed@android.comfbb02e72010-04-13 14:52:52 +0000840 };
reed@google.com9c36a762012-05-02 18:07:33 +0000841 const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842
843 int a_top = *a_runs++;
844 int a_bot = *a_runs++;
845 int b_top = *b_runs++;
846 int b_bot = *b_runs++;
847
reed@google.com9c36a762012-05-02 18:07:33 +0000848 a_runs += 1; // skip the intervalCount;
849 b_runs += 1; // skip the intervalCount;
850
851 // Now a_runs and b_runs to their intervals (or sentinel)
852
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 assert_sentinel(a_top, false);
854 assert_sentinel(a_bot, false);
855 assert_sentinel(b_top, false);
856 assert_sentinel(b_bot, false);
857
858 RgnOper oper(SkMin32(a_top, b_top), dst, op);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000859
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000861
reed@google.com1deaab52011-10-06 13:11:46 +0000862 while (a_bot < SkRegion::kRunTypeSentinel ||
863 b_bot < SkRegion::kRunTypeSentinel) {
864 int top, bot SK_INIT_TO_AVOID_WARNING;
reed@android.comfbb02e72010-04-13 14:52:52 +0000865 const SkRegion::RunType* run0 = gSentinel;
866 const SkRegion::RunType* run1 = gSentinel;
reed@google.com1deaab52011-10-06 13:11:46 +0000867 bool a_flush = false;
868 bool b_flush = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869
reed@google.com1deaab52011-10-06 13:11:46 +0000870 if (a_top < b_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 top = a_top;
872 run0 = a_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000873 if (a_bot <= b_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 bot = a_bot;
875 a_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000876 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 bot = a_top = b_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000878 }
879 } else if (b_top < a_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 top = b_top;
881 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000882 if (b_bot <= a_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 bot = b_bot;
884 b_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000885 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 bot = b_top = a_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000887 }
888 } else { // a_top == b_top
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 top = a_top; // or b_top
890 run0 = a_runs;
891 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000892 if (a_bot <= b_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 bot = b_top = a_bot;
894 a_flush = true;
895 }
reed@google.com1deaab52011-10-06 13:11:46 +0000896 if (b_bot <= a_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 bot = a_top = b_bot;
898 b_flush = true;
899 }
900 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000901
reed@google.com1deaab52011-10-06 13:11:46 +0000902 if (top > prevBot) {
reed@android.comfbb02e72010-04-13 14:52:52 +0000903 oper.addSpan(top, gSentinel, gSentinel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 }
reed@google.com1deaab52011-10-06 13:11:46 +0000905 oper.addSpan(bot, run0, run1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
reed@google.com7d4aee32012-04-30 13:29:02 +0000907 if (quickExit && !oper.isEmpty()) {
908 return QUICK_EXIT_TRUE_COUNT;
909 }
910
reed@google.com1deaab52011-10-06 13:11:46 +0000911 if (a_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000912 a_runs = skip_intervals(a_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 a_top = a_bot;
914 a_bot = *a_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000915 a_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000916 if (a_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 a_top = a_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000918 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 }
reed@google.com1deaab52011-10-06 13:11:46 +0000920 if (b_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000921 b_runs = skip_intervals(b_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 b_top = b_bot;
923 b_bot = *b_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000924 b_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000925 if (b_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 b_top = b_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000927 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000929
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 prevBot = bot;
931 }
932 return oper.flush();
933}
934
935///////////////////////////////////////////////////////////////////////////////
936
937/* Given count RunTypes in a complex region, return the worst case number of
938 logical intervals that represents (i.e. number of rects that would be
939 returned from the iterator).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000940
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 We could just return count/2, since there must be at least 2 values per
942 interval, but we can first trim off the const overhead of the initial TOP
943 value, plus the final BOTTOM + 2 sentinels.
944 */
caryclark@google.com803eceb2012-06-06 12:09:34 +0000945#if 0 // UNUSED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946static int count_to_intervals(int count) {
947 SkASSERT(count >= 6); // a single rect is 6 values
948 return (count - 4) >> 1;
949}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000950#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951
952/* Given a number of intervals, what is the worst case representation of that
953 many intervals?
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000954
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 Worst case (from a storage perspective), is a vertical stack of single
reed@google.com9c36a762012-05-02 18:07:33 +0000956 intervals: TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 */
958static int intervals_to_count(int intervals) {
reed@google.com9c36a762012-05-02 18:07:33 +0000959 return 1 + intervals * 5 + 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960}
961
reed@google.comaf7e6942012-05-01 14:43:22 +0000962/* Given the intervalCounts of RunTypes in two regions, return the worst-case number
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 of RunTypes need to store the result after a region-op.
964 */
reed@google.comaf7e6942012-05-01 14:43:22 +0000965static int compute_worst_case_count(int a_intervals, int b_intervals) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
967 int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
968 // convert back to number of RunType values
969 return intervals_to_count(intervals);
970}
971
reed@google.com7d4aee32012-04-30 13:29:02 +0000972static bool setEmptyCheck(SkRegion* result) {
973 return result ? result->setEmpty() : false;
974}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975
reed@google.com7d4aee32012-04-30 13:29:02 +0000976static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
977 return result ? result->setRect(rect) : !rect.isEmpty();
978}
979
980static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
981 return result ? result->setRegion(rgn) : !rgn.isEmpty();
982}
983
984bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
985 SkRegion* result) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 SkASSERT((unsigned)op < kOpCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000987
reed@google.com7d4aee32012-04-30 13:29:02 +0000988 if (kReplace_Op == op) {
989 return setRegionCheck(result, rgnbOrig);
990 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000991
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 // swith to using pointers, so we can swap them as needed
993 const SkRegion* rgna = &rgnaOrig;
994 const SkRegion* rgnb = &rgnbOrig;
995 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
996
997 // collaps difference and reverse-difference into just difference
reed@google.com7d4aee32012-04-30 13:29:02 +0000998 if (kReverseDifference_Op == op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 SkTSwap<const SkRegion*>(rgna, rgnb);
1000 op = kDifference_Op;
1001 }
1002
1003 SkIRect bounds;
1004 bool a_empty = rgna->isEmpty();
1005 bool b_empty = rgnb->isEmpty();
1006 bool a_rect = rgna->isRect();
1007 bool b_rect = rgnb->isRect();
1008
1009 switch (op) {
1010 case kDifference_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001011 if (a_empty) {
1012 return setEmptyCheck(result);
1013 }
reed@google.com0d102802012-05-31 18:28:59 +00001014 if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
1015 rgnb->fBounds)) {
reed@google.com7d4aee32012-04-30 13:29:02 +00001016 return setRegionCheck(result, *rgna);
1017 }
reed@google.com0d102802012-05-31 18:28:59 +00001018 if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
1019 return setEmptyCheck(result);
1020 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 break;
1022
1023 case kIntersect_Op:
1024 if ((a_empty | b_empty)
reed@google.com7d4aee32012-04-30 13:29:02 +00001025 || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
1026 return setEmptyCheck(result);
1027 }
1028 if (a_rect & b_rect) {
1029 return setRectCheck(result, bounds);
1030 }
reed@google.com633c32b2013-01-31 15:24:24 +00001031 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1032 return setRegionCheck(result, *rgnb);
1033 }
1034 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1035 return setRegionCheck(result, *rgna);
1036 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 break;
1038
1039 case kUnion_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001040 if (a_empty) {
1041 return setRegionCheck(result, *rgnb);
1042 }
1043 if (b_empty) {
1044 return setRegionCheck(result, *rgna);
1045 }
1046 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1047 return setRegionCheck(result, *rgna);
1048 }
1049 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1050 return setRegionCheck(result, *rgnb);
1051 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 break;
1053
1054 case kXOR_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001055 if (a_empty) {
1056 return setRegionCheck(result, *rgnb);
1057 }
1058 if (b_empty) {
1059 return setRegionCheck(result, *rgna);
1060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 break;
1062 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001063 SkDEBUGFAIL("unknown region op");
reed@google.com7d4aee32012-04-30 13:29:02 +00001064 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 }
1066
1067 RunType tmpA[kRectRegionRuns];
1068 RunType tmpB[kRectRegionRuns];
1069
reed@google.comaf7e6942012-05-01 14:43:22 +00001070 int a_intervals, b_intervals;
1071 const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
1072 const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073
reed@google.comaf7e6942012-05-01 14:43:22 +00001074 int dstCount = compute_worst_case_count(a_intervals, b_intervals);
1075 SkAutoSTMalloc<256, RunType> array(dstCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076
reed@google.com9c36a762012-05-02 18:07:33 +00001077#ifdef SK_DEBUG
1078// Sometimes helpful to seed everything with a known value when debugging
1079// sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
1080#endif
1081
reed@google.com7d4aee32012-04-30 13:29:02 +00001082 int count = operate(a_runs, b_runs, array.get(), op, NULL == result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 SkASSERT(count <= dstCount);
reed@google.comaf7e6942012-05-01 14:43:22 +00001084
reed@google.com7d4aee32012-04-30 13:29:02 +00001085 if (result) {
1086 SkASSERT(count >= 0);
1087 return result->setRuns(array.get(), count);
1088 } else {
1089 return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
1090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091}
1092
reed@google.com7d4aee32012-04-30 13:29:02 +00001093bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
1094 SkDEBUGCODE(this->validate();)
1095 return SkRegion::Oper(rgna, rgnb, op, this);
1096}
1097
1098///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099
1100#include "SkBuffer.h"
1101
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001102uint32_t SkRegion::writeToMemory(void* storage) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 if (NULL == storage) {
1104 uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
1105 if (!this->isEmpty()) {
1106 size += sizeof(fBounds);
1107 if (this->isComplex()) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001108 size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 size += fRunHead->fRunCount * sizeof(RunType);
1110 }
1111 }
1112 return size;
1113 }
1114
1115 SkWBuffer buffer(storage);
1116
1117 if (this->isEmpty()) {
1118 buffer.write32(-1);
1119 } else {
1120 bool isRect = this->isRect();
1121
1122 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1123 buffer.write(&fBounds, sizeof(fBounds));
1124
1125 if (!isRect) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001126 buffer.write32(fRunHead->getYSpanCount());
1127 buffer.write32(fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 buffer.write(fRunHead->readonly_runs(),
1129 fRunHead->fRunCount * sizeof(RunType));
1130 }
1131 }
1132 return buffer.pos();
1133}
1134
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001135uint32_t SkRegion::readFromMemory(const void* storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 SkRBuffer buffer(storage);
1137 SkRegion tmp;
1138 int32_t count;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001139
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 count = buffer.readS32();
1141 if (count >= 0) {
1142 buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
1143 if (count == 0) {
1144 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1145 } else {
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001146 int32_t ySpanCount = buffer.readS32();
reed@google.comaf7e6942012-05-01 14:43:22 +00001147 int32_t intervalCount = buffer.readS32();
1148 tmp.allocateRuns(count, ySpanCount, intervalCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
1150 }
1151 }
1152 this->swap(tmp);
1153 return buffer.pos();
1154}
1155
reed@google.com0a0a2362011-03-23 13:51:55 +00001156///////////////////////////////////////////////////////////////////////////////
1157
1158const SkRegion& SkRegion::GetEmptyRegion() {
1159 static SkRegion gEmpty;
1160 return gEmpty;
1161}
1162
1163///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164
1165#ifdef SK_DEBUG
1166
reed@google.com9c36a762012-05-02 18:07:33 +00001167// Starts with first X-interval, and returns a ptr to the X-sentinel
1168static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) {
1169 // want to track that our intevals are all disjoint, such that
1170 // prev-right < next-left. We rely on this optimization in places such as
1171 // contains().
1172 //
1173 SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001174
reed@google.com9c36a762012-05-02 18:07:33 +00001175 while (runs[0] < SkRegion::kRunTypeSentinel) {
1176 SkASSERT(prevR < runs[0]);
1177 SkASSERT(runs[0] < runs[1]);
1178 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
1179 prevR = runs[1];
1180 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 }
reed@google.com9c36a762012-05-02 18:07:33 +00001182 return runs;
1183}
1184
sugoi@google.come0e385c2013-03-11 18:50:03 +00001185static void compute_bounds(const SkRegion::RunType runs[],
reed@google.com9c36a762012-05-02 18:07:33 +00001186 SkIRect* bounds, int* ySpanCountPtr,
1187 int* intervalCountPtr) {
1188 assert_sentinel(runs[0], false); // top
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001189
reed@google.com9c36a762012-05-02 18:07:33 +00001190 int left = SK_MaxS32;
1191 int rite = SK_MinS32;
1192 int bot;
1193 int ySpanCount = 0;
1194 int intervalCount = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001195
reed@google.com9c36a762012-05-02 18:07:33 +00001196 bounds->fTop = *runs++;
1197 do {
1198 bot = *runs++;
1199 SkASSERT(SkRegion::kRunTypeSentinel > bot);
1200
1201 ySpanCount += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001202
reed@google.com9c36a762012-05-02 18:07:33 +00001203 runs += 1; // skip intervalCount for now
1204 if (*runs < SkRegion::kRunTypeSentinel) {
1205 if (left > *runs) {
1206 left = *runs;
1207 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001208
reed@google.com9c36a762012-05-02 18:07:33 +00001209 const SkRegion::RunType* prev = runs;
1210 runs = skip_intervals_slow(runs);
1211 int intervals = (runs - prev) >> 1;
1212 SkASSERT(prev[-1] == intervals);
1213 intervalCount += intervals;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001214
reed@google.com9c36a762012-05-02 18:07:33 +00001215 if (rite < runs[-1]) {
1216 rite = runs[-1];
1217 }
1218 } else {
1219 SkASSERT(0 == runs[-1]); // no intervals
1220 }
1221 SkASSERT(SkRegion::kRunTypeSentinel == *runs);
1222 runs += 1;
1223 } while (SkRegion::kRunTypeSentinel != *runs);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001224
reed@google.com9c36a762012-05-02 18:07:33 +00001225 bounds->fLeft = left;
1226 bounds->fRight = rite;
1227 bounds->fBottom = bot;
1228 *ySpanCountPtr = ySpanCount;
1229 *intervalCountPtr = intervalCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230}
1231
reed@google.comc34f53d2012-04-30 15:10:13 +00001232void SkRegion::validate() const {
1233 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 // check for explicit empty (the zero rect), so we can compare rects to know when
1235 // two regions are equal (i.e. emptyRectA == emptyRectB)
1236 // this is stricter than just asserting fBounds.isEmpty()
1237 SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
reed@google.comc34f53d2012-04-30 15:10:13 +00001238 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 SkASSERT(!fBounds.isEmpty());
reed@google.comc34f53d2012-04-30 15:10:13 +00001240 if (!this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 SkASSERT(fRunHead->fRefCnt >= 1);
reed@google.com9c36a762012-05-02 18:07:33 +00001242 SkASSERT(fRunHead->fRunCount > kRectRegionRuns);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243
1244 const RunType* run = fRunHead->readonly_runs();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
1246 // check that our bounds match our runs
1247 {
1248 SkIRect bounds;
reed@google.comaf7e6942012-05-01 14:43:22 +00001249 int ySpanCount, intervalCount;
sugoi@google.come0e385c2013-03-11 18:50:03 +00001250 compute_bounds(run, &bounds, &ySpanCount, &intervalCount);
reed@google.com9c36a762012-05-02 18:07:33 +00001251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 SkASSERT(bounds == fBounds);
reed@google.comaf7e6942012-05-01 14:43:22 +00001253 SkASSERT(ySpanCount > 0);
1254 SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
reed@google.com9c36a762012-05-02 18:07:33 +00001255 // SkASSERT(intervalCount > 1);
reed@google.comaf7e6942012-05-01 14:43:22 +00001256 SkASSERT(fRunHead->getIntervalCount() == intervalCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 }
1259 }
1260}
1261
reed@google.comc34f53d2012-04-30 15:10:13 +00001262void SkRegion::dump() const {
1263 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 SkDebugf(" rgn: empty\n");
reed@google.comc34f53d2012-04-30 15:10:13 +00001265 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
reed@google.com9c36a762012-05-02 18:07:33 +00001267 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 const RunType* runs = fRunHead->readonly_runs();
1269 for (int i = 0; i < fRunHead->fRunCount; i++)
1270 SkDebugf(" %d", runs[i]);
1271 }
1272 SkDebugf("\n");
1273 }
1274}
1275
1276#endif
1277
reed@google.comc34f53d2012-04-30 15:10:13 +00001278///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1281 this->reset(rgn);
1282}
1283
1284bool SkRegion::Iterator::rewind() {
1285 if (fRgn) {
1286 this->reset(*fRgn);
1287 return true;
1288 }
1289 return false;
1290}
1291
1292void SkRegion::Iterator::reset(const SkRegion& rgn) {
1293 fRgn = &rgn;
1294 if (rgn.isEmpty()) {
1295 fDone = true;
1296 } else {
1297 fDone = false;
1298 if (rgn.isRect()) {
1299 fRect = rgn.fBounds;
1300 fRuns = NULL;
1301 } else {
1302 fRuns = rgn.fRunHead->readonly_runs();
reed@google.com9c36a762012-05-02 18:07:33 +00001303 fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
1304 fRuns += 5;
1305 // Now fRuns points to the 2nd interval (or x-sentinel)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 }
1307 }
1308}
1309
1310void SkRegion::Iterator::next() {
1311 if (fDone) {
1312 return;
1313 }
1314
1315 if (fRuns == NULL) { // rect case
1316 fDone = true;
1317 return;
1318 }
1319
1320 const RunType* runs = fRuns;
1321
1322 if (runs[0] < kRunTypeSentinel) { // valid X value
1323 fRect.fLeft = runs[0];
1324 fRect.fRight = runs[1];
1325 runs += 2;
1326 } else { // we're at the end of a line
1327 runs += 1;
1328 if (runs[0] < kRunTypeSentinel) { // valid Y value
reed@google.com9c36a762012-05-02 18:07:33 +00001329 int intervals = runs[1];
1330 if (0 == intervals) { // empty line
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 fRect.fTop = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001332 runs += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 } else {
1334 fRect.fTop = fRect.fBottom;
1335 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001336
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 fRect.fBottom = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001338 assert_sentinel(runs[2], false);
1339 assert_sentinel(runs[3], false);
1340 fRect.fLeft = runs[2];
1341 fRect.fRight = runs[3];
1342 runs += 4;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 } else { // end of rgn
1344 fDone = true;
1345 }
1346 }
1347 fRuns = runs;
1348}
1349
1350SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1351 : fIter(rgn), fClip(clip), fDone(true) {
1352 const SkIRect& r = fIter.rect();
1353
1354 while (!fIter.done()) {
1355 if (r.fTop >= clip.fBottom) {
1356 break;
1357 }
1358 if (fRect.intersect(clip, r)) {
1359 fDone = false;
1360 break;
1361 }
1362 fIter.next();
1363 }
1364}
1365
1366void SkRegion::Cliperator::next() {
1367 if (fDone) {
1368 return;
1369 }
1370
1371 const SkIRect& r = fIter.rect();
1372
1373 fDone = true;
1374 fIter.next();
1375 while (!fIter.done()) {
1376 if (r.fTop >= fClip.fBottom) {
1377 break;
1378 }
1379 if (fRect.intersect(fClip, r)) {
1380 fDone = false;
1381 break;
1382 }
1383 fIter.next();
1384 }
1385}
1386
reed@google.come72766f2011-01-07 15:00:44 +00001387///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388
reed@google.come72766f2011-01-07 15:00:44 +00001389SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1390 int right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391 SkDEBUGCODE(rgn.validate();)
1392
1393 const SkIRect& r = rgn.getBounds();
1394
1395 fDone = true;
reed@google.come72766f2011-01-07 15:00:44 +00001396 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1397 right > r.fLeft && left < r.fRight) {
1398 if (rgn.isRect()) {
1399 if (left < r.fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 left = r.fLeft;
reed@google.come72766f2011-01-07 15:00:44 +00001401 }
1402 if (right > r.fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 right = r.fRight;
reed@google.come72766f2011-01-07 15:00:44 +00001404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 fLeft = left;
1406 fRight = right;
1407 fRuns = NULL; // means we're a rect, not a rgn
1408 fDone = false;
reed@google.come72766f2011-01-07 15:00:44 +00001409 } else {
reed@google.com9c36a762012-05-02 18:07:33 +00001410 const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
1411 runs += 2; // skip Bottom and IntervalCount
1412 for (;;) {
1413 // runs[0..1] is to the right of the span, so we're done
1414 if (runs[0] >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 break;
1416 }
reed@google.com9c36a762012-05-02 18:07:33 +00001417 // runs[0..1] is to the left of the span, so continue
1418 if (runs[1] <= left) {
1419 runs += 2;
1420 continue;
1421 }
1422 // runs[0..1] intersects the span
1423 fRuns = runs;
1424 fLeft = left;
1425 fRight = right;
1426 fDone = false;
1427 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 }
1429 }
1430 }
1431}
1432
reed@google.come72766f2011-01-07 15:00:44 +00001433bool SkRegion::Spanerator::next(int* left, int* right) {
1434 if (fDone) {
1435 return false;
1436 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437
reed@google.come72766f2011-01-07 15:00:44 +00001438 if (fRuns == NULL) { // we're a rect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 fDone = true; // ok, now we're done
reed@google.come72766f2011-01-07 15:00:44 +00001440 if (left) {
1441 *left = fLeft;
1442 }
1443 if (right) {
1444 *right = fRight;
1445 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 return true; // this interval is legal
1447 }
1448
1449 const SkRegion::RunType* runs = fRuns;
1450
reed@google.come72766f2011-01-07 15:00:44 +00001451 if (runs[0] >= fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 fDone = true;
1453 return false;
1454 }
1455
1456 SkASSERT(runs[1] > fLeft);
1457
reed@google.come72766f2011-01-07 15:00:44 +00001458 if (left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 *left = SkMax32(fLeft, runs[0]);
reed@google.come72766f2011-01-07 15:00:44 +00001460 }
1461 if (right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 *right = SkMin32(fRight, runs[1]);
reed@google.come72766f2011-01-07 15:00:44 +00001463 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 fRuns = runs + 2;
1465 return true;
1466}
1467
1468///////////////////////////////////////////////////////////////////////////////
1469
1470#ifdef SK_DEBUG
1471
1472bool SkRegion::debugSetRuns(const RunType runs[], int count) {
1473 // we need to make a copy, since the real method may modify the array, and
1474 // so it cannot be const.
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 SkAutoTArray<RunType> storage(count);
1477 memcpy(storage.get(), runs, count * sizeof(RunType));
1478 return this->setRuns(storage.get(), count);
1479}
1480
1481#endif