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