diff --git a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts index 235ca7efbd..2402693136 100644 --- a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts +++ b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts @@ -10,6 +10,7 @@ import { SkCanvas, SkColorFilter, SkFont, + SkFontMgr, SkImage, SkImageFilter, SkImageInfo, @@ -22,6 +23,7 @@ import { SkShader, SkSurface, SkTextBlob, + SkTypeface, SkVertices, TypedArray, } from "canvaskit-wasm"; @@ -32,6 +34,8 @@ CanvasKitInit({locateFile: (file: string) => '/node_modules/canvaskit/bin/' + fi colorTests(CK); imageFilterTests(CK); imageTests(CK); + fontTests(CK); + fontMgrTests(CK); mallocTests(CK); maskFilterTests(CK); matrixTests(CK); @@ -39,7 +43,9 @@ CanvasKitInit({locateFile: (file: string) => '/node_modules/canvaskit/bin/' + fi pathEffectTests(CK); pathTests(CK); rectangleTests(CK); + shapedTextTests(CK); surfaceTests(CK); + textBlobTests(CK); }); // In an effort to keep these type-checking tests easy to read and understand, we can "inject" @@ -190,6 +196,59 @@ function imageFilterTests(CK: CanvasKit, colorFilter?: SkColorFilter) { CK.FilterQuality.None, filter6); } +function fontTests(CK: CanvasKit, face?: SkTypeface, paint?: SkPaint) { + if (!face || !paint) return; + const font = new CK.SkFont(); // $ExpectType SkFont + const f2 = new CK.SkFont(face); // $ExpectType SkFont + const f3 = new CK.SkFont(null); // $ExpectType SkFont + const f4 = new CK.SkFont(face, 20); // $ExpectType SkFont + const f5 = new CK.SkFont(null, 20); // $ExpectType SkFont + const f6 = new CK.SkFont(null, 20, 2, 3); // $ExpectType SkFont + const f7 = new CK.SkFont(face, 20, 4, 5); // $ExpectType SkFont + + const glyphMalloc = CK.MallocGlyphIDs(20); + const someGlyphs = [1, 2, 3, 4, 5]; + + const glyphBounds = font.getGlyphBounds(glyphMalloc, paint); // $ExpectType Float32Array + font.getGlyphBounds(someGlyphs, null, glyphBounds); + + const ids = font.getGlyphIDs('abcd'); + font.getGlyphIDs('efgh', 4, ids); + + const widths = font.getGlyphWidths(glyphMalloc, paint); + font.getGlyphWidths(someGlyphs, null, widths); + + font.getScaleX(); + font.getSize(); + font.getSkewX(); + font.getTypeface(); + const w2 = font.getWidths('abcdefg'); // $ExpectType number[] + const w = font.measureText('abc'); // $ExpectType number + font.setEdging(CK.FontEdging.Alias); + font.setEmbeddedBitmaps(true); + font.setHinting(CK.FontHinting.Slight); + font.setLinearMetrics(true); + font.setScaleX(5); + font.setSize(15); + font.setSkewX(2); + font.setSubpixel(true); + font.setTypeface(null); + font.setTypeface(face); +} + +function fontMgrTests(CK: CanvasKit) { + const fm = CK.SkFontMgr.RefDefault(); // $ExpectType SkFontMgr + + const buff1 = new ArrayBuffer(10); + const buff2 = new ArrayBuffer(20); + + const fm2 = CK.SkFontMgr.FromData(buff1, buff2); + fm.countFamilies(); + fm.getFamilyName(0); + + const tf = fm.makeTypefaceFromData(buff1); // $ExpectType SkTypeface +} + function paintTests(CK: CanvasKit, colorFilter?: SkColorFilter, imageFilter?: SkImageFilter, maskFilter?: SkMaskFilter, pathEffect?: SkPathEffect, shader?: SkShader) { if (!colorFilter || !colorFilter || !imageFilter || !maskFilter || !pathEffect || !shader) { @@ -364,6 +423,27 @@ function matrixTests(CK: CanvasKit) { const matr13 = m44.transpose([4, 5, 8]); // $ExpectType number[] } +function rectangleTests(CK: CanvasKit) { + const rectOne = CK.LTRBRect(5, 10, 20, 30); // $ExpectType Float32Array + const rectTwo = CK.XYWHRect(5, 10, 15, 20); // $ExpectType Float32Array + const iRectOne = CK.LTRBiRect(105, 110, 120, 130); // $ExpectType Int32Array + const iRectTwo = CK.XYWHiRect(105, 110, 15, 20); // $ExpectType Int32Array + const rrectOne = CK.RRectXY(rectOne, 3, 7); // $ExpectType Float32Array +} + +function shapedTextTests(CK: CanvasKit, textFont?: SkFont) { + if (!textFont) return; + + const shaped = new CK.ShapedText({ // $ExpectType ShapedText + font: textFont, + leftToRight: true, + text: 'this is shaped', + width: 20, + }); + const bounds = shaped.getBounds(); + shaped.getBounds(bounds); +} + function surfaceTests(CK: CanvasKit) { const canvasEl = document.querySelector('canvas') as HTMLCanvasElement; const surfaceOne = CK.MakeCanvasSurface(canvasEl)!; // $ExpectType SkSurface @@ -401,10 +481,19 @@ function surfaceTests(CK: CanvasKit) { CK.SkColorSpace.ADOBE_RGB)!; } -function rectangleTests(CK: CanvasKit) { - const rectOne = CK.LTRBRect(5, 10, 20, 30); // $ExpectType Float32Array - const rectTwo = CK.XYWHRect(5, 10, 15, 20); // $ExpectType Float32Array - const iRectOne = CK.LTRBiRect(105, 110, 120, 130); // $ExpectType Int32Array - const iRectTwo = CK.XYWHiRect(105, 110, 15, 20); // $ExpectType Int32Array - const rrectOne = CK.RRectXY(rectOne, 3, 7); // $ExpectType Float32Array +function textBlobTests(CK: CanvasKit, font?: SkFont, path?: SkPath) { + if (!font || !path) return; + const tb = CK.SkTextBlob; // less typing + const ids = font.getGlyphIDs('abc'); + const mXforms = CK.Malloc(Float32Array, ids.length * 4); + + const blob = tb.MakeFromGlyphs([5, 6, 7, 8], font); // $ExpectType SkTextBlob + const blob1 = tb.MakeFromGlyphs(ids, font); // $ExpectType SkTextBlob + const blob2 = tb.MakeFromRSXform('cdf', mXforms, font); // $ExpectType SkTextBlob + const blob3 = tb.MakeFromRSXform('c', [-1, 0, 2, 3], font); // $ExpectType SkTextBlob + const blob4 = tb.MakeFromRSXformGlyphs([3, 6], mXforms, font); // $ExpectType SkTextBlob + const blob5 = tb.MakeFromRSXformGlyphs(ids, [-1, 0, 2, 3], font); // $ExpectType SkTextBlob + const blob6 = tb.MakeFromText('xyz', font); // $ExpectType SkTextBlob + const blob7 = tb.MakeOnPath('tuv', path, font); // $ExpectType SkTextBlob + const blob8 = tb.MakeOnPath('tuv', path, font, 10); // $ExpectType SkTextBlob } diff --git a/modules/canvaskit/canvaskit/types/index.d.ts b/modules/canvaskit/canvaskit/types/index.d.ts index fc9e6bc1b1..ce3087fbd5 100644 --- a/modules/canvaskit/canvaskit/types/index.d.ts +++ b/modules/canvaskit/canvaskit/types/index.d.ts @@ -134,6 +134,13 @@ export interface CanvasKit { */ Malloc(typedArray: TypedArrayConstructor, len: number): MallocObj; + /** + * As Malloc but for GlyphIDs. This helper exists to make sure the JS side and the C++ side + * stay in agreement with how wide GlyphIDs are. + * @param len - number of GlyphIDs to make space for. + */ + MallocGlyphIDs(len: number): MallocObj; + /** * Free frees the memory returned by Malloc. * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free. @@ -205,14 +212,18 @@ export interface CanvasKit { getSkDataBytes(data: SkData): Uint8Array; // Constructors, i.e. things made with `new CanvasKit.Foo()`; + readonly ShapedText: ShapedTextConstructor; + readonly SkFont: SkFontConstructor; readonly SkPaint: DefaultConstructor; readonly SkPath: SkPathConstructorAndFactory; // Factories, i.e. things made with CanvasKit.Foo.MakeTurboEncapsulator() readonly SkColorFilter: SkColorFilterFactory; + readonly SkFontMgr: SkFontMgrFactory; readonly SkImageFilter: SkImageFilterFactory; readonly SkMaskFilter: SkMaskFilterFactory; readonly SkPathEffect: SkPathEffectFactory; + readonly SkTextBlob: SkTextBlobFactory; // Misc readonly SkColorMatrix: ColorMatrixHelpers; @@ -225,15 +236,17 @@ export interface CanvasKit { readonly BlurStyle: BlurStyleEnumValues; readonly ClipOp: ClipOpEnumValues; readonly ColorType: ColorTypeEnumValues; - readonly ImageFormat: ImageFormatEnumValues; readonly FillType: FillTypeEnumValues; readonly FilterQuality: FilterQualityEnumValues; + readonly FontEdging: FontEdgingEnumValues; + readonly FontHinting: FontHintingEnumValues; + readonly ImageFormat: ImageFormatEnumValues; readonly PaintStyle: PaintStyleEnumValues; readonly PathOp: PathOpEnumValues; readonly PointMode: PointModeEnumValues; + readonly SkColorSpace: ColorSpaceEnumValues; readonly StrokeCap: StrokeCapEnumValues; readonly StrokeJoin: StrokeJoinEnumValues; - readonly SkColorSpace: ColorSpaceEnumValues; readonly TileMode: TileModeEnumValues; // Constants @@ -363,6 +376,13 @@ export interface ShapedText extends EmbindObject { getBounds(outputArray?: SkRect): SkRect; } +export interface ShapedTextOpts { + text: string; + font: SkFont; + leftToRight: boolean; + width: number; +} + /** * See SkAnimatedImage.h for more information on this class. */ @@ -793,7 +813,7 @@ export interface SkCanvas extends EmbindObject { } /** - * See SkColorFilter.h for more on this class. The objects returned are opaque. + * See SkColorFilter.h for more on this class. The objects are opaque. */ export type SkColorFilter = EmbindObject; @@ -811,7 +831,153 @@ export interface SkData extends EmbindObject { * See SkFont.h for more on this class. */ export interface SkFont extends EmbindObject { - todo: number; // TODO(kjlubick) + /** + * Retrieves the bounds for each glyph in glyphs. + * If paint is not null, its stroking, SkPathEffect, and SkMaskFilter fields are respected. + * These are returned as flattened rectangles. For each glyph, there will be 4 floats for + * left, top, right, bottom (relative to 0, 0) for that glyph. + * @param glyphs + * @param paint + * @param output - if provided, the results will be copied into this array. + */ + getGlyphBounds(glyphs: InputGlyphIDArray, paint?: SkPaint | null, + output?: Float32Array): Float32Array; + + /** + * Retrieves the glyph ids for each code point in the provided string. Note that glyph IDs + * are font-dependent; different fonts may have different ids for the same code point. + * @param str + * @param numCodePoints - the number of code points in the string. Defaults to str.length. + * @param output - if provided, the results will be copied into this array. + */ + getGlyphIDs(str: string, numCodePoints?: number, + output?: TypedArray): GlyphIDArray; + + /** + * Retrieves the advanceX measurements for each glyph. + * If paint is not null, its stroking, SkPathEffect, and SkMaskFilter fields are respected. + * One width per glyph is returned in the returned array. + * @param glyphs + * @param paint + * @param output - if provided, the results will be copied into this array. + */ + getGlyphWidths(glyphs: InputGlyphIDArray, paint?: SkPaint | null, + output?: Float32Array): Float32Array; + + /** + * Returns text scale on x-axis. Default value is 1. + */ + getScaleX(): number; + + /** + * Returns text size in points. + */ + getSize(): number; + + /** + * Returns text skew on x-axis. Default value is zero. + */ + getSkewX(): number; + + /** + * Returns the SkTypeface set for this font. + */ + getTypeface(): SkTypeface | null; + + /** + * Retrieves the advanceX measurements for each code point in str. + * [deprecated] Use getGlyphIDs and getGlyphWidths instead. + * @param str + */ + getWidths(str: string): number[]; + + /** + * Retrieves the total advance with the given string. + * If attempting to shape text to fit into a given width, using getGlyphIDs and getGlyphWidths + * is probably easier / more efficient. + * @param str + */ + measureText(str: string): number; + + /** + * Requests, but does not require, that edge pixels draw opaque or with partial transparency. + * @param edging + */ + setEdging(edging: FontEdging): void; + + /** + * Requests, but does not require, to use bitmaps in fonts instead of outlines. + * @param embeddedBitmaps + */ + setEmbeddedBitmaps(embeddedBitmaps: boolean): void; + + /** + * Sets level of glyph outline adjustment. + * @param hinting + */ + setHinting(hinting: FontHinting): void; + + /** + * Requests, but does not require, linearly scalable font and glyph metrics. + * + * For outline fonts 'true' means font and glyph metrics should ignore hinting and rounding. + * Note that some bitmap formats may not be able to scale linearly and will ignore this flag. + * @param linearMetrics + */ + setLinearMetrics(linearMetrics: boolean): void; + + /** + * Sets the text scale on the x-axis. + * @param sx + */ + setScaleX(sx: number): void; + + /** + * Sets the text size in points on this font. + * @param points + */ + setSize(points: number): void; + + /** + * Sets the text-skew on the x axis for this font. + * @param sx + */ + setSkewX(sx: number): void; + + /** + * Requests, but does not require, that glyphs respect sub-pixel positioning. + * @param subpixel + */ + setSubpixel(subpixel: boolean): void; + + /** + * Sets the typeface to use with this font. null means to clear the typeface and use the + * default one. + * @param face + */ + setTypeface(face: SkTypeface | null): void; +} + +/** + * See SkFontMgr.h for more details + */ +export interface SkFontMgr extends EmbindObject { + /** + * Return the number of font families loaded in this manager. Useful for debugging. + */ + countFamilies(): number; + + /** + * Return the nth family name. Useful for debugging. + * @param index + */ + getFamilyName(index: number): string; + + /** + * Create a typeface for the specified bytes and return it. + * @param fontData + */ + makeTypefaceFromData(fontData: ArrayBuffer): SkTypeface; } /** @@ -862,7 +1028,7 @@ export interface SkImage extends EmbindObject { } /** - * See SkImageFilter.h for more on this class. The objects returned are opaque. + * See SkImageFilter.h for more on this class. The objects are opaque. */ export type SkImageFilter = EmbindObject; @@ -875,7 +1041,7 @@ export interface SkImageInfo { } /** - * See SkMaskFilter.h for more on this class. The objects returned are opaque. + * See SkMaskFilter.h for more on this class. The objects are opaque. */ export type SkMaskFilter = EmbindObject; @@ -1479,7 +1645,7 @@ export interface SkPath extends EmbindObject { } /** - * See SkPathEffect.h for more on this class. The objects returned are opaque. + * See SkPathEffect.h for more on this class. The objects are opaque. */ export type SkPathEffect = EmbindObject; @@ -1568,11 +1734,14 @@ export interface SkSurface extends EmbindObject { } /** - * See SkTextBlob.h for more on this class. + * See SkTextBlob.h for more on this class. The objects are opaque. */ -export interface SkTextBlob extends EmbindObject { - todo: number; // TODO(kjlubick) -} +export type SkTextBlob = EmbindObject; + +/** + * See SkTypeface.h for more on this class. The objects are opaque. + */ +export type SkTypeface = EmbindObject; /** * See SkVertices.h for more on this class. @@ -1824,6 +1993,17 @@ export interface Matrix4x4Helpers { transpose(matrix: Matrix4x4 | number[]): number[]; } +/** + * This class is an abstraction around SkShaper.h + */ +export interface ShapedTextConstructor { + /** + * Return a ShapedText from the given options. See SkShaper.h for more. + * @param opts + */ + new (opts: ShapedTextOpts): ShapedText; +} + /** * See SkColorFilter.h for more. */ @@ -1867,6 +2047,43 @@ export interface SkColorFilterFactory { MakeSRGBToLinearGamma(): SkColorFilter; } +/** + * See SkFont.h for more. + */ +export interface SkFontConstructor extends DefaultConstructor { + /** + * Constructs SkFont with default values with SkTypeface. + * @param face + * @param size - font size in points. If not specified, uses a default value. + */ + new (face: SkTypeface | null, size?: number): SkFont; + + /** + * Constructs SkFont with default values with SkTypeface and size in points, + * horizontal scale, and horizontal skew. Horizontal scale emulates condensed + * and expanded fonts. Horizontal skew emulates oblique fonts. + * @param face + * @param size + * @param scaleX + * @param skewX + */ + new (face: SkTypeface | null, size: number, scaleX: number, skewX: number): SkFont; +} + +export interface SkFontMgrFactory { + /** + * Create an SkFontMgr with the created font data. Returns null if buffers was empty. + * @param buffers + */ + FromData(...buffers: ArrayBuffer[]): SkFontMgr | null; + + /** + * Return the default SkFontMgr. This will generally have 0 or 1 fonts in it, depending on if + * the demo monospace font was compiled in. + */ + RefDefault(): SkFontMgr; +} + /** * See effects/SkImageFilters.h for more. */ @@ -1979,6 +2196,65 @@ export interface SkPathEffectFactory { MakeDiscrete(segLength: number, dev: number, seedAssist: number): SkPathEffect; } +/** + * See SkTextBlob.h for more details. + */ +export interface SkTextBlobFactory { + /** + * Return a TextBlob with a single run of text. + * + * It does not perform typeface fallback for characters not found in the SkTypeface. + * It does not perform kerning or other complex shaping; glyphs are positioned based on their + * default advances. + * @param glyphs - if using Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs(). + * @param font + */ + MakeFromGlyphs(glyphs: InputGlyphIDArray, font: SkFont): SkTextBlob; + + /** + * Returns a TextBlob built from a single run of text with rotation, scale, and translations. + * + * It uses the default character-to-glyph mapping from the typeface in the font. + * @param str + * @param rsxforms + * @param font + */ + MakeFromRSXform(str: string, rsxforms: FlattenedRSXFormArray, font: SkFont): SkTextBlob; + + /** + * Returns a TextBlob built from a single run of text with rotation, scale, and translations. + * + * @param glyphs - if using Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs(). + * @param rsxforms + * @param font + */ + MakeFromRSXformGlyphs(glyphs: InputGlyphIDArray, rsxforms: FlattenedRSXFormArray, + font: SkFont): SkTextBlob; + + /** + * Return a TextBlob with a single run of text. + * + * It uses the default character-to-glyph mapping from the typeface in the font. + * It does not perform typeface fallback for characters not found in the SkTypeface. + * It does not perform kerning or other complex shaping; glyphs are positioned based on their + * default advances. + * @param str + * @param font + */ + MakeFromText(str: string, font: SkFont): SkTextBlob; + + /** + * Returns a TextBlob that has the glyphs following the contours of the given path. + * + * It is a convenience wrapper around MakeFromRSXform and SkPathMeasure. + * @param str + * @param path + * @param font + * @param initialOffset - the length in pixels to start along the path. + */ + MakeOnPath(str: string, path: SkPath, font: SkFont, initialOffset?: number): SkTextBlob; +} + /** * An SkColor is represented by 4 floats, typically with values between 0 and 1.0. In order, * the floats correspond to red, green, blue, alpha. @@ -2043,6 +2319,11 @@ export type FlattenedRectangleArray = MallocObj | Float32Array | number[]; * be scos, ssin, tx, ty for each RSXForm. See RSXForm.h for more details. */ export type FlattenedRSXFormArray = MallocObj | Float32Array | number[]; +/** + * Regardless of the format we use internally for GlyphID (16 bit unsigned atm), we expose them + * as 32 bit unsigned. + */ +export type GlyphIDArray = Uint32Array; /** * PathCommand contains a verb and then any arguments needed to fulfill that path verb. * Examples: @@ -2078,6 +2359,11 @@ export type InputColor = MallocObj | SkColor | number[]; * Length 20. */ export type InputColorMatrix = MallocObj | SkColorMatrix | number[]; +/** + * CanvasKit APIs accept normal arrays, typed arrays, or Malloc'd memory as glyph IDs. + * Length n for n glyph IDs. + */ +export type InputGlyphIDArray = MallocObj | GlyphIDArray | number[]; /** * CanvasKit APIs accept all of these matrix types. Under the hood, we generally use 4x4 matrices. */ @@ -2107,6 +2393,8 @@ export type ColorType = EmbindEnumEntity; export type EncodedImageFormat = EmbindEnumEntity; export type FillType = EmbindEnumEntity; export type FilterQuality = EmbindEnumEntity; +export type FontEdging = EmbindEnumEntity; +export type FontHinting = EmbindEnumEntity; export type PathOp = EmbindEnumEntity; export type PaintStyle = EmbindEnumEntity; export type PointMode = EmbindEnumEntity; @@ -2205,6 +2493,19 @@ export interface FilterQualityEnumValues extends EmbindEnum { High: FilterQuality; } +export interface FontEdgingEnumValues extends EmbindEnum { + Alias: FontEdging; + AntiAlias: FontEdging; + SubpixelAntiAlias: FontEdging; +} + +export interface FontHintingEnumValues extends EmbindEnum { + None: FontHinting; + Slight: FontHinting; + Normal: FontHinting; + Full: FontHinting; +} + export interface PaintStyleEnumValues extends EmbindEnum { Fill: PaintStyle; Stroke: PaintStyle; diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp index e01f961302..708c9bd888 100644 --- a/modules/canvaskit/canvaskit_bindings.cpp +++ b/modules/canvaskit/canvaskit_bindings.cpp @@ -629,6 +629,7 @@ static sk_sp do_shaping(const ShapedTextOpts& opts, SkPoint* pt) { return builder.makeBlob(); } +// TODO(kjlubick) ShapedText is a very thin veneer around SkTextBlob - can probably remove it. class ShapedText { public: ShapedText(ShapedTextOpts opts) : fOpts(opts) {} diff --git a/modules/canvaskit/font.js b/modules/canvaskit/font.js index 0500b9d736..d2e7159275 100644 --- a/modules/canvaskit/font.js +++ b/modules/canvaskit/font.js @@ -93,7 +93,8 @@ CanvasKit._extraInitializations.push(function() { return rv; }; - // Returns an array of the widths of the glyphs in this string. + // Returns an array of the widths of the glyphs in this string. + // TODO(kjlubick) Remove this API - getGlyphWidths is the better API. CanvasKit.SkFont.prototype.getWidths = function(str) { // add 1 for null terminator var codePoints = str.length + 1;