Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable font: differently rounded x-advance between --font-funcs=ot and --font-funcs=ft #2496

Closed
justvanrossum opened this issue Jun 24, 2020 · 7 comments · Fixed by #2509
Closed

Comments

@justvanrossum
Copy link
Contributor

justvanrossum commented Jun 24, 2020

With the attached variable font, I get different x-advances, depending on which --font-funcs I use:

$ hb-shape HBTest-VF.ttf --variations=TEST=491 --font-funcs=ft A
[A=0+496]
$ hb-shape HBTest-VF.ttf --variations=TEST=491 --font-funcs=ot A
[A=0+495]

The unrounded x-advance is 495.5.

The OT spec contains this bit:

Multiply the fractional component by 65536, and round the result to the nearest integer (for fractional values of 0.5 and higher, take the next higher integer; for other fractional values, truncate).

However, this applies to "when converting from float or double data types to 16.16", which isn't what's happening here. So strictly speaking, the OT spec is silent on this (I haven't found any other references to rounding).

Still I think there's a point in arguing that the rounded-up .5 value is the correct one: both FreeType and DirectWrite round like this, as do most traditional rounding funcs that do not apply bankers rounding.

Interestingly enough, at another value along the TEST axis, the ot funcs do round up:

$ hb-shape HBTest-VF.ttf --variations=TEST=509 --font-funcs=ft A
[A=0+505]
$ hb-shape HBTest-VF.ttf --variations=TEST=509 --font-funcs=ot A
[A=0+505]

The unrounded x-advance is 504.5 in this case.

The difference between the two cases is in the delta for the x-advance: for TEST=491 the x-advance delta is negative (the default is 500), and for TEST=509 it is positive. So that might be a clue as to why the rounding is different at all.

HBTest-VF.ttf.zip

@ebraminio
Copy link
Collaborator

ebraminio commented Jun 24, 2020

just to add uharfbuzz currently uses this implementation which apparently have the same result as libc's roundf (as far as I could check by adding HAVE_ROUNDF to defined macros or removing it)

@justvanrossum
Copy link
Contributor Author

Right, so the standard roundf() rounds halfway values away from zero, as does the hb implementation of roundf().

And indeed, the advance code rounds the delta before adding it to the neutral (int) advance, so that perfectly explains the behavior I observe.

If rounding would be applied after the addition, we'd get the correct behavior, but that would cause an unnecessary cast to float. A round function that rounds halfway values towards positive infinity would solve it, too.

(I find rounding halfway values away from zero undesirable for geometry, and this issue is an example of that problem. Bankers rounding has similar problems. In fonttools we implemented a round function that always rounds halfway values towards positive infinity, as Python's builtin round() functions does bankers rounding.)

@justvanrossum
Copy link
Contributor Author

I had a quick look at all calls to roundf() in hb: IMO rounding halfway values towards positive infinity would be better in all cases. (Except when the input value is always positive, then there will obviously be no difference.)

@ebraminio
Copy link
Collaborator

ebraminio commented Jun 24, 2020

And indeed, the advance code rounds the delta before adding it to the neutral (int) advance, so that perfectly explains the behavior I observe.

If rounding would be applied after the addition, we'd get the correct behavior, but that would cause an unnecessary cast to float. A round function that rounds halfway values towards positive infinity would solve it, too.

Great analysis! I believe this cast to float worth the hassle, lets have this in the meanwhile.

If rounding towards positive infinity is what practiced everywhere it is definitely worth to have I believe.

@ebraminio
Copy link
Collaborator

ebraminio commented Jun 24, 2020

Uploaded #2499 maybe as a temporarily workaround based on your analysis.

@behdad
Copy link
Member

behdad commented Jun 28, 2020

IMO rounding halfway values towards positive infinity would be better in all cases.

I agree. And we shouldn't bother merging the clutter of rounding after addition, since that's cruft we'd need to remember after fixing the roundf.

behdad added a commit that referenced this issue Jun 28, 2020
behdad added a commit that referenced this issue Jun 28, 2020
ebraminio pushed a commit that referenced this issue Jun 28, 2020
@justvanrossum
Copy link
Contributor Author

Thanks everyone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants