From 37ec1774a781774c332f8b4f639d208494bfde73 Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Fri, 24 Feb 2017 09:45:31 -0800 Subject: [PATCH] Add rounding to the pixel grid to Yoga Summary: This diff adds rounding to the pixel grid feature to Yoga and appropriate property Reviewed By: emilsjolander Differential Revision: D4565980 fbshipit-source-id: 9700f6d6ed147f82b19f230fbff2e9ccbd625b25 --- tests/YGRoundingMeasureFuncTest.cpp | 34 ++++++++++++++++ yoga/Yoga.c | 62 ++++++++++++++++++++++++----- yoga/Yoga.h | 4 ++ 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/tests/YGRoundingMeasureFuncTest.cpp b/tests/YGRoundingMeasureFuncTest.cpp index a55f142c..0e86cb26 100644 --- a/tests/YGRoundingMeasureFuncTest.cpp +++ b/tests/YGRoundingMeasureFuncTest.cpp @@ -39,13 +39,44 @@ TEST(YogaTest, rounding_feature_with_custom_measure_func_floor) { YGNodeSetMeasureFunc(root_child0, _measureFloor); YGNodeInsertChild(root, root_child0, 0); + YGSetPointScaleFactor(0.0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(10.2, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10.2, YGNodeLayoutGetHeight(root_child0)); + + YGSetPointScaleFactor(1.0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGSetPointScaleFactor(2.0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGSetPointScaleFactor(4.0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10.25, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10.25, YGNodeLayoutGetHeight(root_child0)); + + YGSetPointScaleFactor(1.0 / 3.0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(9.0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(9.0, YGNodeLayoutGetHeight(root_child0)); YGNodeFreeRecursive(root); + YGSetPointScaleFactor(1.0); YGSetExperimentalFeatureEnabled(YGExperimentalFeatureRounding, false); } @@ -58,6 +89,8 @@ TEST(YogaTest, rounding_feature_with_custom_measure_func_ceil) { YGNodeSetMeasureFunc(root_child0, _measureCeil); YGNodeInsertChild(root, root_child0, 0); + YGSetPointScaleFactor(1.0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); ASSERT_FLOAT_EQ(11, YGNodeLayoutGetWidth(root_child0)); @@ -65,5 +98,6 @@ TEST(YogaTest, rounding_feature_with_custom_measure_func_ceil) { YGNodeFreeRecursive(root); + YGSetPointScaleFactor(1.0); YGSetExperimentalFeatureEnabled(YGExperimentalFeatureRounding, false); } diff --git a/yoga/Yoga.c b/yoga/Yoga.c index 2a1b166b..074fd01c 100644 --- a/yoga/Yoga.c +++ b/yoga/Yoga.c @@ -3216,18 +3216,58 @@ bool YGLayoutNodeInternal(const YGNodeRef node, return (needToVisitNode || cachedResults == NULL); } -static void YGRoundToPixelGrid(const YGNodeRef node) { - const float fractialLeft = - node->layout.position[YGEdgeLeft] - floorf(node->layout.position[YGEdgeLeft]); - const float fractialTop = - node->layout.position[YGEdgeTop] - floorf(node->layout.position[YGEdgeTop]); - node->layout.dimensions[YGDimensionWidth] = - roundf(fractialLeft + node->layout.dimensions[YGDimensionWidth]) - roundf(fractialLeft); - node->layout.dimensions[YGDimensionHeight] = - roundf(fractialTop + node->layout.dimensions[YGDimensionHeight]) - roundf(fractialTop); +static float gPointScaleFactor = 1.0; - node->layout.position[YGEdgeLeft] = roundf(node->layout.position[YGEdgeLeft]); - node->layout.position[YGEdgeTop] = roundf(node->layout.position[YGEdgeTop]); +void YGSetPointScaleFactor(float pixelsInPoint) { + YG_ASSERT(pixelsInPoint >= 0.0, "Scale factor should not be less than zero"); + // We store points for Pixel as we will use it for rounding + if (pixelsInPoint == 0.0) { + // Zero is used to skip rounding + gPointScaleFactor = 0.0; + } else { + gPointScaleFactor = 1.0 / pixelsInPoint; + } +} + +static void YGRoundToPixelGrid(const YGNodeRef node) { + if (gPointScaleFactor == 0.0) { + return; + } + const float nodeLeft = node->layout.position[YGEdgeLeft]; + const float nodeTop = node->layout.position[YGEdgeTop]; + + // To round correctly to the pixel grid, first we calculate left and top coordinates + float fractialLeft = fmodf(nodeLeft, gPointScaleFactor); + float fractialTop = fmodf(nodeTop, gPointScaleFactor); + float roundedLeft = nodeLeft - fractialLeft; + float roundedTop = nodeTop - fractialTop; + + // To do the actual rounding we check if leftover fraction is bigger or equal than half of the grid step + if (fractialLeft >= gPointScaleFactor / 2.0) { + roundedLeft += gPointScaleFactor; + fractialLeft -= gPointScaleFactor; + } + if (fractialTop >= gPointScaleFactor / 2.0) { + roundedTop += gPointScaleFactor; + fractialTop -= gPointScaleFactor; + } + node->layout.position[YGEdgeLeft] = roundedLeft; + node->layout.position[YGEdgeTop] = roundedTop; + + // Now we round width and height in the same way accounting for fractial leftovers from rounding position + const float adjustedWidth = fractialLeft + node->layout.dimensions[YGDimensionWidth]; + const float adjustedHeight = fractialTop + node->layout.dimensions[YGDimensionHeight]; + float roundedWidth = adjustedWidth - fmodf(adjustedWidth, gPointScaleFactor); + float roundedHeight = adjustedHeight - fmodf(adjustedHeight, gPointScaleFactor); + + if (adjustedWidth - roundedWidth >= gPointScaleFactor / 2.0) { + roundedWidth += gPointScaleFactor; + } + if (adjustedHeight - roundedHeight >= gPointScaleFactor / 2.0) { + roundedHeight += gPointScaleFactor; + } + node->layout.dimensions[YGDimensionWidth] = roundedWidth; + node->layout.dimensions[YGDimensionHeight] = roundedHeight; const uint32_t childCount = YGNodeListCount(node->children); for (uint32_t i = 0; i < childCount; i++) { diff --git a/yoga/Yoga.h b/yoga/Yoga.h index 4174e786..697a1d80 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -219,6 +219,10 @@ YG_NODE_LAYOUT_EDGE_PROPERTY(float, Padding); WIN_EXPORT void YGSetLogger(YGLogger logger); WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...); +// Set this to number of pixels in 1 point to round calculation results +// If you want to avoid rounding - set PointScaleFactor to 0 +WIN_EXPORT void YGSetPointScaleFactor(float pixelsInPoint); + WIN_EXPORT void YGSetExperimentalFeatureEnabled(YGExperimentalFeature feature, bool enabled); WIN_EXPORT bool YGIsExperimentalFeatureEnabled(YGExperimentalFeature feature);