OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkRRect.h" | 8 #include "SkRRect.h" |
9 #include "SkMatrix.h" | 9 #include "SkMatrix.h" |
10 | 10 |
11 /////////////////////////////////////////////////////////////////////////////// | 11 /////////////////////////////////////////////////////////////////////////////// |
12 | 12 |
13 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { | 13 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { |
14 if (rect.isEmpty() || !rect.isFinite()) { | 14 fRect = rect; |
| 15 fRect.sort(); |
| 16 |
| 17 if (fRect.isEmpty() || !fRect.isFinite()) { |
15 this->setEmpty(); | 18 this->setEmpty(); |
16 return; | 19 return; |
17 } | 20 } |
18 | 21 |
19 if (!SkScalarsAreFinite(xRad, yRad)) { | 22 if (!SkScalarsAreFinite(xRad, yRad)) { |
20 xRad = yRad = 0; // devolve into a simple rect | 23 xRad = yRad = 0; // devolve into a simple rect |
21 } | 24 } |
22 if (xRad <= 0 || yRad <= 0) { | 25 if (xRad <= 0 || yRad <= 0) { |
23 // all corners are square in this case | 26 // all corners are square in this case |
24 this->setRect(rect); | 27 this->setRect(rect); |
25 return; | 28 return; |
26 } | 29 } |
27 | 30 |
28 if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) { | 31 if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) { |
29 SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height()
/ (yRad + yRad)); | 32 SkScalar scale = SkMinScalar(fRect.width() / (xRad + xRad), fRect.height
() / (yRad + yRad)); |
30 SkASSERT(scale < SK_Scalar1); | 33 SkASSERT(scale < SK_Scalar1); |
31 xRad = SkScalarMul(xRad, scale); | 34 xRad = SkScalarMul(xRad, scale); |
32 yRad = SkScalarMul(yRad, scale); | 35 yRad = SkScalarMul(yRad, scale); |
33 } | 36 } |
34 | 37 |
35 fRect = rect; | |
36 for (int i = 0; i < 4; ++i) { | 38 for (int i = 0; i < 4; ++i) { |
37 fRadii[i].set(xRad, yRad); | 39 fRadii[i].set(xRad, yRad); |
38 } | 40 } |
39 fType = kSimple_Type; | 41 fType = kSimple_Type; |
40 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height
())) { | 42 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height
())) { |
41 fType = kOval_Type; | 43 fType = kOval_Type; |
42 // TODO: assert that all the x&y radii are already W/2 & H/2 | 44 // TODO: assert that all the x&y radii are already W/2 & H/2 |
43 } | 45 } |
44 | 46 |
45 SkDEBUGCODE(this->validate();) | 47 SkDEBUGCODE(this->validate();) |
46 } | 48 } |
47 | 49 |
48 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad
, | 50 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad
, |
49 SkScalar rightRad, SkScalar bottomRad) { | 51 SkScalar rightRad, SkScalar bottomRad) { |
50 if (rect.isEmpty() || !rect.isFinite()) { | 52 fRect = rect; |
| 53 fRect.sort(); |
| 54 |
| 55 if (fRect.isEmpty() || !fRect.isFinite()) { |
51 this->setEmpty(); | 56 this->setEmpty(); |
52 return; | 57 return; |
53 } | 58 } |
54 | 59 |
55 const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; | 60 const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; |
56 if (!SkScalarsAreFinite(array, 4)) { | 61 if (!SkScalarsAreFinite(array, 4)) { |
57 this->setRect(rect); // devolve into a simple rect | 62 this->setRect(rect); // devolve into a simple rect |
58 return; | 63 return; |
59 } | 64 } |
60 | 65 |
61 leftRad = SkMaxScalar(leftRad, 0); | 66 leftRad = SkMaxScalar(leftRad, 0); |
62 topRad = SkMaxScalar(topRad, 0); | 67 topRad = SkMaxScalar(topRad, 0); |
63 rightRad = SkMaxScalar(rightRad, 0); | 68 rightRad = SkMaxScalar(rightRad, 0); |
64 bottomRad = SkMaxScalar(bottomRad, 0); | 69 bottomRad = SkMaxScalar(bottomRad, 0); |
65 | 70 |
66 SkScalar scale = SK_Scalar1; | 71 SkScalar scale = SK_Scalar1; |
67 if (leftRad + rightRad > rect.width()) { | 72 if (leftRad + rightRad > fRect.width()) { |
68 scale = rect.width() / (leftRad + rightRad); | 73 scale = fRect.width() / (leftRad + rightRad); |
69 } | 74 } |
70 if (topRad + bottomRad > rect.height()) { | 75 if (topRad + bottomRad > fRect.height()) { |
71 scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad)); | 76 scale = SkMinScalar(scale, fRect.height() / (topRad + bottomRad)); |
72 } | 77 } |
73 | 78 |
74 if (scale < SK_Scalar1) { | 79 if (scale < SK_Scalar1) { |
75 leftRad = SkScalarMul(leftRad, scale); | 80 leftRad = SkScalarMul(leftRad, scale); |
76 topRad = SkScalarMul(topRad, scale); | 81 topRad = SkScalarMul(topRad, scale); |
77 rightRad = SkScalarMul(rightRad, scale); | 82 rightRad = SkScalarMul(rightRad, scale); |
78 bottomRad = SkScalarMul(bottomRad, scale); | 83 bottomRad = SkScalarMul(bottomRad, scale); |
79 } | 84 } |
80 | 85 |
81 if (leftRad == rightRad && topRad == bottomRad) { | 86 if (leftRad == rightRad && topRad == bottomRad) { |
82 if (leftRad >= SkScalarHalf(rect.width()) && topRad >= SkScalarHalf(rect
.height())) { | 87 if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRe
ct.height())) { |
83 fType = kOval_Type; | 88 fType = kOval_Type; |
84 } else if (0 == leftRad || 0 == topRad) { | 89 } else if (0 == leftRad || 0 == topRad) { |
85 // If the left and (by equality check above) right radii are zero th
en it is a rect. | 90 // If the left and (by equality check above) right radii are zero th
en it is a rect. |
86 // Same goes for top/bottom. | 91 // Same goes for top/bottom. |
87 fType = kRect_Type; | 92 fType = kRect_Type; |
88 leftRad = 0; | 93 leftRad = 0; |
89 topRad = 0; | 94 topRad = 0; |
90 rightRad = 0; | 95 rightRad = 0; |
91 bottomRad = 0; | 96 bottomRad = 0; |
92 } else { | 97 } else { |
93 fType = kSimple_Type; | 98 fType = kSimple_Type; |
94 } | 99 } |
95 } else { | 100 } else { |
96 fType = kNinePatch_Type; | 101 fType = kNinePatch_Type; |
97 } | 102 } |
98 | 103 |
99 fRect = rect; | |
100 fRadii[kUpperLeft_Corner].set(leftRad, topRad); | 104 fRadii[kUpperLeft_Corner].set(leftRad, topRad); |
101 fRadii[kUpperRight_Corner].set(rightRad, topRad); | 105 fRadii[kUpperRight_Corner].set(rightRad, topRad); |
102 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); | 106 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); |
103 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); | 107 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); |
104 | 108 |
105 SkDEBUGCODE(this->validate();) | 109 SkDEBUGCODE(this->validate();) |
106 } | 110 } |
107 | 111 |
108 /* | 112 /* |
109 * TODO: clean this guy up and possibly add to SkScalar.h | 113 * TODO: clean this guy up and possibly add to SkScalar.h |
(...skipping 21 matching lines...) Expand all Loading... |
131 // radii is huge while the other is small, single precision math can completely | 135 // radii is huge while the other is small, single precision math can completely |
132 // miss the fact that a scale is required. | 136 // miss the fact that a scale is required. |
133 static double compute_min_scale(double rad1, double rad2, double limit, double c
urMin) { | 137 static double compute_min_scale(double rad1, double rad2, double limit, double c
urMin) { |
134 if ((rad1 + rad2) > limit) { | 138 if ((rad1 + rad2) > limit) { |
135 return SkTMin(curMin, limit / (rad1 + rad2)); | 139 return SkTMin(curMin, limit / (rad1 + rad2)); |
136 } | 140 } |
137 return curMin; | 141 return curMin; |
138 } | 142 } |
139 | 143 |
140 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { | 144 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { |
141 if (rect.isEmpty() || !rect.isFinite()) { | 145 fRect = rect; |
| 146 fRect.sort(); |
| 147 |
| 148 if (fRect.isEmpty() || !fRect.isFinite()) { |
142 this->setEmpty(); | 149 this->setEmpty(); |
143 return; | 150 return; |
144 } | 151 } |
145 | 152 |
146 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { | 153 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { |
147 this->setRect(rect); // devolve into a simple rect | 154 this->setRect(rect); // devolve into a simple rect |
148 return; | 155 return; |
149 } | 156 } |
150 | 157 |
151 fRect = rect; | |
152 memcpy(fRadii, radii, sizeof(fRadii)); | 158 memcpy(fRadii, radii, sizeof(fRadii)); |
153 | 159 |
154 bool allCornersSquare = true; | 160 bool allCornersSquare = true; |
155 | 161 |
156 // Clamp negative radii to zero | 162 // Clamp negative radii to zero |
157 for (int i = 0; i < 4; ++i) { | 163 for (int i = 0; i < 4; ++i) { |
158 if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) { | 164 if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) { |
159 // In this case we are being a little fast & loose. Since one of | 165 // In this case we are being a little fast & loose. Since one of |
160 // the radii is 0 the corner is square. However, the other radii | 166 // the radii is 0 the corner is square. However, the other radii |
161 // could still be non-zero and play in the global scale factor | 167 // could still be non-zero and play in the global scale factor |
(...skipping 15 matching lines...) Expand all Loading... |
177 // that to scale down _all_ the radii. This algorithm is from the | 183 // that to scale down _all_ the radii. This algorithm is from the |
178 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping | 184 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping |
179 // Curves: | 185 // Curves: |
180 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, | 186 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, |
181 // Si is the sum of the two corresponding radii of the corners on side i, | 187 // Si is the sum of the two corresponding radii of the corners on side i, |
182 // and Ltop = Lbottom = the width of the box, | 188 // and Ltop = Lbottom = the width of the box, |
183 // and Lleft = Lright = the height of the box. | 189 // and Lleft = Lright = the height of the box. |
184 // If f < 1, then all corner radii are reduced by multiplying them by f." | 190 // If f < 1, then all corner radii are reduced by multiplying them by f." |
185 double scale = 1.0; | 191 double scale = 1.0; |
186 | 192 |
187 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, rect.width(), scale); | 193 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, fRect.width(), scale)
; |
188 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, rect.height(), scale); | 194 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, fRect.height(), scale)
; |
189 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, rect.width(), scale); | 195 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, fRect.width(), scale)
; |
190 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, rect.height(), scale); | 196 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, fRect.height(), scale)
; |
191 | 197 |
192 if (scale < 1.0) { | 198 if (scale < 1.0) { |
193 for (int i = 0; i < 4; ++i) { | 199 for (int i = 0; i < 4; ++i) { |
194 fRadii[i].fX *= scale; | 200 fRadii[i].fX *= scale; |
195 fRadii[i].fY *= scale; | 201 fRadii[i].fY *= scale; |
196 } | 202 } |
197 } | 203 } |
198 | 204 |
199 // skbug.com/3239 -- its possible that we can hit the following inconsistenc
y: | 205 // skbug.com/3239 -- its possible that we can hit the following inconsistenc
y: |
200 // rad == bounds.bottom - bounds.top | 206 // rad == bounds.bottom - bounds.top |
201 // bounds.bottom - radius < bounds.top | 207 // bounds.bottom - radius < bounds.top |
202 // YIKES | 208 // YIKES |
203 // We need to detect and "fix" this now, otherwise we can have the following
wackiness: | 209 // We need to detect and "fix" this now, otherwise we can have the following
wackiness: |
204 // path.addRRect(rrect); | 210 // path.addRRect(rrect); |
205 // rrect.rect() != path.getBounds() | 211 // rrect.rect() != path.getBounds() |
206 for (int i = 0; i < 4; ++i) { | 212 for (int i = 0; i < 4; ++i) { |
207 fRadii[i].fX = clamp_radius_check_predicates(fRadii[i].fX, rect.fLeft, r
ect.fRight); | 213 fRadii[i].fX = clamp_radius_check_predicates(fRadii[i].fX, fRect.fLeft,
fRect.fRight); |
208 fRadii[i].fY = clamp_radius_check_predicates(fRadii[i].fY, rect.fTop, re
ct.fBottom); | 214 fRadii[i].fY = clamp_radius_check_predicates(fRadii[i].fY, fRect.fTop, f
Rect.fBottom); |
209 } | 215 } |
210 // At this point we're either oval, simple, or complex (not empty or rect). | 216 // At this point we're either oval, simple, or complex (not empty or rect). |
211 this->computeType(); | 217 this->computeType(); |
212 | 218 |
213 SkDEBUGCODE(this->validate();) | 219 SkDEBUGCODE(this->validate();) |
214 } | 220 } |
215 | 221 |
216 // This method determines if a point known to be inside the RRect's bounds is | 222 // This method determines if a point known to be inside the RRect's bounds is |
217 // inside all the corners. | 223 // inside all the corners. |
218 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { | 224 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
586 } | 592 } |
587 | 593 |
588 for (int i = 0; i < 4; ++i) { | 594 for (int i = 0; i < 4; ++i) { |
589 validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight
); | 595 validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight
); |
590 validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom
); | 596 validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom
); |
591 } | 597 } |
592 } | 598 } |
593 #endif // SK_DEBUG | 599 #endif // SK_DEBUG |
594 | 600 |
595 /////////////////////////////////////////////////////////////////////////////// | 601 /////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |