blob: eccca1813a6282cd64d3e94c6c8883428c88387a [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/corecg/SkRegion.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkRegionPriv.h"
19#include "SkTemplates.h"
20#include "SkThread.h"
21
djsollen@google.comcd9d69b2011-03-14 20:30:14 +000022#ifdef ANDROID
23#include <stdio.h>
24#endif
25
reed@android.com8a1c16f2008-12-17 15:59:43 +000026SkDEBUGCODE(int32_t gRgnAllocCounter;)
27
28/////////////////////////////////////////////////////////////////////////////////////////////////
29
30/* Pass in a scanline, beginning with the Left value of the pair (i.e. not the Y beginning)
31*/
32static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[])
33{
34 while (runs[0] != SkRegion::kRunTypeSentinel)
35 {
36 SkASSERT(runs[0] < runs[1]); // valid span
37 runs += 2;
38 }
39 return (SkRegion::RunType*)(runs + 1); // return past the X-sentinel
40}
41
reed@android.com8a1c16f2008-12-17 15:59:43 +000042// returns true if runs are just a rect
43bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds)
44{
45 assert_sentinel(runs[0], false); // top
46
47 if (count == kRectRegionRuns)
48 {
49 assert_sentinel(runs[1], false); // bottom
50 assert_sentinel(runs[2], false); // left
51 assert_sentinel(runs[3], false); // right
52 assert_sentinel(runs[4], true);
53 assert_sentinel(runs[5], true);
54
55 SkASSERT(runs[0] < runs[1]); // valid height
56 SkASSERT(runs[2] < runs[3]); // valid width
57
58 bounds->set(runs[2], runs[0], runs[3], runs[1]);
59 return true;
60 }
61
62 int left = SK_MaxS32;
63 int rite = SK_MinS32;
64 int bot;
65
66 bounds->fTop = *runs++;
67 do {
68 bot = *runs++;
69 if (*runs < SkRegion::kRunTypeSentinel)
70 {
71 if (left > *runs)
72 left = *runs;
73 runs = skip_scanline(runs);
74 if (rite < runs[-2])
75 rite = runs[-2];
76 }
77 else
78 runs += 1; // skip X-sentinel
79 } while (runs[0] < SkRegion::kRunTypeSentinel);
80 bounds->fLeft = left;
81 bounds->fRight = rite;
82 bounds->fBottom = bot;
83 return false;
84}
85
86//////////////////////////////////////////////////////////////////////////
87
88SkRegion::SkRegion()
89{
90 fBounds.set(0, 0, 0, 0);
91 fRunHead = SkRegion_gEmptyRunHeadPtr;
92}
93
94SkRegion::SkRegion(const SkRegion& src)
95{
96 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
97 this->setRegion(src);
98}
99
100SkRegion::SkRegion(const SkIRect& rect)
101{
102 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
103 this->setRect(rect);
104}
105
106SkRegion::~SkRegion()
107{
108 this->freeRuns();
109}
110
111void SkRegion::freeRuns()
112{
113 if (fRunHead->isComplex())
114 {
115 SkASSERT(fRunHead->fRefCnt >= 1);
116 if (sk_atomic_dec(&fRunHead->fRefCnt) == 1)
117 {
118 //SkASSERT(gRgnAllocCounter > 0);
119 //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
120 //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
121 sk_free(fRunHead);
122 }
123 }
124}
125
126void SkRegion::allocateRuns(int count)
127{
128 fRunHead = RunHead::Alloc(count);
129}
130
131SkRegion& SkRegion::operator=(const SkRegion& src)
132{
133 (void)this->setRegion(src);
134 return *this;
135}
136
137void SkRegion::swap(SkRegion& other)
138{
139 SkTSwap<SkIRect>(fBounds, other.fBounds);
140 SkTSwap<RunHead*>(fRunHead, other.fRunHead);
141}
142
143bool SkRegion::setEmpty()
144{
145 this->freeRuns();
146 fBounds.set(0, 0, 0, 0);
147 fRunHead = SkRegion_gEmptyRunHeadPtr;
148 return false;
149}
150
151bool SkRegion::setRect(int32_t left, int32_t top, int32_t right, int32_t bottom)
152{
153 if (left >= right || top >= bottom)
154 return this->setEmpty();
155
156 this->freeRuns();
157 fBounds.set(left, top, right, bottom);
158 fRunHead = SkRegion_gRectRunHeadPtr;
159 return true;
160}
161
162bool SkRegion::setRect(const SkIRect& r)
163{
164 return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
165}
166
167bool SkRegion::setRegion(const SkRegion& src)
168{
169 if (this != &src)
170 {
171 this->freeRuns();
172
173 fBounds = src.fBounds;
174 fRunHead = src.fRunHead;
175 if (fRunHead->isComplex())
176 sk_atomic_inc(&fRunHead->fRefCnt);
177 }
178 return fRunHead != SkRegion_gEmptyRunHeadPtr;
179}
180
181bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op)
182{
183 SkRegion tmp(rect);
184
185 return this->op(tmp, rgn, op);
186}
187
188bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op)
189{
190 SkRegion tmp(rect);
191
192 return this->op(rgn, tmp, op);
193}
194
195//////////////////////////////////////////////////////////////////////////////////////
196
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000197#ifdef ANDROID
198char* SkRegion::toString()
199{
200 Iterator iter(*this);
201 int count = 0;
202 while (!iter.done()) {
203 count++;
204 iter.next();
205 }
206 // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
207 const int max = (count*((11*4)+5))+11+1;
208 char* result = (char*)malloc(max);
209 if (result == NULL) {
210 return NULL;
211 }
212 count = sprintf(result, "SkRegion(");
213 iter.reset(*this);
214 while (!iter.done()) {
215 const SkIRect& r = iter.rect();
216 count += sprintf(result+count, "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
217 iter.next();
218 }
219 count += sprintf(result+count, ")");
220 return result;
221}
222#endif
223
224//////////////////////////////////////////////////////////////////////////////////////
225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226int SkRegion::count_runtype_values(int* itop, int* ibot) const
227{
228 if (this == NULL)
229 {
230 *itop = SK_MinS32;
231 *ibot = SK_MaxS32;
232 return 0;
233 }
234
235 int maxT;
236
237 if (this->isRect())
238 maxT = 2;
239 else
240 {
241 SkASSERT(this->isComplex());
242 // skip the top
243 const RunType* runs = fRunHead->readonly_runs() + 1;
244 maxT = 0;
245
246 do {
247 const RunType* next = skip_scanline(runs + 1);
248 SkASSERT(next > runs);
249 int T = (int)(next - runs - 1);
250 if (maxT < T)
251 maxT = T;
252 runs = next;
253 } while (runs[0] < SkRegion::kRunTypeSentinel);
254 }
255 *itop = fBounds.fTop;
256 *ibot = fBounds.fBottom;
257 return maxT;
258}
259
260bool SkRegion::setRuns(RunType runs[], int count)
261{
262 SkDEBUGCODE(this->validate();)
263 SkASSERT(count > 0);
264
265 if (count <= 2)
266 {
267 // SkDEBUGF(("setRuns: empty\n"));
268 assert_sentinel(runs[count-1], true);
269 return this->setEmpty();
270 }
271
272 // trim off any empty spans from the top and bottom
273 // weird I should need this, perhaps op() could be smarter...
274 if (count > kRectRegionRuns)
275 {
276 RunType* stop = runs + count;
277 assert_sentinel(runs[0], false); // top
278 assert_sentinel(runs[1], false); // bottom
279 if (runs[2] == SkRegion::kRunTypeSentinel) // should be first left...
280 {
281 runs += 2; // skip empty initial span
282 runs[0] = runs[-1]; // set new top to prev bottom
283 assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
284 assert_sentinel(runs[2], false); // left
285 assert_sentinel(runs[3], false); // right
286 }
287
288 // now check for a trailing empty span
289 assert_sentinel(stop[-1], true);
290 assert_sentinel(stop[-2], true);
291 assert_sentinel(stop[-3], false); // should be last right
292 if (stop[-4] == SkRegion::kRunTypeSentinel) // eek, stop[-3] was a bottom with no x-runs
293 {
294 stop[-3] = SkRegion::kRunTypeSentinel; // kill empty last span
295 stop -= 2;
296 assert_sentinel(stop[-1], true);
297 assert_sentinel(stop[-2], true);
298 assert_sentinel(stop[-3], false);
299 assert_sentinel(stop[-4], false);
300 assert_sentinel(stop[-5], false);
301 }
302 count = (int)(stop - runs);
303 }
304
305 SkASSERT(count >= kRectRegionRuns);
306
307 if (ComputeRunBounds(runs, count, &fBounds))
308 {
309 // SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom));
310 return this->setRect(fBounds);
311 }
312
313 // if we get here, we need to become a complex region
314
315 if (!fRunHead->isComplex() || fRunHead->fRunCount != count)
316 {
317#ifdef SK_DEBUGx
318 SkDebugf("setRuns: rgn [");
319 {
320 const RunType* r = runs;
321
322 SkDebugf(" top: %d\n", *r++);
323 while (*r < SkRegion::kRunTypeSentinel)
324 {
325 SkDebugf(" bottom: %d", *r++);
326 while (*r < SkRegion::kRunTypeSentinel)
327 {
328 SkDebugf(" [%d %d]", r[0], r[1]);
329 r += 2;
330 }
331 SkDebugf("\n");
332 }
333 }
334#endif
335 this->freeRuns();
336 this->allocateRuns(count);
337 }
338
339 // must call this before we can write directly into runs()
340 // in case we are sharing the buffer with another region (copy on write)
341 fRunHead = fRunHead->ensureWritable();
342 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
343
344 SkDEBUGCODE(this->validate();)
345
346 return true;
347}
348
349void SkRegion::BuildRectRuns(const SkIRect& bounds,
350 RunType runs[kRectRegionRuns])
351{
352 runs[0] = bounds.fTop;
353 runs[1] = bounds.fBottom;
354 runs[2] = bounds.fLeft;
355 runs[3] = bounds.fRight;
356 runs[4] = kRunTypeSentinel;
357 runs[5] = kRunTypeSentinel;
358}
359
360static SkRegion::RunType* find_scanline(const SkRegion::RunType runs[], int y)
361{
362 SkASSERT(y >= runs[0]); // if this fails, we didn't do a quick check on the boudns
363
364 runs += 1; // skip top-Y
365 for (;;)
366 {
367 if (runs[0] == SkRegion::kRunTypeSentinel)
368 break;
369 if (y < runs[0])
370 return (SkRegion::RunType*)&runs[1];
371 runs = skip_scanline(runs + 1); // skip the Y value before calling
372 }
373 return NULL;
374}
375
agl@chromium.org55747cc2009-12-18 22:50:15 +0000376bool SkRegion::contains(int32_t x, int32_t y) const
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377{
378 if (!fBounds.contains(x, y))
379 return false;
380
381 if (this->isRect())
382 return true;
383
384 SkASSERT(this->isComplex());
385 const RunType* runs = find_scanline(fRunHead->readonly_runs(), y);
386
387 if (runs)
388 { for (;;)
389 { if (x < runs[0])
390 break;
391 if (x < runs[1])
392 return true;
393 runs += 2;
394 }
395 }
396 return false;
397}
398
399bool SkRegion::contains(const SkIRect& r) const
400{
401 SkRegion tmp(r);
402
403 return this->contains(tmp);
404}
405
406bool SkRegion::contains(const SkRegion& rgn) const
407{
408 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds))
409 return false;
410
411 if (this->isRect())
412 return true;
413
414 SkRegion tmp;
415
416 tmp.op(*this, rgn, kUnion_Op);
417 return tmp == *this;
418}
419
420const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const
421{
422 SkASSERT(tmpStorage && count);
423 const RunType* runs = tmpStorage;
424
425 if (this->isEmpty())
426 {
427 tmpStorage[0] = kRunTypeSentinel;
428 *count = 1;
429 }
430 else if (this->isRect())
431 {
432 BuildRectRuns(fBounds, tmpStorage);
433 *count = kRectRegionRuns;
434 }
435 else
436 {
437 *count = fRunHead->fRunCount;
438 runs = fRunHead->readonly_runs();
439 }
440 return runs;
441}
442
443/////////////////////////////////////////////////////////////////////////////////////
444
445bool SkRegion::intersects(const SkIRect& r) const {
446 if (this->isEmpty() || r.isEmpty()) {
447 return false;
448 }
449
450 if (!SkIRect::Intersects(fBounds, r)) {
451 return false;
452 }
453
454 if (this->isRect()) {
455 return true;
456 }
457
458 // we are complex
459 SkRegion tmp;
460 return tmp.op(*this, r, kIntersect_Op);
461}
462
463bool SkRegion::intersects(const SkRegion& rgn) const {
464 if (this->isEmpty() || rgn.isEmpty()) {
465 return false;
466 }
467
468 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
469 return false;
470 }
471
472 if (this->isRect() && rgn.isRect()) {
473 return true;
474 }
475
476 // one or both of us is complex
477 // TODO: write a faster version that aborts as soon as we write the first
478 // non-empty span, to avoid build the entire result
479 SkRegion tmp;
480 return tmp.op(*this, rgn, kIntersect_Op);
481}
482
483/////////////////////////////////////////////////////////////////////////////////////
484
reed@google.com97fa34c2011-03-18 14:44:42 +0000485bool operator==(const SkRegion& a, const SkRegion& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 SkDEBUGCODE(a.validate();)
487 SkDEBUGCODE(b.validate();)
488
reed@google.com97fa34c2011-03-18 14:44:42 +0000489 if (&a == &b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000491 }
492 if (a.fBounds != b.fBounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000494 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
496 const SkRegion::RunHead* ah = a.fRunHead;
497 const SkRegion::RunHead* bh = b.fRunHead;
498
499 // this catches empties and rects being equal
reed@google.com97fa34c2011-03-18 14:44:42 +0000500 if (ah == bh) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 return true;
reed@google.com97fa34c2011-03-18 14:44:42 +0000502 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 // now we insist that both are complex (but different ptrs)
reed@google.com97fa34c2011-03-18 14:44:42 +0000504 if (!ah->isComplex() || !bh->isComplex()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 return false;
reed@google.com97fa34c2011-03-18 14:44:42 +0000506 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 return ah->fRunCount == bh->fRunCount &&
508 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
509 ah->fRunCount * sizeof(SkRegion::RunType));
510}
511
reed@google.com97fa34c2011-03-18 14:44:42 +0000512void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 SkDEBUGCODE(this->validate();)
514
reed@google.com97fa34c2011-03-18 14:44:42 +0000515 if (NULL == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 return;
reed@google.com97fa34c2011-03-18 14:44:42 +0000517 }
518 if (this->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 dst->setEmpty();
reed@google.com97fa34c2011-03-18 14:44:42 +0000520 } else if (this->isRect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
522 fBounds.fRight + dx, fBounds.fBottom + dy);
reed@google.com97fa34c2011-03-18 14:44:42 +0000523 } else {
524 if (this == dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 dst->fRunHead = dst->fRunHead->ensureWritable();
reed@google.com97fa34c2011-03-18 14:44:42 +0000526 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 SkRegion tmp;
528 tmp.allocateRuns(fRunHead->fRunCount);
529 tmp.fBounds = fBounds;
530 dst->swap(tmp);
531 }
532
533 dst->fBounds.offset(dx, dy);
534
535 const RunType* sruns = fRunHead->readonly_runs();
536 RunType* druns = dst->fRunHead->writable_runs();
537
538 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
reed@google.com97fa34c2011-03-18 14:44:42 +0000539 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 int bottom = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000541 if (bottom == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000543 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
reed@google.com97fa34c2011-03-18 14:44:42 +0000545 for (;;) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 int x = *sruns++;
reed@google.com97fa34c2011-03-18 14:44:42 +0000547 if (x == kRunTypeSentinel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 break;
reed@google.com97fa34c2011-03-18 14:44:42 +0000549 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 *druns++ = (SkRegion::RunType)(x + dx);
551 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
552 }
553 *druns++ = kRunTypeSentinel; // x sentinel
554 }
555 *druns++ = kRunTypeSentinel; // y sentinel
556
557 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
558 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
559 }
560
561 SkDEBUGCODE(this->validate();)
562}
563
reed@android.com097a3512010-07-13 18:35:14 +0000564///////////////////////////////////////////////////////////////////////////////
565
reed@android.com097a3512010-07-13 18:35:14 +0000566bool SkRegion::setRects(const SkIRect rects[], int count) {
567 if (0 == count) {
reed@android.comcb342352010-07-22 18:27:53 +0000568 this->setEmpty();
569 } else {
570 this->setRect(rects[0]);
571 for (int i = 1; i < count; i++) {
572 this->op(rects[i], kUnion_Op);
reed@android.com097a3512010-07-13 18:35:14 +0000573 }
574 }
reed@android.comcb342352010-07-22 18:27:53 +0000575 return !this->isEmpty();
reed@android.com097a3512010-07-13 18:35:14 +0000576}
577
578///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579
580#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized
581#pragma warning ( push )
582#pragma warning ( disable : 4701 )
583#endif
584
585#ifdef SK_DEBUG
586static void assert_valid_pair(int left, int rite)
587{
588 SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
589}
590#else
591 #define assert_valid_pair(left, rite)
592#endif
593
594struct spanRec {
595 const SkRegion::RunType* fA_runs;
596 const SkRegion::RunType* fB_runs;
597 int fA_left, fA_rite, fB_left, fB_rite;
598 int fLeft, fRite, fInside;
599
600 void init(const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
601 {
602 fA_left = *a_runs++;
603 fA_rite = *a_runs++;
604 fB_left = *b_runs++;
605 fB_rite = *b_runs++;
606
607 fA_runs = a_runs;
608 fB_runs = b_runs;
609 }
610
611 bool done() const
612 {
613 SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
614 SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
615 return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel;
616 }
617
618 void next()
619 {
620 assert_valid_pair(fA_left, fA_rite);
621 assert_valid_pair(fB_left, fB_rite);
622
623 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
624 bool a_flush = false;
625 bool b_flush = false;
626
627 int a_left = fA_left;
628 int a_rite = fA_rite;
629 int b_left = fB_left;
630 int b_rite = fB_rite;
631
632 if (a_left < b_left)
633 {
634 inside = 1;
635 left = a_left;
636 if (a_rite <= b_left) // [...] <...>
637 {
638 rite = a_rite;
639 a_flush = true;
640 }
641 else // [...<..]...> or [...<...>...]
642 rite = a_left = b_left;
643 }
644 else if (b_left < a_left)
645 {
646 inside = 2;
647 left = b_left;
648 if (b_rite <= a_left) // [...] <...>
649 {
650 rite = b_rite;
651 b_flush = true;
652 }
653 else // [...<..]...> or [...<...>...]
654 rite = b_left = a_left;
655 }
656 else // a_left == b_left
657 {
658 inside = 3;
659 left = a_left; // or b_left
660 if (a_rite <= b_rite)
661 {
662 rite = b_left = a_rite;
663 a_flush = true;
664 }
665 if (b_rite <= a_rite)
666 {
667 rite = a_left = b_rite;
668 b_flush = true;
669 }
670 }
671
672 if (a_flush)
673 {
674 a_left = *fA_runs++;
675 a_rite = *fA_runs++;
676 }
677 if (b_flush)
678 {
679 b_left = *fB_runs++;
680 b_rite = *fB_runs++;
681 }
682
683 SkASSERT(left <= rite);
684
685 // now update our state
686 fA_left = a_left;
687 fA_rite = a_rite;
688 fB_left = b_left;
689 fB_rite = b_rite;
690
691 fLeft = left;
692 fRite = rite;
693 fInside = inside;
694 }
695};
696
697static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
698 const SkRegion::RunType b_runs[],
699 SkRegion::RunType dst[],
700 int min, int max)
701{
702 spanRec rec;
703 bool firstInterval = true;
704
705 rec.init(a_runs, b_runs);
706
707 while (!rec.done())
708 {
709 rec.next();
710
711 int left = rec.fLeft;
712 int rite = rec.fRite;
713
714 // add left,rite to our dst buffer (checking for coincidence
715 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
716 left < rite) // skip if equal
717 {
718 if (firstInterval || dst[-1] < left)
719 {
720 *dst++ = (SkRegion::RunType)(left);
721 *dst++ = (SkRegion::RunType)(rite);
722 firstInterval = false;
723 }
724 else // update the right edge
725 dst[-1] = (SkRegion::RunType)(rite);
726 }
727 }
728
729 *dst++ = SkRegion::kRunTypeSentinel;
730 return dst;
731}
732
733#if defined _WIN32 && _MSC_VER >= 1300
734#pragma warning ( pop )
735#endif
736
737static const struct {
738 uint8_t fMin;
739 uint8_t fMax;
740} gOpMinMax[] = {
741 { 1, 1 }, // Difference
742 { 3, 3 }, // Intersection
743 { 1, 3 }, // Union
744 { 1, 2 } // XOR
745};
746
747class RgnOper {
748public:
749 RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op)
750 {
751 // need to ensure that the op enum lines up with our minmax array
752 SkASSERT(SkRegion::kDifference_Op == 0);
753 SkASSERT(SkRegion::kIntersect_Op == 1);
754 SkASSERT(SkRegion::kUnion_Op == 2);
755 SkASSERT(SkRegion::kXOR_Op == 3);
756 SkASSERT((unsigned)op <= 3);
757
758 fStartDst = dst;
759 fPrevDst = dst + 1;
760 fPrevLen = 0; // will never match a length from operate_on_span
761 fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
762
763 fMin = gOpMinMax[op].fMin;
764 fMax = gOpMinMax[op].fMax;
765 }
766
767 void addSpan(int bottom, const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
768 {
769 SkRegion::RunType* start = fPrevDst + fPrevLen + 1; // skip X values and slot for the next Y
770 SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
771 size_t len = stop - start;
772
773 if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType))) // update Y value
774 fPrevDst[-1] = (SkRegion::RunType)(bottom);
775 else // accept the new span
776 {
777 if (len == 1 && fPrevLen == 0) {
778 fTop = (SkRegion::RunType)(bottom); // just update our bottom
779 } else {
780 start[-1] = (SkRegion::RunType)(bottom);
781 fPrevDst = start;
782 fPrevLen = len;
783 }
784 }
785 }
786
787 int flush()
788 {
789 fStartDst[0] = fTop;
790 fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
791 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
792 }
793
794 uint8_t fMin, fMax;
795
796private:
797 SkRegion::RunType* fStartDst;
798 SkRegion::RunType* fPrevDst;
799 size_t fPrevLen;
800 SkRegion::RunType fTop;
801};
802
803static int operate( const SkRegion::RunType a_runs[],
804 const SkRegion::RunType b_runs[],
805 SkRegion::RunType dst[],
806 SkRegion::Op op)
807{
reed@android.comfbb02e72010-04-13 14:52:52 +0000808 const SkRegion::RunType gSentinel[] = {
809 SkRegion::kRunTypeSentinel,
810 // just need a 2nd value, since spanRec.init() reads 2 values, even
811 // though if the first value is the sentinel, it ignores the 2nd value.
812 // w/o the 2nd value here, we might read uninitialized memory.
813 0,
814 };
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815
816 int a_top = *a_runs++;
817 int a_bot = *a_runs++;
818 int b_top = *b_runs++;
819 int b_bot = *b_runs++;
820
821 assert_sentinel(a_top, false);
822 assert_sentinel(a_bot, false);
823 assert_sentinel(b_top, false);
824 assert_sentinel(b_bot, false);
825
826 RgnOper oper(SkMin32(a_top, b_top), dst, op);
827
828 bool firstInterval = true;
829 int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
830
831 while (a_bot < SkRegion::kRunTypeSentinel || b_bot < SkRegion::kRunTypeSentinel)
832 {
833 int top, bot SK_INIT_TO_AVOID_WARNING;
reed@android.comfbb02e72010-04-13 14:52:52 +0000834 const SkRegion::RunType* run0 = gSentinel;
835 const SkRegion::RunType* run1 = gSentinel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 bool a_flush = false;
837 bool b_flush = false;
838 int inside;
839
840 if (a_top < b_top)
841 {
842 inside = 1;
843 top = a_top;
844 run0 = a_runs;
845 if (a_bot <= b_top) // [...] <...>
846 {
847 bot = a_bot;
848 a_flush = true;
849 }
850 else // [...<..]...> or [...<...>...]
851 bot = a_top = b_top;
852 }
853 else if (b_top < a_top)
854 {
855 inside = 2;
856 top = b_top;
857 run1 = b_runs;
858 if (b_bot <= a_top) // [...] <...>
859 {
860 bot = b_bot;
861 b_flush = true;
862 }
863 else // [...<..]...> or [...<...>...]
864 bot = b_top = a_top;
865 }
866 else // a_top == b_top
867 {
868 inside = 3;
869 top = a_top; // or b_top
870 run0 = a_runs;
871 run1 = b_runs;
872 if (a_bot <= b_bot)
873 {
874 bot = b_top = a_bot;
875 a_flush = true;
876 }
877 if (b_bot <= a_bot)
878 {
879 bot = a_top = b_bot;
880 b_flush = true;
881 }
882 }
883
884 if (top > prevBot)
reed@android.comfbb02e72010-04-13 14:52:52 +0000885 oper.addSpan(top, gSentinel, gSentinel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886
887// if ((unsigned)(inside - oper.fMin) <= (unsigned)(oper.fMax - oper.fMin))
888 {
889 oper.addSpan(bot, run0, run1);
890 firstInterval = false;
891 }
892
893 if (a_flush)
894 {
895 a_runs = skip_scanline(a_runs);
896 a_top = a_bot;
897 a_bot = *a_runs++;
898 if (a_bot == SkRegion::kRunTypeSentinel)
899 a_top = a_bot;
900 }
901 if (b_flush)
902 {
903 b_runs = skip_scanline(b_runs);
904 b_top = b_bot;
905 b_bot = *b_runs++;
906 if (b_bot == SkRegion::kRunTypeSentinel)
907 b_top = b_bot;
908 }
909
910 prevBot = bot;
911 }
912 return oper.flush();
913}
914
915///////////////////////////////////////////////////////////////////////////////
916
917/* Given count RunTypes in a complex region, return the worst case number of
918 logical intervals that represents (i.e. number of rects that would be
919 returned from the iterator).
920
921 We could just return count/2, since there must be at least 2 values per
922 interval, but we can first trim off the const overhead of the initial TOP
923 value, plus the final BOTTOM + 2 sentinels.
924 */
925static int count_to_intervals(int count) {
926 SkASSERT(count >= 6); // a single rect is 6 values
927 return (count - 4) >> 1;
928}
929
930/* Given a number of intervals, what is the worst case representation of that
931 many intervals?
932
933 Worst case (from a storage perspective), is a vertical stack of single
934 intervals: TOP + N * (BOTTOM LEFT RIGHT SENTINEL) + SENTINEL
935 */
936static int intervals_to_count(int intervals) {
937 return 1 + intervals * 4 + 1;
938}
939
940/* Given the counts of RunTypes in two regions, return the worst-case number
941 of RunTypes need to store the result after a region-op.
942 */
943static int compute_worst_case_count(int a_count, int b_count) {
944 int a_intervals = count_to_intervals(a_count);
945 int b_intervals = count_to_intervals(b_count);
946 // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
947 int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
948 // convert back to number of RunType values
949 return intervals_to_count(intervals);
950}
951
952bool SkRegion::op(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op)
953{
954 SkDEBUGCODE(this->validate();)
955
956 SkASSERT((unsigned)op < kOpCount);
957
958 if (kReplace_Op == op)
959 return this->set(rgnbOrig);
960
961 // swith to using pointers, so we can swap them as needed
962 const SkRegion* rgna = &rgnaOrig;
963 const SkRegion* rgnb = &rgnbOrig;
964 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
965
966 // collaps difference and reverse-difference into just difference
967 if (kReverseDifference_Op == op)
968 {
969 SkTSwap<const SkRegion*>(rgna, rgnb);
970 op = kDifference_Op;
971 }
972
973 SkIRect bounds;
974 bool a_empty = rgna->isEmpty();
975 bool b_empty = rgnb->isEmpty();
976 bool a_rect = rgna->isRect();
977 bool b_rect = rgnb->isRect();
978
979 switch (op) {
980 case kDifference_Op:
981 if (a_empty)
982 return this->setEmpty();
983 if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds))
984 return this->setRegion(*rgna);
985 break;
986
987 case kIntersect_Op:
988 if ((a_empty | b_empty)
989 || !bounds.intersect(rgna->fBounds, rgnb->fBounds))
990 return this->setEmpty();
991 if (a_rect & b_rect)
992 return this->setRect(bounds);
993 break;
994
995 case kUnion_Op:
996 if (a_empty)
997 return this->setRegion(*rgnb);
998 if (b_empty)
999 return this->setRegion(*rgna);
1000 if (a_rect && rgna->fBounds.contains(rgnb->fBounds))
1001 return this->setRegion(*rgna);
1002 if (b_rect && rgnb->fBounds.contains(rgna->fBounds))
1003 return this->setRegion(*rgnb);
1004 break;
1005
1006 case kXOR_Op:
1007 if (a_empty)
1008 return this->setRegion(*rgnb);
1009 if (b_empty)
1010 return this->setRegion(*rgna);
1011 break;
1012 default:
1013 SkASSERT(!"unknown region op");
1014 return !this->isEmpty();
1015 }
1016
1017 RunType tmpA[kRectRegionRuns];
1018 RunType tmpB[kRectRegionRuns];
1019
1020 int a_count, b_count;
1021 const RunType* a_runs = rgna->getRuns(tmpA, &a_count);
1022 const RunType* b_runs = rgnb->getRuns(tmpB, &b_count);
1023
1024 int dstCount = compute_worst_case_count(a_count, b_count);
1025 SkAutoSTMalloc<32, RunType> array(dstCount);
1026
1027 int count = operate(a_runs, b_runs, array.get(), op);
1028 SkASSERT(count <= dstCount);
1029 return this->setRuns(array.get(), count);
1030}
1031
1032//////////////////////////////////////////////////////////////////////////////////////////////////////////
1033
1034#include "SkBuffer.h"
1035
1036uint32_t SkRegion::flatten(void* storage) const {
1037 if (NULL == storage) {
1038 uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
1039 if (!this->isEmpty()) {
1040 size += sizeof(fBounds);
1041 if (this->isComplex()) {
1042 size += fRunHead->fRunCount * sizeof(RunType);
1043 }
1044 }
1045 return size;
1046 }
1047
1048 SkWBuffer buffer(storage);
1049
1050 if (this->isEmpty()) {
1051 buffer.write32(-1);
1052 } else {
1053 bool isRect = this->isRect();
1054
1055 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1056 buffer.write(&fBounds, sizeof(fBounds));
1057
1058 if (!isRect) {
1059 buffer.write(fRunHead->readonly_runs(),
1060 fRunHead->fRunCount * sizeof(RunType));
1061 }
1062 }
1063 return buffer.pos();
1064}
1065
1066uint32_t SkRegion::unflatten(const void* storage) {
1067 SkRBuffer buffer(storage);
1068 SkRegion tmp;
1069 int32_t count;
1070
1071 count = buffer.readS32();
1072 if (count >= 0) {
1073 buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
1074 if (count == 0) {
1075 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1076 } else {
1077 tmp.allocateRuns(count);
1078 buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
1079 }
1080 }
1081 this->swap(tmp);
1082 return buffer.pos();
1083}
1084
1085//////////////////////////////////////////////////////////////////////////////////////////////////////////
1086
1087#ifdef SK_DEBUG
1088
1089static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkIRect& bounds)
1090{
1091 // *run is the bottom of the current span
1092 SkASSERT(*run > bounds.fTop);
1093 SkASSERT(*run <= bounds.fBottom);
1094 run += 1;
1095
1096 // check for empty span
1097 if (*run != SkRegion::kRunTypeSentinel)
1098 {
1099 int prevRite = bounds.fLeft - 1;
1100 do {
1101 int left = *run++;
1102 int rite = *run++;
1103 SkASSERT(left < rite);
1104 SkASSERT(left > prevRite);
1105 SkASSERT(rite <= bounds.fRight);
1106 prevRite = rite;
1107 } while (*run < SkRegion::kRunTypeSentinel);
1108 }
1109 return run + 1; // skip sentinel
1110}
1111
1112void SkRegion::validate() const
1113{
1114 if (this->isEmpty())
1115 {
1116 // check for explicit empty (the zero rect), so we can compare rects to know when
1117 // two regions are equal (i.e. emptyRectA == emptyRectB)
1118 // this is stricter than just asserting fBounds.isEmpty()
1119 SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
1120 }
1121 else
1122 {
1123 SkASSERT(!fBounds.isEmpty());
1124 if (!this->isRect())
1125 {
1126 SkASSERT(fRunHead->fRefCnt >= 1);
1127 SkASSERT(fRunHead->fRunCount >= kRectRegionRuns);
1128
1129 const RunType* run = fRunHead->readonly_runs();
1130 const RunType* stop = run + fRunHead->fRunCount;
1131
1132 // check that our bounds match our runs
1133 {
1134 SkIRect bounds;
1135 bool isARect = ComputeRunBounds(run, stop - run, &bounds);
1136 SkASSERT(!isARect);
1137 SkASSERT(bounds == fBounds);
1138 }
1139
1140 SkASSERT(*run == fBounds.fTop);
1141 run++;
1142 do {
1143 run = validate_line(run, fBounds);
1144 } while (*run < kRunTypeSentinel);
1145 SkASSERT(run + 1 == stop);
1146 }
1147 }
1148}
1149
1150void SkRegion::dump() const
1151{
1152 if (this->isEmpty())
1153 SkDebugf(" rgn: empty\n");
1154 else
1155 {
1156 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
1157 if (this->isComplex())
1158 {
1159 const RunType* runs = fRunHead->readonly_runs();
1160 for (int i = 0; i < fRunHead->fRunCount; i++)
1161 SkDebugf(" %d", runs[i]);
1162 }
1163 SkDebugf("\n");
1164 }
1165}
1166
1167#endif
1168
1169/////////////////////////////////////////////////////////////////////////////////////
1170
1171SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1172 this->reset(rgn);
1173}
1174
1175bool SkRegion::Iterator::rewind() {
1176 if (fRgn) {
1177 this->reset(*fRgn);
1178 return true;
1179 }
1180 return false;
1181}
1182
1183void SkRegion::Iterator::reset(const SkRegion& rgn) {
1184 fRgn = &rgn;
1185 if (rgn.isEmpty()) {
1186 fDone = true;
1187 } else {
1188 fDone = false;
1189 if (rgn.isRect()) {
1190 fRect = rgn.fBounds;
1191 fRuns = NULL;
1192 } else {
1193 fRuns = rgn.fRunHead->readonly_runs();
1194 fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]);
1195 fRuns += 4;
1196 }
1197 }
1198}
1199
1200void SkRegion::Iterator::next() {
1201 if (fDone) {
1202 return;
1203 }
1204
1205 if (fRuns == NULL) { // rect case
1206 fDone = true;
1207 return;
1208 }
1209
1210 const RunType* runs = fRuns;
1211
1212 if (runs[0] < kRunTypeSentinel) { // valid X value
1213 fRect.fLeft = runs[0];
1214 fRect.fRight = runs[1];
1215 runs += 2;
1216 } else { // we're at the end of a line
1217 runs += 1;
1218 if (runs[0] < kRunTypeSentinel) { // valid Y value
1219 if (runs[1] == kRunTypeSentinel) { // empty line
1220 fRect.fTop = runs[0];
1221 runs += 2;
1222 } else {
1223 fRect.fTop = fRect.fBottom;
1224 }
1225
1226 fRect.fBottom = runs[0];
1227 assert_sentinel(runs[1], false);
1228 fRect.fLeft = runs[1];
1229 fRect.fRight = runs[2];
1230 runs += 3;
1231 } else { // end of rgn
1232 fDone = true;
1233 }
1234 }
1235 fRuns = runs;
1236}
1237
1238SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1239 : fIter(rgn), fClip(clip), fDone(true) {
1240 const SkIRect& r = fIter.rect();
1241
1242 while (!fIter.done()) {
1243 if (r.fTop >= clip.fBottom) {
1244 break;
1245 }
1246 if (fRect.intersect(clip, r)) {
1247 fDone = false;
1248 break;
1249 }
1250 fIter.next();
1251 }
1252}
1253
1254void SkRegion::Cliperator::next() {
1255 if (fDone) {
1256 return;
1257 }
1258
1259 const SkIRect& r = fIter.rect();
1260
1261 fDone = true;
1262 fIter.next();
1263 while (!fIter.done()) {
1264 if (r.fTop >= fClip.fBottom) {
1265 break;
1266 }
1267 if (fRect.intersect(fClip, r)) {
1268 fDone = false;
1269 break;
1270 }
1271 fIter.next();
1272 }
1273}
1274
reed@google.come72766f2011-01-07 15:00:44 +00001275///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
reed@google.come72766f2011-01-07 15:00:44 +00001277static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y) {
1278 int top = *runs++;
1279 if (top <= y) {
1280 for (;;) {
1281 int bot = *runs++;
1282 if (bot > y) {
1283 if (bot == SkRegion::kRunTypeSentinel ||
1284 *runs == SkRegion::kRunTypeSentinel) {
1285 break;
1286 }
1287 return (SkRegion::RunType*)runs;
1288 }
1289 runs = skip_scanline(runs);
1290 }
1291 }
1292 return NULL;
1293}
1294
1295SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1296 int right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 SkDEBUGCODE(rgn.validate();)
1298
1299 const SkIRect& r = rgn.getBounds();
1300
1301 fDone = true;
reed@google.come72766f2011-01-07 15:00:44 +00001302 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1303 right > r.fLeft && left < r.fRight) {
1304 if (rgn.isRect()) {
1305 if (left < r.fLeft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 left = r.fLeft;
reed@google.come72766f2011-01-07 15:00:44 +00001307 }
1308 if (right > r.fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 right = r.fRight;
reed@google.come72766f2011-01-07 15:00:44 +00001310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 fLeft = left;
1312 fRight = right;
1313 fRuns = NULL; // means we're a rect, not a rgn
1314 fDone = false;
reed@google.come72766f2011-01-07 15:00:44 +00001315 } else {
1316 const SkRegion::RunType* runs = find_y(
1317 rgn.fRunHead->readonly_runs(), y);
1318 if (runs) {
1319 for (;;) {
1320 // runs[0..1] is to the right of the span, so we're done
1321 if (runs[0] >= right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 break;
reed@google.come72766f2011-01-07 15:00:44 +00001323 }
1324 // runs[0..1] is to the left of the span, so continue
1325 if (runs[1] <= left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 runs += 2;
1327 continue;
1328 }
1329 // runs[0..1] intersects the span
1330 fRuns = runs;
1331 fLeft = left;
1332 fRight = right;
1333 fDone = false;
1334 break;
1335 }
1336 }
1337 }
1338 }
1339}
1340
reed@google.come72766f2011-01-07 15:00:44 +00001341bool SkRegion::Spanerator::next(int* left, int* right) {
1342 if (fDone) {
1343 return false;
1344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345
reed@google.come72766f2011-01-07 15:00:44 +00001346 if (fRuns == NULL) { // we're a rect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 fDone = true; // ok, now we're done
reed@google.come72766f2011-01-07 15:00:44 +00001348 if (left) {
1349 *left = fLeft;
1350 }
1351 if (right) {
1352 *right = fRight;
1353 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 return true; // this interval is legal
1355 }
1356
1357 const SkRegion::RunType* runs = fRuns;
1358
reed@google.come72766f2011-01-07 15:00:44 +00001359 if (runs[0] >= fRight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 fDone = true;
1361 return false;
1362 }
1363
1364 SkASSERT(runs[1] > fLeft);
1365
reed@google.come72766f2011-01-07 15:00:44 +00001366 if (left) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 *left = SkMax32(fLeft, runs[0]);
reed@google.come72766f2011-01-07 15:00:44 +00001368 }
1369 if (right) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 *right = SkMin32(fRight, runs[1]);
reed@google.come72766f2011-01-07 15:00:44 +00001371 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 fRuns = runs + 2;
1373 return true;
1374}
1375
1376///////////////////////////////////////////////////////////////////////////////
1377
1378#ifdef SK_DEBUG
1379
1380bool SkRegion::debugSetRuns(const RunType runs[], int count) {
1381 // we need to make a copy, since the real method may modify the array, and
1382 // so it cannot be const.
1383
1384 SkAutoTArray<RunType> storage(count);
1385 memcpy(storage.get(), runs, count * sizeof(RunType));
1386 return this->setRuns(storage.get(), count);
1387}
1388
1389#endif