SkShaper per-line callback
Tweak SkShaper to call out for each line, instead of bundling everything
as a text blob.
Change-Id: Ic522f88afcf31cefd873dc8b5cde1ac2e107c64f
Reviewed-on: https://skia-review.googlesource.com/c/176592
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skshaper/BUILD.gn b/modules/skshaper/BUILD.gn
index eb67f90..cb221e3 100644
--- a/modules/skshaper/BUILD.gn
+++ b/modules/skshaper/BUILD.gn
@@ -16,25 +16,24 @@
source_set("skshaper") {
if (skia_enable_skshaper) {
public_configs = [ ":public_config" ]
- public = [ "include/SkShaper.h" ]
+ public = [
+ "include/SkShaper.h",
+ ]
deps = [
"../..:skia",
]
+ sources = [
+ "src/SkShaper.cpp",
+ ]
if (target_cpu == "wasm") {
- sources = [
- "src/SkShaper_primitive.cpp",
- ]
+ sources += [ "src/SkShaper_primitive.cpp" ]
} else {
- sources = [
- "src/SkShaper_harfbuzz.cpp",
- ]
+ sources += [ "src/SkShaper_harfbuzz.cpp" ]
deps += [
"//third_party/harfbuzz",
"//third_party/icu",
]
}
configs += [ "../../:skia_private" ]
-
}
}
-
diff --git a/modules/skshaper/include/SkShaper.h b/modules/skshaper/include/SkShaper.h
index 92c236c..391e583 100644
--- a/modules/skshaper/include/SkShaper.h
+++ b/modules/skshaper/include/SkShaper.h
@@ -11,14 +11,14 @@
#include <memory>
#include "SkPoint.h"
+#include "SkTextBlob.h"
#include "SkTypeface.h"
class SkFont;
-class SkTextBlobBuilder;
/**
Shapes text using HarfBuzz and places the shaped text into a
- TextBlob.
+ client-managed buffer.
If compiled without HarfBuzz, fall back on SkPaint::textToGlyphs.
*/
@@ -27,8 +27,22 @@
SkShaper(sk_sp<SkTypeface> face);
~SkShaper();
+ class LineHandler {
+ public:
+ virtual ~LineHandler() = default;
+
+ struct Buffer {
+ SkGlyphID* glyphs; // required
+ SkPoint* positions; // required
+ char* utf8text; // optional
+ uint32_t* clusters; // optional
+ };
+
+ virtual Buffer newLineBuffer(const SkFont&, int glyphCount, int utf8textCount) = 0;
+ };
+
bool good() const;
- SkPoint shape(SkTextBlobBuilder* dest,
+ SkPoint shape(LineHandler* handler,
const SkFont& srcPaint,
const char* utf8text,
size_t textBytes,
@@ -44,4 +58,17 @@
std::unique_ptr<Impl> fImpl;
};
+/**
+ * Helper for shaping text directly into a SkTextBlob.
+ */
+class SkTextBlobBuilderLineHandler final : public SkShaper::LineHandler {
+public:
+ sk_sp<SkTextBlob> makeBlob();
+
+ SkShaper::LineHandler::Buffer newLineBuffer(const SkFont&, int, int) override;
+
+private:
+ SkTextBlobBuilder fBuilder;
+};
+
#endif // SkShaper_DEFINED
diff --git a/modules/skshaper/src/SkShaper.cpp b/modules/skshaper/src/SkShaper.cpp
new file mode 100644
index 0000000..35dafed
--- /dev/null
+++ b/modules/skshaper/src/SkShaper.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkShaper.h"
+
+#include "SkTextBlobPriv.h"
+
+SkShaper::LineHandler::Buffer SkTextBlobBuilderLineHandler::newLineBuffer(const SkFont& font,
+ int glyphCount,
+ int textCount) {
+ const auto& runBuffer = SkTextBlobBuilderPriv::AllocRunTextPos(&fBuilder, font, glyphCount,
+ textCount, SkString());
+ return { runBuffer.glyphs,
+ reinterpret_cast<SkPoint*>(runBuffer.pos),
+ runBuffer.utf8text,
+ runBuffer.clusters };
+}
+
+sk_sp<SkTextBlob> SkTextBlobBuilderLineHandler::makeBlob() {
+ return fBuilder.make();
+}
diff --git a/modules/skshaper/src/SkShaper_harfbuzz.cpp b/modules/skshaper/src/SkShaper_harfbuzz.cpp
index e5664cc..beb5923 100644
--- a/modules/skshaper/src/SkShaper_harfbuzz.cpp
+++ b/modules/skshaper/src/SkShaper_harfbuzz.cpp
@@ -22,7 +22,6 @@
#include "SkTFitsIn.h"
#include "SkTLazy.h"
#include "SkTemplates.h"
-#include "SkTextBlobPriv.h"
#include "SkTo.h"
#include "SkTypeface.h"
#include "SkTypes.h"
@@ -414,19 +413,26 @@
return (level & 1) == 0;
}
-static void append(SkTextBlobBuilder* b, const ShapedRun& run, int start, int end, SkPoint* p) {
+static void append(SkShaper::LineHandler* handler, const ShapedRun& run, int start, int end,
+ SkPoint* p) {
unsigned len = end - start;
- auto runBuffer = SkTextBlobBuilderPriv::AllocRunTextPos(b, run.fFont, len,
- run.fUtf8End - run.fUtf8Start, SkString());
- memcpy(runBuffer.utf8text, run.fUtf8Start, run.fUtf8End - run.fUtf8Start);
+
+ const auto buffer = handler->newLineBuffer(run.fFont, len, run.fUtf8End - run.fUtf8Start);
+ SkASSERT(buffer.glyphs);
+ SkASSERT(buffer.positions);
+
+ if (buffer.utf8text) {
+ memcpy(buffer.utf8text, run.fUtf8Start, run.fUtf8End - run.fUtf8Start);
+ }
for (unsigned i = 0; i < len; i++) {
// Glyphs are in logical order, but output ltr since PDF readers seem to expect that.
const ShapedGlyph& glyph = run.fGlyphs[is_LTR(run.fLevel) ? start + i : end - 1 - i];
- runBuffer.glyphs[i] = glyph.fID;
- runBuffer.clusters[i] = glyph.fCluster;
- reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
- SkPoint::Make(p->fX + glyph.fOffset.fX, p->fY - glyph.fOffset.fY);
+ buffer.glyphs[i] = glyph.fID;
+ buffer.positions[i] = SkPoint::Make(p->fX + glyph.fOffset.fX, p->fY - glyph.fOffset.fY);
+ if (buffer.clusters) {
+ buffer.clusters[i] = glyph.fCluster;
+ }
p->fX += glyph.fAdvance.fX;
p->fY += glyph.fAdvance.fY;
}
@@ -516,7 +522,7 @@
fImpl->fBreakIterator;
}
-SkPoint SkShaper::shape(SkTextBlobBuilder* builder,
+SkPoint SkShaper::shape(LineHandler* handler,
const SkFont& srcPaint,
const char* utf8,
size_t utf8Bytes,
@@ -524,7 +530,7 @@
SkPoint point,
SkScalar width) const {
sk_sp<SkFontMgr> fontMgr = SkFontMgr::RefDefault();
- SkASSERT(builder);
+ SkASSERT(handler);
UBiDiLevel defaultLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
//hb_script_t script = ...
@@ -761,7 +767,7 @@
int endGlyphIndex = (logicalIndex == runIndex)
? glyphIndex + 1
: runs[logicalIndex].fNumGlyphs;
- append(builder, runs[logicalIndex], startGlyphIndex, endGlyphIndex, ¤tPoint);
+ append(handler, runs[logicalIndex], startGlyphIndex, endGlyphIndex, ¤tPoint);
}
currentPoint.fY += maxDescent + maxLeading;
diff --git a/modules/skshaper/src/SkShaper_primitive.cpp b/modules/skshaper/src/SkShaper_primitive.cpp
index 7b798e7..3d54262 100644
--- a/modules/skshaper/src/SkShaper_primitive.cpp
+++ b/modules/skshaper/src/SkShaper_primitive.cpp
@@ -8,7 +8,6 @@
#include "SkShaper.h"
#include "SkStream.h"
-#include "SkTextBlob.h"
#include "SkTo.h"
#include "SkTypeface.h"
@@ -32,7 +31,7 @@
return (((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1;
}
-SkPoint SkShaper::shape(SkTextBlobBuilder* builder,
+SkPoint SkShaper::shape(LineHandler* handler,
const SkFont& srcFont,
const char* utf8text,
size_t textBytes,
@@ -40,6 +39,7 @@
SkPoint point,
SkScalar width) const {
sk_ignore_unused_variable(leftToRight);
+ sk_ignore_unused_variable(width);
SkFont font(srcFont);
font.setTypeface(fImpl->fTypeface);
@@ -47,32 +47,29 @@
if (glyphCount <= 0) {
return point;
}
- SkRect bounds;
+
SkFontMetrics metrics;
font.getMetrics(&metrics);
point.fY -= metrics.fAscent;
- (void)font.measureText(utf8text, textBytes, SkTextEncoding::kUTF8, &bounds);
- const SkTextBlobBuilder::RunBuffer& runBuffer =
- builder->allocRunTextPosH(font, glyphCount, point.y(), textBytes, SkString(), &bounds);
- memcpy(runBuffer.utf8text, utf8text, textBytes);
- const char* txtPtr = utf8text;
- for (int i = 0; i < glyphCount; ++i) {
- // Each charater maps to exactly one glyph via SkGlyphCache::unicharToGlyph().
- runBuffer.clusters[i] = SkToU32(txtPtr - utf8text);
- txtPtr += utf8_lead_byte_to_count(txtPtr);
- SkASSERT(txtPtr <= utf8text + textBytes);
- }
- (void)font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8,
- runBuffer.glyphs, glyphCount);
- // replace with getPos()?
- (void)font.getWidths(runBuffer.glyphs, glyphCount, runBuffer.pos);
- SkScalar x = point.x();
- for (int i = 0; i < glyphCount; ++i) {
- SkScalar w = runBuffer.pos[i];
- runBuffer.pos[i] = x;
- x += w;
- }
- point.fY += metrics.fDescent + metrics.fLeading;
- return point;
+ const auto buffer = handler->newLineBuffer(font, glyphCount, textBytes);
+ SkAssertResult(font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, buffer.glyphs,
+ glyphCount) == glyphCount);
+ font.getPos(buffer.glyphs, glyphCount, buffer.positions, point);
+
+ if (buffer.utf8text) {
+ memcpy(buffer.utf8text, utf8text, textBytes);
+ }
+
+ if (buffer.clusters) {
+ const char* txtPtr = utf8text;
+ for (int i = 0; i < glyphCount; ++i) {
+ // Each charater maps to exactly one glyph via SkGlyphCache::unicharToGlyph().
+ buffer.clusters[i] = SkToU32(txtPtr - utf8text);
+ txtPtr += utf8_lead_byte_to_count(txtPtr);
+ SkASSERT(txtPtr <= utf8text + textBytes);
+ }
+ }
+
+ return point + SkVector::Make(0, metrics.fDescent + metrics.fLeading);
}
diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp
index 8762391..7931dfc 100644
--- a/samplecode/SampleTextBox.cpp
+++ b/samplecode/SampleTextBox.cpp
@@ -81,12 +81,12 @@
paint.setColor(fg);
for (int i = 9; i < 24; i += 2) {
- SkTextBlobBuilder builder;
+ SkTextBlobBuilderLineHandler builder;
paint.setTextSize(SkIntToScalar(i));
SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
SkPoint end = shaper.shape(&builder, font, gText, strlen(gText), true,
{ margin, margin }, w - margin);
- canvas->drawTextBlob(builder.make(), 0, 0, paint);
+ canvas->drawTextBlob(builder.makeBlob(), 0, 0, paint);
canvas->translate(0, end.y());
}
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index 4ff6c6e..12b5279 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -1923,11 +1923,11 @@
SkShaper shaper(nullptr);
SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
- SkTextBlobBuilder builder;
+ SkTextBlobBuilderLineHandler builder;
SkPoint end = shaper.shape(&builder, font, text, strlen(text), true,
{ bounds.left(), bounds.top() }, bounds.width());
- push_ref<SkTextBlob>(L, builder.make());
+ push_ref<SkTextBlob>(L, builder.makeBlob());
SkLua(L).pushScalar(end.fY);
return 2;
}
diff --git a/tools/using_skia_and_harfbuzz.cpp b/tools/using_skia_and_harfbuzz.cpp
index 2e3f715..6f378e2 100644
--- a/tools/using_skia_and_harfbuzz.cpp
+++ b/tools/using_skia_and_harfbuzz.cpp
@@ -139,11 +139,11 @@
}
void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) {
- SkTextBlobBuilder textBlobBuilder;
+ SkTextBlobBuilderLineHandler textBlobBuilder;
SkPoint endPoint = shaper.shape(&textBlobBuilder, font, text, textBytes, true,
SkPoint{0, 0},
config->page_width.value - 2*config->left_margin.value);
- sk_sp<const SkTextBlob> blob = textBlobBuilder.make();
+ sk_sp<const SkTextBlob> blob = textBlobBuilder.makeBlob();
// If we don't have a page, or if we're not at the start of the page and the blob won't fit
if (!pageCanvas ||
(current_y > config->line_spacing_ratio.value * config->font_size.value &&