SkRect.h revision d6195f956fdccef865224520a624e3fd968573d0
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 MakeEmpty() {
24        SkIRect r;
25        r.setEmpty();
26        return r;
27    }
28
29    static SkIRect MakeWH(int32_t w, int32_t h) {
30        SkIRect r;
31        r.set(0, 0, w, h);
32        return r;
33    }
34
35    static SkIRect MakeSize(const SkISize& size) {
36        SkIRect r;
37        r.set(0, 0, size.width(), size.height());
38        return r;
39    }
40
41    static SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
42        SkIRect rect;
43        rect.set(l, t, r, b);
44        return rect;
45    }
46
47    static SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
48        SkIRect r;
49        r.set(x, y, x + w, y + h);
50        return r;
51    }
52
53    int left() const { return fLeft; }
54    int top() const { return fTop; }
55    int right() const { return fRight; }
56    int bottom() const { return fBottom; }
57
58    /** return the left edge of the rect */
59    int x() const { return fLeft; }
60    /** return the top edge of the rect */
61    int y() const { return fTop; }
62    /**
63     *  Returns the rectangle's width. This does not check for a valid rect
64     *  (i.e. left <= right) so the result may be negative.
65     */
66    int width() const { return fRight - fLeft; }
67
68    /**
69     *  Returns the rectangle's height. This does not check for a valid rect
70     *  (i.e. top <= bottom) so the result may be negative.
71     */
72    int height() const { return fBottom - fTop; }
73
74    /**
75     *  Return true if the rectangle's width or height are <= 0
76     */
77    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
78
79    friend bool operator==(const SkIRect& a, const SkIRect& b) {
80        return !memcmp(&a, &b, sizeof(a));
81    }
82
83    friend bool operator!=(const SkIRect& a, const SkIRect& b) {
84        return !(a == b);
85    }
86
87    bool is16Bit() const {
88        return  SkIsS16(fLeft) && SkIsS16(fTop) &&
89                SkIsS16(fRight) && SkIsS16(fBottom);
90    }
91
92    /** Set the rectangle to (0,0,0,0)
93    */
94    void setEmpty() { memset(this, 0, sizeof(*this)); }
95
96    void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
97        fLeft   = left;
98        fTop    = top;
99        fRight  = right;
100        fBottom = bottom;
101    }
102    // alias for set(l, t, r, b)
103    void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
104        this->set(left, top, right, bottom);
105    }
106
107    void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
108        fLeft = x;
109        fTop = y;
110        fRight = x + width;
111        fBottom = y + height;
112    }
113
114    /**
115     *  Make the largest representable rectangle
116     */
117    void setLargest() {
118        fLeft = fTop = SK_MinS32;
119        fRight = fBottom = SK_MaxS32;
120    }
121
122    /**
123     *  Make the largest representable rectangle, but inverted (e.g. fLeft will
124     *  be max 32bit and right will be min 32bit).
125     */
126    void setLargestInverted() {
127        fLeft = fTop = SK_MaxS32;
128        fRight = fBottom = SK_MinS32;
129    }
130
131    /** Offset set the rectangle by adding dx to its left and right,
132        and adding dy to its top and bottom.
133    */
134    void offset(int32_t dx, int32_t dy) {
135        fLeft   += dx;
136        fTop    += dy;
137        fRight  += dx;
138        fBottom += dy;
139    }
140
141    void offset(const SkIPoint& delta) {
142        this->offset(delta.fX, delta.fY);
143    }
144
145    /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
146        making the rectangle narrower. If dx is negative, then the sides are moved outwards,
147        making the rectangle wider. The same hods true for dy and the top and bottom.
148    */
149    void inset(int32_t dx, int32_t dy) {
150        fLeft   += dx;
151        fTop    += dy;
152        fRight  -= dx;
153        fBottom -= dy;
154    }
155
156    bool quickReject(int l, int t, int r, int b) const {
157        return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
158    }
159
160    /** Returns true if (x,y) is inside the rectangle and the rectangle is not
161        empty. The left and top are considered to be inside, while the right
162        and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
163        points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
164    */
165    bool contains(int32_t x, int32_t y) const {
166        return  (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
167                (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
168    }
169
170    /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
171        If either rectangle is empty, contains() returns false.
172    */
173    bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
174        return  left < right && top < bottom && !this->isEmpty() && // check for empties
175                fLeft <= left && fTop <= top &&
176                fRight >= right && fBottom >= bottom;
177    }
178
179    /** Returns true if the specified rectangle r is inside or equal to this rectangle.
180    */
181    bool contains(const SkIRect& r) const {
182        return  !r.isEmpty() && !this->isEmpty() &&     // check for empties
183                fLeft <= r.fLeft && fTop <= r.fTop &&
184                fRight >= r.fRight && fBottom >= r.fBottom;
185    }
186
187    /** Return true if this rectangle contains the specified rectangle.
188		For speed, this method does not check if either this or the specified
189		rectangles are empty, and if either is, its return value is undefined.
190		In the debugging build however, we assert that both this and the
191		specified rectangles are non-empty.
192    */
193    bool containsNoEmptyCheck(int32_t left, int32_t top,
194							  int32_t right, int32_t bottom) const {
195		SkASSERT(fLeft < fRight && fTop < fBottom);
196        SkASSERT(left < right && top < bottom);
197
198        return fLeft <= left && fTop <= top &&
199			   fRight >= right && fBottom >= bottom;
200    }
201
202    /** If r intersects this rectangle, return true and set this rectangle to that
203        intersection, otherwise return false and do not change this rectangle.
204        If either rectangle is empty, do nothing and return false.
205    */
206    bool intersect(const SkIRect& r) {
207        SkASSERT(&r);
208        return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
209    }
210
211    /** If rectangles a and b intersect, return true and set this rectangle to
212        that intersection, otherwise return false and do not change this
213        rectangle. If either rectangle is empty, do nothing and return false.
214    */
215    bool intersect(const SkIRect& a, const SkIRect& b) {
216        SkASSERT(&a && &b);
217
218        if (!a.isEmpty() && !b.isEmpty() &&
219                a.fLeft < b.fRight && b.fLeft < a.fRight &&
220                a.fTop < b.fBottom && b.fTop < a.fBottom) {
221            fLeft   = SkMax32(a.fLeft,   b.fLeft);
222            fTop    = SkMax32(a.fTop,    b.fTop);
223            fRight  = SkMin32(a.fRight,  b.fRight);
224            fBottom = SkMin32(a.fBottom, b.fBottom);
225            return true;
226        }
227        return false;
228    }
229
230    /** If rectangles a and b intersect, return true and set this rectangle to
231        that intersection, otherwise return false and do not change this
232        rectangle. For speed, no check to see if a or b are empty is performed.
233        If either is, then the return result is undefined. In the debug build,
234        we assert that both rectangles are non-empty.
235    */
236    bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
237        SkASSERT(&a && &b);
238        SkASSERT(!a.isEmpty() && !b.isEmpty());
239
240        if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
241                a.fTop < b.fBottom && b.fTop < a.fBottom) {
242            fLeft   = SkMax32(a.fLeft,   b.fLeft);
243            fTop    = SkMax32(a.fTop,    b.fTop);
244            fRight  = SkMin32(a.fRight,  b.fRight);
245            fBottom = SkMin32(a.fBottom, b.fBottom);
246            return true;
247        }
248        return false;
249    }
250
251    /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
252        return true and set this rectangle to that intersection,
253        otherwise return false and do not change this rectangle.
254        If either rectangle is empty, do nothing and return false.
255    */
256    bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
257        if (left < right && top < bottom && !this->isEmpty() &&
258                fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
259            if (fLeft < left) fLeft = left;
260            if (fTop < top) fTop = top;
261            if (fRight > right) fRight = right;
262            if (fBottom > bottom) fBottom = bottom;
263            return true;
264        }
265        return false;
266    }
267
268    /** Returns true if a and b are not empty, and they intersect
269    */
270    static bool Intersects(const SkIRect& a, const SkIRect& b) {
271        return  !a.isEmpty() && !b.isEmpty() &&              // check for empties
272                a.fLeft < b.fRight && b.fLeft < a.fRight &&
273                a.fTop < b.fBottom && b.fTop < a.fBottom;
274    }
275
276    /** Update this rectangle to enclose itself and the specified rectangle.
277        If this rectangle is empty, just set it to the specified rectangle. If the specified
278        rectangle is empty, do nothing.
279    */
280    void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
281
282    /** Update this rectangle to enclose itself and the specified rectangle.
283        If this rectangle is empty, just set it to the specified rectangle. If the specified
284        rectangle is empty, do nothing.
285    */
286    void join(const SkIRect& r) {
287        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
288    }
289
290    /** Swap top/bottom or left/right if there are flipped.
291        This can be called if the edges are computed separately,
292        and may have crossed over each other.
293        When this returns, left <= right && top <= bottom
294    */
295    void sort();
296
297    static const SkIRect& EmptyIRect() {
298        static const SkIRect gEmpty = { 0, 0, 0, 0 };
299        return gEmpty;
300    }
301};
302
303/** \struct SkRect
304*/
305struct SK_API SkRect {
306    SkScalar    fLeft, fTop, fRight, fBottom;
307
308    static SkRect MakeEmpty() {
309        SkRect r;
310        r.setEmpty();
311        return r;
312    }
313
314    static SkRect MakeWH(SkScalar w, SkScalar h) {
315        SkRect r;
316        r.set(0, 0, w, h);
317        return r;
318    }
319
320    static SkRect MakeSize(const SkSize& size) {
321        SkRect r;
322        r.set(0, 0, size.width(), size.height());
323        return r;
324    }
325
326    static SkRect MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
327        SkRect rect;
328        rect.set(l, t, r, b);
329        return rect;
330    }
331
332    static SkRect MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
333        SkRect r;
334        r.set(x, y, x + w, y + h);
335        return r;
336    }
337
338    /**
339     *  Return true if the rectangle's width or height are <= 0
340     */
341    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
342
343    /**
344     *  Returns true iff all values in the rect are finite. If any are
345     *  infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
346     *  returns false.
347     */
348    bool isFinite() const {
349#ifdef SK_SCALAR_IS_FLOAT
350        // x * 0 will be NaN iff x is infinity or NaN.
351        // a + b will be NaN iff either a or b is NaN.
352        float value = fLeft * 0 + fTop * 0 + fRight * 0 + fBottom * 0;
353
354        // value is either NaN or it is finite (zero).
355        // value==value will be true iff value is not NaN
356        return value == value;
357#else
358        // use bit-or for speed, since we don't care about short-circuting the
359        // tests, and we expect the common case will be that we need to check all.
360        int isNaN = (SK_FixedNaN == fLeft)  | (SK_FixedNaN == fTop) |
361                    (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
362        return !isNaN;
363#endif
364    }
365
366    SkScalar    x() const { return fLeft; }
367    SkScalar    y() const { return fTop; }
368    SkScalar    left() const { return fLeft; }
369    SkScalar    top() const { return fTop; }
370    SkScalar    right() const { return fRight; }
371    SkScalar    bottom() const { return fBottom; }
372    SkScalar    width() const { return fRight - fLeft; }
373    SkScalar    height() const { return fBottom - fTop; }
374    SkScalar    centerX() const { return SkScalarHalf(fLeft + fRight); }
375    SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
376
377    friend bool operator==(const SkRect& a, const SkRect& b) {
378        return 0 == memcmp(&a, &b, sizeof(a));
379    }
380
381    friend bool operator!=(const SkRect& a, const SkRect& b) {
382        return 0 != memcmp(&a, &b, sizeof(a));
383    }
384
385    /** return the 4 points that enclose the rectangle
386    */
387    void toQuad(SkPoint quad[4]) const;
388
389    /** Set this rectangle to the empty rectangle (0,0,0,0)
390    */
391    void setEmpty() { memset(this, 0, sizeof(*this)); }
392
393    void set(const SkIRect& src) {
394        fLeft   = SkIntToScalar(src.fLeft);
395        fTop    = SkIntToScalar(src.fTop);
396        fRight  = SkIntToScalar(src.fRight);
397        fBottom = SkIntToScalar(src.fBottom);
398    }
399
400    void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
401        fLeft   = left;
402        fTop    = top;
403        fRight  = right;
404        fBottom = bottom;
405    }
406    // alias for set(l, t, r, b)
407    void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
408        this->set(left, top, right, bottom);
409    }
410
411    /** Initialize the rect with the 4 specified integers. The routine handles
412        converting them to scalars (by calling SkIntToScalar)
413     */
414    void iset(int left, int top, int right, int bottom) {
415        fLeft   = SkIntToScalar(left);
416        fTop    = SkIntToScalar(top);
417        fRight  = SkIntToScalar(right);
418        fBottom = SkIntToScalar(bottom);
419    }
420
421    /** Set this rectangle to be the bounds of the array of points.
422        If the array is empty (count == 0), then set this rectangle
423        to the empty rectangle (0,0,0,0)
424    */
425    void set(const SkPoint pts[], int count);
426
427    // alias for set(pts, count)
428    void setBounds(const SkPoint pts[], int count) {
429        this->set(pts, count);
430    }
431
432    void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
433        fLeft = x;
434        fTop = y;
435        fRight = x + width;
436        fBottom = y + height;
437    }
438
439    /**
440     *  Make the largest representable rectangle
441     */
442    void setLargest() {
443        fLeft = fTop = SK_ScalarMin;
444        fRight = fBottom = SK_ScalarMax;
445    }
446
447    /**
448     *  Make the largest representable rectangle, but inverted (e.g. fLeft will
449     *  be max and right will be min).
450     */
451    void setLargestInverted() {
452        fLeft = fTop = SK_ScalarMax;
453        fRight = fBottom = SK_ScalarMin;
454    }
455
456    /** Offset set the rectangle by adding dx to its left and right,
457        and adding dy to its top and bottom.
458    */
459    void offset(SkScalar dx, SkScalar dy) {
460        fLeft   += dx;
461        fTop    += dy;
462        fRight  += dx;
463        fBottom += dy;
464    }
465
466    void offset(const SkPoint& delta) {
467        this->offset(delta.fX, delta.fY);
468    }
469
470    /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
471        moved inwards, making the rectangle narrower. If dx is negative, then
472        the sides are moved outwards, making the rectangle wider. The same holds
473         true for dy and the top and bottom.
474    */
475    void inset(SkScalar dx, SkScalar dy)  {
476        fLeft   += dx;
477        fTop    += dy;
478        fRight  -= dx;
479        fBottom -= dy;
480    }
481
482   /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
483       moved outwards, making the rectangle wider. If dx is negative, then the
484       sides are moved inwards, making the rectangle narrower. The same hods
485       true for dy and the top and bottom.
486    */
487    void outset(SkScalar dx, SkScalar dy)  { this->inset(-dx, -dy); }
488
489    /** If this rectangle intersects r, return true and set this rectangle to that
490        intersection, otherwise return false and do not change this rectangle.
491        If either rectangle is empty, do nothing and return false.
492    */
493    bool intersect(const SkRect& r);
494
495    /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
496        return true and set this rectangle to that intersection, otherwise return false
497        and do not change this rectangle.
498        If either rectangle is empty, do nothing and return false.
499    */
500    bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
501
502    /**
503     *  Return true if this rectangle is not empty, and the specified sides of
504     *  a rectangle are not empty, and they intersect.
505     */
506    bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
507        return // first check that both are not empty
508               left < right && top < bottom &&
509               fLeft < fRight && fTop < fBottom &&
510               // now check for intersection
511               fLeft < right && left < fRight &&
512               fTop < bottom && top < fBottom;
513    }
514
515    /** If rectangles a and b intersect, return true and set this rectangle to
516     *  that intersection, otherwise return false and do not change this
517     *  rectangle. If either rectangle is empty, do nothing and return false.
518     */
519    bool intersect(const SkRect& a, const SkRect& b);
520
521    /**
522     *  Return true if rectangles a and b are not empty and intersect.
523     */
524    static bool Intersects(const SkRect& a, const SkRect& b) {
525        return  !a.isEmpty() && !b.isEmpty() &&
526                a.fLeft < b.fRight && b.fLeft < a.fRight &&
527                a.fTop < b.fBottom && b.fTop < a.fBottom;
528    }
529
530    /**
531     *  Update this rectangle to enclose itself and the specified rectangle.
532     *  If this rectangle is empty, just set it to the specified rectangle.
533     *  If the specified rectangle is empty, do nothing.
534     */
535    void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
536
537    /** Update this rectangle to enclose itself and the specified rectangle.
538        If this rectangle is empty, just set it to the specified rectangle. If the specified
539        rectangle is empty, do nothing.
540    */
541    void join(const SkRect& r) {
542        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
543    }
544    // alias for join()
545    void growToInclude(const SkRect& r) { this->join(r); }
546
547    /**
548     *  Grow the rect to include the specified (x,y). After this call, the
549     *  following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
550     *
551     *  This is close, but not quite the same contract as contains(), since
552     *  contains() treats the left and top different from the right and bottom.
553     *  contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
554     *  that contains(x,y) always returns false if the rect is empty.
555     */
556    void growToInclude(SkScalar x, SkScalar y) {
557        fLeft  = SkMinScalar(x, fLeft);
558        fRight = SkMaxScalar(x, fRight);
559        fTop    = SkMinScalar(y, fTop);
560        fBottom = SkMaxScalar(y, fBottom);
561    }
562
563    /**
564     *  Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
565     *  is not empty.
566     *
567     *  Contains treats the left and top differently from the right and bottom.
568     *  The left and top coordinates of the rectangle are themselves considered
569     *  to be inside, while the right and bottom are not. Thus for the rectangle
570     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
571     */
572    bool contains(const SkPoint& p) const {
573        return !this->isEmpty() &&
574               fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
575    }
576
577    /**
578     *  Returns true if (x,y) is inside the rectangle, and the rectangle
579     *  is not empty.
580     *
581     *  Contains treats the left and top differently from the right and bottom.
582     *  The left and top coordinates of the rectangle are themselves considered
583     *  to be inside, while the right and bottom are not. Thus for the rectangle
584     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
585     */
586    bool contains(SkScalar x, SkScalar y) const {
587        return  !this->isEmpty() &&
588                fLeft <= x && x < fRight && fTop <= y && y < fBottom;
589    }
590
591    /**
592     *  Return true if this rectangle contains r, and if both rectangles are
593     *  not empty.
594     */
595    bool contains(const SkRect& r) const {
596        return  !r.isEmpty() && !this->isEmpty() &&
597                fLeft <= r.fLeft && fTop <= r.fTop &&
598                fRight >= r.fRight && fBottom >= r.fBottom;
599    }
600
601    /**
602     *  Set the dst rectangle by rounding this rectangle's coordinates to their
603     *  nearest integer values using SkScalarRound.
604     */
605    void round(SkIRect* dst) const {
606        SkASSERT(dst);
607        dst->set(SkScalarRound(fLeft), SkScalarRound(fTop),
608                 SkScalarRound(fRight), SkScalarRound(fBottom));
609    }
610
611    /**
612     *  Set the dst rectangle by rounding "out" this rectangle, choosing the
613     *  SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
614     */
615    void roundOut(SkIRect* dst) const {
616        SkASSERT(dst);
617        dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop),
618                 SkScalarCeil(fRight), SkScalarCeil(fBottom));
619    }
620
621    /**
622     *  Expand this rectangle by rounding its coordinates "out", choosing the
623     *  floor of top and left, and the ceil of right and bottom. If this rect
624     *  is already on integer coordinates, then it will be unchanged.
625     */
626    void roundOut() {
627        this->set(SkScalarFloorToScalar(fLeft),
628                  SkScalarFloorToScalar(fTop),
629                  SkScalarCeilToScalar(fRight),
630                  SkScalarCeilToScalar(fBottom));
631    }
632
633    /**
634     *  Swap top/bottom or left/right if there are flipped (i.e. if width()
635     *  or height() would have returned a negative value.) This should be called
636     *  if the edges are computed separately, and may have crossed over each
637     *  other. When this returns, left <= right && top <= bottom
638     */
639    void sort();
640};
641
642#endif
643
644