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