[canvaskit] Add Typeface.MakeFreeTypeFaceFromData

This will replace needing to call FontMgr.RefDefault()
just to call MakeTypefaceFromData().

Change-Id: I72a8c3be62267f6c54c48a5309ef9351acb16768
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/421916
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Kevin Lubick 2021-06-25 14:06:42 -04:00
parent 7bf799956d
commit 7027be602b
7 changed files with 62 additions and 21 deletions

View File

@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- `Typeface.MakeFreeTypeFaceFromData` as a more convenient way to create a Typeface from the bytes
of a .ttf, .woff, or .woff2 file.
### Fixed
- We should no longer have to decode the same font multiple times (skbug.com/12112)
### Deprecated
- `FontMgr.MakeTypefaceFromData` will be removed in favor of `Typeface.MakeFreeTypeFaceFromData`
- `FontMgr.RefDefault` will be removed in an upcoming version. It's only real use was
for `FontMgr.MakeTypeface`.
## [0.28.0] - 2021-06-17
### Added

View File

@ -1866,7 +1866,14 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers());
class_<SkTypeface>("Typeface")
.smart_ptr<sk_sp<SkTypeface>>("sk_sp<Typeface>");
.smart_ptr<sk_sp<SkTypeface>>("sk_sp<Typeface>")
.class_function("_MakeFreeTypeFaceFromData", optional_override([](WASMPointerU8 fPtr,
int flen)->sk_sp<SkTypeface> {
uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
sk_sp<SkData> fontData = SkData::MakeFromMalloc(font, flen);
return SkFontMgr::RefDefault()->makeFromData(fontData);
}), allow_raw_pointers());
#endif
class_<SkVertices>("Vertices")

View File

