[skottie] Use SkShaper for text

Update the text adapter to shape using SkShaper.

Bug: skia:
Change-Id: Ie6ea579a1f58b2f6ba6101dcdfad8ae9809de808
Reviewed-on: https://skia-review.googlesource.com/c/183387
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Florin Malita 2019-02-07 10:05:32 -05:00 committed by Skia Commit-Bot
parent 4d907dac76
commit 3fb7bbe937
2 changed files with 108 additions and 31 deletions

View File

@ -24,6 +24,7 @@ source_set("skottie") {
deps = [
"../..:skia",
"../sksg",
"../skshaper",
]
}
}

View File

@ -21,6 +21,7 @@
#include "SkSGText.h"
#include "SkSGTransform.h"
#include "SkSGTrimEffect.h"
#include "SkShaper.h"
#include "SkTextBlob.h"
#include "SkTextUtils.h"
#include "SkTo.h"
@ -296,41 +297,116 @@ sk_sp<SkTextBlob> TextAdapter::makeBlob() const {
font.setSubpixel(true);
font.setEdging(SkFont::Edging::kAntiAlias);
const auto align_fract = [](SkTextUtils::Align align) {
switch (align) {
case SkTextUtils::kLeft_Align: return 0.0f;
case SkTextUtils::kCenter_Align: return -0.5f;
case SkTextUtils::kRight_Align: return -1.0f;
}
return 0.0f; // go home, msvc...
}(fText.fAlign);
// Helper for interfacing with SkShaper: buffers shaper-fed runs and performs
// per-line position adjustments (for external line breaking, horizontal alignment, etc).
class BlobMaker final : public SkShaper::RunHandler {
public:
BlobMaker(SkTextUtils::Align align)
: fAlignFactor(AlignFactor(align)) {}
const auto line_spacing = font.getSpacing();
float y_off = 0;
SkSTArray<256, SkGlyphID, true> line_glyph_buffer;
SkTextBlobBuilder builder;
const auto& push_line = [&](const char* start, const char* end) {
if (end > start) {
const auto len = SkToSizeT(end - start);
line_glyph_buffer.reset(font.countText(start, len, kUTF8_SkTextEncoding));
SkAssertResult(font.textToGlyphs(start, len, kUTF8_SkTextEncoding, line_glyph_buffer.data(),
line_glyph_buffer.count())
== line_glyph_buffer.count());
const auto x_off = align_fract != 0
? align_fract * font.measureText(start, len, kUTF8_SkTextEncoding)
: 0;
const auto& buf = builder.allocRun(font, line_glyph_buffer.count(), x_off, y_off);
if (!buf.glyphs) {
return;
Buffer newRunBuffer(const RunInfo& info, const SkFont& font, int glyphCount,
int utf8textCount) override {
if (!fPendingLineRuns.empty() &&
fPendingLineRuns.back().fInfo.fLineIndex != info.fLineIndex) {
// SkShaper-triggered new line.
this->commitLine();
}
memcpy(buf.glyphs, line_glyph_buffer.data(),
SkToSizeT(line_glyph_buffer.count()) * sizeof(SkGlyphID));
fPendingLineAdvance += info.fAdvance;
y_off += line_spacing;
auto& run = fPendingLineRuns.emplace_back(font, info, glyphCount);
return {
run.fGlyphs .data(),
run.fPositions.data(),
nullptr,
nullptr,
};
}
void commitLine() {
SkScalar line_spacing = 0;
for (const auto& run : fPendingLineRuns) {
const auto runSize = run.size();
const auto& blobBuffer = fBuilder.allocRunPos(run.fFont, SkToInt(runSize));
sk_careful_memcpy(blobBuffer.glyphs,
run.fGlyphs.data(),
runSize * sizeof(SkGlyphID));
// For each buffered line, perform the following position adjustments:
// 1) horizontal alignment
// 2) vertical advance (based on line number/offset)
// 3) baseline/ascent adjustment
const auto offset = SkVector::Make(fAlignFactor * fPendingLineAdvance.x(),
fPendingLineVOffset + run.fInfo.fAscent);
for (size_t i = 0; i < runSize; ++i) {
blobBuffer.points()[i] = run.fPositions[SkToInt(i)] + offset;
}
line_spacing = SkTMax(line_spacing,
run.fInfo.fDescent - run.fInfo.fAscent + run.fInfo.fLeading);
}
fPendingLineRuns.reset();
fPendingLineVOffset += line_spacing;
fPendingLineAdvance = { 0, 0 };
}
sk_sp<SkTextBlob> makeBlob() {
return fBuilder.make();
}
private:
static float AlignFactor(SkTextUtils::Align align) {
switch (align) {
case SkTextUtils::kLeft_Align: return 0.0f;
case SkTextUtils::kCenter_Align: return -0.5f;
case SkTextUtils::kRight_Align: return -1.0f;
}
return 0.0f; // go home, msvc...
}
struct Run {
SkFont fFont;
SkShaper::RunHandler::RunInfo fInfo;
SkSTArray<128, SkGlyphID, true> fGlyphs;
SkSTArray<128, SkPoint , true> fPositions;
Run(const SkFont& font, const SkShaper::RunHandler::RunInfo& info, int count)
: fFont(font)
, fInfo(info)
, fGlyphs (count)
, fPositions(count) {
fGlyphs .push_back_n(count);
fPositions.push_back_n(count);
}
size_t size() const {
SkASSERT(fGlyphs.size() == fPositions.size());
return fGlyphs.size();
}
};
const float fAlignFactor;
SkTextBlobBuilder fBuilder;
SkSTArray<2, Run, false> fPendingLineRuns;
SkScalar fPendingLineVOffset = 0;
SkVector fPendingLineAdvance = { 0, 0 };
};
BlobMaker blobMaker(fText.fAlign);
const auto& push_line = [&](const char* start, const char* end) {
SkShaper shaper(font.refTypeface());
if (!shaper.good()) {
return;
}
shaper.shape(&blobMaker, font, start, SkToSizeT(end - start), true, { 0, 0 }, SK_ScalarMax);
blobMaker.commitLine();
};
const auto& is_line_break = [](SkUnichar uch) {
@ -350,7 +426,7 @@ sk_sp<SkTextBlob> TextAdapter::makeBlob() const {
}
push_line(line_start, ptr);
return builder.make();
return blobMaker.makeBlob();
}
void TextAdapter::apply() {