blob: 24d7248582f681d6461976ac919d5985eda68b54 [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
bungeman@google.com2c8e9d42013-10-11 19:23:56 +0000186#include <stdio.h>
reed@google.comc34f53d2012-04-30 15:10:13 +0000187char* SkRegion::toString() {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000188 Iterator iter(*this);
189 int count = 0;
190 while (!iter.done()) {
191 count++;
192 iter.next();
193 }
194 // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
195 const int max = (count*((11*4)+5))+11+1;
196 char* result = (char*)malloc(max);
197 if (result == NULL) {
198 return NULL;
199 }
200 count = sprintf(result, "SkRegion(");
201 iter.reset(*this);
202 while (!iter.done()) {
203 const SkIRect& r = iter.rect();
204 count += sprintf(result+count, "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
205 iter.next();
206 }
207 count += sprintf(result+count, ")");
208 return result;
209}
210#endif
211
reed@google.comc34f53d2012-04-30 15:10:13 +0000212///////////////////////////////////////////////////////////////////////////////
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000213
reed@google.comc34f53d2012-04-30 15:10:13 +0000214int SkRegion::count_runtype_values(int* itop, int* ibot) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 int maxT;
216
reed@google.comc34f53d2012-04-30 15:10:13 +0000217 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 maxT = 2;
reed@google.comc34f53d2012-04-30 15:10:13 +0000219 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 SkASSERT(this->isComplex());
reed@google.comaf7e6942012-05-01 14:43:22 +0000221 maxT = fRunHead->getIntervalCount() * 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 }
223 *itop = fBounds.fTop;
224 *ibot = fBounds.fBottom;
225 return maxT;
226}
227
reed@google.com7d4aee32012-04-30 13:29:02 +0000228static bool isRunCountEmpty(int count) {
229 return count <= 2;
230}
231
reed@google.comc34f53d2012-04-30 15:10:13 +0000232bool SkRegion::setRuns(RunType runs[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 SkDEBUGCODE(this->validate();)
234 SkASSERT(count > 0);
235
reed@google.comc34f53d2012-04-30 15:10:13 +0000236 if (isRunCountEmpty(count)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 // SkDEBUGF(("setRuns: empty\n"));
238 assert_sentinel(runs[count-1], true);
239 return this->setEmpty();
240 }
241
242 // trim off any empty spans from the top and bottom
243 // weird I should need this, perhaps op() could be smarter...
reed@google.comc34f53d2012-04-30 15:10:13 +0000244 if (count > kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 RunType* stop = runs + count;
246 assert_sentinel(runs[0], false); // top
247 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +0000248 // runs[2] is uncomputed intervalCount
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000249
reed@google.com9c36a762012-05-02 18:07:33 +0000250 if (runs[3] == SkRegion::kRunTypeSentinel) { // should be first left...
251 runs += 3; // skip empty initial span
252 runs[0] = runs[-2]; // set new top to prev bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
reed@google.com9c36a762012-05-02 18:07:33 +0000254 assert_sentinel(runs[2], false); // intervalcount
255 assert_sentinel(runs[3], false); // left
256 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 }
258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 assert_sentinel(stop[-1], true);
260 assert_sentinel(stop[-2], true);
reed@google.com9c36a762012-05-02 18:07:33 +0000261
262 // now check for a trailing empty span
263 if (stop[-5] == SkRegion::kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
264 stop[-4] = SkRegion::kRunTypeSentinel; // kill empty last span
265 stop -= 3;
266 assert_sentinel(stop[-1], true); // last y-sentinel
267 assert_sentinel(stop[-2], true); // last x-sentinel
268 assert_sentinel(stop[-3], false); // last right
269 assert_sentinel(stop[-4], false); // last left
270 assert_sentinel(stop[-5], false); // last interval-count
271 assert_sentinel(stop[-6], false); // last bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 }
273 count = (int)(stop - runs);
274 }
275
276 SkASSERT(count >= kRectRegionRuns);
277
reed@google.com9c36a762012-05-02 18:07:33 +0000278 if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 return this->setRect(fBounds);
280 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 // if we get here, we need to become a complex region
283
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000284 if (!this->isComplex() || fRunHead->fRunCount != count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 this->freeRuns();
reed@google.com9c36a762012-05-02 18:07:33 +0000286 this->allocateRuns(count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000288
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 // must call this before we can write directly into runs()
290 // in case we are sharing the buffer with another region (copy on write)
291 fRunHead = fRunHead->ensureWritable();
292 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
reed@google.com9c36a762012-05-02 18:07:33 +0000293 fRunHead->computeRunBounds(&fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294
295 SkDEBUGCODE(this->validate();)
296
297 return true;
298}
299
300void SkRegion::BuildRectRuns(const SkIRect& bounds,
reed@google.comc34f53d2012-04-30 15:10:13 +0000301 RunType runs[kRectRegionRuns]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 runs[0] = bounds.fTop;
303 runs[1] = bounds.fBottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000304 runs[2] = 1; // 1 interval for this scanline
305 runs[3] = bounds.fLeft;
306 runs[4] = bounds.fRight;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 runs[5] = kRunTypeSentinel;
reed@google.com9c36a762012-05-02 18:07:33 +0000308 runs[6] = kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309}
310
reed@google.comc34f53d2012-04-30 15:10:13 +0000311bool SkRegion::contains(int32_t x, int32_t y) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000312 SkDEBUGCODE(this->validate();)
313
reed@google.comc34f53d2012-04-30 15:10:13 +0000314 if (!fBounds.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000316 }
317 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000319 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000321
reed@google.com9c36a762012-05-02 18:07:33 +0000322 const RunType* runs = fRunHead->findScanline(y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323
reed@google.com9c36a762012-05-02 18:07:33 +0000324 // Skip the Bottom and IntervalCount
325 runs += 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000326
reed@google.com9c36a762012-05-02 18:07:33 +0000327 // Just walk this scanline, checking each interval. The X-sentinel will
328 // appear as a left-inteval (runs[0]) and should abort the search.
329 //
330 // We could do a bsearch, using interval-count (runs[1]), but need to time
331 // when that would be worthwhile.
332 //
333 for (;;) {
334 if (x < runs[0]) {
335 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 }
reed@google.com9c36a762012-05-02 18:07:33 +0000337 if (x < runs[1]) {
338 return true;
339 }
340 runs += 2;
341 }
342 return false;
343}
344
mike@reedtribe.org796a1752012-11-07 03:39:46 +0000345static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
346 return runs[0];
347}
348
reed@google.com9c36a762012-05-02 18:07:33 +0000349static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
350 // skip [B N [L R]... S]
351 return runs + 2 + runs[1] * 2 + 1;
352}
353
354static bool scanline_contains(const SkRegion::RunType runs[],
355 SkRegion::RunType L, SkRegion::RunType R) {
356 runs += 2; // skip Bottom and IntervalCount
357 for (;;) {
358 if (L < runs[0]) {
359 break;
360 }
361 if (R <= runs[1]) {
362 return true;
363 }
364 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 }
366 return false;
367}
368
reed@google.comc34f53d2012-04-30 15:10:13 +0000369bool SkRegion::contains(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000370 SkDEBUGCODE(this->validate();)
371
372 if (!fBounds.contains(r)) {
373 return false;
374 }
375 if (this->isRect()) {
376 return true;
377 }
reed@google.com9c36a762012-05-02 18:07:33 +0000378 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000379
reed@google.com9c36a762012-05-02 18:07:33 +0000380 const RunType* scanline = fRunHead->findScanline(r.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000381 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000382 if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
383 return false;
384 }
reed@google.combb094b92012-11-07 14:23:42 +0000385 if (r.fBottom <= scanline_bottom(scanline)) {
386 break;
387 }
reed@google.com9c36a762012-05-02 18:07:33 +0000388 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000389 }
reed@google.com9c36a762012-05-02 18:07:33 +0000390 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391}
392
reed@google.comc34f53d2012-04-30 15:10:13 +0000393bool SkRegion::contains(const SkRegion& rgn) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000394 SkDEBUGCODE(this->validate();)
395 SkDEBUGCODE(rgn.validate();)
396
reed@google.comc34f53d2012-04-30 15:10:13 +0000397 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000399 }
400 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000402 }
reed@google.com9c36a762012-05-02 18:07:33 +0000403 if (rgn.isRect()) {
404 return this->contains(rgn.getBounds());
405 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406
reed@google.com7d4aee32012-04-30 13:29:02 +0000407 /*
408 * A contains B is equivalent to
409 * B - A == 0
410 */
411 return !Oper(rgn, *this, kDifference_Op, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412}
413
reed@google.comc34f53d2012-04-30 15:10:13 +0000414const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
reed@google.comaf7e6942012-05-01 14:43:22 +0000415 int* intervals) const {
416 SkASSERT(tmpStorage && intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 const RunType* runs = tmpStorage;
418
reed@google.comc34f53d2012-04-30 15:10:13 +0000419 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 tmpStorage[0] = kRunTypeSentinel;
reed@google.comaf7e6942012-05-01 14:43:22 +0000421 *intervals = 0;
reed@google.comc34f53d2012-04-30 15:10:13 +0000422 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 BuildRectRuns(fBounds, tmpStorage);
reed@google.comaf7e6942012-05-01 14:43:22 +0000424 *intervals = 1;
reed@google.comc34f53d2012-04-30 15:10:13 +0000425 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 runs = fRunHead->readonly_runs();
reed@google.comaf7e6942012-05-01 14:43:22 +0000427 *intervals = fRunHead->getIntervalCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 }
429 return runs;
430}
431
reed@google.comc34f53d2012-04-30 15:10:13 +0000432///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433
reed@google.com9c36a762012-05-02 18:07:33 +0000434static bool scanline_intersects(const SkRegion::RunType runs[],
435 SkRegion::RunType L, SkRegion::RunType R) {
436 runs += 2; // skip Bottom and IntervalCount
437 for (;;) {
438 if (R <= runs[0]) {
439 break;
440 }
441 if (L < runs[1]) {
442 return true;
443 }
444 runs += 2;
445 }
446 return false;
447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449bool SkRegion::intersects(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000450 SkDEBUGCODE(this->validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000451
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 if (this->isEmpty() || r.isEmpty()) {
453 return false;
454 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000455
reed@google.com9c36a762012-05-02 18:07:33 +0000456 SkIRect sect;
457 if (!sect.intersect(fBounds, r)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 return false;
459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 if (this->isRect()) {
461 return true;
462 }
reed@google.combb094b92012-11-07 14:23:42 +0000463 SkASSERT(this->isComplex());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000464
reed@google.com9c36a762012-05-02 18:07:33 +0000465 const RunType* scanline = fRunHead->findScanline(sect.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000466 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000467 if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
468 return true;
469 }
reed@google.combb094b92012-11-07 14:23:42 +0000470 if (sect.fBottom <= scanline_bottom(scanline)) {
471 break;
472 }
reed@google.com9c36a762012-05-02 18:07:33 +0000473 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000474 }
reed@google.com9c36a762012-05-02 18:07:33 +0000475 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476}
477
478bool SkRegion::intersects(const SkRegion& rgn) const {
479 if (this->isEmpty() || rgn.isEmpty()) {
480 return false;
481 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000482
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
484 return false;
485 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000486
reed@google.com9c36a762012-05-02 18:07:33 +0000487 bool weAreARect = this->isRect();
488 bool theyAreARect = rgn.isRect();
489
490 if (weAreARect && theyAreARect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 return true;
492 }
reed@google.com9c36a762012-05-02 18:07:33 +0000493 if (weAreARect) {
494 return rgn.intersects(this->getBounds());
495 }
496 if (theyAreARect) {
497 return this->intersects(rgn.getBounds());
498 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000499
reed@google.com9c36a762012-05-02 18:07:33 +0000500 // both of us are complex
reed@google.com7d4aee32012-04-30 13:29:02 +0000501 return Oper(*this, rgn, kIntersect_Op, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502}
503
reed@google.comc34f53d2012-04-30 15:10:13 +0000504///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505
reed@google.com6d428d32012-01-25 21:53:53 +0000506bool SkRegion::operator==(const SkRegion& b) const {
507 SkDEBUGCODE(validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 SkDEBUGCODE(b.validate();)
509
reed@google.com6d428d32012-01-25 21:53:53 +0000510 if (this == &b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000512 }
reed@google.com6d428d32012-01-25 21:53:53 +0000513 if (fBounds != b.fBounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000515 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000516
reed@google.com6d428d32012-01-25 21:53:53 +0000517 const SkRegion::RunHead* ah = fRunHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 const SkRegion::RunHead* bh = b.fRunHead;
519
520 // this catches empties and rects being equal
reed@google.com97fa34c2011-03-18 14:44:42 +0000521 if (ah == bh) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000523 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 // now we insist that both are complex (but different ptrs)
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000525 if (!this->isComplex() || !b.isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000527 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 return ah->fRunCount == bh->fRunCount &&
529 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
530 ah->fRunCount * sizeof(SkRegion::RunType));
531}
532
reed@google.com97fa34c2011-03-18 14:44:42 +0000533void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 SkDEBUGCODE(this->validate();)
535
reed@google.com97fa34c2011-03-18 14:44:42 +0000536 if (NULL == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 return;
reed@google.com97fa34c2011-03-18 14:44:42 +0000538 }
539 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 dst->setEmpty();
reed@google.com97fa34c2011-03-18 14:44:42 +0000541 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
543 fBounds.fRight + dx, fBounds.fBottom + dy);
reed@google.com97fa34c2011-03-18 14:44:42 +0000544 } else {
545 if (this == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 dst->fRunHead = dst->fRunHead->ensureWritable();
reed@google.com97fa34c2011-03-18 14:44:42 +0000547 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 SkRegion tmp;
reed@google.comaf7e6942012-05-01 14:43:22 +0000549 tmp.allocateRuns(*fRunHead);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 tmp.fBounds = fBounds;
551 dst->swap(tmp);
552 }
553
554 dst->fBounds.offset(dx, dy);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 const RunType* sruns = fRunHead->readonly_runs();
557 RunType* druns = dst->fRunHead->writable_runs();
558
559 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
reed@google.com97fa34c2011-03-18 14:44:42 +0000560 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 int bottom = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000562 if (bottom == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000564 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000566 *druns++ = *sruns++; // copy intervalCount;
reed@google.com97fa34c2011-03-18 14:44:42 +0000567 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 int x = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000569 if (x == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000571 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 *druns++ = (SkRegion::RunType)(x + dx);
573 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
574 }
575 *druns++ = kRunTypeSentinel; // x sentinel
576 }
577 *druns++ = kRunTypeSentinel; // y sentinel
578
579 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
580 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
581 }
582
583 SkDEBUGCODE(this->validate();)
584}
585
reed@android.com097a3512010-07-13 18:35:14 +0000586///////////////////////////////////////////////////////////////////////////////
587
reed@android.com097a3512010-07-13 18:35:14 +0000588bool SkRegion::setRects(const SkIRect rects[], int count) {
589 if (0 == count) {
reed@android.comcb342352010-07-22 18:27:53 +0000590 this->setEmpty();
591 } else {
592 this->setRect(rects[0]);
593 for (int i = 1; i < count; i++) {
594 this->op(rects[i], kUnion_Op);
reed@android.com097a3512010-07-13 18:35:14 +0000595 }
596 }
reed@android.comcb342352010-07-22 18:27:53 +0000597 return !this->isEmpty();
reed@android.com097a3512010-07-13 18:35:14 +0000598}
599
600///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601
602#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized
603#pragma warning ( push )
604#pragma warning ( disable : 4701 )
605#endif
606
607#ifdef SK_DEBUG
608static void assert_valid_pair(int left, int rite)
609{
610 SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
611}
612#else
613 #define assert_valid_pair(left, rite)
614#endif
615
616struct spanRec {
617 const SkRegion::RunType* fA_runs;
618 const SkRegion::RunType* fB_runs;
619 int fA_left, fA_rite, fB_left, fB_rite;
620 int fLeft, fRite, fInside;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000621
reed@google.comc34f53d2012-04-30 15:10:13 +0000622 void init(const SkRegion::RunType a_runs[],
623 const SkRegion::RunType b_runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 fA_left = *a_runs++;
625 fA_rite = *a_runs++;
626 fB_left = *b_runs++;
627 fB_rite = *b_runs++;
628
629 fA_runs = a_runs;
630 fB_runs = b_runs;
631 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000632
reed@google.comc34f53d2012-04-30 15:10:13 +0000633 bool done() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
635 SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
reed@google.comc34f53d2012-04-30 15:10:13 +0000636 return fA_left == SkRegion::kRunTypeSentinel &&
637 fB_left == SkRegion::kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 }
639
reed@google.comc34f53d2012-04-30 15:10:13 +0000640 void next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 assert_valid_pair(fA_left, fA_rite);
642 assert_valid_pair(fB_left, fB_rite);
643
644 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
645 bool a_flush = false;
646 bool b_flush = false;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 int a_left = fA_left;
649 int a_rite = fA_rite;
650 int b_left = fB_left;
651 int b_rite = fB_rite;
652
reed@google.comc34f53d2012-04-30 15:10:13 +0000653 if (a_left < b_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 inside = 1;
655 left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000656 if (a_rite <= b_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 rite = a_rite;
658 a_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000659 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 rite = a_left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000661 }
662 } else if (b_left < a_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 inside = 2;
664 left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000665 if (b_rite <= a_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 rite = b_rite;
667 b_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000668 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 rite = b_left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000670 }
671 } else { // a_left == b_left
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 inside = 3;
673 left = a_left; // or b_left
reed@google.comc34f53d2012-04-30 15:10:13 +0000674 if (a_rite <= b_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 rite = b_left = a_rite;
676 a_flush = true;
677 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000678 if (b_rite <= a_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 rite = a_left = b_rite;
680 b_flush = true;
681 }
682 }
683
reed@google.comc34f53d2012-04-30 15:10:13 +0000684 if (a_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 a_left = *fA_runs++;
686 a_rite = *fA_runs++;
687 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000688 if (b_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 b_left = *fB_runs++;
690 b_rite = *fB_runs++;
691 }
692
693 SkASSERT(left <= rite);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 // now update our state
696 fA_left = a_left;
697 fA_rite = a_rite;
698 fB_left = b_left;
699 fB_rite = b_rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 fLeft = left;
702 fRite = rite;
703 fInside = inside;
704 }
705};
706
707static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
708 const SkRegion::RunType b_runs[],
709 SkRegion::RunType dst[],
reed@google.comc34f53d2012-04-30 15:10:13 +0000710 int min, int max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 spanRec rec;
712 bool firstInterval = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 rec.init(a_runs, b_runs);
715
reed@google.comc34f53d2012-04-30 15:10:13 +0000716 while (!rec.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 rec.next();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 int left = rec.fLeft;
720 int rite = rec.fRite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 // add left,rite to our dst buffer (checking for coincidence
723 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
reed@google.comc34f53d2012-04-30 15:10:13 +0000724 left < rite) { // skip if equal
725 if (firstInterval || dst[-1] < left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 *dst++ = (SkRegion::RunType)(left);
727 *dst++ = (SkRegion::RunType)(rite);
728 firstInterval = false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000729 } else {
730 // update the right edge
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 dst[-1] = (SkRegion::RunType)(rite);
reed@google.comc34f53d2012-04-30 15:10:13 +0000732 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 }
734 }
735
736 *dst++ = SkRegion::kRunTypeSentinel;
737 return dst;
738}
739
740#if defined _WIN32 && _MSC_VER >= 1300
741#pragma warning ( pop )
742#endif
743
744static const struct {
745 uint8_t fMin;
746 uint8_t fMax;
747} gOpMinMax[] = {
748 { 1, 1 }, // Difference
749 { 3, 3 }, // Intersection
750 { 1, 3 }, // Union
751 { 1, 2 } // XOR
752};
753
754class RgnOper {
755public:
reed@google.comc34f53d2012-04-30 15:10:13 +0000756 RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 // need to ensure that the op enum lines up with our minmax array
758 SkASSERT(SkRegion::kDifference_Op == 0);
759 SkASSERT(SkRegion::kIntersect_Op == 1);
760 SkASSERT(SkRegion::kUnion_Op == 2);
761 SkASSERT(SkRegion::kXOR_Op == 3);
762 SkASSERT((unsigned)op <= 3);
763
764 fStartDst = dst;
765 fPrevDst = dst + 1;
766 fPrevLen = 0; // will never match a length from operate_on_span
767 fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
768
769 fMin = gOpMinMax[op].fMin;
770 fMax = gOpMinMax[op].fMax;
771 }
772
reed@google.comc34f53d2012-04-30 15:10:13 +0000773 void addSpan(int bottom, const SkRegion::RunType a_runs[],
774 const SkRegion::RunType b_runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +0000775 // skip X values and slots for the next Y+intervalCount
776 SkRegion::RunType* start = fPrevDst + fPrevLen + 2;
777 // start points to beginning of dst interval
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
779 size_t len = stop - start;
reed@google.com9c36a762012-05-02 18:07:33 +0000780 SkASSERT(len >= 1 && (len & 1) == 1);
781 SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782
reed@google.comc34f53d2012-04-30 15:10:13 +0000783 if (fPrevLen == len &&
reed@google.com9c36a762012-05-02 18:07:33 +0000784 (1 == len || !memcmp(fPrevDst, start,
785 (len - 1) * sizeof(SkRegion::RunType)))) {
reed@google.comc34f53d2012-04-30 15:10:13 +0000786 // update Y value
reed@google.com9c36a762012-05-02 18:07:33 +0000787 fPrevDst[-2] = (SkRegion::RunType)(bottom);
reed@google.comc34f53d2012-04-30 15:10:13 +0000788 } else { // accept the new span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 if (len == 1 && fPrevLen == 0) {
790 fTop = (SkRegion::RunType)(bottom); // just update our bottom
791 } else {
reed@google.com9c36a762012-05-02 18:07:33 +0000792 start[-2] = (SkRegion::RunType)(bottom);
commit-bot@chromium.orgf1177812014-04-23 19:19:44 +0000793 start[-1] = SkToS32(len >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 fPrevDst = start;
795 fPrevLen = len;
796 }
797 }
798 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000799
reed@google.comc34f53d2012-04-30 15:10:13 +0000800 int flush() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 fStartDst[0] = fTop;
802 fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
803 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
804 }
805
reed@google.com7d4aee32012-04-30 13:29:02 +0000806 bool isEmpty() const { return 0 == fPrevLen; }
807
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 uint8_t fMin, fMax;
809
810private:
811 SkRegion::RunType* fStartDst;
812 SkRegion::RunType* fPrevDst;
813 size_t fPrevLen;
814 SkRegion::RunType fTop;
815};
816
reed@google.com7d4aee32012-04-30 13:29:02 +0000817// want a unique value to signal that we exited due to quickExit
818#define QUICK_EXIT_TRUE_COUNT (-1)
819
reed@google.com1deaab52011-10-06 13:11:46 +0000820static int operate(const SkRegion::RunType a_runs[],
821 const SkRegion::RunType b_runs[],
822 SkRegion::RunType dst[],
reed@google.com7d4aee32012-04-30 13:29:02 +0000823 SkRegion::Op op,
824 bool quickExit) {
reed@google.com9c36a762012-05-02 18:07:33 +0000825 const SkRegion::RunType gEmptyScanline[] = {
826 0, // dummy bottom value
827 0, // zero intervals
reed@google.com5f7c8a52012-05-03 16:17:38 +0000828 SkRegion::kRunTypeSentinel,
829 // just need a 2nd value, since spanRec.init() reads 2 values, even
830 // though if the first value is the sentinel, it ignores the 2nd value.
831 // w/o the 2nd value here, we might read uninitialized memory.
832 // This happens when we are using gSentinel, which is pointing at
833 // our sentinel value.
834 0
reed@android.comfbb02e72010-04-13 14:52:52 +0000835 };
reed@google.com9c36a762012-05-02 18:07:33 +0000836 const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837
838 int a_top = *a_runs++;
839 int a_bot = *a_runs++;
840 int b_top = *b_runs++;
841 int b_bot = *b_runs++;
842
reed@google.com9c36a762012-05-02 18:07:33 +0000843 a_runs += 1; // skip the intervalCount;
844 b_runs += 1; // skip the intervalCount;
845
846 // Now a_runs and b_runs to their intervals (or sentinel)
847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 assert_sentinel(a_top, false);
849 assert_sentinel(a_bot, false);
850 assert_sentinel(b_top, false);
851 assert_sentinel(b_bot, false);
852
853 RgnOper oper(SkMin32(a_top, b_top), dst, op);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000856
reed@google.com1deaab52011-10-06 13:11:46 +0000857 while (a_bot < SkRegion::kRunTypeSentinel ||
858 b_bot < SkRegion::kRunTypeSentinel) {
859 int top, bot SK_INIT_TO_AVOID_WARNING;
reed@android.comfbb02e72010-04-13 14:52:52 +0000860 const SkRegion::RunType* run0 = gSentinel;
861 const SkRegion::RunType* run1 = gSentinel;
reed@google.com1deaab52011-10-06 13:11:46 +0000862 bool a_flush = false;
863 bool b_flush = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864
reed@google.com1deaab52011-10-06 13:11:46 +0000865 if (a_top < b_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 top = a_top;
867 run0 = a_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000868 if (a_bot <= b_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 bot = a_bot;
870 a_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000871 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 bot = a_top = b_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000873 }
874 } else if (b_top < a_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 top = b_top;
876 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000877 if (b_bot <= a_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 bot = b_bot;
879 b_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000880 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 bot = b_top = a_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000882 }
883 } else { // a_top == b_top
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 top = a_top; // or b_top
885 run0 = a_runs;
886 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000887 if (a_bot <= b_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 bot = b_top = a_bot;
889 a_flush = true;
890 }
reed@google.com1deaab52011-10-06 13:11:46 +0000891 if (b_bot <= a_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 bot = a_top = b_bot;
893 b_flush = true;
894 }
895 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000896
reed@google.com1deaab52011-10-06 13:11:46 +0000897 if (top > prevBot) {
reed@android.comfbb02e72010-04-13 14:52:52 +0000898 oper.addSpan(top, gSentinel, gSentinel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 }
reed@google.com1deaab52011-10-06 13:11:46 +0000900 oper.addSpan(bot, run0, run1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901
reed@google.com7d4aee32012-04-30 13:29:02 +0000902 if (quickExit && !oper.isEmpty()) {
903 return QUICK_EXIT_TRUE_COUNT;
904 }
905
reed@google.com1deaab52011-10-06 13:11:46 +0000906 if (a_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000907 a_runs = skip_intervals(a_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 a_top = a_bot;
909 a_bot = *a_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000910 a_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000911 if (a_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 a_top = a_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000913 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 }
reed@google.com1deaab52011-10-06 13:11:46 +0000915 if (b_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000916 b_runs = skip_intervals(b_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 b_top = b_bot;
918 b_bot = *b_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000919 b_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000920 if (b_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 b_top = b_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000922 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000924
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 prevBot = bot;
926 }
927 return oper.flush();
928}
929
930///////////////////////////////////////////////////////////////////////////////
931
932/* Given count RunTypes in a complex region, return the worst case number of
933 logical intervals that represents (i.e. number of rects that would be
934 returned from the iterator).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000935
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 We could just return count/2, since there must be at least 2 values per
937 interval, but we can first trim off the const overhead of the initial TOP
938 value, plus the final BOTTOM + 2 sentinels.
939 */
caryclark@google.com803eceb2012-06-06 12:09:34 +0000940#if 0 // UNUSED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941static int count_to_intervals(int count) {
942 SkASSERT(count >= 6); // a single rect is 6 values
943 return (count - 4) >> 1;
944}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000945#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946
947/* Given a number of intervals, what is the worst case representation of that
948 many intervals?
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000949
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 Worst case (from a storage perspective), is a vertical stack of single
reed@google.com9c36a762012-05-02 18:07:33 +0000951 intervals: TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 */
953static int intervals_to_count(int intervals) {
reed@google.com9c36a762012-05-02 18:07:33 +0000954 return 1 + intervals * 5 + 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955}
956
reed@google.comaf7e6942012-05-01 14:43:22 +0000957/* Given the intervalCounts of RunTypes in two regions, return the worst-case number
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 of RunTypes need to store the result after a region-op.
959 */
reed@google.comaf7e6942012-05-01 14:43:22 +0000960static int compute_worst_case_count(int a_intervals, int b_intervals) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
962 int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
963 // convert back to number of RunType values
964 return intervals_to_count(intervals);
965}
966
reed@google.com7d4aee32012-04-30 13:29:02 +0000967static bool setEmptyCheck(SkRegion* result) {
968 return result ? result->setEmpty() : false;
969}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970
reed@google.com7d4aee32012-04-30 13:29:02 +0000971static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
972 return result ? result->setRect(rect) : !rect.isEmpty();
973}
974
975static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
976 return result ? result->setRegion(rgn) : !rgn.isEmpty();
977}
978
979bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
980 SkRegion* result) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 SkASSERT((unsigned)op < kOpCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000982
reed@google.com7d4aee32012-04-30 13:29:02 +0000983 if (kReplace_Op == op) {
984 return setRegionCheck(result, rgnbOrig);
985 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 // swith to using pointers, so we can swap them as needed
988 const SkRegion* rgna = &rgnaOrig;
989 const SkRegion* rgnb = &rgnbOrig;
990 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
991
992 // collaps difference and reverse-difference into just difference
reed@google.com7d4aee32012-04-30 13:29:02 +0000993 if (kReverseDifference_Op == op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 SkTSwap<const SkRegion*>(rgna, rgnb);
995 op = kDifference_Op;
996 }
997
998 SkIRect bounds;
999 bool a_empty = rgna->isEmpty();
1000 bool b_empty = rgnb->isEmpty();
1001 bool a_rect = rgna->isRect();
1002 bool b_rect = rgnb->isRect();
1003
1004 switch (op) {
1005 case kDifference_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001006 if (a_empty) {
1007 return setEmptyCheck(result);
1008 }
reed@google.com0d102802012-05-31 18:28:59 +00001009 if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
1010 rgnb->fBounds)) {
reed@google.com7d4aee32012-04-30 13:29:02 +00001011 return setRegionCheck(result, *rgna);
1012 }
reed@google.com0d102802012-05-31 18:28:59 +00001013 if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
1014 return setEmptyCheck(result);
1015 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 break;
1017
1018 case kIntersect_Op:
1019 if ((a_empty | b_empty)
reed@google.com7d4aee32012-04-30 13:29:02 +00001020 || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
1021 return setEmptyCheck(result);
1022 }
1023 if (a_rect & b_rect) {
1024 return setRectCheck(result, bounds);
1025 }
reed@google.com633c32b2013-01-31 15:24:24 +00001026 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1027 return setRegionCheck(result, *rgnb);
1028 }
1029 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1030 return setRegionCheck(result, *rgna);
1031 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 break;
1033
1034 case kUnion_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001035 if (a_empty) {
1036 return setRegionCheck(result, *rgnb);
1037 }
1038 if (b_empty) {
1039 return setRegionCheck(result, *rgna);
1040 }
1041 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1042 return setRegionCheck(result, *rgna);
1043 }
1044 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1045 return setRegionCheck(result, *rgnb);
1046 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 break;
1048
1049 case kXOR_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001050 if (a_empty) {
1051 return setRegionCheck(result, *rgnb);
1052 }
1053 if (b_empty) {
1054 return setRegionCheck(result, *rgna);
1055 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 break;
1057 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001058 SkDEBUGFAIL("unknown region op");
reed@google.com7d4aee32012-04-30 13:29:02 +00001059 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
1061
1062 RunType tmpA[kRectRegionRuns];
1063 RunType tmpB[kRectRegionRuns];
1064
reed@google.comaf7e6942012-05-01 14:43:22 +00001065 int a_intervals, b_intervals;
1066 const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
1067 const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068
reed@google.comaf7e6942012-05-01 14:43:22 +00001069 int dstCount = compute_worst_case_count(a_intervals, b_intervals);
1070 SkAutoSTMalloc<256, RunType> array(dstCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071
reed@google.com9c36a762012-05-02 18:07:33 +00001072#ifdef SK_DEBUG
1073// Sometimes helpful to seed everything with a known value when debugging
1074// sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
1075#endif
1076
reed@google.com7d4aee32012-04-30 13:29:02 +00001077 int count = operate(a_runs, b_runs, array.get(), op, NULL == result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 SkASSERT(count <= dstCount);
reed@google.comaf7e6942012-05-01 14:43:22 +00001079
reed@google.com7d4aee32012-04-30 13:29:02 +00001080 if (result) {
1081 SkASSERT(count >= 0);
1082 return result->setRuns(array.get(), count);
1083 } else {
1084 return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
1085 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086}
1087
reed@google.com7d4aee32012-04-30 13:29:02 +00001088bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
1089 SkDEBUGCODE(this->validate();)
1090 return SkRegion::Oper(rgna, rgnb, op, this);
1091}
1092
1093///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094
1095#include "SkBuffer.h"
1096
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001097size_t SkRegion::writeToMemory(void* storage) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 if (NULL == storage) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001099 size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 if (!this->isEmpty()) {
1101 size += sizeof(fBounds);
1102 if (this->isComplex()) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001103 size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 size += fRunHead->fRunCount * sizeof(RunType);
1105 }
1106 }
1107 return size;
1108 }
1109
1110 SkWBuffer buffer(storage);
1111
1112 if (this->isEmpty()) {
1113 buffer.write32(-1);
1114 } else {
1115 bool isRect = this->isRect();
1116
1117 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1118 buffer.write(&fBounds, sizeof(fBounds));
1119
1120 if (!isRect) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001121 buffer.write32(fRunHead->getYSpanCount());
1122 buffer.write32(fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 buffer.write(fRunHead->readonly_runs(),
1124 fRunHead->fRunCount * sizeof(RunType));
1125 }
1126 }
1127 return buffer.pos();
1128}
1129
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001130size_t SkRegion::readFromMemory(const void* storage, size_t length) {
1131 SkRBufferWithSizeCheck buffer(storage, length);
1132 SkRegion tmp;
1133 int32_t count;
skia.committer@gmail.com382fde32013-11-06 07:02:11 +00001134
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +00001135 if (buffer.readS32(&count) && (count >= 0) && buffer.read(&tmp.fBounds, sizeof(tmp.fBounds))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 if (count == 0) {
1137 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1138 } else {
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +00001139 int32_t ySpanCount, intervalCount;
1140 if (buffer.readS32(&ySpanCount) && buffer.readS32(&intervalCount)) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001141 tmp.allocateRuns(count, ySpanCount, intervalCount);
1142 buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
1143 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 }
1145 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001146 size_t sizeRead = 0;
1147 if (buffer.isValid()) {
1148 this->swap(tmp);
1149 sizeRead = buffer.pos();
1150 }
1151 return sizeRead;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152}
1153
reed@google.com0a0a2362011-03-23 13:51:55 +00001154///////////////////////////////////////////////////////////////////////////////
1155
1156const SkRegion& SkRegion::GetEmptyRegion() {
1157 static SkRegion gEmpty;
1158 return gEmpty;
1159}
1160
1161///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162
1163#ifdef SK_DEBUG
1164
reed@google.com9c36a762012-05-02 18:07:33 +00001165// Starts with first X-interval, and returns a ptr to the X-sentinel
1166static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) {
1167 // want to track that our intevals are all disjoint, such that
1168 // prev-right < next-left. We rely on this optimization in places such as
1169 // contains().
1170 //
1171 SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001172
reed@google.com9c36a762012-05-02 18:07:33 +00001173 while (runs[0] < SkRegion::kRunTypeSentinel) {
1174 SkASSERT(prevR < runs[0]);
1175 SkASSERT(runs[0] < runs[1]);
1176 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
1177 prevR = runs[1];
1178 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 }
reed@google.com9c36a762012-05-02 18:07:33 +00001180 return runs;
1181}
1182
sugoi@google.come0e385c2013-03-11 18:50:03 +00001183static void compute_bounds(const SkRegion::RunType runs[],
reed@google.com9c36a762012-05-02 18:07:33 +00001184 SkIRect* bounds, int* ySpanCountPtr,
1185 int* intervalCountPtr) {
1186 assert_sentinel(runs[0], false); // top
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001187
reed@google.com9c36a762012-05-02 18:07:33 +00001188 int left = SK_MaxS32;
1189 int rite = SK_MinS32;
1190 int bot;
1191 int ySpanCount = 0;
1192 int intervalCount = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001193
reed@google.com9c36a762012-05-02 18:07:33 +00001194 bounds->fTop = *runs++;
1195 do {
1196 bot = *runs++;
1197 SkASSERT(SkRegion::kRunTypeSentinel > bot);
1198
1199 ySpanCount += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001200
reed@google.com9c36a762012-05-02 18:07:33 +00001201 runs += 1; // skip intervalCount for now
1202 if (*runs < SkRegion::kRunTypeSentinel) {
1203 if (left > *runs) {
1204 left = *runs;
1205 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.com9c36a762012-05-02 18:07:33 +00001207 const SkRegion::RunType* prev = runs;
1208 runs = skip_intervals_slow(runs);
commit-bot@chromium.orgf1177812014-04-23 19:19:44 +00001209 int intervals = SkToInt((runs - prev) >> 1);
reed@google.com9c36a762012-05-02 18:07:33 +00001210 SkASSERT(prev[-1] == intervals);
1211 intervalCount += intervals;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001212
reed@google.com9c36a762012-05-02 18:07:33 +00001213 if (rite < runs[-1]) {
1214 rite = runs[-1];
1215 }
1216 } else {
1217 SkASSERT(0 == runs[-1]); // no intervals
1218 }
1219 SkASSERT(SkRegion::kRunTypeSentinel == *runs);
1220 runs += 1;
1221 } while (SkRegion::kRunTypeSentinel != *runs);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001222
reed@google.com9c36a762012-05-02 18:07:33 +00001223 bounds->fLeft = left;
1224 bounds->fRight = rite;
1225 bounds->fBottom = bot;
1226 *ySpanCountPtr = ySpanCount;
1227 *intervalCountPtr = intervalCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228}
1229
reed@google.comc34f53d2012-04-30 15:10:13 +00001230void SkRegion::validate() const {
1231 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 // check for explicit empty (the zero rect), so we can compare rects to know when
1233 // two regions are equal (i.e. emptyRectA == emptyRectB)
1234 // this is stricter than just asserting fBounds.isEmpty()
1235 SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
reed@google.comc34f53d2012-04-30 15:10:13 +00001236 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 SkASSERT(!fBounds.isEmpty());
reed@google.comc34f53d2012-04-30 15:10:13 +00001238 if (!this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 SkASSERT(fRunHead->fRefCnt >= 1);
reed@google.com9c36a762012-05-02 18:07:33 +00001240 SkASSERT(fRunHead->fRunCount > kRectRegionRuns);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241
1242 const RunType* run = fRunHead->readonly_runs();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243
1244 // check that our bounds match our runs
1245 {
1246 SkIRect bounds;
reed@google.comaf7e6942012-05-01 14:43:22 +00001247 int ySpanCount, intervalCount;
sugoi@google.come0e385c2013-03-11 18:50:03 +00001248 compute_bounds(run, &bounds, &ySpanCount, &intervalCount);
reed@google.com9c36a762012-05-02 18:07:33 +00001249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 SkASSERT(bounds == fBounds);
reed@google.comaf7e6942012-05-01 14:43:22 +00001251 SkASSERT(ySpanCount > 0);
1252 SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
reed@google.com9c36a762012-05-02 18:07:33 +00001253 // SkASSERT(intervalCount > 1);
reed@google.comaf7e6942012-05-01 14:43:22 +00001254 SkASSERT(fRunHead->getIntervalCount() == intervalCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257 }
1258}
1259
reed@google.comc34f53d2012-04-30 15:10:13 +00001260void SkRegion::dump() const {
1261 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 SkDebugf(" rgn: empty\n");
reed@google.comc34f53d2012-04-30 15:10:13 +00001263 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
reed@google.com9c36a762012-05-02 18:07:33 +00001265 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 const RunType* runs = fRunHead->readonly_runs();
1267 for (int i = 0; i < fRunHead->fRunCount; i++)
1268 SkDebugf(" %d", runs[i]);
1269 }
1270 SkDebugf("\n");
1271 }
1272}
1273
1274#endif
1275
reed@google.comc34f53d2012-04-30 15:10:13 +00001276///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
1278SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1279 this->reset(rgn);
1280}
1281
1282bool SkRegion::Iterator::rewind() {
1283 if (fRgn) {
1284 this->reset(*fRgn);
1285 return true;
1286 }
1287 return false;
1288}
1289
1290void SkRegion::Iterator::reset(const SkRegion& rgn) {
1291 fRgn = &rgn;
1292 if (rgn.isEmpty()) {
1293 fDone = true;
1294 } else {
1295 fDone = false;
1296 if (rgn.isRect()) {
1297 fRect = rgn.fBounds;
1298 fRuns = NULL;
1299 } else {
1300 fRuns = rgn.fRunHead->readonly_runs();
reed@google.com9c36a762012-05-02 18:07:33 +00001301 fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
1302 fRuns += 5;
1303 // Now fRuns points to the 2nd interval (or x-sentinel)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 }
1305 }
1306}
1307
1308void SkRegion::Iterator::next() {
1309 if (fDone) {
1310 return;
1311 }
1312
1313 if (fRuns == NULL) { // rect case
1314 fDone = true;
1315 return;
1316 }
1317
1318 const RunType* runs = fRuns;
1319
1320 if (runs[0] < kRunTypeSentinel) { // valid X value
1321 fRect.fLeft = runs[0];
1322 fRect.fRight = runs[1];
1323 runs += 2;
1324 } else { // we're at the end of a line
1325 runs += 1;
1326 if (runs[0] < kRunTypeSentinel) { // valid Y value
reed@google.com9c36a762012-05-02 18:07:33 +00001327 int intervals = runs[1];
1328 if (0 == intervals) { // empty line
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 fRect.fTop = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001330 runs += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 } else {
1332 fRect.fTop = fRect.fBottom;
1333 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 fRect.fBottom = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001336 assert_sentinel(runs[2], false);
1337 assert_sentinel(runs[3], false);
1338 fRect.fLeft = runs[2];
1339 fRect.fRight = runs[3];
1340 runs += 4;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 } else { // end of rgn
1342 fDone = true;
1343 }
1344 }
1345 fRuns = runs;
1346}
1347
1348SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1349 : fIter(rgn), fClip(clip), fDone(true) {
1350 const SkIRect& r = fIter.rect();
1351
1352 while (!fIter.done()) {
1353 if (r.fTop >= clip.fBottom) {
1354 break;
1355 }
1356 if (fRect.intersect(clip, r)) {
1357 fDone = false;
1358 break;
1359 }
1360 fIter.next();
1361 }
1362}
1363
1364void SkRegion::Cliperator::next() {
1365 if (fDone) {
1366 return;
1367 }
1368
1369 const SkIRect& r = fIter.rect();
1370
1371 fDone = true;
1372 fIter.next();
1373 while (!fIter.done()) {
1374 if (r.fTop >= fClip.fBottom) {
1375 break;
1376 }
1377 if (fRect.intersect(fClip, r)) {
1378 fDone = false;
1379 break;
1380 }
1381 fIter.next();
1382 }
1383}
1384
reed@google.come72766f2011-01-07 15:00:44 +00001385///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386
reed@google.come72766f2011-01-07 15:00:44 +00001387SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1388 int right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 SkDEBUGCODE(rgn.validate();)
1390
1391 const SkIRect& r = rgn.getBounds();
1392
1393 fDone = true;
reed@google.come72766f2011-01-07 15:00:44 +00001394 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1395 right > r.fLeft && left < r.fRight) {
1396 if (rgn.isRect()) {
1397 if (left < r.fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 left = r.fLeft;
reed@google.come72766f2011-01-07 15:00:44 +00001399 }
1400 if (right > r.fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 right = r.fRight;
reed@google.come72766f2011-01-07 15:00:44 +00001402 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 fLeft = left;
1404 fRight = right;
1405 fRuns = NULL; // means we're a rect, not a rgn
1406 fDone = false;
reed@google.come72766f2011-01-07 15:00:44 +00001407 } else {
reed@google.com9c36a762012-05-02 18:07:33 +00001408 const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
1409 runs += 2; // skip Bottom and IntervalCount
1410 for (;;) {
1411 // runs[0..1] is to the right of the span, so we're done
1412 if (runs[0] >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 break;
1414 }
reed@google.com9c36a762012-05-02 18:07:33 +00001415 // runs[0..1] is to the left of the span, so continue
1416 if (runs[1] <= left) {
1417 runs += 2;
1418 continue;
1419 }
1420 // runs[0..1] intersects the span
1421 fRuns = runs;
1422 fLeft = left;
1423 fRight = right;
1424 fDone = false;
1425 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 }
1427 }
1428 }
1429}
1430
reed@google.come72766f2011-01-07 15:00:44 +00001431bool SkRegion::Spanerator::next(int* left, int* right) {
1432 if (fDone) {
1433 return false;
1434 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435
reed@google.come72766f2011-01-07 15:00:44 +00001436 if (fRuns == NULL) { // we're a rect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 fDone = true; // ok, now we're done
reed@google.come72766f2011-01-07 15:00:44 +00001438 if (left) {
1439 *left = fLeft;
1440 }
1441 if (right) {
1442 *right = fRight;
1443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 return true; // this interval is legal
1445 }
1446
1447 const SkRegion::RunType* runs = fRuns;
1448
reed@google.come72766f2011-01-07 15:00:44 +00001449 if (runs[0] >= fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 fDone = true;
1451 return false;
1452 }
1453
1454 SkASSERT(runs[1] > fLeft);
1455
reed@google.come72766f2011-01-07 15:00:44 +00001456 if (left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 *left = SkMax32(fLeft, runs[0]);
reed@google.come72766f2011-01-07 15:00:44 +00001458 }
1459 if (right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 *right = SkMin32(fRight, runs[1]);
reed@google.come72766f2011-01-07 15:00:44 +00001461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 fRuns = runs + 2;
1463 return true;
1464}
1465
1466///////////////////////////////////////////////////////////////////////////////
1467
1468#ifdef SK_DEBUG
1469
1470bool SkRegion::debugSetRuns(const RunType runs[], int count) {
1471 // we need to make a copy, since the real method may modify the array, and
1472 // so it cannot be const.
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 SkAutoTArray<RunType> storage(count);
1475 memcpy(storage.get(), runs, count * sizeof(RunType));
1476 return this->setRuns(storage.get(), count);
1477}
1478
1479#endif