SkScaleToSides.h 2.8 KB

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