Repository navigation
Commit 26beed2
committed
subtract rounded edges in pixel space when rounding layout results
Summary:
**problem:** `roundLayoutResultsToPixelGrid()` derives a node's width and height as the
difference of two independently rounded absolute edges. `roundValueToPixelGrid()` divides
the rounded value back by `pointScaleFactor` and narrows it to `float` before returning, so
the subtraction operates on two values that each already carry a representation error, and
that error leaks into the resulting dimension.
It only surfaces when `pointScaleFactor` makes the division inexact — `n/2` is dyadic and
always exactly representable, `n/3` almost never is — and when the absolute coordinates are
large enough for one float ULP to be significant. Sweeping 1001 grid-aligned offsets around
3800pt with `pointScaleFactor` 3, a node measured at exactly 288.0 is committed
287.999755859375 at 192 of them, one ULP short at that magnitude.
This defeats a guarantee the function makes deliberately, and which its own comment states:
"If a node has a custom measure function we never want to round down its size as this could
lead to unwanted text truncation." Each edge is rounded in the intended direction; the
subtraction of the two narrowed operands is not.
The consequence is visible in React Native. A multi-line `<Text>` far down a long list
commits a height a fraction of a point below what it measured. iOS TextKit applies a strict
"does this line fit in the remaining height" test, so that shortfall makes it drop the
entire trailing line, rendering visually truncated text with blank space left in its place
while `onLayout` and `onTextLayout` both report the complete measurement. It reproduces on
3x screens only, tracks scroll position rather than content, and survives remounting — all
consistent with the arithmetic. Measured on a minimal repro (iPhone 17 simulator, 20 fresh
launches per arm, same instrumentation, only this change differing): 19 truncated renders
before, 0 after, with the committed height going from 287.999755859375 to exactly
288.000000000000.
**fix:** round in pixel space, where a grid-aligned value is an exact integer, so the
subtraction is exact and a single narrowing happens at the end.
`roundValueToPixelGridScaled()` returns the rounded value still scaled by
`pointScaleFactor`, and `pixelGridValueToPoints()` converts back once. The public
`roundValueToPixelGrid()` keeps its exact signature and behaviour as a thin wrapper, so
`YGRoundValueToPixelGrid()` and the `Cache.cpp` callers are unaffected.
regression:
`YGRoundingMeasureFuncTest.rounding_measured_size_is_never_rounded_down_at_large_offsets`
sweeps the offsets described above and asserts the committed height is never below the
measured one. It fails on main and passes with this change. Note the exact comparison:
`ASSERT_FLOAT_EQ` tolerates 4 ULPs and would not catch a 1-ULP shortfall. The full suite
passes (843/843).
## Changelog:
[General] [Fixed] - Layout dimensions are no longer rounded below their measured size at large offsets on non-integer pixel scales1 parent 433d463 commit 26beed2
3 files changed
Lines changed: 138 additions & 20 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
11 | 14 | | |
12 | 15 | | |
13 | 16 | | |
| |||
137 | 140 | | |
138 | 141 | | |
139 | 142 | | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
16 | 27 | | |
17 | 28 | | |
18 | 29 | | |
| |||
57 | 68 | | |
58 | 69 | | |
59 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
60 | 90 | | |
61 | 91 | | |
62 | 92 | | |
| |||
86 | 116 | | |
87 | 117 | | |
88 | 118 | | |
89 | | - | |
90 | | - | |
91 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
92 | 123 | | |
93 | 124 | | |
94 | | - | |
95 | | - | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
96 | 128 | | |
97 | 129 | | |
98 | 130 | | |
| |||
106 | 138 | | |
107 | 139 | | |
108 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
109 | 160 | | |
110 | 161 | | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
| 162 | + | |
118 | 163 | | |
119 | 164 | | |
120 | 165 | | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
| 166 | + | |
128 | 167 | | |
129 | 168 | | |
130 | 169 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
15 | 26 | | |
16 | 27 | | |
17 | 28 | | |
| |||
0 commit comments