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

[avar2] Enable non-orthogonal axis distortions #14

Closed
behdad opened this issue Sep 14, 2021 · 24 comments
Closed

[avar2] Enable non-orthogonal axis distortions #14

behdad opened this issue Sep 14, 2021 · 24 comments
Labels
Implemented Implemented in HarfBuzz or doesn't need to VarFonts2.0

Comments

@behdad
Copy link
Member

behdad commented Sep 14, 2021

Update: the aim of this proposal is just to enable non-orthogonal axes distortions. The higher-order interpolation will come by way of ItemVariationStore upgrades, via eg. #17

Update: Settled on the following format:
If version is 0x00020000, the format1-like struct is followed by the following fields:

struct avar2 {
  Offset32To<DeltaSetIndexMap> axisIdxMap;
  Offset32To<ItemVariationStore> varStore;
};
@behdad behdad changed the title [avar2] varfonts++ [avar2] VarFonts 2.0 Sep 14, 2021
@behdad
Copy link
Member Author

behdad commented Sep 14, 2021

cc @rsheeter @Lorp @twardoch

@behdad
Copy link
Member Author

behdad commented Sep 14, 2021

There's no further requirements on the mapping. The mapping is NOT required to be reversible.

The pre-normalization/post-normalization to be determined / documented.

@Lorp
Copy link
Collaborator

Lorp commented Sep 14, 2021

I suggested using varation math on avar mappings in a call to @twardoch a few months ago. I should have written it up! I think we’ll need a new ItemVariationStore format, since we’re talking about deltas in n-space, not the 1-space of MVAR/HVAR/VVAR or the 2-space of gvar.

So if I understand your proposal correctly, these deltas are n-D vectors in normalized variation space, with linear response when 1 axis is non-default, quadratic response when 2 axes are non-default, cubic response when 3 axes are non-default, etc. As you say, normalization is to be determined. What about behaviour if the vector causes a transition across 0 on any axis? Maybe that’s fine.

@behdad behdad changed the title [avar2] VarFonts 2.0 [VarFonts2.0] [avar2] Sep 15, 2021
@behdad behdad added this to the BE 1.0 ~= OT 2.0 milestone Sep 15, 2021
@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

On further thought:

Let's use a VarIdxMap, aka DeltaSetIndexMap for the (optional!) mapping. The mapping logic would be different from that of HVAR table in that, unlike in HVAR, the last entry is not repeated ad infinitum. Entries not in the mapping get a varidx of NO_VARIATION (aka 0xFFFFFFFF; or just 0 variation).

This also removes the 16bit limit on the number of axes, conceptually.

I also wanted to say let's borrow from how we extended COLRv1, and instead of going to major 2, go to major 1 minor 1, such that older implementations can use older representation still. While we can do that, it has some inconvenience because the old representation has to be parsed before new representation can be found. Still, I'm supportive of possibly doing it that way.

struct avar2 {
  uint16 major; // set to 2
  uint16 minor; // set to 0
  Offset32To<ItemVariationStore> varStore; // VarStore mapping axes from unwarped user-space to warped design-space
  Offset32To<DeltaSetIndexMap> map; // Optional VarIdxMap mapping axis index into VarIdx to be looked up in VarStore
};

@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

The pre-normalization/post-normalization to be determined / documented.

Axis values are normalized to -1,+1 based on fvar axis min,default,max; then they are transformed via avar1 or avar2. Finally they are clamped to -1,+1 again. Some implementations then convert them to F2DOT14 at this point.

There is a functional requirement that if all input axes are at their fvar default position, then all output axes are at zero. But there's no mechanism to try to enforce that. OT1.8 made such an assumption and thought it enforced it, but failed at it.

@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

There is a functional requirement that if all input axes are at their fvar default position, then all output axes are at zero.

Or rather, the functional requirement is that: if all input axes are at their fvar default position, the output should be identical to situation that no deltas are applied. That is, as if all output axes are zero...

@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

I suggested using varation math on avar mappings in a call to @twardoch a few months ago. I should have written it up! I think we’ll need a new ItemVariationStore format, since we’re talking about deltas in n-space, not the 1-space of MVAR/HVAR/VVAR or the 2-space of gvar.

No no no. gvar is different in that it encodes spatial deltas.

ItemVariationStore always encodes 1-dimensional scalar deltas. That doesn't have to change. We encode each axes deltas separately.

So if I understand your proposal correctly, these deltas are n-D vectors in normalized variation space,

Except that each dimension's deltas is separately encoded.

with linear response when 1 axis is non-default, quadratic response when 2 axes are non-default, cubic response when 3 axes are non-default, etc.

Correct. Same non-linearity that we enable across the system.

As you say, normalization is to be determined. What about behaviour if the vector causes a transition across 0 on any axis? Maybe that’s fine.

I tried to address that in my previous two comments.

@Lorp
Copy link
Collaborator

Lorp commented Sep 15, 2021

So if I understand your proposal correctly, these deltas are n-D vectors in normalized variation space,

