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