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
This commit is contained in:
Denis Koroskin 2016-02-24 13:07:43 -08:00 committed by Ahmed El-Helw
parent addd233d31
commit 44b6200392
1 changed files with 26 additions and 5 deletions

View File

@ -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);
}
}