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 <cmath> | |
8 #include "SkRRect.h" | 9 #include "SkRRect.h" |
9 #include "SkMatrix.h" | 10 #include "SkMatrix.h" |
10 | 11 |
11 /////////////////////////////////////////////////////////////////////////////// | 12 /////////////////////////////////////////////////////////////////////////////// |
12 | 13 |
13 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { | 14 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { |
14 fRect = rect; | 15 fRect = rect; |
15 fRect.sort(); | 16 fRect.sort(); |
16 | 17 |
17 if (fRect.isEmpty() || !fRect.isFinite()) { | 18 if (fRect.isEmpty() || !fRect.isFinite()) { |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
102 } | 103 } |
103 | 104 |
104 fRadii[kUpperLeft_Corner].set(leftRad, topRad); | 105 fRadii[kUpperLeft_Corner].set(leftRad, topRad); |
105 fRadii[kUpperRight_Corner].set(rightRad, topRad); | 106 fRadii[kUpperRight_Corner].set(rightRad, topRad); |
106 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); | 107 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); |
107 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); | 108 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); |
108 | 109 |
109 SkDEBUGCODE(this->validate();) | 110 SkDEBUGCODE(this->validate();) |
110 } | 111 } |
111 | 112 |
112 /* | |
113 * TODO: clean this guy up and possibly add to SkScalar.h | |
114 */ | |
115 static inline SkScalar SkScalarDecULP(SkScalar value) { | |
116 #if SK_SCALAR_IS_FLOAT | |
117 return SkBits2Float(SkFloat2Bits(value) - 1); | |
118 #else | |
119 #error "need impl for doubles" | |
120 #endif | |
121 } | |
122 | |
123 /** | |
124 * We need all combinations of predicates to be true to have a "safe" radius va lue. | |
125 */ | |
126 static SkScalar clamp_radius_check_predicates(SkScalar rad, SkScalar min, SkScal ar max) { | |
127 SkASSERT(min < max); | |
128 if (rad > max - min || min + rad > max || max - rad < min) { | |
129 rad = SkScalarDecULP(rad); | |
130 } | |
131 return rad; | |
132 } | |
133 | |
134 // These parameters intentionally double. Apropos crbug.com/463920, if one of th e | 113 // These parameters intentionally double. Apropos crbug.com/463920, if one of th e |
135 // radii is huge while the other is small, single precision math can completely | 114 // radii is huge while the other is small, single precision math can completely |
136 // miss the fact that a scale is required. | 115 // miss the fact that a scale is required. |
137 static double compute_min_scale(double rad1, double rad2, double limit, double c urMin) { | 116 static double compute_min_scale(double rad1, double rad2, double limit, double c urMin) { |
138 if ((rad1 + rad2) > limit) { | 117 if ((rad1 + rad2) > limit) { |
139 return SkTMin(curMin, limit / (rad1 + rad2)); | 118 return SkTMin(curMin, limit / (rad1 + rad2)); |
140 } | 119 } |
141 return curMin; | 120 return curMin; |
142 } | 121 } |
143 | 122 |
123 // This code assumes that a and b fit in in a float, and therefore the resulting smaller value of | |
124 // a and b will fit in a float. The side of the rectangle may be larger than a f loat. | |
125 static void adjust_radii(double limit, double scale, float* a, float* b) { | |
126 SkASSERT(scale < 1.0 && scale > 0.0); | |
127 // This check is conservative. (double)*a + (double)*b >= (double)(*a + *b) | |
128 if ((double)*a + (double)*b > limit) { | |
robertphillips
2016/01/07 17:56:41
minRadius & maxRadius ?
herb_g
2016/01/07 18:45:32
Done.
| |
129 float* min_radius = a; | |
130 float* max_radius = b; | |
131 // force min_radius to be the smaller of the two. | |
132 if (*min_radius > *max_radius) { | |
133 SkTSwap(min_radius, max_radius); | |
134 } | |
135 // new_min_radius must be float in order to give the actual value of the radius. | |
robertphillips
2016/01/07 17:56:41
newMinRadius ?
herb_g
2016/01/07 18:45:32
Done.
| |
136 float new_min_radius = *min_radius * scale; | |
robertphillips
2016/01/07 17:56:41
Can it ever happen that newMinRadius is larger tha
herb_g
2016/01/07 18:45:32
Done.
| |
137 *min_radius = new_min_radius; | |
138 // Because new_max_radius is the result of a double to float conversion, it can be larger | |
139 // than limit, but only by one ULP. | |
robertphillips
2016/01/07 17:56:41
newMaxRadius ?
herb_g
2016/01/07 18:45:32
Done.
| |
140 float new_max_radius = (float)(limit - new_min_radius); | |
141 // If new_max_radius is larger than the same value as a double, then it needs to be | |
142 // reduced by one ULP to be less than limit - new_min_radius. | |
143 // Note: nexttowardf is a c99 call and should be std::nexttoward, but th is is not | |
144 // implemented in the ARM compiler. | |
145 if (new_max_radius > limit - new_min_radius) { | |
146 new_max_radius = nexttowardf(new_max_radius, limit - new_min_radius) ; | |
147 } | |
148 // This handles the case where both sets of radii are larger than a side by differing | |
149 // scale factors. The one that needs the smaller scale factor will produ ce short enough | |
150 // radii in the other side just using the scale factor. | |
151 *max_radius = SkMinScalar(scale * *max_radius, new_max_radius); | |
152 } else { | |
153 *a *= scale; | |
154 *b *= scale; | |
155 } | |
robertphillips
2016/01/07 17:56:41
SkASSERT(*a >= 0.0f && *b >= 0.0f); ?
herb_g
2016/01/07 18:45:32
Done.
| |
156 SkASSERT((*a + *b) <= limit); | |
157 } | |
158 | |
144 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { | 159 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { |
145 fRect = rect; | 160 fRect = rect; |
146 fRect.sort(); | 161 fRect.sort(); |
147 | 162 |
148 if (fRect.isEmpty() || !fRect.isFinite()) { | 163 if (fRect.isEmpty() || !fRect.isFinite()) { |
149 this->setEmpty(); | 164 this->setEmpty(); |
150 return; | 165 return; |
151 } | 166 } |
152 | 167 |
153 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { | 168 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { |
(...skipping 29 matching lines...) Expand all Loading... | |
183 // that to scale down _all_ the radii. This algorithm is from the | 198 // that to scale down _all_ the radii. This algorithm is from the |
184 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping | 199 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping |
185 // Curves: | 200 // Curves: |
186 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, | 201 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, |
187 // Si is the sum of the two corresponding radii of the corners on side i, | 202 // Si is the sum of the two corresponding radii of the corners on side i, |
188 // and Ltop = Lbottom = the width of the box, | 203 // and Ltop = Lbottom = the width of the box, |
189 // and Lleft = Lright = the height of the box. | 204 // and Lleft = Lright = the height of the box. |
190 // If f < 1, then all corner radii are reduced by multiplying them by f." | 205 // If f < 1, then all corner radii are reduced by multiplying them by f." |
191 double scale = 1.0; | 206 double scale = 1.0; |
192 | 207 |
193 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, fRect.width(), scale) ; | 208 // The sides of the rectangle may be larger than a float. |
194 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, fRect.height(), scale) ; | 209 double width = (double)fRect.fRight - (double)fRect.fLeft; |
195 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, fRect.width(), scale) ; | 210 double height = (double)fRect.fBottom - (double)fRect.fTop; |
196 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, fRect.height(), scale) ; | 211 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale); |
212 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale); | |
213 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale); | |
214 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale); | |
197 | 215 |
198 if (scale < 1.0) { | 216 if (scale < 1.0) { |
199 for (int i = 0; i < 4; ++i) { | 217 adjust_radii(width, scale, &fRadii[0].fX, &fRadii[1].fX); |
200 fRadii[i].fX *= scale; | 218 adjust_radii(height, scale, &fRadii[1].fY, &fRadii[2].fY); |
201 fRadii[i].fY *= scale; | 219 adjust_radii(width, scale, &fRadii[2].fX, &fRadii[3].fX); |
202 } | 220 adjust_radii(height, scale, &fRadii[3].fY, &fRadii[0].fY); |
203 } | 221 } |
204 | 222 |
205 // https://bug.skia.org/3239 -- its possible that we can hit the following i nconsistency: | |
206 // rad == bounds.bottom - bounds.top | |
207 // bounds.bottom - radius < bounds.top | |
208 // YIKES | |
209 // We need to detect and "fix" this now, otherwise we can have the following wackiness: | |
210 // path.addRRect(rrect); | |
211 // rrect.rect() != path.getBounds() | |
212 for (int i = 0; i < 4; ++i) { | |
213 fRadii[i].fX = clamp_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight); | |
214 fRadii[i].fY = clamp_radius_check_predicates(fRadii[i].fY, fRect.fTop, f Rect.fBottom); | |
215 } | |
216 // At this point we're either oval, simple, or complex (not empty or rect). | 223 // At this point we're either oval, simple, or complex (not empty or rect). |
217 this->computeType(); | 224 this->computeType(); |
218 | 225 |
219 SkDEBUGCODE(this->validate();) | 226 SkDEBUGCODE(this->validate();) |
220 } | 227 } |
221 | 228 |
222 // This method determines if a point known to be inside the RRect's bounds is | 229 // This method determines if a point known to be inside the RRect's bounds is |
223 // inside all the corners. | 230 // inside all the corners. |
224 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { | 231 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { |
225 SkPoint canonicalPt; // (x,y) translated to one of the quadrants | 232 SkPoint canonicalPt; // (x,y) translated to one of the quadrants |
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
592 } | 599 } |
593 | 600 |
594 for (int i = 0; i < 4; ++i) { | 601 for (int i = 0; i < 4; ++i) { |
595 validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight ); | 602 validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight ); |
596 validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom ); | 603 validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom ); |
597 } | 604 } |
598 } | 605 } |
599 #endif // SK_DEBUG | 606 #endif // SK_DEBUG |
600 | 607 |
601 /////////////////////////////////////////////////////////////////////////////// | 608 /////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |