Flutter Engine
 
Loading...
Searching...
No Matches
text_contents_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
12#include "third_party/googletest/googletest/include/gtest/gtest.h"
13#include "txt/platform.h"
14
15#pragma GCC diagnostic ignored "-Wunreachable-code"
16
17namespace impeller {
18namespace testing {
19
22
23using ::testing::Return;
24
25namespace {
26struct TextOptions {
28 bool is_subpixel = false;
29};
30
31std::shared_ptr<TextFrame> MakeTextFrame(const std::string& text,
32 const std::string_view& font_fixture,
33 const TextOptions& options) {
34 auto c_font_fixture = std::string(font_fixture);
35 auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
36 if (!mapping) {
37 return nullptr;
38 }
39 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
40 SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
41 if (options.is_subpixel) {
42 sk_font.setSubpixel(true);
43 }
44 auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
45 if (!blob) {
46 return nullptr;
47 }
48
50}
51
52std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
53 Context& context,
54 const TypographerContext* typographer_context,
55 HostBuffer& data_host_buffer,
57 Rational scale,
58 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
59 const std::shared_ptr<TextFrame>& frame,
60 Point offset) {
61 frame->SetPerFrameData(
62 TextFrame::RoundScaledFontSize(scale), /*offset=*/offset,
63 /*transform=*/
65 Vector3{static_cast<Scalar>(scale), static_cast<Scalar>(scale), 1}),
66 /*properties=*/std::nullopt);
67 return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
68 atlas_context, {frame});
69}
70
71Rect PerVertexDataPositionToRect(
72 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData>::iterator
73 data) {
74 Scalar right = FLT_MIN;
75 Scalar left = FLT_MAX;
76 Scalar top = FLT_MAX;
77 Scalar bottom = FLT_MIN;
78 for (int i = 0; i < 4; ++i) {
79 right = std::max(right, data[i].position.x);
80 left = std::min(left, data[i].position.x);
81 top = std::min(top, data[i].position.y);
82 bottom = std::max(bottom, data[i].position.y);
83 }
84
85 return Rect::MakeLTRB(left, top, right, bottom);
86}
87
88Rect PerVertexDataUVToRect(
89 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData>::iterator data,
90 ISize texture_size) {
91 Scalar right = FLT_MIN;
92 Scalar left = FLT_MAX;
93 Scalar top = FLT_MAX;
94 Scalar bottom = FLT_MIN;
95 for (int i = 0; i < 4; ++i) {
96 right = std::max(right, data[i].uv.x * texture_size.width);
97 left = std::min(left, data[i].uv.x * texture_size.width);
98 top = std::min(top, data[i].uv.y * texture_size.height);
99 bottom = std::max(bottom, data[i].uv.y * texture_size.height);
100 }
101
102 return Rect::MakeLTRB(left, top, right, bottom);
103}
104
105double GetAspectRatio(Rect rect) {
106 return static_cast<double>(rect.GetWidth()) / rect.GetHeight();
107}
108} // namespace
109
110TEST_P(TextContentsTest, SimpleComputeVertexData) {
111#ifndef FML_OS_MACOSX
112 GTEST_SKIP() << "Results aren't stable across linux and macos.";
113#endif
114
115 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
116
117 std::shared_ptr<TextFrame> text_frame =
118 MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50});
119
120 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
121 std::shared_ptr<GlyphAtlasContext> atlas_context =
122 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
123 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
124 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
125 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
126 ASSERT_TRUE(context && context->IsValid());
127 std::shared_ptr<GlyphAtlas> atlas =
128 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
130 atlas_context, text_frame, /*offset=*/{0, 0});
131
132 ISize texture_size = atlas->GetTexture()->GetSize();
133 TextContents::ComputeVertexData(data.data(), text_frame, /*scale=*/1.0,
134 /*entity_transform=*/Matrix(),
135 /*offset=*/Vector2(0, 0),
136 /*glyph_properties=*/std::nullopt, atlas);
137
138 Rect position_rect = PerVertexDataPositionToRect(data.begin());
139 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
140 // The -1 offset comes from Skia in `ComputeGlyphSize`. So since the font size
141 // is 50, the math appears to be to get back a 50x50 rect and apply 1 pixel
142 // of padding.
143 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
144 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
145}
146
147TEST_P(TextContentsTest, SimpleComputeVertexData2x) {
148#ifndef FML_OS_MACOSX
149 GTEST_SKIP() << "Results aren't stable across linux and macos.";
150#endif
151
152 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
153 std::shared_ptr<TextFrame> text_frame =
154 MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50});
155
156 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
157 std::shared_ptr<GlyphAtlasContext> atlas_context =
158 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
159 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
160 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
161 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
162 ASSERT_TRUE(context && context->IsValid());
163 Rational font_scale(2, 1);
164 std::shared_ptr<GlyphAtlas> atlas =
165 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
167 atlas_context, text_frame, /*offset=*/{0, 0});
168
169 ISize texture_size = atlas->GetTexture()->GetSize();
171 data.data(), text_frame, static_cast<Scalar>(font_scale),
172 /*entity_transform=*/
173 Matrix::MakeScale({static_cast<Scalar>(font_scale),
174 static_cast<Scalar>(font_scale), 1}),
175 /*offset=*/Vector2(0, 0),
176 /*glyph_properties=*/std::nullopt, atlas);
177
178 Rect position_rect = PerVertexDataPositionToRect(data.begin());
179 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
180 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -81, 102, 102));
181 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 102, 102));
182}
183
184TEST_P(TextContentsTest, MaintainsShape) {
185 std::shared_ptr<TextFrame> text_frame =
186 MakeTextFrame("th", "ahem.ttf", TextOptions{.font_size = 50});
187
188 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
189 std::shared_ptr<GlyphAtlasContext> atlas_context =
190 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
191 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
192 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
193 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
194 ASSERT_TRUE(context && context->IsValid());
195
196 for (int i = 0; i <= 1000; ++i) {
197 Rational font_scale(440 + i, 1000.0);
198 Rect position_rect[2];
199 Rect uv_rect[2];
200
201 {
202 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(12);
203
204 std::shared_ptr<GlyphAtlas> atlas =
205 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
207 atlas_context, text_frame, /*offset=*/{0, 0});
208 ISize texture_size = atlas->GetTexture()->GetSize();
209
211 data.data(), text_frame, static_cast<Scalar>(font_scale),
212 /*entity_transform=*/
213 Matrix::MakeScale({static_cast<Scalar>(font_scale),
214 static_cast<Scalar>(font_scale), 1}),
215 /*offset=*/Vector2(0, 0),
216 /*glyph_properties=*/std::nullopt, atlas);
217 position_rect[0] = PerVertexDataPositionToRect(data.begin());
218 uv_rect[0] = PerVertexDataUVToRect(data.begin(), texture_size);
219 position_rect[1] = PerVertexDataPositionToRect(data.begin() + 4);
220 uv_rect[1] = PerVertexDataUVToRect(data.begin() + 4, texture_size);
221 }
222 EXPECT_NEAR(GetAspectRatio(position_rect[1]), GetAspectRatio(uv_rect[1]),
223 0.001)
224 << i;
225 }
226}
227
228TEST_P(TextContentsTest, SimpleSubpixel) {
229#ifndef FML_OS_MACOSX
230 GTEST_SKIP() << "Results aren't stable across linux and macos.";
231#endif
232
233 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
234
235 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
236 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
237
238 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
239 std::shared_ptr<GlyphAtlasContext> atlas_context =
240 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
241 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
242 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
243 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
244 ASSERT_TRUE(context && context->IsValid());
245 Point offset = Point(0.5, 0);
246 std::shared_ptr<GlyphAtlas> atlas =
247 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
249 atlas_context, text_frame, offset);
250
251 ISize texture_size = atlas->GetTexture()->GetSize();
253 data.data(), text_frame, /*scale=*/1.0,
254 /*entity_transform=*/Matrix::MakeTranslation(offset), offset,
255 /*glyph_properties=*/std::nullopt, atlas);
256
257 Rect position_rect = PerVertexDataPositionToRect(data.begin());
258 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
259 // The values at Point(0, 0).
260 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
261 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
262 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
263 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
264}
265
266TEST_P(TextContentsTest, SimpleSubpixel3x) {
267#ifndef FML_OS_MACOSX
268 GTEST_SKIP() << "Results aren't stable across linux and macos.";
269#endif
270
271 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
272
273 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
274 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
275
276 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
277 std::shared_ptr<GlyphAtlasContext> atlas_context =
278 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
279 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
280 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
281 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
282 ASSERT_TRUE(context && context->IsValid());
283 Rational font_scale(3, 1);
284 Point offset = {0.16667, 0};
285 std::shared_ptr<GlyphAtlas> atlas =
286 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
288 atlas_context, text_frame, offset);
289
290 ISize texture_size = atlas->GetTexture()->GetSize();
292 data.data(), text_frame, static_cast<Scalar>(font_scale),
293 /*entity_transform=*/
295 Matrix::MakeScale({static_cast<Scalar>(font_scale),
296 static_cast<Scalar>(font_scale), 1}),
297 offset,
298 /*glyph_properties=*/std::nullopt, atlas);
299
300 Rect position_rect = PerVertexDataPositionToRect(data.begin());
301 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
302 // Values at Point(0, 0)
303 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -121, 152, 152));
304 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 152, 152));
305 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -121, 154, 152))
306 << "position size:" << position_rect.GetSize();
307 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 154, 152))
308 << "position size:" << position_rect.GetSize();
309}
310
311TEST_P(TextContentsTest, SimpleSubpixel26) {
312#ifndef FML_OS_MACOSX
313 GTEST_SKIP() << "Results aren't stable across linux and macos.";
314#endif
315
316 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
317
318 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
319 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
320
321 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
322 std::shared_ptr<GlyphAtlasContext> atlas_context =
323 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
324 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
325 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
326 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
327 ASSERT_TRUE(context && context->IsValid());
328 Point offset = Point(0.26, 0);
329 std::shared_ptr<GlyphAtlas> atlas =
330 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
332 atlas_context, text_frame, offset);
333
334 ISize texture_size = atlas->GetTexture()->GetSize();
336 data.data(), text_frame, /*scale=*/1.0,
337 /*entity_transform=*/Matrix::MakeTranslation(offset), offset,
338 /*glyph_properties=*/std::nullopt, atlas);
339
340 Rect position_rect = PerVertexDataPositionToRect(data.begin());
341 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
342 // The values without subpixel.
343 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
344 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
345 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
346 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
347}
348
349TEST_P(TextContentsTest, SimpleSubpixel80) {
350#ifndef FML_OS_MACOSX
351 GTEST_SKIP() << "Results aren't stable across linux and macos.";
352#endif
353
354 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
355
356 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
357 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
358
359 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
360 std::shared_ptr<GlyphAtlasContext> atlas_context =
361 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
362 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
363 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
364 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
365 ASSERT_TRUE(context && context->IsValid());
366 Point offset = Point(0.80, 0);
367 std::shared_ptr<GlyphAtlas> atlas =
368 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
370 atlas_context, text_frame, offset);
371
372 ISize texture_size = atlas->GetTexture()->GetSize();
374 data.data(), text_frame, /*scale=*/1.0,
375 /*entity_transform=*/Matrix::MakeTranslation(offset), offset,
376 /*glyph_properties=*/std::nullopt, atlas);
377
378 Rect position_rect = PerVertexDataPositionToRect(data.begin());
379 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
380 // The values without subpixel.
381 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
382 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
383 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
384 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
385}
386
387} // namespace testing
388} // namespace impeller
GLenum type
Type
Describes how the glyphs are represented in the texture.
Definition glyph_atlas.h:41
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator, const std::shared_ptr< const IdleWaiter > &idle_waiter, size_t minimum_uniform_alignment)
static void ComputeVertexData(GlyphAtlasPipeline::VertexShader::PerVertexData *vtx_contents, const std::shared_ptr< TextFrame > &frame, Scalar scale, const Matrix &entity_transform, Vector2 offset, std::optional< GlyphProperties > glyph_properties, const std::shared_ptr< GlyphAtlas > &atlas)
static Rational RoundScaledFontSize(Scalar scale)
Definition text_frame.cc:55
static std::shared_ptr< TypographerContext > Make()
#define EXPECT_RECT_NEAR(a, b)
std::u16string text
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
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
TEST_P(AiksTest, DrawAtlasNoColor)
static std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, const TypographerContext *typographer_context, HostBuffer &data_host_buffer, GlyphAtlas::Type type, Rational scale, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::shared_ptr< TextFrame > &frame)
Point Vector2
Definition point.h:331
float Scalar
Definition scalar.h:19
TRect< Scalar > Rect
Definition rect.h:788
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
ISize64 ISize
Definition size.h:162
sk_sp< SkFontMgr > GetDefaultFontManager(uint32_t font_initialization_data)
Definition platform.cc:17
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
Scalar font_size
bool is_subpixel