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