Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(956)

Side by Side Diff: src/core/SkRRect.cpp

Issue 1567723004: Fix handling of radii scaling to force the result to always be less (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: make code clearer Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/DrawPathTest.cpp » ('j') | tests/DrawPathTest.cpp » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 ///////////////////////////////////////////////////////////////////////////////
OLDNEW
« no previous file with comments | « no previous file | tests/DrawPathTest.cpp » ('j') | tests/DrawPathTest.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698