• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #ifndef SkRect_DEFINED
11 #define SkRect_DEFINED
12 
13 #include "SkPoint.h"
14 #include "SkSize.h"
15 
16 /** \struct SkIRect
17 
18     SkIRect holds four 32 bit integer coordinates for a rectangle
19 */
20 struct SK_API SkIRect {
21     int32_t fLeft, fTop, fRight, fBottom;
22 
MakeEmptySkIRect23     static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
24         SkIRect r;
25         r.setEmpty();
26         return r;
27     }
28 
MakeLargestSkIRect29     static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
30         SkIRect r;
31         r.setLargest();
32         return r;
33     }
34 
MakeWHSkIRect35     static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
36         SkIRect r;
37         r.set(0, 0, w, h);
38         return r;
39     }
40 
MakeSizeSkIRect41     static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
42         SkIRect r;
43         r.set(0, 0, size.width(), size.height());
44         return r;
45     }
46 
MakeLTRBSkIRect47     static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
48         SkIRect rect;
49         rect.set(l, t, r, b);
50         return rect;
51     }
52 
MakeXYWHSkIRect53     static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
54         SkIRect r;
55         r.set(x, y, x + w, y + h);
56         return r;
57     }
58 
leftSkIRect59     int left() const { return fLeft; }
topSkIRect60     int top() const { return fTop; }
rightSkIRect61     int right() const { return fRight; }
bottomSkIRect62     int bottom() const { return fBottom; }
63 
64     /** return the left edge of the rect */
xSkIRect65     int x() const { return fLeft; }
66     /** return the top edge of the rect */
ySkIRect67     int y() const { return fTop; }
68     /**
69      *  Returns the rectangle's width. This does not check for a valid rect
70      *  (i.e. left <= right) so the result may be negative.
71      */
widthSkIRect72     int width() const { return fRight - fLeft; }
73 
74     /**
75      *  Returns the rectangle's height. This does not check for a valid rect
76      *  (i.e. top <= bottom) so the result may be negative.
77      */
heightSkIRect78     int height() const { return fBottom - fTop; }
79 
80     /**
81      *  Since the center of an integer rect may fall on a factional value, this
82      *  method is defined to return (right + left) >> 1.
83      *
84      *  This is a specific "truncation" of the average, which is different than
85      *  (right + left) / 2 when the sum is negative.
86      */
centerXSkIRect87     int centerX() const { return (fRight + fLeft) >> 1; }
88 
89     /**
90      *  Since the center of an integer rect may fall on a factional value, this
91      *  method is defined to return (bottom + top) >> 1
92      *
93      *  This is a specific "truncation" of the average, which is different than
94      *  (bottom + top) / 2 when the sum is negative.
95      */
centerYSkIRect96     int centerY() const { return (fBottom + fTop) >> 1; }
97 
98     /**
99      *  Return true if the rectangle's width or height are <= 0
100      */
isEmptySkIRect101     bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
102 
isLargestSkIRect103     bool isLargest() const { return SK_MinS32 == fLeft &&
104                                     SK_MinS32 == fTop &&
105                                     SK_MaxS32 == fRight &&
106                                     SK_MaxS32 == fBottom; }
107 
108     friend bool operator==(const SkIRect& a, const SkIRect& b) {
109         return !memcmp(&a, &b, sizeof(a));
110     }
111 
112     friend bool operator!=(const SkIRect& a, const SkIRect& b) {
113         return !(a == b);
114     }
115 
is16BitSkIRect116     bool is16Bit() const {
117         return  SkIsS16(fLeft) && SkIsS16(fTop) &&
118                 SkIsS16(fRight) && SkIsS16(fBottom);
119     }
120 
121     /** Set the rectangle to (0,0,0,0)
122     */
setEmptySkIRect123     void setEmpty() { memset(this, 0, sizeof(*this)); }
124 
setSkIRect125     void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
126         fLeft   = left;
127         fTop    = top;
128         fRight  = right;
129         fBottom = bottom;
130     }
131     // alias for set(l, t, r, b)
setLTRBSkIRect132     void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
133         this->set(left, top, right, bottom);
134     }
135 
setXYWHSkIRect136     void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
137         fLeft = x;
138         fTop = y;
139         fRight = x + width;
140         fBottom = y + height;
141     }
142 
143     /**
144      *  Make the largest representable rectangle
145      */
setLargestSkIRect146     void setLargest() {
147         fLeft = fTop = SK_MinS32;
148         fRight = fBottom = SK_MaxS32;
149     }
150 
151     /**
152      *  Make the largest representable rectangle, but inverted (e.g. fLeft will
153      *  be max 32bit and right will be min 32bit).
154      */
setLargestInvertedSkIRect155     void setLargestInverted() {
156         fLeft = fTop = SK_MaxS32;
157         fRight = fBottom = SK_MinS32;
158     }
159 
160     /** Offset set the rectangle by adding dx to its left and right,
161         and adding dy to its top and bottom.
162     */
offsetSkIRect163     void offset(int32_t dx, int32_t dy) {
164         fLeft   += dx;
165         fTop    += dy;
166         fRight  += dx;
167         fBottom += dy;
168     }
169 
offsetSkIRect170     void offset(const SkIPoint& delta) {
171         this->offset(delta.fX, delta.fY);
172     }
173 
174     /**
175      *  Offset this rect such its new x() and y() will equal newX and newY.
176      */
offsetToSkIRect177     void offsetTo(int32_t newX, int32_t newY) {
178         fRight += newX - fLeft;
179         fBottom += newY - fTop;
180         fLeft = newX;
181         fTop = newY;
182     }
183 
184     /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
185         making the rectangle narrower. If dx is negative, then the sides are moved outwards,
186         making the rectangle wider. The same holds true for dy and the top and bottom.
187     */
insetSkIRect188     void inset(int32_t dx, int32_t dy) {
189         fLeft   += dx;
190         fTop    += dy;
191         fRight  -= dx;
192         fBottom -= dy;
193     }
194 
195    /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
196        moved outwards, making the rectangle wider. If dx is negative, then the
197        sides are moved inwards, making the rectangle narrower. The same holds
198        true for dy and the top and bottom.
199     */
outsetSkIRect200     void outset(int32_t dx, int32_t dy)  { this->inset(-dx, -dy); }
201 
quickRejectSkIRect202     bool quickReject(int l, int t, int r, int b) const {
203         return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
204     }
205 
206     /** Returns true if (x,y) is inside the rectangle and the rectangle is not
207         empty. The left and top are considered to be inside, while the right
208         and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
209         points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
210     */
containsSkIRect211     bool contains(int32_t x, int32_t y) const {
212         return  (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
213                 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
214     }
215 
216     /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
217         If either rectangle is empty, contains() returns false.
218     */
containsSkIRect219     bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
220         return  left < right && top < bottom && !this->isEmpty() && // check for empties
221                 fLeft <= left && fTop <= top &&
222                 fRight >= right && fBottom >= bottom;
223     }
224 
225     /** Returns true if the specified rectangle r is inside or equal to this rectangle.
226     */
containsSkIRect227     bool contains(const SkIRect& r) const {
228         return  !r.isEmpty() && !this->isEmpty() &&     // check for empties
229                 fLeft <= r.fLeft && fTop <= r.fTop &&
230                 fRight >= r.fRight && fBottom >= r.fBottom;
231     }
232 
233     /** Return true if this rectangle contains the specified rectangle.
234         For speed, this method does not check if either this or the specified
235         rectangles are empty, and if either is, its return value is undefined.
236         In the debugging build however, we assert that both this and the
237         specified rectangles are non-empty.
238     */
containsNoEmptyCheckSkIRect239     bool containsNoEmptyCheck(int32_t left, int32_t top,
240                               int32_t right, int32_t bottom) const {
241         SkASSERT(fLeft < fRight && fTop < fBottom);
242         SkASSERT(left < right && top < bottom);
243 
244         return fLeft <= left && fTop <= top &&
245                fRight >= right && fBottom >= bottom;
246     }
247 
containsNoEmptyCheckSkIRect248     bool containsNoEmptyCheck(const SkIRect& r) const {
249         return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
250     }
251 
252     /** If r intersects this rectangle, return true and set this rectangle to that
253         intersection, otherwise return false and do not change this rectangle.
254         If either rectangle is empty, do nothing and return false.
255     */
intersectSkIRect256     bool intersect(const SkIRect& r) {
257         SkASSERT(&r);
258         return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
259     }
260 
261     /** If rectangles a and b intersect, return true and set this rectangle to
262         that intersection, otherwise return false and do not change this
263         rectangle. If either rectangle is empty, do nothing and return false.
264     */
intersectSkIRect265     bool intersect(const SkIRect& a, const SkIRect& b) {
266         SkASSERT(&a && &b);
267 
268         if (!a.isEmpty() && !b.isEmpty() &&
269                 a.fLeft < b.fRight && b.fLeft < a.fRight &&
270                 a.fTop < b.fBottom && b.fTop < a.fBottom) {
271             fLeft   = SkMax32(a.fLeft,   b.fLeft);
272             fTop    = SkMax32(a.fTop,    b.fTop);
273             fRight  = SkMin32(a.fRight,  b.fRight);
274             fBottom = SkMin32(a.fBottom, b.fBottom);
275             return true;
276         }
277         return false;
278     }
279 
280     /** If rectangles a and b intersect, return true and set this rectangle to
281         that intersection, otherwise return false and do not change this
282         rectangle. For speed, no check to see if a or b are empty is performed.
283         If either is, then the return result is undefined. In the debug build,
284         we assert that both rectangles are non-empty.
285     */
intersectNoEmptyCheckSkIRect286     bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
287         SkASSERT(&a && &b);
288         SkASSERT(!a.isEmpty() && !b.isEmpty());
289 
290         if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
291                 a.fTop < b.fBottom && b.fTop < a.fBottom) {
292             fLeft   = SkMax32(a.fLeft,   b.fLeft);
293             fTop    = SkMax32(a.fTop,    b.fTop);
294             fRight  = SkMin32(a.fRight,  b.fRight);
295             fBottom = SkMin32(a.fBottom, b.fBottom);
296             return true;
297         }
298         return false;
299     }
300 
301     /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
302         return true and set this rectangle to that intersection,
303         otherwise return false and do not change this rectangle.
304         If either rectangle is empty, do nothing and return false.
305     */
intersectSkIRect306     bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
307         if (left < right && top < bottom && !this->isEmpty() &&
308                 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
309             if (fLeft < left) fLeft = left;
310             if (fTop < top) fTop = top;
311             if (fRight > right) fRight = right;
312             if (fBottom > bottom) fBottom = bottom;
313             return true;
314         }
315         return false;
316     }
317 
318     /** Returns true if a and b are not empty, and they intersect
319      */
IntersectsSkIRect320     static bool Intersects(const SkIRect& a, const SkIRect& b) {
321         return  !a.isEmpty() && !b.isEmpty() &&              // check for empties
322         a.fLeft < b.fRight && b.fLeft < a.fRight &&
323         a.fTop < b.fBottom && b.fTop < a.fBottom;
324     }
325 
326     /**
327      *  Returns true if a and b intersect. debug-asserts that neither are empty.
328      */
IntersectsNoEmptyCheckSkIRect329     static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
330         SkASSERT(!a.isEmpty());
331         SkASSERT(!b.isEmpty());
332         return  a.fLeft < b.fRight && b.fLeft < a.fRight &&
333                 a.fTop < b.fBottom && b.fTop < a.fBottom;
334     }
335 
336     /** Update this rectangle to enclose itself and the specified rectangle.
337         If this rectangle is empty, just set it to the specified rectangle. If the specified
338         rectangle is empty, do nothing.
339     */
340     void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
341 
342     /** Update this rectangle to enclose itself and the specified rectangle.
343         If this rectangle is empty, just set it to the specified rectangle. If the specified
344         rectangle is empty, do nothing.
345     */
joinSkIRect346     void join(const SkIRect& r) {
347         this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
348     }
349 
350     /** Swap top/bottom or left/right if there are flipped.
351         This can be called if the edges are computed separately,
352         and may have crossed over each other.
353         When this returns, left <= right && top <= bottom
354     */
355     void sort();
356 
EmptyIRectSkIRect357     static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
358         static const SkIRect gEmpty = { 0, 0, 0, 0 };
359         return gEmpty;
360     }
361 };
362 
363 /** \struct SkRect
364 */
365 struct SK_API SkRect {
366     SkScalar    fLeft, fTop, fRight, fBottom;
367 
MakeEmptySkRect368     static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
369         SkRect r;
370         r.setEmpty();
371         return r;
372     }
373 
MakeLargestSkRect374     static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
375         SkRect r;
376         r.setLargest();
377         return r;
378     }
379 
MakeWHSkRect380     static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
381         SkRect r;
382         r.set(0, 0, w, h);
383         return r;
384     }
385 
MakeSizeSkRect386     static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
387         SkRect r;
388         r.set(0, 0, size.width(), size.height());
389         return r;
390     }
391 
MakeLTRBSkRect392     static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
393         SkRect rect;
394         rect.set(l, t, r, b);
395         return rect;
396     }
397 
MakeXYWHSkRect398     static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
399         SkRect r;
400         r.set(x, y, x + w, y + h);
401         return r;
402     }
403 
404     SK_ATTR_DEPRECATED("use Make()")
MakeFromIRectSkRect405     static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
406         SkRect r;
407         r.set(SkIntToScalar(irect.fLeft),
408               SkIntToScalar(irect.fTop),
409               SkIntToScalar(irect.fRight),
410               SkIntToScalar(irect.fBottom));
411         return r;
412     }
413 
MakeSkRect414     static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
415         SkRect r;
416         r.set(SkIntToScalar(irect.fLeft),
417               SkIntToScalar(irect.fTop),
418               SkIntToScalar(irect.fRight),
419               SkIntToScalar(irect.fBottom));
420         return r;
421     }
422 
423     /**
424      *  Return true if the rectangle's width or height are <= 0
425      */
isEmptySkRect426     bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
427 
isLargestSkRect428     bool isLargest() const { return SK_ScalarMin == fLeft &&
429                                     SK_ScalarMin == fTop &&
430                                     SK_ScalarMax == fRight &&
431                                     SK_ScalarMax == fBottom; }
432 
433     /**
434      *  Returns true iff all values in the rect are finite. If any are
435      *  infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
436      *  returns false.
437      */
isFiniteSkRect438     bool isFinite() const {
439 #ifdef SK_SCALAR_IS_FLOAT
440         float accum = 0;
441         accum *= fLeft;
442         accum *= fTop;
443         accum *= fRight;
444         accum *= fBottom;
445 
446         // accum is either NaN or it is finite (zero).
447         SkASSERT(0 == accum || !(accum == accum));
448 
449         // value==value will be true iff value is not NaN
450         // TODO: is it faster to say !accum or accum==accum?
451         return accum == accum;
452 #else
453         // use bit-or for speed, since we don't care about short-circuting the
454         // tests, and we expect the common case will be that we need to check all.
455         int isNaN = (SK_FixedNaN == fLeft)  | (SK_FixedNaN == fTop) |
456                     (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
457         return !isNaN;
458 #endif
459     }
460 
xSkRect461     SkScalar    x() const { return fLeft; }
ySkRect462     SkScalar    y() const { return fTop; }
leftSkRect463     SkScalar    left() const { return fLeft; }
topSkRect464     SkScalar    top() const { return fTop; }
rightSkRect465     SkScalar    right() const { return fRight; }
bottomSkRect466     SkScalar    bottom() const { return fBottom; }
widthSkRect467     SkScalar    width() const { return fRight - fLeft; }
heightSkRect468     SkScalar    height() const { return fBottom - fTop; }
centerXSkRect469     SkScalar    centerX() const { return SkScalarHalf(fLeft + fRight); }
centerYSkRect470     SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
471 
472     friend bool operator==(const SkRect& a, const SkRect& b) {
473         return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
474     }
475 
476     friend bool operator!=(const SkRect& a, const SkRect& b) {
477         return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
478     }
479 
480     /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
481         bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
482      */
483     void toQuad(SkPoint quad[4]) const;
484 
485     /** Set this rectangle to the empty rectangle (0,0,0,0)
486     */
setEmptySkRect487     void setEmpty() { memset(this, 0, sizeof(*this)); }
488 
setSkRect489     void set(const SkIRect& src) {
490         fLeft   = SkIntToScalar(src.fLeft);
491         fTop    = SkIntToScalar(src.fTop);
492         fRight  = SkIntToScalar(src.fRight);
493         fBottom = SkIntToScalar(src.fBottom);
494     }
495 
setSkRect496     void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
497         fLeft   = left;
498         fTop    = top;
499         fRight  = right;
500         fBottom = bottom;
501     }
502     // alias for set(l, t, r, b)
setLTRBSkRect503     void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
504         this->set(left, top, right, bottom);
505     }
506 
507     /** Initialize the rect with the 4 specified integers. The routine handles
508         converting them to scalars (by calling SkIntToScalar)
509      */
isetSkRect510     void iset(int left, int top, int right, int bottom) {
511         fLeft   = SkIntToScalar(left);
512         fTop    = SkIntToScalar(top);
513         fRight  = SkIntToScalar(right);
514         fBottom = SkIntToScalar(bottom);
515     }
516 
517     /**
518      *  Set this rectangle to be left/top at 0,0, and have the specified width
519      *  and height (automatically converted to SkScalar).
520      */
isetWHSkRect521     void isetWH(int width, int height) {
522         fLeft = fTop = 0;
523         fRight = SkIntToScalar(width);
524         fBottom = SkIntToScalar(height);
525     }
526 
527     /** Set this rectangle to be the bounds of the array of points.
528         If the array is empty (count == 0), then set this rectangle
529         to the empty rectangle (0,0,0,0)
530     */
setSkRect531     void set(const SkPoint pts[], int count) {
532         // set() had been checking for non-finite values, so keep that behavior
533         // for now. Now that we have setBoundsCheck(), we may decide to make
534         // set() be simpler/faster, and not check for those.
535         (void)this->setBoundsCheck(pts, count);
536     }
537 
538     // alias for set(pts, count)
setBoundsSkRect539     void setBounds(const SkPoint pts[], int count) {
540         (void)this->setBoundsCheck(pts, count);
541     }
542 
543     /**
544      *  Compute the bounds of the array of points, and set this rect to that
545      *  bounds and return true... unless a non-finite value is encountered,
546      *  in which case this rect is set to empty and false is returned.
547      */
548     bool setBoundsCheck(const SkPoint pts[], int count);
549 
setSkRect550     void set(const SkPoint& p0, const SkPoint& p1) {
551         fLeft =   SkMinScalar(p0.fX, p1.fX);
552         fRight =  SkMaxScalar(p0.fX, p1.fX);
553         fTop =    SkMinScalar(p0.fY, p1.fY);
554         fBottom = SkMaxScalar(p0.fY, p1.fY);
555     }
556 
setXYWHSkRect557     void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
558         fLeft = x;
559         fTop = y;
560         fRight = x + width;
561         fBottom = y + height;
562     }
563 
setWHSkRect564     void setWH(SkScalar width, SkScalar height) {
565         fLeft = 0;
566         fTop = 0;
567         fRight = width;
568         fBottom = height;
569     }
570 
571     /**
572      *  Make the largest representable rectangle
573      */
setLargestSkRect574     void setLargest() {
575         fLeft = fTop = SK_ScalarMin;
576         fRight = fBottom = SK_ScalarMax;
577     }
578 
579     /**
580      *  Make the largest representable rectangle, but inverted (e.g. fLeft will
581      *  be max and right will be min).
582      */
setLargestInvertedSkRect583     void setLargestInverted() {
584         fLeft = fTop = SK_ScalarMax;
585         fRight = fBottom = SK_ScalarMin;
586     }
587 
588     /** Offset set the rectangle by adding dx to its left and right,
589         and adding dy to its top and bottom.
590     */
offsetSkRect591     void offset(SkScalar dx, SkScalar dy) {
592         fLeft   += dx;
593         fTop    += dy;
594         fRight  += dx;
595         fBottom += dy;
596     }
597 
offsetSkRect598     void offset(const SkPoint& delta) {
599         this->offset(delta.fX, delta.fY);
600     }
601 
602     /**
603      *  Offset this rect such its new x() and y() will equal newX and newY.
604      */
offsetToSkRect605     void offsetTo(SkScalar newX, SkScalar newY) {
606         fRight += newX - fLeft;
607         fBottom += newY - fTop;
608         fLeft = newX;
609         fTop = newY;
610     }
611 
612     /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
613         moved inwards, making the rectangle narrower. If dx is negative, then
614         the sides are moved outwards, making the rectangle wider. The same holds
615          true for dy and the top and bottom.
616     */
insetSkRect617     void inset(SkScalar dx, SkScalar dy)  {
618         fLeft   += dx;
619         fTop    += dy;
620         fRight  -= dx;
621         fBottom -= dy;
622     }
623 
624    /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
625        moved outwards, making the rectangle wider. If dx is negative, then the
626        sides are moved inwards, making the rectangle narrower. The same holds
627        true for dy and the top and bottom.
628     */
outsetSkRect629     void outset(SkScalar dx, SkScalar dy)  { this->inset(-dx, -dy); }
630 
631     /** If this rectangle intersects r, return true and set this rectangle to that
632         intersection, otherwise return false and do not change this rectangle.
633         If either rectangle is empty, do nothing and return false.
634     */
635     bool intersect(const SkRect& r);
636 
637     /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
638         return true and set this rectangle to that intersection, otherwise return false
639         and do not change this rectangle.
640         If either rectangle is empty, do nothing and return false.
641     */
642     bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
643 
644     /**
645      *  Return true if this rectangle is not empty, and the specified sides of
646      *  a rectangle are not empty, and they intersect.
647      */
intersectsSkRect648     bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
649         return // first check that both are not empty
650                left < right && top < bottom &&
651                fLeft < fRight && fTop < fBottom &&
652                // now check for intersection
653                fLeft < right && left < fRight &&
654                fTop < bottom && top < fBottom;
655     }
656 
657     /** If rectangles a and b intersect, return true and set this rectangle to
658      *  that intersection, otherwise return false and do not change this
659      *  rectangle. If either rectangle is empty, do nothing and return false.
660      */
661     bool intersect(const SkRect& a, const SkRect& b);
662 
663     /**
664      *  Return true if rectangles a and b are not empty and intersect.
665      */
IntersectsSkRect666     static bool Intersects(const SkRect& a, const SkRect& b) {
667         return  !a.isEmpty() && !b.isEmpty() &&
668                 a.fLeft < b.fRight && b.fLeft < a.fRight &&
669                 a.fTop < b.fBottom && b.fTop < a.fBottom;
670     }
671 
672     /**
673      *  Update this rectangle to enclose itself and the specified rectangle.
674      *  If this rectangle is empty, just set it to the specified rectangle.
675      *  If the specified rectangle is empty, do nothing.
676      */
677     void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
678 
679     /** Update this rectangle to enclose itself and the specified rectangle.
680         If this rectangle is empty, just set it to the specified rectangle. If the specified
681         rectangle is empty, do nothing.
682     */
joinSkRect683     void join(const SkRect& r) {
684         this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
685     }
686     // alias for join()
growToIncludeSkRect687     void growToInclude(const SkRect& r) { this->join(r); }
688 
689     /**
690      *  Grow the rect to include the specified (x,y). After this call, the
691      *  following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
692      *
693      *  This is close, but not quite the same contract as contains(), since
694      *  contains() treats the left and top different from the right and bottom.
695      *  contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
696      *  that contains(x,y) always returns false if the rect is empty.
697      */
growToIncludeSkRect698     void growToInclude(SkScalar x, SkScalar y) {
699         fLeft  = SkMinScalar(x, fLeft);
700         fRight = SkMaxScalar(x, fRight);
701         fTop    = SkMinScalar(y, fTop);
702         fBottom = SkMaxScalar(y, fBottom);
703     }
704 
705     /** Bulk version of growToInclude */
growToIncludeSkRect706     void growToInclude(const SkPoint pts[], int count) {
707         this->growToInclude(pts, sizeof(SkPoint), count);
708     }
709 
710     /** Bulk version of growToInclude with stride. */
growToIncludeSkRect711     void growToInclude(const SkPoint pts[], size_t stride, int count) {
712         SkASSERT(count >= 0);
713         SkASSERT(stride >= sizeof(SkPoint));
714         const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
715         for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
716             this->growToInclude(pts->fX, pts->fY);
717         }
718     }
719 
720     /**
721      *  Return true if this rectangle contains r, and if both rectangles are
722      *  not empty.
723      */
containsSkRect724     bool contains(const SkRect& r) const {
725         // todo: can we eliminate the this->isEmpty check?
726         return  !r.isEmpty() && !this->isEmpty() &&
727                 fLeft <= r.fLeft && fTop <= r.fTop &&
728                 fRight >= r.fRight && fBottom >= r.fBottom;
729     }
730 
731     /**
732      *  Set the dst rectangle by rounding this rectangle's coordinates to their
733      *  nearest integer values using SkScalarRoundToInt.
734      */
roundSkRect735     void round(SkIRect* dst) const {
736         SkASSERT(dst);
737         dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
738                  SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
739     }
740 
741     /**
742      *  Set the dst rectangle by rounding "out" this rectangle, choosing the
743      *  SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
744      */
roundOutSkRect745     void roundOut(SkIRect* dst) const {
746         SkASSERT(dst);
747         dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
748                  SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
749     }
750 
751     /**
752      *  Expand this rectangle by rounding its coordinates "out", choosing the
753      *  floor of top and left, and the ceil of right and bottom. If this rect
754      *  is already on integer coordinates, then it will be unchanged.
755      */
roundOutSkRect756     void roundOut() {
757         this->set(SkScalarFloorToScalar(fLeft),
758                   SkScalarFloorToScalar(fTop),
759                   SkScalarCeilToScalar(fRight),
760                   SkScalarCeilToScalar(fBottom));
761     }
762 
763     /**
764      *  Set the dst rectangle by rounding "in" this rectangle, choosing the
765      *  ceil of top and left, and the floor of right and bottom. This does *not*
766      *  call sort(), so it is possible that the resulting rect is inverted...
767      *  e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
768      */
roundInSkRect769     void roundIn(SkIRect* dst) const {
770         SkASSERT(dst);
771         dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
772                  SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
773     }
774 
775     /**
776      *  Return a new SkIRect which is contains the rounded coordinates of this
777      *  rect using SkScalarRoundToInt.
778      */
roundSkRect779     SkIRect round() const {
780         SkIRect ir;
781         this->round(&ir);
782         return ir;
783     }
784 
785     /**
786      *  Swap top/bottom or left/right if there are flipped (i.e. if width()
787      *  or height() would have returned a negative value.) This should be called
788      *  if the edges are computed separately, and may have crossed over each
789      *  other. When this returns, left <= right && top <= bottom
790      */
791     void sort();
792 
793     /**
794      *  cast-safe way to treat the rect as an array of (4) SkScalars.
795      */
asScalarsSkRect796     const SkScalar* asScalars() const { return &fLeft; }
797 };
798 
799 #endif
800