blob: 1188989cdb8f7070637f89cb6300660aeaa8cd6f [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
herb7cf12dd2016-01-11 08:08:56 -08008#include <cmath>
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00009#include "SkRRect.h"
scroggo@google.com20e3cd22013-11-05 15:54:42 +000010#include "SkMatrix.h"
herb7cf12dd2016-01-11 08:08:56 -080011#include "SkScaleToSides.h"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000012
13///////////////////////////////////////////////////////////////////////////////
14
15void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
robertphillips05302f82015-09-29 11:24:07 -070016 fRect = rect;
17 fRect.sort();
18
19 if (fRect.isEmpty() || !fRect.isFinite()) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000020 this->setEmpty();
21 return;
22 }
23
reed454fa712015-02-10 08:46:22 -080024 if (!SkScalarsAreFinite(xRad, yRad)) {
25 xRad = yRad = 0; // devolve into a simple rect
26 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000027 if (xRad <= 0 || yRad <= 0) {
28 // all corners are square in this case
29 this->setRect(rect);
30 return;
31 }
32
robertphillips05302f82015-09-29 11:24:07 -070033 if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
34 SkScalar scale = SkMinScalar(fRect.width() / (xRad + xRad), fRect.height() / (yRad + yRad));
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000035 SkASSERT(scale < SK_Scalar1);
36 xRad = SkScalarMul(xRad, scale);
37 yRad = SkScalarMul(yRad, scale);
38 }
39
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000040 for (int i = 0; i < 4; ++i) {
41 fRadii[i].set(xRad, yRad);
42 }
43 fType = kSimple_Type;
44 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
45 fType = kOval_Type;
robertphillips@google.com5b332112013-01-30 20:33:12 +000046 // TODO: assert that all the x&y radii are already W/2 & H/2
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000047 }
48
Robert Phillips49da3342016-09-23 14:23:22 -040049 SkASSERT(this->isValid());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000050}
51
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000052void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
53 SkScalar rightRad, SkScalar bottomRad) {
robertphillips05302f82015-09-29 11:24:07 -070054 fRect = rect;
55 fRect.sort();
56
57 if (fRect.isEmpty() || !fRect.isFinite()) {
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000058 this->setEmpty();
59 return;
60 }
61
reed454fa712015-02-10 08:46:22 -080062 const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
63 if (!SkScalarsAreFinite(array, 4)) {
64 this->setRect(rect); // devolve into a simple rect
65 return;
66 }
67
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000068 leftRad = SkMaxScalar(leftRad, 0);
69 topRad = SkMaxScalar(topRad, 0);
70 rightRad = SkMaxScalar(rightRad, 0);
71 bottomRad = SkMaxScalar(bottomRad, 0);
72
73 SkScalar scale = SK_Scalar1;
robertphillips05302f82015-09-29 11:24:07 -070074 if (leftRad + rightRad > fRect.width()) {
75 scale = fRect.width() / (leftRad + rightRad);
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000076 }
robertphillips05302f82015-09-29 11:24:07 -070077 if (topRad + bottomRad > fRect.height()) {
78 scale = SkMinScalar(scale, fRect.height() / (topRad + bottomRad));
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000079 }
80
81 if (scale < SK_Scalar1) {
82 leftRad = SkScalarMul(leftRad, scale);
83 topRad = SkScalarMul(topRad, scale);
84 rightRad = SkScalarMul(rightRad, scale);
85 bottomRad = SkScalarMul(bottomRad, scale);
86 }
87
88 if (leftRad == rightRad && topRad == bottomRad) {
robertphillips05302f82015-09-29 11:24:07 -070089 if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000090 fType = kOval_Type;
91 } else if (0 == leftRad || 0 == topRad) {
92 // If the left and (by equality check above) right radii are zero then it is a rect.
93 // Same goes for top/bottom.
94 fType = kRect_Type;
95 leftRad = 0;
96 topRad = 0;
97 rightRad = 0;
98 bottomRad = 0;
99 } else {
100 fType = kSimple_Type;
101 }
102 } else {
103 fType = kNinePatch_Type;
104 }
105
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000106 fRadii[kUpperLeft_Corner].set(leftRad, topRad);
107 fRadii[kUpperRight_Corner].set(rightRad, topRad);
108 fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
109 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
110
Robert Phillips49da3342016-09-23 14:23:22 -0400111 SkASSERT(this->isValid());
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000112}
113
robertphillips2a679ae2015-03-13 09:53:01 -0700114// These parameters intentionally double. Apropos crbug.com/463920, if one of the
115// radii is huge while the other is small, single precision math can completely
116// miss the fact that a scale is required.
117static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
118 if ((rad1 + rad2) > limit) {
119 return SkTMin(curMin, limit / (rad1 + rad2));
120 }
121 return curMin;
122}
123
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000124void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
robertphillips05302f82015-09-29 11:24:07 -0700125 fRect = rect;
126 fRect.sort();
127
128 if (fRect.isEmpty() || !fRect.isFinite()) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000129 this->setEmpty();
130 return;
131 }
132
reed454fa712015-02-10 08:46:22 -0800133 if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
134 this->setRect(rect); // devolve into a simple rect
135 return;
136 }
137
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000138 memcpy(fRadii, radii, sizeof(fRadii));
139
140 bool allCornersSquare = true;
141
142 // Clamp negative radii to zero
143 for (int i = 0; i < 4; ++i) {
144 if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
145 // In this case we are being a little fast & loose. Since one of
146 // the radii is 0 the corner is square. However, the other radii
147 // could still be non-zero and play in the global scale factor
148 // computation.
149 fRadii[i].fX = 0;
150 fRadii[i].fY = 0;
151 } else {
152 allCornersSquare = false;
153 }
154 }
155
156 if (allCornersSquare) {
157 this->setRect(rect);
158 return;
159 }
160
caryclark5a70bc72016-02-23 10:32:40 -0800161 this->scaleRadii();
162}
163
164void SkRRect::scaleRadii() {
165
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000166 // Proportionally scale down all radii to fit. Find the minimum ratio
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000167 // of a side and the radii on that side (for all four sides) and use
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000168 // that to scale down _all_ the radii. This algorithm is from the
169 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
170 // Curves:
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000171 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000172 // Si is the sum of the two corresponding radii of the corners on side i,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000173 // and Ltop = Lbottom = the width of the box,
174 // and Lleft = Lright = the height of the box.
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000175 // If f < 1, then all corner radii are reduced by multiplying them by f."
robertphillips2a679ae2015-03-13 09:53:01 -0700176 double scale = 1.0;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000177
herb7cf12dd2016-01-11 08:08:56 -0800178 // The sides of the rectangle may be larger than a float.
179 double width = (double)fRect.fRight - (double)fRect.fLeft;
180 double height = (double)fRect.fBottom - (double)fRect.fTop;
181 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale);
182 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
183 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale);
184 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000185
robertphillips2a679ae2015-03-13 09:53:01 -0700186 if (scale < 1.0) {
herb97293c62016-01-22 11:58:55 -0800187 SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX);
188 SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
189 SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX);
190 SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000191 }
192
mtklein8aacf202014-12-18 13:29:54 -0800193 // At this point we're either oval, simple, or complex (not empty or rect).
194 this->computeType();
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000195
Robert Phillips49da3342016-09-23 14:23:22 -0400196 SkASSERT(this->isValid());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000197}
198
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000199// This method determines if a point known to be inside the RRect's bounds is
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000200// inside all the corners.
201bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000202 SkPoint canonicalPt; // (x,y) translated to one of the quadrants
203 int index;
204
205 if (kOval_Type == this->type()) {
206 canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
207 index = kUpperLeft_Corner; // any corner will do in this case
208 } else {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000209 if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000210 y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
211 // UL corner
212 index = kUpperLeft_Corner;
213 canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
214 y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
215 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
216 } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
217 y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
218 // LL corner
219 index = kLowerLeft_Corner;
220 canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
221 y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
222 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
223 } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
224 y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
225 // UR corner
226 index = kUpperRight_Corner;
227 canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
228 y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
229 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
230 } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
231 y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
232 // LR corner
233 index = kLowerRight_Corner;
234 canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
235 y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
236 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
237 } else {
238 // not in any of the corners
239 return true;
240 }
241 }
242
243 // A point is in an ellipse (in standard position) if:
244 // x^2 y^2
245 // ----- + ----- <= 1
246 // a^2 b^2
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000247 // or :
248 // b^2*x^2 + a^2*y^2 <= (ab)^2
249 SkScalar dist = SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) +
250 SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX));
251 return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY));
252}
253
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000254bool SkRRect::allCornersCircular() const {
255 return fRadii[0].fX == fRadii[0].fY &&
256 fRadii[1].fX == fRadii[1].fY &&
257 fRadii[2].fX == fRadii[2].fY &&
258 fRadii[3].fX == fRadii[3].fY;
259}
260
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000261bool SkRRect::contains(const SkRect& rect) const {
262 if (!this->getBounds().contains(rect)) {
263 // If 'rect' isn't contained by the RR's bounds then the
264 // RR definitely doesn't contain it
265 return false;
266 }
267
268 if (this->isRect()) {
269 // the prior test was sufficient
270 return true;
271 }
272
273 // At this point we know all four corners of 'rect' are inside the
274 // bounds of of this RR. Check to make sure all the corners are inside
275 // all the curves
276 return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
277 this->checkCornerContainment(rect.fRight, rect.fTop) &&
278 this->checkCornerContainment(rect.fRight, rect.fBottom) &&
279 this->checkCornerContainment(rect.fLeft, rect.fBottom);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000280}
281
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000282static bool radii_are_nine_patch(const SkVector radii[4]) {
283 return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
284 radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
285 radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
286 radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
287}
288
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000289// There is a simplified version of this method in setRectXY
mtklein8aacf202014-12-18 13:29:54 -0800290void SkRRect::computeType() {
291 struct Validator {
292 Validator(const SkRRect* r) : fR(r) {}
Robert Phillips49da3342016-09-23 14:23:22 -0400293 ~Validator() { SkASSERT(fR->isValid()); }
mtklein8aacf202014-12-18 13:29:54 -0800294 const SkRRect* fR;
295 } autoValidate(this);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000296
297 if (fRect.isEmpty()) {
298 fType = kEmpty_Type;
299 return;
300 }
301
302 bool allRadiiEqual = true; // are all x radii equal and all y radii?
303 bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
304
305 for (int i = 1; i < 4; ++i) {
306 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
307 // if either radius is zero the corner is square so both have to
308 // be non-zero to have a rounded corner
309 allCornersSquare = false;
310 }
311 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
312 allRadiiEqual = false;
313 }
314 }
315
316 if (allCornersSquare) {
317 fType = kRect_Type;
318 return;
319 }
320
321 if (allRadiiEqual) {
322 if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
323 fRadii[0].fY >= SkScalarHalf(fRect.height())) {
324 fType = kOval_Type;
325 } else {
326 fType = kSimple_Type;
327 }
328 return;
329 }
330
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000331 if (radii_are_nine_patch(fRadii)) {
332 fType = kNinePatch_Type;
333 } else {
334 fType = kComplex_Type;
335 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000336}
337
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000338static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
339 const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
340 | SkMatrix::kPerspective_Mask);
341 return (matrix.getType() & m) == 0;
342}
343
344bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700345 if (nullptr == dst) {
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000346 return false;
347 }
348
349 // Assert that the caller is not trying to do this in place, which
350 // would violate const-ness. Do not return false though, so that
351 // if they know what they're doing and want to violate it they can.
352 SkASSERT(dst != this);
353
354 if (matrix.isIdentity()) {
355 *dst = *this;
356 return true;
357 }
358
359 // If transform supported 90 degree rotations (which it could), we could
360 // use SkMatrix::rectStaysRect() to check for a valid transformation.
361 if (!matrix_only_scale_and_translate(matrix)) {
362 return false;
363 }
364
365 SkRect newRect;
366 if (!matrix.mapRect(&newRect, fRect)) {
367 return false;
368 }
369
reed694b0d12015-02-13 14:33:02 -0800370 // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
371 // some dimension of the rect, so we need to check for that.
372 if (newRect.isEmpty()) {
373 dst->setEmpty();
374 return true;
375 }
376
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000377 // At this point, this is guaranteed to succeed, so we can modify dst.
378 dst->fRect = newRect;
379
robertphillipse5c1e3c2014-06-27 08:59:26 -0700380 // Since the only transforms that were allowed are scale and translate, the type
381 // remains unchanged.
382 dst->fType = fType;
383
384 if (kOval_Type == fType) {
385 for (int i = 0; i < 4; ++i) {
386 dst->fRadii[i].fX = SkScalarHalf(newRect.width());
387 dst->fRadii[i].fY = SkScalarHalf(newRect.height());
388 }
Robert Phillips49da3342016-09-23 14:23:22 -0400389 SkASSERT(dst->isValid());
robertphillipse5c1e3c2014-06-27 08:59:26 -0700390 return true;
391 }
392
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000393 // Now scale each corner
394 SkScalar xScale = matrix.getScaleX();
395 const bool flipX = xScale < 0;
396 if (flipX) {
397 xScale = -xScale;
398 }
399 SkScalar yScale = matrix.getScaleY();
400 const bool flipY = yScale < 0;
401 if (flipY) {
402 yScale = -yScale;
403 }
404
405 // Scale the radii without respecting the flip.
406 for (int i = 0; i < 4; ++i) {
407 dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
408 dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
409 }
410
411 // Now swap as necessary.
412 if (flipX) {
413 if (flipY) {
414 // Swap with opposite corners
415 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
416 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
417 } else {
418 // Only swap in x
419 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
420 SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
421 }
422 } else if (flipY) {
423 // Only swap in y
424 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
425 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
426 }
427
caryclark5a70bc72016-02-23 10:32:40 -0800428 dst->scaleRadii();
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000429
430 return true;
431}
432
reed@google.com4ed0fb72012-12-12 20:48:18 +0000433///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.org37071642012-12-17 02:10:42 +0000434
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000435void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
reed11fa2242015-03-13 06:08:28 -0700436 const SkRect r = fRect.makeInset(dx, dy);
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +0000437
mike@reedtribe.org37071642012-12-17 02:10:42 +0000438 if (r.isEmpty()) {
439 dst->setEmpty();
440 return;
441 }
442
443 SkVector radii[4];
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000444 memcpy(radii, fRadii, sizeof(radii));
mike@reedtribe.org37071642012-12-17 02:10:42 +0000445 for (int i = 0; i < 4; ++i) {
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000446 if (radii[i].fX) {
447 radii[i].fX -= dx;
448 }
449 if (radii[i].fY) {
450 radii[i].fY -= dy;
451 }
mike@reedtribe.org37071642012-12-17 02:10:42 +0000452 }
453 dst->setRectRadii(r, radii);
454}
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000455
mike@reedtribe.org37071642012-12-17 02:10:42 +0000456///////////////////////////////////////////////////////////////////////////////
457
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000458size_t SkRRect::writeToMemory(void* buffer) const {
reed@google.com4ed0fb72012-12-12 20:48:18 +0000459 SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
460
461 memcpy(buffer, &fRect, sizeof(SkRect));
462 memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
463 return kSizeInMemory;
464}
465
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000466size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
467 if (length < kSizeInMemory) {
468 return 0;
469 }
470
reed@google.com4ed0fb72012-12-12 20:48:18 +0000471 SkScalar storage[12];
472 SkASSERT(sizeof(storage) == kSizeInMemory);
473
474 // we make a local copy, to ensure alignment before we cast
475 memcpy(storage, buffer, kSizeInMemory);
476
477 this->setRectRadii(*(const SkRect*)&storage[0],
478 (const SkVector*)&storage[4]);
479 return kSizeInMemory;
480}
481
reede05fed02014-12-15 07:59:53 -0800482#include "SkString.h"
483#include "SkStringUtils.h"
484
485void SkRRect::dump(bool asHex) const {
486 SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
487
488 fRect.dump(asHex);
489 SkString line("const SkPoint corners[] = {\n");
490 for (int i = 0; i < 4; ++i) {
491 SkString strX, strY;
492 SkAppendScalar(&strX, fRadii[i].x(), asType);
493 SkAppendScalar(&strY, fRadii[i].y(), asType);
494 line.appendf(" { %s, %s },", strX.c_str(), strY.c_str());
495 if (asHex) {
496 line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
497 }
498 line.append("\n");
499 }
500 line.append("};");
501 SkDebugf("%s\n", line.c_str());
bsalomonb6b02522014-06-09 07:59:06 -0700502}
bsalomonb6b02522014-06-09 07:59:06 -0700503
reed@google.com4ed0fb72012-12-12 20:48:18 +0000504///////////////////////////////////////////////////////////////////////////////
505
reed694b0d12015-02-13 14:33:02 -0800506/**
507 * We need all combinations of predicates to be true to have a "safe" radius value.
508 */
Robert Phillips49da3342016-09-23 14:23:22 -0400509static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
510 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min);
reed694b0d12015-02-13 14:33:02 -0800511}
512
Robert Phillips49da3342016-09-23 14:23:22 -0400513bool SkRRect::isValid() const {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000514 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
515 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
516 bool allRadiiSame = true;
517
518 for (int i = 1; i < 4; ++i) {
519 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
520 allRadiiZero = false;
521 }
522
523 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
524 allRadiiSame = false;
525 }
526
527 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
528 allCornersSquare = false;
529 }
530 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000531 bool patchesOfNine = radii_are_nine_patch(fRadii);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000532
533 switch (fType) {
534 case kEmpty_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400535 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
536 return false;
537 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000538 break;
539 case kRect_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400540 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
541 return false;
542 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000543 break;
544 case kOval_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400545 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
546 return false;
547 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000548
549 for (int i = 0; i < 4; ++i) {
Robert Phillips49da3342016-09-23 14:23:22 -0400550 if (!SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())) ||
551 !SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))) {
552 return false;
553 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000554 }
555 break;
556 case kSimple_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400557 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
558 return false;
559 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000560 break;
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000561 case kNinePatch_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400562 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
563 !patchesOfNine) {
564 return false;
565 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000566 break;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000567 case kComplex_Type:
Robert Phillips49da3342016-09-23 14:23:22 -0400568 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
569 patchesOfNine) {
570 return false;
571 }
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000572 break;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000573 }
reed694b0d12015-02-13 14:33:02 -0800574
575 for (int i = 0; i < 4; ++i) {
Robert Phillips49da3342016-09-23 14:23:22 -0400576 if (!are_radius_check_predicates_valid(fRadii[i].fX, fRect.fLeft, fRect.fRight) ||
577 !are_radius_check_predicates_valid(fRadii[i].fY, fRect.fTop, fRect.fBottom)) {
578 return false;
579 }
reed694b0d12015-02-13 14:33:02 -0800580 }
Robert Phillips49da3342016-09-23 14:23:22 -0400581
582 return true;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000583}
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000584
585///////////////////////////////////////////////////////////////////////////////