Flutter Engine
 
Loading...
Searching...
No Matches
paragraph_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <memory>
11#include "gtest/gtest.h"
12#include "include/core/SkScalar.h"
15#include "testing/canvas_test.h"
16#include "testing/testing.h"
17#include "txt/platform.h"
18
19namespace flutter {
20namespace testing {
21
22[[maybe_unused]]
23static const std::string kEmojiFontFile =
24#if FML_OS_MACOSX
25 "Apple Color Emoji.ttc";
26#else
27 "NotoColorEmoji.ttf";
28#endif
29
30[[maybe_unused]]
31static const std::string kEmojiFontName =
32#if FML_OS_MACOSX
33 "Apple Color Emoji";
34#else
35 "Noto Color Emoji";
36#endif
37
38//------------------------------------------------------------------------------
39/// @brief A custom |DlOpReceiver| that records some |DlOps| it receives.
40class DlOpRecorder final : public virtual DlOpReceiver,
45 public:
46 int lineCount() const { return lines_.size(); }
47 int dashedLineCount() const { return dashed_lines_.size(); }
48 int rectCount() const { return rects_.size(); }
49 int pathCount() const { return paths_.size(); }
50 int textFrameCount() const { return text_frames_.size(); }
51 int blobCount() const { return blobs_.size(); }
52
53 private:
54 void drawLine(const DlPoint& p0, const DlPoint& p1) override {
55 lines_.emplace_back(p0, p1);
56 }
57
58 void drawDashedLine(const DlPoint& p0,
59 const DlPoint& p1,
60 DlScalar on_length,
61 DlScalar off_length) override {
62 dashed_lines_.emplace_back(p0, p1, DlPoint(on_length, off_length));
63 }
64
65 void drawText(const std::shared_ptr<DlText>& text,
66 SkScalar x,
67 SkScalar y) override {
68 auto blob = text->GetTextBlob();
69 if (blob) {
70 blobs_.push_back(sk_ref_sp(blob));
71 } else {
72 auto frame = text->GetTextFrame();
73 FML_CHECK(frame);
74 text_frames_.push_back(frame);
75 }
76 }
77
78 void drawRect(const DlRect& rect) override { rects_.push_back(rect); }
79
80 void drawPath(const DlPath& path) override { paths_.push_back(path); }
81
82 std::vector<std::shared_ptr<impeller::TextFrame>> text_frames_;
83 std::vector<sk_sp<SkTextBlob>> blobs_;
84 std::vector<std::pair<DlPoint, DlPoint>> lines_;
85 std::vector<std::tuple<DlPoint, DlPoint, DlPoint>> dashed_lines_;
86 std::vector<DlRect> rects_;
87 std::vector<DlPath> paths_;
88};
89
90template <typename T>
92 public:
93 PainterTestBase() = default;
94
95 void PretendImpellerIsEnabled(bool impeller) { impeller_ = impeller; }
96
97 protected:
99 auto t_style = txt::TextStyle();
100 t_style.color = SK_ColorBLACK; // default
101 t_style.font_weight = txt::FontWeight::normal;
102 t_style.font_size = 14; // default
103 t_style.decoration = txt::TextDecoration::kUnderline;
104 t_style.decoration_style = style;
105 t_style.decoration_color = SK_ColorBLACK;
106 t_style.font_families.push_back("ahem");
107 return t_style;
108 }
109
111 auto t_style = txt::TextStyle();
112 t_style.color = SK_ColorBLACK; // default
113 t_style.font_weight = txt::FontWeight::normal;
114 t_style.font_size = 14; // default
115 t_style.font_families.push_back(kEmojiFontName);
116 return t_style;
117 }
118
120 auto t_style = txt::TextStyle();
121 t_style.color = SK_ColorBLACK; // default
122 t_style.font_weight = txt::FontWeight::normal;
123 t_style.font_size = 14; // default
124 t_style.font_families.push_back("ahem");
125 return t_style;
126 }
127
128 sk_sp<DisplayList> drawText(const txt::TextStyle& style,
129 const std::u16string& text) const {
130 auto pb_skia = makeParagraphBuilder();
131 pb_skia.PushStyle(style);
132 pb_skia.AddText(text);
133 pb_skia.Pop();
134
135 auto builder = DisplayListBuilder();
136 auto paragraph = pb_skia.Build();
137 paragraph->Layout(10000);
138 paragraph->Paint(&builder, 0, 0);
139
140 return builder.Build();
141 }
142
143 sk_sp<DisplayList> draw(const txt::TextStyle& style) const {
144 auto pb_skia = makeParagraphBuilder();
145 pb_skia.PushStyle(style);
146 pb_skia.AddText(u"Hello World!");
147 pb_skia.Pop();
148
149 auto builder = DisplayListBuilder();
150 auto paragraph = pb_skia.Build();
151 paragraph->Layout(10000);
152 paragraph->Paint(&builder, 0, 0);
153
154 return builder.Build();
155 }
156
157 private:
158 std::shared_ptr<txt::FontCollection> makeFontCollection() const {
159 auto f_collection = std::make_shared<txt::FontCollection>();
160 auto font_provider = std::make_unique<txt::TypefaceFontAssetProvider>();
161 for (auto& font : GetTestFontData()) {
162 font_provider->RegisterTypeface(font);
163 }
164 // Load emoji font.
165 {
166 auto c_font_fixture = std::string(kEmojiFontFile);
167 auto mapping =
168 flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
169 auto typeface = txt::GetDefaultFontManager()->makeFromData(mapping);
170 font_provider->RegisterTypeface(typeface);
171 }
172
173 auto manager = sk_make_sp<txt::AssetFontManager>(std::move(font_provider));
174 f_collection->SetAssetFontManager(manager);
175 return f_collection;
176 }
177
178 txt::ParagraphBuilderSkia makeParagraphBuilder() const {
179 auto p_style = txt::ParagraphStyle();
180 auto f_collection = makeFontCollection();
181 return txt::ParagraphBuilderSkia(p_style, f_collection, impeller_);
182 }
183
184 bool impeller_ = false;
185};
186
188
189TEST_F(PainterTest, DrawsSolidLineSkia) {
190 PretendImpellerIsEnabled(false);
191
192 auto recorder = DlOpRecorder();
193 draw(makeDecoratedStyle(txt::TextDecorationStyle::kSolid))
194 ->Dispatch(recorder);
195
196 // Skia may draw a solid underline as a filled rectangle:
197 // https://skia.googlesource.com/skia/+/refs/heads/main/modules/skparagraph/src/Decorations.cpp#91
198 EXPECT_EQ(recorder.rectCount(), 1);
199}
200
201TEST_F(PainterTest, DrawDashedLineSkia) {
202 PretendImpellerIsEnabled(false);
203
204 auto recorder = DlOpRecorder();
205 draw(makeDecoratedStyle(txt::TextDecorationStyle::kDashed))
206 ->Dispatch(recorder);
207
208 // Skia draws a dashed underline as a filled rectangle with a path effect.
209 EXPECT_EQ(recorder.lineCount(), 0);
210 EXPECT_EQ(recorder.dashedLineCount(), 1);
211}
212
213#ifdef IMPELLER_SUPPORTS_RENDERING
214TEST_F(PainterTest, DrawsSolidLineImpeller) {
215 PretendImpellerIsEnabled(true);
216
217 auto recorder = DlOpRecorder();
218 draw(makeDecoratedStyle(txt::TextDecorationStyle::kSolid))
219 ->Dispatch(recorder);
220
221 // Skia may draw a solid underline as a filled rectangle:
222 // https://skia.googlesource.com/skia/+/refs/heads/main/modules/skparagraph/src/Decorations.cpp#91
223 EXPECT_EQ(recorder.rectCount(), 1);
224}
225
226TEST_F(PainterTest, DrawDashedLineImpeller) {
227 PretendImpellerIsEnabled(true);
228
229 auto recorder = DlOpRecorder();
230 draw(makeDecoratedStyle(txt::TextDecorationStyle::kDashed))
231 ->Dispatch(recorder);
232
233 // Impeller draws a dashed underline as a path.
234 EXPECT_EQ(recorder.pathCount(), 0);
235 EXPECT_EQ(recorder.dashedLineCount(), 1);
236}
237
238TEST_F(PainterTest, DrawTextImpeller) {
239 PretendImpellerIsEnabled(true);
240
241 auto recorder = DlOpRecorder();
242 draw(makeStyle())->Dispatch(recorder);
243
244 EXPECT_EQ(recorder.textFrameCount(), 1);
245 EXPECT_EQ(recorder.blobCount(), 0);
246}
247
248TEST_F(PainterTest, DrawStrokedTextImpeller) {
249 PretendImpellerIsEnabled(true);
250
251 auto style = makeStyle();
252 // What is your shtyle?
253 DlPaint foreground;
255 style.foreground = foreground;
256
257 auto recorder = DlOpRecorder();
258 draw(style)->Dispatch(recorder);
259
260 EXPECT_EQ(recorder.textFrameCount(), 1);
261 EXPECT_EQ(recorder.blobCount(), 0);
262 EXPECT_EQ(recorder.pathCount(), 0);
263}
264
265TEST_F(PainterTest, DrawStrokedTextWithLargeWidthImpeller) {
266 PretendImpellerIsEnabled(true);
267
268 auto style = makeStyle();
269 DlPaint foreground;
271 foreground.setStrokeWidth(10);
272 style.foreground = foreground;
273
274 auto recorder = DlOpRecorder();
275 draw(style)->Dispatch(recorder);
276
277 EXPECT_EQ(recorder.textFrameCount(), 0);
278 EXPECT_EQ(recorder.blobCount(), 0);
279 EXPECT_EQ(recorder.pathCount(), 1);
280}
281
282TEST_F(PainterTest, DrawTextWithGradientImpeller) {
283 PretendImpellerIsEnabled(true);
284
285 auto style = makeStyle();
286 // how do you like my shtyle?
287 DlPaint foreground;
288 std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kCyan()};
289 std::vector<float> stops = {0.0, 1.0};
290 foreground.setColorSource(DlColorSource::MakeLinear(
291 DlPoint(0, 0), DlPoint(100, 100), 2, colors.data(), stops.data(),
293 style.foreground = foreground;
294
295 auto recorder = DlOpRecorder();
296 draw(style)->Dispatch(recorder);
297
298 EXPECT_EQ(recorder.textFrameCount(), 0);
299 EXPECT_EQ(recorder.blobCount(), 0);
300 EXPECT_EQ(recorder.pathCount(), 1);
301}
302
303TEST_F(PainterTest, DrawEmojiTextWithGradientImpeller) {
304 PretendImpellerIsEnabled(true);
305
306 auto style = makeEmoji();
307 // how do you like my shtyle?
308 DlPaint foreground;
309 std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kCyan()};
310 std::vector<float> stops = {0.0, 1.0};
311 foreground.setColorSource(DlColorSource::MakeLinear(
312 DlPoint(0, 0), DlPoint(100, 100), 2, colors.data(), stops.data(),
314 style.foreground = foreground;
315
316 auto recorder = DlOpRecorder();
317 drawText(style, u"😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊")->Dispatch(recorder);
318
319 EXPECT_EQ(recorder.textFrameCount(), 1);
320 EXPECT_EQ(recorder.blobCount(), 0);
321 EXPECT_EQ(recorder.pathCount(), 0);
322}
323
324TEST_F(PainterTest, DrawTextBlobNoImpeller) {
325 PretendImpellerIsEnabled(false);
326
327 auto recorder = DlOpRecorder();
328 draw(makeStyle())->Dispatch(recorder);
329
330 EXPECT_EQ(recorder.textFrameCount(), 0);
331 EXPECT_EQ(recorder.blobCount(), 1);
332}
333#endif // IMPELLER_SUPPORTS_RENDERING
334
335} // namespace testing
336} // namespace flutter
static std::shared_ptr< DlColorSource > MakeLinear(const DlPoint start_point, const DlPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
Internal API for rendering recorded display lists to backends.
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
A custom |DlOpReceiver| that records some |DlOps| it receives.
sk_sp< DisplayList > draw(const txt::TextStyle &style) const
txt::TextStyle makeDecoratedStyle(txt::TextDecorationStyle style)
sk_sp< DisplayList > drawText(const txt::TextStyle &style, const std::u16string &text) const
ParagraphBuilder implementation using Skia's text layout module.
int32_t x
#define FML_CHECK(condition)
Definition logging.h:104
std::u16string text
double y
static const std::string kEmojiFontName
TEST_F(DisplayListTest, Defaults)
sk_sp< SkData > OpenFixtureAsSkData(const std::string &fixture_name)
Opens a fixture of the given file name and returns a Skia SkData holding its contents.
Definition testing.cc:63
PainterTestBase<::testing::Test > PainterTest
static const std::string kEmojiFontFile
impeller::Scalar DlScalar
impeller::Rect DlRect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
@ kStroke
strokes boundary of shapes
std::vector< sk_sp< SkTypeface > > GetTestFontData()
impeller::Point DlPoint
flutter::DlPaint DlPaint
TextDecorationStyle
sk_sp< SkFontMgr > GetDefaultFontManager(uint32_t font_initialization_data)
Definition platform.cc:17
@ normal
Definition font_weight.h:12
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kCyan()
Definition dl_color.h:74