Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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 const Matrix& transform,
58 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
59 const std::shared_ptr<TextFrame>& frame,
60 Point offset) {
61 RenderableText render_frame{
62 .text_frame = frame,
63 .origin_transform = transform * Matrix::MakeTranslation(offset),
64 };
65 return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
66 atlas_context, {render_frame});
67}
68
69Rect PerVertexDataPositionToRect(
70 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData>::iterator
71 data) {
72 Scalar right = FLT_MIN;
73 Scalar left = FLT_MAX;
74 Scalar top = FLT_MAX;
75 Scalar bottom = FLT_MIN;
76 for (int i = 0; i < 4; ++i) {
77 right = std::max(right, data[i].position.x);
78 left = std::min(left, data[i].position.x);
79 top = std::min(top, data[i].position.y);
80 bottom = std::max(bottom, data[i].position.y);
81 }
82
83 return Rect::MakeLTRB(left, top, right, bottom);
84}
85
86Rect PerVertexDataUVToRect(
87 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData>::iterator data,
88 ISize texture_size) {
89 Scalar right = FLT_MIN;
90 Scalar left = FLT_MAX;
91 Scalar top = FLT_MAX;
92 Scalar bottom = FLT_MIN;
93 for (int i = 0; i < 4; ++i) {
94 right = std::max(right, data[i].uv.x * texture_size.width);
95 left = std::min(left, data[i].uv.x * texture_size.width);
96 top = std::min(top, data[i].uv.y * texture_size.height);
97 bottom = std::max(bottom, data[i].uv.y * texture_size.height);
98 }
99
100 return Rect::MakeLTRB(left, top, right, bottom);
101}
102
103double GetAspectRatio(Rect rect) {
104 return static_cast<double>(rect.GetWidth()) / rect.GetHeight();
105}
106} // namespace
107
108TEST_P(TextContentsTest, SimpleComputeVertexData) {
109#ifndef FML_OS_MACOSX
110 GTEST_SKIP() << "Results aren't stable across linux and macos.";
111#endif
112
113 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
114
115 std::shared_ptr<TextFrame> text_frame =
116 MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50});
117
118 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
119 std::shared_ptr<GlyphAtlasContext> atlas_context =
120 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
121 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
122 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
123 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
124 ASSERT_TRUE(context && context->IsValid());
125 std::shared_ptr<GlyphAtlas> atlas =
126 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
127 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
128 text_frame, /*offset=*/{0, 0});
129
130 ISize texture_size = atlas->GetTexture()->GetSize();
132 /*entity_transform=*/Matrix(),
133 /*frame=*/text_frame,
134 /*position=*/Point(0, 0),
135 /*screen_transform=*/Matrix(),
136 /*glyph_properties=*/GlyphProperties{},
137 /*atlas=*/atlas);
138
139 Rect position_rect = PerVertexDataPositionToRect(data.begin());
140 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
141 // The -1 offset comes from Skia in `ComputeGlyphSize`. So since the font size
142 // is 50, the math appears to be to get back a 50x50 rect and apply 1 pixel
143 // of padding.
144 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
145 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
146}
147
148TEST_P(TextContentsTest, SimpleComputeVertexData2x) {
149#ifndef FML_OS_MACOSX
150 GTEST_SKIP() << "Results aren't stable across linux and macos.";
151#endif
152
153 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
154 std::shared_ptr<TextFrame> text_frame =
155 MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50});
156
157 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
158 std::shared_ptr<GlyphAtlasContext> atlas_context =
159 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
160 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
161 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
162 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
163 ASSERT_TRUE(context && context->IsValid());
164 Matrix render_transform = Matrix::MakeScale({2.0f, 2.0f, 1.0f});
165 std::shared_ptr<GlyphAtlas> atlas =
166 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
167 GlyphAtlas::Type::kAlphaBitmap, render_transform,
168 atlas_context, text_frame, /*offset=*/{0, 0});
169
170 ISize texture_size = atlas->GetTexture()->GetSize();
172 /*vtx_contents=*/data.data(),
173 /*entity_transform=*/render_transform,
174 /*frame=*/text_frame,
175 /*position=*/Point(0, 0),
176 /*screen_transform=*/render_transform,
177 /*glyph_properties=*/GlyphProperties{},
178 /*atlas=*/atlas);
179
180 Rect position_rect = PerVertexDataPositionToRect(data.begin());
181 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
182 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -81, 102, 102));
183 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 102, 102));
184}
185
186TEST_P(TextContentsTest, MaintainsShape) {
187 std::shared_ptr<TextFrame> text_frame =
188 MakeTextFrame("th", "ahem.ttf", TextOptions{.font_size = 50});
189
190 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
191 std::shared_ptr<GlyphAtlasContext> atlas_context =
192 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
193 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
194 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
195 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
196 ASSERT_TRUE(context && context->IsValid());
197
198 for (int i = 0; i <= 1000; ++i) {
199 Scalar scale = (440.0f + i) / 1000.0f;
200 Matrix transform = Matrix::MakeScale({scale, scale, 1.0f});
201 Rect position_rect[2];
202 Rect uv_rect[2];
203
204 {
205 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(12);
206
207 std::shared_ptr<GlyphAtlas> atlas =
208 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
210 atlas_context, text_frame, /*offset=*/{0, 0});
211 ISize texture_size = atlas->GetTexture()->GetSize();
212
214 /*vtx_contents=*/data.data(),
215 /*entity_transform=*/transform,
216 /*frame=*/text_frame,
217 /*position=*/Point(0, 0),
218 /*screen_transform=*/transform,
219 /*glyph_properties=*/GlyphProperties{},
220 /*atlas=*/atlas);
221 position_rect[0] = PerVertexDataPositionToRect(data.begin());
222 uv_rect[0] = PerVertexDataUVToRect(data.begin(), texture_size);
223 position_rect[1] = PerVertexDataPositionToRect(data.begin() + 4);
224 uv_rect[1] = PerVertexDataUVToRect(data.begin() + 4, texture_size);
225 }
226 EXPECT_NEAR(GetAspectRatio(position_rect[1]), GetAspectRatio(uv_rect[1]),
227 0.001)
228 << i;
229 }
230}
231
232TEST_P(TextContentsTest, SimpleSubpixel) {
233#ifndef FML_OS_MACOSX
234 GTEST_SKIP() << "Results aren't stable across linux and macos.";
235#endif
236
237 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
238
239 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
240 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
241
242 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
243 std::shared_ptr<GlyphAtlasContext> atlas_context =
244 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
245 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
246 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
247 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
248 ASSERT_TRUE(context && context->IsValid());
249 Point offset = Point(0.5, 0);
250 std::shared_ptr<GlyphAtlas> atlas =
251 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
252 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
253 text_frame, offset);
254
255 ISize texture_size = atlas->GetTexture()->GetSize();
257 /*vtx_contents=*/data.data(),
258 /*entity_transform=*/Matrix(),
259 /*frame=*/text_frame,
260 /*position=*/offset,
261 /*screen_transform=*/Matrix(),
262 /*glyph_properties=*/GlyphProperties{},
263 /*atlas=*/atlas);
264
265 Rect position_rect = PerVertexDataPositionToRect(data.begin());
266 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
267 // The values at Point(0, 0).
268 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
269 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
270 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
271 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
272}
273
274TEST_P(TextContentsTest, SimpleSubpixel3x) {
275#ifndef FML_OS_MACOSX
276 GTEST_SKIP() << "Results aren't stable across linux and macos.";
277#endif
278
279 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
280
281 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
282 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
283
284 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
285 std::shared_ptr<GlyphAtlasContext> atlas_context =
286 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
287 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
288 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
289 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
290 ASSERT_TRUE(context && context->IsValid());
291 Matrix transform = Matrix::MakeScale({3.0f, 3.0f, 1.0f});
292 Point offset = {0.16667, 0};
293 std::shared_ptr<GlyphAtlas> atlas =
294 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
296 text_frame, offset);
297
298 ISize texture_size = atlas->GetTexture()->GetSize();
300 /*vtx_contents=*/data.data(),
301 /*entity_transform=*/transform,
302 /*frame=*/text_frame,
303 /*position=*/offset,
304 /*screen_transform=*/transform,
305 /*glyph_properties=*/GlyphProperties{},
306 /*atlas=*/atlas);
307
308 Rect position_rect = PerVertexDataPositionToRect(data.begin());
309 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
310 // Values at Point(0, 0)
311 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -121, 152, 152));
312 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 152, 152));
313 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -121, 154, 152))
314 << "position size:" << position_rect.GetSize();
315 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 154, 152))
316 << "position size:" << position_rect.GetSize();
317}
318
319TEST_P(TextContentsTest, SimpleSubpixel26) {
320#ifndef FML_OS_MACOSX
321 GTEST_SKIP() << "Results aren't stable across linux and macos.";
322#endif
323
324 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
325
326 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
327 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
328
329 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
330 std::shared_ptr<GlyphAtlasContext> atlas_context =
331 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
332 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
333 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
334 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
335 ASSERT_TRUE(context && context->IsValid());
336 Point offset = Point(0.26, 0);
337 std::shared_ptr<GlyphAtlas> atlas =
338 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
339 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
340 text_frame, offset);
341
342 ISize texture_size = atlas->GetTexture()->GetSize();
344 /*vtx_contents=*/data.data(),
345 /*entity_transform=*/Matrix(),
346 /*frame=*/text_frame,
347 /*position=*/offset,
348 /*screen_transform=*/Matrix(),
349 /*glyph_properties=*/GlyphProperties{},
350 /*atlas=*/atlas);
351
352 Rect position_rect = PerVertexDataPositionToRect(data.begin());
353 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
354 // The values without subpixel.
355 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
356 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
357 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
358 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
359}
360
361TEST_P(TextContentsTest, SimpleSubpixel80) {
362#ifndef FML_OS_MACOSX
363 GTEST_SKIP() << "Results aren't stable across linux and macos.";
364#endif
365
366 std::vector<GlyphAtlasPipeline::VertexShader::PerVertexData> data(4);
367
368 std::shared_ptr<TextFrame> text_frame = MakeTextFrame(
369 "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true});
370
371 std::shared_ptr<TypographerContext> context = TypographerContextSkia::Make();
372 std::shared_ptr<GlyphAtlasContext> atlas_context =
373 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
374 std::shared_ptr<HostBuffer> data_host_buffer = HostBuffer::Create(
375 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
376 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
377 ASSERT_TRUE(context && context->IsValid());
378 Point offset = Point(0.80, 0);
379 std::shared_ptr<GlyphAtlas> atlas =
380 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
381 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
382 text_frame, offset);
383
384 ISize texture_size = atlas->GetTexture()->GetSize();
386 /*vtx_contents=*/data.data(),
387 /*entity_transform=*/Matrix(),
388 /*frame=*/text_frame,
389 /*position=*/offset,
390 /*screen_transform=*/Matrix(),
391 /*glyph_properties=*/GlyphProperties{},
392 /*atlas=*/atlas);
393
394 Rect position_rect = PerVertexDataPositionToRect(data.begin());
395 Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size);
396 // The values without subpixel.
397 // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52));
398 // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));
399 EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-2, -41, 54, 52));
400 EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 54, 52));
401}
402
403} // namespace testing
404} // namespace impeller
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 Matrix &entity_transform, const std::shared_ptr< TextFrame > &frame, Point position, const Matrix &screen_transform, GlyphProperties glyph_properties, const std::shared_ptr< GlyphAtlas > &atlas)
Computes the vertex data for the render operation from a collection of data drawn from the DrawTextFr...
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
static std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, const TypographerContext *typographer_context, HostBuffer &data_host_buffer, GlyphAtlas::Type type, const Matrix &transform, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::shared_ptr< TextFrame > &frame)
TEST_P(AiksTest, DrawAtlasNoColor)
float Scalar
Definition scalar.h:19
TRect< Scalar > Rect
Definition rect.h:822
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)
impeller::ShaderType type
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