Expose proposed Shaper API to JS
Requires: https://skia-review.googlesource.com/c/skia/+/392837
Change-Id: I3b779b699fbcade7702049a83b98db8dfe86433d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/397436
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/modules/canvaskit/WasmCommon.h b/modules/canvaskit/WasmCommon.h
index bcc6705..9bc7a1b 100644
--- a/modules/canvaskit/WasmCommon.h
+++ b/modules/canvaskit/WasmCommon.h
@@ -19,7 +19,26 @@
using JSObject = emscripten::val;
using JSString = emscripten::val;
using SkPathOrNull = emscripten::val;
+using TypedArray = emscripten::val;
using Uint8Array = emscripten::val;
+using Uint16Array = emscripten::val;
+using Uint32Array = emscripten::val;
using Float32Array = emscripten::val;
+/**
+ * Create a typed-array (in the JS heap) and initialize it with the provided
+ * data (from the wasm heap). The caller is responsible for matching the type of data
+ * with the specified arrayType.
+ *
+ * TODO: can we specialize this on T and provide the correct string?
+ * e.g. T==uint8_t --> "Uint8Array"
+ */
+template <typename T>
+TypedArray MakeTypedArray(int count, const T src[], const char arrayType[]) {
+ emscripten::val length = emscripten::val(count);
+ emscripten::val jarray = emscripten::val::global(arrayType).new_(count);
+ jarray.call<void>("set", val(typed_memory_view(count, src)));
+ return jarray;
+}
+
#endif
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index 3d9436e..fc021de 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -134,6 +134,7 @@
getMaxWidth: function() {},
getMinIntrinsicWidth: function() {},
getWordBoundary: function() {},
+ getShapedRuns: function() {},
layout: function() {},
// private API
diff --git a/modules/canvaskit/npm_build/extra.html b/modules/canvaskit/npm_build/extra.html
index 769eb71..6a2bb69 100644
--- a/modules/canvaskit/npm_build/extra.html
+++ b/modules/canvaskit/npm_build/extra.html
@@ -246,7 +246,8 @@
let X = 100;
let Y = 100;
- const font = new CanvasKit.Font(null, 18);
+ const tf = fontMgr.MakeTypefaceFromData(fontData);
+ const font = new CanvasKit.Font(tf, 50);
const fontPaint = new CanvasKit.Paint();
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
fontPaint.setAntiAlias(true);
@@ -259,8 +260,15 @@
canvas.drawLine(wrapTo, 0, wrapTo, 400, fontPaint);
- let posA = paragraph.getGlyphPositionAtCoordinate(X, Y);
- canvas.drawText(`At (${X.toFixed(2)}, ${Y.toFixed(2)}) glyph is ${posA.pos}`, 5, 450, fontPaint, font);
+ {
+ let runs = paragraph.getShapedRuns();
+
+ fontPaint.setColor(CanvasKit.RED);
+ for (let r of runs) {
+ canvas.drawGlyphs(r.glyphs, r.positions, r.origin_x, r.origin_y, font, fontPaint);
+ }
+ fontPaint.setColor(CanvasKit.BLACK);
+ }
surface.requestAnimationFrame(drawFrame);
}
diff --git a/modules/canvaskit/npm_build/types/index.d.ts b/modules/canvaskit/npm_build/types/index.d.ts
index 7da8a92..397c93a 100644
--- a/modules/canvaskit/npm_build/types/index.d.ts
+++ b/modules/canvaskit/npm_build/types/index.d.ts
@@ -648,6 +648,14 @@
lineNumber: number;
}
+export interface GlyphRun {
+ glyphs: Uint16Array;
+ positions: Float32Array; // alternating x0, y0, x1, y1, ...
+ offsets: Uint32Array;
+ origin_x: number;
+ origin_y: number;
+}
+
/**
* This object is a wrapper around a pointer to some memory on the WASM heap. The type of the
* pointer was determined at creation time.
@@ -805,6 +813,8 @@
*/
getWordBoundary(offset: number): URange;
+ getShapedRuns(): GlyphRun[];
+
/**
* Lays out the text in the paragraph so it is wrapped to the given width.
* @param width
diff --git a/modules/canvaskit/paragraph_bindings.cpp b/modules/canvaskit/paragraph_bindings.cpp
index 1d5f739..1051421 100644
--- a/modules/canvaskit/paragraph_bindings.cpp
+++ b/modules/canvaskit/paragraph_bindings.cpp
@@ -304,6 +304,55 @@
return result;
}
+/*
+ * Returns Runs[K]
+ *
+ * Run --> { font: ???, glyphs[N], positions[N*2], offsets[N], origin: x,y }
+ *
+ * K = number of runs
+ * N = number of glyphs in a given run
+ */
+JSArray GetShapedRuns(para::Paragraph& self) {
+ struct Run {
+ SkFont font;
+ SkPoint origin;
+ int index;
+ int count;
+ };
+ std::vector<Run> runs;
+ std::vector<uint16_t> glyphs;
+ std::vector<SkPoint> positions;
+ std::vector<uint32_t> offsets;
+
+ self.visit([&](const para::Paragraph::VisitorInfo& info) {
+ // add 1 Run
+ runs.push_back({info.font, info.origin, (int)glyphs.size(), info.count});
+ // append the arrays
+ glyphs.insert(glyphs.end(), info.glyphs, info.glyphs + info.count);
+ positions.insert(positions.end(), info.positions, info.positions + info.count);
+ offsets.insert(offsets.end(), info.utf8Starts, info.utf8Starts + info.count);
+ });
+
+ JSArray jruns = emscripten::val::array();
+
+ for (const auto& crun : runs) {
+ const int N = crun.count;
+ const int I = crun.index;
+
+ JSObject jrun = emscripten::val::object();
+
+ jrun.set("glyphs" , MakeTypedArray(N, &glyphs[I], "Uint16Array"));
+ jrun.set("positions", MakeTypedArray(N*2, &positions[I].fX, "Float32Array"));
+ jrun.set("offsets" , MakeTypedArray(N, &offsets[I], "Uint32Array"));
+ jrun.set("origin_x" , crun.origin.fX);
+ jrun.set("origin_y" , crun.origin.fY);
+
+ jruns.call<void>("push", jrun);
+
+ }
+ return jruns;
+}
+
EMSCRIPTEN_BINDINGS(Paragraph) {
class_<para::Paragraph>("Paragraph")
@@ -319,6 +368,7 @@
.function("getMinIntrinsicWidth", ¶::Paragraph::getMinIntrinsicWidth)
.function("_getRectsForPlaceholders", &GetRectsForPlaceholders)
.function("_getRectsForRange", &GetRectsForRange)
+ .function("getShapedRuns", &GetShapedRuns)
.function("getWordBoundary", ¶::Paragraph::getWordBoundary)
.function("layout", ¶::Paragraph::layout);