Chromium Code Reviews| 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 |