Skip to content

Commit 98806cc

Browse files
Round node dimensions in double so far-off nodes keep their full size (#2037)
* Round node dimensions in double so far-off nodes keep their full size A node's rounded width and height are the difference of its two rounded absolute edges. roundValueToPixelGrid returns float, so that difference was taken in float; far from the origin a float has fewer bits than a pixel, and when the two edges fall on either side of a power of two they are quantized differently. A measured text node spanning y = 2048 on a 3x screen came out 43.9998779 tall instead of 44, one float step short of the two lines it was measured at, and the text renderer then dropped the last line. Round in double and subtract before narrowing to float. The public roundValueToPixelGrid keeps its signature. * Cover both fractions on either side of 1024, 2048 and 4096 in the rounding test On the previous rounding each of these came out off by a float step (44.00006, 43.99994, 43.99988, 43.99988 and 43.99976); all are exactly 44 now. * Remove comments --------- Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
1 parent 667da3c commit 98806cc

2 files changed

Lines changed: 79 additions & 16 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
#include <yoga/Yoga.h>
10+
11+
static YGSize _measureTwoLines(
12+
YGNodeConstRef /*node*/,
13+
float /*width*/,
14+
YGMeasureMode /*widthMode*/,
15+
float /*height*/,
16+
YGMeasureMode /*heightMode*/) {
17+
return YGSize{292.0f, 44.0f};
18+
}
19+
20+
TEST(YogaTest, rounding_measured_height_is_exact_across_float_steps) {
21+
for (const float spacer :
22+
{1012.0f + 1.0f / 3.0f,
23+
1012.0f + 2.0f / 3.0f,
24+
2004.0f + 1.0f / 3.0f,
25+
2028.0f + 2.0f / 3.0f,
26+
4052.0f + 2.0f / 3.0f}) {
27+
YGConfigRef config = YGConfigNew();
28+
YGConfigSetPointScaleFactor(config, 3.0f);
29+
30+
YGNodeRef root = YGNodeNewWithConfig(config);
31+
YGNodeStyleSetWidth(root, 300);
32+
33+
YGNodeRef spacerNode = YGNodeNewWithConfig(config);
34+
YGNodeStyleSetHeight(spacerNode, spacer);
35+
YGNodeInsertChild(root, spacerNode, 0);
36+
37+
YGNodeRef text = YGNodeNewWithConfig(config);
38+
YGNodeSetMeasureFunc(text, _measureTwoLines);
39+
YGNodeInsertChild(root, text, 1);
40+
41+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
42+
43+
ASSERT_EQ(44.0f, YGNodeLayoutGetHeight(text)) << "spacer " << spacer;
44+
45+
YGNodeFreeRecursive(root);
46+
YGConfigFree(config);
47+
}
48+
}

‎yoga/algorithm/PixelGrid.cpp‎

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
namespace facebook::yoga {
1414

15-
float roundValueToPixelGrid(
15+
namespace {
16+
17+
double roundValueToPixelGridDouble(
1618
const double value,
1719
const double pointScaleFactor,
1820
const bool forceCeil,
@@ -60,7 +62,18 @@ float roundValueToPixelGrid(
6062
}
6163
return (std::isnan(scaledValue) || std::isnan(pointScaleFactor))
6264
? YGUndefined
63-
: (float)(scaledValue / pointScaleFactor);
65+
: scaledValue / pointScaleFactor;
66+
}
67+
68+
} // namespace
69+
70+
float roundValueToPixelGrid(
71+
const double value,
72+
const double pointScaleFactor,
73+
const bool forceCeil,
74+
const bool forceFloor) {
75+
return static_cast<float>(roundValueToPixelGridDouble(
76+
value, pointScaleFactor, forceCeil, forceFloor));
6477
}
6578

6679
void roundLayoutResultsToPixelGrid(
@@ -109,23 +122,25 @@ void roundLayoutResultsToPixelGrid(
109122

110123
node->getLayout().setDimension(
111124
Dimension::Width,
112-
roundValueToPixelGrid(
113-
absoluteNodeRight,
114-
pointScaleFactor,
115-
(textRounding && hasFractionalWidth),
116-
(textRounding && !hasFractionalWidth)) -
117-
roundValueToPixelGrid(
118-
absoluteNodeLeft, pointScaleFactor, false, textRounding));
125+
static_cast<float>(
126+
roundValueToPixelGridDouble(
127+
absoluteNodeRight,
128+
pointScaleFactor,
129+
(textRounding && hasFractionalWidth),
130+
(textRounding && !hasFractionalWidth)) -
131+
roundValueToPixelGridDouble(
132+
absoluteNodeLeft, pointScaleFactor, false, textRounding)));
119133

120134
node->getLayout().setDimension(
121135
Dimension::Height,
122-
roundValueToPixelGrid(
123-
absoluteNodeBottom,
124-
pointScaleFactor,
125-
(textRounding && hasFractionalHeight),
126-
(textRounding && !hasFractionalHeight)) -
127-
roundValueToPixelGrid(
128-
absoluteNodeTop, pointScaleFactor, false, textRounding));
136+
static_cast<float>(
137+
roundValueToPixelGridDouble(
138+
absoluteNodeBottom,
139+
pointScaleFactor,
140+
(textRounding && hasFractionalHeight),
141+
(textRounding && !hasFractionalHeight)) -
142+
roundValueToPixelGridDouble(
143+
absoluteNodeTop, pointScaleFactor, false, textRounding)));
129144
}
130145

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

0 commit comments

Comments
 (0)