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