From 51012ce332016cca5160b5a63b91a04b6cb56899 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Wed, 31 Jan 2018 17:06:59 -0500 Subject: [sksg] Initial text support Use the new node type for SkottieSlide2 labels. TBR= Change-Id: Icd6a4faf1c281bd83a2331c0072d1a6ed71acc09 Reviewed-on: https://skia-review.googlesource.com/102441 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- BUILD.gn | 1 + experimental/sksg/geometry/SkSGGeometryTransform.h | 2 +- experimental/sksg/geometry/SkSGMerge.h | 2 + experimental/sksg/geometry/SkSGPath.h | 2 + experimental/sksg/geometry/SkSGRect.h | 4 + experimental/sksg/geometry/SkSGText.cpp | 77 +++++++++++++++++++ experimental/sksg/geometry/SkSGText.h | 69 +++++++++++++++++ experimental/sksg/geometry/SkSGTrimEffect.h | 2 + tools/viewer/SkottieSlide.h | 2 +- tools/viewer/SkottieSlide2.cpp | 89 ++++++++++++---------- 10 files changed, 209 insertions(+), 41 deletions(-) create mode 100644 experimental/sksg/geometry/SkSGText.cpp create mode 100644 experimental/sksg/geometry/SkSGText.h diff --git a/BUILD.gn b/BUILD.gn index 18d16ded01..f48c62e2a6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1401,6 +1401,7 @@ if (skia_enable_tools) { "experimental/sksg/geometry/SkSGMerge.cpp", "experimental/sksg/geometry/SkSGPath.cpp", "experimental/sksg/geometry/SkSGRect.cpp", + "experimental/sksg/geometry/SkSGText.cpp", "experimental/sksg/geometry/SkSGTrimEffect.cpp", "experimental/sksg/paint/SkSGColor.cpp", "experimental/sksg/paint/SkSGGradient.cpp", diff --git a/experimental/sksg/geometry/SkSGGeometryTransform.h b/experimental/sksg/geometry/SkSGGeometryTransform.h index ea990c6f29..fe7e026031 100644 --- a/experimental/sksg/geometry/SkSGGeometryTransform.h +++ b/experimental/sksg/geometry/SkSGGeometryTransform.h @@ -50,7 +50,7 @@ private: const sk_sp fMatrix; SkPath fTransformed; - typedef GeometryNode INHERITED; + using INHERITED = GeometryNode; }; } diff --git a/experimental/sksg/geometry/SkSGMerge.h b/experimental/sksg/geometry/SkSGMerge.h index 69f824e011..54924d6475 100644 --- a/experimental/sksg/geometry/SkSGMerge.h +++ b/experimental/sksg/geometry/SkSGMerge.h @@ -55,6 +55,8 @@ private: std::vector> fGeos; SkPath fMerged; Mode fMode; + + using INHERITED = GeometryNode; }; } // namespace sksg diff --git a/experimental/sksg/geometry/SkSGPath.h b/experimental/sksg/geometry/SkSGPath.h index 6dd259394b..a398e2f0a4 100644 --- a/experimental/sksg/geometry/SkSGPath.h +++ b/experimental/sksg/geometry/SkSGPath.h @@ -38,6 +38,8 @@ private: explicit Path(const SkPath&); SkPath fPath; + + using INHERITED = GeometryNode; }; } // namespace sksg diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h index a99c76ab98..f5fcb962c6 100644 --- a/experimental/sksg/geometry/SkSGRect.h +++ b/experimental/sksg/geometry/SkSGRect.h @@ -42,6 +42,8 @@ private: explicit Rect(const SkRect&); SkRect fRect; + + using INHERITED = GeometryNode; }; /** @@ -65,6 +67,8 @@ private: explicit RRect(const SkRRect&); SkRRect fRRect; + + using INHERITED = GeometryNode; }; } // namespace sksg diff --git a/experimental/sksg/geometry/SkSGText.cpp b/experimental/sksg/geometry/SkSGText.cpp new file mode 100644 index 0000000000..c149390023 --- /dev/null +++ b/experimental/sksg/geometry/SkSGText.cpp @@ -0,0 +1,77 @@ +/* + * 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 "SkSGText.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkTArray.h" +#include "SkTextBlob.h" +#include "SkTypeface.h" + +namespace sksg { + +sk_sp Text::Make(sk_sp tf, const SkString& text) { + return sk_sp(new Text(std::move(tf), text)); +} + +Text::Text(sk_sp tf, const SkString& text) + : fTypeface(std::move(tf)) + , fText(text) {} + +Text::~Text() = default; + +SkRect Text::onRevalidate(InvalidationController*, const SkMatrix&) { + // TODO: we could potentially track invals which don't require rebuilding the blob. + + SkPaint font; + font.setFlags(fFlags); + font.setTypeface(fTypeface); + font.setTextSize(fSize); + font.setTextScaleX(fScaleX); + font.setTextSkewX(fSkewX); + font.setTextAlign(fAlign); + font.setHinting(fHinting); + + // First, convert to glyphIDs. + font.setTextEncoding(SkPaint::kUTF8_TextEncoding); + SkSTArray<256, SkGlyphID, true> glyphs; + glyphs.reset(font.textToGlyphs(fText.c_str(), fText.size(), nullptr)); + SkAssertResult(font.textToGlyphs(fText.c_str(), fText.size(), glyphs.begin()) == glyphs.count()); + font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + // Next, build the cached blob. + SkTextBlobBuilder builder; + const auto& buf = builder.allocRun(font, glyphs.count(), 0, 0, nullptr); + if (!buf.glyphs) { + fBlob.reset(); + return SkRect::MakeEmpty(); + } + + memcpy(buf.glyphs, glyphs.begin(), glyphs.count() * sizeof(SkGlyphID)); + + fBlob = builder.make(); + return fBlob + ? fBlob->bounds().makeOffset(fPosition.x(), fPosition.y()) + : SkRect::MakeEmpty(); +} + +void Text::onDraw(SkCanvas* canvas, const SkPaint& paint) const { + canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint); +} + +SkPath Text::onAsPath() const { + // TODO + return SkPath(); +} + +void Text::onClip(SkCanvas* canvas, bool antiAlias) const { + canvas->clipPath(this->asPath(), antiAlias); +} + +} // namespace sksg diff --git a/experimental/sksg/geometry/SkSGText.h b/experimental/sksg/geometry/SkSGText.h new file mode 100644 index 0000000000..eb43337a10 --- /dev/null +++ b/experimental/sksg/geometry/SkSGText.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSGText_DEFINED +#define SkSGText_DEFINED + +#include "SkSGGeometryNode.h" + +#include "SkPaintDefaults.h" +#include "SkPoint.h" +#include "SkString.h" + +class SkCanvas; +class SkPaint; +class SkTextBlob; +class SkTypeface; + +namespace sksg { + +/** + * Concrete Geometry node, wrapping a (shaped) SkTextBlob. + */ +class Text final : public GeometryNode { +public: + static sk_sp Make(sk_sp tf, const SkString& text); + ~Text() override; + + SG_ATTRIBUTE(Text , SkString , fText ) + SG_ATTRIBUTE(Flags , uint32_t , fFlags ) + SG_ATTRIBUTE(Position, SkPoint , fPosition) + SG_ATTRIBUTE(Size , SkScalar , fSize ) + SG_ATTRIBUTE(ScaleX , SkScalar , fScaleX ) + SG_ATTRIBUTE(SkewX , SkScalar , fSkewX ) + SG_ATTRIBUTE(Align , SkPaint::Align, fAlign ) + + // TODO: add shaping functionality. + +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 Text(sk_sp, const SkString&); + + const sk_sp fTypeface; + SkString fText; + uint32_t fFlags = SkPaintDefaults_Flags; + SkPoint fPosition = SkPoint::Make(0, 0); + SkScalar fSize = SkPaintDefaults_TextSize; + SkScalar fScaleX = 1; + SkScalar fSkewX = 0; + SkPaint::Align fAlign = SkPaint::kLeft_Align; + SkPaint::Hinting fHinting = SkPaintDefaults_Hinting; + + sk_sp fBlob; // cached text blob + + using INHERITED = GeometryNode; +}; + +} // namespace sksg + +#endif // SkSGText_DEFINED diff --git a/experimental/sksg/geometry/SkSGTrimEffect.h b/experimental/sksg/geometry/SkSGTrimEffect.h index 860cfafc25..e86ede83e6 100644 --- a/experimental/sksg/geometry/SkSGTrimEffect.h +++ b/experimental/sksg/geometry/SkSGTrimEffect.h @@ -47,6 +47,8 @@ private: SkScalar fStart = 0, // starting t fEnd = 1, // ending t fOffset = 0; // t offset + + using INHERITED = GeometryNode; }; } // namespace sksg diff --git a/tools/viewer/SkottieSlide.h b/tools/viewer/SkottieSlide.h index f573c2ed09..44929d7be5 100644 --- a/tools/viewer/SkottieSlide.h +++ b/tools/viewer/SkottieSlide.h @@ -50,13 +50,13 @@ public: void draw(SkCanvas*) override; bool animate(const SkAnimTimer&) override; bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState, uint32_t modifiers) override; + private: class AnimationWrapper; struct Rec { sk_sp fWrapper; SkMSec fTimeBase = 0; - SkString fName; bool fShowAnimationInval = false; explicit Rec(sk_sp); diff --git a/tools/viewer/SkottieSlide2.cpp b/tools/viewer/SkottieSlide2.cpp index 7a9854031d..7b2c7c25a1 100644 --- a/tools/viewer/SkottieSlide2.cpp +++ b/tools/viewer/SkottieSlide2.cpp @@ -12,11 +12,17 @@ #include "Skottie.h" #include "SkOSFile.h" #include "SkOSPath.h" +#include "SkSGColor.h" +#include "SkSGDraw.h" #include "SkSGGroup.h" #include "SkSGRenderNode.h" #include "SkSGScene.h" +#include "SkSGText.h" #include "SkSGTransform.h" #include "SkStream.h" +#include "SkTypeface.h" + +#include static constexpr int CELL_WIDTH = 240; static constexpr int CELL_HEIGHT = 160; @@ -65,38 +71,64 @@ SkottieSlide2::SkottieSlide2(const SkString& path) fName.set("skottie-dir"); } +// Build a global scene using tranformed animation fragments: +// +// [Group(root)] +// [Transform] +// [Group] +// [AnimationWrapper] +// [Draw] +// [Text] +// [Color] +// [Transform] +// [Group] +// [AnimationWrapper] +// [Draw] +// [Text] +// [Color] +// ... +// +// Note: for now animation wrappers are also tracked externally in fAnims, for tick dispatching. + +static sk_sp MakeLabel(const SkString& txt, + const SkRect& src, + const SkMatrix& dstXform) { + auto text = sksg::Text::Make(nullptr, txt); + text->setFlags(SkPaint::kAntiAlias_Flag); + text->setSize(12 / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY())); + text->setAlign(SkPaint::kCenter_Align); + text->setPosition(SkPoint::Make(src.width() / 2, src.height() + text->getSize())); + + return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK)); +} + void SkottieSlide2::load(SkScalar, SkScalar) { SkString name; SkOSFile::Iter iter(fPath.c_str(), "json"); int x = 0, y = 0; - // Build a global scene using tranformed animation fragments: - // - // [Group] - // [Transform] - // [AnimationWrapper] - // [Transform] - // [AnimationWrapper] - // ... - // - // Note: for now animation wrappers are also tracked externally in fAnims, for tick dispatching. auto scene_root = sksg::Group::Make(); while (iter.next(&name)) { SkString path = SkOSPath::Join(fPath.c_str(), name.c_str()); if (auto anim = skottie::Animation::MakeFromFile(path.c_str())) { - const SkRect src = SkRect::MakeSize(anim->size()), - dst = SkRect::MakeXYWH(MARGIN + x * (CELL_WIDTH + SPACER_X), - MARGIN + y * (CELL_HEIGHT + SPACER_Y), - CELL_WIDTH, CELL_HEIGHT); + const auto src = SkRect::MakeSize(anim->size()), + dst = SkRect::MakeXYWH(MARGIN + x * (CELL_WIDTH + SPACER_X), + MARGIN + y * (CELL_HEIGHT + SPACER_Y), + CELL_WIDTH, CELL_HEIGHT); + const auto m = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit); + auto wrapper = sk_make_sp(std::move(anim)); - auto matrix = sksg::Matrix::Make( - SkMatrix::MakeRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit)); - auto xform = sksg::Transform::Make(wrapper, std::move(matrix)); + + auto group = sksg::Group::Make(); + group->addChild(wrapper); + group->addChild(MakeLabel(name, src, m)); + + auto xform = sksg::Transform::Make(std::move(group), m); scene_root->addChild(xform); - fAnims.emplace_back(std::move(wrapper)).fName = name; + fAnims.emplace_back(std::move(wrapper)); if (++x == COL_COUNT) { x = 0; @@ -123,27 +155,6 @@ SkISize SkottieSlide2::getDimensions() const { void SkottieSlide2::draw(SkCanvas* canvas) { fScene->render(canvas); - - // TODO: this is all only to draw labels; replace with sksg::Text nodes, when available. - SkPaint paint; - paint.setTextSize(12); - paint.setAntiAlias(true); - paint.setTextAlign(SkPaint::kCenter_Align); - - const SkRect dst = SkRect::MakeIWH(CELL_WIDTH, CELL_HEIGHT); - int x = 0, y = 0; - - canvas->translate(MARGIN, MARGIN); - for (const auto& rec : fAnims) { - SkAutoCanvasRestore acr(canvas, true); - canvas->translate(x * (CELL_WIDTH + SPACER_X), y * (CELL_HEIGHT + SPACER_Y)); - canvas->drawText(rec.fName.c_str(), rec.fName.size(), - dst.centerX(), dst.bottom() + paint.getTextSize(), paint); - if (++x == COL_COUNT) { - x = 0; - y += 1; - } - } } bool SkottieSlide2::animate(const SkAnimTimer& timer) { -- cgit v1.2.3