blob: f66c6f75b111373aae3a3e9a4f3b296cce290617 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkRegion.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/private/SkMacros.h"
11#include "include/private/SkTemplates.h"
12#include "include/private/SkTo.h"
13#include "src/core/SkRegionPriv.h"
14#include "src/core/SkSafeMath.h"
15#include "src/utils/SkUTF.h"
reed@google.com9c36a762012-05-02 18:07:33 +000016
Ben Wagnerf08d1d02018-06-18 15:11:00 -040017#include <utility>
18
reed@google.com9c36a762012-05-02 18:07:33 +000019/* Region Layout
20 *
21 * TOP
22 *
23 * [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
24 * ...
25 *
26 * Y-Sentinel
27 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
reed@android.com8a1c16f2008-12-17 15:59:43 +000029/////////////////////////////////////////////////////////////////////////////////////////////////
30
Cary Clark69436892018-07-28 10:14:27 -040031#define SkRegion_gEmptyRunHeadPtr ((SkRegionPriv::RunHead*)-1)
32#define SkRegion_gRectRunHeadPtr nullptr
33
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040034constexpr int kRunArrayStackCount = 256;
Cary Clark69436892018-07-28 10:14:27 -040035
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040036// This is a simple data structure which is like a SkSTArray<N,T,true>, except that:
37// - It does not initialize memory.
38// - It does not distinguish between reserved space and initialized space.
39// - resizeToAtLeast() instead of resize()
40// - Uses sk_realloc_throw()
41// - Can never be made smaller.
42// Measurement: for the `region_union_16` benchmark, this is 6% faster.
43class RunArray {
44public:
45 RunArray() { fPtr = fStack; }
46 #ifdef SK_DEBUG
47 int count() const { return fCount; }
48 #endif
Cary Clark69436892018-07-28 10:14:27 -040049 SkRegionPriv::RunType& operator[](int i) {
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040050 SkASSERT((unsigned)i < (unsigned)fCount);
51 return fPtr[i];
52 }
53 /** Resize the array to a size greater-than-or-equal-to count. */
54 void resizeToAtLeast(int count) {
55 if (count > fCount) {
56 // leave at least 50% extra space for future growth.
57 count += count >> 1;
58 fMalloc.realloc(count);
59 if (fPtr == fStack) {
Cary Clark69436892018-07-28 10:14:27 -040060 memcpy(fMalloc.get(), fStack, fCount * sizeof(SkRegionPriv::RunType));
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040061 }
62 fPtr = fMalloc.get();
63 fCount = count;
64 }
65 }
66private:
Cary Clark69436892018-07-28 10:14:27 -040067 SkRegionPriv::RunType fStack[kRunArrayStackCount];
68 SkAutoTMalloc<SkRegionPriv::RunType> fMalloc;
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040069 int fCount = kRunArrayStackCount;
Cary Clark69436892018-07-28 10:14:27 -040070 SkRegionPriv::RunType* fPtr; // non-owning pointer
Hal Canaryc5a1c1f2018-04-04 10:52:00 -040071};
72
reed@google.com9c36a762012-05-02 18:07:33 +000073/* Pass in the beginning with the intervals.
74 * We back up 1 to read the interval-count.
75 * Return the beginning of the next scanline (i.e. the next Y-value)
76 */
Cary Clark69436892018-07-28 10:14:27 -040077static SkRegionPriv::RunType* skip_intervals(const SkRegionPriv::RunType runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +000078 int intervals = runs[-1];
79#ifdef SK_DEBUG
80 if (intervals > 0) {
81 SkASSERT(runs[0] < runs[1]);
Cary Clark69436892018-07-28 10:14:27 -040082 SkASSERT(runs[1] < SkRegion_kRunTypeSentinel);
reed@google.com9c36a762012-05-02 18:07:33 +000083 } else {
84 SkASSERT(0 == intervals);
Cary Clark69436892018-07-28 10:14:27 -040085 SkASSERT(SkRegion_kRunTypeSentinel == runs[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
reed@google.com9c36a762012-05-02 18:07:33 +000087#endif
88 runs += intervals * 2 + 1;
Cary Clark69436892018-07-28 10:14:27 -040089 return const_cast<SkRegionPriv::RunType*>(runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +000090}
91
reed@google.com9c36a762012-05-02 18:07:33 +000092bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
93 SkIRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 assert_sentinel(runs[0], false); // top
reed@google.com9c36a762012-05-02 18:07:33 +000095 SkASSERT(count >= kRectRegionRuns);
reed@android.com8a1c16f2008-12-17 15:59:43 +000096
reed@google.comc34f53d2012-04-30 15:10:13 +000097 if (count == kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +000099 SkASSERT(1 == runs[2]);
100 assert_sentinel(runs[3], false); // left
101 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 assert_sentinel(runs[5], true);
reed@google.com9c36a762012-05-02 18:07:33 +0000103 assert_sentinel(runs[6], true);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 SkASSERT(runs[0] < runs[1]); // valid height
reed@google.com9c36a762012-05-02 18:07:33 +0000106 SkASSERT(runs[3] < runs[4]); // valid width
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000107
reed@google.com9c36a762012-05-02 18:07:33 +0000108 bounds->set(runs[3], runs[0], runs[4], runs[1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 return true;
110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 return false;
112}
113
114//////////////////////////////////////////////////////////////////////////
115
reed@google.com0a0a2362011-03-23 13:51:55 +0000116SkRegion::SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 fBounds.set(0, 0, 0, 0);
118 fRunHead = SkRegion_gEmptyRunHeadPtr;
119}
120
reed@google.com0a0a2362011-03-23 13:51:55 +0000121SkRegion::SkRegion(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
123 this->setRegion(src);
124}
125
reed@google.com0a0a2362011-03-23 13:51:55 +0000126SkRegion::SkRegion(const SkIRect& rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
128 this->setRect(rect);
129}
130
reed@google.com0a0a2362011-03-23 13:51:55 +0000131SkRegion::~SkRegion() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 this->freeRuns();
133}
134
reed@google.com0a0a2362011-03-23 13:51:55 +0000135void SkRegion::freeRuns() {
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000136 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 SkASSERT(fRunHead->fRefCnt >= 1);
Mike Reedf80989f2018-04-27 12:06:44 -0400138 if (--fRunHead->fRefCnt == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 sk_free(fRunHead);
140 }
141 }
142}
143
reed@google.comaf7e6942012-05-01 14:43:22 +0000144void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
145 fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
146}
147
reed@google.com9c36a762012-05-02 18:07:33 +0000148void SkRegion::allocateRuns(int count) {
149 fRunHead = RunHead::Alloc(count);
150}
151
reed@google.comaf7e6942012-05-01 14:43:22 +0000152void SkRegion::allocateRuns(const RunHead& head) {
153 fRunHead = RunHead::Alloc(head.fRunCount,
154 head.getYSpanCount(),
155 head.getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156}
157
reed@google.com0a0a2362011-03-23 13:51:55 +0000158SkRegion& SkRegion::operator=(const SkRegion& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 (void)this->setRegion(src);
160 return *this;
161}
162
reed@google.com0a0a2362011-03-23 13:51:55 +0000163void SkRegion::swap(SkRegion& other) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400164 using std::swap;
165 swap(fBounds, other.fBounds);
166 swap(fRunHead, other.fRunHead);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167}
168
commit-bot@chromium.org5dd567c2013-07-17 21:39:28 +0000169int SkRegion::computeRegionComplexity() const {
170 if (this->isEmpty()) {
171 return 0;
172 } else if (this->isRect()) {
173 return 1;
174 }
175 return fRunHead->getIntervalCount();
176}
skia.committer@gmail.comf7d01ce2013-07-18 07:00:56 +0000177
reed@google.com0a0a2362011-03-23 13:51:55 +0000178bool SkRegion::setEmpty() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 this->freeRuns();
180 fBounds.set(0, 0, 0, 0);
181 fRunHead = SkRegion_gEmptyRunHeadPtr;
182 return false;
183}
184
Mike Reeda766ca92018-01-09 11:31:53 -0500185bool SkRegion::setRect(const SkIRect& r) {
Hal Canary1b95ef92018-08-13 13:52:22 -0400186 if (r.isEmpty() ||
187 SkRegion_kRunTypeSentinel == r.right() ||
188 SkRegion_kRunTypeSentinel == r.bottom()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 return this->setEmpty();
reed@google.com0a0a2362011-03-23 13:51:55 +0000190 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 this->freeRuns();
Mike Reeda766ca92018-01-09 11:31:53 -0500192 fBounds = r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fRunHead = SkRegion_gRectRunHeadPtr;
194 return true;
195}
196
reed@google.com0a0a2362011-03-23 13:51:55 +0000197bool SkRegion::setRegion(const SkRegion& src) {
198 if (this != &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 this->freeRuns();
200
201 fBounds = src.fBounds;
202 fRunHead = src.fRunHead;
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000203 if (this->isComplex()) {
Mike Reedf80989f2018-04-27 12:06:44 -0400204 fRunHead->fRefCnt++;
reed@google.com0a0a2362011-03-23 13:51:55 +0000205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 }
207 return fRunHead != SkRegion_gEmptyRunHeadPtr;
208}
209
reed@google.com0a0a2362011-03-23 13:51:55 +0000210bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkRegion tmp(rect);
212
213 return this->op(tmp, rgn, op);
214}
215
reed@google.com0a0a2362011-03-23 13:51:55 +0000216bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SkRegion tmp(rect);
218
219 return this->op(rgn, tmp, op);
220}
221
reed@google.com0a0a2362011-03-23 13:51:55 +0000222///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
Leon Scroggins IIIc41a5f52018-11-15 15:54:59 -0500224#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
bungeman@google.com2c8e9d42013-10-11 19:23:56 +0000225#include <stdio.h>
reed@google.comc34f53d2012-04-30 15:10:13 +0000226char* SkRegion::toString() {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000227 Iterator iter(*this);
228 int count = 0;
229 while (!iter.done()) {
230 count++;
231 iter.next();
232 }
233 // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
234 const int max = (count*((11*4)+5))+11+1;
mtklein002c2ce2015-08-26 05:43:22 -0700235 char* result = (char*)sk_malloc_throw(max);
halcanary96fcdcc2015-08-27 07:41:13 -0700236 if (result == nullptr) {
237 return nullptr;
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000238 }
caryclark8f186432016-10-06 11:46:25 -0700239 count = snprintf(result, max, "SkRegion(");
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000240 iter.reset(*this);
241 while (!iter.done()) {
242 const SkIRect& r = iter.rect();
Ben Wagner63fd7602017-10-09 15:45:33 -0400243 count += snprintf(result+count, max - count,
caryclark8f186432016-10-06 11:46:25 -0700244 "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000245 iter.next();
246 }
caryclark8f186432016-10-06 11:46:25 -0700247 count += snprintf(result+count, max - count, ")");
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000248 return result;
249}
250#endif
251
reed@google.comc34f53d2012-04-30 15:10:13 +0000252///////////////////////////////////////////////////////////////////////////////
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000253
reed@google.comc34f53d2012-04-30 15:10:13 +0000254int SkRegion::count_runtype_values(int* itop, int* ibot) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 int maxT;
256
reed@google.comc34f53d2012-04-30 15:10:13 +0000257 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 maxT = 2;
reed@google.comc34f53d2012-04-30 15:10:13 +0000259 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkASSERT(this->isComplex());
reed@google.comaf7e6942012-05-01 14:43:22 +0000261 maxT = fRunHead->getIntervalCount() * 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 }
263 *itop = fBounds.fTop;
264 *ibot = fBounds.fBottom;
265 return maxT;
266}
267
reed@google.com7d4aee32012-04-30 13:29:02 +0000268static bool isRunCountEmpty(int count) {
269 return count <= 2;
270}
271
reed@google.comc34f53d2012-04-30 15:10:13 +0000272bool SkRegion::setRuns(RunType runs[], int count) {
Cary Clark69436892018-07-28 10:14:27 -0400273 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 SkASSERT(count > 0);
275
reed@google.comc34f53d2012-04-30 15:10:13 +0000276 if (isRunCountEmpty(count)) {
Hal Canary2b0e6cd2018-07-09 12:43:39 -0400277 // SkDEBUGF("setRuns: empty\n");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 assert_sentinel(runs[count-1], true);
279 return this->setEmpty();
280 }
281
282 // trim off any empty spans from the top and bottom
283 // weird I should need this, perhaps op() could be smarter...
reed@google.comc34f53d2012-04-30 15:10:13 +0000284 if (count > kRectRegionRuns) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 RunType* stop = runs + count;
286 assert_sentinel(runs[0], false); // top
287 assert_sentinel(runs[1], false); // bottom
reed@google.com9c36a762012-05-02 18:07:33 +0000288 // runs[2] is uncomputed intervalCount
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000289
Cary Clark69436892018-07-28 10:14:27 -0400290 if (runs[3] == SkRegion_kRunTypeSentinel) { // should be first left...
reed@google.com9c36a762012-05-02 18:07:33 +0000291 runs += 3; // skip empty initial span
292 runs[0] = runs[-2]; // set new top to prev bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
reed@google.com9c36a762012-05-02 18:07:33 +0000294 assert_sentinel(runs[2], false); // intervalcount
295 assert_sentinel(runs[3], false); // left
296 assert_sentinel(runs[4], false); // right
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 }
298
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 assert_sentinel(stop[-1], true);
300 assert_sentinel(stop[-2], true);
reed@google.com9c36a762012-05-02 18:07:33 +0000301
302 // now check for a trailing empty span
Cary Clark69436892018-07-28 10:14:27 -0400303 if (stop[-5] == SkRegion_kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
304 stop[-4] = SkRegion_kRunTypeSentinel; // kill empty last span
reed@google.com9c36a762012-05-02 18:07:33 +0000305 stop -= 3;
306 assert_sentinel(stop[-1], true); // last y-sentinel
307 assert_sentinel(stop[-2], true); // last x-sentinel
308 assert_sentinel(stop[-3], false); // last right
309 assert_sentinel(stop[-4], false); // last left
310 assert_sentinel(stop[-5], false); // last interval-count
311 assert_sentinel(stop[-6], false); // last bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 }
313 count = (int)(stop - runs);
314 }
315
316 SkASSERT(count >= kRectRegionRuns);
317
reed@google.com9c36a762012-05-02 18:07:33 +0000318 if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 return this->setRect(fBounds);
320 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000321
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 // if we get here, we need to become a complex region
323
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000324 if (!this->isComplex() || fRunHead->fRunCount != count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 this->freeRuns();
reed@google.com9c36a762012-05-02 18:07:33 +0000326 this->allocateRuns(count);
Hal Canary251bf3e2017-02-16 12:42:24 -0500327 SkASSERT(this->isComplex());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 // must call this before we can write directly into runs()
331 // in case we are sharing the buffer with another region (copy on write)
332 fRunHead = fRunHead->ensureWritable();
333 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
reed@google.com9c36a762012-05-02 18:07:33 +0000334 fRunHead->computeRunBounds(&fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
Mike Reed0ec7dc72018-01-16 13:29:18 -0500336 // Our computed bounds might be too large, so we have to check here.
337 if (fBounds.isEmpty()) {
338 return this->setEmpty();
339 }
340
Cary Clark69436892018-07-28 10:14:27 -0400341 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342
343 return true;
344}
345
346void SkRegion::BuildRectRuns(const SkIRect& bounds,
reed@google.comc34f53d2012-04-30 15:10:13 +0000347 RunType runs[kRectRegionRuns]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 runs[0] = bounds.fTop;
349 runs[1] = bounds.fBottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000350 runs[2] = 1; // 1 interval for this scanline
351 runs[3] = bounds.fLeft;
352 runs[4] = bounds.fRight;
Cary Clark69436892018-07-28 10:14:27 -0400353 runs[5] = SkRegion_kRunTypeSentinel;
354 runs[6] = SkRegion_kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355}
356
reed@google.comc34f53d2012-04-30 15:10:13 +0000357bool SkRegion::contains(int32_t x, int32_t y) const {
Cary Clark69436892018-07-28 10:14:27 -0400358 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@google.com9c36a762012-05-02 18:07:33 +0000359
reed@google.comc34f53d2012-04-30 15:10:13 +0000360 if (!fBounds.contains(x, y)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000362 }
363 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000365 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000367
reed@google.com9c36a762012-05-02 18:07:33 +0000368 const RunType* runs = fRunHead->findScanline(y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369
reed@google.com9c36a762012-05-02 18:07:33 +0000370 // Skip the Bottom and IntervalCount
371 runs += 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000372
reed@google.com9c36a762012-05-02 18:07:33 +0000373 // Just walk this scanline, checking each interval. The X-sentinel will
374 // appear as a left-inteval (runs[0]) and should abort the search.
375 //
376 // We could do a bsearch, using interval-count (runs[1]), but need to time
377 // when that would be worthwhile.
378 //
379 for (;;) {
380 if (x < runs[0]) {
381 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 }
reed@google.com9c36a762012-05-02 18:07:33 +0000383 if (x < runs[1]) {
384 return true;
385 }
386 runs += 2;
387 }
388 return false;
389}
390
Cary Clark69436892018-07-28 10:14:27 -0400391static SkRegionPriv::RunType scanline_bottom(const SkRegionPriv::RunType runs[]) {
mike@reedtribe.org796a1752012-11-07 03:39:46 +0000392 return runs[0];
393}
394
Cary Clark69436892018-07-28 10:14:27 -0400395static const SkRegionPriv::RunType* scanline_next(const SkRegionPriv::RunType runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +0000396 // skip [B N [L R]... S]
397 return runs + 2 + runs[1] * 2 + 1;
398}
399
Cary Clark69436892018-07-28 10:14:27 -0400400static bool scanline_contains(const SkRegionPriv::RunType runs[],
401 SkRegionPriv::RunType L, SkRegionPriv::RunType R) {
reed@google.com9c36a762012-05-02 18:07:33 +0000402 runs += 2; // skip Bottom and IntervalCount
403 for (;;) {
404 if (L < runs[0]) {
405 break;
406 }
407 if (R <= runs[1]) {
408 return true;
409 }
410 runs += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 }
412 return false;
413}
414
reed@google.comc34f53d2012-04-30 15:10:13 +0000415bool SkRegion::contains(const SkIRect& r) const {
Cary Clark69436892018-07-28 10:14:27 -0400416 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@google.com9c36a762012-05-02 18:07:33 +0000417
418 if (!fBounds.contains(r)) {
419 return false;
420 }
421 if (this->isRect()) {
422 return true;
423 }
reed@google.com9c36a762012-05-02 18:07:33 +0000424 SkASSERT(this->isComplex());
reed@google.combb094b92012-11-07 14:23:42 +0000425
reed@google.com9c36a762012-05-02 18:07:33 +0000426 const RunType* scanline = fRunHead->findScanline(r.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000427 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000428 if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
429 return false;
430 }
reed@google.combb094b92012-11-07 14:23:42 +0000431 if (r.fBottom <= scanline_bottom(scanline)) {
432 break;
433 }
reed@google.com9c36a762012-05-02 18:07:33 +0000434 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000435 }
reed@google.com9c36a762012-05-02 18:07:33 +0000436 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437}
438
reed@google.comc34f53d2012-04-30 15:10:13 +0000439bool SkRegion::contains(const SkRegion& rgn) const {
Cary Clark69436892018-07-28 10:14:27 -0400440 SkDEBUGCODE(SkRegionPriv::Validate(*this));
441 SkDEBUGCODE(SkRegionPriv::Validate(rgn));
reed@google.com9c36a762012-05-02 18:07:33 +0000442
reed@google.comc34f53d2012-04-30 15:10:13 +0000443 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 return false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000445 }
446 if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 return true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000448 }
reed@google.com9c36a762012-05-02 18:07:33 +0000449 if (rgn.isRect()) {
450 return this->contains(rgn.getBounds());
451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452
reed@google.com7d4aee32012-04-30 13:29:02 +0000453 /*
454 * A contains B is equivalent to
455 * B - A == 0
456 */
halcanary96fcdcc2015-08-27 07:41:13 -0700457 return !Oper(rgn, *this, kDifference_Op, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458}
459
reed@google.comc34f53d2012-04-30 15:10:13 +0000460const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
reed@google.comaf7e6942012-05-01 14:43:22 +0000461 int* intervals) const {
462 SkASSERT(tmpStorage && intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 const RunType* runs = tmpStorage;
464
reed@google.comc34f53d2012-04-30 15:10:13 +0000465 if (this->isEmpty()) {
Cary Clark69436892018-07-28 10:14:27 -0400466 tmpStorage[0] = SkRegion_kRunTypeSentinel;
reed@google.comaf7e6942012-05-01 14:43:22 +0000467 *intervals = 0;
reed@google.comc34f53d2012-04-30 15:10:13 +0000468 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 BuildRectRuns(fBounds, tmpStorage);
reed@google.comaf7e6942012-05-01 14:43:22 +0000470 *intervals = 1;
reed@google.comc34f53d2012-04-30 15:10:13 +0000471 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 runs = fRunHead->readonly_runs();
reed@google.comaf7e6942012-05-01 14:43:22 +0000473 *intervals = fRunHead->getIntervalCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 }
475 return runs;
476}
477
reed@google.comc34f53d2012-04-30 15:10:13 +0000478///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479
Cary Clark69436892018-07-28 10:14:27 -0400480static bool scanline_intersects(const SkRegionPriv::RunType runs[],
481 SkRegionPriv::RunType L, SkRegionPriv::RunType R) {
reed@google.com9c36a762012-05-02 18:07:33 +0000482 runs += 2; // skip Bottom and IntervalCount
483 for (;;) {
484 if (R <= runs[0]) {
485 break;
486 }
487 if (L < runs[1]) {
488 return true;
489 }
490 runs += 2;
491 }
492 return false;
493}
494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495bool SkRegion::intersects(const SkIRect& r) const {
Cary Clark69436892018-07-28 10:14:27 -0400496 SkDEBUGCODE(SkRegionPriv::Validate(*this));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000497
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 if (this->isEmpty() || r.isEmpty()) {
499 return false;
500 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000501
reed@google.com9c36a762012-05-02 18:07:33 +0000502 SkIRect sect;
503 if (!sect.intersect(fBounds, r)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 return false;
505 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 if (this->isRect()) {
507 return true;
508 }
reed@google.combb094b92012-11-07 14:23:42 +0000509 SkASSERT(this->isComplex());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000510
reed@google.com9c36a762012-05-02 18:07:33 +0000511 const RunType* scanline = fRunHead->findScanline(sect.fTop);
reed@google.combb094b92012-11-07 14:23:42 +0000512 for (;;) {
reed@google.com9c36a762012-05-02 18:07:33 +0000513 if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
514 return true;
515 }
reed@google.combb094b92012-11-07 14:23:42 +0000516 if (sect.fBottom <= scanline_bottom(scanline)) {
517 break;
518 }
reed@google.com9c36a762012-05-02 18:07:33 +0000519 scanline = scanline_next(scanline);
reed@google.combb094b92012-11-07 14:23:42 +0000520 }
reed@google.com9c36a762012-05-02 18:07:33 +0000521 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
524bool SkRegion::intersects(const SkRegion& rgn) const {
525 if (this->isEmpty() || rgn.isEmpty()) {
526 return false;
527 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
530 return false;
531 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000532
reed@google.com9c36a762012-05-02 18:07:33 +0000533 bool weAreARect = this->isRect();
534 bool theyAreARect = rgn.isRect();
535
536 if (weAreARect && theyAreARect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 return true;
538 }
reed@google.com9c36a762012-05-02 18:07:33 +0000539 if (weAreARect) {
540 return rgn.intersects(this->getBounds());
541 }
542 if (theyAreARect) {
543 return this->intersects(rgn.getBounds());
544 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000545
reed@google.com9c36a762012-05-02 18:07:33 +0000546 // both of us are complex
halcanary96fcdcc2015-08-27 07:41:13 -0700547 return Oper(*this, rgn, kIntersect_Op, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548}
549
reed@google.comc34f53d2012-04-30 15:10:13 +0000550///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551
reed@google.com6d428d32012-01-25 21:53:53 +0000552bool SkRegion::operator==(const SkRegion& b) const {
Cary Clark69436892018-07-28 10:14:27 -0400553 SkDEBUGCODE(SkRegionPriv::Validate(*this));
554 SkDEBUGCODE(SkRegionPriv::Validate(b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555
reed@google.com6d428d32012-01-25 21:53:53 +0000556 if (this == &b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000558 }
reed@google.com6d428d32012-01-25 21:53:53 +0000559 if (fBounds != b.fBounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000561 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000562
reed@google.com6d428d32012-01-25 21:53:53 +0000563 const SkRegion::RunHead* ah = fRunHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 const SkRegion::RunHead* bh = b.fRunHead;
565
566 // this catches empties and rects being equal
reed@google.com97fa34c2011-03-18 14:44:42 +0000567 if (ah == bh) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000569 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 // now we insist that both are complex (but different ptrs)
bungeman@google.comb77b69f2013-01-24 16:38:23 +0000571 if (!this->isComplex() || !b.isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000573 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 return ah->fRunCount == bh->fRunCount &&
575 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
576 ah->fRunCount * sizeof(SkRegion::RunType));
577}
578
Mike Reeddc3192b2018-04-30 17:05:15 -0400579// Return a (new) offset such that when applied (+=) to min and max, we don't overflow/underflow
580static int32_t pin_offset_s32(int32_t min, int32_t max, int32_t offset) {
581 SkASSERT(min <= max);
582 const int32_t lo = -SK_MaxS32-1,
583 hi = +SK_MaxS32;
584 if ((int64_t)min + offset < lo) { offset = lo - min; }
585 if ((int64_t)max + offset > hi) { offset = hi - max; }
586 return offset;
587}
588
reed@google.com97fa34c2011-03-18 14:44:42 +0000589void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
Cary Clark69436892018-07-28 10:14:27 -0400590 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591
halcanary96fcdcc2015-08-27 07:41:13 -0700592 if (nullptr == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 return;
reed@google.com97fa34c2011-03-18 14:44:42 +0000594 }
595 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 dst->setEmpty();
Mike Reeddc3192b2018-04-30 17:05:15 -0400597 return;
598 }
599 // pin dx and dy so we don't overflow our existing bounds
600 dx = pin_offset_s32(fBounds.fLeft, fBounds.fRight, dx);
601 dy = pin_offset_s32(fBounds.fTop, fBounds.fBottom, dy);
602
603 if (this->isRect()) {
604 dst->setRect(fBounds.makeOffset(dx, dy));
reed@google.com97fa34c2011-03-18 14:44:42 +0000605 } else {
606 if (this == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 dst->fRunHead = dst->fRunHead->ensureWritable();
reed@google.com97fa34c2011-03-18 14:44:42 +0000608 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkRegion tmp;
reed@google.comaf7e6942012-05-01 14:43:22 +0000610 tmp.allocateRuns(*fRunHead);
Hal Canary251bf3e2017-02-16 12:42:24 -0500611 SkASSERT(tmp.isComplex());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 tmp.fBounds = fBounds;
613 dst->swap(tmp);
614 }
615
616 dst->fBounds.offset(dx, dy);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 const RunType* sruns = fRunHead->readonly_runs();
619 RunType* druns = dst->fRunHead->writable_runs();
620
621 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
reed@google.com97fa34c2011-03-18 14:44:42 +0000622 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 int bottom = *sruns++;
Cary Clark69436892018-07-28 10:14:27 -0400624 if (bottom == SkRegion_kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
reed@google.com9c36a762012-05-02 18:07:33 +0000628 *druns++ = *sruns++; // copy intervalCount;
reed@google.com97fa34c2011-03-18 14:44:42 +0000629 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 int x = *sruns++;
Cary Clark69436892018-07-28 10:14:27 -0400631 if (x == SkRegion_kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000633 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 *druns++ = (SkRegion::RunType)(x + dx);
635 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
636 }
Cary Clark69436892018-07-28 10:14:27 -0400637 *druns++ = SkRegion_kRunTypeSentinel; // x sentinel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 }
Cary Clark69436892018-07-28 10:14:27 -0400639 *druns++ = SkRegion_kRunTypeSentinel; // y sentinel
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640
641 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
642 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
643 }
644
Cary Clark69436892018-07-28 10:14:27 -0400645 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646}
647
reed@android.com097a3512010-07-13 18:35:14 +0000648///////////////////////////////////////////////////////////////////////////////
649
reed@android.com097a3512010-07-13 18:35:14 +0000650bool SkRegion::setRects(const SkIRect rects[], int count) {
651 if (0 == count) {
reed@android.comcb342352010-07-22 18:27:53 +0000652 this->setEmpty();
653 } else {
654 this->setRect(rects[0]);
655 for (int i = 1; i < count; i++) {
656 this->op(rects[i], kUnion_Op);
reed@android.com097a3512010-07-13 18:35:14 +0000657 }
658 }
reed@android.comcb342352010-07-22 18:27:53 +0000659 return !this->isEmpty();
reed@android.com097a3512010-07-13 18:35:14 +0000660}
661
662///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663
bungemand7dc76f2016-03-10 11:14:40 -0800664#if defined _WIN32 // disable warning : local variable used without having been initialized
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665#pragma warning ( push )
666#pragma warning ( disable : 4701 )
667#endif
668
669#ifdef SK_DEBUG
670static void assert_valid_pair(int left, int rite)
671{
Cary Clark69436892018-07-28 10:14:27 -0400672 SkASSERT(left == SkRegion_kRunTypeSentinel || left < rite);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673}
674#else
675 #define assert_valid_pair(left, rite)
676#endif
677
678struct spanRec {
Cary Clark69436892018-07-28 10:14:27 -0400679 const SkRegionPriv::RunType* fA_runs;
680 const SkRegionPriv::RunType* fB_runs;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 int fA_left, fA_rite, fB_left, fB_rite;
682 int fLeft, fRite, fInside;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000683
Cary Clark69436892018-07-28 10:14:27 -0400684 void init(const SkRegionPriv::RunType a_runs[],
685 const SkRegionPriv::RunType b_runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 fA_left = *a_runs++;
687 fA_rite = *a_runs++;
688 fB_left = *b_runs++;
689 fB_rite = *b_runs++;
690
691 fA_runs = a_runs;
692 fB_runs = b_runs;
693 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000694
reed@google.comc34f53d2012-04-30 15:10:13 +0000695 bool done() const {
Cary Clark69436892018-07-28 10:14:27 -0400696 SkASSERT(fA_left <= SkRegion_kRunTypeSentinel);
697 SkASSERT(fB_left <= SkRegion_kRunTypeSentinel);
698 return fA_left == SkRegion_kRunTypeSentinel &&
699 fB_left == SkRegion_kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 }
701
reed@google.comc34f53d2012-04-30 15:10:13 +0000702 void next() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 assert_valid_pair(fA_left, fA_rite);
704 assert_valid_pair(fB_left, fB_rite);
705
706 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
707 bool a_flush = false;
708 bool b_flush = false;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 int a_left = fA_left;
711 int a_rite = fA_rite;
712 int b_left = fB_left;
713 int b_rite = fB_rite;
714
reed@google.comc34f53d2012-04-30 15:10:13 +0000715 if (a_left < b_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 inside = 1;
717 left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000718 if (a_rite <= b_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 rite = a_rite;
720 a_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000721 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 rite = a_left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000723 }
724 } else if (b_left < a_left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 inside = 2;
726 left = b_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000727 if (b_rite <= a_left) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 rite = b_rite;
729 b_flush = true;
reed@google.comc34f53d2012-04-30 15:10:13 +0000730 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 rite = b_left = a_left;
reed@google.comc34f53d2012-04-30 15:10:13 +0000732 }
733 } else { // a_left == b_left
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 inside = 3;
735 left = a_left; // or b_left
reed@google.comc34f53d2012-04-30 15:10:13 +0000736 if (a_rite <= b_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 rite = b_left = a_rite;
738 a_flush = true;
739 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000740 if (b_rite <= a_rite) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 rite = a_left = b_rite;
742 b_flush = true;
743 }
744 }
745
reed@google.comc34f53d2012-04-30 15:10:13 +0000746 if (a_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747 a_left = *fA_runs++;
748 a_rite = *fA_runs++;
749 }
reed@google.comc34f53d2012-04-30 15:10:13 +0000750 if (b_flush) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 b_left = *fB_runs++;
752 b_rite = *fB_runs++;
753 }
754
755 SkASSERT(left <= rite);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 // now update our state
758 fA_left = a_left;
759 fA_rite = a_rite;
760 fB_left = b_left;
761 fB_rite = b_rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000762
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763 fLeft = left;
764 fRite = rite;
765 fInside = inside;
766 }
767};
768
Cary Clark69436892018-07-28 10:14:27 -0400769static int distance_to_sentinel(const SkRegionPriv::RunType* runs) {
770 const SkRegionPriv::RunType* ptr = runs;
771 while (*ptr != SkRegion_kRunTypeSentinel) { ptr += 2; }
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400772 return ptr - runs;
773}
774
Cary Clark69436892018-07-28 10:14:27 -0400775static int operate_on_span(const SkRegionPriv::RunType a_runs[],
776 const SkRegionPriv::RunType b_runs[],
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400777 RunArray* array, int dstOffset,
778 int min, int max) {
779 // This is a worst-case for this span plus two for TWO terminating sentinels.
780 array->resizeToAtLeast(
781 dstOffset + distance_to_sentinel(a_runs) + distance_to_sentinel(b_runs) + 2);
Cary Clark69436892018-07-28 10:14:27 -0400782 SkRegionPriv::RunType* dst = &(*array)[dstOffset]; // get pointer AFTER resizing.
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 spanRec rec;
785 bool firstInterval = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 rec.init(a_runs, b_runs);
788
reed@google.comc34f53d2012-04-30 15:10:13 +0000789 while (!rec.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 rec.next();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 int left = rec.fLeft;
793 int rite = rec.fRite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 // add left,rite to our dst buffer (checking for coincidence
796 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
reed@google.comc34f53d2012-04-30 15:10:13 +0000797 left < rite) { // skip if equal
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400798 if (firstInterval || *(dst - 1) < left) {
Cary Clark69436892018-07-28 10:14:27 -0400799 *dst++ = (SkRegionPriv::RunType)(left);
800 *dst++ = (SkRegionPriv::RunType)(rite);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 firstInterval = false;
reed@google.comc34f53d2012-04-30 15:10:13 +0000802 } else {
803 // update the right edge
Cary Clark69436892018-07-28 10:14:27 -0400804 *(dst - 1) = (SkRegionPriv::RunType)(rite);
reed@google.comc34f53d2012-04-30 15:10:13 +0000805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 }
807 }
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400808 SkASSERT(dst < &(*array)[array->count() - 1]);
Cary Clark69436892018-07-28 10:14:27 -0400809 *dst++ = SkRegion_kRunTypeSentinel;
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400810 return dst - &(*array)[0];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811}
812
bungemand7dc76f2016-03-10 11:14:40 -0800813#if defined _WIN32
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814#pragma warning ( pop )
815#endif
816
817static const struct {
818 uint8_t fMin;
819 uint8_t fMax;
820} gOpMinMax[] = {
821 { 1, 1 }, // Difference
822 { 3, 3 }, // Intersection
823 { 1, 3 }, // Union
824 { 1, 2 } // XOR
825};
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400826// need to ensure that the op enum lines up with our minmax array
827static_assert(0 == SkRegion::kDifference_Op, "");
828static_assert(1 == SkRegion::kIntersect_Op, "");
829static_assert(2 == SkRegion::kUnion_Op, "");
830static_assert(3 == SkRegion::kXOR_Op, "");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831
832class RgnOper {
833public:
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400834 RgnOper(int top, RunArray* array, SkRegion::Op op)
835 : fMin(gOpMinMax[op].fMin)
836 , fMax(gOpMinMax[op].fMax)
837 , fArray(array)
Cary Clark69436892018-07-28 10:14:27 -0400838 , fTop((SkRegionPriv::RunType)top) // just a first guess, we might update this
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400839 { SkASSERT((unsigned)op <= 3); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840
Cary Clark69436892018-07-28 10:14:27 -0400841 void addSpan(int bottom, const SkRegionPriv::RunType a_runs[],
842 const SkRegionPriv::RunType b_runs[]) {
reed@google.com9c36a762012-05-02 18:07:33 +0000843 // skip X values and slots for the next Y+intervalCount
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400844 int start = fPrevDst + fPrevLen + 2;
reed@google.com9c36a762012-05-02 18:07:33 +0000845 // start points to beginning of dst interval
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400846 int stop = operate_on_span(a_runs, b_runs, fArray, start, fMin, fMax);
847 size_t len = SkToSizeT(stop - start);
reed@google.com9c36a762012-05-02 18:07:33 +0000848 SkASSERT(len >= 1 && (len & 1) == 1);
Cary Clark69436892018-07-28 10:14:27 -0400849 SkASSERT(SkRegion_kRunTypeSentinel == (*fArray)[stop - 1]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400851 // Assert memcmp won't exceed fArray->count().
852 SkASSERT(fArray->count() >= SkToInt(start + len - 1));
reed@google.comc34f53d2012-04-30 15:10:13 +0000853 if (fPrevLen == len &&
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400854 (1 == len || !memcmp(&(*fArray)[fPrevDst],
855 &(*fArray)[start],
Cary Clark69436892018-07-28 10:14:27 -0400856 (len - 1) * sizeof(SkRegionPriv::RunType)))) {
reed@google.comc34f53d2012-04-30 15:10:13 +0000857 // update Y value
Cary Clark69436892018-07-28 10:14:27 -0400858 (*fArray)[fPrevDst - 2] = (SkRegionPriv::RunType)bottom;
reed@google.comc34f53d2012-04-30 15:10:13 +0000859 } else { // accept the new span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 if (len == 1 && fPrevLen == 0) {
Cary Clark69436892018-07-28 10:14:27 -0400861 fTop = (SkRegionPriv::RunType)bottom; // just update our bottom
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 } else {
Cary Clark69436892018-07-28 10:14:27 -0400863 (*fArray)[start - 2] = (SkRegionPriv::RunType)bottom;
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400864 (*fArray)[start - 1] = SkToS32(len >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 fPrevDst = start;
866 fPrevLen = len;
867 }
868 }
869 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000870
reed@google.comc34f53d2012-04-30 15:10:13 +0000871 int flush() {
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400872 (*fArray)[fStartDst] = fTop;
873 // Previously reserved enough for TWO sentinals.
874 SkASSERT(fArray->count() > SkToInt(fPrevDst + fPrevLen));
Cary Clark69436892018-07-28 10:14:27 -0400875 (*fArray)[fPrevDst + fPrevLen] = SkRegion_kRunTypeSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
877 }
878
reed@google.com7d4aee32012-04-30 13:29:02 +0000879 bool isEmpty() const { return 0 == fPrevLen; }
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 uint8_t fMin, fMax;
882
883private:
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400884 RunArray* fArray;
885 int fStartDst = 0;
886 int fPrevDst = 1;
887 size_t fPrevLen = 0; // will never match a length from operate_on_span
Cary Clark69436892018-07-28 10:14:27 -0400888 SkRegionPriv::RunType fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889};
890
reed@google.com7d4aee32012-04-30 13:29:02 +0000891// want a unique value to signal that we exited due to quickExit
892#define QUICK_EXIT_TRUE_COUNT (-1)
893
Cary Clark69436892018-07-28 10:14:27 -0400894static int operate(const SkRegionPriv::RunType a_runs[],
895 const SkRegionPriv::RunType b_runs[],
Hal Canaryc5a1c1f2018-04-04 10:52:00 -0400896 RunArray* dst,
reed@google.com7d4aee32012-04-30 13:29:02 +0000897 SkRegion::Op op,
898 bool quickExit) {
Cary Clark69436892018-07-28 10:14:27 -0400899 const SkRegionPriv::RunType gEmptyScanline[] = {
reed@google.com9c36a762012-05-02 18:07:33 +0000900 0, // dummy bottom value
901 0, // zero intervals
Cary Clark69436892018-07-28 10:14:27 -0400902 SkRegion_kRunTypeSentinel,
reed@google.com5f7c8a52012-05-03 16:17:38 +0000903 // just need a 2nd value, since spanRec.init() reads 2 values, even
904 // though if the first value is the sentinel, it ignores the 2nd value.
905 // w/o the 2nd value here, we might read uninitialized memory.
906 // This happens when we are using gSentinel, which is pointing at
907 // our sentinel value.
908 0
reed@android.comfbb02e72010-04-13 14:52:52 +0000909 };
Cary Clark69436892018-07-28 10:14:27 -0400910 const SkRegionPriv::RunType* const gSentinel = &gEmptyScanline[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911
912 int a_top = *a_runs++;
913 int a_bot = *a_runs++;
914 int b_top = *b_runs++;
915 int b_bot = *b_runs++;
916
reed@google.com9c36a762012-05-02 18:07:33 +0000917 a_runs += 1; // skip the intervalCount;
918 b_runs += 1; // skip the intervalCount;
919
920 // Now a_runs and b_runs to their intervals (or sentinel)
921
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 assert_sentinel(a_top, false);
923 assert_sentinel(a_bot, false);
924 assert_sentinel(b_top, false);
925 assert_sentinel(b_bot, false);
926
927 RgnOper oper(SkMin32(a_top, b_top), dst, op);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000928
Cary Clark69436892018-07-28 10:14:27 -0400929 int prevBot = SkRegion_kRunTypeSentinel; // so we fail the first test
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000930
Cary Clark69436892018-07-28 10:14:27 -0400931 while (a_bot < SkRegion_kRunTypeSentinel ||
932 b_bot < SkRegion_kRunTypeSentinel) {
reed@google.com1deaab52011-10-06 13:11:46 +0000933 int top, bot SK_INIT_TO_AVOID_WARNING;
Cary Clark69436892018-07-28 10:14:27 -0400934 const SkRegionPriv::RunType* run0 = gSentinel;
935 const SkRegionPriv::RunType* run1 = gSentinel;
reed@google.com1deaab52011-10-06 13:11:46 +0000936 bool a_flush = false;
937 bool b_flush = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938
reed@google.com1deaab52011-10-06 13:11:46 +0000939 if (a_top < b_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 top = a_top;
941 run0 = a_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000942 if (a_bot <= b_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 bot = a_bot;
944 a_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000945 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 bot = a_top = b_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000947 }
948 } else if (b_top < a_top) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 top = b_top;
950 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000951 if (b_bot <= a_top) { // [...] <...>
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 bot = b_bot;
953 b_flush = true;
reed@google.com1deaab52011-10-06 13:11:46 +0000954 } else { // [...<..]...> or [...<...>...]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 bot = b_top = a_top;
reed@google.com1deaab52011-10-06 13:11:46 +0000956 }
957 } else { // a_top == b_top
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 top = a_top; // or b_top
959 run0 = a_runs;
960 run1 = b_runs;
reed@google.com1deaab52011-10-06 13:11:46 +0000961 if (a_bot <= b_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 bot = b_top = a_bot;
963 a_flush = true;
964 }
reed@google.com1deaab52011-10-06 13:11:46 +0000965 if (b_bot <= a_bot) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 bot = a_top = b_bot;
967 b_flush = true;
968 }
969 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000970
reed@google.com1deaab52011-10-06 13:11:46 +0000971 if (top > prevBot) {
reed@android.comfbb02e72010-04-13 14:52:52 +0000972 oper.addSpan(top, gSentinel, gSentinel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 }
reed@google.com1deaab52011-10-06 13:11:46 +0000974 oper.addSpan(bot, run0, run1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975
reed@google.com7d4aee32012-04-30 13:29:02 +0000976 if (quickExit && !oper.isEmpty()) {
977 return QUICK_EXIT_TRUE_COUNT;
978 }
979
reed@google.com1deaab52011-10-06 13:11:46 +0000980 if (a_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000981 a_runs = skip_intervals(a_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 a_top = a_bot;
983 a_bot = *a_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000984 a_runs += 1; // skip uninitialized intervalCount
Cary Clark69436892018-07-28 10:14:27 -0400985 if (a_bot == SkRegion_kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 a_top = a_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000987 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
reed@google.com1deaab52011-10-06 13:11:46 +0000989 if (b_flush) {
reed@google.com9c36a762012-05-02 18:07:33 +0000990 b_runs = skip_intervals(b_runs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 b_top = b_bot;
992 b_bot = *b_runs++;
reed@google.com9c36a762012-05-02 18:07:33 +0000993 b_runs += 1; // skip uninitialized intervalCount
Cary Clark69436892018-07-28 10:14:27 -0400994 if (b_bot == SkRegion_kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 b_top = b_bot;
reed@google.com1deaab52011-10-06 13:11:46 +0000996 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000998
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 prevBot = bot;
1000 }
1001 return oper.flush();
1002}
1003
1004///////////////////////////////////////////////////////////////////////////////
1005
1006/* Given count RunTypes in a complex region, return the worst case number of
1007 logical intervals that represents (i.e. number of rects that would be
1008 returned from the iterator).
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001009
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 We could just return count/2, since there must be at least 2 values per
1011 interval, but we can first trim off the const overhead of the initial TOP
1012 value, plus the final BOTTOM + 2 sentinels.
1013 */
caryclark@google.com803eceb2012-06-06 12:09:34 +00001014#if 0 // UNUSED
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015static int count_to_intervals(int count) {
1016 SkASSERT(count >= 6); // a single rect is 6 values
1017 return (count - 4) >> 1;
1018}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001019#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020
reed@google.com7d4aee32012-04-30 13:29:02 +00001021static bool setEmptyCheck(SkRegion* result) {
1022 return result ? result->setEmpty() : false;
1023}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024
reed@google.com7d4aee32012-04-30 13:29:02 +00001025static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
1026 return result ? result->setRect(rect) : !rect.isEmpty();
1027}
1028
1029static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
1030 return result ? result->setRegion(rgn) : !rgn.isEmpty();
1031}
1032
1033bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
1034 SkRegion* result) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 SkASSERT((unsigned)op < kOpCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001036
reed@google.com7d4aee32012-04-30 13:29:02 +00001037 if (kReplace_Op == op) {
1038 return setRegionCheck(result, rgnbOrig);
1039 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 // swith to using pointers, so we can swap them as needed
1042 const SkRegion* rgna = &rgnaOrig;
1043 const SkRegion* rgnb = &rgnbOrig;
1044 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
1045
1046 // collaps difference and reverse-difference into just difference
reed@google.com7d4aee32012-04-30 13:29:02 +00001047 if (kReverseDifference_Op == op) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001048 using std::swap;
1049 swap(rgna, rgnb);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 op = kDifference_Op;
1051 }
1052
1053 SkIRect bounds;
1054 bool a_empty = rgna->isEmpty();
1055 bool b_empty = rgnb->isEmpty();
1056 bool a_rect = rgna->isRect();
1057 bool b_rect = rgnb->isRect();
1058
1059 switch (op) {
1060 case kDifference_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001061 if (a_empty) {
1062 return setEmptyCheck(result);
1063 }
reed@google.com0d102802012-05-31 18:28:59 +00001064 if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
1065 rgnb->fBounds)) {
reed@google.com7d4aee32012-04-30 13:29:02 +00001066 return setRegionCheck(result, *rgna);
1067 }
reed@google.com0d102802012-05-31 18:28:59 +00001068 if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
1069 return setEmptyCheck(result);
1070 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 break;
1072
1073 case kIntersect_Op:
1074 if ((a_empty | b_empty)
reed@google.com7d4aee32012-04-30 13:29:02 +00001075 || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
1076 return setEmptyCheck(result);
1077 }
1078 if (a_rect & b_rect) {
1079 return setRectCheck(result, bounds);
1080 }
reed@google.com633c32b2013-01-31 15:24:24 +00001081 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1082 return setRegionCheck(result, *rgnb);
1083 }
1084 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1085 return setRegionCheck(result, *rgna);
1086 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 break;
1088
1089 case kUnion_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001090 if (a_empty) {
1091 return setRegionCheck(result, *rgnb);
1092 }
1093 if (b_empty) {
1094 return setRegionCheck(result, *rgna);
1095 }
1096 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1097 return setRegionCheck(result, *rgna);
1098 }
1099 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1100 return setRegionCheck(result, *rgnb);
1101 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 break;
1103
1104 case kXOR_Op:
reed@google.com7d4aee32012-04-30 13:29:02 +00001105 if (a_empty) {
1106 return setRegionCheck(result, *rgnb);
1107 }
1108 if (b_empty) {
1109 return setRegionCheck(result, *rgna);
1110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 break;
1112 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001113 SkDEBUGFAIL("unknown region op");
reed@google.com7d4aee32012-04-30 13:29:02 +00001114 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 }
1116
1117 RunType tmpA[kRectRegionRuns];
1118 RunType tmpB[kRectRegionRuns];
1119
reed@google.comaf7e6942012-05-01 14:43:22 +00001120 int a_intervals, b_intervals;
1121 const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
1122 const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123
Hal Canaryc5a1c1f2018-04-04 10:52:00 -04001124 RunArray array;
1125 int count = operate(a_runs, b_runs, &array, op, nullptr == result);
1126 SkASSERT(count <= array.count());
reed@google.comaf7e6942012-05-01 14:43:22 +00001127
reed@google.com7d4aee32012-04-30 13:29:02 +00001128 if (result) {
1129 SkASSERT(count >= 0);
Hal Canaryc5a1c1f2018-04-04 10:52:00 -04001130 return result->setRuns(&array[0], count);
reed@google.com7d4aee32012-04-30 13:29:02 +00001131 } else {
1132 return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
1133 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134}
1135
reed@google.com7d4aee32012-04-30 13:29:02 +00001136bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
Cary Clark69436892018-07-28 10:14:27 -04001137 SkDEBUGCODE(SkRegionPriv::Validate(*this));
reed@google.com7d4aee32012-04-30 13:29:02 +00001138 return SkRegion::Oper(rgna, rgnb, op, this);
1139}
1140
1141///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142
Mike Kleinc0bd9f92019-04-23 12:05:21 -05001143#include "src/core/SkBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001145size_t SkRegion::writeToMemory(void* storage) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001146 if (nullptr == storage) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001147 size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 if (!this->isEmpty()) {
1149 size += sizeof(fBounds);
1150 if (this->isComplex()) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001151 size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 size += fRunHead->fRunCount * sizeof(RunType);
1153 }
1154 }
1155 return size;
1156 }
1157
1158 SkWBuffer buffer(storage);
1159
1160 if (this->isEmpty()) {
1161 buffer.write32(-1);
1162 } else {
1163 bool isRect = this->isRect();
1164
1165 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1166 buffer.write(&fBounds, sizeof(fBounds));
1167
1168 if (!isRect) {
reed@google.comaf7e6942012-05-01 14:43:22 +00001169 buffer.write32(fRunHead->getYSpanCount());
1170 buffer.write32(fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 buffer.write(fRunHead->readonly_runs(),
1172 fRunHead->fRunCount * sizeof(RunType));
1173 }
1174 }
1175 return buffer.pos();
1176}
1177
Hal Canary5ade1f62017-12-20 13:09:26 -05001178static bool validate_run_count(int ySpanCount, int intervalCount, int runCount) {
1179 // return 2 + 3 * ySpanCount + 2 * intervalCount;
1180 if (ySpanCount < 1 || intervalCount < 2) {
1181 return false;
1182 }
1183 SkSafeMath safeMath;
1184 int sum = 2;
1185 sum = safeMath.addInt(sum, ySpanCount);
1186 sum = safeMath.addInt(sum, ySpanCount);
1187 sum = safeMath.addInt(sum, ySpanCount);
1188 sum = safeMath.addInt(sum, intervalCount);
1189 sum = safeMath.addInt(sum, intervalCount);
1190 return safeMath && sum == runCount;
1191}
1192
Hal Canary58a1ea82017-02-19 22:16:10 -05001193// Validate that a memory sequence is a valid region.
1194// Try to check all possible errors.
1195// never read beyond &runs[runCount-1].
1196static bool validate_run(const int32_t* runs,
1197 int runCount,
1198 const SkIRect& givenBounds,
1199 int32_t ySpanCount,
1200 int32_t intervalCount) {
1201 // Region Layout:
1202 // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel
Hal Canary5ade1f62017-12-20 13:09:26 -05001203 if (!validate_run_count(SkToInt(ySpanCount), SkToInt(intervalCount), runCount)) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001204 return false;
1205 }
1206 SkASSERT(runCount >= 7); // 7==SkRegion::kRectRegionRuns
1207 // quick sanity check:
Cary Clark69436892018-07-28 10:14:27 -04001208 if (runs[runCount - 1] != SkRegion_kRunTypeSentinel ||
1209 runs[runCount - 2] != SkRegion_kRunTypeSentinel) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001210 return false;
1211 }
1212 const int32_t* const end = runs + runCount;
1213 SkIRect bounds = {0, 0, 0 ,0}; // calulated bounds
1214 SkIRect rect = {0, 0, 0, 0}; // current rect
1215 rect.fTop = *runs++;
Cary Clark69436892018-07-28 10:14:27 -04001216 if (rect.fTop == SkRegion_kRunTypeSentinel) {
1217 return false; // no rect can contain SkRegion_kRunTypeSentinel
Hal Canary58a1ea82017-02-19 22:16:10 -05001218 }
Hal Canaryf975b3e2017-06-22 11:21:57 +00001219 if (rect.fTop != givenBounds.fTop) {
1220 return false; // Must not begin with empty span that does not contribute to bounds.
1221 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001222 do {
1223 --ySpanCount;
1224 if (ySpanCount < 0) {
1225 return false; // too many yspans
1226 }
1227 rect.fBottom = *runs++;
Cary Clark69436892018-07-28 10:14:27 -04001228 if (rect.fBottom == SkRegion_kRunTypeSentinel) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001229 return false;
1230 }
Hal Canaryf975b3e2017-06-22 11:21:57 +00001231 if (rect.fBottom > givenBounds.fBottom) {
1232 return false; // Must not end with empty span that does not contribute to bounds.
1233 }
1234 if (rect.fBottom <= rect.fTop) {
1235 return false; // y-intervals must be ordered; rects must be non-empty.
1236 }
1237
Hal Canary58a1ea82017-02-19 22:16:10 -05001238 int32_t xIntervals = *runs++;
1239 SkASSERT(runs < end);
Mike Reed83455562018-05-02 14:58:36 -04001240 if (xIntervals < 0 || xIntervals > intervalCount || runs + 1 + 2 * xIntervals > end) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001241 return false;
1242 }
1243 intervalCount -= xIntervals;
Hal Canaryf975b3e2017-06-22 11:21:57 +00001244 bool firstInterval = true;
Kevin Lubick42846132018-01-05 10:11:11 -05001245 int32_t lastRight = 0; // check that x-intervals are distinct and ordered.
Hal Canary58a1ea82017-02-19 22:16:10 -05001246 while (xIntervals-- > 0) {
1247 rect.fLeft = *runs++;
1248 rect.fRight = *runs++;
Cary Clark69436892018-07-28 10:14:27 -04001249 if (rect.fLeft == SkRegion_kRunTypeSentinel ||
1250 rect.fRight == SkRegion_kRunTypeSentinel ||
Hal Canaryf975b3e2017-06-22 11:21:57 +00001251 rect.fLeft >= rect.fRight || // check non-empty rect
1252 (!firstInterval && rect.fLeft <= lastRight)) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001253 return false;
1254 }
Hal Canaryf975b3e2017-06-22 11:21:57 +00001255 lastRight = rect.fRight;
1256 firstInterval = false;
Hal Canary58a1ea82017-02-19 22:16:10 -05001257 bounds.join(rect);
1258 }
Cary Clark69436892018-07-28 10:14:27 -04001259 if (*runs++ != SkRegion_kRunTypeSentinel) {
Hal Canary58a1ea82017-02-19 22:16:10 -05001260 return false; // required check sentinal.
1261 }
1262 rect.fTop = rect.fBottom;
1263 SkASSERT(runs < end);
Cary Clark69436892018-07-28 10:14:27 -04001264 } while (*runs != SkRegion_kRunTypeSentinel);
Hal Canary58a1ea82017-02-19 22:16:10 -05001265 ++runs;
1266 if (ySpanCount != 0 || intervalCount != 0 || givenBounds != bounds) {
1267 return false;
1268 }
1269 SkASSERT(runs == end); // if ySpanCount && intervalCount are right, must be correct length.
1270 return true;
1271}
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001272size_t SkRegion::readFromMemory(const void* storage, size_t length) {
Mike Reed1026ccf2017-01-08 14:35:29 -05001273 SkRBuffer buffer(storage, length);
1274 SkRegion tmp;
1275 int32_t count;
skia.committer@gmail.com382fde32013-11-06 07:02:11 +00001276
Hal Canary58a1ea82017-02-19 22:16:10 -05001277 // Serialized Region Format:
1278 // Empty:
1279 // -1
1280 // Simple Rect:
1281 // 0 LEFT TOP RIGHT BOTTOM
1282 // Complex Region:
1283 // COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT [RUNS....]
1284 if (!buffer.readS32(&count) || count < -1) {
1285 return 0;
1286 }
1287 if (count >= 0) {
1288 if (!buffer.read(&tmp.fBounds, sizeof(tmp.fBounds)) || tmp.fBounds.isEmpty()) {
1289 return 0; // Short buffer or bad bounds for non-empty region; report failure.
Hal Canary251bf3e2017-02-16 12:42:24 -05001290 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 if (count == 0) {
1292 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1293 } else {
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +00001294 int32_t ySpanCount, intervalCount;
Hal Canary58a1ea82017-02-19 22:16:10 -05001295 if (!buffer.readS32(&ySpanCount) ||
1296 !buffer.readS32(&intervalCount) ||
1297 buffer.available() < count * sizeof(int32_t)) {
1298 return 0;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001299 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001300 if (!validate_run((const int32_t*)((const char*)storage + buffer.pos()), count,
1301 tmp.fBounds, ySpanCount, intervalCount)) {
1302 return 0; // invalid runs, don't even allocate
1303 }
1304 tmp.allocateRuns(count, ySpanCount, intervalCount);
1305 SkASSERT(tmp.isComplex());
1306 SkAssertResult(buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(int32_t)));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
1308 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001309 SkASSERT(tmp.isValid());
1310 SkASSERT(buffer.isValid());
1311 this->swap(tmp);
1312 return buffer.pos();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313}
1314
reed@google.com0a0a2362011-03-23 13:51:55 +00001315///////////////////////////////////////////////////////////////////////////////
1316
Hal Canary251bf3e2017-02-16 12:42:24 -05001317bool SkRegion::isValid() const {
reed@google.comc34f53d2012-04-30 15:10:13 +00001318 if (this->isEmpty()) {
Hal Canary251bf3e2017-02-16 12:42:24 -05001319 return fBounds == SkIRect{0, 0, 0, 0};
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
Hal Canary58a1ea82017-02-19 22:16:10 -05001321 if (fBounds.isEmpty()) {
1322 return false;
1323 }
1324 if (this->isRect()) {
1325 return true;
1326 }
1327 return fRunHead && fRunHead->fRefCnt > 0 &&
1328 validate_run(fRunHead->readonly_runs(), fRunHead->fRunCount, fBounds,
1329 fRunHead->getYSpanCount(), fRunHead->getIntervalCount());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330}
1331
Hal Canary251bf3e2017-02-16 12:42:24 -05001332#ifdef SK_DEBUG
Cary Clark69436892018-07-28 10:14:27 -04001333void SkRegionPriv::Validate(const SkRegion& rgn) { SkASSERT(rgn.isValid()); }
Hal Canary251bf3e2017-02-16 12:42:24 -05001334
reed@google.comc34f53d2012-04-30 15:10:13 +00001335void SkRegion::dump() const {
1336 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 SkDebugf(" rgn: empty\n");
reed@google.comc34f53d2012-04-30 15:10:13 +00001338 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
reed@google.com9c36a762012-05-02 18:07:33 +00001340 if (this->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 const RunType* runs = fRunHead->readonly_runs();
1342 for (int i = 0; i < fRunHead->fRunCount; i++)
1343 SkDebugf(" %d", runs[i]);
1344 }
1345 SkDebugf("\n");
1346 }
1347}
1348
1349#endif
1350
reed@google.comc34f53d2012-04-30 15:10:13 +00001351///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352
1353SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1354 this->reset(rgn);
1355}
1356
1357bool SkRegion::Iterator::rewind() {
1358 if (fRgn) {
1359 this->reset(*fRgn);
1360 return true;
1361 }
1362 return false;
1363}
1364
1365void SkRegion::Iterator::reset(const SkRegion& rgn) {
1366 fRgn = &rgn;
1367 if (rgn.isEmpty()) {
1368 fDone = true;
1369 } else {
1370 fDone = false;
1371 if (rgn.isRect()) {
1372 fRect = rgn.fBounds;
halcanary96fcdcc2015-08-27 07:41:13 -07001373 fRuns = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 } else {
1375 fRuns = rgn.fRunHead->readonly_runs();
reed@google.com9c36a762012-05-02 18:07:33 +00001376 fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
1377 fRuns += 5;
1378 // Now fRuns points to the 2nd interval (or x-sentinel)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 }
1380 }
1381}
1382
1383void SkRegion::Iterator::next() {
1384 if (fDone) {
1385 return;
1386 }
1387
halcanary96fcdcc2015-08-27 07:41:13 -07001388 if (fRuns == nullptr) { // rect case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 fDone = true;
1390 return;
1391 }
1392
1393 const RunType* runs = fRuns;
1394
Cary Clark69436892018-07-28 10:14:27 -04001395 if (runs[0] < SkRegion_kRunTypeSentinel) { // valid X value
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 fRect.fLeft = runs[0];
1397 fRect.fRight = runs[1];
1398 runs += 2;
1399 } else { // we're at the end of a line
1400 runs += 1;
Cary Clark69436892018-07-28 10:14:27 -04001401 if (runs[0] < SkRegion_kRunTypeSentinel) { // valid Y value
reed@google.com9c36a762012-05-02 18:07:33 +00001402 int intervals = runs[1];
1403 if (0 == intervals) { // empty line
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 fRect.fTop = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001405 runs += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 } else {
1407 fRect.fTop = fRect.fBottom;
1408 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001409
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 fRect.fBottom = runs[0];
reed@google.com9c36a762012-05-02 18:07:33 +00001411 assert_sentinel(runs[2], false);
1412 assert_sentinel(runs[3], false);
1413 fRect.fLeft = runs[2];
1414 fRect.fRight = runs[3];
1415 runs += 4;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 } else { // end of rgn
1417 fDone = true;
1418 }
1419 }
1420 fRuns = runs;
1421}
1422
1423SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1424 : fIter(rgn), fClip(clip), fDone(true) {
1425 const SkIRect& r = fIter.rect();
1426
1427 while (!fIter.done()) {
1428 if (r.fTop >= clip.fBottom) {
1429 break;
1430 }
1431 if (fRect.intersect(clip, r)) {
1432 fDone = false;
1433 break;
1434 }
1435 fIter.next();
1436 }
1437}
1438
1439void SkRegion::Cliperator::next() {
1440 if (fDone) {
1441 return;
1442 }
1443
1444 const SkIRect& r = fIter.rect();
1445
1446 fDone = true;
1447 fIter.next();
1448 while (!fIter.done()) {
1449 if (r.fTop >= fClip.fBottom) {
1450 break;
1451 }
1452 if (fRect.intersect(fClip, r)) {
1453 fDone = false;
1454 break;
1455 }
1456 fIter.next();
1457 }
1458}
1459
reed@google.come72766f2011-01-07 15:00:44 +00001460///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461
reed@google.come72766f2011-01-07 15:00:44 +00001462SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1463 int right) {
Cary Clark69436892018-07-28 10:14:27 -04001464 SkDEBUGCODE(SkRegionPriv::Validate(rgn));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465
1466 const SkIRect& r = rgn.getBounds();
1467
1468 fDone = true;
reed@google.come72766f2011-01-07 15:00:44 +00001469 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1470 right > r.fLeft && left < r.fRight) {
1471 if (rgn.isRect()) {
1472 if (left < r.fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 left = r.fLeft;
reed@google.come72766f2011-01-07 15:00:44 +00001474 }
1475 if (right > r.fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 right = r.fRight;
reed@google.come72766f2011-01-07 15:00:44 +00001477 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 fLeft = left;
1479 fRight = right;
halcanary96fcdcc2015-08-27 07:41:13 -07001480 fRuns = nullptr; // means we're a rect, not a rgn
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 fDone = false;
reed@google.come72766f2011-01-07 15:00:44 +00001482 } else {
reed@google.com9c36a762012-05-02 18:07:33 +00001483 const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
1484 runs += 2; // skip Bottom and IntervalCount
1485 for (;;) {
1486 // runs[0..1] is to the right of the span, so we're done
1487 if (runs[0] >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 break;
1489 }
reed@google.com9c36a762012-05-02 18:07:33 +00001490 // runs[0..1] is to the left of the span, so continue
1491 if (runs[1] <= left) {
1492 runs += 2;
1493 continue;
1494 }
1495 // runs[0..1] intersects the span
1496 fRuns = runs;
1497 fLeft = left;
1498 fRight = right;
1499 fDone = false;
1500 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 }
1502 }
1503 }
1504}
1505
reed@google.come72766f2011-01-07 15:00:44 +00001506bool SkRegion::Spanerator::next(int* left, int* right) {
1507 if (fDone) {
1508 return false;
1509 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510
halcanary96fcdcc2015-08-27 07:41:13 -07001511 if (fRuns == nullptr) { // we're a rect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 fDone = true; // ok, now we're done
reed@google.come72766f2011-01-07 15:00:44 +00001513 if (left) {
1514 *left = fLeft;
1515 }
1516 if (right) {
1517 *right = fRight;
1518 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 return true; // this interval is legal
1520 }
1521
1522 const SkRegion::RunType* runs = fRuns;
1523
reed@google.come72766f2011-01-07 15:00:44 +00001524 if (runs[0] >= fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525 fDone = true;
1526 return false;
1527 }
1528
1529 SkASSERT(runs[1] > fLeft);
1530
reed@google.come72766f2011-01-07 15:00:44 +00001531 if (left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 *left = SkMax32(fLeft, runs[0]);
reed@google.come72766f2011-01-07 15:00:44 +00001533 }
1534 if (right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535 *right = SkMin32(fRight, runs[1]);
reed@google.come72766f2011-01-07 15:00:44 +00001536 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 fRuns = runs + 2;
1538 return true;
1539}
1540
Mike Reedbb586b22018-02-27 15:44:36 -05001541///////////////////////////////////////////////////////////////////////////////////////////////////
1542
1543static void visit_pairs(int pairCount, int y, const int32_t pairs[],
1544 const std::function<void(const SkIRect&)>& visitor) {
1545 for (int i = 0; i < pairCount; ++i) {
1546 visitor({ pairs[0], y, pairs[1], y + 1 });
1547 pairs += 2;
1548 }
1549}
1550
1551void SkRegionPriv::VisitSpans(const SkRegion& rgn,
1552 const std::function<void(const SkIRect&)>& visitor) {
1553 if (rgn.isEmpty()) {
1554 return;
1555 }
1556 if (rgn.isRect()) {
1557 visitor(rgn.getBounds());
1558 } else {
1559 const int32_t* p = rgn.fRunHead->readonly_runs();
1560 int32_t top = *p++;
1561 int32_t bot = *p++;
1562 do {
1563 int pairCount = *p++;
1564 if (pairCount == 1) {
1565 visitor({ p[0], top, p[1], bot });
1566 p += 2;
1567 } else if (pairCount > 1) {
1568 // we have to loop repeated in Y, sending each interval in Y -> X order
1569 for (int y = top; y < bot; ++y) {
1570 visit_pairs(pairCount, y, p, visitor);
1571 }
1572 p += pairCount * 2;
1573 }
1574 assert_sentinel(*p, true);
1575 p += 1; // skip sentinel
1576
1577 // read next bottom or sentinel
1578 top = bot;
1579 bot = *p++;
1580 } while (!SkRegionValueIsSentinel(bot));
1581 }
1582}
1583