SkRegion.cpp revision 1026ccf1d2de57ae6e7d2f30ea92c245942121d3
1/*
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
8
9#include "SkAtomics.h"
10#include "SkRegionPriv.h"
11#include "SkTemplates.h"
12#include "SkUtils.h"
13
14/* Region Layout
15 *
16 *  TOP
17 *
18 *  [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
19 *  ...
20 *
21 *  Y-Sentinel
22 */
23
24SkDEBUGCODE(int32_t gRgnAllocCounter;)
25
26/////////////////////////////////////////////////////////////////////////////////////////////////
27
28/*  Pass in the beginning with the intervals.
29 *  We back up 1 to read the interval-count.
30 *  Return the beginning of the next scanline (i.e. the next Y-value)
31 */
32static SkRegion::RunType* skip_intervals(const SkRegion::RunType runs[]) {
33    int intervals = runs[-1];
34#ifdef SK_DEBUG
35    if (intervals > 0) {
36        SkASSERT(runs[0] < runs[1]);
37        SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
38    } else {
39        SkASSERT(0 == intervals);
40        SkASSERT(SkRegion::kRunTypeSentinel == runs[0]);
41    }
42#endif
43    runs += intervals * 2 + 1;
44    return const_cast<SkRegion::RunType*>(runs);
45}
46
47bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
48                            SkIRect* bounds) {
49    assert_sentinel(runs[0], false);    // top
50    SkASSERT(count >= kRectRegionRuns);
51
52    if (count == kRectRegionRuns) {
53        assert_sentinel(runs[1], false);    // bottom
54        SkASSERT(1 == runs[2]);
55        assert_sentinel(runs[3], false);    // left
56        assert_sentinel(runs[4], false);    // right
57        assert_sentinel(runs[5], true);
58        assert_sentinel(runs[6], true);
59
60        SkASSERT(runs[0] < runs[1]);    // valid height
61        SkASSERT(runs[3] < runs[4]);    // valid width
62
63        bounds->set(runs[3], runs[0], runs[4], runs[1]);
64        return true;
65    }
66    return false;
67}
68
69//////////////////////////////////////////////////////////////////////////
70
71SkRegion::SkRegion() {
72    fBounds.set(0, 0, 0, 0);
73    fRunHead = SkRegion_gEmptyRunHeadPtr;
74}
75
76SkRegion::SkRegion(const SkRegion& src) {
77    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
78    this->setRegion(src);
79}
80
81SkRegion::SkRegion(const SkIRect& rect) {
82    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
83    this->setRect(rect);
84}
85
86SkRegion::~SkRegion() {
87    this->freeRuns();
88}
89
90void SkRegion::freeRuns() {
91    if (this->isComplex()) {
92        SkASSERT(fRunHead->fRefCnt >= 1);
93        if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
94            //SkASSERT(gRgnAllocCounter > 0);
95            //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
96            //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
97            sk_free(fRunHead);
98        }
99    }
100}
101
102void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
103    fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
104}
105
106void SkRegion::allocateRuns(int count) {
107    fRunHead = RunHead::Alloc(count);
108}
109
110void SkRegion::allocateRuns(const RunHead& head) {
111    fRunHead = RunHead::Alloc(head.fRunCount,
112                              head.getYSpanCount(),
113                              head.getIntervalCount());
114}
115
116SkRegion& SkRegion::operator=(const SkRegion& src) {
117    (void)this->setRegion(src);
118    return *this;
119}
120
121void SkRegion::swap(SkRegion& other) {
122    SkTSwap<SkIRect>(fBounds, other.fBounds);
123    SkTSwap<RunHead*>(fRunHead, other.fRunHead);
124}
125
126int SkRegion::computeRegionComplexity() const {
127  if (this->isEmpty()) {
128    return 0;
129  } else if (this->isRect()) {
130    return 1;
131  }
132  return fRunHead->getIntervalCount();
133}
134
135bool SkRegion::setEmpty() {
136    this->freeRuns();
137    fBounds.set(0, 0, 0, 0);
138    fRunHead = SkRegion_gEmptyRunHeadPtr;
139    return false;
140}
141
142bool SkRegion::setRect(int32_t left, int32_t top,
143                       int32_t right, int32_t bottom) {
144    if (left >= right || top >= bottom) {
145        return this->setEmpty();
146    }
147    this->freeRuns();
148    fBounds.set(left, top, right, bottom);
149    fRunHead = SkRegion_gRectRunHeadPtr;
150    return true;
151}
152
153bool SkRegion::setRect(const SkIRect& r) {
154    return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
155}
156
157bool SkRegion::setRegion(const SkRegion& src) {
158    if (this != &src) {
159        this->freeRuns();
160
161        fBounds = src.fBounds;
162        fRunHead = src.fRunHead;
163        if (this->isComplex()) {
164            sk_atomic_inc(&fRunHead->fRefCnt);
165        }
166    }
167    return fRunHead != SkRegion_gEmptyRunHeadPtr;
168}
169
170bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
171    SkRegion tmp(rect);
172
173    return this->op(tmp, rgn, op);
174}
175
176bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
177    SkRegion tmp(rect);
178
179    return this->op(rgn, tmp, op);
180}
181
182///////////////////////////////////////////////////////////////////////////////
183
184#ifdef SK_BUILD_FOR_ANDROID
185#include <stdio.h>
186char* SkRegion::toString() {
187    Iterator iter(*this);
188    int count = 0;
189    while (!iter.done()) {
190        count++;
191        iter.next();
192    }
193    // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
194    const int max = (count*((11*4)+5))+11+1;
195    char* result = (char*)sk_malloc_throw(max);
196    if (result == nullptr) {
197        return nullptr;
198    }
199    count = snprintf(result, max, "SkRegion(");
200    iter.reset(*this);
201    while (!iter.done()) {
202        const SkIRect& r = iter.rect();
203        count += snprintf(result+count, max - count,
204                "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
205        iter.next();
206    }
207    count += snprintf(result+count, max - count, ")");
208    return result;
209}
210#endif
211
212///////////////////////////////////////////////////////////////////////////////
213
214int SkRegion::count_runtype_values(int* itop, int* ibot) const {
215    int maxT;
216
217    if (this->isRect()) {
218        maxT = 2;
219    } else {
220        SkASSERT(this->isComplex());
221        maxT = fRunHead->getIntervalCount() * 2;
222    }
223    *itop = fBounds.fTop;
224    *ibot = fBounds.fBottom;
225    return maxT;
226}
227
228static bool isRunCountEmpty(int count) {
229    return count <= 2;
230}
231
232bool SkRegion::setRuns(RunType runs[], int count) {
233    SkDEBUGCODE(this->validate();)
234    SkASSERT(count > 0);
235
236    if (isRunCountEmpty(count)) {
237    //  SkDEBUGF(("setRuns: empty\n"));
238        assert_sentinel(runs[count-1], true);
239        return this->setEmpty();
240    }
241
242    // trim off any empty spans from the top and bottom
243    // weird I should need this, perhaps op() could be smarter...
244    if (count > kRectRegionRuns) {
245        RunType* stop = runs + count;
246        assert_sentinel(runs[0], false);    // top
247        assert_sentinel(runs[1], false);    // bottom
248        // runs[2] is uncomputed intervalCount
249
250        if (runs[3] == SkRegion::kRunTypeSentinel) {  // should be first left...
251            runs += 3;  // skip empty initial span
252            runs[0] = runs[-2]; // set new top to prev bottom
253            assert_sentinel(runs[1], false);    // bot: a sentinal would mean two in a row
254            assert_sentinel(runs[2], false);    // intervalcount
255            assert_sentinel(runs[3], false);    // left
256            assert_sentinel(runs[4], false);    // right
257        }
258
259        assert_sentinel(stop[-1], true);
260        assert_sentinel(stop[-2], true);
261
262        // now check for a trailing empty span
263        if (stop[-5] == SkRegion::kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
264            stop[-4] = SkRegion::kRunTypeSentinel;    // kill empty last span
265            stop -= 3;
266            assert_sentinel(stop[-1], true);    // last y-sentinel
267            assert_sentinel(stop[-2], true);    // last x-sentinel
268            assert_sentinel(stop[-3], false);   // last right
269            assert_sentinel(stop[-4], false);   // last left
270            assert_sentinel(stop[-5], false);   // last interval-count
271            assert_sentinel(stop[-6], false);   // last bottom
272        }
273        count = (int)(stop - runs);
274    }
275
276    SkASSERT(count >= kRectRegionRuns);
277
278    if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
279        return this->setRect(fBounds);
280    }
281
282    //  if we get here, we need to become a complex region
283
284    if (!this->isComplex() || fRunHead->fRunCount != count) {
285        this->freeRuns();
286        this->allocateRuns(count);
287    }
288
289    // must call this before we can write directly into runs()
290    // in case we are sharing the buffer with another region (copy on write)
291    fRunHead = fRunHead->ensureWritable();
292    memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
293    fRunHead->computeRunBounds(&fBounds);
294
295    SkDEBUGCODE(this->validate();)
296
297    return true;
298}
299
300void SkRegion::BuildRectRuns(const SkIRect& bounds,
301                             RunType runs[kRectRegionRuns]) {
302    runs[0] = bounds.fTop;
303    runs[1] = bounds.fBottom;
304    runs[2] = 1;    // 1 interval for this scanline
305    runs[3] = bounds.fLeft;
306    runs[4] = bounds.fRight;
307    runs[5] = kRunTypeSentinel;
308    runs[6] = kRunTypeSentinel;
309}
310
311bool SkRegion::contains(int32_t x, int32_t y) const {
312    SkDEBUGCODE(this->validate();)
313
314    if (!fBounds.contains(x, y)) {
315        return false;
316    }
317    if (this->isRect()) {
318        return true;
319    }
320    SkASSERT(this->isComplex());
321
322    const RunType* runs = fRunHead->findScanline(y);
323
324    // Skip the Bottom and IntervalCount
325    runs += 2;
326
327    // Just walk this scanline, checking each interval. The X-sentinel will
328    // appear as a left-inteval (runs[0]) and should abort the search.
329    //
330    // We could do a bsearch, using interval-count (runs[1]), but need to time
331    // when that would be worthwhile.
332    //
333    for (;;) {
334        if (x < runs[0]) {
335            break;
336        }
337        if (x < runs[1]) {
338            return true;
339        }
340        runs += 2;
341    }
342    return false;
343}
344
345static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
346    return runs[0];
347}
348
349static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
350    // skip [B N [L R]... S]
351    return runs + 2 + runs[1] * 2 + 1;
352}
353
354static bool scanline_contains(const SkRegion::RunType runs[],
355                              SkRegion::RunType L, SkRegion::RunType R) {
356    runs += 2;  // skip Bottom and IntervalCount
357    for (;;) {
358        if (L < runs[0]) {
359            break;
360        }
361        if (R <= runs[1]) {
362            return true;
363        }
364        runs += 2;
365    }
366    return false;
367}
368
369bool SkRegion::contains(const SkIRect& r) const {
370    SkDEBUGCODE(this->validate();)
371
372    if (!fBounds.contains(r)) {
373        return false;
374    }
375    if (this->isRect()) {
376        return true;
377    }
378    SkASSERT(this->isComplex());
379
380    const RunType* scanline = fRunHead->findScanline(r.fTop);
381    for (;;) {
382        if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
383            return false;
384        }
385        if (r.fBottom <= scanline_bottom(scanline)) {
386            break;
387        }
388        scanline = scanline_next(scanline);
389    }
390    return true;
391}
392
393bool SkRegion::contains(const SkRegion& rgn) const {
394    SkDEBUGCODE(this->validate();)
395    SkDEBUGCODE(rgn.validate();)
396
397    if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
398        return false;
399    }
400    if (this->isRect()) {
401        return true;
402    }
403    if (rgn.isRect()) {
404        return this->contains(rgn.getBounds());
405    }
406
407    /*
408     *  A contains B is equivalent to
409     *  B - A == 0
410     */
411    return !Oper(rgn, *this, kDifference_Op, nullptr);
412}
413
414const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
415                                           int* intervals) const {
416    SkASSERT(tmpStorage && intervals);
417    const RunType* runs = tmpStorage;
418
419    if (this->isEmpty()) {
420        tmpStorage[0] = kRunTypeSentinel;
421        *intervals = 0;
422    } else if (this->isRect()) {
423        BuildRectRuns(fBounds, tmpStorage);
424        *intervals = 1;
425    } else {
426        runs = fRunHead->readonly_runs();
427        *intervals = fRunHead->getIntervalCount();
428    }
429    return runs;
430}
431
432///////////////////////////////////////////////////////////////////////////////
433
434static bool scanline_intersects(const SkRegion::RunType runs[],
435                                SkRegion::RunType L, SkRegion::RunType R) {
436    runs += 2;  // skip Bottom and IntervalCount
437    for (;;) {
438        if (R <= runs[0]) {
439            break;
440        }
441        if (L < runs[1]) {
442            return true;
443        }
444        runs += 2;
445    }
446    return false;
447}
448
449bool SkRegion::intersects(const SkIRect& r) const {
450    SkDEBUGCODE(this->validate();)
451
452    if (this->isEmpty() || r.isEmpty()) {
453        return false;
454    }
455
456    SkIRect sect;
457    if (!sect.intersect(fBounds, r)) {
458        return false;
459    }
460    if (this->isRect()) {
461        return true;
462    }
463    SkASSERT(this->isComplex());
464
465    const RunType* scanline = fRunHead->findScanline(sect.fTop);
466    for (;;) {
467        if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
468            return true;
469        }
470        if (sect.fBottom <= scanline_bottom(scanline)) {
471            break;
472        }
473        scanline = scanline_next(scanline);
474    }
475    return false;
476}
477
478bool SkRegion::intersects(const SkRegion& rgn) const {
479    if (this->isEmpty() || rgn.isEmpty()) {
480        return false;
481    }
482
483    if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
484        return false;
485    }
486
487    bool weAreARect = this->isRect();
488    bool theyAreARect = rgn.isRect();
489
490    if (weAreARect && theyAreARect) {
491        return true;
492    }
493    if (weAreARect) {
494        return rgn.intersects(this->getBounds());
495    }
496    if (theyAreARect) {
497        return this->intersects(rgn.getBounds());
498    }
499
500    // both of us are complex
501    return Oper(*this, rgn, kIntersect_Op, nullptr);
502}
503
504///////////////////////////////////////////////////////////////////////////////
505
506bool SkRegion::operator==(const SkRegion& b) const {
507    SkDEBUGCODE(validate();)
508    SkDEBUGCODE(b.validate();)
509
510    if (this == &b) {
511        return true;
512    }
513    if (fBounds != b.fBounds) {
514        return false;
515    }
516
517    const SkRegion::RunHead* ah = fRunHead;
518    const SkRegion::RunHead* bh = b.fRunHead;
519
520    // this catches empties and rects being equal
521    if (ah == bh) {
522        return true;
523    }
524    // now we insist that both are complex (but different ptrs)
525    if (!this->isComplex() || !b.isComplex()) {
526        return false;
527    }
528    return  ah->fRunCount == bh->fRunCount &&
529            !memcmp(ah->readonly_runs(), bh->readonly_runs(),
530                    ah->fRunCount * sizeof(SkRegion::RunType));
531}
532
533void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
534    SkDEBUGCODE(this->validate();)
535
536    if (nullptr == dst) {
537        return;
538    }
539    if (this->isEmpty()) {
540        dst->setEmpty();
541    } else if (this->isRect()) {
542        dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
543                     fBounds.fRight + dx, fBounds.fBottom + dy);
544    } else {
545        if (this == dst) {
546            dst->fRunHead = dst->fRunHead->ensureWritable();
547        } else {
548            SkRegion    tmp;
549            tmp.allocateRuns(*fRunHead);
550            tmp.fBounds = fBounds;
551            dst->swap(tmp);
552        }
553
554        dst->fBounds.offset(dx, dy);
555
556        const RunType*  sruns = fRunHead->readonly_runs();
557        RunType*        druns = dst->fRunHead->writable_runs();
558
559        *druns++ = (SkRegion::RunType)(*sruns++ + dy);    // top
560        for (;;) {
561            int bottom = *sruns++;
562            if (bottom == kRunTypeSentinel) {
563                break;
564            }
565            *druns++ = (SkRegion::RunType)(bottom + dy);  // bottom;
566            *druns++ = *sruns++;    // copy intervalCount;
567            for (;;) {
568                int x = *sruns++;
569                if (x == kRunTypeSentinel) {
570                    break;
571                }
572                *druns++ = (SkRegion::RunType)(x + dx);
573                *druns++ = (SkRegion::RunType)(*sruns++ + dx);
574            }
575            *druns++ = kRunTypeSentinel;    // x sentinel
576        }
577        *druns++ = kRunTypeSentinel;    // y sentinel
578
579        SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
580        SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
581    }
582
583    SkDEBUGCODE(this->validate();)
584}
585
586///////////////////////////////////////////////////////////////////////////////
587
588bool SkRegion::setRects(const SkIRect rects[], int count) {
589    if (0 == count) {
590        this->setEmpty();
591    } else {
592        this->setRect(rects[0]);
593        for (int i = 1; i < count; i++) {
594            this->op(rects[i], kUnion_Op);
595        }
596    }
597    return !this->isEmpty();
598}
599
600///////////////////////////////////////////////////////////////////////////////
601
602#if defined _WIN32  // disable warning : local variable used without having been initialized
603#pragma warning ( push )
604#pragma warning ( disable : 4701 )
605#endif
606
607#ifdef SK_DEBUG
608static void assert_valid_pair(int left, int rite)
609{
610    SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
611}
612#else
613    #define assert_valid_pair(left, rite)
614#endif
615
616struct spanRec {
617    const SkRegion::RunType*    fA_runs;
618    const SkRegion::RunType*    fB_runs;
619    int                         fA_left, fA_rite, fB_left, fB_rite;
620    int                         fLeft, fRite, fInside;
621
622    void init(const SkRegion::RunType a_runs[],
623              const SkRegion::RunType b_runs[]) {
624        fA_left = *a_runs++;
625        fA_rite = *a_runs++;
626        fB_left = *b_runs++;
627        fB_rite = *b_runs++;
628
629        fA_runs = a_runs;
630        fB_runs = b_runs;
631    }
632
633    bool done() const {
634        SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
635        SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
636        return fA_left == SkRegion::kRunTypeSentinel &&
637               fB_left == SkRegion::kRunTypeSentinel;
638    }
639
640    void next() {
641        assert_valid_pair(fA_left, fA_rite);
642        assert_valid_pair(fB_left, fB_rite);
643
644        int     inside, left, rite SK_INIT_TO_AVOID_WARNING;
645        bool    a_flush = false;
646        bool    b_flush = false;
647
648        int a_left = fA_left;
649        int a_rite = fA_rite;
650        int b_left = fB_left;
651        int b_rite = fB_rite;
652
653        if (a_left < b_left) {
654            inside = 1;
655            left = a_left;
656            if (a_rite <= b_left) {   // [...] <...>
657                rite = a_rite;
658                a_flush = true;
659            } else { // [...<..]...> or [...<...>...]
660                rite = a_left = b_left;
661            }
662        } else if (b_left < a_left) {
663            inside = 2;
664            left = b_left;
665            if (b_rite <= a_left) {   // [...] <...>
666                rite = b_rite;
667                b_flush = true;
668            } else {    // [...<..]...> or [...<...>...]
669                rite = b_left = a_left;
670            }
671        } else {    // a_left == b_left
672            inside = 3;
673            left = a_left;  // or b_left
674            if (a_rite <= b_rite) {
675                rite = b_left = a_rite;
676                a_flush = true;
677            }
678            if (b_rite <= a_rite) {
679                rite = a_left = b_rite;
680                b_flush = true;
681            }
682        }
683
684        if (a_flush) {
685            a_left = *fA_runs++;
686            a_rite = *fA_runs++;
687        }
688        if (b_flush) {
689            b_left = *fB_runs++;
690            b_rite = *fB_runs++;
691        }
692
693        SkASSERT(left <= rite);
694
695        // now update our state
696        fA_left = a_left;
697        fA_rite = a_rite;
698        fB_left = b_left;
699        fB_rite = b_rite;
700
701        fLeft = left;
702        fRite = rite;
703        fInside = inside;
704    }
705};
706
707static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
708                                          const SkRegion::RunType b_runs[],
709                                          SkRegion::RunType dst[],
710                                          int min, int max) {
711    spanRec rec;
712    bool    firstInterval = true;
713
714    rec.init(a_runs, b_runs);
715
716    while (!rec.done()) {
717        rec.next();
718
719        int left = rec.fLeft;
720        int rite = rec.fRite;
721
722        // add left,rite to our dst buffer (checking for coincidence
723        if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
724                left < rite) {    // skip if equal
725            if (firstInterval || dst[-1] < left) {
726                *dst++ = (SkRegion::RunType)(left);
727                *dst++ = (SkRegion::RunType)(rite);
728                firstInterval = false;
729            } else {
730                // update the right edge
731                dst[-1] = (SkRegion::RunType)(rite);
732            }
733        }
734    }
735
736    *dst++ = SkRegion::kRunTypeSentinel;
737    return dst;
738}
739
740#if defined _WIN32
741#pragma warning ( pop )
742#endif
743
744static const struct {
745    uint8_t fMin;
746    uint8_t fMax;
747} gOpMinMax[] = {
748    { 1, 1 },   // Difference
749    { 3, 3 },   // Intersection
750    { 1, 3 },   // Union
751    { 1, 2 }    // XOR
752};
753
754class RgnOper {
755public:
756    RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
757        // need to ensure that the op enum lines up with our minmax array
758        SkASSERT(SkRegion::kDifference_Op == 0);
759        SkASSERT(SkRegion::kIntersect_Op == 1);
760        SkASSERT(SkRegion::kUnion_Op == 2);
761        SkASSERT(SkRegion::kXOR_Op == 3);
762        SkASSERT((unsigned)op <= 3);
763
764        fStartDst = dst;
765        fPrevDst = dst + 1;
766        fPrevLen = 0;       // will never match a length from operate_on_span
767        fTop = (SkRegion::RunType)(top);    // just a first guess, we might update this
768
769        fMin = gOpMinMax[op].fMin;
770        fMax = gOpMinMax[op].fMax;
771    }
772
773    void addSpan(int bottom, const SkRegion::RunType a_runs[],
774                 const SkRegion::RunType b_runs[]) {
775        // skip X values and slots for the next Y+intervalCount
776        SkRegion::RunType*  start = fPrevDst + fPrevLen + 2;
777        // start points to beginning of dst interval
778        SkRegion::RunType*  stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
779        size_t              len = stop - start;
780        SkASSERT(len >= 1 && (len & 1) == 1);
781        SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
782
783        if (fPrevLen == len &&
784            (1 == len || !memcmp(fPrevDst, start,
785                                 (len - 1) * sizeof(SkRegion::RunType)))) {
786            // update Y value
787            fPrevDst[-2] = (SkRegion::RunType)(bottom);
788        } else {    // accept the new span
789            if (len == 1 && fPrevLen == 0) {
790                fTop = (SkRegion::RunType)(bottom); // just update our bottom
791            } else {
792                start[-2] = (SkRegion::RunType)(bottom);
793                start[-1] = SkToS32(len >> 1);
794                fPrevDst = start;
795                fPrevLen = len;
796            }
797        }
798    }
799
800    int flush() {
801        fStartDst[0] = fTop;
802        fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
803        return (int)(fPrevDst - fStartDst + fPrevLen + 1);
804    }
805
806    bool isEmpty() const { return 0 == fPrevLen; }
807
808    uint8_t fMin, fMax;
809
810private:
811    SkRegion::RunType*  fStartDst;
812    SkRegion::RunType*  fPrevDst;
813    size_t              fPrevLen;
814    SkRegion::RunType   fTop;
815};
816
817// want a unique value to signal that we exited due to quickExit
818#define QUICK_EXIT_TRUE_COUNT   (-1)
819
820static int operate(const SkRegion::RunType a_runs[],
821                   const SkRegion::RunType b_runs[],
822                   SkRegion::RunType dst[],
823                   SkRegion::Op op,
824                   bool quickExit) {
825    const SkRegion::RunType gEmptyScanline[] = {
826        0,  // dummy bottom value
827        0,  // zero intervals
828        SkRegion::kRunTypeSentinel,
829        // just need a 2nd value, since spanRec.init() reads 2 values, even
830        // though if the first value is the sentinel, it ignores the 2nd value.
831        // w/o the 2nd value here, we might read uninitialized memory.
832        // This happens when we are using gSentinel, which is pointing at
833        // our sentinel value.
834        0
835    };
836    const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
837
838    int a_top = *a_runs++;
839    int a_bot = *a_runs++;
840    int b_top = *b_runs++;
841    int b_bot = *b_runs++;
842
843    a_runs += 1;    // skip the intervalCount;
844    b_runs += 1;    // skip the intervalCount;
845
846    // Now a_runs and b_runs to their intervals (or sentinel)
847
848    assert_sentinel(a_top, false);
849    assert_sentinel(a_bot, false);
850    assert_sentinel(b_top, false);
851    assert_sentinel(b_bot, false);
852
853    RgnOper oper(SkMin32(a_top, b_top), dst, op);
854
855    int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
856
857    while (a_bot < SkRegion::kRunTypeSentinel ||
858           b_bot < SkRegion::kRunTypeSentinel) {
859        int                         top, bot SK_INIT_TO_AVOID_WARNING;
860        const SkRegion::RunType*    run0 = gSentinel;
861        const SkRegion::RunType*    run1 = gSentinel;
862        bool                        a_flush = false;
863        bool                        b_flush = false;
864
865        if (a_top < b_top) {
866            top = a_top;
867            run0 = a_runs;
868            if (a_bot <= b_top) {   // [...] <...>
869                bot = a_bot;
870                a_flush = true;
871            } else {  // [...<..]...> or [...<...>...]
872                bot = a_top = b_top;
873            }
874        } else if (b_top < a_top) {
875            top = b_top;
876            run1 = b_runs;
877            if (b_bot <= a_top) {   // [...] <...>
878                bot = b_bot;
879                b_flush = true;
880            } else {    // [...<..]...> or [...<...>...]
881                bot = b_top = a_top;
882            }
883        } else {    // a_top == b_top
884            top = a_top;    // or b_top
885            run0 = a_runs;
886            run1 = b_runs;
887            if (a_bot <= b_bot) {
888                bot = b_top = a_bot;
889                a_flush = true;
890            }
891            if (b_bot <= a_bot) {
892                bot = a_top = b_bot;
893                b_flush = true;
894            }
895        }
896
897        if (top > prevBot) {
898            oper.addSpan(top, gSentinel, gSentinel);
899        }
900        oper.addSpan(bot, run0, run1);
901
902        if (quickExit && !oper.isEmpty()) {
903            return QUICK_EXIT_TRUE_COUNT;
904        }
905
906        if (a_flush) {
907            a_runs = skip_intervals(a_runs);
908            a_top = a_bot;
909            a_bot = *a_runs++;
910            a_runs += 1;    // skip uninitialized intervalCount
911            if (a_bot == SkRegion::kRunTypeSentinel) {
912                a_top = a_bot;
913            }
914        }
915        if (b_flush) {
916            b_runs = skip_intervals(b_runs);
917            b_top = b_bot;
918            b_bot = *b_runs++;
919            b_runs += 1;    // skip uninitialized intervalCount
920            if (b_bot == SkRegion::kRunTypeSentinel) {
921                b_top = b_bot;
922            }
923        }
924
925        prevBot = bot;
926    }
927    return oper.flush();
928}
929
930///////////////////////////////////////////////////////////////////////////////
931
932/*  Given count RunTypes in a complex region, return the worst case number of
933    logical intervals that represents (i.e. number of rects that would be
934    returned from the iterator).
935
936    We could just return count/2, since there must be at least 2 values per
937    interval, but we can first trim off the const overhead of the initial TOP
938    value, plus the final BOTTOM + 2 sentinels.
939 */
940#if 0 // UNUSED
941static int count_to_intervals(int count) {
942    SkASSERT(count >= 6);   // a single rect is 6 values
943    return (count - 4) >> 1;
944}
945#endif
946
947/*  Given a number of intervals, what is the worst case representation of that
948    many intervals?
949
950    Worst case (from a storage perspective), is a vertical stack of single
951    intervals:  TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
952 */
953static int intervals_to_count(int intervals) {
954    return 1 + intervals * 5 + 1;
955}
956
957/*  Given the intervalCounts of RunTypes in two regions, return the worst-case number
958    of RunTypes need to store the result after a region-op.
959 */
960static int compute_worst_case_count(int a_intervals, int b_intervals) {
961    // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
962    int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
963    // convert back to number of RunType values
964    return intervals_to_count(intervals);
965}
966
967static bool setEmptyCheck(SkRegion* result) {
968    return result ? result->setEmpty() : false;
969}
970
971static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
972    return result ? result->setRect(rect) : !rect.isEmpty();
973}
974
975static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
976    return result ? result->setRegion(rgn) : !rgn.isEmpty();
977}
978
979bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
980                    SkRegion* result) {
981    SkASSERT((unsigned)op < kOpCount);
982
983    if (kReplace_Op == op) {
984        return setRegionCheck(result, rgnbOrig);
985    }
986
987    // swith to using pointers, so we can swap them as needed
988    const SkRegion* rgna = &rgnaOrig;
989    const SkRegion* rgnb = &rgnbOrig;
990    // after this point, do not refer to rgnaOrig or rgnbOrig!!!
991
992    // collaps difference and reverse-difference into just difference
993    if (kReverseDifference_Op == op) {
994        SkTSwap<const SkRegion*>(rgna, rgnb);
995        op = kDifference_Op;
996    }
997
998    SkIRect bounds;
999    bool    a_empty = rgna->isEmpty();
1000    bool    b_empty = rgnb->isEmpty();
1001    bool    a_rect = rgna->isRect();
1002    bool    b_rect = rgnb->isRect();
1003
1004    switch (op) {
1005    case kDifference_Op:
1006        if (a_empty) {
1007            return setEmptyCheck(result);
1008        }
1009        if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
1010                                                        rgnb->fBounds)) {
1011            return setRegionCheck(result, *rgna);
1012        }
1013        if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
1014            return setEmptyCheck(result);
1015        }
1016        break;
1017
1018    case kIntersect_Op:
1019        if ((a_empty | b_empty)
1020                || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
1021            return setEmptyCheck(result);
1022        }
1023        if (a_rect & b_rect) {
1024            return setRectCheck(result, bounds);
1025        }
1026        if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1027            return setRegionCheck(result, *rgnb);
1028        }
1029        if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1030            return setRegionCheck(result, *rgna);
1031        }
1032        break;
1033
1034    case kUnion_Op:
1035        if (a_empty) {
1036            return setRegionCheck(result, *rgnb);
1037        }
1038        if (b_empty) {
1039            return setRegionCheck(result, *rgna);
1040        }
1041        if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
1042            return setRegionCheck(result, *rgna);
1043        }
1044        if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
1045            return setRegionCheck(result, *rgnb);
1046        }
1047        break;
1048
1049    case kXOR_Op:
1050        if (a_empty) {
1051            return setRegionCheck(result, *rgnb);
1052        }
1053        if (b_empty) {
1054            return setRegionCheck(result, *rgna);
1055        }
1056        break;
1057    default:
1058        SkDEBUGFAIL("unknown region op");
1059        return false;
1060    }
1061
1062    RunType tmpA[kRectRegionRuns];
1063    RunType tmpB[kRectRegionRuns];
1064
1065    int a_intervals, b_intervals;
1066    const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
1067    const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
1068
1069    int dstCount = compute_worst_case_count(a_intervals, b_intervals);
1070    SkAutoSTMalloc<256, RunType> array(dstCount);
1071
1072#ifdef SK_DEBUG
1073//  Sometimes helpful to seed everything with a known value when debugging
1074//  sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
1075#endif
1076
1077    int count = operate(a_runs, b_runs, array.get(), op, nullptr == result);
1078    SkASSERT(count <= dstCount);
1079
1080    if (result) {
1081        SkASSERT(count >= 0);
1082        return result->setRuns(array.get(), count);
1083    } else {
1084        return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
1085    }
1086}
1087
1088bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
1089    SkDEBUGCODE(this->validate();)
1090    return SkRegion::Oper(rgna, rgnb, op, this);
1091}
1092
1093///////////////////////////////////////////////////////////////////////////////
1094
1095#include "SkBuffer.h"
1096
1097size_t SkRegion::writeToMemory(void* storage) const {
1098    if (nullptr == storage) {
1099        size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
1100        if (!this->isEmpty()) {
1101            size += sizeof(fBounds);
1102            if (this->isComplex()) {
1103                size += 2 * sizeof(int32_t);    // ySpanCount + intervalCount
1104                size += fRunHead->fRunCount * sizeof(RunType);
1105            }
1106        }
1107        return size;
1108    }
1109
1110    SkWBuffer   buffer(storage);
1111
1112    if (this->isEmpty()) {
1113        buffer.write32(-1);
1114    } else {
1115        bool isRect = this->isRect();
1116
1117        buffer.write32(isRect ? 0 : fRunHead->fRunCount);
1118        buffer.write(&fBounds, sizeof(fBounds));
1119
1120        if (!isRect) {
1121            buffer.write32(fRunHead->getYSpanCount());
1122            buffer.write32(fRunHead->getIntervalCount());
1123            buffer.write(fRunHead->readonly_runs(),
1124                         fRunHead->fRunCount * sizeof(RunType));
1125        }
1126    }
1127    return buffer.pos();
1128}
1129
1130size_t SkRegion::readFromMemory(const void* storage, size_t length) {
1131    SkRBuffer   buffer(storage, length);
1132    SkRegion    tmp;
1133    int32_t     count;
1134
1135    if (buffer.readS32(&count) && (count >= 0) && buffer.read(&tmp.fBounds, sizeof(tmp.fBounds))) {
1136        if (count == 0) {
1137            tmp.fRunHead = SkRegion_gRectRunHeadPtr;
1138        } else {
1139            int32_t ySpanCount, intervalCount;
1140            if (buffer.readS32(&ySpanCount) && buffer.readS32(&intervalCount) &&
1141                intervalCount > 1) {
1142                tmp.allocateRuns(count, ySpanCount, intervalCount);
1143                buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
1144            }
1145        }
1146    }
1147    size_t sizeRead = 0;
1148    if (buffer.isValid()) {
1149        this->swap(tmp);
1150        sizeRead = buffer.pos();
1151    }
1152    return sizeRead;
1153}
1154
1155///////////////////////////////////////////////////////////////////////////////
1156
1157const SkRegion& SkRegion::GetEmptyRegion() {
1158    static SkRegion gEmpty;
1159    return gEmpty;
1160}
1161
1162///////////////////////////////////////////////////////////////////////////////
1163
1164#ifdef SK_DEBUG
1165
1166// Starts with first X-interval, and returns a ptr to the X-sentinel
1167static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) {
1168    // want to track that our intevals are all disjoint, such that
1169    // prev-right < next-left. We rely on this optimization in places such as
1170    // contains().
1171    //
1172    SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel;
1173
1174    while (runs[0] < SkRegion::kRunTypeSentinel) {
1175        SkASSERT(prevR < runs[0]);
1176        SkASSERT(runs[0] < runs[1]);
1177        SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
1178        prevR = runs[1];
1179        runs += 2;
1180    }
1181    return runs;
1182}
1183
1184static void compute_bounds(const SkRegion::RunType runs[],
1185                           SkIRect* bounds, int* ySpanCountPtr,
1186                           int* intervalCountPtr) {
1187    assert_sentinel(runs[0], false);    // top
1188
1189    int left = SK_MaxS32;
1190    int rite = SK_MinS32;
1191    int bot;
1192    int ySpanCount = 0;
1193    int intervalCount = 0;
1194
1195    bounds->fTop = *runs++;
1196    do {
1197        bot = *runs++;
1198        SkASSERT(SkRegion::kRunTypeSentinel > bot);
1199
1200        ySpanCount += 1;
1201
1202        runs += 1;  // skip intervalCount for now
1203        if (*runs < SkRegion::kRunTypeSentinel) {
1204            if (left > *runs) {
1205                left = *runs;
1206            }
1207
1208            const SkRegion::RunType* prev = runs;
1209            runs = skip_intervals_slow(runs);
1210            int intervals = SkToInt((runs - prev) >> 1);
1211            SkASSERT(prev[-1] == intervals);
1212            intervalCount += intervals;
1213
1214            if (rite < runs[-1]) {
1215                rite = runs[-1];
1216            }
1217        } else {
1218            SkASSERT(0 == runs[-1]);    // no intervals
1219        }
1220        SkASSERT(SkRegion::kRunTypeSentinel == *runs);
1221        runs += 1;
1222    } while (SkRegion::kRunTypeSentinel != *runs);
1223
1224    bounds->fLeft = left;
1225    bounds->fRight = rite;
1226    bounds->fBottom = bot;
1227    *ySpanCountPtr = ySpanCount;
1228    *intervalCountPtr = intervalCount;
1229}
1230
1231void SkRegion::validate() const {
1232    if (this->isEmpty()) {
1233        // check for explicit empty (the zero rect), so we can compare rects to know when
1234        // two regions are equal (i.e. emptyRectA == emptyRectB)
1235        // this is stricter than just asserting fBounds.isEmpty()
1236        SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
1237    } else {
1238        SkASSERT(!fBounds.isEmpty());
1239        if (!this->isRect()) {
1240            SkASSERT(fRunHead->fRefCnt >= 1);
1241            SkASSERT(fRunHead->fRunCount > kRectRegionRuns);
1242
1243            const RunType* run = fRunHead->readonly_runs();
1244
1245            // check that our bounds match our runs
1246            {
1247                SkIRect bounds;
1248                int ySpanCount, intervalCount;
1249                compute_bounds(run, &bounds, &ySpanCount, &intervalCount);
1250
1251                SkASSERT(bounds == fBounds);
1252                SkASSERT(ySpanCount > 0);
1253                SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
1254           //     SkASSERT(intervalCount > 1);
1255                SkASSERT(fRunHead->getIntervalCount() == intervalCount);
1256            }
1257        }
1258    }
1259}
1260
1261void SkRegion::dump() const {
1262    if (this->isEmpty()) {
1263        SkDebugf("  rgn: empty\n");
1264    } else {
1265        SkDebugf("  rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
1266        if (this->isComplex()) {
1267            const RunType* runs = fRunHead->readonly_runs();
1268            for (int i = 0; i < fRunHead->fRunCount; i++)
1269                SkDebugf(" %d", runs[i]);
1270        }
1271        SkDebugf("\n");
1272    }
1273}
1274
1275#endif
1276
1277///////////////////////////////////////////////////////////////////////////////
1278
1279SkRegion::Iterator::Iterator(const SkRegion& rgn) {
1280    this->reset(rgn);
1281}
1282
1283bool SkRegion::Iterator::rewind() {
1284    if (fRgn) {
1285        this->reset(*fRgn);
1286        return true;
1287    }
1288    return false;
1289}
1290
1291void SkRegion::Iterator::reset(const SkRegion& rgn) {
1292    fRgn = &rgn;
1293    if (rgn.isEmpty()) {
1294        fDone = true;
1295    } else {
1296        fDone = false;
1297        if (rgn.isRect()) {
1298            fRect = rgn.fBounds;
1299            fRuns = nullptr;
1300        } else {
1301            fRuns = rgn.fRunHead->readonly_runs();
1302            fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
1303            fRuns += 5;
1304            // Now fRuns points to the 2nd interval (or x-sentinel)
1305        }
1306    }
1307}
1308
1309void SkRegion::Iterator::next() {
1310    if (fDone) {
1311        return;
1312    }
1313
1314    if (fRuns == nullptr) {   // rect case
1315        fDone = true;
1316        return;
1317    }
1318
1319    const RunType* runs = fRuns;
1320
1321    if (runs[0] < kRunTypeSentinel) { // valid X value
1322        fRect.fLeft = runs[0];
1323        fRect.fRight = runs[1];
1324        runs += 2;
1325    } else {    // we're at the end of a line
1326        runs += 1;
1327        if (runs[0] < kRunTypeSentinel) { // valid Y value
1328            int intervals = runs[1];
1329            if (0 == intervals) {    // empty line
1330                fRect.fTop = runs[0];
1331                runs += 3;
1332            } else {
1333                fRect.fTop = fRect.fBottom;
1334            }
1335
1336            fRect.fBottom = runs[0];
1337            assert_sentinel(runs[2], false);
1338            assert_sentinel(runs[3], false);
1339            fRect.fLeft = runs[2];
1340            fRect.fRight = runs[3];
1341            runs += 4;
1342        } else {    // end of rgn
1343            fDone = true;
1344        }
1345    }
1346    fRuns = runs;
1347}
1348
1349SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
1350        : fIter(rgn), fClip(clip), fDone(true) {
1351    const SkIRect& r = fIter.rect();
1352
1353    while (!fIter.done()) {
1354        if (r.fTop >= clip.fBottom) {
1355            break;
1356        }
1357        if (fRect.intersect(clip, r)) {
1358            fDone = false;
1359            break;
1360        }
1361        fIter.next();
1362    }
1363}
1364
1365void SkRegion::Cliperator::next() {
1366    if (fDone) {
1367        return;
1368    }
1369
1370    const SkIRect& r = fIter.rect();
1371
1372    fDone = true;
1373    fIter.next();
1374    while (!fIter.done()) {
1375        if (r.fTop >= fClip.fBottom) {
1376            break;
1377        }
1378        if (fRect.intersect(fClip, r)) {
1379            fDone = false;
1380            break;
1381        }
1382        fIter.next();
1383    }
1384}
1385
1386///////////////////////////////////////////////////////////////////////////////
1387
1388SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
1389                                 int right) {
1390    SkDEBUGCODE(rgn.validate();)
1391
1392    const SkIRect& r = rgn.getBounds();
1393
1394    fDone = true;
1395    if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
1396            right > r.fLeft && left < r.fRight) {
1397        if (rgn.isRect()) {
1398            if (left < r.fLeft) {
1399                left = r.fLeft;
1400            }
1401            if (right > r.fRight) {
1402                right = r.fRight;
1403            }
1404            fLeft = left;
1405            fRight = right;
1406            fRuns = nullptr;    // means we're a rect, not a rgn
1407            fDone = false;
1408        } else {
1409            const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
1410            runs += 2;  // skip Bottom and IntervalCount
1411            for (;;) {
1412                // runs[0..1] is to the right of the span, so we're done
1413                if (runs[0] >= right) {
1414                    break;
1415                }
1416                // runs[0..1] is to the left of the span, so continue
1417                if (runs[1] <= left) {
1418                    runs += 2;
1419                    continue;
1420                }
1421                // runs[0..1] intersects the span
1422                fRuns = runs;
1423                fLeft = left;
1424                fRight = right;
1425                fDone = false;
1426                break;
1427            }
1428        }
1429    }
1430}
1431
1432bool SkRegion::Spanerator::next(int* left, int* right) {
1433    if (fDone) {
1434        return false;
1435    }
1436
1437    if (fRuns == nullptr) {   // we're a rect
1438        fDone = true;   // ok, now we're done
1439        if (left) {
1440            *left = fLeft;
1441        }
1442        if (right) {
1443            *right = fRight;
1444        }
1445        return true;    // this interval is legal
1446    }
1447
1448    const SkRegion::RunType* runs = fRuns;
1449
1450    if (runs[0] >= fRight) {
1451        fDone = true;
1452        return false;
1453    }
1454
1455    SkASSERT(runs[1] > fLeft);
1456
1457    if (left) {
1458        *left = SkMax32(fLeft, runs[0]);
1459    }
1460    if (right) {
1461        *right = SkMin32(fRight, runs[1]);
1462    }
1463    fRuns = runs + 2;
1464    return true;
1465}
1466
1467///////////////////////////////////////////////////////////////////////////////
1468
1469#ifdef SK_DEBUG
1470
1471bool SkRegion::debugSetRuns(const RunType runs[], int count) {
1472    // we need to make a copy, since the real method may modify the array, and
1473    // so it cannot be const.
1474
1475    SkAutoTArray<RunType> storage(count);
1476    memcpy(storage.get(), runs, count * sizeof(RunType));
1477    return this->setRuns(storage.get(), count);
1478}
1479
1480#endif
1481