1 /*
2  * Copyright 2005 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 #ifndef SkRegion_DEFINED
9 #define SkRegion_DEFINED
10 
11 #include "include/core/SkRect.h"
12 
13 class SkPath;
14 class SkRgnBuilder;
15 
16 /** \class SkRegion
17     SkRegion describes the set of pixels used to clip SkCanvas. SkRegion is compact,
18     efficiently storing a single integer rectangle, or a run length encoded array
19     of rectangles. SkRegion may reduce the current SkCanvas clip, or may be drawn as
20     one or more integer rectangles. SkRegion iterator returns the scan lines or
21     rectangles contained by it, optionally intersecting a bounding rectangle.
22 */
23 class SK_API SkRegion {
24     typedef int32_t RunType;
25 public:
26 
27     /** Constructs an empty SkRegion. SkRegion is set to empty bounds
28         at (0, 0) with zero width and height.
29 
30         @return  empty SkRegion
31 
32         example: https://fiddle.skia.org/c/@Region_empty_constructor
33     */
34     SkRegion();
35 
36     /** Constructs a copy of an existing region.
37         Copy constructor makes two regions identical by value. Internally, region and
38         the returned result share pointer values. The underlying SkRect array is
39         copied when modified.
40 
41         Creating a SkRegion copy is very efficient and never allocates memory.
42         SkRegion are always copied by value from the interface; the underlying shared
43         pointers are not exposed.
44 
45         @param region  SkRegion to copy by value
46         @return        copy of SkRegion
47 
48         example: https://fiddle.skia.org/c/@Region_copy_const_SkRegion
49     */
50     SkRegion(const SkRegion& region);
51 
52     /** Constructs a rectangular SkRegion matching the bounds of rect.
53 
54         @param rect  bounds of constructed SkRegion
55         @return      rectangular SkRegion
56 
57         example: https://fiddle.skia.org/c/@Region_copy_const_SkIRect
58     */
59     explicit SkRegion(const SkIRect& rect);
60 
61     /** Releases ownership of any shared data and deletes data if SkRegion is sole owner.
62 
63         example: https://fiddle.skia.org/c/@Region_destructor
64     */
65     ~SkRegion();
66 
67     /** Constructs a copy of an existing region.
68         Makes two regions identical by value. Internally, region and
69         the returned result share pointer values. The underlying SkRect array is
70         copied when modified.
71 
72         Creating a SkRegion copy is very efficient and never allocates memory.
73         SkRegion are always copied by value from the interface; the underlying shared
74         pointers are not exposed.
75 
76         @param region  SkRegion to copy by value
77         @return        SkRegion to copy by value
78 
79         example: https://fiddle.skia.org/c/@Region_copy_operator
80     */
81     SkRegion& operator=(const SkRegion& region);
82 
83     /** Compares SkRegion and other; returns true if they enclose exactly
84         the same area.
85 
86         @param other  SkRegion to compare
87         @return       true if SkRegion pair are equivalent
88 
89         example: https://fiddle.skia.org/c/@Region_equal1_operator
90     */
91     bool operator==(const SkRegion& other) const;
92 
93     /** Compares SkRegion and other; returns true if they do not enclose the same area.
94 
95         @param other  SkRegion to compare
96         @return       true if SkRegion pair are not equivalent
97     */
98     bool operator!=(const SkRegion& other) const {
99         return !(*this == other);
100     }
101 
102     /** Sets SkRegion to src, and returns true if src bounds is not empty.
103         This makes SkRegion and src identical by value. Internally,
104         SkRegion and src share pointer values. The underlying SkRect array is
105         copied when modified.
106 
107         Creating a SkRegion copy is very efficient and never allocates memory.
108         SkRegion are always copied by value from the interface; the underlying shared
109         pointers are not exposed.
110 
111         @param src  SkRegion to copy
112         @return     copy of src
113     */
set(const SkRegion & src)114     bool set(const SkRegion& src) {
115         *this = src;
116         return !this->isEmpty();
117     }
118 
119     /** Exchanges SkIRect array of SkRegion and other. swap() internally exchanges pointers,
120         so it is lightweight and does not allocate memory.
121 
122         swap() usage has largely been replaced by operator=(const SkRegion& region).
123         SkPath do not copy their content on assignment until they are written to,
124         making assignment as efficient as swap().
125 
126         @param other  operator=(const SkRegion& region) set
127 
128         example: https://fiddle.skia.org/c/@Region_swap
129     */
130     void swap(SkRegion& other);
131 
132     /** Returns true if SkRegion is empty.
133         Empty SkRegion has bounds width or height less than or equal to zero.
134         SkRegion() constructs empty SkRegion; setEmpty()
135         and setRect() with dimensionless data make SkRegion empty.
136 
137         @return  true if bounds has no width or height
138     */
isEmpty()139     bool isEmpty() const { return fRunHead == emptyRunHeadPtr(); }
140 
141     /** Returns true if SkRegion is one SkIRect with positive dimensions.
142 
143         @return  true if SkRegion contains one SkIRect
144     */
isRect()145     bool isRect() const { return fRunHead == kRectRunHeadPtr; }
146 
147     /** Returns true if SkRegion is described by more than one rectangle.
148 
149         @return  true if SkRegion contains more than one SkIRect
150     */
isComplex()151     bool isComplex() const { return !this->isEmpty() && !this->isRect(); }
152 
153     /** Returns minimum and maximum axes values of SkIRect array.
154         Returns (0, 0, 0, 0) if SkRegion is empty.
155 
156         @return  combined bounds of all SkIRect elements
157     */
getBounds()158     const SkIRect& getBounds() const { return fBounds; }
159 
160     /** Returns a value that increases with the number of
161         elements in SkRegion. Returns zero if SkRegion is empty.
162         Returns one if SkRegion equals SkIRect; otherwise, returns
163         value greater than one indicating that SkRegion is complex.
164 
165         Call to compare SkRegion for relative complexity.
166 
167         @return  relative complexity
168 
169         example: https://fiddle.skia.org/c/@Region_computeRegionComplexity
170     */
171     int computeRegionComplexity() const;
172 
173     /** Appends outline of SkRegion to path.
174         Returns true if SkRegion is not empty; otherwise, returns false, and leaves path
175         unmodified.
176 
177         @param path  SkPath to append to
178         @return      true if path changed
179 
180         example: https://fiddle.skia.org/c/@Region_getBoundaryPath
181     */
182     bool getBoundaryPath(SkPath* path) const;
183 
184     /** Constructs an empty SkRegion. SkRegion is set to empty bounds
185         at (0, 0) with zero width and height. Always returns false.
186 
187         @return  false
188 
189         example: https://fiddle.skia.org/c/@Region_setEmpty
190     */
191     bool setEmpty();
192 
193     /** Constructs a rectangular SkRegion matching the bounds of rect.
194         If rect is empty, constructs empty and returns false.
195 
196         @param rect  bounds of constructed SkRegion
197         @return      true if rect is not empty
198 
199         example: https://fiddle.skia.org/c/@Region_setRect
200     */
201     bool setRect(const SkIRect& rect);
202 
203     /** Constructs SkRegion as the union of SkIRect in rects array. If count is
204         zero, constructs empty SkRegion. Returns false if constructed SkRegion is empty.
205 
206         May be faster than repeated calls to op().
207 
208         @param rects  array of SkIRect
209         @param count  array size
210         @return       true if constructed SkRegion is not empty
211 
212         example: https://fiddle.skia.org/c/@Region_setRects
213     */
214     bool setRects(const SkIRect rects[], int count);
215 
216     /** Constructs a copy of an existing region.
217         Makes two regions identical by value. Internally, region and
218         the returned result share pointer values. The underlying SkRect array is
219         copied when modified.
220 
221         Creating a SkRegion copy is very efficient and never allocates memory.
222         SkRegion are always copied by value from the interface; the underlying shared
223         pointers are not exposed.
224 
225         @param region  SkRegion to copy by value
226         @return        SkRegion to copy by value
227 
228         example: https://fiddle.skia.org/c/@Region_setRegion
229     */
230     bool setRegion(const SkRegion& region);
231 
232     /** Constructs SkRegion to match outline of path within clip.
233         Returns false if constructed SkRegion is empty.
234 
235         Constructed SkRegion draws the same pixels as path through clip when
236         anti-aliasing is disabled.
237 
238         @param path  SkPath providing outline
239         @param clip  SkRegion containing path
240         @return      true if constructed SkRegion is not empty
241 
242         example: https://fiddle.skia.org/c/@Region_setPath
243     */
244     bool setPath(const SkPath& path, const SkRegion& clip);
245 
246     /** Returns true if SkRegion intersects rect.
247         Returns false if either rect or SkRegion is empty, or do not intersect.
248 
249         @param rect  SkIRect to intersect
250         @return      true if rect and SkRegion have area in common
251 
252         example: https://fiddle.skia.org/c/@Region_intersects
253     */
254     bool intersects(const SkIRect& rect) const;
255 
256     /** Returns true if SkRegion intersects other.
257         Returns false if either other or SkRegion is empty, or do not intersect.
258 
259         @param other  SkRegion to intersect
260         @return       true if other and SkRegion have area in common
261 
262         example: https://fiddle.skia.org/c/@Region_intersects_2
263     */
264     bool intersects(const SkRegion& other) const;
265 
266     /** Returns true if SkIPoint (x, y) is inside SkRegion.
267         Returns false if SkRegion is empty.
268 
269         @param x  test SkIPoint x-coordinate
270         @param y  test SkIPoint y-coordinate
271         @return   true if (x, y) is inside SkRegion
272 
273         example: https://fiddle.skia.org/c/@Region_contains
274     */
275     bool contains(int32_t x, int32_t y) const;
276 
277     /** Returns true if other is completely inside SkRegion.
278         Returns false if SkRegion or other is empty.
279 
280         @param other  SkIRect to contain
281         @return       true if other is inside SkRegion
282 
283         example: https://fiddle.skia.org/c/@Region_contains_2
284     */
285     bool contains(const SkIRect& other) const;
286 
287     /** Returns true if other is completely inside SkRegion.
288         Returns false if SkRegion or other is empty.
289 
290         @param other  SkRegion to contain
291         @return       true if other is inside SkRegion
292 
293         example: https://fiddle.skia.org/c/@Region_contains_3
294     */
295     bool contains(const SkRegion& other) const;
296 
297     /** Returns true if SkRegion is a single rectangle and contains r.
298         May return false even though SkRegion contains r.
299 
300         @param r  SkIRect to contain
301         @return   true quickly if r points are equal or inside
302     */
quickContains(const SkIRect & r)303     bool quickContains(const SkIRect& r) const {
304         SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region
305 
306         return  r.fLeft < r.fRight && r.fTop < r.fBottom &&
307                 fRunHead == kRectRunHeadPtr &&  // this->isRect()
308                 /* fBounds.contains(left, top, right, bottom); */
309                 fBounds.fLeft <= r.fLeft   && fBounds.fTop <= r.fTop &&
310                 fBounds.fRight >= r.fRight && fBounds.fBottom >= r.fBottom;
311     }
312 
313     /** Returns true if SkRegion does not intersect rect.
314         Returns true if rect is empty or SkRegion is empty.
315         May return false even though SkRegion does not intersect rect.
316 
317         @param rect  SkIRect to intersect
318         @return      true if rect does not intersect
319     */
quickReject(const SkIRect & rect)320     bool quickReject(const SkIRect& rect) const {
321         return this->isEmpty() || rect.isEmpty() ||
322                 !SkIRect::Intersects(fBounds, rect);
323     }
324 
325     /** Returns true if SkRegion does not intersect rgn.
326         Returns true if rgn is empty or SkRegion is empty.
327         May return false even though SkRegion does not intersect rgn.
328 
329         @param rgn  SkRegion to intersect
330         @return     true if rgn does not intersect
331     */
quickReject(const SkRegion & rgn)332     bool quickReject(const SkRegion& rgn) const {
333         return this->isEmpty() || rgn.isEmpty() ||
334                !SkIRect::Intersects(fBounds, rgn.fBounds);
335     }
336 
337     /** Offsets SkRegion by ivector (dx, dy). Has no effect if SkRegion is empty.
338 
339         @param dx  x-axis offset
340         @param dy  y-axis offset
341     */
translate(int dx,int dy)342     void translate(int dx, int dy) { this->translate(dx, dy, this); }
343 
344     /** Offsets SkRegion by ivector (dx, dy), writing result to dst. SkRegion may be passed
345         as dst parameter, translating SkRegion in place. Has no effect if dst is nullptr.
346         If SkRegion is empty, sets dst to empty.
347 
348         @param dx   x-axis offset
349         @param dy   y-axis offset
350         @param dst  translated result
351 
352         example: https://fiddle.skia.org/c/@Region_translate_2
353     */
354     void translate(int dx, int dy, SkRegion* dst) const;
355 
356     /** \enum SkRegion::Op
357         The logical operations that can be performed when combining two SkRegion.
358     */
359     enum Op {
360         kDifference_Op,                      //!< target minus operand
361         kIntersect_Op,                       //!< target intersected with operand
362         kUnion_Op,                           //!< target unioned with operand
363         kXOR_Op,                             //!< target exclusive or with operand
364         kReverseDifference_Op,               //!< operand minus target
365         kReplace_Op,                         //!< replace target with operand
366         kLastOp               = kReplace_Op, //!< last operator
367     };
368 
369     static const int kOpCnt = kLastOp + 1;
370 
371     /** Replaces SkRegion with the result of SkRegion op rect.
372         Returns true if replaced SkRegion is not empty.
373 
374         @param rect  SkIRect operand
375         @return      false if result is empty
376     */
op(const SkIRect & rect,Op op)377     bool op(const SkIRect& rect, Op op) {
378         if (this->isRect() && kIntersect_Op == op) {
379             if (!fBounds.intersect(rect)) {
380                 return this->setEmpty();
381             }
382             return true;
383         }
384         return this->op(*this, rect, op);
385     }
386 
387     /** Replaces SkRegion with the result of SkRegion op rgn.
388         Returns true if replaced SkRegion is not empty.
389 
390         @param rgn  SkRegion operand
391         @return     false if result is empty
392     */
op(const SkRegion & rgn,Op op)393     bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); }
394 
395     /** Replaces SkRegion with the result of rect op rgn.
396         Returns true if replaced SkRegion is not empty.
397 
398         @param rect  SkIRect operand
399         @param rgn   SkRegion operand
400         @return      false if result is empty
401 
402         example: https://fiddle.skia.org/c/@Region_op_4
403     */
404     bool op(const SkIRect& rect, const SkRegion& rgn, Op op);
405 
406     /** Replaces SkRegion with the result of rgn op rect.
407         Returns true if replaced SkRegion is not empty.
408 
409         @param rgn   SkRegion operand
410         @param rect  SkIRect operand
411         @return      false if result is empty
412 
413         example: https://fiddle.skia.org/c/@Region_op_5
414     */
415     bool op(const SkRegion& rgn, const SkIRect& rect, Op op);
416 
417     /** Replaces SkRegion with the result of rgna op rgnb.
418         Returns true if replaced SkRegion is not empty.
419 
420         @param rgna  SkRegion operand
421         @param rgnb  SkRegion operand
422         @return      false if result is empty
423 
424         example: https://fiddle.skia.org/c/@Region_op_6
425     */
426     bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op);
427 
428 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
429     /** Private. Android framework only.
430 
431         @return  string representation of SkRegion
432     */
433     char* toString();
434 #endif
435 
436     /** \class SkRegion::Iterator
437         Returns sequence of rectangles, sorted along y-axis, then x-axis, that make
438         up SkRegion.
439     */
440     class SK_API Iterator {
441     public:
442 
443         /** Initializes SkRegion::Iterator with an empty SkRegion. done() on SkRegion::Iterator
444             returns true.
445             Call reset() to initialized SkRegion::Iterator at a later time.
446 
447             @return  empty SkRegion iterator
448         */
Iterator()449         Iterator() : fRgn(nullptr), fDone(true) {}
450 
451         /** Sets SkRegion::Iterator to return elements of SkIRect array in region.
452 
453             @param region  SkRegion to iterate
454             @return        SkRegion iterator
455 
456         example: https://fiddle.skia.org/c/@Region_Iterator_copy_const_SkRegion
457         */
458         Iterator(const SkRegion& region);
459 
460         /** SkPoint SkRegion::Iterator to start of SkRegion.
461             Returns true if SkRegion was set; otherwise, returns false.
462 
463             @return  true if SkRegion was set
464 
465         example: https://fiddle.skia.org/c/@Region_Iterator_rewind
466         */
467         bool rewind();
468 
469         /** Resets iterator, using the new SkRegion.
470 
471             @param region  SkRegion to iterate
472 
473         example: https://fiddle.skia.org/c/@Region_Iterator_reset
474         */
475         void reset(const SkRegion& region);
476 
477         /** Returns true if SkRegion::Iterator is pointing to final SkIRect in SkRegion.
478 
479             @return  true if data parsing is complete
480         */
done()481         bool done() const { return fDone; }
482 
483         /** Advances SkRegion::Iterator to next SkIRect in SkRegion if it is not done.
484 
485         example: https://fiddle.skia.org/c/@Region_Iterator_next
486         */
487         void next();
488 
489         /** Returns SkIRect element in SkRegion. Does not return predictable results if SkRegion
490             is empty.
491 
492             @return  part of SkRegion as SkIRect
493         */
rect()494         const SkIRect& rect() const { return fRect; }
495 
496         /** Returns SkRegion if set; otherwise, returns nullptr.
497 
498             @return  iterated SkRegion
499         */
rgn()500         const SkRegion* rgn() const { return fRgn; }
501 
502     private:
503         const SkRegion* fRgn;
504         const SkRegion::RunType*  fRuns;
505         SkIRect         fRect = {0, 0, 0, 0};
506         bool            fDone;
507     };
508 
509     /** \class SkRegion::Cliperator
510         Returns the sequence of rectangles, sorted along y-axis, then x-axis, that make
511         up SkRegion intersected with the specified clip rectangle.
512     */
513     class SK_API Cliperator {
514     public:
515 
516         /** Sets SkRegion::Cliperator to return elements of SkIRect array in SkRegion within clip.
517 
518             @param region  SkRegion to iterate
519             @param clip    bounds of iteration
520             @return        SkRegion iterator
521 
522         example: https://fiddle.skia.org/c/@Region_Cliperator_const_SkRegion_const_SkIRect
523         */
524         Cliperator(const SkRegion& region, const SkIRect& clip);
525 
526         /** Returns true if SkRegion::Cliperator is pointing to final SkIRect in SkRegion.
527 
528             @return  true if data parsing is complete
529         */
done()530         bool done() { return fDone; }
531 
532         /** Advances iterator to next SkIRect in SkRegion contained by clip.
533 
534         example: https://fiddle.skia.org/c/@Region_Cliperator_next
535         */
536         void  next();
537 
538         /** Returns SkIRect element in SkRegion, intersected with clip passed to
539             SkRegion::Cliperator constructor. Does not return predictable results if SkRegion
540             is empty.
541 
542             @return  part of SkRegion inside clip as SkIRect
543         */
rect()544         const SkIRect& rect() const { return fRect; }
545 
546     private:
547         Iterator    fIter;
548         SkIRect     fClip;
549         SkIRect     fRect = {0, 0, 0, 0};
550         bool        fDone;
551     };
552 
553     /** \class SkRegion::Spanerator
554         Returns the line segment ends within SkRegion that intersect a horizontal line.
555     */
556     class Spanerator {
557     public:
558 
559         /** Sets SkRegion::Spanerator to return line segments in SkRegion on scan line.
560 
561             @param region  SkRegion to iterate
562             @param y       horizontal line to intersect
563             @param left    bounds of iteration
564             @param right   bounds of iteration
565             @return        SkRegion iterator
566 
567         example: https://fiddle.skia.org/c/@Region_Spanerator_const_SkRegion_int_int_int
568         */
569         Spanerator(const SkRegion& region, int y, int left, int right);
570 
571         /** Advances iterator to next span intersecting SkRegion within line segment provided
572             in constructor. Returns true if interval was found.
573 
574             @param left   pointer to span start; may be nullptr
575             @param right  pointer to span end; may be nullptr
576             @return       true if interval was found
577 
578         example: https://fiddle.skia.org/c/@Region_Spanerator_next
579         */
580         bool next(int* left, int* right);
581 
582     private:
583         const SkRegion::RunType* fRuns;
584         int     fLeft, fRight;
585         bool    fDone;
586     };
587 
588     /** Writes SkRegion to buffer, and returns number of bytes written.
589         If buffer is nullptr, returns number number of bytes that would be written.
590 
591         @param buffer  storage for binary data
592         @return        size of SkRegion
593 
594         example: https://fiddle.skia.org/c/@Region_writeToMemory
595     */
596     size_t writeToMemory(void* buffer) const;
597 
598     /** Constructs SkRegion from buffer of size length. Returns bytes read.
599         Returned value will be multiple of four or zero if length was too small.
600 
601         @param buffer  storage for binary data
602         @param length  size of buffer
603         @return        bytes read
604 
605         example: https://fiddle.skia.org/c/@Region_readFromMemory
606     */
607     size_t readFromMemory(const void* buffer, size_t length);
608 
609 private:
610     static constexpr int kOpCount = kReplace_Op + 1;
611 
612     // T
613     // [B N L R S]
614     // S
615     static constexpr int kRectRegionRuns = 7;
616 
617     struct RunHead;
618 
emptyRunHeadPtr()619     static RunHead* emptyRunHeadPtr() { return (SkRegion::RunHead*) -1; }
620     static constexpr RunHead* kRectRunHeadPtr = nullptr;
621 
622     // allocate space for count runs
623     void allocateRuns(int count);
624     void allocateRuns(int count, int ySpanCount, int intervalCount);
625     void allocateRuns(const RunHead& src);
626 
627     SkDEBUGCODE(void dump() const;)
628 
629     SkIRect     fBounds;
630     RunHead*    fRunHead;
631 
632     void freeRuns();
633 
634     /**
635      *  Return the runs from this region, consing up fake runs if the region
636      *  is empty or a rect. In those 2 cases, we use tmpStorage to hold the
637      *  run data.
638      */
639     const RunType*  getRuns(RunType tmpStorage[], int* intervals) const;
640 
641     // This is called with runs[] that do not yet have their interval-count
642     // field set on each scanline. That is computed as part of this call
643     // (inside ComputeRunBounds).
644     bool setRuns(RunType runs[], int count);
645 
646     int count_runtype_values(int* itop, int* ibot) const;
647 
648     bool isValid() const;
649 
650     static void BuildRectRuns(const SkIRect& bounds,
651                               RunType runs[kRectRegionRuns]);
652 
653     // If the runs define a simple rect, return true and set bounds to that
654     // rect. If not, return false and ignore bounds.
655     static bool RunsAreARect(const SkRegion::RunType runs[], int count,
656                              SkIRect* bounds);
657 
658     /**
659      *  If the last arg is null, just return if the result is non-empty,
660      *  else store the result in the last arg.
661      */
662     static bool Oper(const SkRegion&, const SkRegion&, SkRegion::Op, SkRegion*);
663 
664     friend struct RunHead;
665     friend class Iterator;
666     friend class Spanerator;
667     friend class SkRegionPriv;
668     friend class SkRgnBuilder;
669     friend class SkFlatRegion;
670 };
671 
672 #endif
673