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

Support SkFontArguments::Palette #297

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

khaledhosny
Copy link

To allow selecting color fonts palette.

@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

I have let CI started, but I wish you have got in touch earlier, as I have already got some of this overlapping in my hard disk already. See #259

src/skia/Font.cpp Outdated Show resolved Hide resolved
tests/test_font.py Outdated Show resolved Hide resolved
tests/test_font.py Outdated Show resolved Hide resolved
tests/test_font.py Outdated Show resolved Hide resolved
@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

@khaledhosny ci failed on mac os (which has python 3.13.1). You need to pull in the fix for #295 , which is #296 , to pass ci.

@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

FWIW, even namespacing these classes under FontArguments is troublesome - there is aSkFontArguments class upstream in core, and a skia::textlayout::FontArguments class in the paragraph module.

@khaledhosny
Copy link
Author

I’m not sure I get the namespacing comments. The classes are already nested in Python, skia.FontArguments.Palette(), skia.FontArguments.Palette.Overrides(), and skia.FontArguments.Palette.Override(), so I’m not sure what should I do other than that.

@khaledhosny khaledhosny force-pushed the fontargument-palette branch from 0441314 to 3bab798 Compare January 2, 2025 17:54
@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

This #298 isn't how it is on my hard disk (I have neighbouring code with does something else, so there are collisions while cherry-picking), nor ready for used or tested, but I think if it works, together with your pull, should mostly just be the other half of allowing one to address #259 , loading COLRv1 fonts with custom / non-default palettes.

@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

With the two together, you should be able to do something like this to load a colrv1 font with custom palette:

# Read font file into fontdata
scanner = skia.FontScanner()
# do stuff with FontArguments.Palette
newface = scanner.MakeFromStream(fontdata, fontargs)

@khaledhosny
Copy link
Author

khaledhosny commented Jan 2, 2025

With this PR I can already select non-default palettes and override their colors in COLR (v0 and v1) fonts, but I’m testing only with FreeType fonts:

fontMgr = skia.FontMgr.New_Custom_Empty()

face1 = fontMgr.makeFromFile(fontpath)
font1 = skia.Font(face, face.getUnitsPerEm())

fontArgs2 = skia.FontArguments()
fontArgs2.setPalette(1)
face2 = face1.makeClone(fontArgs2)
font2 = skia.Font(face2, size)

fontArgs3 = skia.FontArguments()
palette3 = skia.FontArguments.Palette(
    1,
    skia.FontArguments.Palette.Overrides(
        [
            skia.FontArguments.Palette.Override(0, skia.ColorMAGENTA),
            skia.FontArguments.Palette.Override(6, skia.ColorBLUE),
            skia.FontArguments.Palette.Override(7, skia.ColorGREEN),
            skia.FontArguments.Palette.Override(8, skia.ColorCYAN),
        ]
    ),
)
fontArgs3.setPalette(palette3)
face3 = face1.makeClone(fontArgs3)
font3 = skia.Font(face3, size)

string = "\u06DD"
text1 = skia.TextBlob.MakeFromShapedText(string, font1)
text2 = skia.TextBlob.MakeFromShapedText(string, font2)
text3 = skia.TextBlob.MakeFromShapedText(string, font3)

bounds = text.bounds()

width = math.floor(bounds.width() * 3)
height = math.floor(bounds.height())

surface = skia.Surface(width, height)
with surface as canvas:
    canvas.drawColor(skia.ColorWHITE)
    paint = skia.Paint()
    canvas.drawTextBlob(text1, -bounds.x(), -bounds.y(), paint)
    canvas.drawTextBlob(text2, bounds.width() - bounds.x(), -bounds.y(), paint)
    canvas.drawTextBlob(text3, bounds.width() * 2 - bounds.x(), -bounds.y(), paint)

output

@HinTak
Copy link
Collaborator

HinTak commented Jan 2, 2025

Interesting. Thanks for the code - some of it can go into the tests directory. (skia's resources/fonts/ is available for tests).

FWIW, I have some incomplete code which supposedly let you do face.getFontDescriptor().setPaletteIndex(N) (and short-circuiting to face.setPaletteIndex(N)) to switch. Does not work (yet).

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

If the asserts are not logically independent I.e. you just want an exact match of a composite structure, it might be clearer to do assert x and y and z a single mulit-line assert, any mis-match is considered a failure. If there are multiple independent/stages ways of failure, you do a fixture then multiple tests.

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

I think face.getFontDescriptor().setPaletteIndex(N) not working is an upstream bug but I have not gotten round to file it yet. Basically the paletteindex is stored in an intermediate structure but never passed onto the font renderer. In the case of colrv1, freetype never gets it set. But similar comments applies to the colrv0 path in the directwrite rendering path too.

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

assert isinstance... is typically one per test, and the object itself done as a re-used fixture. See the other tests.

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

See for example the fixture at

@pytest.fixture(scope='session')
and the init test below.

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

And all the fxtures finally get passed to the usage test at

def test_Paragraph_linebreak(paragraph_builder, textlayout_text_style, textlayout_font_collection, paragraph_style):

To allow selecting color fonts palette.
@khaledhosny khaledhosny force-pushed the fontargument-palette branch from 3bab798 to 6e18951 Compare January 3, 2025 12:54
@khaledhosny
Copy link
Author

Interesting. Thanks for the code - some of it can go into the tests directory. (skia's resources/fonts/ is available for tests).

That would be nice, but testing palette overrides will requiring checking the rendered output (possibly against a reference image), but I don’t see any tests doing something like this.

@HinTak
Copy link
Collaborator

HinTak commented Jan 3, 2025

That would be nice, but testing palette overrides will requiring checking the rendered output (possibly against a reference image), but I don’t see any tests doing something like this.

Well, setting and getting it back should work. It is probably possible to render to a surface and detect it is "mostly red", say, just by count the red pixels. I have a test in the paragraph part, which used to have trouble with rendering "\n" as a new line rather than .notdef. The test is simply that the rendered area should be tall than wide, whichever the default font is.

@@ -46,6 +46,44 @@ def test_FontArguments_setCollectionIndex(fontarguments):
fontarguments.setCollectionIndex(0)


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there needs to be a dozen more tests for each of the new properties/methods of the new classes added.

return std::vector<Override>(
self.overrides, self.overrides + self.overrideCount);
},
&SetPaletteOverrides)
Copy link
Collaborator

@HinTak HinTak Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need a keep_Alive policy here too? On the set method.

@HinTak
Copy link
Collaborator

HinTak commented Jan 5, 2025

I am starting to see the variationposition code from which this modelled from a bit buggy about life times and zero length arguments...

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.

2 participants