Skip to content

Render VARC fonts#1733

Open
behdad wants to merge 114 commits intomainfrom
varc-render
Open

Render VARC fonts#1733
behdad wants to merge 114 commits intomainfrom
varc-render

Conversation

@behdad
Copy link
Contributor

@behdad behdad commented Feb 5, 2026

This hooks up VARC fonts. I have tested it with HB cmdline tools and seems to work nicely, although, 25% slower than HB on my test CJK font.

This breaks the VARC table API in read-fonts / write-fonts. But nothing else (hopefully).

@behdad
Copy link
Contributor Author

behdad commented Feb 6, 2026

Using hb-draw-compare, I'm seeing some correctness issues that I'm debugging.

Four bug fixes later, I'm down to diffs of up to 0.12 font units. Investigating more.

The spec is a bit confusing as the flag orders are opposite,
but the record clearly orders them this way.
@behdad
Copy link
Contributor Author

behdad commented Feb 6, 2026

Using hb-draw-compare, I'm seeing some correctness issues that I'm debugging.

Four bug fixes later, I'm down to diffs of up to 0.12 font units. Investigating more.

I tracked this down to pure float noise. I gave up investigation after a few hours. Accepting difference, since the diff is so small.

I think this is ready for review. Thanks.

- Old: round(base + (c1 + c2 + ...))
- HB/new: round(((base + c1) + c2) + ...)
- Those can differ by 1 ULP and flip rounding by 1 raw unit.

Still doesn't address the discrepancy I'm seeing :(.
This reverts commit 5124bd9.

While closer to HB, this commit had a measurable slowdown, for no
good benefit.
@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

I tracked this down to pure float noise.

Hours later... I convinced myself that the differences arises from Skrifa's FreeTypeScaler working in 26.6 whereas HB works in floats for drawing. All errors seem to be closely multiples of 1/256 font units...

There was a single-unit float value difference in the computed axes during VARC processing, but I don't think that one can have this scale of effect.

Anyway. Here's two graphs, of the max error over all glyphs, of the hanzi test font and the hangul.

image image

@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

All errors seem to be closely multiples of 1/256 font units...

I found the culprit to be cairo's 24.8 fixed type. The numbers from fontations are all multiples of 1/64, as we expect, whereas from HB they are more floaty. Because of scaling and rotation, this smaller error gets amplified, then quantized in cairo's SVG output.

I spent quite a few hours digging this rabbit hole, graphed the error (HB vs Skrifa) of all coordinates values in the hanzi VARC font I'm working with (45k glyphs). The error is behaving as we expect it given the parameters (HB in float; Skrifa in Fixed; cario I configured as 10.12 for this experiment; cairo SVG output to 17 digits of float printing.)

Here's a couple of graphs for display purposes only:

Code_Generated_Image (16) image image

Cairo patch used:

diff --git a/src/cairo-fixed-type-private.h b/src/cairo-fixed-type-private.h
index e9f26f615..fcac865c6 100644
--- a/src/cairo-fixed-type-private.h
+++ b/src/cairo-fixed-type-private.h
@@ -59,7 +59,7 @@ typedef cairo_int128_t        cairo_fixed_96_32_t;
  * making sure that you compute a double-to-fixed magic number.
  * (see below).
  */
-#define CAIRO_FIXED_FRAC_BITS  8
+#define CAIRO_FIXED_FRAC_BITS  12
 
 /* A signed type %CAIRO_FIXED_BITS in size; the main fixed point type */
 typedef int32_t cairo_fixed_t;
diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
index 799a73e04..f58bea335 100644
--- a/src/cairo-output-stream.c
+++ b/src/cairo-output-stream.c
@@ -319,6 +319,8 @@ _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precisi
 
     if (limited_precision) {
        snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d);
+    } else if (1) {
+       snprintf (buffer, size, "%.17f", d);
     } else {
        /* Using "%f" to print numbers less than 0.1 will result in
         * reduced precision due to the default 6 digits after the

@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

The rabbithole also led me to file this issue, which doesn't seem to have a significant effect on this dataset, but a bug nonetheless: #1738

@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

I spent quite a few hours digging this rabbit hole

For my own reference: Gemini session: https://gemini.google.com/share/54b90c1c1d1f

@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

The final verdict is that the Fixed (26.6) pipeline in Skrifa's FreeTypeScaler is hurting VARC accuracy a lot, because of the transformations applied to component shapes (scale of up to 10 in my test data), amplifying the precision error. Max error observed is 12% of a font unit on a 1000 upem. Still invisible, but not zero...

@behdad
Copy link
Contributor Author

behdad commented Feb 7, 2026

Tests added.

@behdad behdad force-pushed the varc-render branch 5 times, most recently from 64fefb9 to 09d32d5 Compare February 7, 2026 23:32
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 this pull request may close these issues.

3 participants