Skip to content

Commit 0a8b4e1

Browse files
fmalitaSkia Commit-Bot
authored andcommitted
[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>
1 parent 16d91aa commit 0a8b4e1

4 files changed

Lines changed: 139 additions & 24 deletions

File tree

modules/skottie/src/SkottieAdapter.cpp

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "SkottieAdapter.h"
99

10+
#include "SkFont.h"
1011
#include "SkMatrix.h"
1112
#include "SkPath.h"
1213
#include "SkRRect.h"
@@ -19,7 +20,10 @@
1920
#include "SkSGText.h"
2021
#include "SkSGTransform.h"
2122
#include "SkSGTrimEffect.h"
23+
#include "SkTextBlob.h"
24+
#include "SkTextUtils.h"
2225
#include "SkTo.h"
26+
#include "SkUTF.h"
2327
#include "SkottieValue.h"
2428

2529
#include <cmath>
@@ -179,7 +183,7 @@ void TrimEffectAdapter::apply() {
179183

180184
TextAdapter::TextAdapter(sk_sp<sksg::Group> root)
181185
: fRoot(std::move(root))
182-
, fTextNode(sksg::Text::Make(nullptr, SkString()))
186+
, fTextNode(sksg::TextBlob::Make())
183187
, fFillColor(sksg::Color::Make(SK_ColorTRANSPARENT))
184188
, fStrokeColor(sksg::Color::Make(SK_ColorTRANSPARENT))
185189
, fFillNode(sksg::Draw::Make(fTextNode, fFillColor))
@@ -198,21 +202,80 @@ TextAdapter::TextAdapter(sk_sp<sksg::Group> root)
198202
//
199203
// * where the text node is shared
200204

201-
fTextNode->setFlags(fTextNode->getFlags() |
202-
SkPaint::kAntiAlias_Flag |
203-
SkPaint::kSubpixelText_Flag);
204-
fTextNode->setHinting(SkPaint::kNo_Hinting);
205-
205+
fFillColor->setAntiAlias(true);
206+
fStrokeColor->setAntiAlias(true);
206207
fStrokeColor->setStyle(SkPaint::kStroke_Style);
207208
}
208209

209-
void TextAdapter::apply() {
210-
// Push text props to the scene graph.
211-
fTextNode->setTypeface(fText.fTypeface);
212-
fTextNode->setText(fText.fText);
213-
fTextNode->setSize(fText.fTextSize);
214-
fTextNode->setAlign(fText.fAlign);
210+
sk_sp<SkTextBlob> TextAdapter::makeBlob() const {
211+
// TODO: convert to SkFont (missing getFontSpacing, measureText).
212+
SkPaint font;
213+
font.setTypeface(fText.fTypeface);
214+
font.setTextSize(fText.fTextSize);
215+
font.setHinting(SkPaint::kNo_Hinting);
216+
font.setSubpixelText(true);
217+
font.setAntiAlias(true);
218+
font.setTextEncoding(SkPaint::kUTF8_TextEncoding);
219+
220+
const auto align_fract = [](SkTextUtils::Align align) {
221+
switch (align) {
222+
case SkTextUtils::kLeft_Align: return 0.0f;
223+
case SkTextUtils::kCenter_Align: return -0.5f;
224+
case SkTextUtils::kRight_Align: return -1.0f;
225+
}
226+
return 0.0f; // go home, msvc...
227+
}(fText.fAlign);
228+
229+
const auto line_spacing = font.getFontSpacing();
230+
const auto blob_font = SkFont::LEGACY_ExtractFromPaint(font);
231+
float y_off = 0;
232+
SkSTArray<256, SkGlyphID, true> line_glyph_buffer;
233+
SkTextBlobBuilder builder;
234+
235+
const auto& push_line = [&](const char* start, const char* end) {
236+
if (end > start) {
237+
const auto len = SkToSizeT(end - start);
238+
line_glyph_buffer.reset(font.textToGlyphs(start, len, nullptr));
239+
SkAssertResult(font.textToGlyphs(start, len, line_glyph_buffer.data())
240+
== line_glyph_buffer.count());
241+
242+
const auto x_off = align_fract != 0
243+
? align_fract * font.measureText(start, len)
244+
: 0;
245+
const auto& buf = builder.allocRun(blob_font, line_glyph_buffer.count(), x_off, y_off);
246+
if (!buf.glyphs) {
247+
return;
248+
}
249+
250+
memcpy(buf.glyphs, line_glyph_buffer.data(),
251+
SkToSizeT(line_glyph_buffer.count()) * sizeof(SkGlyphID));
252+
253+
y_off += line_spacing;
254+
}
255+
};
256+
257+
const auto& is_line_break = [](SkUnichar uch) {
258+
// TODO: other explicit breaks?
259+
return uch == '\r';
260+
};
215261

262+
const char* ptr = fText.fText.c_str();
263+
const char* line_start = ptr;
264+
const char* end = ptr + fText.fText.size();
265+
266+
while (ptr < end) {
267+
if (is_line_break(SkUTF::NextUTF8(&ptr, end))) {
268+
push_line(line_start, ptr - 1);
269+
line_start = ptr;
270+
}
271+
}
272+
push_line(line_start, ptr);
273+
274+
return builder.make();
275+
}
276+
277+
void TextAdapter::apply() {
278+
fTextNode->setBlob(this->makeBlob());
216279
fFillColor->setColor(fText.fFillColor);
217280
fStrokeColor->setColor(fText.fStrokeColor);
218281
fStrokeColor->setStrokeWidth(fText.fStrokeWidth);

modules/skottie/src/SkottieAdapter.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Matrix;
2424
class Path;
2525
class RadialGradient;
2626
class RRect;
27-
class Text;
27+
class TextBlob;
2828
class TrimEffect;
2929

3030
};
@@ -175,16 +175,17 @@ class TextAdapter final : public SkRefCnt {
175175

176176
private:
177177
void apply();
178+
sk_sp<SkTextBlob> makeBlob() const;
178179

179-
sk_sp<sksg::Group> fRoot;
180-
sk_sp<sksg::Text> fTextNode;
181-
sk_sp<sksg::Color> fFillColor,
182-
fStrokeColor;
183-
sk_sp<sksg::Draw> fFillNode,
184-
fStrokeNode;
180+
sk_sp<sksg::Group> fRoot;
181+
sk_sp<sksg::TextBlob> fTextNode;
182+
sk_sp<sksg::Color> fFillColor,
183+
fStrokeColor;
184+
sk_sp<sksg::Draw> fFillNode,
185+
fStrokeNode;
185186

186-
bool fHadFill : 1, // - state cached from the prev apply()
187-
fHadStroke : 1; // /
187+
bool fHadFill : 1, // - state cached from the prev apply()
188+
fHadStroke : 1; // /
188189

189190
using INHERITED = SkRefCnt;
190191
};

modules/sksg/include/SkSGText.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
#include "SkPaintDefaults.h"
1414
#include "SkPoint.h"
1515
#include "SkString.h"
16+
#include "SkTextBlob.h"
1617
#include "SkTextUtils.h"
1718

1819
class SkCanvas;
1920
class SkPaint;
20-
class SkTextBlob;
2121
class SkTypeface;
2222

2323
namespace sksg {
@@ -50,7 +50,7 @@ class Text final : public GeometryNode {
5050
SkPath onAsPath() const override;
5151

5252
private:
53-
explicit Text(sk_sp<SkTypeface>, const SkString&);
53+
Text(sk_sp<SkTypeface>, const SkString&);
5454

5555
SkPoint alignedPosition(SkScalar advance) const;
5656

@@ -69,6 +69,32 @@ class Text final : public GeometryNode {
6969
using INHERITED = GeometryNode;
7070
};
7171

72+
/**
73+
* Concrete Geometry node, wrapping an external SkTextBlob.
74+
*/
75+
class TextBlob final : public GeometryNode {
76+
public:
77+
static sk_sp<TextBlob> Make(sk_sp<SkTextBlob> = nullptr);
78+
~TextBlob() override;
79+
80+
SG_ATTRIBUTE(Blob , sk_sp<SkTextBlob>, fBlob )
81+
SG_ATTRIBUTE(Position, SkPoint , fPosition)
82+
83+
protected:
84+
void onClip(SkCanvas*, bool antiAlias) const override;
85+
void onDraw(SkCanvas*, const SkPaint&) const override;
86+
87+
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
88+
SkPath onAsPath() const override;
89+
90+
private:
91+
explicit TextBlob(sk_sp<SkTextBlob>);
92+
93+
sk_sp<SkTextBlob> fBlob;
94+
SkPoint fPosition = SkPoint::Make(0, 0);
95+
96+
using INHERITED = GeometryNode;
97+
};
7298
} // namespace sksg
7399

74100
#endif // SkSGText_DEFINED

modules/sksg/src/SkSGText.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "SkPaint.h"
1212
#include "SkPath.h"
1313
#include "SkTArray.h"
14-
#include "SkTextBlob.h"
1514
#include "SkTypeface.h"
1615

1716
namespace sksg {
@@ -100,4 +99,30 @@ void Text::onClip(SkCanvas* canvas, bool antiAlias) const {
10099
canvas->clipPath(this->asPath(), antiAlias);
101100
}
102101

102+
sk_sp<TextBlob> TextBlob::Make(sk_sp<SkTextBlob> blob) {
103+
return sk_sp<TextBlob>(new TextBlob(std::move(blob)));
104+
}
105+
106+
TextBlob::TextBlob(sk_sp<SkTextBlob> blob)
107+
: fBlob(std::move(blob)) {}
108+
109+
TextBlob::~TextBlob() = default;
110+
111+
SkRect TextBlob::onRevalidate(InvalidationController*, const SkMatrix&) {
112+
return fBlob ? fBlob->bounds() : SkRect::MakeEmpty();
113+
}
114+
115+
void TextBlob::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
116+
canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint);
117+
}
118+
119+
SkPath TextBlob::onAsPath() const {
120+
// TODO
121+
return SkPath();
122+
}
123+
124+
void TextBlob::onClip(SkCanvas* canvas, bool antiAlias) const {
125+
canvas->clipPath(this->asPath(), antiAlias);
126+
}
127+
103128
} // namespace sksg

0 commit comments

Comments
 (0)