blob: 32d62856bc90fddec1a33196f8f691ce8506de68 [file] [log] [blame]
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00001/*
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
reed@google.com4ed0fb72012-12-12 20:48:18 +000014class SkPath;
15
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000016// 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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000028// add entry points (clipRRect, drawRRect) - plumb down to SkBaseDevice
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000029// 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
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000038//
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000039
40/** \class SkRRect
41
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000042 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,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000045 setRectRadii, etc.)
46
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000047 This class is intended to roughly match CSS' border-*-*-radius capabilities.
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000048 This means:
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000049 If either of a corner's radii are 0 the corner will be square.
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000050 Negative radii are not allowed (they are clamped to zero).
51 If the corner curves overlap they will be proportionally reduced to fit.
52*/
53class SK_API SkRRect {
54public:
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000055 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000056 * Enum to capture the various possible subtypes of RR. Accessed
57 * by type(). The subtypes become progressively less restrictive.
58 */
59 enum Type {
tomhudson@google.com0da23a52012-12-03 11:41:21 +000060 // !< Internal indicator that the sub type must be computed.
61 kUnknown_Type = -1,
62
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000063 // !< 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)
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000068 kRect_Type,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000069
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000070 //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000071 //!< and >= width/2 and all the y radii are equal and >= height/2
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000072 kOval_Type,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000073
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000074 //!< 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
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000076 //!< the curves) nor a rect (i.e., both radii are non-zero)
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000077 kSimple_Type,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000078
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
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000085 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000086 * Returns the RR's sub type.
87 */
reed@google.com4ed0fb72012-12-12 20:48:18 +000088 Type getType() const {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000089 SkDEBUGCODE(this->validate();)
90
91 if (kUnknown_Type == fType) {
92 this->computeType();
93 }
94 SkASSERT(kUnknown_Type != fType);
95 return fType;
96 }
97
reed@google.com4ed0fb72012-12-12 20:48:18 +000098 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
mike@reedtribe.org37071642012-12-17 02:10:42 +0000106 SkScalar width() const { return fRect.width(); }
107 SkScalar height() const { return fRect.height(); }
108
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000109 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000110 * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
111 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000112 void setEmpty() {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000113 fRect.setEmpty();
114 memset(fRadii, 0, sizeof(fRadii));
115 fType = kEmpty_Type;
116
117 SkDEBUGCODE(this->validate();)
118 }
119
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000120 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000121 * 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
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000136 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000137 * 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
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000158 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000159 * Initialize the RR with the same radii for all four corners.
160 */
161 void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
162
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000163 /**
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000164 * Initialize the RR with potentially different radii for all four corners.
165 */
166 void setRectRadii(const SkRect& rect, const SkVector radii[4]);
167
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000168 // The radii are stored in UL, UR, LR, LL order.
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000169 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]; }
reed@google.com4ed0fb72012-12-12 20:48:18 +0000178 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 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000188
189 friend bool operator==(const SkRRect& a, const SkRRect& b) {
190 return a.fRect == b.fRect &&
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000191 SkScalarsEqual(a.fRadii[0].asScalars(),
192 b.fRadii[0].asScalars(), 8);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000193 }
194
195 friend bool operator!=(const SkRRect& a, const SkRRect& b) {
196 return a.fRect != b.fRect ||
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000197 !SkScalarsEqual(a.fRadii[0].asScalars(),
198 b.fRadii[0].asScalars(), 8);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000199 }
200
201 /**
202 * Returns true if (p.fX,p.fY) is inside the RR, and the RR
203 * is not empty.
204 *
205 * Contains treats the left and top differently from the right and bottom.
206 * The left and top coordinates of the RR are themselves considered
207 * to be inside, while the right and bottom are not. All the points on the
208 * edges of the corners are considered to be inside.
209 */
210 bool contains(const SkPoint& p) const {
211 return contains(p.fX, p.fY);
212 }
213
214 /**
215 * Returns true if (x,y) is inside the RR, and the RR
216 * is not empty.
217 *
218 * Contains treats the left and top differently from the right and bottom.
219 * The left and top coordinates of the RR are themselves considered
220 * to be inside, while the right and bottom are not. All the points on the
221 * edges of the corners are considered to be inside.
222 */
223 bool contains(SkScalar x, SkScalar y) const;
224
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000225 /**
226 * Call inset on the bounds, and adjust the radii to reflect what happens
227 * in stroking: If the corner is sharp (no curvature), leave it alone,
228 * otherwise we grow/shrink the radii by the amount of the inset. If a
229 * given radius becomes negative, it is pinned to 0.
230 *
231 * It is valid for dst == this.
232 */
mike@reedtribe.org37071642012-12-17 02:10:42 +0000233 void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000234
mike@reedtribe.org37071642012-12-17 02:10:42 +0000235 void inset(SkScalar dx, SkScalar dy) {
236 this->inset(dx, dy, this);
237 }
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000238
239 /**
240 * Call outset on the bounds, and adjust the radii to reflect what happens
241 * in stroking: If the corner is sharp (no curvature), leave it alone,
242 * otherwise we grow/shrink the radii by the amount of the inset. If a
243 * given radius becomes negative, it is pinned to 0.
244 *
245 * It is valid for dst == this.
246 */
mike@reedtribe.org37071642012-12-17 02:10:42 +0000247 void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
248 this->inset(-dx, -dy, dst);
249 }
250 void outset(SkScalar dx, SkScalar dy) {
251 this->inset(-dx, -dy, this);
252 }
253
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000254 /**
255 * Returns true if 'rect' is wholy inside the RR, and both
256 * are not empty.
257 */
258 bool contains(const SkRect& rect) const;
259
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000260 SkDEBUGCODE(void validate() const;)
261
reed@google.com4ed0fb72012-12-12 20:48:18 +0000262 enum {
263 kSizeInMemory = 12 * sizeof(SkScalar)
264 };
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000265
reed@google.com4ed0fb72012-12-12 20:48:18 +0000266 /**
267 * Write the rrect into the specified buffer. This is guaranteed to always
268 * write kSizeInMemory bytes, and that value is guaranteed to always be
269 * a multiple of 4. Return kSizeInMemory.
270 */
271 uint32_t writeToMemory(void* buffer) const;
272
273 /**
274 * Read the rrect from the specified buffer. This is guaranteed to always
275 * read kSizeInMemory bytes, and that value is guaranteed to always be
276 * a multiple of 4. Return kSizeInMemory.
277 */
278 uint32_t readFromMemory(const void* buffer);
279
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000280private:
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000281 SkRect fRect;
282 // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000283 SkVector fRadii[4];
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000284 mutable Type fType;
285 // TODO: add padding so we can use memcpy for flattening and not copy
286 // uninitialized data
287
288 void computeType() const;
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000289 bool checkCornerContainment(SkScalar x, SkScalar y) const;
reed@google.com4ed0fb72012-12-12 20:48:18 +0000290
291 // to access fRadii directly
292 friend class SkPath;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000293};
294
robertphillips@google.com85203012012-11-29 13:49:33 +0000295#endif