Line data Source code
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 <cmath>
9 : #include "SkRRect.h"
10 : #include "SkMatrix.h"
11 : #include "SkScaleToSides.h"
12 :
13 : ///////////////////////////////////////////////////////////////////////////////
14 :
15 0 : void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
16 0 : fRect = rect;
17 0 : fRect.sort();
18 :
19 0 : if (fRect.isEmpty() || !fRect.isFinite()) {
20 0 : this->setEmpty();
21 0 : return;
22 : }
23 :
24 0 : if (!SkScalarsAreFinite(xRad, yRad)) {
25 0 : xRad = yRad = 0; // devolve into a simple rect
26 : }
27 0 : if (xRad <= 0 || yRad <= 0) {
28 : // all corners are square in this case
29 0 : this->setRect(rect);
30 0 : return;
31 : }
32 :
33 0 : if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
34 0 : SkScalar scale = SkMinScalar(fRect.width() / (xRad + xRad), fRect.height() / (yRad + yRad));
35 0 : SkASSERT(scale < SK_Scalar1);
36 0 : xRad *= scale;
37 0 : yRad *= scale;
38 : }
39 :
40 0 : for (int i = 0; i < 4; ++i) {
41 0 : fRadii[i].set(xRad, yRad);
42 : }
43 0 : fType = kSimple_Type;
44 0 : if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
45 0 : fType = kOval_Type;
46 : // TODO: assert that all the x&y radii are already W/2 & H/2
47 : }
48 :
49 0 : SkASSERT(this->isValid());
50 : }
51 :
52 0 : void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
53 : SkScalar rightRad, SkScalar bottomRad) {
54 0 : fRect = rect;
55 0 : fRect.sort();
56 :
57 0 : if (fRect.isEmpty() || !fRect.isFinite()) {
58 0 : this->setEmpty();
59 0 : return;
60 : }
61 :
62 0 : const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
63 0 : if (!SkScalarsAreFinite(array, 4)) {
64 0 : this->setRect(rect); // devolve into a simple rect
65 0 : return;
66 : }
67 :
68 0 : leftRad = SkMaxScalar(leftRad, 0);
69 0 : topRad = SkMaxScalar(topRad, 0);
70 0 : rightRad = SkMaxScalar(rightRad, 0);
71 0 : bottomRad = SkMaxScalar(bottomRad, 0);
72 :
73 0 : SkScalar scale = SK_Scalar1;
74 0 : if (leftRad + rightRad > fRect.width()) {
75 0 : scale = fRect.width() / (leftRad + rightRad);
76 : }
77 0 : if (topRad + bottomRad > fRect.height()) {
78 0 : scale = SkMinScalar(scale, fRect.height() / (topRad + bottomRad));
79 : }
80 :
81 0 : if (scale < SK_Scalar1) {
82 0 : leftRad *= scale;
83 0 : topRad *= scale;
84 0 : rightRad *= scale;
85 0 : bottomRad *= scale;
86 : }
87 :
88 0 : if (leftRad == rightRad && topRad == bottomRad) {
89 0 : if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
90 0 : fType = kOval_Type;
91 0 : } else if (0 == leftRad || 0 == topRad) {
92 : // If the left and (by equality check above) right radii are zero then it is a rect.
93 : // Same goes for top/bottom.
94 0 : fType = kRect_Type;
95 0 : leftRad = 0;
96 0 : topRad = 0;
97 0 : rightRad = 0;
98 0 : bottomRad = 0;
99 : } else {
100 0 : fType = kSimple_Type;
101 : }
102 : } else {
103 0 : fType = kNinePatch_Type;
104 : }
105 :
106 0 : fRadii[kUpperLeft_Corner].set(leftRad, topRad);
107 0 : fRadii[kUpperRight_Corner].set(rightRad, topRad);
108 0 : fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
109 0 : fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
110 :
111 0 : SkASSERT(this->isValid());
112 : }
113 :
114 : // These parameters intentionally double. Apropos crbug.com/463920, if one of the
115 : // radii is huge while the other is small, single precision math can completely
116 : // miss the fact that a scale is required.
117 0 : static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
118 0 : if ((rad1 + rad2) > limit) {
119 0 : return SkTMin(curMin, limit / (rad1 + rad2));
120 : }
121 0 : return curMin;
122 : }
123 :
124 0 : void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
125 0 : fRect = rect;
126 0 : fRect.sort();
127 :
128 0 : if (fRect.isEmpty() || !fRect.isFinite()) {
129 0 : this->setEmpty();
130 0 : return;
131 : }
132 :
133 0 : if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
134 0 : this->setRect(rect); // devolve into a simple rect
135 0 : return;
136 : }
137 :
138 0 : memcpy(fRadii, radii, sizeof(fRadii));
139 :
140 0 : bool allCornersSquare = true;
141 :
142 : // Clamp negative radii to zero
143 0 : for (int i = 0; i < 4; ++i) {
144 0 : if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
145 : // In this case we are being a little fast & loose. Since one of
146 : // the radii is 0 the corner is square. However, the other radii
147 : // could still be non-zero and play in the global scale factor
148 : // computation.
149 0 : fRadii[i].fX = 0;
150 0 : fRadii[i].fY = 0;
151 : } else {
152 0 : allCornersSquare = false;
153 : }
154 : }
155 :
156 0 : if (allCornersSquare) {
157 0 : this->setRect(rect);
158 0 : return;
159 : }
160 :
161 0 : this->scaleRadii();
162 : }
163 :
164 0 : void SkRRect::scaleRadii() {
165 :
166 : // Proportionally scale down all radii to fit. Find the minimum ratio
167 : // of a side and the radii on that side (for all four sides) and use
168 : // that to scale down _all_ the radii. This algorithm is from the
169 : // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
170 : // Curves:
171 : // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
172 : // Si is the sum of the two corresponding radii of the corners on side i,
173 : // and Ltop = Lbottom = the width of the box,
174 : // and Lleft = Lright = the height of the box.
175 : // If f < 1, then all corner radii are reduced by multiplying them by f."
176 0 : double scale = 1.0;
177 :
178 : // The sides of the rectangle may be larger than a float.
179 0 : double width = (double)fRect.fRight - (double)fRect.fLeft;
180 0 : double height = (double)fRect.fBottom - (double)fRect.fTop;
181 0 : scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale);
182 0 : scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
183 0 : scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale);
184 0 : scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
185 :
186 0 : if (scale < 1.0) {
187 0 : SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX);
188 0 : SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
189 0 : SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX);
190 0 : SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
191 : }
192 :
193 : // At this point we're either oval, simple, or complex (not empty or rect).
194 0 : this->computeType();
195 :
196 0 : SkASSERT(this->isValid());
197 0 : }
198 :
199 : // This method determines if a point known to be inside the RRect's bounds is
200 : // inside all the corners.
201 0 : bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
202 : SkPoint canonicalPt; // (x,y) translated to one of the quadrants
203 : int index;
204 :
205 0 : if (kOval_Type == this->type()) {
206 0 : canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
207 0 : index = kUpperLeft_Corner; // any corner will do in this case
208 : } else {
209 0 : if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
210 0 : y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
211 : // UL corner
212 0 : index = kUpperLeft_Corner;
213 0 : canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
214 0 : y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
215 0 : SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
216 0 : } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
217 0 : y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
218 : // LL corner
219 0 : index = kLowerLeft_Corner;
220 0 : canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
221 0 : y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
222 0 : SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
223 0 : } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
224 0 : y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
225 : // UR corner
226 0 : index = kUpperRight_Corner;
227 0 : canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
228 0 : y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
229 0 : SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
230 0 : } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
231 0 : y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
232 : // LR corner
233 0 : index = kLowerRight_Corner;
234 0 : canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
235 0 : y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
236 0 : SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
237 : } else {
238 : // not in any of the corners
239 0 : return true;
240 : }
241 : }
242 :
243 : // A point is in an ellipse (in standard position) if:
244 : // x^2 y^2
245 : // ----- + ----- <= 1
246 : // a^2 b^2
247 : // or :
248 : // b^2*x^2 + a^2*y^2 <= (ab)^2
249 0 : SkScalar dist = SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) +
250 0 : SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX);
251 0 : return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
252 : }
253 :
254 0 : bool SkRRect::allCornersCircular() const {
255 0 : return fRadii[0].fX == fRadii[0].fY &&
256 0 : fRadii[1].fX == fRadii[1].fY &&
257 0 : fRadii[2].fX == fRadii[2].fY &&
258 0 : fRadii[3].fX == fRadii[3].fY;
259 : }
260 :
261 0 : bool SkRRect::contains(const SkRect& rect) const {
262 0 : if (!this->getBounds().contains(rect)) {
263 : // If 'rect' isn't contained by the RR's bounds then the
264 : // RR definitely doesn't contain it
265 0 : return false;
266 : }
267 :
268 0 : if (this->isRect()) {
269 : // the prior test was sufficient
270 0 : return true;
271 : }
272 :
273 : // At this point we know all four corners of 'rect' are inside the
274 : // bounds of of this RR. Check to make sure all the corners are inside
275 : // all the curves
276 0 : return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
277 0 : this->checkCornerContainment(rect.fRight, rect.fTop) &&
278 0 : this->checkCornerContainment(rect.fRight, rect.fBottom) &&
279 0 : this->checkCornerContainment(rect.fLeft, rect.fBottom);
280 : }
281 :
282 0 : static bool radii_are_nine_patch(const SkVector radii[4]) {
283 0 : return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
284 0 : radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
285 0 : radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
286 0 : radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
287 : }
288 :
289 : // There is a simplified version of this method in setRectXY
290 0 : void SkRRect::computeType() {
291 : struct Validator {
292 0 : Validator(const SkRRect* r) : fR(r) {}
293 0 : ~Validator() { SkASSERT(fR->isValid()); }
294 : const SkRRect* fR;
295 0 : } autoValidate(this);
296 :
297 0 : if (fRect.isEmpty()) {
298 0 : fType = kEmpty_Type;
299 0 : return;
300 : }
301 :
302 0 : bool allRadiiEqual = true; // are all x radii equal and all y radii?
303 0 : bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
304 :
305 0 : for (int i = 1; i < 4; ++i) {
306 0 : if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
307 : // if either radius is zero the corner is square so both have to
308 : // be non-zero to have a rounded corner
309 0 : allCornersSquare = false;
310 : }
311 0 : if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
312 0 : allRadiiEqual = false;
313 : }
314 : }
315 :
316 0 : if (allCornersSquare) {
317 0 : fType = kRect_Type;
318 0 : return;
319 : }
320 :
321 0 : if (allRadiiEqual) {
322 0 : if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
323 0 : fRadii[0].fY >= SkScalarHalf(fRect.height())) {
324 0 : fType = kOval_Type;
325 : } else {
326 0 : fType = kSimple_Type;
327 : }
328 0 : return;
329 : }
330 :
331 0 : if (radii_are_nine_patch(fRadii)) {
332 0 : fType = kNinePatch_Type;
333 : } else {
334 0 : fType = kComplex_Type;
335 : }
336 : }
337 :
338 0 : static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
339 : const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
340 0 : | SkMatrix::kPerspective_Mask);
341 0 : return (matrix.getType() & m) == 0;
342 : }
343 :
344 0 : bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
345 0 : if (nullptr == dst) {
346 0 : return false;
347 : }
348 :
349 : // Assert that the caller is not trying to do this in place, which
350 : // would violate const-ness. Do not return false though, so that
351 : // if they know what they're doing and want to violate it they can.
352 0 : SkASSERT(dst != this);
353 :
354 0 : if (matrix.isIdentity()) {
355 0 : *dst = *this;
356 0 : return true;
357 : }
358 :
359 : // If transform supported 90 degree rotations (which it could), we could
360 : // use SkMatrix::rectStaysRect() to check for a valid transformation.
361 0 : if (!matrix_only_scale_and_translate(matrix)) {
362 0 : return false;
363 : }
364 :
365 : SkRect newRect;
366 0 : if (!matrix.mapRect(&newRect, fRect)) {
367 0 : return false;
368 : }
369 :
370 : // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
371 : // some dimension of the rect, so we need to check for that.
372 0 : if (newRect.isEmpty()) {
373 0 : dst->setEmpty();
374 0 : return true;
375 : }
376 :
377 : // At this point, this is guaranteed to succeed, so we can modify dst.
378 0 : dst->fRect = newRect;
379 :
380 : // Since the only transforms that were allowed are scale and translate, the type
381 : // remains unchanged.
382 0 : dst->fType = fType;
383 :
384 0 : if (kOval_Type == fType) {
385 0 : for (int i = 0; i < 4; ++i) {
386 0 : dst->fRadii[i].fX = SkScalarHalf(newRect.width());
387 0 : dst->fRadii[i].fY = SkScalarHalf(newRect.height());
388 : }
389 0 : SkASSERT(dst->isValid());
390 0 : return true;
391 : }
392 :
393 : // Now scale each corner
394 0 : SkScalar xScale = matrix.getScaleX();
395 0 : const bool flipX = xScale < 0;
396 0 : if (flipX) {
397 0 : xScale = -xScale;
398 : }
399 0 : SkScalar yScale = matrix.getScaleY();
400 0 : const bool flipY = yScale < 0;
401 0 : if (flipY) {
402 0 : yScale = -yScale;
403 : }
404 :
405 : // Scale the radii without respecting the flip.
406 0 : for (int i = 0; i < 4; ++i) {
407 0 : dst->fRadii[i].fX = fRadii[i].fX * xScale;
408 0 : dst->fRadii[i].fY = fRadii[i].fY * yScale;
409 : }
410 :
411 : // Now swap as necessary.
412 0 : if (flipX) {
413 0 : if (flipY) {
414 : // Swap with opposite corners
415 0 : SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
416 0 : SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
417 : } else {
418 : // Only swap in x
419 0 : SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
420 0 : SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
421 : }
422 0 : } else if (flipY) {
423 : // Only swap in y
424 0 : SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
425 0 : SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
426 : }
427 :
428 0 : dst->scaleRadii();
429 :
430 0 : return true;
431 : }
432 :
433 : ///////////////////////////////////////////////////////////////////////////////
434 :
435 0 : void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
436 0 : const SkRect r = fRect.makeInset(dx, dy);
437 :
438 0 : if (r.isEmpty()) {
439 0 : dst->setEmpty();
440 0 : return;
441 : }
442 :
443 : SkVector radii[4];
444 0 : memcpy(radii, fRadii, sizeof(radii));
445 0 : for (int i = 0; i < 4; ++i) {
446 0 : if (radii[i].fX) {
447 0 : radii[i].fX -= dx;
448 : }
449 0 : if (radii[i].fY) {
450 0 : radii[i].fY -= dy;
451 : }
452 : }
453 0 : dst->setRectRadii(r, radii);
454 : }
455 :
456 : ///////////////////////////////////////////////////////////////////////////////
457 :
458 0 : size_t SkRRect::writeToMemory(void* buffer) const {
459 : // Serialize only the rect and corners, but not the derived type tag.
460 0 : memcpy(buffer, this, kSizeInMemory);
461 0 : return kSizeInMemory;
462 : }
463 :
464 0 : size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
465 0 : if (length < kSizeInMemory) {
466 0 : return 0;
467 : }
468 :
469 : // Deserialize rect and corners, then rederive the type tag.
470 0 : memcpy(this, buffer, kSizeInMemory);
471 0 : this->computeType();
472 :
473 0 : return kSizeInMemory;
474 : }
475 :
476 : #include "SkString.h"
477 : #include "SkStringUtils.h"
478 :
479 0 : void SkRRect::dump(bool asHex) const {
480 0 : SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
481 :
482 0 : fRect.dump(asHex);
483 0 : SkString line("const SkPoint corners[] = {\n");
484 0 : for (int i = 0; i < 4; ++i) {
485 0 : SkString strX, strY;
486 0 : SkAppendScalar(&strX, fRadii[i].x(), asType);
487 0 : SkAppendScalar(&strY, fRadii[i].y(), asType);
488 0 : line.appendf(" { %s, %s },", strX.c_str(), strY.c_str());
489 0 : if (asHex) {
490 0 : line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
491 : }
492 0 : line.append("\n");
493 : }
494 0 : line.append("};");
495 0 : SkDebugf("%s\n", line.c_str());
496 0 : }
497 :
498 : ///////////////////////////////////////////////////////////////////////////////
499 :
500 : /**
501 : * We need all combinations of predicates to be true to have a "safe" radius value.
502 : */
503 0 : static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
504 0 : return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min);
505 : }
506 :
507 0 : bool SkRRect::isValid() const {
508 0 : bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
509 0 : bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
510 0 : bool allRadiiSame = true;
511 :
512 0 : for (int i = 1; i < 4; ++i) {
513 0 : if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
514 0 : allRadiiZero = false;
515 : }
516 :
517 0 : if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
518 0 : allRadiiSame = false;
519 : }
520 :
521 0 : if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
522 0 : allCornersSquare = false;
523 : }
524 : }
525 0 : bool patchesOfNine = radii_are_nine_patch(fRadii);
526 :
527 0 : switch (fType) {
528 : case kEmpty_Type:
529 0 : if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
530 0 : return false;
531 : }
532 0 : break;
533 : case kRect_Type:
534 0 : if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
535 0 : return false;
536 : }
537 0 : break;
538 : case kOval_Type:
539 0 : if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
540 0 : return false;
541 : }
542 :
543 0 : for (int i = 0; i < 4; ++i) {
544 0 : if (!SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())) ||
545 0 : !SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))) {
546 0 : return false;
547 : }
548 : }
549 0 : break;
550 : case kSimple_Type:
551 0 : if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
552 0 : return false;
553 : }
554 0 : break;
555 : case kNinePatch_Type:
556 0 : if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
557 0 : !patchesOfNine) {
558 0 : return false;
559 : }
560 0 : break;
561 : case kComplex_Type:
562 0 : if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
563 : patchesOfNine) {
564 0 : return false;
565 : }
566 0 : break;
567 : }
568 :
569 0 : for (int i = 0; i < 4; ++i) {
570 0 : if (!are_radius_check_predicates_valid(fRadii[i].fX, fRect.fLeft, fRect.fRight) ||
571 0 : !are_radius_check_predicates_valid(fRadii[i].fY, fRect.fTop, fRect.fBottom)) {
572 0 : return false;
573 : }
574 : }
575 :
576 0 : return true;
577 : }
578 :
579 : ///////////////////////////////////////////////////////////////////////////////
|