Except that each dimension's deltas is separately encoded.

How can you keep axes separate if an arbitrary location in n-space maps to another arbitrary location in n-space?

@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

How can you keep axes separate if an arbitrary location in n-space maps to another arbitrary location in n-space?

Each output axis is a piecewise-linear function of all input axes.

Imagine a point rotating in HOI. Both output X and Y are functions of both input X and Y, but you can represent/encode them separately.

@Lorp
Copy link
Collaborator

Lorp commented Sep 15, 2021

I don’t follow you. With 2 fvar axes both acting on avar, a point designspace location is acted on by the sum of axis0 × vector0 (linear), axis1 × vector1 (linear), and axis0 × axis1 × vector2 (quadratic), where the vectors are 2-D.

@behdad
Copy link
Member Author

behdad commented Sep 15, 2021

Now I don't follow you either. Let's get on VC.

@behdad behdad changed the title [VarFonts2.0] [avar2] [VarFonts2.0] [avar2] Enable non-orthogonal axis distortions Sep 15, 2021
@behdad behdad changed the title [VarFonts2.0] [avar2] Enable non-orthogonal axis distortions [avar2] Enable non-orthogonal axis distortions Sep 19, 2021
@behdad
Copy link
Member Author

behdad commented Sep 22, 2021

There's no requirement that if all input axes are at default, then all output axes must be at zero. This would allow moving default freely. See: #17 (comment)

We can declare fonts with avar2 requiring variations processing. I want to think more about it though.

@behdad behdad added the WIP label Sep 22, 2021
@davelab6
Copy link
Contributor

Will google/fonts#2981 (comment) be supported?

@behdad
Copy link
Member Author

behdad commented Sep 30, 2021

Will google/fonts#2981 (comment) be supported?

Yes. No such constraints.

@davelab6
Copy link
Contributor

davelab6 commented May 5, 2022

Apple's VF version of SF Pro just dropped last week, with similar VF axes to Roboto Serif, https://v-fonts.com/fonts/sf-pro

The grade axis isn't "safe" with the weight axis; a good case study for avar2 :)

Screen Shot 2022-05-05 at 1 39 19 PM

@davelab6
Copy link
Contributor

Nice comments from @tiroj at https://twitter.com/TiroTypeworks/status/1524085630367506432

@behdad
Copy link
Member Author

behdad commented Jun 17, 2022

After some more thinking I decided it might be better to retain avar1 fields that do a linear per-axis mapping first, then do the full-matrix mapping. I also sketched a simple designspace-file xml that implements wght axis HOI:
https://gist.github.com/behdad/7e74e0970280a0cf621671af6f0fa749#file-avar1-1-designspace-sketch

@behdad
Copy link
Member Author

behdad commented Jun 17, 2022

So this is the new avar table format I'm proposing, in fonttools otData format:

        ('avar', [
                ('Version', 'Version', None, None, 'Version of the avar table- 0x00010000 or 0x00020000'),
                ('uint16', 'Reserved', None, None, 'Permanently reserved; set to zero'),
                ('uint16', 'AxisCount', None, None, 'The number of variation axes for this font. This must be the same number as axisCount in the "fvar" table'),
                ('AxisSegmentMap', 'AxisSegmentMap', 'AxisCount', 0, 'The segment maps array — one segment map for each axis, in the order of axes specified in the "fvar" table'),
                ('LOffset', 'VarIdxMap', None, 'Version >= 0x00020000', ''),
                ('LOffset', 'VarStore', None, 'Version >= 0x00020000', ''),
        ]),

@behdad
Copy link
Member Author

behdad commented Jun 17, 2022

('LOffset', 'VarIdxMap', None, 'Version >= 0x00020000', ''),

Note this VarIdxMap is called DeltaSetIndexMap in the OT spec.

@behdad behdad added the Implemented Implemented in HarfBuzz or doesn't need to label Jul 12, 2022
@behdad
Copy link
Member Author

behdad commented Jul 22, 2022

Here's sample script to generate avar2 table from RobotoFlex relationships between wght/wdth/opsz axes and its parametric axes:
https://github.com/behdad/robotoflex-avar2/blob/main/robotoflex-avar2.py

@behdad
Copy link
Member Author

behdad commented Jul 26, 2022

The code for this is in HarfBuzz 5.0.0, and a FreeType patch is in:

https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/188

A sample font file (RobotoFlex) is in:

https://drive.google.com/file/d/1fcb9pyt6P7tDOnMf7AaTK4B7Apt4P1mK/view

The wght, wdth, and opsz axes are routed through avar2.

@behdad
Copy link
Member Author

behdad commented Aug 2, 2022

I'm drafting a spec for this here:
https://github.com/harfbuzz/boring-expansion-spec/blob/avar2/avar2.md

@behdad
Copy link
Member Author

behdad commented Feb 7, 2023

This is complete.

@behdad behdad closed this as completed Feb 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Implemented Implemented in HarfBuzz or doesn't need to VarFonts2.0
Projects
None yet
Development

No branches or pull requests

3 participants