diff options
author | Florin Malita <fmalita@chromium.org> | 2018-11-01 11:16:18 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-11-04 15:19:46 +0000 |
commit | 0a8b4e1acbd6100e81f3d825ce3d087928744010 (patch) | |
tree | 66a36149b57dec6a09cf3679713985683b356934 | |
parent | 16d91aaf44faa8ca02305a4572c80d05b43f2883 (diff) | |
download | skqp-0a8b4e1acbd6100e81f3d825ce3d087928744010.tar.gz |
[skottie] Add support for explicit text line breaks
To support wacky explicit AE line breaking:
* add sksg::TextBlob (SG node backed by externally-built text blobs)
* add skottie::TextAdapter logic to handle \r line breaks and construct
the blob explicitly
Change-Id: I2eed9adf28a8c3c1f7de5bbec3d32abd7ddbd484
Reviewed-on: https://skia-review.googlesource.com/c/167384
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
-rw-r--r-- | modules/skottie/src/SkottieAdapter.cpp | 87 | ||||
-rw-r--r-- | modules/skottie/src/SkottieAdapter.h | 19 | ||||
-rw-r--r-- | modules/sksg/include/SkSGText.h | 30 | ||||
-rw-r--r-- | modules/sksg/src/SkSGText.cpp | 27 |
4 files changed, 139 insertions, 24 deletions
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp index b317c75061..1426b79c9f 100644 --- a/modules/skottie/src/SkottieAdapter.cpp +++ b/modules/skottie/src/SkottieAdapter.cpp @@ -7,6 +7,7 @@ #include "SkottieAdapter.h" +#include "SkFont.h" #include "SkMatrix.h" #include "SkPath.h" #include "SkRRect.h" @@ -19,7 +20,10 @@ #include "SkSGText.h" #include "SkSGTransform.h" #include "SkSGTrimEffect.h" +#include "SkTextBlob.h" +#include "SkTextUtils.h" #include "SkTo.h" +#include "SkUTF.h" #include "SkottieValue.h" #include <cmath> @@ -179,7 +183,7 @@ void TrimEffectAdapter::apply() { TextAdapter::TextAdapter(sk_sp<sksg::Group> root) : fRoot(std::move(root)) - , fTextNode(sksg::Text::Make(nullptr, SkString())) + , fTextNode(sksg::TextBlob::Make()) , fFillColor(sksg::Color::Make(SK_ColorTRANSPARENT)) , fStrokeColor(sksg::Color::Make(SK_ColorTRANSPARENT)) , fFillNode(sksg::Draw::Make(fTextNode, fFillColor)) @@ -198,21 +202,80 @@ TextAdapter::TextAdapter(sk_sp<sksg::Group> root) // // * where the text node is shared - fTextNode->setFlags(fTextNode->getFlags() | - SkPaint::kAntiAlias_Flag | - SkPaint::kSubpixelText_Flag); - fTextNode->setHinting(SkPaint::kNo_Hinting); - + fFillColor->setAntiAlias(true); + fStrokeColor->setAntiAlias(true); fStrokeColor->setStyle(SkPaint::kStroke_Style); } -void TextAdapter::apply() { - // Push text props to the scene graph. - fTextNode->setTypeface(fText.fTypeface); - fTextNode->setText(fText.fText); - fTextNode->setSize(fText.fTextSize); - fTextNode->setAlign(fText.fAlign); +sk_sp<SkTextBlob> TextAdapter::makeBlob() const { + // TODO: convert to SkFont (missing getFontSpacing, measureText). + SkPaint font; + font.setTypeface(fText.fTypeface); + font.setTextSize(fText.fTextSize); + font.setHinting(SkPaint::kNo_Hinting); + font.setSubpixelText(true); + font.setAntiAlias(true); + font.setTextEncoding(SkPaint::kUTF8_TextEncoding); + + 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); + + const auto line_spacing = font.getFontSpacing(); + const auto blob_font = SkFont::LEGACY_ExtractFromPaint(font); + 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.textToGlyphs(start, len, nullptr)); + SkAssertResult(font.textToGlyphs(start, len, line_glyph_buffer.data()) + == line_glyph_buffer.count()); + + const auto x_off = align_fract != 0 + ? align_fract * font.measureText(start, len) + : 0; + const auto& buf = builder.allocRun(blob_font, line_glyph_buffer.count(), x_off, y_off); + if (!buf.glyphs) { + return; + } + + memcpy(buf.glyphs, line_glyph_buffer.data(), + SkToSizeT(line_glyph_buffer.count()) * sizeof(SkGlyphID)); + + y_off += line_spacing; + } + }; + + const auto& is_line_break = [](SkUnichar uch) { + // TODO: other explicit breaks? + return uch == '\r'; + }; + const char* ptr = fText.fText.c_str(); + const char* line_start = ptr; + const char* end = ptr + fText.fText.size(); + + while (ptr < end) { + if (is_line_break(SkUTF::NextUTF8(&ptr, end))) { + push_line(line_start, ptr - 1); + line_start = ptr; + } + } + push_line(line_start, ptr); + + return builder.make(); +} + +void TextAdapter::apply() { + fTextNode->setBlob(this->makeBlob()); fFillColor->setColor(fText.fFillColor); fStrokeColor->setColor(fText.fStrokeColor); fStrokeColor->setStrokeWidth(fText.fStrokeWidth); diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h index f9a40fdadc..a162e06f81 100644 --- a/modules/skottie/src/SkottieAdapter.h +++ b/modules/skottie/src/SkottieAdapter.h @@ -24,7 +24,7 @@ class Matrix; class Path; class RadialGradient; class RRect; -class Text; +class TextBlob; class TrimEffect; }; @@ -175,16 +175,17 @@ public: private: void apply(); + sk_sp<SkTextBlob> makeBlob() const; - sk_sp<sksg::Group> fRoot; - sk_sp<sksg::Text> fTextNode; - sk_sp<sksg::Color> fFillColor, - fStrokeColor; - sk_sp<sksg::Draw> fFillNode, - fStrokeNode; + sk_sp<sksg::Group> fRoot; + sk_sp<sksg::TextBlob> fTextNode; + sk_sp<sksg::Color> fFillColor, + fStrokeColor; + sk_sp<sksg::Draw> fFillNode, + fStrokeNode; - bool fHadFill : 1, // - state cached from the prev apply() - fHadStroke : 1; // / + bool fHadFill : 1, // - state cached from the prev apply() + fHadStroke : 1; // / using INHERITED = SkRefCnt; }; diff --git a/modules/sksg/include/SkSGText.h b/modules/sksg/include/SkSGText.h index 09683e9c3b..cce9c5a3f8 100644 --- a/modules/sksg/include/SkSGText.h +++ b/modules/sksg/include/SkSGText.h @@ -13,11 +13,11 @@ #include "SkPaintDefaults.h" #include "SkPoint.h" #include "SkString.h" +#include "SkTextBlob.h" #include "SkTextUtils.h" class SkCanvas; class SkPaint; -class SkTextBlob; class SkTypeface; namespace sksg { @@ -50,7 +50,7 @@ protected: SkPath onAsPath() const override; private: - explicit Text(sk_sp<SkTypeface>, const SkString&); + Text(sk_sp<SkTypeface>, const SkString&); SkPoint alignedPosition(SkScalar advance) const; @@ -69,6 +69,32 @@ private: using INHERITED = GeometryNode; }; +/** + * Concrete Geometry node, wrapping an external SkTextBlob. + */ +class TextBlob final : public GeometryNode { +public: + static sk_sp<TextBlob> Make(sk_sp<SkTextBlob> = nullptr); + ~TextBlob() override; + + SG_ATTRIBUTE(Blob , sk_sp<SkTextBlob>, fBlob ) + SG_ATTRIBUTE(Position, SkPoint , fPosition) + +protected: + void onClip(SkCanvas*, bool antiAlias) const override; + void onDraw(SkCanvas*, const SkPaint&) const override; + + SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; + +private: + explicit TextBlob(sk_sp<SkTextBlob>); + + sk_sp<SkTextBlob> fBlob; + SkPoint fPosition = SkPoint::Make(0, 0); + + using INHERITED = GeometryNode; +}; } // namespace sksg #endif // SkSGText_DEFINED diff --git a/modules/sksg/src/SkSGText.cpp b/modules/sksg/src/SkSGText.cpp index f63fcf035d..81a909366d 100644 --- a/modules/sksg/src/SkSGText.cpp +++ b/modules/sksg/src/SkSGText.cpp @@ -11,7 +11,6 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkTArray.h" -#include "SkTextBlob.h" #include "SkTypeface.h" namespace sksg { @@ -100,4 +99,30 @@ void Text::onClip(SkCanvas* canvas, bool antiAlias) const { canvas->clipPath(this->asPath(), antiAlias); } +sk_sp<TextBlob> TextBlob::Make(sk_sp<SkTextBlob> blob) { + return sk_sp<TextBlob>(new TextBlob(std::move(blob))); +} + +TextBlob::TextBlob(sk_sp<SkTextBlob> blob) + : fBlob(std::move(blob)) {} + +TextBlob::~TextBlob() = default; + +SkRect TextBlob::onRevalidate(InvalidationController*, const SkMatrix&) { + return fBlob ? fBlob->bounds() : SkRect::MakeEmpty(); +} + +void TextBlob::onDraw(SkCanvas* canvas, const SkPaint& paint) const { + canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint); +} + +SkPath TextBlob::onAsPath() const { + // TODO + return SkPath(); +} + +void TextBlob::onClip(SkCanvas* canvas, bool antiAlias) const { + canvas->clipPath(this->asPath(), antiAlias); +} + } // namespace sksg |