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