blob: e873ebb4534cc78391e33ce1de619a28f01bbd7c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
herbb906daf2015-09-29 09:37:59 -07009#include "SkAtomics.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkRegionPriv.h"
11#include "SkTemplates.h"
reed@google.com9c36a762012-05-02 18:07:33 +000012#include "SkUtils.h"
13
14/* Region Layout
15 *
16 * TOP
17 *
18 * [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
19 * ...
20 *
21 * Y-Sentinel
22 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24SkDEBUGCODE(int32_t gRgnAllocCounter;)
25
26/////////////////////////////////////////////////////////////////////////////////////////////////
27
reed@google.com9c36a762012-05-02 18:07:33 +000028/* Pass in the beginning with the intervals.
29 * We back up 1 to read the interval-count.
30 * Return the beginning of the next scanline (i.e. the next Y-value)
31 */
32static SkRegion::RunType* skip_intervals(const SkRegion::RunType runs[]) {
33 int intervals = runs[-1];
34#ifdef SK_DEBUG
35 if (intervals > 0) {
36 SkASSERT(runs[0] < runs[1]);
37 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
38 } else {
39 SkASSERT(0 == intervals);
40 SkASSERT(SkRegion::kRunTypeSentinel == runs[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 }
reed@google.com9c36a762012-05-02 18:07:33 +000042#endif
43 runs += intervals * 2 + 1;
44 return const_cast<SkRegion::RunType*>(runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +000045}
46
reed@google.com9c36a762012-05-02 18:07:33 +000047bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
48 SkIRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 assert_sentinel(runs[0], false); // top
reed@google.com9c36a762012-05-02 18:07:33 +000050 SkASSERT(count >= kRectRegionRuns);
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
reed@google.comc34f53d2012-04-30 15:10:13 +000052 if (count == kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +000054 SkASSERT(1 == runs[2]);
55 assert_sentinel(runs[3], false); // left
56 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 assert_sentinel(runs[5], true);
reed@google.com9c36a762012-05-02 18:07:33 +000058 assert_sentinel(runs[6], true);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000059
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 SkASSERT(runs[0] < runs[1]); // valid height
reed@google.com9c36a762012-05-02 18:07:33 +000061 SkASSERT(runs[3] < runs[4]); // valid width
rmistry@google.comfbfcd562012-08-23 18:09:54 +000062
reed@google.com9c36a762012-05-02 18:07:33 +000063 bounds->set(runs[3], runs[0], runs[4], runs[1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 return true;
65 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 return false;
67}
68
69//////////////////////////////////////////////////////////////////////////
70
reed@google.com0a0a2362011-03-23 13:51:55 +000071SkRegion::SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 fBounds.set(0, 0, 0, 0);
73 fRunHead = SkRegion_gEmptyRunHeadPtr;
74}
75
reed@google.com0a0a2362011-03-23 13:51:55 +000076SkRegion::SkRegion(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
78 this->setRegion(src);
79}
80
reed@google.com0a0a2362011-03-23 13:51:55 +000081SkRegion::SkRegion(const SkIRect& rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
83 this->setRect(rect);
84}
85
reed@google.com0a0a2362011-03-23 13:51:55 +000086SkRegion::~SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 this->freeRuns();
88}
89
reed@google.com0a0a2362011-03-23 13:51:55 +000090void SkRegion::freeRuns() {
bungeman@google.comb77b69f2013-01-24 16:38:23 +000091 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 SkASSERT(fRunHead->fRefCnt >= 1);
reed@google.com0a0a2362011-03-23 13:51:55 +000093 if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 //SkASSERT(gRgnAllocCounter > 0);
95 //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
96 //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
97 sk_free(fRunHead);
98 }
99 }
100}
101
reed@google.comaf7e6942012-05-01 14:43:22 +0000102void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
103 fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
104}
105
reed@google.com9c36a762012-05-02 18:07:33 +0000106void SkRegion::allocateRuns(int count) {
107 fRunHead = RunHead::Alloc(count);
108}
109
reed@google.comaf7e6942012-05-01 14:43:22 +0000110void SkRegion::allocateRuns(const RunHead& head) {
111 fRunHead = RunHead::Alloc(head.fRunCount,
112 head.getYSpanCount(),
113 head.getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114}
115
reed@google.com0a0a2362011-03-23 13:51:55 +0000116SkRegion& SkRegion::operator=(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 (void)this->setRegion(src);
118 return *this;
119}
120
reed@google.com0a0a2362011-03-23 13:51:55 +0000121void SkRegion::swap(SkRegion& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 SkTSwap<SkIRect>(fBounds, other.fBounds);
123 SkTSwap<RunHead*>(fRunHead, other.fRunHead);
124}
125
commit-bot@chromium.org5dd567c2013-07-17 21:39:28 +0000126int SkRegion::computeRegionComplexity() const {
127 if (this->isEmpty()) {
128 return 0;
129 } else if (this->isRect()) {
130 return 1;
131 }
132 return fRunHead->getIntervalCount();
133}
skia.committer@gmail.comf7d01ce2013-07-18 07:00:56 +0000134
reed@google.com0a0a2362011-03-23 13:51:55 +0000135bool SkRegion::setEmpty() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 this->freeRuns();
137 fBounds.set(0, 0, 0, 0);
138 fRunHead = SkRegion_gEmptyRunHeadPtr;
139 return false;
140}
141
reed@google.com0a0a2362011-03-23 13:51:55 +0000142bool SkRegion::setRect(int32_t left, int32_t top,
143 int32_t right, int32_t bottom) {
144 if (left >= right || top >= bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 return this->setEmpty();
reed@google.com0a0a2362011-03-23 13:51:55 +0000146 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 this->freeRuns();
148 fBounds.set(left, top, right, bottom);
149 fRunHead = SkRegion_gRectRunHeadPtr;
150 return true;
151}
152
reed@google.com0a0a2362011-03-23 13:51:55 +0000153bool SkRegion::setRect(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
155}
156
reed@google.com0a0a2362011-03-23 13:51:55 +0000157bool SkRegion::setRegion(const SkRegion& src) {
158 if (this != &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 this->freeRuns();
160
161 fBounds = src.fBounds;
162 fRunHead = src.fRunHead;
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000163 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 sk_atomic_inc(&fRunHead->fRefCnt);
reed@google.com0a0a2362011-03-23 13:51:55 +0000165 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 }
167 return fRunHead != SkRegion_gEmptyRunHeadPtr;
168}
169
reed@google.com0a0a2362011-03-23 13:51:55 +0000170bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 SkRegion tmp(rect);
172
173 return this->op(tmp, rgn, op);
174}
175
reed@google.com0a0a2362011-03-23 13:51:55 +0000176bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 SkRegion tmp(rect);
178
179 return this->op(rgn, tmp, op);
180}
181
reed@google.com0a0a2362011-03-23 13:51:55 +0000182///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
djsollen@google.com56c69772011-11-08 19:00:26 +0000184#ifdef SK_BUILD_FOR_ANDROID
bungeman@google.com2c8e9d42013-10-11 19:23:56 +0000185#include <stdio.h>
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;
mtklein002c2ce2015-08-26 05:43:22 -0700195 char* result = (char*)sk_malloc_throw(max);
halcanary96fcdcc2015-08-27 07:41:13 -0700196 if (result == nullptr) {
197 return nullptr;
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000198 }
caryclark8f186432016-10-06 11:46:25 -0700199 count = snprintf(result, max, "SkRegion(");
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000200 iter.reset(*this);
201 while (!iter.done()) {
202 const SkIRect& r = iter.rect();
caryclark8f186432016-10-06 11:46:25 -0700203 count += snprintf(result+count, max - count,
204 "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000205 iter.next();
206 }
caryclark8f186432016-10-06 11:46:25 -0700207 count += snprintf(result+count, max - count, ")");
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000208 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);
Hal Canary251bf3e2017-02-16 12:42:24 -0500287 SkASSERT(this->isComplex());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 // must call this before we can write directly into runs()
291 // in case we are sharing the buffer with another region (copy on write)
292 fRunHead = fRunHead->ensureWritable();
293 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
reed@google.com9c36a762012-05-02 18:07:33 +0000294 fRunHead->computeRunBounds(&fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295
296 SkDEBUGCODE(this->validate();)
297
298 return true;
299}
300
301void SkRegion::BuildRectRuns(const SkIRect& bounds,
reed@google.comc34f53d2012-04-30 15:10:13 +0000302 RunType runs[kRectRegionRuns]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 runs[0] = bounds.fTop;
304 runs[1] = bounds.fBottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000305 runs[2] = 1; // 1 interval for this scanline
306 runs[3] = bounds.fLeft;
307 runs[4] = bounds.fRight;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 runs[5] = kRunTypeSentinel;
reed@google.com9c36a762012-05-02 18:07:33 +0000309 runs[6] = kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310}
311
reed@google.comc34f53d2012-04-30 15:10:13 +0000312bool SkRegion::contains(int32_t x, int32_t y) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000313 SkDEBUGCODE(this->validate();)
314
reed@google.comc34f53d2012-04-30 15:10:13 +0000315 if (!fBounds.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000317 }
318 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000322
reed@google.com9c36a762012-05-02 18:07:33 +0000323 const RunType* runs = fRunHead->findScanline(y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324
reed@google.com9c36a762012-05-02 18:07:33 +0000325 // Skip the Bottom and IntervalCount
326 runs += 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000327
reed@google.com9c36a762012-05-02 18:07:33 +0000328 // Just walk this scanline, checking each interval. The X-sentinel will
329 // appear as a left-inteval (runs[0]) and should abort the search.
330 //
331 // We could do a bsearch, using interval-count (runs[1]), but need to time
332 // when that would be worthwhile.
333 //
334 for (;;) {
335 if (x < runs[0]) {
336 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 }
reed@google.com9c36a762012-05-02 18:07:33 +0000338 if (x < runs[1]) {
339 return true;
340 }
341 runs += 2;
342 }
343 return false;
344}
345
mike@reedtribe.org796a1752012-11-07 03:39:46 +0000346static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
347 return runs[0];
348}
349
reed@google.com9c36a762012-05-02 18:07:33 +0000350static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
351 // skip [B N [L R]... S]
352 return runs + 2 + runs[1] * 2 + 1;
353}
354
355static bool scanline_contains(const SkRegion::RunType runs[],
356 SkRegion::RunType L, SkRegion::RunType R) {
357 runs += 2; // skip Bottom and IntervalCount
358 for (;;) {
359 if (L < runs[0]) {
360 break;
361 }
362 if (R <= runs[1]) {
363 return true;
364 }
365 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 }
367 return false;
368}
369
reed@google.comc34f53d2012-04-30 15:10:13 +0000370bool SkRegion::contains(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000371 SkDEBUGCODE(this->validate();)
372
373 if (!fBounds.contains(r)) {
374 return false;
375 }
376 if (this->isRect()) {
377 return true;
378 }
reed@google.com9c36a762012-05-02 18:07:33 +0000379 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000380
reed@google.com9c36a762012-05-02 18:07:33 +0000381 const RunType* scanline = fRunHead->findScanline(r.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000382 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000383 if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
384 return false;
385 }
reed@google.combb094b92012-11-07 14:23:42 +0000386 if (r.fBottom <= scanline_bottom(scanline)) {
387 break;
388 }
reed@google.com9c36a762012-05-02 18:07:33 +0000389 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000390 }
reed@google.com9c36a762012-05-02 18:07:33 +0000391 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392}
393
reed@google.comc34f53d2012-04-30 15:10:13 +0000394bool SkRegion::contains(const SkRegion& rgn) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000395 SkDEBUGCODE(this->validate();)
396 SkDEBUGCODE(rgn.validate();)
397
reed@google.comc34f53d2012-04-30 15:10:13 +0000398 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000400 }
401 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000403 }
reed@google.com9c36a762012-05-02 18:07:33 +0000404 if (rgn.isRect()) {
405 return this->contains(rgn.getBounds());
406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407
reed@google.com7d4aee32012-04-30 13:29:02 +0000408 /*
409 * A contains B is equivalent to
410 * B - A == 0
411 */
halcanary96fcdcc2015-08-27 07:41:13 -0700412 return !Oper(rgn, *this, kDifference_Op, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413}
414
reed@google.comc34f53d2012-04-30 15:10:13 +0000415const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
reed@google.comaf7e6942012-05-01 14:43:22 +0000416 int* intervals) const {
417 SkASSERT(tmpStorage && intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 const RunType* runs = tmpStorage;
419
reed@google.comc34f53d2012-04-30 15:10:13 +0000420 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 tmpStorage[0] = kRunTypeSentinel;
reed@google.comaf7e6942012-05-01 14:43:22 +0000422 *intervals = 0;
reed@google.comc34f53d2012-04-30 15:10:13 +0000423 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 BuildRectRuns(fBounds, tmpStorage);
reed@google.comaf7e6942012-05-01 14:43:22 +0000425 *intervals = 1;
reed@google.comc34f53d2012-04-30 15:10:13 +0000426 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 runs = fRunHead->readonly_runs();
reed@google.comaf7e6942012-05-01 14:43:22 +0000428 *intervals = fRunHead->getIntervalCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 }
430 return runs;
431}
432
reed@google.comc34f53d2012-04-30 15:10:13 +0000433///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434
reed@google.com9c36a762012-05-02 18:07:33 +0000435static bool scanline_intersects(const SkRegion::RunType runs[],
436 SkRegion::RunType L, SkRegion::RunType R) {
437 runs += 2; // skip Bottom and IntervalCount
438 for (;;) {
439 if (R <= runs[0]) {
440 break;
441 }
442 if (L < runs[1]) {
443 return true;
444 }
445 runs += 2;
446 }
447 return false;
448}
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450bool SkRegion::intersects(const SkIRect& r) const {
reed@google.com9c36a762012-05-02 18:07:33 +0000451 SkDEBUGCODE(this->validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000452
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 if (this->isEmpty() || r.isEmpty()) {
454 return false;
455 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000456
reed@google.com9c36a762012-05-02 18:07:33 +0000457 SkIRect sect;
458 if (!sect.intersect(fBounds, r)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 return false;
460 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 if (this->isRect()) {
462 return true;
463 }
reed@google.combb094b92012-11-07 14:23:42 +0000464 SkASSERT(this->isComplex());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000465
reed@google.com9c36a762012-05-02 18:07:33 +0000466 const RunType* scanline = fRunHead->findScanline(sect.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000467 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000468 if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
469 return true;
470 }
reed@google.combb094b92012-11-07 14:23:42 +0000471 if (sect.fBottom <= scanline_bottom(scanline)) {
472 break;
473 }
reed@google.com9c36a762012-05-02 18:07:33 +0000474 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000475 }
reed@google.com9c36a762012-05-02 18:07:33 +0000476 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479bool SkRegion::intersects(const SkRegion& rgn) const {
480 if (this->isEmpty() || rgn.isEmpty()) {
481 return false;
482 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
485 return false;
486 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000487
reed@google.com9c36a762012-05-02 18:07:33 +0000488 bool weAreARect = this->isRect();
489 bool theyAreARect = rgn.isRect();
490
491 if (weAreARect && theyAreARect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 return true;
493 }
reed@google.com9c36a762012-05-02 18:07:33 +0000494 if (weAreARect) {
495 return rgn.intersects(this->getBounds());
496 }
497 if (theyAreARect) {
498 return this->intersects(rgn.getBounds());
499 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000500
reed@google.com9c36a762012-05-02 18:07:33 +0000501 // both of us are complex
halcanary96fcdcc2015-08-27 07:41:13 -0700502 return Oper(*this, rgn, kIntersect_Op, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503}
504
reed@google.comc34f53d2012-04-30 15:10:13 +0000505///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
reed@google.com6d428d32012-01-25 21:53:53 +0000507bool SkRegion::operator==(const SkRegion& b) const {
508 SkDEBUGCODE(validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 SkDEBUGCODE(b.validate();)
510
reed@google.com6d428d32012-01-25 21:53:53 +0000511 if (this == &b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000513 }
reed@google.com6d428d32012-01-25 21:53:53 +0000514 if (fBounds != b.fBounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000516 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000517
reed@google.com6d428d32012-01-25 21:53:53 +0000518 const SkRegion::RunHead* ah = fRunHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 const SkRegion::RunHead* bh = b.fRunHead;
520
521 // this catches empties and rects being equal
reed@google.com97fa34c2011-03-18 14:44:42 +0000522 if (ah == bh) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000524 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 // now we insist that both are complex (but different ptrs)
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000526 if (!this->isComplex() || !b.isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000528 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 return ah->fRunCount == bh->fRunCount &&
530 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
531 ah->fRunCount * sizeof(SkRegion::RunType));
532}
533
reed@google.com97fa34c2011-03-18 14:44:42 +0000534void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 SkDEBUGCODE(this->validate();)
536
halcanary96fcdcc2015-08-27 07:41:13 -0700537 if (nullptr == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 return;
reed@google.com97fa34c2011-03-18 14:44:42 +0000539 }
540 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 dst->setEmpty();
reed@google.com97fa34c2011-03-18 14:44:42 +0000542 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
544 fBounds.fRight + dx, fBounds.fBottom + dy);
reed@google.com97fa34c2011-03-18 14:44:42 +0000545 } else {
546 if (this == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 dst->fRunHead = dst->fRunHead->ensureWritable();
reed@google.com97fa34c2011-03-18 14:44:42 +0000548 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkRegion tmp;
reed@google.comaf7e6942012-05-01 14:43:22 +0000550 tmp.allocateRuns(*fRunHead);
Hal Canary251bf3e2017-02-16 12:42:24 -0500551 SkASSERT(tmp.isComplex());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 tmp.fBounds = fBounds;
553 dst->swap(tmp);
554 }
555
556 dst->fBounds.offset(dx, dy);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 const RunType* sruns = fRunHead->readonly_runs();
559 RunType* druns = dst->fRunHead->writable_runs();
560
561 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
reed@google.com97fa34c2011-03-18 14:44:42 +0000562 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 int bottom = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000564 if (bottom == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000566 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000568 *druns++ = *sruns++; // copy intervalCount;
reed@google.com97fa34c2011-03-18 14:44:42 +0000569 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 int x = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000571 if (x == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000573 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 *druns++ = (SkRegion::RunType)(x + dx);
575 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
576 }
577 *druns++ = kRunTypeSentinel; // x sentinel
578 }
579 *druns++ = kRunTypeSentinel; // y sentinel
580
581 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
582 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
583 }
584
585 SkDEBUGCODE(this->validate();)
586}
587
reed@android.com097a3512010-07-13 18:35:14 +0000588///////////////////////////////////////////////////////////////////////////////
589
reed@android.com097a3512010-07-13 18:35:14 +0000590bool SkRegion::setRects(const SkIRect rects[], int count) {
591 if (0 == count) {
reed@android.comcb342352010-07-22 18:27:53 +0000592 this->setEmpty();
593 } else {
594 this->setRect(rects[0]);
595 for (int i = 1; i < count; i++) {
596 this->op(rects[i], kUnion_Op);
reed@android.com097a3512010-07-13 18:35:14 +0000597 }
598 }
reed@android.comcb342352010-07-22 18:27:53 +0000599 return !this->isEmpty();
reed@android.com097a3512010-07-13 18:35:14 +0000600}
601
602///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603
bungemand7dc76f2016-03-10 11:14:40 -0800604#if defined _WIN32 // disable warning : local variable used without having been initialized
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605#pragma warning ( push )
606#pragma warning ( disable : 4701 )
607#endif
608
609#ifdef SK_DEBUG
610static void assert_valid_pair(int left, int rite)
611{
612 SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
613}
614#else
615 #define assert_valid_pair(left, rite)
616#endif
617
618struct spanRec {
619 const SkRegion::RunType* fA_runs;
620 const SkRegion::RunType* fB_runs;
621 int fA_left, fA_rite, fB_left, fB_rite;
622 int fLeft, fRite, fInside;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000623
reed@google.comc34f53d2012-04-30 15:10:13 +0000624 void init(const SkRegion::RunType a_runs[],
625 const SkRegion::RunType b_runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 fA_left = *a_runs++;
627 fA_rite = *a_runs++;
628 fB_left = *b_runs++;
629 fB_rite = *b_runs++;
630
631 fA_runs = a_runs;
632 fB_runs = b_runs;
633 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000634
reed@google.comc34f53d2012-04-30 15:10:13 +0000635 bool done() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
637 SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
reed@google.comc34f53d2012-04-30 15:10:13 +0000638 return fA_left == SkRegion::kRunTypeSentinel &&
639 fB_left == SkRegion::kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 }
641
reed@google.comc34f53d2012-04-30 15:10:13 +0000642 void next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 assert_valid_pair(fA_left, fA_rite);
644 assert_valid_pair(fB_left, fB_rite);
645
646 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
647 bool a_flush = false;
648 bool b_flush = false;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 int a_left = fA_left;
651 int a_rite = fA_rite;
652 int b_left = fB_left;
653 int b_rite = fB_rite;
654
reed@google.comc34f53d2012-04-30 15:10:13 +0000655 if (a_left < b_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 inside = 1;
657 left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000658 if (a_rite <= b_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 rite = a_rite;
660 a_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000661 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 rite = a_left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000663 }
664 } else if (b_left < a_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 inside = 2;
666 left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000667 if (b_rite <= a_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 rite = b_rite;
669 b_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000670 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 rite = b_left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000672 }
673 } else { // a_left == b_left
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 inside = 3;
675 left = a_left; // or b_left
reed@google.comc34f53d2012-04-30 15:10:13 +0000676 if (a_rite <= b_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 rite = b_left = a_rite;
678 a_flush = true;
679 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000680 if (b_rite <= a_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 rite = a_left = b_rite;
682 b_flush = true;
683 }
684 }
685
reed@google.comc34f53d2012-04-30 15:10:13 +0000686 if (a_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 a_left = *fA_runs++;
688 a_rite = *fA_runs++;
689 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000690 if (b_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 b_left = *fB_runs++;
692 b_rite = *fB_runs++;
693 }
694
695 SkASSERT(left <= rite);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 // now update our state
698 fA_left = a_left;
699 fA_rite = a_rite;
700 fB_left = b_left;
701 fB_rite = b_rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 fLeft = left;
704 fRite = rite;
705 fInside = inside;
706 }
707};
708
709static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
710 const SkRegion::RunType b_runs[],
711 SkRegion::RunType dst[],
reed@google.comc34f53d2012-04-30 15:10:13 +0000712 int min, int max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 spanRec rec;
714 bool firstInterval = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 rec.init(a_runs, b_runs);
717
reed@google.comc34f53d2012-04-30 15:10:13 +0000718 while (!rec.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 rec.next();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 int left = rec.fLeft;
722 int rite = rec.fRite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000723
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 // add left,rite to our dst buffer (checking for coincidence
725 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
reed@google.comc34f53d2012-04-30 15:10:13 +0000726 left < rite) { // skip if equal
727 if (firstInterval || dst[-1] < left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 *dst++ = (SkRegion::RunType)(left);
729 *dst++ = (SkRegion::RunType)(rite);
730 firstInterval = false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000731 } else {
732 // update the right edge
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 dst[-1] = (SkRegion::RunType)(rite);
reed@google.comc34f53d2012-04-30 15:10:13 +0000734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 }
736 }
737
738 *dst++ = SkRegion::kRunTypeSentinel;
739 return dst;
740}
741
bungemand7dc76f2016-03-10 11:14:40 -0800742#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743#pragma warning ( pop )
744#endif
745
746static const struct {
747 uint8_t fMin;
748 uint8_t fMax;
749} gOpMinMax[] = {
750 { 1, 1 }, // Difference
751 { 3, 3 }, // Intersection
752 { 1, 3 }, // Union
753 { 1, 2 } // XOR
754};
755
756class RgnOper {
757public:
reed@google.comc34f53d2012-04-30 15:10:13 +0000758 RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759 // need to ensure that the op enum lines up with our minmax array
760 SkASSERT(SkRegion::kDifference_Op == 0);
761 SkASSERT(SkRegion::kIntersect_Op == 1);
762 SkASSERT(SkRegion::kUnion_Op == 2);
763 SkASSERT(SkRegion::kXOR_Op == 3);
764 SkASSERT((unsigned)op <= 3);
765
766 fStartDst = dst;
767 fPrevDst = dst + 1;
768 fPrevLen = 0; // will never match a length from operate_on_span
769 fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
770
771 fMin = gOpMinMax[op].fMin;
772 fMax = gOpMinMax[op].fMax;
773 }
774
reed@google.comc34f53d2012-04-30 15:10:13 +0000775 void addSpan(int bottom, const SkRegion::RunType a_runs[],
776 const SkRegion::RunType b_runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +0000777 // skip X values and slots for the next Y+intervalCount
778 SkRegion::RunType* start = fPrevDst + fPrevLen + 2;
779 // start points to beginning of dst interval
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
781 size_t len = stop - start;
reed@google.com9c36a762012-05-02 18:07:33 +0000782 SkASSERT(len >= 1 && (len & 1) == 1);
783 SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784
reed@google.comc34f53d2012-04-30 15:10:13 +0000785 if (fPrevLen == len &&
reed@google.com9c36a762012-05-02 18:07:33 +0000786 (1 == len || !memcmp(fPrevDst, start,
787 (len - 1) * sizeof(SkRegion::RunType)))) {
reed@google.comc34f53d2012-04-30 15:10:13 +0000788 // update Y value
reed@google.com9c36a762012-05-02 18:07:33 +0000789 fPrevDst[-2] = (SkRegion::RunType)(bottom);
reed@google.comc34f53d2012-04-30 15:10:13 +0000790 } else { // accept the new span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 if (len == 1 && fPrevLen == 0) {
792 fTop = (SkRegion::RunType)(bottom); // just update our bottom
793 } else {
reed@google.com9c36a762012-05-02 18:07:33 +0000794 start[-2] = (SkRegion::RunType)(bottom);
commit-bot@chromium.orgf1177812014-04-23 19:19:44 +0000795 start[-1] = SkToS32(len >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 fPrevDst = start;
797 fPrevLen = len;
798 }
799 }
800 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000801
reed@google.comc34f53d2012-04-30 15:10:13 +0000802 int flush() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 fStartDst[0] = fTop;
804 fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
805 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
806 }
807
reed@google.com7d4aee32012-04-30 13:29:02 +0000808 bool isEmpty() const { return 0 == fPrevLen; }
809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 uint8_t fMin, fMax;
811
812private:
813 SkRegion::RunType* fStartDst;
814 SkRegion::RunType* fPrevDst;
815 size_t fPrevLen;
816 SkRegion::RunType fTop;
817};
818
reed@google.com7d4aee32012-04-30 13:29:02 +0000819// want a unique value to signal that we exited due to quickExit
820#define QUICK_EXIT_TRUE_COUNT (-1)
821
reed@google.com1deaab52011-10-06 13:11:46 +0000822static int operate(const SkRegion::RunType a_runs[],
823 const SkRegion::RunType b_runs[],
824 SkRegion::RunType dst[],
reed@google.com7d4aee32012-04-30 13:29:02 +0000825 SkRegion::Op op,
826 bool quickExit) {
reed@google.com9c36a762012-05-02 18:07:33 +0000827 const SkRegion::RunType gEmptyScanline[] = {
828 0, // dummy bottom value
829 0, // zero intervals
reed@google.com5f7c8a52012-05-03 16:17:38 +0000830 SkRegion::kRunTypeSentinel,
831 // just need a 2nd value, since spanRec.init() reads 2 values, even
832 // though if the first value is the sentinel, it ignores the 2nd value.
833 // w/o the 2nd value here, we might read uninitialized memory.
834 // This happens when we are using gSentinel, which is pointing at
835 // our sentinel value.
836 0
reed@android.comfbb02e72010-04-13 14:52:52 +0000837 };
reed@google.com9c36a762012-05-02 18:07:33 +0000838 const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839
840 int a_top = *a_runs++;
841 int a_bot = *a_runs++;
842 int b_top = *b_runs++;
843 int b_bot = *b_runs++;
844
reed@google.com9c36a762012-05-02 18:07:33 +0000845 a_runs += 1; // skip the intervalCount;
846 b_runs += 1; // skip the intervalCount;
847
848 // Now a_runs and b_runs to their intervals (or sentinel)
849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 assert_sentinel(a_top, false);
851 assert_sentinel(a_bot, false);
852 assert_sentinel(b_top, false);
853 assert_sentinel(b_bot, false);
854
855 RgnOper oper(SkMin32(a_top, b_top), dst, op);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000858
reed@google.com1deaab52011-10-06 13:11:46 +0000859 while (a_bot < SkRegion::kRunTypeSentinel ||
860 b_bot < SkRegion::kRunTypeSentinel) {
861 int top, bot SK_INIT_TO_AVOID_WARNING;
reed@android.comfbb02e72010-04-13 14:52:52 +0000862 const SkRegion::RunType* run0 = gSentinel;
863 const SkRegion::RunType* run1 = gSentinel;
reed@google.com1deaab52011-10-06 13:11:46 +0000864 bool a_flush = false;
865 bool b_flush = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866
reed@google.com1deaab52011-10-06 13:11:46 +0000867 if (a_top < b_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 top = a_top;
869 run0 = a_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000870 if (a_bot <= b_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 bot = a_bot;
872 a_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000873 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 bot = a_top = b_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000875 }
876 } else if (b_top < a_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 top = b_top;
878 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000879 if (b_bot <= a_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 bot = b_bot;
881 b_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000882 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 bot = b_top = a_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000884 }
885 } else { // a_top == b_top
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 top = a_top; // or b_top
887 run0 = a_runs;
888 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000889 if (a_bot <= b_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 bot = b_top = a_bot;
891 a_flush = true;
892 }
reed@google.com1deaab52011-10-06 13:11:46 +0000893 if (b_bot <= a_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 bot = a_top = b_bot;
895 b_flush = true;
896 }
897 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000898
reed@google.com1deaab52011-10-06 13:11:46 +0000899 if (top > prevBot) {
reed@android.comfbb02e72010-04-13 14:52:52 +0000900 oper.addSpan(top, gSentinel, gSentinel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 }
reed@google.com1deaab52011-10-06 13:11:46 +0000902 oper.addSpan(bot, run0, run1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903
reed@google.com7d4aee32012-04-30 13:29:02 +0000904 if (quickExit && !oper.isEmpty()) {
905 return QUICK_EXIT_TRUE_COUNT;
906 }
907
reed@google.com1deaab52011-10-06 13:11:46 +0000908 if (a_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000909 a_runs = skip_intervals(a_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 a_top = a_bot;
911 a_bot = *a_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000912 a_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000913 if (a_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 a_top = a_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000915 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 }
reed@google.com1deaab52011-10-06 13:11:46 +0000917 if (b_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000918 b_runs = skip_intervals(b_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 b_top = b_bot;
920 b_bot = *b_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000921 b_runs += 1; // skip uninitialized intervalCount
reed@google.com1deaab52011-10-06 13:11:46 +0000922 if (b_bot == SkRegion::kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 b_top = b_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000924 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000926
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 prevBot = bot;
928 }
929 return oper.flush();
930}
931
932///////////////////////////////////////////////////////////////////////////////
933
934/* Given count RunTypes in a complex region, return the worst case number of
935 logical intervals that represents (i.e. number of rects that would be
936 returned from the iterator).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000937
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 We could just return count/2, since there must be at least 2 values per
939 interval, but we can first trim off the const overhead of the initial TOP
940 value, plus the final BOTTOM + 2 sentinels.
941 */
caryclark@google.com803eceb2012-06-06 12:09:34 +0000942#if 0 // UNUSED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943static int count_to_intervals(int count) {
944 SkASSERT(count >= 6); // a single rect is 6 values
945 return (count - 4) >> 1;
946}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000947#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948
949/* Given a number of intervals, what is the worst case representation of that
950 many intervals?
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 Worst case (from a storage perspective), is a vertical stack of single
reed@google.com9c36a762012-05-02 18:07:33 +0000953 intervals: TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 */
955static int intervals_to_count(int intervals) {
reed@google.com9c36a762012-05-02 18:07:33 +0000956 return 1 + intervals * 5 + 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957}
958
reed@google.comaf7e6942012-05-01 14:43:22 +0000959/* Given the intervalCounts of RunTypes in two regions, return the worst-case number
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 of RunTypes need to store the result after a region-op.
961 */
reed@google.comaf7e6942012-05-01 14:43:22 +0000962static int compute_worst_case_count(int a_intervals, int b_intervals) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
964 int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
965 // convert back to number of RunType values
966 return intervals_to_count(intervals);
967}
968
reed@google.com7d4aee32012-04-30 13:29:02 +0000969static bool setEmptyCheck(SkRegion* result) {
970 return result ? result->setEmpty() : false;
971}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972
reed@google.com7d4aee32012-04-30 13:29:02 +0000973static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
974 return result ? result->setRect(rect) : !rect.isEmpty();
975}
976
977static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
978 return result ? result->setRegion(rgn) : !rgn.isEmpty();
979}
980
981bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
982 SkRegion* result) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 SkASSERT((unsigned)op < kOpCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000984
reed@google.com7d4aee32012-04-30 13:29:02 +0000985 if (kReplace_Op == op) {
986 return setRegionCheck(result, rgnbOrig);
987 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 // swith to using pointers, so we can swap them as needed
990 const SkRegion* rgna = &rgnaOrig;
991 const SkRegion* rgnb = &rgnbOrig;
992 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
993
994 // collaps difference and reverse-difference into just difference
reed@google.com7d4aee32012-04-30 13:29:02 +0000995 if (kReverseDifference_Op == op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 SkTSwap<const SkRegion*>(rgna, rgnb);
997 op = kDifference_Op;
998 }
999
1000 SkIRect bounds;
1001 bool a_empty = rgna->isEmpty();
1002 bool b_empty = rgnb->isEmpty();
1003 bool a_rect = rgna->isRect();
1004 bool b_rect = rgnb->isRect();
1005
1006 switch (op) {
1007 case kDifference_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001008 if (a_empty) {
1009 return setEmptyCheck(result);
1010 }
reed@google.com0d102802012-05-31 18:28:59 +00001011 if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
1012 rgnb->fBounds)) {
reed@google.com7d4aee32012-04-30 13:29:02 +00001013 return setRegionCheck(result, *rgna);
1014 }
reed@google.com0d102802012-05-31 18:28:59 +00001015 if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
1016 return setEmptyCheck(result);
1017 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 break;
1019
1020 case kIntersect_Op:
1021 if ((a_empty | b_empty)
reed@google.com7d4aee32012-04-30 13:29:02 +00001022 || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
1023 return setEmptyCheck(result);
1024 }
1025 if (a_rect & b_rect) {
1026 return setRectCheck(result, bounds);
1027 }
reed@google.com633c32b2013-01-31 15:24:24 +00001028 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1029 return setRegionCheck(result, *rgnb);
1030 }
1031 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1032 return setRegionCheck(result, *rgna);
1033 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 break;
1035
1036 case kUnion_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001037 if (a_empty) {
1038 return setRegionCheck(result, *rgnb);
1039 }
1040 if (b_empty) {
1041 return setRegionCheck(result, *rgna);
1042 }
1043 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1044 return setRegionCheck(result, *rgna);
1045 }
1046 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1047 return setRegionCheck(result, *rgnb);
1048 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 break;
1050
1051 case kXOR_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001052 if (a_empty) {
1053 return setRegionCheck(result, *rgnb);
1054 }
1055 if (b_empty) {
1056 return setRegionCheck(result, *rgna);
1057 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 break;
1059 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001060 SkDEBUGFAIL("unknown region op");
reed@google.com7d4aee32012-04-30 13:29:02 +00001061 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 }
1063
1064 RunType tmpA[kRectRegionRuns];
1065 RunType tmpB[kRectRegionRuns];
1066
reed@google.comaf7e6942012-05-01 14:43:22 +00001067 int a_intervals, b_intervals;
1068 const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
1069 const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070
reed@google.comaf7e6942012-05-01 14:43:22 +00001071 int dstCount = compute_worst_case_count(a_intervals, b_intervals);
1072 SkAutoSTMalloc<256, RunType> array(dstCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073
reed@google.com9c36a762012-05-02 18:07:33 +00001074#ifdef SK_DEBUG
1075// Sometimes helpful to seed everything with a known value when debugging
1076// sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
1077#endif
1078
halcanary96fcdcc2015-08-27 07:41:13 -07001079 int count = operate(a_runs, b_runs, array.get(), op, nullptr == result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 SkASSERT(count <= dstCount);
reed@google.comaf7e6942012-05-01 14:43:22 +00001081
reed@google.com7d4aee32012-04-30 13:29:02 +00001082 if (result) {
1083 SkASSERT(count >= 0);
1084 return result->setRuns(array.get(), count);
1085 } else {
1086 return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
1087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088}
1089
reed@google.com7d4aee32012-04-30 13:29:02 +00001090bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
1091 SkDEBUGCODE(this->validate();)
1092 return SkRegion::Oper(rgna, rgnb, op, this);
1093}
1094
1095///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096
1097#include "SkBuffer.h"
1098
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001099size_t SkRegion::writeToMemory(void* storage) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001100 if (nullptr == storage) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001101 size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 if (!this->isEmpty()) {
1103 size += sizeof(fBounds);
1104 if (this->isComplex()) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001105 size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 size += fRunHead->fRunCount * sizeof(RunType);
1107 }
1108 }
1109 return size;
1110 }
1111
1112 SkWBuffer buffer(storage);
1113
1114 if (this->isEmpty()) {
1115 buffer.write32(-1);
1116 } else {
1117 bool isRect = this->isRect();
1118
1119 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1120 buffer.write(&fBounds, sizeof(fBounds));
1121
1122 if (!isRect) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001123 buffer.write32(fRunHead->getYSpanCount());
1124 buffer.write32(fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 buffer.write(fRunHead->readonly_runs(),
1126 fRunHead->fRunCount * sizeof(RunType));
1127 }
1128 }
1129 return buffer.pos();
1130}
1131
Hal Canary58a1ea82017-02-19 22:16:10 -05001132// Validate that a memory sequence is a valid region.
1133// Try to check all possible errors.
1134// never read beyond &runs[runCount-1].
1135static bool validate_run(const int32_t* runs,
1136 int runCount,
1137 const SkIRect& givenBounds,
1138 int32_t ySpanCount,
1139 int32_t intervalCount) {
1140 // Region Layout:
1141 // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel
1142 if (ySpanCount < 1 || intervalCount < 2 || runCount != 2 + 3 * ySpanCount + 2 * intervalCount) {
1143 return false;
1144 }
1145 SkASSERT(runCount >= 7); // 7==SkRegion::kRectRegionRuns
1146 // quick sanity check:
1147 if (runs[runCount - 1] != SkRegion::kRunTypeSentinel ||
1148 runs[runCount - 2] != SkRegion::kRunTypeSentinel) {
1149 return false;
1150 }
1151 const int32_t* const end = runs + runCount;
1152 SkIRect bounds = {0, 0, 0 ,0}; // calulated bounds
1153 SkIRect rect = {0, 0, 0, 0}; // current rect
1154 rect.fTop = *runs++;
1155 if (rect.fTop == SkRegion::kRunTypeSentinel) {
1156 return false; // no rect can contain SkRegion::kRunTypeSentinel
1157 }
1158 do {
1159 --ySpanCount;
1160 if (ySpanCount < 0) {
1161 return false; // too many yspans
1162 }
1163 rect.fBottom = *runs++;
1164 if (rect.fBottom == SkRegion::kRunTypeSentinel) {
1165 return false;
1166 }
1167 int32_t xIntervals = *runs++;
1168 SkASSERT(runs < end);
1169 if (xIntervals < 0 || runs + 1 + 2 * xIntervals > end) {
1170 return false;
1171 }
1172 intervalCount -= xIntervals;
1173 if (intervalCount < 0) {
1174 return false; // too many intervals
1175 }
1176 while (xIntervals-- > 0) {
1177 rect.fLeft = *runs++;
1178 rect.fRight = *runs++;
1179 if (rect.fLeft == SkRegion::kRunTypeSentinel ||
1180 rect.fRight == SkRegion::kRunTypeSentinel || rect.isEmpty()) {
1181 return false;
1182 }
1183 bounds.join(rect);
1184 }
1185 if (*runs++ != SkRegion::kRunTypeSentinel) {
1186 return false; // required check sentinal.
1187 }
1188 rect.fTop = rect.fBottom;
1189 SkASSERT(runs < end);
1190 } while (*runs != SkRegion::kRunTypeSentinel);
1191 ++runs;
1192 if (ySpanCount != 0 || intervalCount != 0 || givenBounds != bounds) {
1193 return false;
1194 }
1195 SkASSERT(runs == end); // if ySpanCount && intervalCount are right, must be correct length.
1196 return true;
1197}
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001198size_t SkRegion::readFromMemory(const void* storage, size_t length) {
Mike Reed1026ccf2017-01-08 14:35:29 -05001199 SkRBuffer buffer(storage, length);
1200 SkRegion tmp;
1201 int32_t count;
skia.committer@gmail.com382fde32013-11-06 07:02:11 +00001202
Hal Canary58a1ea82017-02-19 22:16:10 -05001203 // Serialized Region Format:
1204 // Empty:
1205 // -1
1206 // Simple Rect:
1207 // 0 LEFT TOP RIGHT BOTTOM
1208 // Complex Region:
1209 // COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT [RUNS....]
1210 if (!buffer.readS32(&count) || count < -1) {
1211 return 0;
1212 }
1213 if (count >= 0) {
1214 if (!buffer.read(&tmp.fBounds, sizeof(tmp.fBounds)) || tmp.fBounds.isEmpty()) {
1215 return 0; // Short buffer or bad bounds for non-empty region; report failure.
Hal Canary251bf3e2017-02-16 12:42:24 -05001216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 if (count == 0) {
1218 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1219 } else {
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +00001220 int32_t ySpanCount, intervalCount;
Hal Canary58a1ea82017-02-19 22:16:10 -05001221 if (!buffer.readS32(&ySpanCount) ||
1222 !buffer.readS32(&intervalCount) ||
1223 buffer.available() < count * sizeof(int32_t)) {
1224 return 0;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001225 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001226 if (!validate_run((const int32_t*)((const char*)storage + buffer.pos()), count,
1227 tmp.fBounds, ySpanCount, intervalCount)) {
1228 return 0; // invalid runs, don't even allocate
1229 }
1230 tmp.allocateRuns(count, ySpanCount, intervalCount);
1231 SkASSERT(tmp.isComplex());
1232 SkAssertResult(buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(int32_t)));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 }
1234 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001235 SkASSERT(tmp.isValid());
1236 SkASSERT(buffer.isValid());
1237 this->swap(tmp);
1238 return buffer.pos();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239}
1240
reed@google.com0a0a2362011-03-23 13:51:55 +00001241///////////////////////////////////////////////////////////////////////////////
1242
1243const SkRegion& SkRegion::GetEmptyRegion() {
1244 static SkRegion gEmpty;
1245 return gEmpty;
1246}
1247
1248///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249
Hal Canary251bf3e2017-02-16 12:42:24 -05001250bool SkRegion::isValid() const {
reed@google.comc34f53d2012-04-30 15:10:13 +00001251 if (this->isEmpty()) {
Hal Canary251bf3e2017-02-16 12:42:24 -05001252 return fBounds == SkIRect{0, 0, 0, 0};
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001254 if (fBounds.isEmpty()) {
1255 return false;
1256 }
1257 if (this->isRect()) {
1258 return true;
1259 }
1260 return fRunHead && fRunHead->fRefCnt > 0 &&
1261 validate_run(fRunHead->readonly_runs(), fRunHead->fRunCount, fBounds,
1262 fRunHead->getYSpanCount(), fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263}
1264
Hal Canary251bf3e2017-02-16 12:42:24 -05001265#ifdef SK_DEBUG
1266void SkRegion::validate() const { SkASSERT(this->isValid()); }
1267
reed@google.comc34f53d2012-04-30 15:10:13 +00001268void SkRegion::dump() const {
1269 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 SkDebugf(" rgn: empty\n");
reed@google.comc34f53d2012-04-30 15:10:13 +00001271 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
reed@google.com9c36a762012-05-02 18:07:33 +00001273 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 const RunType* runs = fRunHead->readonly_runs();
1275 for (int i = 0; i < fRunHead->fRunCount; i++)
1276 SkDebugf(" %d", runs[i]);
1277 }
1278 SkDebugf("\n");
1279 }
1280}
1281
1282#endif
1283
reed@google.comc34f53d2012-04-30 15:10:13 +00001284///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285
1286SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1287 this->reset(rgn);
1288}
1289
1290bool SkRegion::Iterator::rewind() {
1291 if (fRgn) {
1292 this->reset(*fRgn);
1293 return true;
1294 }
1295 return false;
1296}
1297
1298void SkRegion::Iterator::reset(const SkRegion& rgn) {
1299 fRgn = &rgn;
1300 if (rgn.isEmpty()) {
1301 fDone = true;
1302 } else {
1303 fDone = false;
1304 if (rgn.isRect()) {
1305 fRect = rgn.fBounds;
halcanary96fcdcc2015-08-27 07:41:13 -07001306 fRuns = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 } else {
1308 fRuns = rgn.fRunHead->readonly_runs();
reed@google.com9c36a762012-05-02 18:07:33 +00001309 fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
1310 fRuns += 5;
1311 // Now fRuns points to the 2nd interval (or x-sentinel)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
1313 }
1314}
1315
1316void SkRegion::Iterator::next() {
1317 if (fDone) {
1318 return;
1319 }
1320
halcanary96fcdcc2015-08-27 07:41:13 -07001321 if (fRuns == nullptr) { // rect case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 fDone = true;
1323 return;
1324 }
1325
1326 const RunType* runs = fRuns;
1327
1328 if (runs[0] < kRunTypeSentinel) { // valid X value
1329 fRect.fLeft = runs[0];
1330 fRect.fRight = runs[1];
1331 runs += 2;
1332 } else { // we're at the end of a line
1333 runs += 1;
1334 if (runs[0] < kRunTypeSentinel) { // valid Y value
reed@google.com9c36a762012-05-02 18:07:33 +00001335 int intervals = runs[1];
1336 if (0 == intervals) { // empty line
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 fRect.fTop = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001338 runs += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 } else {
1340 fRect.fTop = fRect.fBottom;
1341 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 fRect.fBottom = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001344 assert_sentinel(runs[2], false);
1345 assert_sentinel(runs[3], false);
1346 fRect.fLeft = runs[2];
1347 fRect.fRight = runs[3];
1348 runs += 4;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 } else { // end of rgn
1350 fDone = true;
1351 }
1352 }
1353 fRuns = runs;
1354}
1355
1356SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1357 : fIter(rgn), fClip(clip), fDone(true) {
1358 const SkIRect& r = fIter.rect();
1359
1360 while (!fIter.done()) {
1361 if (r.fTop >= clip.fBottom) {
1362 break;
1363 }
1364 if (fRect.intersect(clip, r)) {
1365 fDone = false;
1366 break;
1367 }
1368 fIter.next();
1369 }
1370}
1371
1372void SkRegion::Cliperator::next() {
1373 if (fDone) {
1374 return;
1375 }
1376
1377 const SkIRect& r = fIter.rect();
1378
1379 fDone = true;
1380 fIter.next();
1381 while (!fIter.done()) {
1382 if (r.fTop >= fClip.fBottom) {
1383 break;
1384 }
1385 if (fRect.intersect(fClip, r)) {
1386 fDone = false;
1387 break;
1388 }
1389 fIter.next();
1390 }
1391}
1392
reed@google.come72766f2011-01-07 15:00:44 +00001393///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394
reed@google.come72766f2011-01-07 15:00:44 +00001395SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1396 int right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 SkDEBUGCODE(rgn.validate();)
1398
1399 const SkIRect& r = rgn.getBounds();
1400
1401 fDone = true;
reed@google.come72766f2011-01-07 15:00:44 +00001402 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1403 right > r.fLeft && left < r.fRight) {
1404 if (rgn.isRect()) {
1405 if (left < r.fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 left = r.fLeft;
reed@google.come72766f2011-01-07 15:00:44 +00001407 }
1408 if (right > r.fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 right = r.fRight;
reed@google.come72766f2011-01-07 15:00:44 +00001410 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 fLeft = left;
1412 fRight = right;
halcanary96fcdcc2015-08-27 07:41:13 -07001413 fRuns = nullptr; // means we're a rect, not a rgn
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 fDone = false;
reed@google.come72766f2011-01-07 15:00:44 +00001415 } else {
reed@google.com9c36a762012-05-02 18:07:33 +00001416 const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
1417 runs += 2; // skip Bottom and IntervalCount
1418 for (;;) {
1419 // runs[0..1] is to the right of the span, so we're done
1420 if (runs[0] >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 break;
1422 }
reed@google.com9c36a762012-05-02 18:07:33 +00001423 // runs[0..1] is to the left of the span, so continue
1424 if (runs[1] <= left) {
1425 runs += 2;
1426 continue;
1427 }
1428 // runs[0..1] intersects the span
1429 fRuns = runs;
1430 fLeft = left;
1431 fRight = right;
1432 fDone = false;
1433 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 }
1435 }
1436 }
1437}
1438
reed@google.come72766f2011-01-07 15:00:44 +00001439bool SkRegion::Spanerator::next(int* left, int* right) {
1440 if (fDone) {
1441 return false;
1442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443
halcanary96fcdcc2015-08-27 07:41:13 -07001444 if (fRuns == nullptr) { // we're a rect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 fDone = true; // ok, now we're done
reed@google.come72766f2011-01-07 15:00:44 +00001446 if (left) {
1447 *left = fLeft;
1448 }
1449 if (right) {
1450 *right = fRight;
1451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 return true; // this interval is legal
1453 }
1454
1455 const SkRegion::RunType* runs = fRuns;
1456
reed@google.come72766f2011-01-07 15:00:44 +00001457 if (runs[0] >= fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 fDone = true;
1459 return false;
1460 }
1461
1462 SkASSERT(runs[1] > fLeft);
1463
reed@google.come72766f2011-01-07 15:00:44 +00001464 if (left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 *left = SkMax32(fLeft, runs[0]);
reed@google.come72766f2011-01-07 15:00:44 +00001466 }
1467 if (right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 *right = SkMin32(fRight, runs[1]);
reed@google.come72766f2011-01-07 15:00:44 +00001469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 fRuns = runs + 2;
1471 return true;
1472}
1473
1474///////////////////////////////////////////////////////////////////////////////
1475
1476#ifdef SK_DEBUG
1477
1478bool SkRegion::debugSetRuns(const RunType runs[], int count) {
1479 // we need to make a copy, since the real method may modify the array, and
1480 // so it cannot be const.
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001481
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 SkAutoTArray<RunType> storage(count);
1483 memcpy(storage.get(), runs, count * sizeof(RunType));
1484 return this->setRuns(storage.get(), count);
1485}
1486
1487#endif