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
14class SkPath;
15class 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*/
54class SK_API SkRRect {
55public:
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
292private:
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