Skip to content

Commit

Permalink
[Issue 838] Update ASCeilPixelValue and ASRoundPixelValue (#864)
Browse files Browse the repository at this point in the history
* [Issue 838] Update ASCeilPixelValue and ASRoundPixelValue

Layouts can come back for 3x devices as a repeating decimal. Floats/doubles have a hard time representing these values and often the last digit or two will be  garbage. This garbage can result in unexpected values from `ceil` or `round`. I try to fix this by subtracting `FLT_EPSILON` from the value before calling `ceil` or `round`

#838

* addressed comments on the pr
  • Loading branch information
rcancro authored and Adlai-Holler committed Mar 31, 2018
1 parent 4147384 commit 1e8c6f0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- User FLT_EPSILON in ASCeilPixelValue and ASFloorPixelValue to help with floating point precision errors when computing layouts for 3x devices. [Ricky Cancro](https://github.com/rcancro) [#838](https://github.com/TextureGroup/Texture/pull/864)
- Disable interface colescing and match to pre-colescing interface update behavior [Max Wang](https://github.com/wsdwsd0829) [#862](https://github.com/TextureGroup/Texture/pull/862)
- [ASDisplayNode] Add safeAreaInsets, layoutMargins and related properties to ASDisplayNode, with full support for older OS versions [Yevgen Pogribnyi](https://github.com/ypogribnyi) [#685](https://github.com/TextureGroup/Texture/pull/685)
- [ASPINRemoteImageDownloader] Allow cache to provide animated image. [Max Wang](https://github.com/wsdwsd0829) [#850](https://github.com/TextureGroup/Texture/pull/850)
Expand Down
36 changes: 34 additions & 2 deletions Source/Private/ASInternalHelpers.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,23 @@ CGSize ASFloorSizeValues(CGSize s)
return CGSizeMake(ASFloorPixelValue(s.width), ASFloorPixelValue(s.height));
}

// See ASCeilPixelValue for a more thoroguh explanation of (f + FLT_EPSILON),
// but here is some quick math:
//
// Imagine a layout that comes back with a height of 100.66666666663
// for a 3x deice:
// 100.66666666663 * 3 = 301.99999999988995
// floor(301.99999999988995) = 301
// 301 / 3 = 100.333333333
//
// If we add FLT_EPSILON to normalize the garbage at the end we get:
// po (100.66666666663 + FLT_EPSILON) * 3 = 302.00000035751782
// floor(302.00000035751782) = 302
// 302/3 = 100.66666666
CGFloat ASFloorPixelValue(CGFloat f)
{
CGFloat scale = ASScreenScale();
return floor(f * scale) / scale;
return floor((f + FLT_EPSILON) * scale) / scale;
}

CGPoint ASCeilPointValues(CGPoint p)
Expand All @@ -193,10 +206,29 @@ CGSize ASCeilSizeValues(CGSize s)
return CGSizeMake(ASCeilPixelValue(s.width), ASCeilPixelValue(s.height));
}

// With 3x devices layouts will often to compute to pixel bounds but
// include garbage values beyond the precision of a float/double.
// This garbage can result in a pixel value being rounded up when it isn't
// necessary.
//
// For example, imagine a layout that comes back with a height of 100.666666666669
// for a 3x device:
// 100.666666666669 * 3 = 302.00000000000699
// ceil(302.00000000000699) = 303
// 303/3 = 101
//
// If we use FLT_EPSILON to get rid of the garbage at the end of the value,
// things work as expected:
// (100.666666666669 - FLT_EPSILON) * 3 = 301.99999964237912
// ceil(301.99999964237912) = 302
// 302/3 = 100.666666666
//
// For even more conversation around this, see:
// https://github.com/TextureGroup/Texture/issues/838
CGFloat ASCeilPixelValue(CGFloat f)
{
CGFloat scale = ASScreenScale();
return ceil(f * scale) / scale;
return ceil((f - FLT_EPSILON) * scale) / scale;
}

CGFloat ASRoundPixelValue(CGFloat f)
Expand Down

0 comments on commit 1e8c6f0

Please sign in to comment.