Flutter Impeller
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 
5 #include "flutter/display_list/testing/dl_test_snippets.h"
6 #include "flutter/testing/testing.h"
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 
26 namespace impeller {
27 namespace testing {
28 
31 
32 static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
33  Context& context,
34  const TypographerContext* typographer_context,
35  HostBuffer& data_host_buffer,
36  GlyphAtlas::Type type,
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  };
44  return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
45  atlas_context, {render_frame});
46 }
47 
48 static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
49  Context& context,
50  const TypographerContext* typographer_context,
51  HostBuffer& data_host_buffer,
52  GlyphAtlas::Type type,
53  const Matrix& transform,
54  const std::shared_ptr<GlyphAtlasContext>& atlas_context,
55  const std::vector<std::shared_ptr<TextFrame>>& frames,
56  const std::vector<std::optional<GlyphProperties>>& properties) {
57  size_t offset = 0;
58  std::vector<RenderableText> render_frames;
59  render_frames.reserve(frames.size());
60  for (auto& frame : frames) {
61  render_frames.emplace_back(frame, transform, properties[offset++]);
62  }
63  return typographer_context->CreateGlyphAtlas(context, type, data_host_buffer,
64  atlas_context, render_frames);
65 }
66 
67 TEST_P(TypographerTest, CanConvertTextBlob) {
68  SkFont font = flutter::testing::CreateTestFontOfSize(12);
69  auto blob = SkTextBlob::MakeFromString(
70  "the quick brown fox jumped over the lazy dog.", font);
71  ASSERT_TRUE(blob);
72  auto frame = MakeTextFrameFromTextBlobSkia(blob);
73  ASSERT_EQ(frame->GetRunCount(), 1u);
74  for (const auto& run : frame->GetRuns()) {
75  ASSERT_TRUE(run.IsValid());
76  ASSERT_EQ(run.GetGlyphCount(), 45u);
77  }
78 }
79 
80 TEST_P(TypographerTest, CanCreateRenderContext) {
81  auto context = TypographerContextSkia::Make();
82  ASSERT_TRUE(context && context->IsValid());
83 }
84 
85 TEST_P(TypographerTest, CanCreateGlyphAtlas) {
86  auto context = TypographerContextSkia::Make();
87  auto atlas_context =
88  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
89  auto data_host_buffer = HostBuffer::Create(
90  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
91  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
92  ASSERT_TRUE(context && context->IsValid());
93  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
94  auto blob = SkTextBlob::MakeFromString("hello", sk_font);
95  ASSERT_TRUE(blob);
96  auto atlas =
97  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
98  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
100 
101  ASSERT_NE(atlas, nullptr);
102  ASSERT_NE(atlas->GetTexture(), nullptr);
103  ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
104  ASSERT_EQ(atlas->GetGlyphCount(), 4llu);
105 
106  std::optional<impeller::ScaledFont> first_scaled_font;
107  std::optional<impeller::SubpixelGlyph> first_glyph;
108  Rect first_rect;
109  atlas->IterateGlyphs([&](const ScaledFont& scaled_font,
110  const SubpixelGlyph& glyph,
111  const Rect& rect) -> bool {
112  first_scaled_font = scaled_font;
113  first_glyph = glyph;
114  first_rect = rect;
115  return false;
116  });
117 
118  ASSERT_TRUE(first_scaled_font.has_value());
119  ASSERT_TRUE(atlas
120  ->FindFontGlyphBounds(
121  {first_scaled_font.value(), first_glyph.value()})
122  .has_value());
123 }
124 
125 TEST_P(TypographerTest, LazyAtlasTracksColor) {
126  auto data_host_buffer = HostBuffer::Create(
127  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
128  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
129 #if FML_OS_MACOSX
130  auto mapping = flutter::testing::OpenFixtureAsSkData("Apple Color Emoji.ttc");
131 #else
132  auto mapping = flutter::testing::OpenFixtureAsSkData("NotoColorEmoji.ttf");
133 #endif
134  ASSERT_TRUE(mapping);
135  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
136  SkFont emoji_font(font_mgr->makeFromData(mapping), 50.0);
137  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
138 
139  auto blob = SkTextBlob::MakeFromString("hello", sk_font);
140  ASSERT_TRUE(blob);
141  auto frame = MakeTextFrameFromTextBlobSkia(blob);
142 
143  ASSERT_FALSE(frame->GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
144 
146 
147  lazy_atlas.AddTextFrame(frame, {0, 0}, Matrix(), {});
148 
150  SkTextBlob::MakeFromString("😀 ", emoji_font));
151 
152  ASSERT_TRUE(frame->GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
153 
154  lazy_atlas.AddTextFrame(frame, {0, 0}, Matrix(), {});
155 
156  // Creates different atlases for color and red bitmap.
157  auto color_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
158  *GetContext(), *data_host_buffer, GlyphAtlas::Type::kColorBitmap);
159 
160  auto bitmap_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
161  *GetContext(), *data_host_buffer, GlyphAtlas::Type::kAlphaBitmap);
162 
163  ASSERT_FALSE(color_atlas == bitmap_atlas);
164 }
165 
166 TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
167  auto context = TypographerContextSkia::Make();
168  auto atlas_context =
169  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
170  auto data_host_buffer = HostBuffer::Create(
171  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
172  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
173  ASSERT_TRUE(context && context->IsValid());
174  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
175  auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
176  ASSERT_TRUE(blob);
177  auto atlas =
178  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
179  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
181  ASSERT_NE(atlas, nullptr);
182  ASSERT_NE(atlas->GetTexture(), nullptr);
183 
184  EXPECT_EQ(atlas->GetTexture()->GetSize().width, 4096u);
185  EXPECT_EQ(atlas->GetTexture()->GetSize().height, 1024u);
186 }
187 
188 TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
189  auto context = TypographerContextSkia::Make();
190  auto atlas_context =
191  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
192  auto data_host_buffer = HostBuffer::Create(
193  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
194  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
195  ASSERT_TRUE(context && context->IsValid());
196  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
197  auto blob = SkTextBlob::MakeFromString("spooky skellingtons", sk_font);
198  ASSERT_TRUE(blob);
199  auto atlas =
200  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
201  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
203  ASSERT_NE(atlas, nullptr);
204  ASSERT_NE(atlas->GetTexture(), nullptr);
205  ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
206 
207  // now attempt to re-create an atlas with the same text blob.
208 
209  auto next_atlas =
210  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
211  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
213  ASSERT_EQ(atlas, next_atlas);
214  ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
215 }
216 
217 TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
218  auto data_host_buffer = HostBuffer::Create(
219  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
220  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
221  auto context = TypographerContextSkia::Make();
222  auto atlas_context =
223  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
224  ASSERT_TRUE(context && context->IsValid());
225 
226  const char* test_string =
227  "QWERTYUIOPASDFGHJKLZXCVBNMqewrtyuiopasdfghjklzxcvbnm,.<>[]{};':"
228  "2134567890-=!@#$%^&*()_+"
229  "œ∑´®†¥¨ˆøπ““‘‘åß∂ƒ©˙∆˚¬…æ≈ç√∫˜µ≤≥≥≥≥÷¡™£¢∞§¶•ªº–≠⁄€‹›fifl‡°·‚—±Œ„´‰Á¨Ø∏”’/"
230  "* Í˝ */¸˛Ç◊ı˜Â¯˘¿";
231 
232  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
233  auto blob = SkTextBlob::MakeFromString(test_string, sk_font);
234  ASSERT_TRUE(blob);
235 
236  size_t size_count = 8;
237  std::vector<RenderableText> render_frames;
238  for (size_t index = 0; index < size_count; index += 1) {
239  Scalar scale = 6.0f * index / 10.0f;
240  render_frames.emplace_back(MakeTextFrameFromTextBlobSkia(blob),
241  Matrix::MakeScale({scale, scale, 1.0f}),
242  GlyphProperties{});
243  };
244  auto atlas = context->CreateGlyphAtlas(
245  *GetContext(), GlyphAtlas::Type::kAlphaBitmap, *data_host_buffer,
246  atlas_context, render_frames);
247  ASSERT_NE(atlas, nullptr);
248  ASSERT_NE(atlas->GetTexture(), nullptr);
249 
250  std::set<uint16_t> unique_glyphs;
251  std::vector<uint16_t> total_glyphs;
252  atlas->IterateGlyphs([&](const ScaledFont& scaled_font,
253  const SubpixelGlyph& glyph, const Rect& rect) {
254  unique_glyphs.insert(glyph.glyph.index);
255  total_glyphs.push_back(glyph.glyph.index);
256  return true;
257  });
258 
259  // These numbers may be different due to subpixel positions.
260  EXPECT_LE(unique_glyphs.size() * size_count, atlas->GetGlyphCount());
261  EXPECT_EQ(total_glyphs.size(), atlas->GetGlyphCount());
262 
263  EXPECT_TRUE(atlas->GetGlyphCount() > 0);
264  EXPECT_TRUE(atlas->GetTexture()->GetSize().width > 0);
265  EXPECT_TRUE(atlas->GetTexture()->GetSize().height > 0);
266 }
267 
268 TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
269  auto data_host_buffer = HostBuffer::Create(
270  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
271  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
272  auto context = TypographerContextSkia::Make();
273  auto atlas_context =
274  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
275  ASSERT_TRUE(context && context->IsValid());
276  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
277  auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
278  ASSERT_TRUE(blob);
279  auto atlas =
280  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
281  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
283  auto old_packer = atlas_context->GetRectPacker();
284 
285  ASSERT_NE(atlas, nullptr);
286  ASSERT_NE(atlas->GetTexture(), nullptr);
287  ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
288 
289  auto* first_texture = atlas->GetTexture().get();
290 
291  // Now create a new glyph atlas with a nearly identical blob.
292 
293  auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
294  auto next_atlas =
295  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
296  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
298  ASSERT_EQ(atlas, next_atlas);
299  auto* second_texture = next_atlas->GetTexture().get();
300 
301  auto new_packer = atlas_context->GetRectPacker();
302 
303  ASSERT_EQ(second_texture, first_texture);
304  ASSERT_EQ(old_packer, new_packer);
305 }
306 
307 TEST_P(TypographerTest, GlyphColorIsPartOfCacheKey) {
308  auto data_host_buffer = HostBuffer::Create(
309  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
310  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
311 #if FML_OS_MACOSX
312  auto mapping = flutter::testing::OpenFixtureAsSkData("Apple Color Emoji.ttc");
313 #else
314  auto mapping = flutter::testing::OpenFixtureAsSkData("NotoColorEmoji.ttf");
315 #endif
316  ASSERT_TRUE(mapping);
317  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
318  SkFont emoji_font(font_mgr->makeFromData(mapping), 50.0);
319 
320  auto context = TypographerContextSkia::Make();
321  auto atlas_context =
322  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kColorBitmap);
323 
324  // Create two frames with the same character and a different color, expect
325  // that it adds a character.
326  auto frame = MakeTextFrameFromTextBlobSkia(
327  SkTextBlob::MakeFromString("😂", emoji_font));
328  auto frame_2 = MakeTextFrameFromTextBlobSkia(
329  SkTextBlob::MakeFromString("😂", emoji_font));
330  std::vector<std::optional<GlyphProperties>> properties = {
333  };
334 
335  auto next_atlas =
336  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
337  GlyphAtlas::Type::kColorBitmap, Matrix(), atlas_context,
338  {frame, frame_2}, properties);
339 
340  EXPECT_EQ(next_atlas->GetGlyphCount(), 2u);
341 }
342 
343 TEST_P(TypographerTest, GlyphColorIsIgnoredForNonEmojiFonts) {
344  auto data_host_buffer = HostBuffer::Create(
345  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
346  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
347  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
348  sk_sp<SkTypeface> typeface =
349  font_mgr->matchFamilyStyle("Arial", SkFontStyle::Normal());
350  SkFont sk_font(typeface, 0.5f);
351 
352  auto context = TypographerContextSkia::Make();
353  auto atlas_context =
354  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kColorBitmap);
355 
356  // Create two frames with the same character and a different color, but as a
357  // non-emoji font the text frame constructor will ignore it.
358  auto frame =
359  MakeTextFrameFromTextBlobSkia(SkTextBlob::MakeFromString("A", sk_font));
360  auto frame_2 =
361  MakeTextFrameFromTextBlobSkia(SkTextBlob::MakeFromString("A", sk_font));
362  std::vector<std::optional<GlyphProperties>> properties = {
363  GlyphProperties{},
364  GlyphProperties{},
365  };
366 
367  auto next_atlas =
368  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
369  GlyphAtlas::Type::kColorBitmap, Matrix(), atlas_context,
370  {frame, frame_2}, properties);
371 
372  EXPECT_EQ(next_atlas->GetGlyphCount(), 1u);
373 }
374 
375 TEST_P(TypographerTest, RectanglePackerAddsNonoverlapingRectangles) {
376  auto packer = RectanglePacker::Factory(200, 100);
377  ASSERT_NE(packer, nullptr);
378  ASSERT_EQ(packer->PercentFull(), 0);
379 
380  const SkIRect packer_area = SkIRect::MakeXYWH(0, 0, 200, 100);
381 
382  IPoint16 first_output = {-1, -1}; // Fill with sentinel values
383  ASSERT_TRUE(packer->AddRect(20, 20, &first_output));
384  // Make sure the rectangle is placed such that it is inside the bounds of
385  // the packer's area.
386  const SkIRect first_rect =
387  SkIRect::MakeXYWH(first_output.x(), first_output.y(), 20, 20);
388  ASSERT_TRUE(SkIRect::Intersects(packer_area, first_rect));
389 
390  // Initial area was 200 x 100 = 20_000
391  // We added 20x20 = 400. 400 / 20_000 == 0.02 == 2%
392  ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.02));
393 
394  IPoint16 second_output = {-1, -1};
395  ASSERT_TRUE(packer->AddRect(140, 90, &second_output));
396  const SkIRect second_rect =
397  SkIRect::MakeXYWH(second_output.x(), second_output.y(), 140, 90);
398  // Make sure the rectangle is placed such that it is inside the bounds of
399  // the packer's area but not in the are of the first rectangle.
400  ASSERT_TRUE(SkIRect::Intersects(packer_area, second_rect));
401  ASSERT_FALSE(SkIRect::Intersects(first_rect, second_rect));
402 
403  // We added another 90 x 140 = 12_600 units, now taking us to 13_000
404  // 13_000 / 20_000 == 0.65 == 65%
405  ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.65));
406 
407  // There's enough area to add this rectangle, but no space big enough for
408  // the 50 units of width.
409  IPoint16 output;
410  ASSERT_FALSE(packer->AddRect(50, 50, &output));
411  // Should be unchanged.
412  ASSERT_TRUE(flutter::testing::NumberNear(packer->PercentFull(), 0.65));
413 
414  packer->Reset();
415  // Should be empty now.
416  ASSERT_EQ(packer->PercentFull(), 0);
417 }
418 
419 TEST(TypographerTest, RectanglePackerFillsRows) {
420  auto skyline = RectanglePacker::Factory(257, 256);
421 
422  // Fill up the first row.
423  IPoint16 loc;
424  for (auto i = 0u; i < 16; i++) {
425  skyline->AddRect(16, 16, &loc);
426  }
427  // Last rectangle still in first row.
428  EXPECT_EQ(loc.x(), 256 - 16);
429  EXPECT_EQ(loc.y(), 0);
430 
431  // Fill up second row.
432  for (auto i = 0u; i < 16; i++) {
433  skyline->AddRect(16, 16, &loc);
434  }
435 
436  EXPECT_EQ(loc.x(), 256 - 16);
437  EXPECT_EQ(loc.y(), 16);
438 }
439 
440 TEST_P(TypographerTest, GlyphAtlasTextureWillGrowTilMaxTextureSize) {
441  if (GetBackend() == PlaygroundBackend::kOpenGLES) {
442  GTEST_SKIP() << "Atlas growth isn't supported for OpenGLES currently.";
443  }
444 
445  auto data_host_buffer = HostBuffer::Create(
446  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
447  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
448  auto context = TypographerContextSkia::Make();
449  auto atlas_context =
450  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
451  ASSERT_TRUE(context && context->IsValid());
452  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
453  auto blob = SkTextBlob::MakeFromString("A", sk_font);
454  ASSERT_TRUE(blob);
455  auto atlas =
456  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
457  GlyphAtlas::Type::kAlphaBitmap, Matrix(), atlas_context,
459  // Continually append new glyphs until the glyph size grows to the maximum.
460  // Note that the sizes here are more or less experimentally determined, but
461  // the important expectation is that the atlas size will shrink again after
462  // growing to the maximum size.
463  constexpr ISize expected_sizes[13] = {
464  {4096, 4096}, //
465  {4096, 4096}, //
466  {4096, 8192}, //
467  {4096, 8192}, //
468  {4096, 8192}, //
469  {4096, 8192}, //
470  {4096, 16384}, //
471  {4096, 16384}, //
472  {4096, 16384}, //
473  {4096, 16384}, //
474  {4096, 16384}, //
475  {4096, 16384}, //
476  {4096, 4096} // Shrinks!
477  };
478 
479  SkFont sk_font_small = flutter::testing::CreateTestFontOfSize(10);
480 
481  for (int i = 0; i < 13; i++) {
482  SkTextBlobBuilder builder;
483 
484  auto add_char = [&](const SkFont& sk_font, char c) {
485  int count = sk_font.countText(&c, 1, SkTextEncoding::kUTF8);
486  auto buffer = builder.allocRunPos(sk_font, count);
487  sk_font.textToGlyphs(&c, 1, SkTextEncoding::kUTF8,
488  {buffer.glyphs, count});
489  sk_font.getPos({buffer.glyphs, count}, {buffer.points(), count},
490  {0, 0} /*=origin*/);
491  };
492 
493  SkFont sk_font = flutter::testing::CreateTestFontOfSize(50 + i);
494  add_char(sk_font, 'A');
495  add_char(sk_font_small, 'B');
496  auto blob = builder.make();
497 
498  Matrix transform = Matrix::MakeScale({50.0f + i, 50.0f + i, 1.0f});
499  atlas =
500  CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
502  atlas_context, MakeTextFrameFromTextBlobSkia(blob));
503  ASSERT_TRUE(!!atlas);
504  EXPECT_EQ(atlas->GetTexture()->GetTextureDescriptor().size,
505  expected_sizes[i]);
506  }
507 
508  // The final atlas should contain both the "A" glyph (which was not present
509  // in the previous atlas) and the "B" glyph (which existed in the previous
510  // atlas).
511  ASSERT_EQ(atlas->GetGlyphCount(), 2u);
512 }
513 
514 TEST_P(TypographerTest, InvalidAtlasForcesRepopulation) {
515  SkFont font = flutter::testing::CreateTestFontOfSize(12);
516  auto blob = SkTextBlob::MakeFromString(
517  "the quick brown fox jumped over the lazy dog.", font);
518  ASSERT_TRUE(blob);
519  auto frame = MakeTextFrameFromTextBlobSkia(blob);
520 
521  auto context = TypographerContextSkia::Make();
522  auto atlas_context =
523  context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
524  auto data_host_buffer = HostBuffer::Create(
525  GetContext()->GetResourceAllocator(), GetContext()->GetIdleWaiter(),
526  GetContext()->GetCapabilities()->GetMinimumUniformAlignment());
527 
528  auto atlas = CreateGlyphAtlas(*GetContext(), context.get(), *data_host_buffer,
530  atlas_context, frame);
531 
532  auto second_context = TypographerContextSkia::Make();
533  auto second_atlas_context =
534  second_context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
535 
536  EXPECT_FALSE(second_atlas_context->GetGlyphAtlas()->IsValid());
537 
538  atlas = CreateGlyphAtlas(*GetContext(), second_context.get(),
539  *data_host_buffer, GlyphAtlas::Type::kAlphaBitmap,
540  Matrix(), second_atlas_context, frame);
541 
542  EXPECT_TRUE(second_atlas_context->GetGlyphAtlas()->IsValid());
543 }
544 
545 } // namespace testing
546 } // namespace impeller
547 
548 // NOLINTEND(bugprone-unchecked-optional-access)
To do anything rendering related with Impeller, you need a context.
Definition: context.h:65
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)
Definition: host_buffer.cc:21
const std::shared_ptr< GlyphAtlas > & CreateOrGetGlyphAtlas(Context &context, HostBuffer &host_buffer, GlyphAtlas::Type type)
void AddTextFrame(const std::shared_ptr< TextFrame > &frame, Point position, const Matrix &transform, const std::optional< GlyphProperties > &properties)
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 std::shared_ptr< TypographerContext > Make()
bool NumberNear(double a, double b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
TEST_P(AiksTest, DrawAtlasNoColor)
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)
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
float Scalar
Definition: scalar.h:19
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
static constexpr Color Red()
Definition: color.h:272
static constexpr Color Blue()
Definition: color.h:276
uint16_t index
Definition: glyph.h:22
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.