Flutter Engine
The Flutter Engine
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"
15#include "testing/canvas_test.h"
16#include "testing/testing.h"
17#include "txt/platform.h"
18
19namespace flutter {
20namespace testing {
21
22static const std::string kEmojiFontFile =
23#if FML_OS_MACOSX
24 "Apple Color Emoji.ttc";
25#else
26 "NotoColorEmoji.ttf";
27#endif
28
29static const std::string kEmojiFontName =
30#if FML_OS_MACOSX
31 "Apple Color Emoji";
32#else
33 "Noto Color Emoji";
34#endif
35
36
37//------------------------------------------------------------------------------
38/// @brief A custom |DlOpReceiver| that records some |DlOps| it receives.
39class DlOpRecorder final : public virtual DlOpReceiver,
44 public:
45 int lineCount() const { return lines_.size(); }
46 int dashedLineCount() const { return dashed_lines_.size(); }
47 int rectCount() const { return rects_.size(); }
48 int pathCount() const { return paths_.size(); }
49 int textFrameCount() const { return text_frames_.size(); }
50 int blobCount() const { return blobs_.size(); }
51
52 private:
53 void drawLine(const SkPoint& p0, const SkPoint& p1) override {
54 lines_.emplace_back(p0, p1);
55 }
56
57 void drawDashedLine(const DlPoint& p0,
58 const DlPoint& p1,
59 DlScalar on_length,
60 DlScalar off_length) override {
61 dashed_lines_.emplace_back(p0, p1, DlPoint(on_length, off_length));
62 }
63
64 void drawTextFrame(const std::shared_ptr<impeller::TextFrame>& text_frame,
65 SkScalar x,
66 SkScalar y) override {
67 text_frames_.push_back(text_frame);
68 }
69
70 void drawTextBlob(const sk_sp<SkTextBlob> blob,
71 SkScalar x,
72 SkScalar y) override {
73 blobs_.push_back(blob);
74 }
75
76 void drawRect(const SkRect& rect) override { rects_.push_back(rect); }
77
78 void drawPath(const SkPath& path) override { paths_.push_back(path); }
79
80 std::vector<std::shared_ptr<impeller::TextFrame>> text_frames_;
81 std::vector<sk_sp<SkTextBlob>> blobs_;
82 std::vector<std::pair<SkPoint, SkPoint>> lines_;
83 std::vector<std::tuple<DlPoint, DlPoint, DlPoint>> dashed_lines_;
84 std::vector<SkRect> rects_;
85 std::vector<SkPath> paths_;
86};
87
88template <typename T>
90 public:
91 PainterTestBase() = default;
92
93 void PretendImpellerIsEnabled(bool impeller) { impeller_ = impeller; }
94
95 protected:
97 auto t_style = txt::TextStyle();
98 t_style.color = SK_ColorBLACK; // default
99 t_style.font_weight = txt::FontWeight::w400; // normal
100 t_style.font_size = 14; // default
101 t_style.decoration = txt::TextDecoration::kUnderline;
102 t_style.decoration_style = style;
103 t_style.decoration_color = SK_ColorBLACK;
104 t_style.font_families.push_back("ahem");
105 return t_style;
106 }
107
109 auto t_style = txt::TextStyle();
110 t_style.color = SK_ColorBLACK; // default
111 t_style.font_weight = txt::FontWeight::w400; // normal
112 t_style.font_size = 14; // default
113 t_style.font_families.push_back(kEmojiFontName);
114 return t_style;
115 }
116
118 auto t_style = txt::TextStyle();
119 t_style.color = SK_ColorBLACK; // default
120 t_style.font_weight = txt::FontWeight::w400; // normal
121 t_style.font_size = 14; // default
122 t_style.font_families.push_back("ahem");
123 return t_style;
124 }
125
126 sk_sp<DisplayList> drawText(txt::TextStyle style, std::u16string text) const {
127 auto pb_skia = makeParagraphBuilder();
128 pb_skia.PushStyle(style);
129 pb_skia.AddText(text);
130 pb_skia.Pop();
131
133 auto paragraph = pb_skia.Build();
134 paragraph->Layout(10000);
135 paragraph->Paint(&builder, 0, 0);
136
137 return builder.Build();
138 }
139
141 auto pb_skia = makeParagraphBuilder();
142 pb_skia.PushStyle(style);
143 pb_skia.AddText(u"Hello World!");
144 pb_skia.Pop();
145
147 auto paragraph = pb_skia.Build();
148 paragraph->Layout(10000);
149 paragraph->Paint(&builder, 0, 0);
150
151 return builder.Build();
152 }
153
154 private:
155 std::shared_ptr<txt::FontCollection> makeFontCollection() const {
156 auto f_collection = std::make_shared<txt::FontCollection>();
157 auto font_provider = std::make_unique<txt::TypefaceFontAssetProvider>();
158 for (auto& font : GetTestFontData()) {
159 font_provider->RegisterTypeface(font);
160 }
161 // Load emoji font.
162 {
163 auto c_font_fixture = std::string(kEmojiFontFile);
164 auto mapping =
165 flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
166 auto typeface = txt::GetDefaultFontManager()->makeFromData(mapping);
167 font_provider->RegisterTypeface(typeface);
168 }
169
170 auto manager = sk_make_sp<txt::AssetFontManager>(std::move(font_provider));
171 f_collection->SetAssetFontManager(manager);
172 return f_collection;
173 }
174
175 txt::ParagraphBuilderSkia makeParagraphBuilder() const {
176 auto p_style = txt::ParagraphStyle();
177 auto f_collection = makeFontCollection();
178 return txt::ParagraphBuilderSkia(p_style, f_collection, impeller_);
179 }
180
181 bool impeller_ = false;
182};
183
185
186TEST_F(PainterTest, DrawsSolidLineSkia) {
187 PretendImpellerIsEnabled(false);
188
189 auto recorder = DlOpRecorder();
190 draw(makeDecoratedStyle(txt::TextDecorationStyle::kSolid))
191 ->Dispatch(recorder);
192
193 // Skia may draw a solid underline as a filled rectangle:
194 // https://skia.googlesource.com/skia/+/refs/heads/main/modules/skparagraph/src/Decorations.cpp#91
195 EXPECT_EQ(recorder.rectCount(), 1);
196}
197
198TEST_F(PainterTest, DrawDashedLineSkia) {
199 PretendImpellerIsEnabled(false);
200
201 auto recorder = DlOpRecorder();
202 draw(makeDecoratedStyle(txt::TextDecorationStyle::kDashed))
203 ->Dispatch(recorder);
204
205 // Skia draws a dashed underline as a filled rectangle with a path effect.
206 EXPECT_EQ(recorder.lineCount(), 0);
207 EXPECT_EQ(recorder.dashedLineCount(), 1);
208}
209
210#ifdef IMPELLER_SUPPORTS_RENDERING
211TEST_F(PainterTest, DrawsSolidLineImpeller) {
212 PretendImpellerIsEnabled(true);
213
214 auto recorder = DlOpRecorder();
215 draw(makeDecoratedStyle(txt::TextDecorationStyle::kSolid))
216 ->Dispatch(recorder);
217
218 // Skia may draw a solid underline as a filled rectangle:
219 // https://skia.googlesource.com/skia/+/refs/heads/main/modules/skparagraph/src/Decorations.cpp#91
220 EXPECT_EQ(recorder.rectCount(), 1);
221}
222
223TEST_F(PainterTest, DrawDashedLineImpeller) {
224 PretendImpellerIsEnabled(true);
225
226 auto recorder = DlOpRecorder();
227 draw(makeDecoratedStyle(txt::TextDecorationStyle::kDashed))
228 ->Dispatch(recorder);
229
230 // Impeller draws a dashed underline as a path.
231 EXPECT_EQ(recorder.pathCount(), 0);
232 EXPECT_EQ(recorder.dashedLineCount(), 1);
233}
234
235TEST_F(PainterTest, DrawTextFrameImpeller) {
236 PretendImpellerIsEnabled(true);
237
238 auto recorder = DlOpRecorder();
239 draw(makeStyle())->Dispatch(recorder);
240
241 EXPECT_EQ(recorder.textFrameCount(), 1);
242 EXPECT_EQ(recorder.blobCount(), 0);
243}
244
245TEST_F(PainterTest, DrawStrokedTextImpeller) {
246 PretendImpellerIsEnabled(true);
247
248 auto style = makeStyle();
249 // What is your shtyle?
250 DlPaint foreground;
252 style.foreground = foreground;
253
254 auto recorder = DlOpRecorder();
255 draw(style)->Dispatch(recorder);
256
257 EXPECT_EQ(recorder.textFrameCount(), 1);
258 EXPECT_EQ(recorder.blobCount(), 0);
259 EXPECT_EQ(recorder.pathCount(), 0);
260}
261
262TEST_F(PainterTest, DrawTextWithGradientImpeller) {
263 PretendImpellerIsEnabled(true);
264
265 auto style = makeStyle();
266 // how do you like my shtyle?
267 DlPaint foreground;
268 std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kCyan()};
269 std::vector<float> stops = {0.0, 1.0};
270 foreground.setColorSource(DlColorSource::MakeLinear(
271 SkPoint::Make(0, 0), SkPoint::Make(100, 100), 2, colors.data(),
272 stops.data(), DlTileMode::kClamp));
273 style.foreground = foreground;
274
275 auto recorder = DlOpRecorder();
276 draw(style)->Dispatch(recorder);
277
278 EXPECT_EQ(recorder.textFrameCount(), 0);
279 EXPECT_EQ(recorder.blobCount(), 0);
280 EXPECT_EQ(recorder.pathCount(), 1);
281}
282
283TEST_F(PainterTest, DrawEmojiTextWithGradientImpeller) {
284 PretendImpellerIsEnabled(true);
285
286 auto style = makeEmoji();
287 // how do you like my shtyle?
288 DlPaint foreground;
289 std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kCyan()};
290 std::vector<float> stops = {0.0, 1.0};
291 foreground.setColorSource(DlColorSource::MakeLinear(
292 SkPoint::Make(0, 0), SkPoint::Make(100, 100), 2, colors.data(),
293 stops.data(), DlTileMode::kClamp));
294 style.foreground = foreground;
295
296 auto recorder = DlOpRecorder();
297 drawText(style, u"😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊")->Dispatch(recorder);
298
299 EXPECT_EQ(recorder.textFrameCount(), 1);
300 EXPECT_EQ(recorder.blobCount(), 0);
301 EXPECT_EQ(recorder.pathCount(), 0);
302}
303
304TEST_F(PainterTest, DrawTextBlobNoImpeller) {
305 PretendImpellerIsEnabled(false);
306
307 auto recorder = DlOpRecorder();
308 draw(makeStyle())->Dispatch(recorder);
309
310 EXPECT_EQ(recorder.textFrameCount(), 0);
311 EXPECT_EQ(recorder.blobCount(), 1);
312}
313#endif // IMPELLER_SUPPORTS_RENDERING
314
315} // namespace testing
316} // namespace flutter
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
sk_sp< SkTypeface > makeFromData(sk_sp< SkData >, int ttcIndex=0) const
Definition: SkFontMgr.cpp:120
Definition: SkPath.h:59
static std::shared_ptr< DlLinearGradientColorSource > MakeLinear(const SkPoint start_point, const SkPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const SkMatrix *matrix=nullptr)
Internal API for rendering recorded display lists to backends.
DlPaint & setDrawStyle(DlDrawStyle style)
Definition: dl_paint.h:94
A custom |DlOpReceiver| that records some |DlOps| it receives.
txt::TextStyle makeDecoratedStyle(txt::TextDecorationStyle style)
sk_sp< DisplayList > draw(txt::TextStyle style) const
void PretendImpellerIsEnabled(bool impeller)
sk_sp< DisplayList > drawText(txt::TextStyle style, std::u16string text) const
ParagraphBuilder implementation using Skia's text layout module.
float SkScalar
Definition: extension.cpp:12
std::u16string text
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
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:64
PainterTestBase<::testing::Test > PainterTest
static const std::string kEmojiFontFile
impeller::Scalar DlScalar
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition: switches.h:218
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: switches.h:57
@ kStroke
strokes boundary of shapes
std::vector< sk_sp< SkTypeface > > GetTestFontData()
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap The size limit in megabytes for the Dart VM old gen heap space enable impeller
Definition: switches.h:266
impeller::Point DlPoint
font
Font Metadata and Metrics.
flutter::DlPaint DlPaint
TextDecorationStyle
sk_sp< SkFontMgr > GetDefaultFontManager(uint32_t font_initialization_data)
Definition: platform.cc:17
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
static constexpr DlColor kRed()
Definition: dl_color.h:24
static constexpr DlColor kCyan()
Definition: dl_color.h:27