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