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

[COLRv2] Paint templates #371

Open
behdad opened this issue Jul 21, 2023 · 7 comments
Open

[COLRv2] Paint templates #371

behdad opened this issue Jul 21, 2023 · 7 comments
Milestone

Comments

@behdad
Copy link
Collaborator

behdad commented Jul 21, 2023

Motivated by #370, I like to go further and propose what @matthiasclasen and I have been talking about, which is paint templates. This does NOT supersede @simoncozens's proposal in #370 though. That would be separately a good idea.

struct PaintTemplateArgument
{
  uint8 PaintFormat;
  uint8 ArgumentIndex;
};

struct PaintTemplateInstance
{
  uint8 PaintFormat;
  Offset24To<Paint> TemplatePaint;
  Array8Of<Offset24To<Paint>> Arguments;
};

The way it works is that the PaintTemplateInstantce will /instantiate/ the template paint, with the arguments set in Arguments. The template then can invoke arguments using PaintTemplateArgument paints.

@anthrotype
Copy link
Member

sorry, what does it mean to "instantiate a template paint"? Can you make an example of how this would work?

@behdad
Copy link
Collaborator Author

behdad commented Jul 21, 2023

sorry, what does it mean to "instantiate a template paint"?

It will draw the PaintTemplate paint subgraph, and every time it encounters a PaintTemplateArgument paint, it replaces it with Arguments[ArgumentIndex] paint where Arguments is from the closest PaintTemplateInstance in the paint parent chain, and ArgumentIndex is from the encountered PaintTemplateArgument.

That is, PaintTemplateArguments are substituted with actual paints from the Arguments vector.

Can you make an example of how this would work?

I'll try to add.

@behdad
Copy link
Collaborator Author

behdad commented Jul 21, 2023

Here's an example. Imagine we want to create "boy" emoji with multiple skin colors. What we can do is to have one template paint for the emoji, which has one variable paint for the face gradient:

Paint boy {
   ...//drawing commands, one of which uses:
     PaintTemplateArgument{
       ArgumentIndex = 0;
     },
  ...
};

Paint faceColor0 { PaintLinearGradient { ... } };
Paint faceColor1 { PaintLinearGradient { ... } };
Paint faceColor2 { PaintLinearGradient { ... } };
Paint faceColor3 { PaintLinearGradient { ... } };
Paint faceColor4 { PaintLinearGradient { ... } };

PaintTemplateInstantce boy_faceColor0 {
  TemplatePaint = boy;
  Arguments = [faceColor0];
};

@anthrotype

This comment was marked as outdated.

@behdad

This comment was marked as outdated.

behdad added a commit to fonttools/fonttools that referenced this issue Aug 3, 2023
behdad added a commit to fonttools/fonttools that referenced this issue Aug 3, 2023
behdad added a commit to fonttools/fonttools that referenced this issue Aug 3, 2023
behdad added a commit to fonttools/fonttools that referenced this issue Aug 3, 2023
behdad added a commit to harfbuzz/harfbuzz that referenced this issue Aug 3, 2023
behdad added a commit to harfbuzz/harfbuzz that referenced this issue Aug 3, 2023
behdad added a commit to fonttools/fonttools that referenced this issue Aug 3, 2023
behdad added a commit to fonttools/fonttools that referenced this issue Aug 4, 2023
@behdad
Copy link
Collaborator Author

behdad commented Aug 4, 2023

So I went ahead and implemented this in FontTools & HarfBuzz.

I also wrote some code to take NotoColorEmoji-Regular.ttf or any other COLRv1 font and extract templates from it and templatize glyphs that can benefit.

The savings unfortunately are meager. It saves 2.84% of the COLR table size, which translates to only 0.63% of the total font. This is expected since most glyphs do not templatize in a full emoji repertoire. Here's the tools output:

$ time fonttools colorLib.templatize NotoColorEmoji-Regular.ttf 
3719 glyph paints
67532 layer paints
Original COLR table is 1058175 bytes
294 unique templates for 1246 glyphs
Skipped 23 templates as they didn't save space
Building templatized font
3719 glyph paints
58905 layer paints
Reconstructed COLR table is 1028118 bytes
Templatized COLR table is 2.84% smaller.
Saving NotoColorEmoji-Regular.templatized.ttf

real	0m14.381s
user	0m14.126s
sys	0m0.197s

To summarize: I think this is still worth considering, but the savings would not justify jumping on it just yet. Perhaps when we have more COLRv2 ideas we can sneak this in too.

FontTools PR: fonttools/fonttools#3242
HarfBuzz PR: harfbuzz/harfbuzz#4361
Templatizer: fonttools colorLib.templatize with the above FontTools PR

@behdad behdad added this to the COLRv2 milestone Aug 6, 2023
@simoncozens
Copy link

This may not give you much on its own, but I think the savings of this combined with PaintSelf might be more interesting to look at.

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

No branches or pull requests

3 participants