Try new factory for ShapedLines
Known hacks for now: - only default typeface - only ascii support Fonts have several limitations for now. SkParagraph (our temporary backend) wants a fontmgr up front, rather than just relying on the typefaces in its styles. This makes it hard to inter-op with our JS api. Not a long-term problem, as this backend is short-term. The hacky work-around is just to pass NULL for the typeface for now. Related but different, I haven't yet figured out how to "return" a JS Typeface object. Something about bare-pointers and sk_sp. I'll figure it out eventually, but for now I just set the output slot to null as well. (there's a TODO in the .cpp) Ascii support JS strings are UTF16 (afaik). However, our current backend thinks in terms of UTF8 offsets. Plus, (at the moment) when I try to access the JS string for C++, it coerces to utf8 (std::string). We can fix both of these eventually, but for now I'll just test with ascii input characters, and the API can still work. Change-Id: I62ca71b0b45d017ac8e3881c06720776dc2d75a1 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/405400 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
2ed22faaa1
commit
c48c3e5d57
@ -146,6 +146,7 @@ var CanvasKit = {
|
||||
ParagraphBuilder: {
|
||||
Make: function() {},
|
||||
MakeFromFontProvider: function() {},
|
||||
ShapeText: function() {},
|
||||
addText: function() {},
|
||||
build: function() {},
|
||||
pop: function() {},
|
||||
@ -159,6 +160,7 @@ var CanvasKit = {
|
||||
// private API
|
||||
_Make: function() {},
|
||||
_MakeFromFontProvider: function() {},
|
||||
_ShapeText: function() {},
|
||||
_pushStyle: function() {},
|
||||
_pushPaintStyle: function() {},
|
||||
_addPlaceholder: function() {},
|
||||
|
@ -311,9 +311,9 @@
|
||||
LOC_Y = 20;
|
||||
const WIDTH = 400;
|
||||
|
||||
const tf = fontMgr.MakeTypefaceFromData(fontData);
|
||||
const font = new CanvasKit.Font(tf, TEXTSIZE);
|
||||
const fm = font.getMetrics();
|
||||
// const tf = fontMgr.MakeTypefaceFromData(fontData);
|
||||
const tf = null; // trouble with skparagraph fontmgr (for now)
|
||||
const runFont = new CanvasKit.Font(tf, TEXTSIZE);
|
||||
const fontPaint = new CanvasKit.Paint();
|
||||
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||
fontPaint.setAntiAlias(true);
|
||||
@ -321,7 +321,7 @@
|
||||
const bgPaint = new CanvasKit.Paint();
|
||||
bgPaint.setColor([0.965, 0.965, 0.965, 1]);
|
||||
|
||||
const editor = MakeEditor(text0, cursor, WIDTH, builder);
|
||||
const editor = MakeEditor(text0, tf, cursor, WIDTH, builder);
|
||||
|
||||
function drawFrame(canvas) {
|
||||
const lines = editor.getLines();
|
||||
@ -347,7 +347,11 @@
|
||||
cursor.draw_before(canvas);
|
||||
for (const l of lines) {
|
||||
for (let r of l.runs) {
|
||||
canvas.drawGlyphs(r.glyphs, r.positions, 0, 0, font, fontPaint);
|
||||
runFont.setTypeface(tf); // r.typeface is always null right now
|
||||
runFont.setSize(r.size);
|
||||
// runFont.setEmbolden(r.fakeBold); // todo: expose embolden in CK
|
||||
runFont.setSkewX(r.fakeItalic ? -0.2 : 0);
|
||||
canvas.drawGlyphs(r.glyphs, r.positions, 0, 0, runFont, fontPaint);
|
||||
}
|
||||
}
|
||||
cursor.draw_after(canvas);
|
||||
|
@ -175,9 +175,10 @@ function string_del(str, start, end) {
|
||||
return str.slice(0, start) + str.slice(end, str.length);
|
||||
}
|
||||
|
||||
function MakeEditor(text, cursor, width, builder) {
|
||||
function MakeEditor(text, typeface, cursor, width, builder) {
|
||||
const ed = {
|
||||
_text: text,
|
||||
_typeface: typeface,
|
||||
_lines: null,
|
||||
_builder: builder,
|
||||
_cursor: cursor,
|
||||
@ -232,21 +233,32 @@ function MakeEditor(text, cursor, width, builder) {
|
||||
this.setIndex(index);
|
||||
},
|
||||
_buildLines: function() {
|
||||
const builder = this._builder();
|
||||
builder.addText(this._text);
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(this._width);
|
||||
|
||||
const rec = new CanvasKit.PictureRecorder();
|
||||
const can = rec.beginRecording([0,0,9999,9999]);
|
||||
can.drawParagraph(paragraph, 0, 0);
|
||||
rec.delete();
|
||||
|
||||
this._lines = paragraph.getShapedLines();
|
||||
if (!this._lines) throw "null lines";
|
||||
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
const blocks = [];
|
||||
blocks.push({
|
||||
length: 1,
|
||||
typeface: this._typeface,
|
||||
size: 100,
|
||||
// fakeBold, fakeItalic
|
||||
});
|
||||
blocks.push({
|
||||
length: 37,
|
||||
typeface: this._typeface,
|
||||
size: 24,
|
||||
// fakeBold, fakeItalic
|
||||
});
|
||||
blocks.push({
|
||||
length: 6,
|
||||
typeface: this._typeface,
|
||||
size: 24,
|
||||
fakeItalic: true,
|
||||
});
|
||||
blocks.push({
|
||||
length: this._text.length - 1-37-6,
|
||||
typeface: this._typeface,
|
||||
size: 24,
|
||||
// fakeBold, fakeItalic
|
||||
});
|
||||
this._lines = CanvasKit.ParagraphBuilder.ShapeText(this._text, blocks, this._width);
|
||||
},
|
||||
deleteSelection: function() {
|
||||
let start = this._index.start;
|
||||
|
22
modules/canvaskit/npm_build/types/index.d.ts
vendored
22
modules/canvaskit/npm_build/types/index.d.ts
vendored
@ -666,6 +666,11 @@ export interface Range {
|
||||
* to describe the location "after" the last glyph in the glyphs array.
|
||||
*/
|
||||
export interface GlyphRun {
|
||||
typeface: Typeface; // currently set to null (temporary)
|
||||
size: number;
|
||||
fakeBold: Boolean;
|
||||
fakeItalic: Boolean;
|
||||
|
||||
glyphs: Uint16Array;
|
||||
positions: Float32Array; // alternating x0, y0, x1, y1, ...
|
||||
offsets: Uint32Array;
|
||||
@ -683,6 +688,18 @@ export interface GlyphRun {
|
||||
runs: GlyphRun[]; // array of GlyphRun objects for the line
|
||||
}
|
||||
|
||||
/**
|
||||
* Input to ShapeText(..., FontBlock[], ...);
|
||||
*/
|
||||
export interface FontBlock {
|
||||
length: number; // number of text codepoints this block is applied to
|
||||
|
||||
typeface: Typeface;
|
||||
size: number;
|
||||
fakeBold: Boolean;
|
||||
fakeItalic: Boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -3011,6 +3028,11 @@ export interface ParagraphBuilderFactory {
|
||||
* @param fontSrc
|
||||
*/
|
||||
MakeFromFontProvider(style: ParagraphStyle, fontSrc: TypefaceFontProvider): ParagraphBuilder;
|
||||
|
||||
/**
|
||||
* Return a shaped array of lines
|
||||
*/
|
||||
ShapeText(text: string, runs: FontBlock[], width?: number): ShapedLine[];
|
||||
}
|
||||
|
||||
export interface ParagraphStyleConstructor {
|
||||
|
@ -264,6 +264,17 @@
|
||||
return result;
|
||||
};
|
||||
|
||||
CanvasKit.ParagraphBuilder.ShapeText = function(text, blocks, width) {
|
||||
let length = 0;
|
||||
for (const b of blocks) {
|
||||
length += b.length;
|
||||
}
|
||||
if (length !== text.length) {
|
||||
throw "Accumulated block lengths must equal text.length";
|
||||
}
|
||||
return CanvasKit.ParagraphBuilder._ShapeText(text, blocks, width);
|
||||
};
|
||||
|
||||
CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) {
|
||||
copyArrays(textStyle);
|
||||
this._pushStyle(textStyle);
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkFontStyle.h"
|
||||
#include "include/core/SkPictureRecorder.h"
|
||||
#include "include/core/SkString.h"
|
||||
|
||||
#include "modules/skparagraph/include/DartTypes.h"
|
||||
@ -338,6 +339,7 @@ JSArray GetShapedLines(para::Paragraph& self) {
|
||||
|
||||
self.visit([&](int lineNumber, const para::Paragraph::VisitorInfo* info) {
|
||||
if (!info) {
|
||||
if (!jline) return; // how???
|
||||
// end of current line
|
||||
JSObject range = emscripten::val::object();
|
||||
range.set("first", accum.minOffset);
|
||||
@ -369,9 +371,17 @@ JSArray GetShapedLines(para::Paragraph& self) {
|
||||
|
||||
JSObject jrun = emscripten::val::object();
|
||||
|
||||
jrun.set("flags", info->flags);
|
||||
jrun.set("glyphs", MakeTypedArray(N, info->glyphs, "Uint16Array"));
|
||||
jrun.set("offsets", MakeTypedArray(N1, info->utf8Starts, "Uint32Array"));
|
||||
jrun.set("flags", info->flags);
|
||||
|
||||
// TODO: figure out how to set a wrapped sk_sp<SkTypeface>
|
||||
// jrun.set("typeface", info->font.getTypeface());
|
||||
jrun.set("typeface", emscripten::val::null());
|
||||
jrun.set("size", info->font.getSize());
|
||||
jrun.set("fakeBold", info->font.isEmbolden());
|
||||
jrun.set("fakeItalic", info->font.getSkewX() != 0);
|
||||
|
||||
jrun.set("glyphs", MakeTypedArray(N, info->glyphs, "Uint16Array"));
|
||||
jrun.set("offsets", MakeTypedArray(N1, info->utf8Starts, "Uint32Array"));
|
||||
|
||||
// we need to modify the positions, so make a temp copy
|
||||
SkAutoSTMalloc<32, SkPoint> positions(N1);
|
||||
@ -446,6 +456,72 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
static_cast<para::ParagraphBuilderImpl*>(pb.release()));
|
||||
}),
|
||||
allow_raw_pointers())
|
||||
.class_function(
|
||||
"_ShapeText",
|
||||
optional_override([](JSString jtext, JSArray jruns, float width) -> JSArray {
|
||||
std::string textStorage = jtext.as<std::string>();
|
||||
const char* text = textStorage.data();
|
||||
size_t textCount = textStorage.size();
|
||||
|
||||
auto fc = sk_make_sp<para::FontCollection>();
|
||||
fc->setDefaultFontManager(SkFontMgr::RefDefault());
|
||||
fc->enableFontFallback();
|
||||
|
||||
para::ParagraphStyle pstyle;
|
||||
{
|
||||
// For the most part this is ignored, since we set an explicit TextStyle
|
||||
// for all of our text runs, but it is required by SkParagraph.
|
||||
para::TextStyle style;
|
||||
style.setFontFamilies({SkString("sans-serif")});
|
||||
style.setFontSize(32);
|
||||
pstyle.setTextStyle(style);
|
||||
}
|
||||
|
||||
auto pb = para::ParagraphBuilder::make(pstyle, fc);
|
||||
|
||||
// tease apart the FontBlock runs
|
||||
size_t runCount = jruns["length"].as<size_t>();
|
||||
for (size_t i = 0; i < runCount; ++i) {
|
||||
emscripten::val r = jruns[i];
|
||||
|
||||
para::TextStyle style;
|
||||
style.setTypeface(r["typeface"].as< sk_sp<SkTypeface> >());
|
||||
style.setFontSize(r["size"].as<float>());
|
||||
style.setFontStyle({
|
||||
r["fakeBold"].as<bool>() ? SkFontStyle::kBold_Weight
|
||||
: SkFontStyle::kNormal_Weight,
|
||||
SkFontStyle::kNormal_Width,
|
||||
r["fakeItalic"].as<bool>() ? SkFontStyle::kItalic_Slant
|
||||
: SkFontStyle::kUpright_Slant,
|
||||
});
|
||||
|
||||
const size_t subTextCount = r["length"].as<size_t>();
|
||||
if (subTextCount > textCount) {
|
||||
return emscripten::val("block runs exceed text length!");
|
||||
}
|
||||
|
||||
pb->pushStyle(style);
|
||||
pb->addText(text, subTextCount);
|
||||
pb->pop();
|
||||
|
||||
text += subTextCount;
|
||||
textCount -= subTextCount;
|
||||
}
|
||||
if (textCount != 0) {
|
||||
return emscripten::val("Didn't have enough block runs to cover text");
|
||||
}
|
||||
|
||||
auto pa = pb->Build();
|
||||
pa->layout(width);
|
||||
|
||||
// workaround until this is fixed in SkParagraph
|
||||
{
|
||||
SkPictureRecorder rec;
|
||||
pa->paint(rec.beginRecording({0,0,9999,9999}), 0, 0);
|
||||
}
|
||||
return GetShapedLines(*pa);
|
||||
}),
|
||||
allow_raw_pointers())
|
||||
.function("addText",
|
||||
optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
|
||||
return self.addText(text.c_str(), text.length());
|
||||
|
Loading…
Reference in New Issue
Block a user