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