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
dump(std::string & desc,int depth) const641 void SkRRect::dump(std::string& desc, int depth) const {
642 std::string split(depth, '\t');
643 desc += split + "\n SkRRect:{ \n";
644
645 fRect.dump(desc, depth + 1);
646 desc += split + "\t const SkPoint corners[] = {\n";
647 for (int i = 0; i < 4; ++i) {
648 fRadii[i].dump(desc, depth + 1);
649 }
650 desc += split + "\t}\n";
651 desc += split + "\t fType:" + std::to_string(fType) + "\n";
652 desc += split + "}\n";
653 }
654
655 ///////////////////////////////////////////////////////////////////////////////
656
657 /**
658 * We need all combinations of predicates to be true to have a "safe" radius value.
659 */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)660 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
661 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
662 rad >= 0;
663 }
664
isValid() const665 bool SkRRect::isValid() const {
666 if (!AreRectAndRadiiValid(fRect, fRadii)) {
667 return false;
668 }
669
670 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
671 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
672 bool allRadiiSame = true;
673
674 for (int i = 1; i < 4; ++i) {
675 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
676 allRadiiZero = false;
677 }
678
679 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
680 allRadiiSame = false;
681 }
682
683 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
684 allCornersSquare = false;
685 }
686 }
687 bool patchesOfNine = radii_are_nine_patch(fRadii);
688
689 if (fType < 0 || fType > kLastType) {
690 return false;
691 }
692
693 switch (fType) {
694 case kEmpty_Type:
695 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
696 return false;
697 }
698 break;
699 case kRect_Type:
700 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701 return false;
702 }
703 break;
704 case kOval_Type:
705 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
706 return false;
707 }
708
709 for (int i = 0; i < 4; ++i) {
710 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
711 !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
712 return false;
713 }
714 }
715 break;
716 case kSimple_Type:
717 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
718 return false;
719 }
720 break;
721 case kNinePatch_Type:
722 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
723 !patchesOfNine) {
724 return false;
725 }
726 break;
727 case kComplex_Type:
728 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
729 patchesOfNine) {
730 return false;
731 }
732 break;
733 }
734
735 return true;
736 }
737
AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])738 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
739 if (!rect.isFinite() || !rect.isSorted()) {
740 return false;
741 }
742 for (int i = 0; i < 4; ++i) {
743 if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
744 !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
745 return false;
746 }
747 }
748 return true;
749 }
750 ///////////////////////////////////////////////////////////////////////////////
751
InnerBounds(const SkRRect & rr)752 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
753 if (rr.isEmpty() || rr.isRect()) {
754 return rr.rect();
755 }
756
757 // We start with the outer bounds of the round rect and consider three subsets and take the
758 // one with maximum area. The first two are the horizontal and vertical rects inset from the
759 // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
760 // the exact solution when all corners have the same radii (the radii do not have to be
761 // circular).
762 SkRect innerBounds = rr.getBounds();
763 SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
764 SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
765 SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
766 SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
767
768 // Select maximum inset per edge, which may move an adjacent corner of the inscribed
769 // rectangle off of the rounded-rect path, but that is acceptable given that the general
770 // equation for inscribed area is non-trivial to evaluate.
771 SkScalar leftShift = std::max(tl.fX, bl.fX);
772 SkScalar topShift = std::max(tl.fY, tr.fY);
773 SkScalar rightShift = std::max(tr.fX, br.fX);
774 SkScalar bottomShift = std::max(bl.fY, br.fY);
775
776 SkScalar dw = leftShift + rightShift;
777 SkScalar dh = topShift + bottomShift;
778
779 // Area removed by shifting left/right
780 SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
781 // And by shifting top/bottom
782 SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
783 // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
784 // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
785 // safe shift per edge (since the shifts already are the max radius for that edge).
786 // - We actually scale by a value slightly increased to make it so that the shifted corners are
787 // safely inside the curves, otherwise numerical stability can cause it to fail contains().
788 static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
789 SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
790
791 if (horizArea > vertArea && horizArea > innerArea) {
792 // Cut off corners by insetting left and right
793 innerBounds.fLeft += leftShift;
794 innerBounds.fRight -= rightShift;
795 } else if (vertArea > innerArea) {
796 // Cut off corners by insetting top and bottom
797 innerBounds.fTop += topShift;
798 innerBounds.fBottom -= bottomShift;
799 } else if (innerArea > 0.f) {
800 // Inset on all sides, scaled to touch
801 innerBounds.fLeft += kScale * leftShift;
802 innerBounds.fRight -= kScale * rightShift;
803 innerBounds.fTop += kScale * topShift;
804 innerBounds.fBottom -= kScale * bottomShift;
805 } else {
806 // Inner region would collapse to empty
807 return SkRect::MakeEmpty();
808 }
809
810 SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
811 return innerBounds;
812 }
813
ConservativeIntersect(const SkRRect & a,const SkRRect & b)814 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
815 // Returns the coordinate of the rect matching the corner enum.
816 auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
817 switch(corner) {
818 case SkRRect::kUpperLeft_Corner: return {r.fLeft, r.fTop};
819 case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
820 case SkRRect::kLowerLeft_Corner: return {r.fLeft, r.fBottom};
821 case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
822 default: SkUNREACHABLE;
823 }
824 };
825 // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
826 // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
827 // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
828 auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
829 switch(corner) {
830 case SkRRect::kUpperLeft_Corner: return a.fX >= b.fX && a.fY >= b.fY;
831 case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
832 case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
833 case SkRRect::kLowerLeft_Corner: return a.fX >= b.fX && a.fY <= b.fY;
834 default: SkUNREACHABLE;
835 }
836 };
837
838 auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
839 SkPoint test = getCorner(r, corner);
840 SkPoint aCorner = getCorner(a.rect(), corner);
841 SkPoint bCorner = getCorner(b.rect(), corner);
842
843 if (test == aCorner && test == bCorner) {
844 // The round rects share a corner anchor, so pick A or B such that its X and Y radii
845 // are both larger than the other rrect's, or return false if neither A or B has the max
846 // corner radii (this is more permissive than the single corner tests below).
847 SkVector aRadii = a.radii(corner);
848 SkVector bRadii = b.radii(corner);
849 if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
850 *radii = aRadii;
851 return true;
852 } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
853 *radii = bRadii;
854 return true;
855 } else {
856 return false;
857 }
858 } else if (test == aCorner) {
859 // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
860 // so we resrict it to when the corners have the same radii. If not, we use the more
861 // conservative test that the extreme point of A's bounding box is contained in B.
862 *radii = a.radii(corner);
863 if (*radii == b.radii(corner)) {
864 return insideCorner(corner, aCorner, bCorner); // A inside B
865 } else {
866 return b.checkCornerContainment(aCorner.fX, aCorner.fY);
867 }
868 } else if (test == bCorner) {
869 // Mirror of the above
870 *radii = b.radii(corner);
871 if (*radii == a.radii(corner)) {
872 return insideCorner(corner, bCorner, aCorner); // B inside A
873 } else {
874 return a.checkCornerContainment(bCorner.fX, bCorner.fY);
875 }
876 } else {
877 // This is a corner formed by two straight edges of A and B, so confirm that it is
878 // contained in both (if not, then the intersection can't be a round rect).
879 *radii = {0.f, 0.f};
880 return a.checkCornerContainment(test.fX, test.fY) &&
881 b.checkCornerContainment(test.fX, test.fY);
882 }
883 };
884
885 // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
886 // valid existing SkRRects, we know we are finite.
887 SkRRect intersection;
888 if (!intersection.fRect.intersect(a.rect(), b.rect())) {
889 // Definitely no intersection
890 return SkRRect::MakeEmpty();
891 }
892
893 const SkRRect::Corner corners[] = {
894 SkRRect::kUpperLeft_Corner,
895 SkRRect::kUpperRight_Corner,
896 SkRRect::kLowerRight_Corner,
897 SkRRect::kLowerLeft_Corner
898 };
899 // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
900 // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
901 // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
902 // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
903 // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
904 // intersection shape.
905 for (auto c : corners) {
906 if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
907 return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
908 }
909 }
910
911 // Check for radius overlap along the four edges, since the earlier evaluation was only a
912 // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
913 // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
914 // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
915 // the intersection shape is definitively not a round rect.
916 if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
917 intersection.scaleRadii()) {
918 return SkRRect::MakeEmpty();
919 }
920
921 // The intersection is an rrect of the given radii. Potentially all 4 corners could have
922 // been simplified to (0,0) radii, making the intersection a rectangle.
923 intersection.computeType();
924 return intersection;
925 }
926