From f45059e1e696727c1282742b89d2c8bf06345254 Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Wed, 26 Jul 2017 19:22:03 -0700 Subject: [PATCH] Fixing edge case issue in Yoga where text node was unnecessary rounded down Summary: There was an uncovered edge case where number close to the whole was forced to round down because it was considered non-whole and had forced flooring. This diff covers that + adds a bunch of test cases to cover rounding function Reviewed By: emilsjolander Differential Revision: D5465632 fbshipit-source-id: 57e11092a97eba5dd76daad15fa8619535ff9c1b --- tests/YGRoundingFunctionTest.cpp | 31 +++++++++++++++++++++++++++++++ yoga/Yoga-internal.h | 19 +++++++++++++++++++ yoga/Yoga.c | 9 +++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/YGRoundingFunctionTest.cpp create mode 100644 yoga/Yoga-internal.h diff --git a/tests/YGRoundingFunctionTest.cpp b/tests/YGRoundingFunctionTest.cpp new file mode 100644 index 00000000..94d5623b --- /dev/null +++ b/tests/YGRoundingFunctionTest.cpp @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include + +#include +#include + +TEST(YogaTest, rounding_value) { + // Test that whole numbers are rounded to whole despite ceil/floor flags + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, false, true)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, true)); + + // Test that numbers with fraction are rounded correctly accounting for ceil/floor flags + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.01, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, true)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, true, false)); + ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.99, 2.0, false, true)); +} diff --git a/yoga/Yoga-internal.h b/yoga/Yoga-internal.h new file mode 100644 index 00000000..7689b925 --- /dev/null +++ b/yoga/Yoga-internal.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +YG_EXTERN_C_BEGIN + +WIN_EXPORT float YGRoundValueToPixelGrid(const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); + +YG_EXTERN_C_END diff --git a/yoga/Yoga.c b/yoga/Yoga.c index 398d3774..ab2ef235 100644 --- a/yoga/Yoga.c +++ b/yoga/Yoga.c @@ -11,6 +11,7 @@ #include "YGNodeList.h" #include "Yoga.h" +#include "Yoga-internal.h" #ifdef _MSC_VER #include @@ -3158,20 +3159,24 @@ static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureM lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); } -static float YGRoundValueToPixelGrid(const float value, +float YGRoundValueToPixelGrid(const float value, const float pointScaleFactor, const bool forceCeil, const bool forceFloor) { float scaledValue = value * pointScaleFactor; float fractial = fmodf(scaledValue, 1.0); if (YGFloatsEqual(fractial, 0)) { - // Still remove fractial as fractial could be extremely small. + // First we check if the value is already rounded scaledValue = scaledValue - fractial; + } else if (YGFloatsEqual(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; } else if (forceCeil) { + // Next we check if we need to use forced rounding scaledValue = scaledValue - fractial + 1.0; } else if (forceFloor) { scaledValue = scaledValue - fractial; } else { + // Finally we just round the value scaledValue = scaledValue - fractial + (fractial >= 0.5f ? 1.0 : 0); } return scaledValue / pointScaleFactor;