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 
     16 // Path forward:
     17 //   core work
     18 //      add validate method (all radii positive, all radii sums < rect size, etc.)
     19 //      add contains(SkRect&)  - for clip stack
     20 //      add contains(SkRRect&) - for clip stack
     21 //      add heart rect computation (max rect inside RR)
     22 //      add 9patch rect computation
     23 //      add growToInclude(SkPath&)
     24 //   analysis
     25 //      use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
     26 //      check on # of rectorus's the RRs could handle
     27 //   rendering work
     28 //      add entry points (clipRRect, drawRRect) - plumb down to SkDevice
     29 //      update SkPath.addRRect() to take an SkRRect - only use quads
     30 //          -- alternatively add addRRectToPath here
     31 //      add GM and bench
     32 //   clipping opt
     33 //      update SkClipStack to perform logic with RRs
     34 //   further out
     35 //      add RR rendering shader to Ganesh (akin to cicle drawing code)
     36 //          - only for simple RRs
     37 //      detect and triangulate RRectorii rather than falling back to SW in Ganesh
     38 //
     39 
     40 /** \class SkRRect
     41 
     42     The SkRRect class represents a rounded rect with a potentially different
     43     radii for each corner. It does not have a constructor so must be
     44     initialized with one of the initialization functions (e.g., setEmpty,
     45     setRectRadii, etc.)
     46 
     47     This class is intended to roughly match CSS' border-*-*-radius capabilities.
     48     This means:
     49         If either of a corner's radii are 0 the corner will be square.
     50         Negative radii are not allowed (they are clamped to zero).
     51         If the corner curves overlap they will be proportionally reduced to fit.
     52 */
     53 class SK_API SkRRect {
     54 public:
     55     /**
     56      * Enum to capture the various possible subtypes of RR. Accessed
     57      * by type(). The subtypes become progressively less restrictive.
     58      */
     59     enum Type {
     60         // !< Internal indicator that the sub type must be computed.
     61         kUnknown_Type = -1,
     62 
     63         // !< The RR is empty
     64         kEmpty_Type,
     65 
     66         //!< The RR is actually a (non-empty) rect (i.e., at least one radius
     67         //!< at each corner is zero)
     68         kRect_Type,
     69 
     70         //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
     71         //!< and >= width/2 and all the y radii are equal and >= height/2
     72         kOval_Type,
     73 
     74         //!< The RR is non-empty and all the x radii are equal & all y radii
     75         //!< are equal but it is not an oval (i.e., there are lines between
     76         //!< the curves) nor a rect (i.e., both radii are non-zero)
     77         kSimple_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 
     91         if (kUnknown_Type == fType) {
     92             this->computeType();
     93         }
     94         SkASSERT(kUnknown_Type != fType);
     95         return fType;
     96     }
     97 
     98     Type type() const { return this->getType(); }
     99 
    100     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
    101     inline bool isRect() const { return kRect_Type == this->getType(); }
    102     inline bool isOval() const { return kOval_Type == this->getType(); }
    103     inline bool isSimple() const { return kSimple_Type == this->getType(); }
    104     inline bool isComplex() const { return kComplex_Type == this->getType(); }
    105 
    106     SkScalar width() const { return fRect.width(); }
    107     SkScalar height() const { return fRect.height(); }
    108 
    109     /**
    110      * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
    111      */
    112     void setEmpty() {
    113         fRect.setEmpty();
    114         memset(fRadii, 0, sizeof(fRadii));
    115         fType = kEmpty_Type;
    116 
    117         SkDEBUGCODE(this->validate();)
    118     }
    119 
    120     /**
    121      * Set this RR to match the supplied rect. All radii will be 0.
    122      */
    123     void setRect(const SkRect& rect) {
    124         if (rect.isEmpty()) {
    125             this->setEmpty();
    126             return;
    127         }
    128 
    129         fRect = rect;
    130         memset(fRadii, 0, sizeof(fRadii));
    131         fType = kRect_Type;
    132 
    133         SkDEBUGCODE(this->validate();)
    134     }
    135 
    136     /**
    137      * Set this RR to match the supplied oval. All x radii will equal half the
    138      * width and all y radii will equal half the height.
    139      */
    140     void setOval(const SkRect& oval) {
    141         if (oval.isEmpty()) {
    142             this->setEmpty();
    143             return;
    144         }
    145 
    146         SkScalar xRad = SkScalarHalf(oval.width());
    147         SkScalar yRad = SkScalarHalf(oval.height());
    148 
    149         fRect = oval;
    150         for (int i = 0; i < 4; ++i) {
    151             fRadii[i].set(xRad, yRad);
    152         }
    153         fType = kOval_Type;
    154 
    155         SkDEBUGCODE(this->validate();)
    156     }
    157 
    158     /**
    159      * Initialize the RR with the same radii for all four corners.
    160      */
    161     void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
    162 
    163     /**
    164      * Initialize the RR with potentially different radii for all four corners.
    165      */
    166     void setRectRadii(const SkRect& rect, const SkVector radii[4]);
    167 
    168     // The radii are stored in UL, UR, LR, LL order.
    169     enum Corner {
    170         kUpperLeft_Corner,
    171         kUpperRight_Corner,
    172         kLowerRight_Corner,
    173         kLowerLeft_Corner
    174     };
    175 
    176     const SkRect& rect() const { return fRect; }
    177     const SkVector& radii(Corner corner) const { return fRadii[corner]; }
    178     const SkRect& getBounds() const { return fRect; }
    179 
    180     /**
    181      *  When a rrect is simple, all of its radii are equal. This returns one
    182      *  of those radii. This call requires the rrect to be non-complex.
    183      */
    184     const SkVector& getSimpleRadii() const {
    185         SkASSERT(!this->isComplex());
    186         return fRadii[0];
    187     }
    188 
    189     friend bool operator==(const SkRRect& a, const SkRRect& b) {
    190         return a.fRect == b.fRect &&
    191                SkScalarsEqual(SkTCast<const SkScalar*>(a.fRadii), SkTCast<const SkScalar*>(b.fRadii), 8);
    192     }
    193 
    194     friend bool operator!=(const SkRRect& a, const SkRRect& b) {
    195         return a.fRect != b.fRect ||
    196                !SkScalarsEqual(SkTCast<const SkScalar*>(a.fRadii), SkTCast<const SkScalar*>(b.fRadii), 8);
    197     }
    198 
    199     /**
    200      *  Returns true if (p.fX,p.fY) is inside the RR, and the RR
    201      *  is not empty.
    202      *
    203      *  Contains treats the left and top differently from the right and bottom.
    204      *  The left and top coordinates of the RR are themselves considered
    205      *  to be inside, while the right and bottom are not. All the points on the
    206      *  edges of the corners are considered to be inside.
    207      */
    208     bool contains(const SkPoint& p) const {
    209         return contains(p.fX, p.fY);
    210     }
    211 
    212     /**
    213      *  Returns true if (x,y) is inside the RR, and the RR
    214      *  is not empty.
    215      *
    216      *  Contains treats the left and top differently from the right and bottom.
    217      *  The left and top coordinates of the RR are themselves considered
    218      *  to be inside, while the right and bottom are not. All the points on the
    219      *  edges of the corners are considered to be inside.
    220      */
    221     bool contains(SkScalar x, SkScalar y) const;
    222 
    223     /**
    224      *  Call inset on the bounds, and adjust the radii to reflect what happens
    225      *  in stroking: If the corner is sharp (no curvature), leave it alone,
    226      *  otherwise we grow/shrink the radii by the amount of the inset. If a
    227      *  given radius becomes negative, it is pinned to 0.
    228      *
    229      *  It is valid for dst == this.
    230      */
    231     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
    232 
    233     void inset(SkScalar dx, SkScalar dy) {
    234         this->inset(dx, dy, this);
    235     }
    236 
    237     /**
    238      *  Call outset on the bounds, and adjust the radii to reflect what happens
    239      *  in stroking: If the corner is sharp (no curvature), leave it alone,
    240      *  otherwise we grow/shrink the radii by the amount of the inset. If a
    241      *  given radius becomes negative, it is pinned to 0.
    242      *
    243      *  It is valid for dst == this.
    244      */
    245     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
    246         this->inset(-dx, -dy, dst);
    247     }
    248     void outset(SkScalar dx, SkScalar dy) {
    249         this->inset(-dx, -dy, this);
    250     }
    251 
    252     SkDEBUGCODE(void validate() const;)
    253 
    254     enum {
    255         kSizeInMemory = 12 * sizeof(SkScalar)
    256     };
    257 
    258     /**
    259      *  Write the rrect into the specified buffer. This is guaranteed to always
    260      *  write kSizeInMemory bytes, and that value is guaranteed to always be
    261      *  a multiple of 4. Return kSizeInMemory.
    262      */
    263     uint32_t writeToMemory(void* buffer) const;
    264 
    265     /**
    266      *  Read the rrect from the specified buffer. This is guaranteed to always
    267      *  read kSizeInMemory bytes, and that value is guaranteed to always be
    268      *  a multiple of 4. Return kSizeInMemory.
    269      */
    270     uint32_t readFromMemory(const void* buffer);
    271 
    272 private:
    273     SkRect fRect;
    274     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
    275     SkVector fRadii[4];
    276     mutable Type fType;
    277     // TODO: add padding so we can use memcpy for flattening and not copy
    278     // uninitialized data
    279 
    280     void computeType() const;
    281 
    282     // to access fRadii directly
    283     friend class SkPath;
    284 };
    285 
    286 #endif
    287