From 44b6200392db13ec3cb45a252fc7491fd769046d Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Wed, 24 Feb 2016 13:07:43 -0800 Subject: [PATCH] Round ShadowNode layout coordinates to a pixel boundary Summary: When layout happens, left/top/right/bottom coordinates (and thus clip-*, too) are sometimes not at a pixel boundary for 2 reasons: a) width/height in Flexbox are floats, and can contain any value, not just a whole pixel. E.g. style={{width: 3.1415926}} is perfectly valid b) floating point arithmetics sometimes leads to values barely outside of pixel boundaries (width 18.0f can become 18.000001f when a sibling/child size changes). a) is \"breaking\" screenshot tests, which slightly but differ from reference implementation b) causes extra View updates/redraws for no reason This patch is rounding DrawCommand bounds to a whole pixel to avoid these 2 issues. Reviewed By: ahmedre Differential Revision: D2934401 --- .../com/facebook/react/flat/StateBuilder.java | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java index 92510e3ca..1029e19d3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java @@ -359,15 +359,20 @@ import com.facebook.react.uimanager.events.EventDispatcher; node.markLayoutSeen(); } + float roundedLeft = roundToPixel(left); + float roundedTop = roundToPixel(top); + float roundedRight = roundToPixel(right); + float roundedBottom = roundToPixel(bottom); + // notify JS about layout event if requested if (node.shouldNotifyOnLayout()) { mOnLayoutEvents.add( OnLayoutEvent.obtain( node.getReactTag(), - Math.round(left), - Math.round(top), - Math.round(right - left), - Math.round(bottom - top))); + (int) roundedLeft, + (int) roundedTop, + (int) (roundedRight - roundedLeft), + (int) (roundedBottom - roundedTop))); } if (node.clipToBounds()) { @@ -377,7 +382,16 @@ import com.facebook.react.uimanager.events.EventDispatcher; clipBottom = Math.min(bottom, clipBottom); } - node.collectState(this, left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom); + node.collectState( + this, + roundedLeft, + roundedTop, + roundedRight, + roundedBottom, + roundToPixel(clipLeft), + roundToPixel(clipTop), + roundToPixel(clipRight), + clipBottom); for (int i = 0, childCount = node.getChildCount(); i != childCount; ++i) { ReactShadowNode child = node.getChildAt(i); @@ -499,4 +513,11 @@ import com.facebook.react.uimanager.events.EventDispatcher; return viewTags; } + + /** + * This is what Math.round() does, except it returns float. + */ + private static float roundToPixel(float pos) { + return (float) Math.floor(pos + 0.5f); + } }