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