Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
typographer_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
7#include "gtest/gtest.h"
16#include "third_party/skia/include/core/SkFont.h"
17#include "third_party/skia/include/core/SkFontMgr.h"
18#include "third_party/skia/include/core/SkRect.h"
19#include "third_party/skia/include/core/SkTextBlob.h"
20#include "third_party/skia/include/core/SkTypeface.h"
21#include "txt/platform.h"
22
23// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
24// NOLINTBEGIN(bugprone-unchecked-optional-access)
25
26namespace impeller {
27namespace testing {
28
31
32static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
33 Context& context,
34 const TypographerContext* typographer_context,
35 HostBuffer& data_host_buffer,
37 const Matrix& transform,
38 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
39 const std::shared_ptr<TextFrame>& frame) {
40 RenderableText render_frame{
41 .text_frame = frame,
42 .origin_transform = transform,
43 .properties = GlyphProperties{},
44 };
45 return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
46 atlas_context, {render_frame});
47}
48
49static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
50 Context& context,
51 const TypographerContext* typographer_context,
52 HostBuffer& data_host_buffer,
54 const Matrix& transform,
55 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
56 const std::vector<std::shared_ptr<TextFrame>>& frames,
57 const std::vector<GlyphProperties>& properties) {
58 size_t offset = 0;
59 std::vector<RenderableText> render_frames;
60 render_frames.reserve(frames.size());
61 for (auto& frame : frames) {
62 render_frames.emplace_back(frame, transform, properties[offset++]);
63 }
64 return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
65 atlas_context, render_frames);
66}
67
68TEST_P(TypographerTest, CanConvertTextBlob) {
70 auto blob = SkTextBlob::MakeFromString(
71 "the quick brown fox jumped over the lazy dog.", font);
72 ASSERT_TRUE(blob);
73 auto frame = MakeTextFrameFromTextBlobSkia(blob);
74 ASSERT_EQ(frame->GetRunCount(), 1u);
75 for (const auto& run : frame->GetRuns()) {
76 ASSERT_TRUE(run.IsValid());
77 ASSERT_EQ(run.GetGlyphCount(), 45u);
78 }
79}
80
81TEST_P(TypographerTest, CanCreateRenderContext) {
82 auto context = TypographerContextSkia::Make();
83 ASSERT_TRUE(context && context->IsValid());
84}
85
86TEST_P(TypographerTest, CanCreateGlyphAtlas) {
87 auto context = TypographerContextSkia::Make();
88 auto atlas_context =
89 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
90 auto data_host_buffer = HostBuffer::Create(
91 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
92 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
93 ASSERT_TRUE(context && context->IsValid());
94 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
95 auto blob = SkTextBlob::MakeFromString("hello", sk_font);
96 ASSERT_TRUE(blob);
97 auto atlas =
98 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
99 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
101
102 ASSERT_NE(atlas, nullptr);
103 ASSERT_NE(atlas->GetTexture(), nullptr);
104 ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
105 ASSERT_EQ(atlas->GetGlyphCount(), 4llu);
106
107 std::optional<impeller::ScaledFont> first_scaled_font;
108 std::optional<impeller::SubpixelGlyph> first_glyph;
109 Rect first_rect;
110 atlas->IterateGlyphs([&](const ScaledFont& scaled_font,
111 const SubpixelGlyph& glyph,
112 const Rect& rect) -> bool {
113 first_scaled_font = scaled_font;
114 first_glyph = glyph;
115 first_rect = rect;
116 return false;
117 });
118
119 ASSERT_TRUE(first_scaled_font.has_value());
120 ASSERT_TRUE(atlas
121 ->FindFontGlyphBounds(
122 {first_scaled_font.value(), first_glyph.value()})
123 .has_value());
124}
125
126TEST_P(TypographerTest, LazyAtlasTracksColor) {
127 auto data_host_buffer = HostBuffer::Create(
128 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
129 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
130#if FML_OS_MACOSX
131 auto mapping = flutter::testing::OpenFixtureAsSkData("Apple Color Emoji.ttc");
132#else
133 auto mapping = flutter::testing::OpenFixtureAsSkData("NotoColorEmoji.ttf");
134#endif
135 ASSERT_TRUE(mapping);
136 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
137 SkFont emoji_font(font_mgr->makeFromData(mapping), 50.0);
138 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
139
140 auto blob = SkTextBlob::MakeFromString("hello", sk_font);
141 ASSERT_TRUE(blob);
142 auto frame = MakeTextFrameFromTextBlobSkia(blob);
143
144 ASSERT_FALSE(frame->GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
145
147
148 lazy_atlas.AddTextFrame(frame, {0, 0}, Matrix(), {});
149
151 SkTextBlob::MakeFromString("😀 ", emoji_font));
152
153 ASSERT_TRUE(frame->GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
154
155 lazy_atlas.AddTextFrame(frame, {0, 0}, Matrix(), {});
156
157 // Creates different atlases for color and red bitmap.
158 auto color_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
159 *GetContext(), *data_host_buffer, GlyphAtlas::Type::kColorBitmap);
160
161 auto bitmap_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
162 *GetContext(), *data_host_buffer, GlyphAtlas::Type::kAlphaBitmap);
163
164 ASSERT_FALSE(color_atlas == bitmap_atlas);
165}
166
167TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
168 auto context = TypographerContextSkia::Make();
169 auto atlas_context =
170 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
171 auto data_host_buffer = HostBuffer::Create(
172 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
173 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
174 ASSERT_TRUE(context && context->IsValid());
175 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
176 auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
177 ASSERT_TRUE(blob);
178 auto atlas =
179 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
180 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
182 ASSERT_NE(atlas, nullptr);
183 ASSERT_NE(atlas->GetTexture(), nullptr);
184
185 EXPECT_EQ(atlas->GetTexture()->GetSize().width, 4096u);
186 EXPECT_EQ(atlas->GetTexture()->GetSize().height, 1024u);
187}
188
189TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
190 auto context = TypographerContextSkia::Make();
191 auto atlas_context =
192 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
193 auto data_host_buffer = HostBuffer::Create(
194 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
195 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
196 ASSERT_TRUE(context && context->IsValid());
197 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
198 auto blob = SkTextBlob::MakeFromString("spooky skellingtons", sk_font);
199 ASSERT_TRUE(blob);
200 auto atlas =
201 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
202 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
204 ASSERT_NE(atlas, nullptr);
205 ASSERT_NE(atlas->GetTexture(), nullptr);
206 ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
207
208 // now attempt to re-create an atlas with the same text blob.
209
210 auto next_atlas =
211 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
212 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
214 ASSERT_EQ(atlas, next_atlas);
215 ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
216}
217
218TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
219 auto data_host_buffer = HostBuffer::Create(
220 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
221 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
222 auto context = TypographerContextSkia::Make();
223 auto atlas_context =
224 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
225 ASSERT_TRUE(context && context->IsValid());
226
227 const char* test_string =
228 "QWERTYUIOPASDFGHJKLZXCVBNMqewrtyuiopasdfghjklzxcvbnm,.<>[]{};':"
229 "2134567890-=!@#$%^&*()_+"
230 "œ∑´®†¥¨ˆøπ““‘‘åß∂ƒ©˙∆˚¬…æ≈ç√∫˜µ≤≥≥≥≥÷¡™£¢∞§¶•ªº–≠⁄€‹›fifl‡°·‚—±Œ„´‰Á¨Ø∏”’/"
231 "* Í˝ */¸˛Ç◊ı˜Â¯˘¿";
232
233 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
234 auto blob = SkTextBlob::MakeFromString(test_string, sk_font);
235 ASSERT_TRUE(blob);
236
237 size_t size_count = 8;
238 std::vector<RenderableText> render_frames;
239 for (size_t index = 0; index < size_count; index += 1) {
240 Scalar scale = 6.0f * index / 10.0f;
241 render_frames.emplace_back(MakeTextFrameFromTextBlobSkia(blob),
242 Matrix::MakeScale({scale, scale, 1.0f}),
244 };
245 auto atlas = context->CreateGlyphAtlas(
246 *GetContext(), GlyphAtlas::Type::kAlphaBitmap, *data_host_buffer,
247 atlas_context, render_frames);
248 ASSERT_NE(atlas, nullptr);
249 ASSERT_NE(atlas->GetTexture(), nullptr);
250
251 std::set<uint16_t> unique_glyphs;
252 std::vector<uint16_t> total_glyphs;
253 atlas->IterateGlyphs([&](const ScaledFont& scaled_font,
254 const SubpixelGlyph& glyph, const Rect& rect) {
255 unique_glyphs.insert(glyph.glyph.index);
256 total_glyphs.push_back(glyph.glyph.index);
257 return true;
258 });
259
260 // These numbers may be different due to subpixel positions.
261 EXPECT_LE(unique_glyphs.size() * size_count, atlas->GetGlyphCount());
262 EXPECT_EQ(total_glyphs.size(), atlas->GetGlyphCount());
263
264 EXPECT_TRUE(atlas->GetGlyphCount() > 0);
265 EXPECT_TRUE(atlas->GetTexture()->GetSize().width > 0);
266 EXPECT_TRUE(atlas->GetTexture()->GetSize().height > 0);
267}
268
269TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
270 auto data_host_buffer = HostBuffer::Create(
271 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
272 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
273 auto context = TypographerContextSkia::Make();
274 auto atlas_context =
275 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
276 ASSERT_TRUE(context && context->IsValid());
277 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
278 auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
279 ASSERT_TRUE(blob);
280 auto atlas =
281 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
282 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
284 auto old_packer = atlas_context->GetRectPacker();
285
286 ASSERT_NE(atlas, nullptr);
287 ASSERT_NE(atlas->GetTexture(), nullptr);
288 ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
289
290 auto* first_texture = atlas->GetTexture().get();
291
292 // Now create a new glyph atlas with a nearly identical blob.
293
294 auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
295 auto next_atlas =
296 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
297 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
299 ASSERT_EQ(atlas, next_atlas);
300 auto* second_texture = next_atlas->GetTexture().get();
301
302 auto new_packer = atlas_context->GetRectPacker();
303
304 ASSERT_EQ(second_texture, first_texture);
305 ASSERT_EQ(old_packer, new_packer);
306}
307
308TEST_P(TypographerTest, GlyphColorIsPartOfCacheKey) {
309 auto data_host_buffer = HostBuffer::Create(
310 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
311 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
312#if FML_OS_MACOSX
313 auto mapping = flutter::testing::OpenFixtureAsSkData("Apple Color Emoji.ttc");
314#else
315 auto mapping = flutter::testing::OpenFixtureAsSkData("NotoColorEmoji.ttf");
316#endif
317 ASSERT_TRUE(mapping);
318 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
319 SkFont emoji_font(font_mgr->makeFromData(mapping), 50.0);
320
321 auto context = TypographerContextSkia::Make();
322 auto atlas_context =
323 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kColorBitmap);
324
325 // Create two frames with the same character and a different color, expect
326 // that it adds a character.
328 SkTextBlob::MakeFromString("😂", emoji_font));
329 auto frame_2 = MakeTextFrameFromTextBlobSkia(
330 SkTextBlob::MakeFromString("😂", emoji_font));
331 std::vector<GlyphProperties> properties = {
334 };
335
336 auto next_atlas =
337 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
338 GlyphAtlas::Type::kColorBitmap, Matrix(), atlas_context,
339 {frame, frame_2}, properties);
340
341 EXPECT_EQ(next_atlas->GetGlyphCount(), 2u);
342}
343
344TEST_P(TypographerTest, GlyphColorIsIgnoredForNonEmojiFonts) {
345 auto data_host_buffer = HostBuffer::Create(
346 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
347 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
348 sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
349 sk_sp<SkTypeface> typeface =
350 font_mgr->matchFamilyStyle("Arial", SkFontStyle::Normal());
351 SkFont sk_font(typeface, 0.5f);
352
353 auto context = TypographerContextSkia::Make();
354 auto atlas_context =
355 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kColorBitmap);
356
357 // Create two frames with the same character and a different color, but as a
358 // non-emoji font the text frame constructor will ignore it.
359 auto frame =
360 MakeTextFrameFromTextBlobSkia(SkTextBlob::MakeFromString("A", sk_font));
361 auto frame_2 =
362 MakeTextFrameFromTextBlobSkia(SkTextBlob::MakeFromString("A", sk_font));
363 std::vector<GlyphProperties> properties = {
366 };
367
368 auto next_atlas =
369 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
370 GlyphAtlas::Type::kColorBitmap, Matrix(), atlas_context,
371 {frame, frame_2}, properties);
372
373 EXPECT_EQ(next_atlas->GetGlyphCount(), 1u);
374}
375
376TEST_P(TypographerTest, GlyphIsLightIsPartOfCacheKey) {
377 auto data_host_buffer = HostBuffer::Create(
378 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
379 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
380 auto context = TypographerContextSkia::Make();
381 auto atlas_context =
382 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
383
384 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
385 auto blob1 = SkTextBlob::MakeFromString("A", sk_font);
386 auto blob2 = SkTextBlob::MakeFromString("A", sk_font);
387 auto frame1 = MakeTextFrameFromTextBlobSkia(blob1);
388 auto frame2 = MakeTextFrameFromTextBlobSkia(blob2);
389
390 std::vector<GlyphProperties> properties = {
393 };
394 // One light color, one dark color
396 properties[1].tone_or_color = GlyphProperties::ComputeTone(Color::Crimson());
397
398 auto next_atlas =
399 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
400 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
401 {frame1, frame2}, properties);
402
403#if defined(FML_OS_MACOSX)
404 // On Apple platforms, they should be separated into 2 entries
405 EXPECT_EQ(next_atlas->GetGlyphCount(), 2u);
406#else
407 // On other platforms, both are classified as "dark" (false) and share 1 entry
408 EXPECT_EQ(next_atlas->GetGlyphCount(), 1u);
409#endif
410}
411
412TEST_P(TypographerTest, RectanglePackerAddsNonoverlapingRectangles) {
413 auto packer = RectanglePacker::Factory(200, 100);
414 ASSERT_NE(packer, nullptr);
415 ASSERT_EQ(packer->PercentFull(), 0);
416
417 const SkIRect packer_area = SkIRect::MakeXYWH(0, 0, 200, 100);
418
419 IPoint16 first_output = {-1, -1}; // Fill with sentinel values
420 ASSERT_TRUE(packer->AddRect(20, 20, &first_output));
421 // Make sure the rectangle is placed such that it is inside the bounds of
422 // the packer's area.
423 const SkIRect first_rect =
424 SkIRect::MakeXYWH(first_output.x(), first_output.y(), 20, 20);
425 ASSERT_TRUE(SkIRect::Intersects(packer_area, first_rect));
426
427 // Initial area was 200 x 100 = 20_000
428 // We added 20x20 = 400. 400 / 20_000 == 0.02 == 2%
429 ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.02));
430
431 IPoint16 second_output = {-1, -1};
432 ASSERT_TRUE(packer->AddRect(140, 90, &second_output));
433 const SkIRect second_rect =
434 SkIRect::MakeXYWH(second_output.x(), second_output.y(), 140, 90);
435 // Make sure the rectangle is placed such that it is inside the bounds of
436 // the packer's area but not in the are of the first rectangle.
437 ASSERT_TRUE(SkIRect::Intersects(packer_area, second_rect));
438 ASSERT_FALSE(SkIRect::Intersects(first_rect, second_rect));
439
440 // We added another 90 x 140 = 12_600 units, now taking us to 13_000
441 // 13_000 / 20_000 == 0.65 == 65%
442 ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.65));
443
444 // There's enough area to add this rectangle, but no space big enough for
445 // the 50 units of width.
446 IPoint16 output;
447 ASSERT_FALSE(packer->AddRect(50, 50, &output));
448 // Should be unchanged.
449 ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.65));
450
451 packer->Reset();
452 // Should be empty now.
453 ASSERT_EQ(packer->PercentFull(), 0);
454}
455
456TEST(TypographerTest, RectanglePackerFillsRows) {
457 auto skyline = RectanglePacker::Factory(257, 256);
458
459 // Fill up the first row.
460 IPoint16 loc;
461 for (auto i = 0u; i < 16; i++) {
462 skyline->AddRect(16, 16, &loc);
463 }
464 // Last rectangle still in first row.
465 EXPECT_EQ(loc.x(), 256 - 16);
466 EXPECT_EQ(loc.y(), 0);
467
468 // Fill up second row.
469 for (auto i = 0u; i < 16; i++) {
470 skyline->AddRect(16, 16, &loc);
471 }
472
473 EXPECT_EQ(loc.x(), 256 - 16);
474 EXPECT_EQ(loc.y(), 16);
475}
476
477TEST_P(TypographerTest, GlyphAtlasTextureWillGrowTilMaxTextureSize) {
478 if (GetBackend() == PlaygroundBackend::kOpenGLES) {
479 GTEST_SKIP() << "Atlas growth isn't supported for OpenGLES currently.";
480 }
481
482 auto data_host_buffer = HostBuffer::Create(
483 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
484 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
485 auto context = TypographerContextSkia::Make();
486 auto atlas_context =
487 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
488 ASSERT_TRUE(context && context->IsValid());
489 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
490 auto blob = SkTextBlob::MakeFromString("A", sk_font);
491 ASSERT_TRUE(blob);
492 auto atlas =
493 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
494 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
496 // Continually append new glyphs until the glyph size grows to the maximum.
497 // Note that the sizes here are more or less experimentally determined, but
498 // the important expectation is that the atlas size will shrink again after
499 // growing to the maximum size.
500 constexpr ISize expected_sizes[13] = {
501 {4096, 4096}, //
502 {4096, 4096}, //
503 {4096, 8192}, //
504 {4096, 8192}, //
505 {4096, 8192}, //
506 {4096, 8192}, //
507 {4096, 16384}, //
508 {4096, 16384}, //
509 {4096, 16384}, //
510 {4096, 16384}, //
511 {4096, 16384}, //
512 {4096, 16384}, //
513 {4096, 4096} // Shrinks!
514 };
515
516 SkFont sk_font_small = flutter::testing::CreateTestFontOfSize(10);
517
518 for (int i = 0; i < 13; i++) {
519 SkTextBlobBuilder builder;
520
521 auto add_char = [&](const SkFont& sk_font, char c) {
522 int count = sk_font.countText(&c, 1, SkTextEncoding::kUTF8);
523 auto buffer = builder.allocRunPos(sk_font, count);
524 sk_font.textToGlyphs(&c, 1, SkTextEncoding::kUTF8,
525 {buffer.glyphs, count});
526 sk_font.getPos({buffer.glyphs, count}, {buffer.points(), count},
527 {0, 0} /*=origin*/);
528 };
529
530 SkFont sk_font = flutter::testing::CreateTestFontOfSize(50 + i);
531 add_char(sk_font, 'A');
532 add_char(sk_font_small, 'B');
533 auto blob = builder.make();
534
535 Matrix transform = Matrix::MakeScale({50.0f + i, 50.0f + i, 1.0f});
536 atlas =
537 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
539 atlas_context, MakeTextFrameFromTextBlobSkia(blob));
540 ASSERT_TRUE(!!atlas);
541 EXPECT_EQ(atlas->GetTexture()->GetTextureDescriptor().size,
542 expected_sizes[i]);
543 }
544
545 // The final atlas should contain both the "A" glyph (which was not present
546 // in the previous atlas) and the "B" glyph (which existed in the previous
547 // atlas).
548 ASSERT_EQ(atlas->GetGlyphCount(), 2u);
549}
550
551TEST_P(TypographerTest, InvalidAtlasForcesRepopulation) {
553 auto blob = SkTextBlob::MakeFromString(
554 "the quick brown fox jumped over the lazy dog.", font);
555 ASSERT_TRUE(blob);
556 auto frame = MakeTextFrameFromTextBlobSkia(blob);
557
558 auto context = TypographerContextSkia::Make();
559 auto atlas_context =
560 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
561 auto data_host_buffer = HostBuffer::Create(
562 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
563 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
564
565 auto atlas = CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
567 atlas_context, frame);
568
569 auto second_context = TypographerContextSkia::Make();
570 auto second_atlas_context =
571 second_context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
572
573 EXPECT_FALSE(second_atlas_context->GetGlyphAtlas()->IsValid());
574
575 atlas = CreateGlyphAtlas(*GetContext(), second_context.get(),
576 *data_host_buffer, GlyphAtlas::Type::kAlphaBitmap,
577 Matrix(), second_atlas_context, frame);
578
579 EXPECT_TRUE(second_atlas_context->GetGlyphAtlas()->IsValid());
580}
581
582TEST_P(TypographerTest, ColorBitmapAtlasSkiaBitmapIsRGBA8888) {
583 auto context = TypographerContextSkia::Make();
584 auto atlas_context =
585 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kColorBitmap);
586 auto data_host_buffer = HostBuffer::Create(
587 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
588 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
589 ASSERT_TRUE(context && context->IsValid());
590 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
591 auto blob = SkTextBlob::MakeFromString("A", sk_font);
592 ASSERT_TRUE(blob);
593 auto atlas =
594 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
595 GlyphAtlas::Type::kColorBitmap, Matrix(), atlas_context,
597
598 SkImageInfo image_info = TypographerContextSkia::GetImageInfo(
599 *atlas, Size{100, 100}, /*support_light_glyphs=*/false);
600 EXPECT_EQ(image_info.colorType(), kRGBA_8888_SkColorType);
601}
602
603TEST_P(TypographerTest, AlphaBitmapAtlasSkiaBitmapIsA8) {
604 auto context = TypographerContextSkia::Make();
605 auto atlas_context =
606 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
607 auto data_host_buffer = HostBuffer::Create(
608 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
609 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
610 ASSERT_TRUE(context && context->IsValid());
611 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
612 auto blob = SkTextBlob::MakeFromString("A", sk_font);
613 ASSERT_TRUE(blob);
614 auto atlas =
615 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
616 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
618
619 SkImageInfo image_info = TypographerContextSkia::GetImageInfo(
620 *atlas, Size{100, 100}, /*support_light_glyphs=*/false);
621 EXPECT_EQ(image_info.colorType(), kAlpha_8_SkColorType);
622}
623
624TEST_P(TypographerTest, AlphaBitmapAtlasWithLightGlyphsSkiaBitmapIsRGBA8888) {
625 auto context = TypographerContextSkia::Make();
626 auto atlas_context =
627 context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
628 auto data_host_buffer = HostBuffer::Create(
629 GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
630 GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
631 ASSERT_TRUE(context && context->IsValid());
632 SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
633 auto blob = SkTextBlob::MakeFromString("A", sk_font);
634 ASSERT_TRUE(blob);
635 auto atlas =
636 CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
637 GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
639
640 SkImageInfo image_info = TypographerContextSkia::GetImageInfo(
641 *atlas, Size{100, 100}, /*support_light_glyphs=*/true);
642 EXPECT_EQ(image_info.colorType(), kRGBA_8888_SkColorType);
643}
644
645} // namespace testing
646} // namespace impeller
647
648// NOLINTEND(bugprone-unchecked-optional-access)
To do anything rendering related with Impeller, you need a context.
Definition context.h:69
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)
void AddTextFrame(const std::shared_ptr< TextFrame > &frame, Point position, const Matrix &transform, const GlyphProperties &properties)
const std::shared_ptr< GlyphAtlas > & CreateOrGetGlyphAtlas(Context &context, HostBuffer &host_buffer, GlyphAtlas::Type type)
static std::shared_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
The graphics context necessary to render text.
virtual std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, GlyphAtlas::Type type, HostBuffer &host_buffer, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< RenderableText > &text_frames) const =0
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size, bool support_light_glyphs)
static std::shared_ptr< TypographerContext > Make()
SkFont CreateTestFontOfSize(DlScalar scalar)
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
bool NumberNear(double a, double b)
Definition assertions.h:12
TEST(FrameTimingsRecorderTest, RecordVsync)
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
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
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
sk_sp< SkFontMgr > GetDefaultFontManager(uint32_t font_initialization_data)
Definition platform.cc:17
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
impeller::ShaderType type
static constexpr Color Crimson()
Definition color.h:355
static constexpr Color Beige()
Definition color.h:307
static constexpr Color Red()
Definition color.h:277
static constexpr Color Blue()
Definition color.h:281
uint16_t index
Definition glyph.h:22
static Tone ComputeTone(const Color &c)
int16_t y() const
int16_t x() const
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
const std::shared_ptr< TextFrame > text_frame
The TextFrame being rendered.
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
A glyph and its subpixel position.