@ -691,6 +691,11 @@ var CanvasKit = {
_MakeFromText: function() {},
},
Typeface: {
MakeFreeTypeFaceFromData: function() {},
_MakeFreeTypeFaceFromData: function() {},
},
// These are defined in interface.js
Vector: {
add: function() {},

View File

@ -128,7 +128,7 @@ CanvasKit._extraInitializations.push(function() {
return fm;
};
// fontData should be an arrayBuffer
// TODO(kjlubick) Remove this deprecated API
CanvasKit.FontMgr.prototype.MakeTypefaceFromData = function(fontData) {
var data = new Uint8Array(fontData);
@ -143,6 +143,20 @@ CanvasKit._extraInitializations.push(function() {
return font;
};
CanvasKit.Typeface.MakeFreeTypeFaceFromData = function(fontData) {
var data = new Uint8Array(fontData);
var fptr = copy1dArray(data, 'HEAPU8');
var font = CanvasKit.Typeface._MakeFreeTypeFaceFromData(fptr, data.byteLength);
if (!font) {
Debug('Could not decode font data');
// We do not need to free the data since the C++ will do that for us
// when the font is deleted (or fails to decode);
return null;
}
return font;
};
CanvasKit.TextBlob.MakeOnPath = function(str, path, font, initialOffset) {
if (!str || !str.length) {
Debug('ignoring 0 length string');

View File

@ -51,6 +51,7 @@ CanvasKitInit({locateFile: (file: string) => '/node_modules/canvaskit/bin/' + fi
shaderTests(CK);
surfaceTests(CK);
textBlobTests(CK);
typefaceTests(CK);
vectorTests(CK);
verticesTests(CK);
});
@ -899,6 +900,10 @@ function textBlobTests(CK: CanvasKit, font?: Font, path?: Path) {
const blob8 = tb.MakeOnPath('tuv', path, font, 10); // $ExpectType TextBlob
}
function typefaceTests(CK: CanvasKit) {
const face = CK.Typeface.MakeFreeTypeFaceFromData(new ArrayBuffer(10));
}
function vectorTests(CK: CanvasKit) {
const a = [1, 2, 3];
const b = [4, 5, 6];

View File

@ -419,6 +419,7 @@ export interface CanvasKit {
readonly RuntimeEffect: RuntimeEffectFactory;
readonly Shader: ShaderFactory;
readonly TextBlob: TextBlobFactory;
readonly Typeface: TypefaceFactory;
readonly TypefaceFontProvider: TypefaceFontProviderFactory;
// Misc
@ -3523,6 +3524,15 @@ export interface TextStyleConstructor {
new(ts: TextStyle): TextStyle;
}
export interface TypefaceFactory {
/**
* Create a typeface using Freetype from the specified bytes and return it. CanvasKit supports
* .ttf, .woff and .woff2 fonts. It returns null if the bytes cannot be decoded.
* @param fontData
*/
MakeFreeTypeFaceFromData(fontData: ArrayBuffer): Typeface | null;
}
export interface TypefaceFontProviderFactory {
/**
* Return an empty TypefaceFontProvider

View File

@ -64,8 +64,7 @@ describe('Font Behavior', () => {
});
gm('serif_text_on_path', (canvas) => {
const fontMgr = CanvasKit.FontMgr.RefDefault();
const notoSerif = fontMgr.MakeTypefaceFromData(notoSerifFontBuffer);
const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer);
const paint = new CanvasKit.Paint();
paint.setAntiAlias(true);
@ -93,13 +92,11 @@ describe('Font Behavior', () => {
notoSerif.delete();
font.delete();
fontPaint.delete();
fontMgr.delete();
});
// https://bugs.chromium.org/p/skia/issues/detail?id=9314
gm('nullterminators_skbug_9314', (canvas) => {
const fontMgr = CanvasKit.FontMgr.RefDefault();
const bungee = fontMgr.MakeTypefaceFromData(bungeeFontBuffer);
const bungee = CanvasKit.Typeface.MakeFreeTypeFaceFromData(bungeeFontBuffer);
// yellow, to make sure tofu is plainly visible
canvas.clear(CanvasKit.Color(255, 255, 0, 1));
@ -123,13 +120,11 @@ describe('Font Behavior', () => {
bungee.delete();
font.delete();
fontPaint.delete();
fontMgr.delete();
});
gm('textblobs_with_glyphs', (canvas) => {
canvas.clear(CanvasKit.WHITE);
const fontMgr = CanvasKit.FontMgr.RefDefault();
const notoSerif = fontMgr.MakeTypefaceFromData(notoSerifFontBuffer);
const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer);
const font = new CanvasKit.Font(notoSerif, 24);
const bluePaint = new CanvasKit.Paint();
@ -187,7 +182,6 @@ describe('Font Behavior', () => {
redPaint.delete();
notoSerif.delete();
font.delete();
fontMgr.delete();
});
it('can make a font mgr with passed in fonts', () => {
@ -216,7 +210,6 @@ describe('Font Behavior', () => {
});
gm('various_font_formats', (canvas, fetchedByteBuffers) => {
const fontMgr = CanvasKit.FontMgr.RefDefault();
const fontPaint = new CanvasKit.Paint();
fontPaint.setAntiAlias(true);
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
@ -245,7 +238,7 @@ describe('Font Behavior', () => {
// smoke test that the font bytes loaded.
expect(fontType.buffer).toBeTruthy(fontType.type + ' did not load');
const typeface = fontMgr.MakeTypefaceFromData(fontType.buffer);
const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(fontType.buffer);
const font = new CanvasKit.Font(typeface, 24);
if (font && typeface) {
@ -261,7 +254,7 @@ describe('Font Behavior', () => {
// which doesn't have very many glyphs in it, so we just check that we got a non-zero
// typeface for it. I was able to load NotoSansCJK-Regular.ttc just fine in a
// manual test.
const typeface = fontMgr.MakeTypefaceFromData(fetchedByteBuffers[3]);
const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(fetchedByteBuffers[3]);
expect(typeface).toBeTruthy('.ttc font');
if (typeface) {
canvas.drawText('.ttc loaded', 5, 180, fontPaint, defaultFont);
@ -272,12 +265,10 @@ describe('Font Behavior', () => {
defaultFont.delete();
fontPaint.delete();
fontMgr.delete();
}, '/assets/Roboto-Regular.otf', '/assets/Roboto-Regular.woff', '/assets/Roboto-Regular.woff2', '/assets/test.ttc');
it('can measure text very precisely with proper settings', () => {
const fontMgr = CanvasKit.FontMgr.RefDefault();
const typeface = fontMgr.MakeTypefaceFromData(notoSerifFontBuffer);
const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer);
const fontSizes = [257, 100, 11];
// The point of these values is to let us know 1) we can measure to sub-pixel levels
// and 2) that measurements don't drastically change. If these change a little bit,
@ -298,15 +289,13 @@ describe('Font Behavior', () => {
}
typeface.delete();
fontMgr.delete();
});
gm('font_edging', (canvas) => {
// Draw a small font scaled up to see the aliasing artifacts.
canvas.scale(8, 8);
canvas.clear(CanvasKit.WHITE);
const fontMgr = CanvasKit.FontMgr.RefDefault();
const notoSerif = fontMgr.MakeTypefaceFromData(notoSerifFontBuffer);
const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer);
const textPaint = new CanvasKit.Paint();
const annotationFont = new CanvasKit.Font(notoSerif, 6);
@ -330,7 +319,6 @@ describe('Font Behavior', () => {
annotationFont.delete();
testFont.delete();
notoSerif.delete();
fontMgr.delete();
});
it('can get the intercepts of glyphs', () => {