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

Updating the SKFont members #1101

Merged
merged 6 commits into from Jan 15, 2020
Merged

Updating the SKFont members #1101

merged 6 commits into from Jan 15, 2020

Conversation

@mattleibow
Copy link
Contributor

mattleibow commented Jan 14, 2020

So far, the GetGlyphs and CountGlyphs is what I am focusing on right now to decide what the best API is.

Previously it was all arrays and allocations. I am now trying to have the option to use arrays if need be, but more provide the option to have zero-allocation code.

mattleibow added 3 commits Jan 14, 2020
var glyphs = new ushort[codepoints.Length];
GetGlyphs (codepoints, glyphs);
return glyphs;
Comment on lines +140 to +142

This comment has been minimized.

Copy link
@mattleibow

mattleibow Jan 14, 2020

Author Contributor

This is option A for the most basic. Ask for a value, and we will make sure we give you the return you are expecting. This will result in an allocation, but does not require you to worry about releasing memory.

var n = CountGlyphs (text);
if (n <= 0)
return new ushort[0];

var glyphs = new ushort[n];
GetGlyphs (text, glyphs);
return glyphs;
Comment on lines +160 to +166

This comment has been minimized.

Copy link
@mattleibow

mattleibow Jan 14, 2020

Author Contributor

This is an alternate option A that has 2 steps to allocation - first determine the size, then allocate. Similar to the previous, the allocation happens inside the member and the GC cleans up.

fixed (int* up = codepoints)
fixed (ushort* gp = glyphs) {
SkiaApi.sk_font_unichars_to_glyphs (Handle, up, codepoints.Length, gp);
}
Comment on lines +147 to +150

This comment has been minimized.

Copy link
@mattleibow

mattleibow Jan 14, 2020

Author Contributor

This is option B, no allocations internally. All memory must be provided and it is then used as is.

Potential usage with ArrayPool:

// magic some codepoints up
int[] codepoints = GetCodepoints ();

// get from the pool
var glyphs = ArrayPool<ushort>.Shared.Rent (codepoints.Length);

var font = new SKFont ();
font.GetGlyphs (codepoints, glyphs);

ProcessGlyphs (glyphs);

// return to pool
ArrayPool<ushort>.Shared.Return (glyphs);
fixed (void* p = text)
fixed (ushort* gp = glyphs) {
SkiaApi.sk_font_text_to_glyphs (Handle, p, (IntPtr)text.Length, SKTextEncoding.Utf16, gp, glyphs.Length);
}
Comment on lines +185 to +188

This comment has been minimized.

Copy link
@mattleibow

mattleibow Jan 14, 2020

Author Contributor

Again, option B without allocations.

Potential usage:

// some magic string
string text = "Hello World!";

var font = new SKFont ();

// first determine the size
var n = font.CountGlyphs (text);

// get from the pool
var glyphs = ArrayPool<ushort>.Shared.Rent (n);

// get the glyphs
font.GetGlyphs(text, glyphs);

ProcessGlyphs(glyphs);

// return to pool
ArrayPool<ushort>.Shared.Return(glyphs);
@mattleibow mattleibow added this to In progress in v2.x (maybe v2.80 or v2.81) via automation Jan 14, 2020
@Gillibald

This comment has been minimized.

Copy link
Contributor

Gillibald commented Jan 14, 2020

Looks very promising. I think we should use SKFont in combination with SKTextBlobBuilder from the beginning to make sure both work well with each other and are easy to use.

Passing a string to SKFont to get corresponding glyphs always needs to allocate. Being in control of the allocation is always a good option.

Sometimes users just want the data and don't want to deal with creating buffers and sometimes reusing a buffer is the best perfoming option. Not easy to design an API around these requirements.

Will add more comments in a bit.

@Gillibald

This comment has been minimized.

Copy link
Contributor

Gillibald commented Jan 14, 2020

Your overloads let you pass a pre-allocated buffer and also provide a way to let SkiaSharp allocate everything for you. All variants cover most possible scenarios.

In general, if SkiaSharp allocates it should return an array if the consumer of the API allocates a buffer the parameter should be of type Span.

So I think we agree here.

@Gillibald

This comment has been minimized.

Copy link
Contributor

Gillibald commented Jan 14, 2020

In general, we will not work with these methods directly and instead SkiaSharp.HarfBuzz will interact with SKFont to get the required information for shaping etc.

var text = "MyText".AsSpan();

var textBlob = skFont.GetTextBlob(text);
paint.DrawText(textBlob);

var shapedTextBlob = skFont.GetShapedTextBlob(text); // This could be an extention method
paint.DrawText(shapedTextBlob);

Both need to have some font fallback to produce desired results.

After the mapping step, we have to look for unmapped characters and match a fallback font for each range that wasn't mapped. Each sequence of mapped character is added to a SKTextBlobBuilder. The build SKTextBlob is then used to draw the glyphs.

SKTextBlobBuilder should be used to allocate the buffer it will allocate anyways so we should reuse this buffer. builder.AllocateRun(glyphCount) -> GetGlyphs(text, run.GlyphSpan) -> Build() -> Draw(textBlob)

mattleibow added 3 commits Jan 14, 2020
@mattleibow mattleibow merged commit 2aac145 into dev/update-skia Jan 15, 2020
1 of 2 checks passed
1 of 2 checks passed
SkiaSharp in progress
Details
license/cla All CLA requirements met.
Details
v2.x (maybe v2.80 or v2.81) automation moved this from In progress to Done Jan 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants
You can’t perform that action at this time.