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 "include/core/SkRRect.h"
9
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkDebug.h"
17 #include "include/private/base/SkFloatingPoint.h"
18 #include "src/base/SkBuffer.h"
19 #include "src/core/SkRRectPriv.h"
20 #include "src/core/SkRectPriv.h"
21 #include "src/core/SkScaleToSides.h"
22 #include "src/core/SkStringUtils.h"
23
24 #include <algorithm>
25 #include <cstring>
26 #include <iterator>
27
28 ///////////////////////////////////////////////////////////////////////////////
29
setOval(const SkRect & oval)30 void SkRRect::setOval(const SkRect& oval) {
31 if (!this->initializeRect(oval)) {
32 return;
33 }
34
35 SkScalar xRad = SkRectPriv::HalfWidth(fRect);
36 SkScalar yRad = SkRectPriv::HalfHeight(fRect);
37
38 if (xRad == 0.0f || yRad == 0.0f) {
39 // All the corners will be square
40 memset(fRadii, 0, sizeof(fRadii));
41 fType = kRect_Type;
42 } else {
43 for (int i = 0; i < 4; ++i) {
44 fRadii[i].set(xRad, yRad);
45 }
46 fType = kOval_Type;
47 }
48
49 SkASSERT(this->isValid());
50 }
51
setRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)52 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
53 if (!this->initializeRect(rect)) {
54 return;
55 }
56
57 if (!SkIsFinite(xRad, yRad)) {
58 xRad = yRad = 0; // devolve into a simple rect
59 }
60
61 if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
62 // At most one of these two divides will be by zero, and neither numerator is zero.
63 SkScalar scale = std::min(sk_ieee_float_divide(fRect. width(), xRad + xRad),
64 sk_ieee_float_divide(fRect.height(), yRad + yRad));
65 SkASSERT(scale < SK_Scalar1);
66 xRad *= scale;
67 yRad *= scale;
68 }
69
70 if (xRad <= 0 || yRad <= 0) {
71 // all corners are square in this case
72 this->setRect(rect);
73 return;
74 }
75
76 for (int i = 0; i < 4; ++i) {
77 fRadii[i].set(xRad, yRad);
78 }
79 fType = kSimple_Type;
80 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
81 fType = kOval_Type;
82 // TODO: assert that all the x&y radii are already W/2 & H/2
83 }
84
85 SkASSERT(this->isValid());
86 }
87
clamp_to_zero(SkVector radii[4])88 static bool clamp_to_zero(SkVector radii[4]) {
89 bool allCornersSquare = true;
90
91 // Clamp negative radii to zero
92 for (int i = 0; i < 4; ++i) {
93 if (radii[i].fX <= 0 || radii[i].fY <= 0) {
94 // In this case we are being a little fast & loose. Since one of
95 // the radii is 0 the corner is square. However, the other radii
96 // could still be non-zero and play in the global scale factor
97 // computation.
98 radii[i].fX = 0;
99 radii[i].fY = 0;
100 } else {
101 allCornersSquare = false;
102 }
103 }
104
105 return allCornersSquare;
106 }
107
radii_are_nine_patch(const SkVector radii[4])108 static bool radii_are_nine_patch(const SkVector radii[4]) {
109 return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
110 radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
111 radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
112 radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
113 }
114
setNinePatch(const SkRect & rect,SkScalar leftRad,SkScalar topRad,SkScalar rightRad,SkScalar bottomRad)115 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
116 SkScalar rightRad, SkScalar bottomRad) {
117 if (!this->initializeRect(rect)) {
118 return;
119 }
120
121 if (!SkIsFinite(leftRad, topRad, rightRad, bottomRad)) {
122 this->setRect(rect); // devolve into a simple rect
123 return;
124 }
125
126 leftRad = std::max(leftRad, 0.0f);
127 topRad = std::max(topRad, 0.0f);
128 rightRad = std::max(rightRad, 0.0f);
129 bottomRad = std::max(bottomRad, 0.0f);
130
131 SkScalar scale = SK_Scalar1;
132 if (leftRad + rightRad > fRect.width()) {
133 scale = fRect.width() / (leftRad + rightRad);
134 }
135 if (topRad + bottomRad > fRect.height()) {
136 scale = std::min(scale, fRect.height() / (topRad + bottomRad));
137 }
138
139 if (scale < SK_Scalar1) {
140 leftRad *= scale;
141 topRad *= scale;
142 rightRad *= scale;
143 bottomRad *= scale;
144 }
145
146 if (leftRad == rightRad && topRad == bottomRad) {
147 if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
148 fType = kOval_Type;
149 } else if (0 == leftRad || 0 == topRad) {
150 // If the left and (by equality check above) right radii are zero then it is a rect.
151 // Same goes for top/bottom.
152 fType = kRect_Type;
153 leftRad = 0;
154 topRad = 0;
155 rightRad = 0;
156 bottomRad = 0;
157 } else {
158 fType = kSimple_Type;
159 }
160 } else {
161 fType = kNinePatch_Type;
162 }
163
164 fRadii[kUpperLeft_Corner].set(leftRad, topRad);
165 fRadii[kUpperRight_Corner].set(rightRad, topRad);
166 fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
167 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
168 if (clamp_to_zero(fRadii)) {
169 this->setRect(rect); // devolve into a simple rect
170 return;
171 }
172 if (fType == kNinePatch_Type && !radii_are_nine_patch(fRadii)) {
173 fType = kComplex_Type;
174 }
175
176 SkASSERT(this->isValid());
177 }
178
179 // These parameters intentionally double. Apropos crbug.com/463920, if one of the
180 // radii is huge while the other is small, single precision math can completely
181 // miss the fact that a scale is required.
compute_min_scale(double rad1,double rad2,double limit,double curMin)182 static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
183 if ((rad1 + rad2) > limit) {
184 return std::min(curMin, limit / (rad1 + rad2));
185 }
186 return curMin;
187 }
188
setRectRadii(const SkRect & rect,const SkVector radii[4])189 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
190 if (!this->initializeRect(rect)) {
191 return;
192 }
193
194 if (!SkIsFinite(&radii[0].fX, 8)) {
195 this->setRect(rect); // devolve into a simple rect
196 return;
197 }
198
199 memcpy(fRadii, radii, sizeof(fRadii));
200
201 if (clamp_to_zero(fRadii)) {
202 this->setRect(rect);
203 return;
204 }
205
206 this->scaleRadii();
207
208 if (!this->isValid()) {
209 this->setRect(rect);
210 return;
211 }
212 }
213
initializeRect(const SkRect & rect)214 bool SkRRect::initializeRect(const SkRect& rect) {
215 // Check this before sorting because sorting can hide nans.
216 if (!rect.isFinite()) {
217 *this = SkRRect();
218 return false;
219 }
220 fRect = rect.makeSorted();
221 if (fRect.isEmpty()) {
222 memset(fRadii, 0, sizeof(fRadii));
223 fType = kEmpty_Type;
224 return false;
225 }
226 return true;
227 }
228
229 // If we can't distinguish one of the radii relative to the other, force it to zero so it
230 // doesn't confuse us later. See crbug.com/850350
231 //
flush_to_zero(SkScalar & a,SkScalar & b)232 static void flush_to_zero(SkScalar& a, SkScalar& b) {
233 SkASSERT(a >= 0);
234 SkASSERT(b >= 0);
235 if (a + b == a) {
236 b = 0;
237 } else if (a + b == b) {
238 a = 0;
239 }
240 }
241
scaleRadii()242 bool SkRRect::scaleRadii() {
243 // Proportionally scale down all radii to fit. Find the minimum ratio
244 // of a side and the radii on that side (for all four sides) and use
245 // that to scale down _all_ the radii. This algorithm is from the
246 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
247 // Curves:
248 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
249 // Si is the sum of the two corresponding radii of the corners on side i,
250 // and Ltop = Lbottom = the width of the box,
251 // and Lleft = Lright = the height of the box.
252 // If f < 1, then all corner radii are reduced by multiplying them by f."
253 double scale = 1.0;
254
255 // The sides of the rectangle may be larger than a float.
256 double width = (double)fRect.fRight - (double)fRect.fLeft;
257 double height = (double)fRect.fBottom - (double)fRect.fTop;
258 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale);
259 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
260 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale);
261 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
262
263 flush_to_zero(fRadii[0].fX, fRadii[1].fX);
264 flush_to_zero(fRadii[1].fY, fRadii[2].fY);
265 flush_to_zero(fRadii[2].fX, fRadii[3].fX);
266 flush_to_zero(fRadii[3].fY, fRadii[0].fY);
267
268 if (scale < 1.0) {
269 SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX);
270 SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
271 SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX);
272 SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
273 }
274
275 // adjust radii may set x or y to zero; set companion to zero as well
276 clamp_to_zero(fRadii);
277
278 // May be simple, oval, or complex, or become a rect/empty if the radii adjustment made them 0
279 this->computeType();
280
281 // TODO: Why can't we assert this here?
282 //SkASSERT(this->isValid());
283
284 return scale < 1.0;
285 }
286
287 // This method determines if a point known to be inside the RRect's bounds is
288 // inside all the corners.
checkCornerContainment(SkScalar x,SkScalar y) const289 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
290 SkPoint canonicalPt; // (x,y) translated to one of the quadrants
291 int index;
292
293 if (kOval_Type == this->type()) {
294 canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
295 index = kUpperLeft_Corner; // any corner will do in this case
296 } else {
297 if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
298 y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
299 // UL corner
300 index = kUpperLeft_Corner;
301 canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
302 y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
303 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
304 } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
305 y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
306 // LL corner
307 index = kLowerLeft_Corner;
308 canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
309 y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
310 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
311 } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
312 y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
313 // UR corner
314 index = kUpperRight_Corner;
315 canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
316 y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
317 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
318 } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
319 y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
320 // LR corner
321 index = kLowerRight_Corner;
322 canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
323 y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
324 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
325 } else {
326 // not in any of the corners
327 return true;
328 }
329 }
330
331 // A point is in an ellipse (in standard position) if:
332 // x^2 y^2
333 // ----- + ----- <= 1
334 // a^2 b^2
335 // or :
336 // b^2*x^2 + a^2*y^2 <= (ab)^2
337 SkScalar dist = SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) +
338 SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX);
339 return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
340 }
341
IsNearlySimpleCircular(const SkRRect & rr,SkScalar tolerance)342 bool SkRRectPriv::IsNearlySimpleCircular(const SkRRect& rr, SkScalar tolerance) {
343 SkScalar simpleRadius = rr.fRadii[0].fX;
344 return SkScalarNearlyEqual(simpleRadius, rr.fRadii[0].fY, tolerance) &&
345 SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fX, tolerance) &&
346 SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fY, tolerance) &&
347 SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fX, tolerance) &&
348 SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fY, tolerance) &&
349 SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fX, tolerance) &&
350 SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fY, tolerance);
351 }
352
AllCornersCircular(const SkRRect & rr,SkScalar tolerance)353 bool SkRRectPriv::AllCornersCircular(const SkRRect& rr, SkScalar tolerance) {
354 return SkScalarNearlyEqual(rr.fRadii[0].fX, rr.fRadii[0].fY, tolerance) &&
355 SkScalarNearlyEqual(rr.fRadii[1].fX, rr.fRadii[1].fY, tolerance) &&
356 SkScalarNearlyEqual(rr.fRadii[2].fX, rr.fRadii[2].fY, tolerance) &&
357 SkScalarNearlyEqual(rr.fRadii[3].fX, rr.fRadii[3].fY, tolerance);
358 }
359
contains(const SkRect & rect) const360 bool SkRRect::contains(const SkRect& rect) const {
361 if (!this->getBounds().contains(rect)) {
362 // If 'rect' isn't contained by the RR's bounds then the
363 // RR definitely doesn't contain it
364 return false;
365 }
366
367 if (this->isRect()) {
368 // the prior test was sufficient
369 return true;
370 }
371
372 // At this point we know all four corners of 'rect' are inside the
373 // bounds of of this RR. Check to make sure all the corners are inside
374 // all the curves
375 return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
376 this->checkCornerContainment(rect.fRight, rect.fTop) &&
377 this->checkCornerContainment(rect.fRight, rect.fBottom) &&
378 this->checkCornerContainment(rect.fLeft, rect.fBottom);
379 }
380
381 // There is a simplified version of this method in setRectXY
computeType()382 void SkRRect::computeType() {
383 if (fRect.isEmpty()) {
384 SkASSERT(fRect.isSorted());
385 for (size_t i = 0; i < std::size(fRadii); ++i) {
386 SkASSERT((fRadii[i] == SkVector{0, 0}));
387 }
388 fType = kEmpty_Type;
389 SkASSERT(this->isValid());
390 return;
391 }
392
393 bool allRadiiEqual = true; // are all x radii equal and all y radii?
394 bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
395
396 for (int i = 1; i < 4; ++i) {
397 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
398 // if either radius is zero the corner is square so both have to
399 // be non-zero to have a rounded corner
400 allCornersSquare = false;
401 }
402 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
403 allRadiiEqual = false;
404 }
405 }
406
407 if (allCornersSquare) {
408 fType = kRect_Type;
409 SkASSERT(this->isValid());
410 return;
411 }
412
413 if (allRadiiEqual) {
414 if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
415 fRadii[0].fY >= SkScalarHalf(fRect.height())) {
416 fType = kOval_Type;
417 } else {
418 fType = kSimple_Type;
419 }
420 SkASSERT(this->isValid());
421 return;
422 }
423
424 if (radii_are_nine_patch(fRadii)) {
425 fType = kNinePatch_Type;
426 } else {
427 fType = kComplex_Type;
428 }
429
430 if (!this->isValid()) {
431 this->setRect(this->rect());
432 SkASSERT(this->isValid());
433 }
434 }
435
transform(const SkMatrix & matrix,SkRRect * dst) const436 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
437 if (nullptr == dst) {
438 return false;
439 }
440
441 // Assert that the caller is not trying to do this in place, which
442 // would violate const-ness. Do not return false though, so that
443 // if they know what they're doing and want to violate it they can.
444 SkASSERT(dst != this);
445
446 if (matrix.isIdentity()) {
447 *dst = *this;
448 return true;
449 }
450
451 if (!matrix.preservesAxisAlignment()) {
452 return false;
453 }
454
455 SkRect newRect;
456 if (!matrix.mapRect(&newRect, fRect)) {
457 return false;
458 }
459
460 // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
461 // some dimension of the rect, so we need to check for that. Note that matrix must be
462 // scale and translate and mapRect() produces a sorted rect. So an empty rect indicates
463 // loss of precision.
464 if (!newRect.isFinite() || newRect.isEmpty()) {
465 return false;
466 }
467
468 // At this point, this is guaranteed to succeed, so we can modify dst.
469 dst->fRect = newRect;
470
471 // Since the only transforms that were allowed are axis aligned, the type
472 // remains unchanged.
473 dst->fType = fType;
474
475 if (kRect_Type == fType) {
476 SkASSERT(dst->isValid());
477 return true;
478 }
479 if (kOval_Type == fType) {
480 for (int i = 0; i < 4; ++i) {
481 dst->fRadii[i].fX = SkScalarHalf(newRect.width());
482 dst->fRadii[i].fY = SkScalarHalf(newRect.height());
483 }
484 SkASSERT(dst->isValid());
485 return true;
486 }
487
488 // Now scale each corner
489 SkScalar xScale = matrix.getScaleX();
490 SkScalar yScale = matrix.getScaleY();
491
492 // There is a rotation of 90 (Clockwise 90) or 270 (Counter clockwise 90).
493 // 180 degrees rotations are simply flipX with a flipY and would come under
494 // a scale transform.
495 if (!matrix.isScaleTranslate()) {
496 // If we got here, the matrix preserves axis alignment (earlier return check) but isn't
497 // a regular scale matrix. To confirm that it's a 90/270 rotation, the scale components are
498 // 0s and the skew components are non-zero (+/-1 if there is no other scale factor).
499 SkASSERT(matrix.getScaleX() == 0.f && matrix.getScaleY() == 0.f &&
500 matrix.getSkewX() != 0.f && matrix.getSkewY() != 0.f);
501 const bool isClockwise = matrix.getSkewX() < 0;
502
503 // The matrix location for scale changes if there is a rotation.
504 // xScale and yScale represent scales applied to the dst radii, so we store the src x scale
505 // in yScale and vice versa.
506 yScale = matrix.getSkewY() * (isClockwise ? 1 : -1);
507 xScale = matrix.getSkewX() * (isClockwise ? -1 : 1);
508
509 const int dir = isClockwise ? 3 : 1;
510 for (int i = 0; i < 4; ++i) {
511 const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir);
512 // Swap X and Y axis for the radii.
513 dst->fRadii[i].fX = fRadii[src].fY;
514 dst->fRadii[i].fY = fRadii[src].fX;
515 }
516 } else {
517 for (int i = 0; i < 4; ++i) {
518 dst->fRadii[i].fX = fRadii[i].fX;
519 dst->fRadii[i].fY = fRadii[i].fY;
520 }
521 }
522
523 const bool flipX = xScale < 0;
524 if (flipX) {
525 xScale = -xScale;
526 }
527
528 const bool flipY = yScale < 0;
529 if (flipY) {
530 yScale = -yScale;
531 }
532
533 // Scale the radii without respecting the flip.
534 for (int i = 0; i < 4; ++i) {
535 dst->fRadii[i].fX *= xScale;
536 dst->fRadii[i].fY *= yScale;
537 }
538
539 // Now swap as necessary.
540 using std::swap;
541 if (flipX) {
542 if (flipY) {
543 // Swap with opposite corners
544 swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
545 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
546 } else {
547 // Only swap in x
548 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
549 swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
550 }
551 } else if (flipY) {
552 // Only swap in y
553 swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
554 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
555 }
556
557 dst->scaleRadii();
558
559 if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) {
560 return false;
561 }
562
563 SkASSERT(dst->isValid());
564 return true;
565 }
566
567 ///////////////////////////////////////////////////////////////////////////////
568
inset(SkScalar dx,SkScalar dy,SkRRect * dst) const569 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
570 SkRect r = fRect.makeInset(dx, dy);
571 bool degenerate = false;
572 if (r.fRight <= r.fLeft) {
573 degenerate = true;
574 r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight);
575 }
576 if (r.fBottom <= r.fTop) {
577 degenerate = true;
578 r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom);
579 }
580 if (degenerate) {
581 dst->fRect = r;
582 memset(dst->fRadii, 0, sizeof(dst->fRadii));
583 dst->fType = kEmpty_Type;
584 return;
585 }
586 if (!r.isFinite()) {
587 *dst = SkRRect();
588 return;
589 }
590
591 SkVector radii[4];
592 memcpy(radii, fRadii, sizeof(radii));
593 for (int i = 0; i < 4; ++i) {
594 if (radii[i].fX) {
595 radii[i].fX -= dx;
596 }
597 if (radii[i].fY) {
598 radii[i].fY -= dy;
599 }
600 }
601 dst->setRectRadii(r, radii);
602 }
603
604 ///////////////////////////////////////////////////////////////////////////////
605
writeToMemory(void * buffer) const606 size_t SkRRect::writeToMemory(void* buffer) const {
607 // Serialize only the rect and corners, but not the derived type tag.
608 memcpy(buffer, this, kSizeInMemory);
609 return kSizeInMemory;
610 }
611
WriteToBuffer(const SkRRect & rr,SkWBuffer * buffer)612 void SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) {
613 // Serialize only the rect and corners, but not the derived type tag.
614 buffer->write(&rr, SkRRect::kSizeInMemory);
615 }
616
readFromMemory(const void * buffer,size_t length)617 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
618 if (length < kSizeInMemory) {
619 return 0;
620 }
621
622 // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect).
623
624 SkRRect raw;
625 memcpy((void*)&raw, buffer, kSizeInMemory);
626 this->setRectRadii(raw.fRect, raw.fRadii);
627 return kSizeInMemory;
628 }
629
ReadFromBuffer(SkRBuffer * buffer,SkRRect * rr)630 bool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) {
631 if (buffer->available() < SkRRect::kSizeInMemory) {
632 return false;
633 }
634 SkRRect storage;
635 return buffer->read(&storage, SkRRect::kSizeInMemory) &&
636 (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory);
637 }
638
dumpToString(bool asHex) const639 SkString SkRRect::dumpToString(bool asHex) const {
640 SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
641
642 SkString line = fRect.dumpToString(asHex);
643 line.appendf("\nconst SkPoint corners[] = {\n");
644 for (int i = 0; i < 4; ++i) {
645 SkString strX, strY;
646 SkAppendScalar(&strX, fRadii[i].x(), asType);
647 SkAppendScalar(&strY, fRadii[i].y(), asType);
648 line.appendf(" { %s, %s },", strX.c_str(), strY.c_str());
649 if (asHex) {
650 line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
651 }
652 line.append("\n");
653 }
654 line.append("};");
655 return line;
656 }
657
dump(bool asHex) const658 void SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); }
659
660 ///////////////////////////////////////////////////////////////////////////////
661
662 /**
663 * We need all combinations of predicates to be true to have a "safe" radius value.
664 */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)665 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
666 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
667 rad >= 0;
668 }
669
isValid() const670 bool SkRRect::isValid() const {
671 if (!AreRectAndRadiiValid(fRect, fRadii)) {
672 return false;
673 }
674
675 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
676 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
677 bool allRadiiSame = true;
678
679 for (int i = 1; i < 4; ++i) {
680 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
681 allRadiiZero = false;
682 }
683
684 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
685 allRadiiSame = false;
686 }
687
688 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
689 allCornersSquare = false;
690 }
691 }
692 bool patchesOfNine = radii_are_nine_patch(fRadii);
693
694 if (fType < 0 || fType > kLastType) {
695 return false;
696 }
697
698 switch (fType) {
699 case kEmpty_Type:
700 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701 return false;
702 }
703 break;
704 case kRect_Type:
705 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
706 return false;
707 }
708 break;
709 case kOval_Type:
710 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
711 return false;
712 }
713
714 for (int i = 0; i < 4; ++i) {
715 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
716 !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
717 return false;
718 }
719 }
720 break;
721 case kSimple_Type:
722 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
723 return false;
724 }
725 break;
726 case kNinePatch_Type:
727 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
728 !patchesOfNine) {
729 return false;
730 }
731 break;
732 case kComplex_Type:
733 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
734 patchesOfNine) {
735 return false;
736 }
737 break;
738 }
739
740 return true;
741 }
742
AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])743 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
744 if (!rect.isFinite() || !rect.isSorted()) {
745 return false;
746 }
747 for (int i = 0; i < 4; ++i) {
748 if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
749 !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
750 return false;
751 }
752 }
753 return true;
754 }
755 ///////////////////////////////////////////////////////////////////////////////
756
InnerBounds(const SkRRect & rr)757 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
758 if (rr.isEmpty() || rr.isRect()) {
759 return rr.rect();
760 }
761
762 // We start with the outer bounds of the round rect and consider three subsets and take the
763 // one with maximum area. The first two are the horizontal and vertical rects inset from the
764 // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
765 // the exact solution when all corners have the same radii (the radii do not have to be
766 // circular).
767 SkRect innerBounds = rr.getBounds();
768 SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
769 SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
770 SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
771 SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
772
773 // Select maximum inset per edge, which may move an adjacent corner of the inscribed
774 // rectangle off of the rounded-rect path, but that is acceptable given that the general
775 // equation for inscribed area is non-trivial to evaluate.
776 SkScalar leftShift = std::max(tl.fX, bl.fX);
777 SkScalar topShift = std::max(tl.fY, tr.fY);
778 SkScalar rightShift = std::max(tr.fX, br.fX);
779 SkScalar bottomShift = std::max(bl.fY, br.fY);
780
781 SkScalar dw = leftShift + rightShift;
782 SkScalar dh = topShift + bottomShift;
783
784 // Area removed by shifting left/right
785 SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
786 // And by shifting top/bottom
787 SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
788 // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
789 // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
790 // safe shift per edge (since the shifts already are the max radius for that edge).
791 // - We actually scale by a value slightly increased to make it so that the shifted corners are
792 // safely inside the curves, otherwise numerical stability can cause it to fail contains().
793 static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
794 SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
795
796 if (horizArea > vertArea && horizArea > innerArea) {
797 // Cut off corners by insetting left and right
798 innerBounds.fLeft += leftShift;
799 innerBounds.fRight -= rightShift;
800 } else if (vertArea > innerArea) {
801 // Cut off corners by insetting top and bottom
802 innerBounds.fTop += topShift;
803 innerBounds.fBottom -= bottomShift;
804 } else if (innerArea > 0.f) {
805 // Inset on all sides, scaled to touch
806 innerBounds.fLeft += kScale * leftShift;
807 innerBounds.fRight -= kScale * rightShift;
808 innerBounds.fTop += kScale * topShift;
809 innerBounds.fBottom -= kScale * bottomShift;
810 } else {
811 // Inner region would collapse to empty
812 return SkRect::MakeEmpty();
813 }
814
815 SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
816 return innerBounds;
817 }
818
ConservativeIntersect(const SkRRect & a,const SkRRect & b)819 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
820 // Returns the coordinate of the rect matching the corner enum.
821 auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
822 switch(corner) {
823 case SkRRect::kUpperLeft_Corner: return {r.fLeft, r.fTop};
824 case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
825 case SkRRect::kLowerLeft_Corner: return {r.fLeft, r.fBottom};
826 case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
827 default: SkUNREACHABLE;
828 }
829 };
830 // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
831 // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
832 // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
833 auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
834 switch(corner) {
835 case SkRRect::kUpperLeft_Corner: return a.fX >= b.fX && a.fY >= b.fY;
836 case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
837 case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
838 case SkRRect::kLowerLeft_Corner: return a.fX >= b.fX && a.fY <= b.fY;
839 default: SkUNREACHABLE;
840 }
841 };
842
843 auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
844 SkPoint test = getCorner(r, corner);
845 SkPoint aCorner = getCorner(a.rect(), corner);
846 SkPoint bCorner = getCorner(b.rect(), corner);
847
848 if (test == aCorner && test == bCorner) {
849 // The round rects share a corner anchor, so pick A or B such that its X and Y radii
850 // are both larger than the other rrect's, or return false if neither A or B has the max
851 // corner radii (this is more permissive than the single corner tests below).
852 SkVector aRadii = a.radii(corner);
853 SkVector bRadii = b.radii(corner);
854 if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
855 *radii = aRadii;
856 return true;
857 } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
858 *radii = bRadii;
859 return true;
860 } else {
861 return false;
862 }
863 } else if (test == aCorner) {
864 // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
865 // so we resrict it to when the corners have the same radii. If not, we use the more
866 // conservative test that the extreme point of A's bounding box is contained in B.
867 *radii = a.radii(corner);
868 if (*radii == b.radii(corner)) {
869 return insideCorner(corner, aCorner, bCorner); // A inside B
870 } else {
871 return b.checkCornerContainment(aCorner.fX, aCorner.fY);
872 }
873 } else if (test == bCorner) {
874 // Mirror of the above
875 *radii = b.radii(corner);
876 if (*radii == a.radii(corner)) {
877 return insideCorner(corner, bCorner, aCorner); // B inside A
878 } else {
879 return a.checkCornerContainment(bCorner.fX, bCorner.fY);
880 }
881 } else {
882 // This is a corner formed by two straight edges of A and B, so confirm that it is
883 // contained in both (if not, then the intersection can't be a round rect).
884 *radii = {0.f, 0.f};
885 return a.checkCornerContainment(test.fX, test.fY) &&
886 b.checkCornerContainment(test.fX, test.fY);
887 }
888 };
889
890 // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
891 // valid existing SkRRects, we know we are finite.
892 SkRRect intersection;
893 if (!intersection.fRect.intersect(a.rect(), b.rect())) {
894 // Definitely no intersection
895 return SkRRect::MakeEmpty();
896 }
897
898 const SkRRect::Corner corners[] = {
899 SkRRect::kUpperLeft_Corner,
900 SkRRect::kUpperRight_Corner,
901 SkRRect::kLowerRight_Corner,
902 SkRRect::kLowerLeft_Corner
903 };
904 // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
905 // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
906 // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
907 // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
908 // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
909 // intersection shape.
910 for (auto c : corners) {
911 if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
912 return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
913 }
914 }
915
916 // Check for radius overlap along the four edges, since the earlier evaluation was only a
917 // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
918 // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
919 // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
920 // the intersection shape is definitively not a round rect.
921 if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
922 intersection.scaleRadii()) {
923 return SkRRect::MakeEmpty();
924 }
925
926 // The intersection is an rrect of the given radii. Potentially all 4 corners could have
927 // been simplified to (0,0) radii, making the intersection a rectangle.
928 intersection.computeType();
929 return intersection;
930 }
931