Return shapedlines for text api
Change-Id: I8f9aa6e0cffb3441706bd0efeb0886209cac012a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/403256 Reviewed-by: Julia Lavrova <jlavrova@google.com> Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
parent
f1ff004a7a
commit
a46fe7d587
@ -33,6 +33,7 @@
|
||||
<h2> Paragraph </h2>
|
||||
<canvas id=para1 width=600 height=600></canvas>
|
||||
<canvas id=para2 width=600 height=600></canvas>
|
||||
<canvas id=para3 width=600 height=600></canvas>
|
||||
|
||||
<h2> CanvasKit can serialize/deserialize .skp files</h2>
|
||||
<canvas id=skp width=500 height=500></canvas>
|
||||
@ -143,6 +144,7 @@
|
||||
Promise.all([ckLoaded, loadFont]).then((results) => {
|
||||
ParagraphAPI1(...results);
|
||||
ParagraphAPI2(...results);
|
||||
ParagraphAPI3(...results);
|
||||
GlyphGame(...results)
|
||||
});
|
||||
Promise.all([ckLoaded, loadSkp]).then((results) => {SkpExample(...results)});
|
||||
@ -329,10 +331,10 @@
|
||||
setBlinkRate: function(blinks_per_sec) {
|
||||
this._draws_per_sec = blinks_per_sec;
|
||||
},
|
||||
place: function(x, y, fontMetrics) {
|
||||
place: function(x, top, bottom) {
|
||||
this._x = x;
|
||||
this._top = y + fontMetrics.ascent;
|
||||
this._bottom = y + fontMetrics.descent;
|
||||
this._top = top;
|
||||
this._bottom = bottom;
|
||||
|
||||
this._path = null;
|
||||
},
|
||||
@ -355,31 +357,33 @@
|
||||
};
|
||||
}
|
||||
|
||||
let mouse = {
|
||||
_start_x: 0, _start_y: 0,
|
||||
_curr_x: 0, _curr_y: 0,
|
||||
_active: false,
|
||||
function MakeMouse() {
|
||||
return {
|
||||
_start_x: 0, _start_y: 0,
|
||||
_curr_x: 0, _curr_y: 0,
|
||||
_active: false,
|
||||
|
||||
isActive: function() {
|
||||
return this._active;
|
||||
},
|
||||
setDown: function(x, y) {
|
||||
this._start_x = this._curr_x = x;
|
||||
this._start_y = this._curr_y = y;
|
||||
this._active = true;
|
||||
},
|
||||
setMove: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
},
|
||||
setUp: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
this._active = false;
|
||||
},
|
||||
getPos: function() {
|
||||
return [ this._start_x, this._start_y, this._curr_x, this._curr_y ];
|
||||
},
|
||||
isActive: function() {
|
||||
return this._active;
|
||||
},
|
||||
setDown: function(x, y) {
|
||||
this._start_x = this._curr_x = x;
|
||||
this._start_y = this._curr_y = y;
|
||||
this._active = true;
|
||||
},
|
||||
setMove: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
},
|
||||
setUp: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
this._active = false;
|
||||
},
|
||||
getPos: function() {
|
||||
return [ this._start_x, this._start_y, this._curr_x, this._curr_y ];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function ParagraphAPI2(CanvasKit, fontData) {
|
||||
@ -393,6 +397,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const mouse = MakeMouse();
|
||||
const cursor = MakeCursor(CanvasKit);
|
||||
const canvas = surface.getCanvas();
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([fontData]);
|
||||
@ -517,7 +522,7 @@
|
||||
if (a == b) {
|
||||
INDEX = a;
|
||||
const p = index_to_pos(runs, INDEX);
|
||||
cursor.place(p[0], p[1], fm);
|
||||
cursor.place(p[0], p[1] + fm.ascent, p[1] + fm.descent);
|
||||
} else {
|
||||
cursor.setPath(indices_to_path(runs, a, b, fm, WIDTH));
|
||||
}
|
||||
@ -552,6 +557,211 @@
|
||||
return surface;
|
||||
}
|
||||
|
||||
function ParagraphAPI3(CanvasKit, fontData) {
|
||||
if (!CanvasKit || !fontData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const surface = CanvasKit.MakeCanvasSurface('para3');
|
||||
if (!surface) {
|
||||
console.error('Could not make surface');
|
||||
return;
|
||||
}
|
||||
|
||||
const mouse = MakeMouse();
|
||||
const cursor = MakeCursor(CanvasKit);
|
||||
const canvas = surface.getCanvas();
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([fontData]);
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.GRAY,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 40,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
});
|
||||
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
const text = "In a hole in the ground there lived a hobbit. Not a nasty, dirty, " +
|
||||
"wet hole full of worms and oozy smells. This was a hobbit-hole and " +
|
||||
"that means good food, a warm hearth, and all the comforts of home.";
|
||||
builder.addText(text);
|
||||
const paragraph = builder.build();
|
||||
const WIDTH = 600;
|
||||
paragraph.layout(WIDTH);
|
||||
|
||||
const tf = fontMgr.MakeTypefaceFromData(fontData);
|
||||
const font = new CanvasKit.Font(tf, 40);
|
||||
const fm = font.getMetrics();
|
||||
const fontPaint = new CanvasKit.Paint();
|
||||
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||
fontPaint.setAntiAlias(true);
|
||||
|
||||
function mid(a, b) { return (a + b) * 0.5; }
|
||||
|
||||
function runs_pos_to_index(runs, x, y) {
|
||||
for (const r of runs) {
|
||||
for (let i = 1; i < r.offsets.length; i += 1) {
|
||||
if (x < r.positions[i*2]) {
|
||||
if (x <= mid(r.positions[i*2-2], r.positions[i*2])) {
|
||||
return r.offsets[i-1];
|
||||
} else {
|
||||
return r.offsets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const r = runs[runs.length-1];
|
||||
return r.offsets[r.offsets.length-1];
|
||||
}
|
||||
|
||||
function lines_pos_to_index(lines, x, y) {
|
||||
if (y < lines[0].baseline_y + lines[0].metrics.ascent) {
|
||||
return 0;
|
||||
}
|
||||
for (const l of lines) {
|
||||
if (y <= l.baseline_y + l.metrics.descent) {
|
||||
return runs_pos_to_index(l.runs, x, y);
|
||||
}
|
||||
}
|
||||
return text.length;
|
||||
}
|
||||
|
||||
function runs_index_to_run(runs, index) {
|
||||
for (const r of runs) {
|
||||
if (index <= r.offsets[r.offsets.length-1]) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function runs_index_to_x(runs, index) {
|
||||
const r = runs_index_to_run(runs, index);
|
||||
for (const i in r.offsets) {
|
||||
if (index == r.offsets[i]) {
|
||||
return r.positions[i*2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lines_index_to_line(lines, index) {
|
||||
for (const l of lines) {
|
||||
if (index <= l.textRange.last) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return lines[lines.length-1];
|
||||
}
|
||||
|
||||
function lines_index_to_x(lines, index) {
|
||||
for (const l of lines) {
|
||||
if (index <= l.textRange.last) {
|
||||
return runs_index_to_x(l.runs, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function line_top(line) {
|
||||
return line.baseline_y + line.metrics.ascent;
|
||||
}
|
||||
|
||||
function line_bottom(line) {
|
||||
return line.baseline_y + line.metrics.descent;
|
||||
}
|
||||
|
||||
function lines_indices_to_path(lines, a, b, fm, width) {
|
||||
if (a == b) {
|
||||
return null;
|
||||
}
|
||||
if (a > b) {
|
||||
const tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
const path = new CanvasKit.Path();
|
||||
const la = lines_index_to_line(lines, a);
|
||||
const lb = lines_index_to_line(lines, b);
|
||||
const ax = runs_index_to_x(la.runs, a);
|
||||
const bx = runs_index_to_x(lb.runs, b);
|
||||
if (la == lb) {
|
||||
path.addRect([ax, line_top(la), bx, line_bottom(la)]);
|
||||
} else {
|
||||
const a_bot = line_bottom(la);
|
||||
const b_top = line_top(lb);
|
||||
path.addRect([ax, line_top(la), width, a_bot]);
|
||||
path.addRect([0, b_top, bx, line_bottom(lb)]);
|
||||
if (a_bot < b_top) {
|
||||
path.addRect([0, a_bot, width, b_top]); // extra lines inbetween
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
let INDEX = 0;
|
||||
let lines;
|
||||
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 0, 0);
|
||||
if (!lines) {
|
||||
lines = paragraph.getShapedLines();
|
||||
}
|
||||
|
||||
if (mouse.isActive()) {
|
||||
const pos = mouse.getPos();
|
||||
const a = lines_pos_to_index(lines, pos[0], pos[1]);
|
||||
const b = lines_pos_to_index(lines, pos[2], pos[3]);
|
||||
if (a == b) {
|
||||
INDEX = a;
|
||||
const l = lines_index_to_line(lines, INDEX);
|
||||
const x = runs_index_to_x(l.runs, INDEX);
|
||||
cursor.place(x, line_top(l), line_bottom(l));
|
||||
} else {
|
||||
cursor.setPath(lines_indices_to_path(lines, a, b, fm, WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
const bgcolors = [[1,0,0,0.05], [0,1,0,0.05]];
|
||||
let lineNo = 0;
|
||||
cursor.draw_before(canvas);
|
||||
for (const l of lines) {
|
||||
if (true) { // test line bounds
|
||||
const bounds = [0, l.baseline_y + l.metrics.ascent,
|
||||
WIDTH, l.baseline_y + l.metrics.descent];
|
||||
fontPaint.setColor(bgcolors[lineNo & 1]);
|
||||
canvas.drawRect(bounds, fontPaint);
|
||||
fontPaint.setColor([0,0,0,1]);
|
||||
}
|
||||
for (let r of l.runs) {
|
||||
canvas.drawGlyphs(r.glyphs, r.positions, 0, 0, font, fontPaint);
|
||||
}
|
||||
lineNo += 1;
|
||||
}
|
||||
cursor.draw_after(canvas);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
|
||||
function interact(e) {
|
||||
const type = e.type;
|
||||
if (type === 'pointerup') {
|
||||
mouse.setUp(e.offsetX, e.offsetY);
|
||||
} else if (type === 'pointermove') {
|
||||
mouse.setMove(e.offsetX, e.offsetY);
|
||||
} else if (type === 'pointerdown') {
|
||||
mouse.setDown(e.offsetX, e.offsetY);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('para3').addEventListener('pointermove', interact);
|
||||
document.getElementById('para3').addEventListener('pointerdown', interact);
|
||||
document.getElementById('para3').addEventListener('pointerup', interact);
|
||||
return surface;
|
||||
}
|
||||
|
||||
function RTShaderAPI1(CanvasKit) {
|
||||
if (!CanvasKit) {
|
||||
return;
|
||||
|
22
modules/canvaskit/npm_build/types/index.d.ts
vendored
22
modules/canvaskit/npm_build/types/index.d.ts
vendored
@ -650,6 +650,17 @@ export interface LineMetrics {
|
||||
lineNumber: number;
|
||||
}
|
||||
|
||||
export interface Range {
|
||||
first: number;
|
||||
last: number;
|
||||
}
|
||||
|
||||
export interface YMetrics {
|
||||
ascent: number; // distance above the baseline (negative) to top of 'normal' letters
|
||||
descent: number; // distance below the baseline (positive) to bottom of 'normal' letters
|
||||
leading: number; // extra space recommended between lines (needed?)
|
||||
}
|
||||
|
||||
/**
|
||||
* Information for a run of shaped text. See Paragraph.getShapedRuns()
|
||||
*
|
||||
@ -667,6 +678,13 @@ export interface GlyphRun {
|
||||
flags: number; // see GlyphRunFlags
|
||||
}
|
||||
|
||||
export interface ShapedLine {
|
||||
textRange: Range;
|
||||
metrics: YMetrics;
|
||||
baseline_y: number;
|
||||
runs: GlyphRun[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -824,7 +842,9 @@ export interface Paragraph extends EmbindObject<Paragraph> {
|
||||
*/
|
||||
getWordBoundary(offset: number): URange;
|
||||
|
||||
getShapedRuns(): GlyphRun[];
|
||||
getShapedRuns(): GlyphRun[]; // deprecated
|
||||
|
||||
getShapedLines(): ShapedLine[];
|
||||
|
||||
/**
|
||||
* Lays out the text in the paragraph so it is wrapped to the given width.
|
||||
|
@ -323,22 +323,25 @@ JSArray GetShapedRuns(para::Paragraph& self) {
|
||||
// where we accumulate our js output
|
||||
JSArray jruns = emscripten::val::array();
|
||||
|
||||
self.visit([&](const para::Paragraph::VisitorInfo& info) {
|
||||
const int N = info.count; // glyphs
|
||||
self.visit([&](int, const para::Paragraph::VisitorInfo* info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
const int N = info->count; // glyphs
|
||||
const int N1 = N + 1; // positions, offsets have 1 extra (trailing) slot
|
||||
|
||||
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);
|
||||
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);
|
||||
for (int i = 0; i < N; ++i) {
|
||||
positions.get()[i] = info.positions[i] + info.origin;
|
||||
positions.get()[i] = info->positions[i] + info->origin;
|
||||
}
|
||||
positions.get()[N] = { info.advanceX, positions.get()[N - 1].fY };
|
||||
positions.get()[N] = { info->advanceX, positions.get()[N - 1].fY };
|
||||
jrun.set("positions", MakeTypedArray(N1*2, (const float*)positions.get(), "Float32Array"));
|
||||
|
||||
jruns.call<void>("push", jrun);
|
||||
@ -346,6 +349,96 @@ JSArray GetShapedRuns(para::Paragraph& self) {
|
||||
return jruns;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns Lines[]
|
||||
*/
|
||||
JSArray GetShapedLines(para::Paragraph& self) {
|
||||
struct LineAccumulate {
|
||||
int lineNumber = -1; // deliberately -1 from starting value
|
||||
uint32_t minOffset = 0xFFFFFFFF;
|
||||
uint32_t maxOffset = 0;
|
||||
float minAscent = 0;
|
||||
float maxDescent = 0;
|
||||
float maxLeading = 0;
|
||||
|
||||
void reset(int lineNumber) {
|
||||
new (this) LineAccumulate;
|
||||
this->lineNumber = lineNumber;
|
||||
}
|
||||
};
|
||||
|
||||
// where we accumulate our js output
|
||||
JSArray jlines = emscripten::val::array();
|
||||
JSObject jline = emscripten::val::null();
|
||||
JSArray jruns = emscripten::val::null();
|
||||
LineAccumulate accum;
|
||||
|
||||
self.visit([&](int lineNumber, const para::Paragraph::VisitorInfo* info) {
|
||||
if (!info) {
|
||||
// end of current line
|
||||
JSObject range = emscripten::val::object();
|
||||
range.set("first", accum.minOffset);
|
||||
range.set("last", accum.maxOffset);
|
||||
jline.set("textRange", range);
|
||||
|
||||
JSObject metrics = emscripten::val::object();
|
||||
metrics.set("ascent", accum.minAscent);
|
||||
metrics.set("descent", accum.maxDescent);
|
||||
metrics.set("leading", accum.maxLeading);
|
||||
jline.set("metrics", metrics);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineNumber != accum.lineNumber) {
|
||||
SkASSERT(lineNumber == accum.lineNumber + 1); // assume monotonic
|
||||
|
||||
accum.reset(lineNumber);
|
||||
jruns = emscripten::val::array();
|
||||
|
||||
jline = emscripten::val::array();
|
||||
jline.set("baseline_y", info->origin.fY);
|
||||
jline.set("runs", jruns);
|
||||
// will assign textRange and metrics on end-of-line signal
|
||||
|
||||
jlines.call<void>("push", jline);
|
||||
}
|
||||
|
||||
// append the run
|
||||
const int N = info->count; // glyphs
|
||||
const int N1 = N + 1; // positions, offsets have 1 extra (trailing) slot
|
||||
|
||||
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"));
|
||||
|
||||
// we need to modify the positions, so make a temp copy
|
||||
SkAutoSTMalloc<32, SkPoint> positions(N1);
|
||||
for (int i = 0; i < N; ++i) {
|
||||
positions.get()[i] = info->positions[i] + info->origin;
|
||||
}
|
||||
positions.get()[N] = { info->advanceX, positions.get()[N - 1].fY };
|
||||
jrun.set("positions", MakeTypedArray(N1*2, (const float*)positions.get(), "Float32Array"));
|
||||
|
||||
jruns.call<void>("push", jrun);
|
||||
|
||||
// update accum
|
||||
{ SkFontMetrics fm;
|
||||
info->font.getMetrics(&fm);
|
||||
|
||||
accum.minAscent = std::min(accum.minAscent, fm.fAscent);
|
||||
accum.maxDescent = std::max(accum.maxDescent, fm.fDescent);
|
||||
accum.maxLeading = std::max(accum.maxLeading, fm.fLeading);
|
||||
|
||||
accum.minOffset = std::min(accum.minOffset, info->utf8Starts[0]);
|
||||
accum.maxOffset = std::max(accum.maxOffset, info->utf8Starts[N]);
|
||||
}
|
||||
|
||||
});
|
||||
return jlines;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
|
||||
class_<para::Paragraph>("Paragraph")
|
||||
@ -362,6 +455,7 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
.function("_getRectsForPlaceholders", &GetRectsForPlaceholders)
|
||||
.function("_getRectsForRange", &GetRectsForRange)
|
||||
.function("getShapedRuns", &GetShapedRuns)
|
||||
.function("getShapedLines", &GetShapedLines)
|
||||
.function("getWordBoundary", ¶::Paragraph::getWordBoundary)
|
||||
.function("layout", ¶::Paragraph::layout);
|
||||
|
||||
|
@ -100,21 +100,25 @@ protected:
|
||||
p2.setStrokeWidth(4);
|
||||
p2.setStrokeCap(SkPaint::kSquare_Cap);
|
||||
|
||||
para->visit([&](const skia::textlayout::Paragraph::VisitorInfo& info) {
|
||||
canvas->drawGlyphs(info.count, info.glyphs, info.positions, info.origin, info.font, p);
|
||||
para->visit([&](int, const skia::textlayout::Paragraph::VisitorInfo* info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
canvas->drawGlyphs(info->count, info->glyphs, info->positions, info->origin,
|
||||
info->font, p);
|
||||
|
||||
if (info.utf8Starts && false) {
|
||||
if (info->utf8Starts && false) {
|
||||
SkString str;
|
||||
for (int i = 0; i < info.count; ++i) {
|
||||
str.appendUnichar(gSpeach[info.utf8Starts[i]]);
|
||||
for (int i = 0; i < info->count; ++i) {
|
||||
str.appendUnichar(gSpeach[info->utf8Starts[i]]);
|
||||
}
|
||||
SkDebugf("'%s'\n", str.c_str());
|
||||
}
|
||||
|
||||
if (false) { // show position points
|
||||
for (int i = 0; i < info.count; ++i) {
|
||||
auto pos = info.positions[i];
|
||||
canvas->drawPoint(pos.fX + info.origin.fX, pos.fY + info.origin.fY, p2);
|
||||
for (int i = 0; i < info->count; ++i) {
|
||||
auto pos = info->positions[i];
|
||||
canvas->drawPoint(pos.fX + info->origin.fX, pos.fY + info->origin.fY, p2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -86,7 +86,9 @@ public:
|
||||
const uint32_t* utf8Starts; // count+1 values
|
||||
unsigned flags;
|
||||
};
|
||||
using Visitor = std::function<void(const VisitorInfo&)>;
|
||||
|
||||
// lineNumber begins at 0. If info is null, this signals the end of that line.
|
||||
using Visitor = std::function<void(int lineNumber, const VisitorInfo*)>;
|
||||
virtual void visit(const Visitor&) = 0;
|
||||
|
||||
protected:
|
||||
|
@ -3469,12 +3469,18 @@ protected:
|
||||
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
|
||||
paragraph->visit([&](const skia::textlayout::Paragraph::VisitorInfo& info) {
|
||||
paragraph->visit([&](int, const skia::textlayout::Paragraph::VisitorInfo* info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
SkFontMetrics metrics;
|
||||
info.font.getMetrics(&metrics);
|
||||
info->font.getMetrics(&metrics);
|
||||
|
||||
auto first = info.positions[0]; first.offset(info.origin.fX, info.origin.fY);
|
||||
SkRect rect = SkRect::MakeXYWH(first.fX, first.fY + metrics.fAscent, info.advanceX - first.fX, metrics.fDescent - metrics.fAscent);
|
||||
auto first = info->positions[0]; first.offset(info->origin.fX, info->origin.fY);
|
||||
SkRect rect = SkRect::MakeXYWH(first.fX,
|
||||
first.fY + metrics.fAscent,
|
||||
info->advanceX - first.fX,
|
||||
metrics.fDescent - metrics.fAscent);
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorLTGRAY);
|
||||
canvas->drawRect(rect, paint);
|
||||
|
@ -1051,6 +1051,7 @@ void ParagraphImpl::ensureUTF16Mapping() {
|
||||
}
|
||||
|
||||
void ParagraphImpl::visit(const Visitor& visitor) {
|
||||
int lineNumber = 0;
|
||||
for (auto& line : fLines) {
|
||||
for (auto& rec : line.fTextBlobCache) {
|
||||
SkTextBlob::Iter iter(*rec.fBlob);
|
||||
@ -1071,7 +1072,7 @@ void ParagraphImpl::visit(const Visitor& visitor) {
|
||||
clusterPtr += rec.fVisitor_Pos;
|
||||
|
||||
while (iter.experimentalNext(&run)) {
|
||||
visitor({
|
||||
const Paragraph::VisitorInfo info = {
|
||||
run.font,
|
||||
rec.fOffset,
|
||||
rec.fClipRect.fRight,
|
||||
@ -1080,10 +1081,13 @@ void ParagraphImpl::visit(const Visitor& visitor) {
|
||||
run.positions,
|
||||
clusterPtr,
|
||||
0, // flags
|
||||
});
|
||||
};
|
||||
visitor(lineNumber, &info);
|
||||
clusterPtr += run.count;
|
||||
}
|
||||
}
|
||||
visitor(lineNumber, nullptr); // signal end of line
|
||||
lineNumber += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user