Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkRRect_DEFINED
      9 #define SkRRect_DEFINED
     10 
     11 #include "SkRect.h"
     12 #include "SkPoint.h"
     13 
     14 class SkPath;
     15 class SkMatrix;
     16 
     17 // Path forward:
     18 //   core work
     19 //      add validate method (all radii positive, all radii sums < rect size, etc.)
     20 //      add contains(SkRect&)  - for clip stack
     21 //      add contains(SkRRect&) - for clip stack
     22 //      add heart rect computation (max rect inside RR)
     23 //      add 9patch rect computation
     24 //      add growToInclude(SkPath&)
     25 //   analysis
     26 //      use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
     27 //      check on # of rectorus's the RRs could handle
     28 //   rendering work
     29 //      update SkPath.addRRect() to only use quads
     30 //      add GM and bench
     31 //   further out
     32 //      detect and triangulate RRectorii rather than falling back to SW in Ganesh
     33 //
     34 
     35 /** \class SkRRect
     36 
     37     The SkRRect class represents a rounded rect with a potentially different
     38     radii for each corner. It does not have a constructor so must be
     39     initialized with one of the initialization functions (e.g., setEmpty,
     40     setRectRadii, etc.)
     41 
     42     This class is intended to roughly match CSS' border-*-*-radius capabilities.
     43     This means:
     44         If either of a corner's radii are 0 the corner will be square.
     45         Negative radii are not allowed (they are clamped to zero).
     46         If the corner curves overlap they will be proportionally reduced to fit.
     47 */
     48 class SK_API SkRRect {
     49 public:
     50     /**
     51      * Enum to capture the various possible subtypes of RR. Accessed
     52      * by type(). The subtypes become progressively less restrictive.
     53      */
     54     enum Type {
     55         // !< The RR is empty
     56         kEmpty_Type,
     57 
     58         //!< The RR is actually a (non-empty) rect (i.e., at least one radius
     59         //!< at each corner is zero)
     60         kRect_Type,
     61 
     62         //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
     63         //!< and >= width/2 and all the y radii are equal and >= height/2
     64         kOval_Type,
     65 
     66         //!< The RR is non-empty and all the x radii are equal & all y radii
     67         //!< are equal but it is not an oval (i.e., there are lines between
     68         //!< the curves) nor a rect (i.e., both radii are non-zero)
     69         kSimple_Type,
     70 
     71         //!< The RR is non-empty and the two left x radii are equal, the two top
     72         //!< y radii are equal, and the same for the right and bottom but it is
     73         //!< neither an rect, oval, nor a simple RR. It is called "nine patch"
     74         //!< because the centers of the corner ellipses form an axis aligned
     75         //!< rect with edges that divide the RR into an 9 rectangular patches:
     76         //!< an interior patch, four edge patches, and four corner patches.
     77         kNinePatch_Type,
     78 
     79         //!< A fully general (non-empty) RR. Some of the x and/or y radii are
     80         //!< different from the others and there must be one corner where
     81         //!< both radii are non-zero.
     82         kComplex_Type,
     83     };
     84 
     85     /**
     86      * Returns the RR's sub type.
     87      */
     88     Type getType() const {
     89         SkDEBUGCODE(this->validate();)
     90         return static_cast<Type>(fType);
     91     }
     92 
     93     Type type() const { return this->getType(); }
     94 
     95     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
     96     inline bool isRect() const { return kRect_Type == this->getType(); }
     97     inline bool isOval() const { return kOval_Type == this->getType(); }
     98     inline bool isSimple() const { return kSimple_Type == this->getType(); }
     99     // TODO: should isSimpleCircular & isCircle take a tolerance? This could help
    100     // instances where the mapping to device space is noisy.
    101     inline bool isSimpleCircular() const {
    102         return this->isSimple() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
    103     }
    104     inline bool isCircle() const {
    105         return this->isOval() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
    106     }
    107     inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
    108     inline bool isComplex() const { return kComplex_Type == this->getType(); }
    109 
    110     bool allCornersCircular() const;
    111 
    112     SkScalar width() const { return fRect.width(); }
    113     SkScalar height() const { return fRect.height(); }
    114 
    115     /**
    116      * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
    117      */
    118     void setEmpty() {
    119         fRect.setEmpty();
    120         memset(fRadii, 0, sizeof(fRadii));
    121         fType = kEmpty_Type;
    122 
    123         SkDEBUGCODE(this->validate();)
    124     }
    125 
    126     /**
    127      * Set this RR to match the supplied rect. All radii will be 0.
    128      */
    129     void setRect(const SkRect& rect) {
    130         fRect = rect;
    131         fRect.sort();
    132 
    133         if (fRect.isEmpty()) {
    134             this->setEmpty();
    135             return;
    136         }
    137 
    138         memset(fRadii, 0, sizeof(fRadii));
    139         fType = kRect_Type;
    140 
    141         SkDEBUGCODE(this->validate();)
    142     }
    143 
    144     static SkRRect MakeRect(const SkRect& r) {
    145         SkRRect rr;
    146         rr.setRect(r);
    147         return rr;
    148     }
    149 
    150     static SkRRect MakeOval(const SkRect& oval) {
    151         SkRRect rr;
    152         rr.setOval(oval);
    153         return rr;
    154     }
    155 
    156     static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
    157         SkRRect rr;
    158         rr.setRectXY(rect, xRad, yRad);
    159         return rr;
    160     }
    161 
    162     /**
    163      * Set this RR to match the supplied oval. All x radii will equal half the
    164      * width and all y radii will equal half the height.
    165      */
    166     void setOval(const SkRect& oval) {
    167         fRect = oval;
    168         fRect.sort();
    169 
    170         if (fRect.isEmpty()) {
    171             this->setEmpty();
    172             return;
    173         }
    174 
    175         SkScalar xRad = SkScalarHalf(fRect.width());
    176         SkScalar yRad = SkScalarHalf(fRect.height());
    177 
    178         for (int i = 0; i < 4; ++i) {
    179             fRadii[i].set(xRad, yRad);
    180         }
    181         fType = kOval_Type;
    182 
    183         SkDEBUGCODE(this->validate();)
    184     }
    185 
    186     /**
    187      * Initialize the RR with the same radii for all four corners.
    188      */
    189     void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
    190 
    191     /**
    192      * Initialize the rr with one radius per-side.
    193      */
    194     void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
    195                       SkScalar rightRad, SkScalar bottomRad);
    196 
    197     /**
    198      * Initialize the RR with potentially different radii for all four corners.
    199      */
    200     void setRectRadii(const SkRect& rect, const SkVector radii[4]);
    201 
    202     // The radii are stored in UL, UR, LR, LL order.
    203     enum Corner {
    204         kUpperLeft_Corner,
    205         kUpperRight_Corner,
    206         kLowerRight_Corner,
    207         kLowerLeft_Corner
    208     };
    209 
    210     const SkRect& rect() const { return fRect; }
    211     const SkVector& radii(Corner corner) const { return fRadii[corner]; }
    212     const SkRect& getBounds() const { return fRect; }
    213 
    214     /**
    215      *  When a rrect is simple, all of its radii are equal. This returns one
    216      *  of those radii. This call requires the rrect to be non-complex.
    217      */
    218     const SkVector& getSimpleRadii() const {
    219         SkASSERT(!this->isComplex());
    220         return fRadii[0];
    221     }
    222 
    223     friend bool operator==(const SkRRect& a, const SkRRect& b) {
    224         return a.fRect == b.fRect &&
    225                SkScalarsEqual(a.fRadii[0].asScalars(),
    226                               b.fRadii[0].asScalars(), 8);
    227     }
    228 
    229     friend bool operator!=(const SkRRect& a, const SkRRect& b) {
    230         return a.fRect != b.fRect ||
    231                !SkScalarsEqual(a.fRadii[0].asScalars(),
    232                                b.fRadii[0].asScalars(), 8);
    233     }
    234 
    235     /**
    236      *  Call inset on the bounds, and adjust the radii to reflect what happens
    237      *  in stroking: If the corner is sharp (no curvature), leave it alone,
    238      *  otherwise we grow/shrink the radii by the amount of the inset. If a
    239      *  given radius becomes negative, it is pinned to 0.
    240      *
    241      *  It is valid for dst == this.
    242      */
    243     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
    244 
    245     void inset(SkScalar dx, SkScalar dy) {
    246         this->inset(dx, dy, this);
    247     }
    248 
    249     /**
    250      *  Call outset on the bounds, and adjust the radii to reflect what happens
    251      *  in stroking: If the corner is sharp (no curvature), leave it alone,
    252      *  otherwise we grow/shrink the radii by the amount of the inset. If a
    253      *  given radius becomes negative, it is pinned to 0.
    254      *
    255      *  It is valid for dst == this.
    256      */
    257     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
    258         this->inset(-dx, -dy, dst);
    259     }
    260     void outset(SkScalar dx, SkScalar dy) {
    261         this->inset(-dx, -dy, this);
    262     }
    263 
    264     /**
    265      * Translate the rrect by (dx, dy).
    266      */
    267     void offset(SkScalar dx, SkScalar dy) {
    268         fRect.offset(dx, dy);
    269     }
    270 
    271     /**
    272      *  Returns true if 'rect' is wholy inside the RR, and both
    273      *  are not empty.
    274      */
    275     bool contains(const SkRect& rect) const;
    276 
    277     SkDEBUGCODE(void validate() const;)
    278 
    279     enum {
    280         kSizeInMemory = 12 * sizeof(SkScalar)
    281     };
    282 
    283     /**
    284      *  Write the rrect into the specified buffer. This is guaranteed to always
    285      *  write kSizeInMemory bytes, and that value is guaranteed to always be
    286      *  a multiple of 4. Return kSizeInMemory.
    287      */
    288     size_t writeToMemory(void* buffer) const;
    289 
    290     /**
    291      * Reads the rrect from the specified buffer
    292      *
    293      * If the specified buffer is large enough, this will read kSizeInMemory bytes,
    294      * and that value is guaranteed to always be a multiple of 4.
    295      *
    296      * @param buffer Memory to read from
    297      * @param length Amount of memory available in the buffer
    298      * @return number of bytes read (must be a multiple of 4) or
    299      *         0 if there was not enough memory available
    300      */
    301     size_t readFromMemory(const void* buffer, size_t length);
    302 
    303     /**
    304      *  Transform by the specified matrix, and put the result in dst.
    305      *
    306      *  @param matrix SkMatrix specifying the transform. Must only contain
    307      *      scale and/or translate, or this call will fail.
    308      *  @param dst SkRRect to store the result. It is an error to use this,
    309      *      which would make this function no longer const.
    310      *  @return true on success, false on failure. If false, dst is unmodified.
    311      */
    312     bool transform(const SkMatrix& matrix, SkRRect* dst) const;
    313 
    314     void dump(bool asHex) const;
    315     void dump() const { this->dump(false); }
    316     void dumpHex() const { this->dump(true); }
    317 
    318 private:
    319     SkRect fRect;
    320     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
    321     SkVector fRadii[4];
    322     // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
    323     int32_t fType;
    324     // TODO: add padding so we can use memcpy for flattening and not copy
    325     // uninitialized data
    326 
    327     void computeType();
    328     bool checkCornerContainment(SkScalar x, SkScalar y) const;
    329     void scaleRadii();
    330 
    331     // to access fRadii directly
    332     friend class SkPath;
    333 };
    334 
    335 #endif
    336