Flutter Engine
The Flutter Engine
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
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
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
108static bool radii_are_nine_patch(const SkVector radii[4]) {
113}
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
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]);
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
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
Definition: SkStringUtils.h:18
@ kHex_SkScalarAsStringType
Definition: SkStringUtils.h:20
@ kDec_SkScalarAsStringType
Definition: SkStringUtils.h:19
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 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
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
SkRect oval
Definition: SkRecords.h:249
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
const Scalar scale
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
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