gfx/skia/trunk/src/core/SkRRect.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:98f6dbb30764
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 #include "SkRRect.h"
9 #include "SkMatrix.h"
10
11 ///////////////////////////////////////////////////////////////////////////////
12
13 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
14 if (rect.isEmpty()) {
15 this->setEmpty();
16 return;
17 }
18
19 if (xRad <= 0 || yRad <= 0) {
20 // all corners are square in this case
21 this->setRect(rect);
22 return;
23 }
24
25 if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
26 SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
27 SkScalarDiv(rect.height(), yRad + yRad));
28 SkASSERT(scale < SK_Scalar1);
29 xRad = SkScalarMul(xRad, scale);
30 yRad = SkScalarMul(yRad, scale);
31 }
32
33 fRect = rect;
34 for (int i = 0; i < 4; ++i) {
35 fRadii[i].set(xRad, yRad);
36 }
37 fType = kSimple_Type;
38 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
39 fType = kOval_Type;
40 // TODO: assert that all the x&y radii are already W/2 & H/2
41 }
42
43 SkDEBUGCODE(this->validate();)
44 }
45
46 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
47 if (rect.isEmpty()) {
48 this->setEmpty();
49 return;
50 }
51
52 fRect = rect;
53 memcpy(fRadii, radii, sizeof(fRadii));
54
55 bool allCornersSquare = true;
56
57 // Clamp negative radii to zero
58 for (int i = 0; i < 4; ++i) {
59 if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
60 // In this case we are being a little fast & loose. Since one of
61 // the radii is 0 the corner is square. However, the other radii
62 // could still be non-zero and play in the global scale factor
63 // computation.
64 fRadii[i].fX = 0;
65 fRadii[i].fY = 0;
66 } else {
67 allCornersSquare = false;
68 }
69 }
70
71 if (allCornersSquare) {
72 this->setRect(rect);
73 return;
74 }
75
76 // Proportionally scale down all radii to fit. Find the minimum ratio
77 // of a side and the radii on that side (for all four sides) and use
78 // that to scale down _all_ the radii. This algorithm is from the
79 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
80 // Curves:
81 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
82 // Si is the sum of the two corresponding radii of the corners on side i,
83 // and Ltop = Lbottom = the width of the box,
84 // and Lleft = Lright = the height of the box.
85 // If f < 1, then all corner radii are reduced by multiplying them by f."
86 SkScalar scale = SK_Scalar1;
87
88 if (fRadii[0].fX + fRadii[1].fX > rect.width()) {
89 scale = SkMinScalar(scale,
90 SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX));
91 }
92 if (fRadii[1].fY + fRadii[2].fY > rect.height()) {
93 scale = SkMinScalar(scale,
94 SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY));
95 }
96 if (fRadii[2].fX + fRadii[3].fX > rect.width()) {
97 scale = SkMinScalar(scale,
98 SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX));
99 }
100 if (fRadii[3].fY + fRadii[0].fY > rect.height()) {
101 scale = SkMinScalar(scale,
102 SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY));
103 }
104
105 if (scale < SK_Scalar1) {
106 for (int i = 0; i < 4; ++i) {
107 fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale);
108 fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale);
109 }
110 }
111
112 // At this point we're either oval, simple, or complex (not empty or rect)
113 // but we lazily resolve the type to avoid the work if the information
114 // isn't required.
115 fType = (SkRRect::Type) kUnknown_Type;
116
117 SkDEBUGCODE(this->validate();)
118 }
119
120 // This method determines if a point known to be inside the RRect's bounds is
121 // inside all the corners.
122 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
123 SkPoint canonicalPt; // (x,y) translated to one of the quadrants
124 int index;
125
126 if (kOval_Type == this->type()) {
127 canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
128 index = kUpperLeft_Corner; // any corner will do in this case
129 } else {
130 if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
131 y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
132 // UL corner
133 index = kUpperLeft_Corner;
134 canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
135 y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
136 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
137 } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
138 y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
139 // LL corner
140 index = kLowerLeft_Corner;
141 canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
142 y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
143 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
144 } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
145 y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
146 // UR corner
147 index = kUpperRight_Corner;
148 canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
149 y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
150 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
151 } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
152 y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
153 // LR corner
154 index = kLowerRight_Corner;
155 canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
156 y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
157 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
158 } else {
159 // not in any of the corners
160 return true;
161 }
162 }
163
164 // A point is in an ellipse (in standard position) if:
165 // x^2 y^2
166 // ----- + ----- <= 1
167 // a^2 b^2
168 // or :
169 // b^2*x^2 + a^2*y^2 <= (ab)^2
170 SkScalar dist = SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) +
171 SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX));
172 return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY));
173 }
174
175 bool SkRRect::allCornersCircular() const {
176 return fRadii[0].fX == fRadii[0].fY &&
177 fRadii[1].fX == fRadii[1].fY &&
178 fRadii[2].fX == fRadii[2].fY &&
179 fRadii[3].fX == fRadii[3].fY;
180 }
181
182 bool SkRRect::contains(const SkRect& rect) const {
183 if (!this->getBounds().contains(rect)) {
184 // If 'rect' isn't contained by the RR's bounds then the
185 // RR definitely doesn't contain it
186 return false;
187 }
188
189 if (this->isRect()) {
190 // the prior test was sufficient
191 return true;
192 }
193
194 // At this point we know all four corners of 'rect' are inside the
195 // bounds of of this RR. Check to make sure all the corners are inside
196 // all the curves
197 return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
198 this->checkCornerContainment(rect.fRight, rect.fTop) &&
199 this->checkCornerContainment(rect.fRight, rect.fBottom) &&
200 this->checkCornerContainment(rect.fLeft, rect.fBottom);
201 }
202
203 // There is a simplified version of this method in setRectXY
204 void SkRRect::computeType() const {
205 SkDEBUGCODE(this->validate();)
206
207 if (fRect.isEmpty()) {
208 fType = kEmpty_Type;
209 return;
210 }
211
212 bool allRadiiEqual = true; // are all x radii equal and all y radii?
213 bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
214
215 for (int i = 1; i < 4; ++i) {
216 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
217 // if either radius is zero the corner is square so both have to
218 // be non-zero to have a rounded corner
219 allCornersSquare = false;
220 }
221 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
222 allRadiiEqual = false;
223 }
224 }
225
226 if (allCornersSquare) {
227 fType = kRect_Type;
228 return;
229 }
230
231 if (allRadiiEqual) {
232 if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
233 fRadii[0].fY >= SkScalarHalf(fRect.height())) {
234 fType = kOval_Type;
235 } else {
236 fType = kSimple_Type;
237 }
238 return;
239 }
240
241 fType = kComplex_Type;
242 }
243
244 static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
245 const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
246 | SkMatrix::kPerspective_Mask);
247 return (matrix.getType() & m) == 0;
248 }
249
250 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
251 if (NULL == dst) {
252 return false;
253 }
254
255 // Assert that the caller is not trying to do this in place, which
256 // would violate const-ness. Do not return false though, so that
257 // if they know what they're doing and want to violate it they can.
258 SkASSERT(dst != this);
259
260 if (matrix.isIdentity()) {
261 *dst = *this;
262 return true;
263 }
264
265 // If transform supported 90 degree rotations (which it could), we could
266 // use SkMatrix::rectStaysRect() to check for a valid transformation.
267 if (!matrix_only_scale_and_translate(matrix)) {
268 return false;
269 }
270
271 SkRect newRect;
272 if (!matrix.mapRect(&newRect, fRect)) {
273 return false;
274 }
275
276 // At this point, this is guaranteed to succeed, so we can modify dst.
277 dst->fRect = newRect;
278
279 // Now scale each corner
280 SkScalar xScale = matrix.getScaleX();
281 const bool flipX = xScale < 0;
282 if (flipX) {
283 xScale = -xScale;
284 }
285 SkScalar yScale = matrix.getScaleY();
286 const bool flipY = yScale < 0;
287 if (flipY) {
288 yScale = -yScale;
289 }
290
291 // Scale the radii without respecting the flip.
292 for (int i = 0; i < 4; ++i) {
293 dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
294 dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
295 }
296
297 // Now swap as necessary.
298 if (flipX) {
299 if (flipY) {
300 // Swap with opposite corners
301 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
302 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
303 } else {
304 // Only swap in x
305 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
306 SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
307 }
308 } else if (flipY) {
309 // Only swap in y
310 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
311 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
312 }
313
314 // Since the only transforms that were allowed are scale and translate, the type
315 // remains unchanged.
316 dst->fType = fType;
317
318 SkDEBUGCODE(dst->validate();)
319
320 return true;
321 }
322
323 ///////////////////////////////////////////////////////////////////////////////
324
325 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
326 SkRect r = fRect;
327
328 r.inset(dx, dy);
329 if (r.isEmpty()) {
330 dst->setEmpty();
331 return;
332 }
333
334 SkVector radii[4];
335 memcpy(radii, fRadii, sizeof(radii));
336 for (int i = 0; i < 4; ++i) {
337 if (radii[i].fX) {
338 radii[i].fX -= dx;
339 }
340 if (radii[i].fY) {
341 radii[i].fY -= dy;
342 }
343 }
344 dst->setRectRadii(r, radii);
345 }
346
347 ///////////////////////////////////////////////////////////////////////////////
348
349 size_t SkRRect::writeToMemory(void* buffer) const {
350 SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
351
352 memcpy(buffer, &fRect, sizeof(SkRect));
353 memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
354 return kSizeInMemory;
355 }
356
357 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
358 if (length < kSizeInMemory) {
359 return 0;
360 }
361
362 SkScalar storage[12];
363 SkASSERT(sizeof(storage) == kSizeInMemory);
364
365 // we make a local copy, to ensure alignment before we cast
366 memcpy(storage, buffer, kSizeInMemory);
367
368 this->setRectRadii(*(const SkRect*)&storage[0],
369 (const SkVector*)&storage[4]);
370 return kSizeInMemory;
371 }
372
373 ///////////////////////////////////////////////////////////////////////////////
374
375 #ifdef SK_DEBUG
376 void SkRRect::validate() const {
377 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
378 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
379 bool allRadiiSame = true;
380
381 for (int i = 1; i < 4; ++i) {
382 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
383 allRadiiZero = false;
384 }
385
386 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
387 allRadiiSame = false;
388 }
389
390 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
391 allCornersSquare = false;
392 }
393 }
394
395 switch (fType) {
396 case kEmpty_Type:
397 SkASSERT(fRect.isEmpty());
398 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
399
400 SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
401 0 == fRect.fRight && 0 == fRect.fBottom);
402 break;
403 case kRect_Type:
404 SkASSERT(!fRect.isEmpty());
405 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
406 break;
407 case kOval_Type:
408 SkASSERT(!fRect.isEmpty());
409 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
410
411 for (int i = 0; i < 4; ++i) {
412 SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
413 SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
414 }
415 break;
416 case kSimple_Type:
417 SkASSERT(!fRect.isEmpty());
418 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
419 break;
420 case kComplex_Type:
421 SkASSERT(!fRect.isEmpty());
422 SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
423 break;
424 case kUnknown_Type:
425 // no limits on this
426 break;
427 }
428 }
429 #endif // SK_DEBUG
430
431 ///////////////////////////////////////////////////////////////////////////////

mercurial