Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tests/YGRoundingLargeOffsetTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <gtest/gtest.h>
#include <yoga/Yoga.h>

static YGSize _measureTwoLines(
YGNodeConstRef /*node*/,
float /*width*/,
YGMeasureMode /*widthMode*/,
float /*height*/,
YGMeasureMode /*heightMode*/) {
return YGSize{292.0f, 44.0f};
}

// A measured (text) node far from the origin on a 3x screen, with its top on
// one side of a power of two and its bottom on the other. Each edge is rounded
// to the pixel grid and the height is their difference; in float the two
// edges have different precision and the height came out one step short (for
// example 43.9998779 instead of 44 for a node spanning 2048), so a text
// renderer given that box dropped the last line.
TEST(YogaTest, rounding_measured_height_is_exact_across_float_steps) {
for (const float spacer :
{1012.0f + 1.0f / 3.0f,
1012.0f + 2.0f / 3.0f,
2004.0f + 1.0f / 3.0f,
2028.0f + 2.0f / 3.0f,
4052.0f + 2.0f / 3.0f}) {
YGConfigRef config = YGConfigNew();
YGConfigSetPointScaleFactor(config, 3.0f);

YGNodeRef root = YGNodeNewWithConfig(config);
YGNodeStyleSetWidth(root, 300);

YGNodeRef spacerNode = YGNodeNewWithConfig(config);
YGNodeStyleSetHeight(spacerNode, spacer);
YGNodeInsertChild(root, spacerNode, 0);

YGNodeRef text = YGNodeNewWithConfig(config);
YGNodeSetMeasureFunc(text, _measureTwoLines);
YGNodeInsertChild(root, text, 1);

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

ASSERT_EQ(44.0f, YGNodeLayoutGetHeight(text)) << "spacer " << spacer;

YGNodeFreeRecursive(root);
YGConfigFree(config);
}
}
52 changes: 36 additions & 16 deletions yoga/algorithm/PixelGrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@

namespace facebook::yoga {

float roundValueToPixelGrid(
namespace {

// Rounds in double precision. A dimension is the difference of two rounded
// edges, and far from the origin a float has fewer bits than a pixel: taking
// that difference in float can leave it a step short of the real size (for
// example 43.9998 instead of 44 for a node spanning y = 2048 on a 3x screen),
// which makes a text renderer drop the last line. Subtract first, then narrow.
double roundValueToPixelGridDouble(
const double value,
const double pointScaleFactor,
const bool forceCeil,
Expand Down Expand Up @@ -60,7 +67,18 @@ float roundValueToPixelGrid(
}
return (std::isnan(scaledValue) || std::isnan(pointScaleFactor))
? YGUndefined
: (float)(scaledValue / pointScaleFactor);
: scaledValue / pointScaleFactor;
}

} // namespace

float roundValueToPixelGrid(
const double value,
const double pointScaleFactor,
const bool forceCeil,
const bool forceFloor) {
return static_cast<float>(roundValueToPixelGridDouble(
value, pointScaleFactor, forceCeil, forceFloor));
}

void roundLayoutResultsToPixelGrid(
Expand Down Expand Up @@ -109,23 +127,25 @@ void roundLayoutResultsToPixelGrid(

node->getLayout().setDimension(
Dimension::Width,
roundValueToPixelGrid(
absoluteNodeRight,
pointScaleFactor,
(textRounding && hasFractionalWidth),
(textRounding && !hasFractionalWidth)) -
roundValueToPixelGrid(
absoluteNodeLeft, pointScaleFactor, false, textRounding));
static_cast<float>(
roundValueToPixelGridDouble(
absoluteNodeRight,
pointScaleFactor,
(textRounding && hasFractionalWidth),
(textRounding && !hasFractionalWidth)) -
roundValueToPixelGridDouble(
absoluteNodeLeft, pointScaleFactor, false, textRounding)));

node->getLayout().setDimension(
Dimension::Height,
roundValueToPixelGrid(
absoluteNodeBottom,
pointScaleFactor,
(textRounding && hasFractionalHeight),
(textRounding && !hasFractionalHeight)) -
roundValueToPixelGrid(
absoluteNodeTop, pointScaleFactor, false, textRounding));
static_cast<float>(
roundValueToPixelGridDouble(
absoluteNodeBottom,
pointScaleFactor,
(textRounding && hasFractionalHeight),
(textRounding && !hasFractionalHeight)) -
roundValueToPixelGridDouble(
absoluteNodeTop, pointScaleFactor, false, textRounding)));
}

for (yoga::Node* child : node->getChildren()) {
Expand Down