1 /* 2 * Copyright 2016 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 #ifndef SkScaleToSides_DEFINED 9 #define SkScaleToSides_DEFINED 10 11 #include "SkScalar.h" 12 #include "SkTypes.h" 13 14 #include <cmath> 15 #include <utility> 16 17 class SkScaleToSides { 18 public: 19 // This code assumes that a and b fit in a float, and therefore the resulting smaller value 20 // of a and b will fit in a float. The side of the rectangle may be larger than a float. 21 // Scale must be less than or equal to the ratio limit / (*a + *b). 22 // This code assumes that NaN and Inf are never passed in. AdjustRadii(double limit,double scale,SkScalar * a,SkScalar * b)23 static void AdjustRadii(double limit, double scale, SkScalar* a, SkScalar* b) { 24 SkASSERTF(scale < 1.0 && scale > 0.0, "scale: %g", scale); 25 26 *a = (float)((double)*a * scale); 27 *b = (float)((double)*b * scale); 28 29 if (*a + *b > limit) { 30 float* minRadius = a; 31 float* maxRadius = b; 32 33 // Force minRadius to be the smaller of the two. 34 if (*minRadius > *maxRadius) { 35 using std::swap; 36 swap(minRadius, maxRadius); 37 } 38 39 // newMinRadius must be float in order to give the actual value of the radius. 40 // The newMinRadius will always be smaller than limit. The largest that minRadius can be 41 // is 1/2 the ratio of minRadius : (minRadius + maxRadius), therefore in the resulting 42 // division, minRadius can be no larger than 1/2 limit + ULP. The newMinRadius can be 43 // 1/2 a ULP off at this point. 44 float newMinRadius = *minRadius; 45 46 // Because newMaxRadius is the result of a double to float conversion, it can be larger 47 // than limit, but only by one ULP. 48 float newMaxRadius = (float)(limit - newMinRadius); 49 50 // The total sum of newMinRadius and newMaxRadius can be upto 1.5 ULPs off. If the 51 // sum is greater than the limit then newMaxRadius may have to be reduced twice. 52 // Note: nextafterf is a c99 call and should be std::nextafter, but this is not 53 // implemented in the GCC ARM compiler. 54 if (newMaxRadius + newMinRadius > limit) { 55 newMaxRadius = nextafterf(newMaxRadius, 0.0f); 56 if (newMaxRadius + newMinRadius > limit) { 57 newMaxRadius = nextafterf(newMaxRadius, 0.0f); 58 } 59 } 60 *maxRadius = newMaxRadius; 61 } 62 63 SkASSERTF(*a >= 0.0f && *b >= 0.0f, "a: %g, b: %g, limit: %g, scale: %g", *a, *b, limit, 64 scale); 65 66 SkASSERTF(*a + *b <= limit, 67 "\nlimit: %.17f, sum: %.17f, a: %.10f, b: %.10f, scale: %.20f", 68 limit, *a + *b, *a, *b, scale); 69 } 70 }; 71 #endif // ScaleToSides_DEFINED 72