SkRect.h revision b8bd6cbbcde9846094ade18cafadfad46dc00889
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*/
20struct SK_API SkIRect {
21    int32_t fLeft, fTop, fRight, fBottom;
22
23    static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
24        SkIRect r;
25        r.setEmpty();
26        return r;
27    }
28
29    static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
30        SkIRect r;
31        r.setLargest();
32        return r;
33    }
34
35    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
41    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
47    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
53    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
59    int left() const { return fLeft; }
60    int top() const { return fTop; }
61    int right() const { return fRight; }
62    int bottom() const { return fBottom; }
63
64    /** return the left edge of the rect */
65    int x() const { return fLeft; }
66    /** return the top edge of the rect */
67    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     */
72    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     */
78    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     */
87    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     */
96    int centerY() const { return (fBottom + fTop) >> 1; }
97
98    /**
99     *  Return true if the rectangle's width or height are <= 0
100     */
101    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
102
103    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
116    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    */
123    void setEmpty() { memset(this, 0, sizeof(*this)); }
124
125    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)
132    void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
133        this->set(left, top, right, bottom);
134    }
135
136    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     */
146    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     */
155    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    */
163    void offset(int32_t dx, int32_t dy) {
164        fLeft   += dx;
165        fTop    += dy;
166        fRight  += dx;
167        fBottom += dy;
168    }
169
170    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     */
177    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    */
188    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    */
200    void outset(int32_t dx, int32_t dy)  { this->inset(-dx, -dy); }
201
202    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    */
211    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    */
219    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    */
227    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    */
239    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
248    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    */
256    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    */
265    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    */
286    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    */
306    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     */
320    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     */
329    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    */
346    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
357    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*/
365struct SK_API SkRect {
366    SkScalar    fLeft, fTop, fRight, fBottom;
367
368    static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
369        SkRect r;
370        r.setEmpty();
371        return r;
372    }
373
374    static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
375        SkRect r;
376        r.set(0, 0, w, h);
377        return r;
378    }
379
380    static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
381        SkRect r;
382        r.set(0, 0, size.width(), size.height());
383        return r;
384    }
385
386    static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
387        SkRect rect;
388        rect.set(l, t, r, b);
389        return rect;
390    }
391
392    static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
393        SkRect r;
394        r.set(x, y, x + w, y + h);
395        return r;
396    }
397
398    // DEPRECATED: call Make(r)
399    static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
400        SkRect r;
401        r.set(SkIntToScalar(irect.fLeft),
402              SkIntToScalar(irect.fTop),
403              SkIntToScalar(irect.fRight),
404              SkIntToScalar(irect.fBottom));
405        return r;
406    }
407
408    static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
409        SkRect r;
410        r.set(SkIntToScalar(irect.fLeft),
411              SkIntToScalar(irect.fTop),
412              SkIntToScalar(irect.fRight),
413              SkIntToScalar(irect.fBottom));
414        return r;
415    }
416
417    /**
418     *  Return true if the rectangle's width or height are <= 0
419     */
420    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
421
422    /**
423     *  Returns true iff all values in the rect are finite. If any are
424     *  infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
425     *  returns false.
426     */
427    bool isFinite() const {
428#ifdef SK_SCALAR_IS_FLOAT
429        float accum = 0;
430        accum *= fLeft;
431        accum *= fTop;
432        accum *= fRight;
433        accum *= fBottom;
434
435        // accum is either NaN or it is finite (zero).
436        SkASSERT(0 == accum || !(accum == accum));
437
438        // value==value will be true iff value is not NaN
439        // TODO: is it faster to say !accum or accum==accum?
440        return accum == accum;
441#else
442        // use bit-or for speed, since we don't care about short-circuting the
443        // tests, and we expect the common case will be that we need to check all.
444        int isNaN = (SK_FixedNaN == fLeft)  | (SK_FixedNaN == fTop) |
445                    (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
446        return !isNaN;
447#endif
448    }
449
450    SkScalar    x() const { return fLeft; }
451    SkScalar    y() const { return fTop; }
452    SkScalar    left() const { return fLeft; }
453    SkScalar    top() const { return fTop; }
454    SkScalar    right() const { return fRight; }
455    SkScalar    bottom() const { return fBottom; }
456    SkScalar    width() const { return fRight - fLeft; }
457    SkScalar    height() const { return fBottom - fTop; }
458    SkScalar    centerX() const { return SkScalarHalf(fLeft + fRight); }
459    SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
460
461    friend bool operator==(const SkRect& a, const SkRect& b) {
462        return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
463    }
464
465    friend bool operator!=(const SkRect& a, const SkRect& b) {
466        return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
467    }
468
469    /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
470        bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
471     */
472    void toQuad(SkPoint quad[4]) const;
473
474    /** Set this rectangle to the empty rectangle (0,0,0,0)
475    */
476    void setEmpty() { memset(this, 0, sizeof(*this)); }
477
478    void set(const SkIRect& src) {
479        fLeft   = SkIntToScalar(src.fLeft);
480        fTop    = SkIntToScalar(src.fTop);
481        fRight  = SkIntToScalar(src.fRight);
482        fBottom = SkIntToScalar(src.fBottom);
483    }
484
485    void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
486        fLeft   = left;
487        fTop    = top;
488        fRight  = right;
489        fBottom = bottom;
490    }
491    // alias for set(l, t, r, b)
492    void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
493        this->set(left, top, right, bottom);
494    }
495
496    /** Initialize the rect with the 4 specified integers. The routine handles
497        converting them to scalars (by calling SkIntToScalar)
498     */
499    void iset(int left, int top, int right, int bottom) {
500        fLeft   = SkIntToScalar(left);
501        fTop    = SkIntToScalar(top);
502        fRight  = SkIntToScalar(right);
503        fBottom = SkIntToScalar(bottom);
504    }
505
506    /**
507     *  Set this rectangle to be left/top at 0,0, and have the specified width
508     *  and height (automatically converted to SkScalar).
509     */
510    void isetWH(int width, int height) {
511        fLeft = fTop = 0;
512        fRight = SkIntToScalar(width);
513        fBottom = SkIntToScalar(height);
514    }
515
516    /** Set this rectangle to be the bounds of the array of points.
517        If the array is empty (count == 0), then set this rectangle
518        to the empty rectangle (0,0,0,0)
519    */
520    void set(const SkPoint pts[], int count) {
521        // set() had been checking for non-finite values, so keep that behavior
522        // for now. Now that we have setBoundsCheck(), we may decide to make
523        // set() be simpler/faster, and not check for those.
524        (void)this->setBoundsCheck(pts, count);
525    }
526
527    // alias for set(pts, count)
528    void setBounds(const SkPoint pts[], int count) {
529        (void)this->setBoundsCheck(pts, count);
530    }
531
532    /**
533     *  Compute the bounds of the array of points, and set this rect to that
534     *  bounds and return true... unless a non-finite value is encountered,
535     *  in which case this rect is set to empty and false is returned.
536     */
537    bool setBoundsCheck(const SkPoint pts[], int count);
538
539    void set(const SkPoint& p0, const SkPoint& p1) {
540        fLeft =   SkMinScalar(p0.fX, p1.fX);
541        fRight =  SkMaxScalar(p0.fX, p1.fX);
542        fTop =    SkMinScalar(p0.fY, p1.fY);
543        fBottom = SkMaxScalar(p0.fY, p1.fY);
544    }
545
546    void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
547        fLeft = x;
548        fTop = y;
549        fRight = x + width;
550        fBottom = y + height;
551    }
552
553    void setWH(SkScalar width, SkScalar height) {
554        fLeft = 0;
555        fTop = 0;
556        fRight = width;
557        fBottom = height;
558    }
559
560    /**
561     *  Make the largest representable rectangle
562     */
563    void setLargest() {
564        fLeft = fTop = SK_ScalarMin;
565        fRight = fBottom = SK_ScalarMax;
566    }
567
568    /**
569     *  Make the largest representable rectangle, but inverted (e.g. fLeft will
570     *  be max and right will be min).
571     */
572    void setLargestInverted() {
573        fLeft = fTop = SK_ScalarMax;
574        fRight = fBottom = SK_ScalarMin;
575    }
576
577    /** Offset set the rectangle by adding dx to its left and right,
578        and adding dy to its top and bottom.
579    */
580    void offset(SkScalar dx, SkScalar dy) {
581        fLeft   += dx;
582        fTop    += dy;
583        fRight  += dx;
584        fBottom += dy;
585    }
586
587    void offset(const SkPoint& delta) {
588        this->offset(delta.fX, delta.fY);
589    }
590
591    /**
592     *  Offset this rect such its new x() and y() will equal newX and newY.
593     */
594    void offsetTo(SkScalar newX, SkScalar newY) {
595        fRight += newX - fLeft;
596        fBottom += newY - fTop;
597        fLeft = newX;
598        fTop = newY;
599    }
600
601    /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
602        moved inwards, making the rectangle narrower. If dx is negative, then
603        the sides are moved outwards, making the rectangle wider. The same holds
604         true for dy and the top and bottom.
605    */
606    void inset(SkScalar dx, SkScalar dy)  {
607        fLeft   += dx;
608        fTop    += dy;
609        fRight  -= dx;
610        fBottom -= dy;
611    }
612
613   /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
614       moved outwards, making the rectangle wider. If dx is negative, then the
615       sides are moved inwards, making the rectangle narrower. The same holds
616       true for dy and the top and bottom.
617    */
618    void outset(SkScalar dx, SkScalar dy)  { this->inset(-dx, -dy); }
619
620    /** If this rectangle intersects r, return true and set this rectangle to that
621        intersection, otherwise return false and do not change this rectangle.
622        If either rectangle is empty, do nothing and return false.
623    */
624    bool intersect(const SkRect& r);
625
626    /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
627        return true and set this rectangle to that intersection, otherwise return false
628        and do not change this rectangle.
629        If either rectangle is empty, do nothing and return false.
630    */
631    bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
632
633    /**
634     *  Return true if this rectangle is not empty, and the specified sides of
635     *  a rectangle are not empty, and they intersect.
636     */
637    bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
638        return // first check that both are not empty
639               left < right && top < bottom &&
640               fLeft < fRight && fTop < fBottom &&
641               // now check for intersection
642               fLeft < right && left < fRight &&
643               fTop < bottom && top < fBottom;
644    }
645
646    /** If rectangles a and b intersect, return true and set this rectangle to
647     *  that intersection, otherwise return false and do not change this
648     *  rectangle. If either rectangle is empty, do nothing and return false.
649     */
650    bool intersect(const SkRect& a, const SkRect& b);
651
652    /**
653     *  Return true if rectangles a and b are not empty and intersect.
654     */
655    static bool Intersects(const SkRect& a, const SkRect& b) {
656        return  !a.isEmpty() && !b.isEmpty() &&
657                a.fLeft < b.fRight && b.fLeft < a.fRight &&
658                a.fTop < b.fBottom && b.fTop < a.fBottom;
659    }
660
661    /**
662     *  Update this rectangle to enclose itself and the specified rectangle.
663     *  If this rectangle is empty, just set it to the specified rectangle.
664     *  If the specified rectangle is empty, do nothing.
665     */
666    void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
667
668    /** Update this rectangle to enclose itself and the specified rectangle.
669        If this rectangle is empty, just set it to the specified rectangle. If the specified
670        rectangle is empty, do nothing.
671    */
672    void join(const SkRect& r) {
673        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
674    }
675    // alias for join()
676    void growToInclude(const SkRect& r) { this->join(r); }
677
678    /**
679     *  Grow the rect to include the specified (x,y). After this call, the
680     *  following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
681     *
682     *  This is close, but not quite the same contract as contains(), since
683     *  contains() treats the left and top different from the right and bottom.
684     *  contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
685     *  that contains(x,y) always returns false if the rect is empty.
686     */
687    void growToInclude(SkScalar x, SkScalar y) {
688        fLeft  = SkMinScalar(x, fLeft);
689        fRight = SkMaxScalar(x, fRight);
690        fTop    = SkMinScalar(y, fTop);
691        fBottom = SkMaxScalar(y, fBottom);
692    }
693
694    /** Bulk version of growToInclude */
695    void growToInclude(const SkPoint pts[], int count) {
696        this->growToInclude(pts, sizeof(SkPoint), count);
697    }
698
699    /** Bulk version of growToInclude with stride. */
700    void growToInclude(const SkPoint pts[], size_t stride, int count) {
701        SkASSERT(count >= 0);
702        SkASSERT(stride >= sizeof(SkPoint));
703        const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
704        for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
705            this->growToInclude(pts->fX, pts->fY);
706        }
707    }
708
709    /**
710     *  Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
711     *  is not empty.
712     *
713     *  Contains treats the left and top differently from the right and bottom.
714     *  The left and top coordinates of the rectangle are themselves considered
715     *  to be inside, while the right and bottom are not. Thus for the rectangle
716     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
717     */
718    bool contains(const SkPoint& p) const {
719        return !this->isEmpty() &&
720               fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
721    }
722
723    /**
724     *  Returns true if (x,y) is inside the rectangle, and the rectangle
725     *  is not empty.
726     *
727     *  Contains treats the left and top differently from the right and bottom.
728     *  The left and top coordinates of the rectangle are themselves considered
729     *  to be inside, while the right and bottom are not. Thus for the rectangle
730     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
731     */
732    bool contains(SkScalar x, SkScalar y) const {
733        return  !this->isEmpty() &&
734                fLeft <= x && x < fRight && fTop <= y && y < fBottom;
735    }
736
737    /**
738     *  Return true if this rectangle contains r, and if both rectangles are
739     *  not empty.
740     */
741    bool contains(const SkRect& r) const {
742        return  !r.isEmpty() && !this->isEmpty() &&
743                fLeft <= r.fLeft && fTop <= r.fTop &&
744                fRight >= r.fRight && fBottom >= r.fBottom;
745    }
746
747    /**
748     *  Set the dst rectangle by rounding this rectangle's coordinates to their
749     *  nearest integer values using SkScalarRound.
750     */
751    void round(SkIRect* dst) const {
752        SkASSERT(dst);
753        dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
754                 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
755    }
756
757    /**
758     *  Set the dst rectangle by rounding "out" this rectangle, choosing the
759     *  SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
760     */
761    void roundOut(SkIRect* dst) const {
762        SkASSERT(dst);
763        dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
764                 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
765    }
766
767    /**
768     *  Expand this rectangle by rounding its coordinates "out", choosing the
769     *  floor of top and left, and the ceil of right and bottom. If this rect
770     *  is already on integer coordinates, then it will be unchanged.
771     */
772    void roundOut() {
773        this->set(SkScalarFloorToScalar(fLeft),
774                  SkScalarFloorToScalar(fTop),
775                  SkScalarCeilToScalar(fRight),
776                  SkScalarCeilToScalar(fBottom));
777    }
778
779    /**
780     *  Set the dst rectangle by rounding "in" this rectangle, choosing the
781     *  ceil of top and left, and the floor of right and bottom. This does *not*
782     *  call sort(), so it is possible that the resulting rect is inverted...
783     *  e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
784     */
785    void roundIn(SkIRect* dst) const {
786        SkASSERT(dst);
787        dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
788                 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
789    }
790
791
792    /**
793     *  Swap top/bottom or left/right if there are flipped (i.e. if width()
794     *  or height() would have returned a negative value.) This should be called
795     *  if the edges are computed separately, and may have crossed over each
796     *  other. When this returns, left <= right && top <= bottom
797     */
798    void sort();
799
800    /**
801     *  cast-safe way to treat the rect as an array of (4) SkScalars.
802     */
803    const SkScalar* asScalars() const { return &fLeft; }
804};
805
806#endif
807