Flutter Impeller
aiks_dl_text_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/display_list.h"
6 #include "flutter/display_list/dl_blend_mode.h"
7 #include "flutter/display_list/dl_builder.h"
8 #include "flutter/display_list/dl_color.h"
9 #include "flutter/display_list/dl_paint.h"
10 #include "flutter/display_list/dl_tile_mode.h"
11 #include "flutter/display_list/effects/dl_color_source.h"
12 #include "flutter/display_list/effects/dl_mask_filter.h"
13 #include "flutter/display_list/geometry/dl_path_builder.h"
14 #include "flutter/display_list/testing/dl_test_snippets.h"
15 #include "flutter/fml/build_config.h"
17 #include "flutter/testing/testing.h"
24 #include "impeller/entity/entity.h"
27 
29 #include "txt/platform.h"
30 
31 using namespace flutter;
32 /////////////////////////////////////////////////////
33 
34 namespace impeller {
35 namespace testing {
36 
38  bool stroke = false;
40  Scalar stroke_width = 1;
41  DlColor color = DlColor::kYellow();
42  DlPoint position = DlPoint(100, 200);
43  std::shared_ptr<DlMaskFilter> filter;
44  bool is_subpixel = false;
45 };
46 
47 bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
48  DisplayListBuilder& canvas,
49  const std::string& text,
50  const std::string_view& font_fixture,
51  const TextRenderOptions& options = {},
52  const std::optional<SkFont>& font = std::nullopt) {
53  // Draw the baseline.
54  DlPaint paint;
55  paint.setColor(DlColor::kAqua().withAlpha(255 * 0.25));
56  canvas.DrawRect(
57  DlRect::MakeXYWH(options.position.x - 50, options.position.y, 900, 10),
58  paint);
59 
60  // Mark the point at which the text is drawn.
61  paint.setColor(DlColor::kRed().withAlpha(255 * 0.25));
62  canvas.DrawCircle(options.position, 5.0, paint);
63 
64  // Construct the text blob.
65  SkFont selected_font;
66  if (!font.has_value()) {
67  auto c_font_fixture = std::string(font_fixture);
68  auto mapping =
69  flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
70  if (!mapping) {
71  return false;
72  }
73  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
74  selected_font = SkFont(font_mgr->makeFromData(mapping), options.font_size);
75  if (options.is_subpixel) {
76  selected_font.setSubpixel(true);
77  }
78  } else {
79  selected_font = font.value();
80  }
81  auto blob = SkTextBlob::MakeFromString(text.c_str(), selected_font);
82  if (!blob) {
83  return false;
84  }
85 
86  // Create the Impeller text frame and draw it at the designated baseline.
87  auto frame = MakeTextFrameFromTextBlobSkia(blob);
88 
89  DlPaint text_paint;
90  text_paint.setColor(options.color);
91  text_paint.setMaskFilter(options.filter);
92  text_paint.setStrokeWidth(options.stroke_width);
93  text_paint.setDrawStyle(options.stroke ? DlDrawStyle::kStroke
94  : DlDrawStyle::kFill);
95  canvas.DrawText(DlTextImpeller::Make(frame), options.position.x,
96  options.position.y, text_paint);
97  return true;
98 }
99 
100 TEST_P(AiksTest, CanRenderTextFrame) {
101  DisplayListBuilder builder;
102 
103  DlPaint paint;
104  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
105  builder.DrawPaint(paint);
106  ASSERT_TRUE(RenderTextInCanvasSkia(
107  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
108  "Roboto-Regular.ttf"));
109 
110  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
111 }
112 
113 TEST_P(AiksTest, CanRenderTextFrameWithInvertedTransform) {
114  DisplayListBuilder builder;
115 
116  DlPaint paint;
117  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
118  builder.DrawPaint(paint);
119  builder.Translate(1000, 0);
120  builder.Scale(-1, 1);
121 
122  ASSERT_TRUE(RenderTextInCanvasSkia(
123  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
124  "Roboto-Regular.ttf"));
125 
126  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
127 }
128 
129 TEST_P(AiksTest, CanRenderStrokedTextFrame) {
130  DisplayListBuilder builder;
131 
132  DlPaint paint;
133  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
134  builder.DrawPaint(paint);
135 
136  ASSERT_TRUE(RenderTextInCanvasSkia(
137  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
138  "Roboto-Regular.ttf",
139  {
140  .stroke = true,
141  }));
142  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
143 }
144 
145 TEST_P(AiksTest, CanRenderTextStrokeWidth) {
146  DisplayListBuilder builder;
147 
148  DlPaint paint;
149  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
150  builder.DrawPaint(paint);
151 
152  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "LMNOP VWXYZ",
153  "Roboto-Medium.ttf",
154  {
155  .stroke = true,
156  .stroke_width = 4,
157  }));
158  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
159 }
160 
161 TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
162  DisplayListBuilder builder;
163 
164  DlPaint paint;
165  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
166  builder.DrawPaint(paint);
167  builder.Scale(0.5, 0.5);
168 
169  ASSERT_TRUE(RenderTextInCanvasSkia(
170  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
171  "Roboto-Regular.ttf"));
172  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
173 }
174 
175 // This is a test that looks for glyph artifacts we've see.
176 TEST_P(AiksTest, ScaledK) {
177  DisplayListBuilder builder;
178  DlPaint paint;
179  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
180  builder.DrawPaint(paint);
181  for (int i = 0; i < 6; ++i) {
182  builder.Save();
183  builder.Translate(300 * i, 0);
184  Scalar scale = 0.445 - (i / 1000.f);
185  builder.Scale(scale, scale);
187  GetContext(), builder, "k", "Roboto-Regular.ttf",
188  TextRenderOptions{.font_size = 600, .position = DlPoint(10, 500)});
190  GetContext(), builder, "k", "Roboto-Regular.ttf",
191  TextRenderOptions{.font_size = 300, .position = DlPoint(10, 800)});
192  builder.Restore();
193  }
194  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
195 }
196 
197 // This is a test that looks for glyph artifacts we've see.
198 TEST_P(AiksTest, MassiveScaleConvertToPath) {
199  Scalar scale = 16.0;
200  auto callback = [&]() -> sk_sp<DisplayList> {
201  if (AiksTest::ImGuiBegin("Controls", nullptr,
202  ImGuiWindowFlags_AlwaysAutoResize)) {
203  ImGui::SliderFloat("Scale", &scale, 4, 20);
204  ImGui::End();
205  }
206 
207  DisplayListBuilder builder;
208  DlPaint paint;
209  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
210  builder.DrawPaint(paint);
211 
212  builder.Scale(scale, scale);
214  GetContext(), builder, "HELLO", "Roboto-Regular.ttf",
216  .color = (16 * scale >= 250) ? DlColor::kYellow()
217  : DlColor::kOrange(),
218  .position = DlPoint(0, 20)});
219  return builder.Build();
220  };
221 
222  ASSERT_TRUE(OpenPlaygroundHere(callback));
223 }
224 
225 TEST_P(AiksTest, CanRenderTextFrameWithScalingOverflow) {
226  Scalar scale = 60.0;
227  Scalar offsetx = -500.0;
228  Scalar offsety = 700.0;
229  auto callback = [&]() -> sk_sp<DisplayList> {
230  if (AiksTest::ImGuiBegin("Controls", nullptr,
231  ImGuiWindowFlags_AlwaysAutoResize)) {
232  ImGui::SliderFloat("scale", &scale, 1.f, 300.f);
233  ImGui::SliderFloat("offsetx", &offsetx, -600.f, 100.f);
234  ImGui::SliderFloat("offsety", &offsety, 600.f, 2048.f);
235  ImGui::End();
236  }
237  DisplayListBuilder builder;
238  builder.Scale(GetContentScale().x, GetContentScale().y);
239  DlPaint paint;
240  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
241  builder.DrawPaint(paint);
242  builder.Scale(scale, scale);
243 
245  GetContext(), builder, "test", "Roboto-Regular.ttf",
247  .position = DlPoint(offsetx / scale, offsety / scale),
248  });
249  return builder.Build();
250  };
251  ASSERT_TRUE(OpenPlaygroundHere(callback));
252 }
253 
254 TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
255  Scalar fine_scale = 0.f;
256  bool is_subpixel = false;
257  auto callback = [&]() -> sk_sp<DisplayList> {
258  if (AiksTest::ImGuiBegin("Controls", nullptr,
259  ImGuiWindowFlags_AlwaysAutoResize)) {
260  ImGui::SliderFloat("Fine Scale", &fine_scale, -1, 1);
261  ImGui::Checkbox("subpixel", &is_subpixel);
262  ImGui::End();
263  }
264 
265  DisplayListBuilder builder;
266  DlPaint paint;
267  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
268  builder.DrawPaint(paint);
269  Scalar scale = 2.625 + fine_scale;
270  builder.Scale(scale, scale);
271  RenderTextInCanvasSkia(GetContext(), builder,
272  "the quick brown fox jumped over the lazy dog!.?",
273  "Roboto-Regular.ttf",
275  return builder.Build();
276  };
277 
278  ASSERT_TRUE(OpenPlaygroundHere(callback));
279 }
280 
281 // https://github.com/flutter/flutter/issues/164958
282 TEST_P(AiksTest, TextRotated180Degrees) {
283  float fpivot[2] = {200 + 30, 200 - 20};
284  float rotation = 180;
285  float foffset[2] = {200, 200};
286 
287  auto callback = [&]() -> sk_sp<DisplayList> {
288  if (AiksTest::ImGuiBegin("Controls", nullptr,
289  ImGuiWindowFlags_AlwaysAutoResize)) {
290  ImGui::SliderFloat("pivotx", &fpivot[0], 0, 300);
291  ImGui::SliderFloat("pivoty", &fpivot[1], 0, 300);
292  ImGui::SliderFloat("rotation", &rotation, 0, 360);
293  ImGui::SliderFloat("foffsetx", &foffset[0], 0, 300);
294  ImGui::SliderFloat("foffsety", &foffset[1], 0, 300);
295  ImGui::End();
296  }
297  DisplayListBuilder builder;
298  builder.Scale(GetContentScale().x, GetContentScale().y);
299  builder.DrawPaint(DlPaint().setColor(DlColor(0xffffeeff)));
300 
301  builder.Save();
302  DlPoint pivot = Point(fpivot[0], fpivot[1]);
303  builder.Translate(pivot.x, pivot.y);
304  builder.Rotate(rotation);
305  builder.Translate(-pivot.x, -pivot.y);
306 
307  RenderTextInCanvasSkia(GetContext(), builder, "test", "Roboto-Regular.ttf",
309  .color = DlColor::kBlack(),
310  .position = DlPoint(foffset[0], foffset[1]),
311  });
312 
313  builder.Restore();
314  return builder.Build();
315  };
316  ASSERT_TRUE(OpenPlaygroundHere(callback));
317 }
318 
319 TEST_P(AiksTest, TextFrameSubpixelAlignment) {
320  // "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
321  std::array<Scalar, 20> phase_offsets = {
322  7.82637e-06, 0.131538, 0.755605, 0.45865, 0.532767,
323  0.218959, 0.0470446, 0.678865, 0.679296, 0.934693,
324  0.383502, 0.519416, 0.830965, 0.0345721, 0.0534616,
325  0.5297, 0.671149, 0.00769819, 0.383416, 0.0668422};
326  auto callback = [&]() -> sk_sp<DisplayList> {
327  static float font_size = 20;
328  static float phase_variation = 0.2;
329  static float speed = 0.5;
330  static float magnitude = 100;
331  if (AiksTest::ImGuiBegin("Controls", nullptr,
332  ImGuiWindowFlags_AlwaysAutoResize)) {
333  ImGui::SliderFloat("Font size", &font_size, 5, 50);
334  ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
335  ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
336  ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
337  ImGui::End();
338  }
339 
340  DisplayListBuilder builder;
341  builder.Scale(GetContentScale().x, GetContentScale().y);
342 
343  for (size_t i = 0; i < phase_offsets.size(); i++) {
344  DlPoint position = DlPoint(
345  200 +
346  magnitude * std::sin((-phase_offsets[i] * k2Pi * phase_variation +
347  GetSecondsElapsed() * speed)), //
348  200 + i * font_size * 1.1 //
349  );
351  GetContext(), builder,
352  "the quick brown fox jumped over "
353  "the lazy dog!.?",
354  "Roboto-Regular.ttf",
355  {.font_size = font_size, .position = position})) {
356  return nullptr;
357  }
358  }
359  return builder.Build();
360  };
361 
362  ASSERT_TRUE(OpenPlaygroundHere(callback));
363 }
364 
365 TEST_P(AiksTest, CanRenderItalicizedText) {
366  DisplayListBuilder builder;
367 
368  DlPaint paint;
369  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
370  builder.DrawPaint(paint);
371 
372  ASSERT_TRUE(RenderTextInCanvasSkia(
373  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
374  "HomemadeApple.ttf"));
375  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
376 }
377 
378 static constexpr std::string_view kFontFixture =
379 #if FML_OS_MACOSX
380  "Apple Color Emoji.ttc";
381 #else
382  "NotoColorEmoji.ttf";
383 #endif
384 
385 TEST_P(AiksTest, CanRenderEmojiTextFrame) {
386  DisplayListBuilder builder;
387 
388  DlPaint paint;
389  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
390  builder.DrawPaint(paint);
391 
392  ASSERT_TRUE(RenderTextInCanvasSkia(
393  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture));
394  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
395 }
396 
397 TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) {
398  DisplayListBuilder builder;
399 
400  builder.Scale(GetContentScale().x, GetContentScale().y);
401  DlPaint paint;
402  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
403  builder.DrawPaint(paint);
404 
405  ASSERT_TRUE(RenderTextInCanvasSkia(
406  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
408  .color = DlColor::kBlue(),
409  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
410  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
411 }
412 
413 TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
414  DisplayListBuilder builder;
415 
416  DlPaint paint;
417  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
418  builder.DrawPaint(paint);
419 
420  ASSERT_TRUE(RenderTextInCanvasSkia(
421  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
422  {.color = DlColor::kBlack().modulateOpacity(0.5)}));
423  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
424 }
425 
426 TEST_P(AiksTest, CanRenderTextInSaveLayer) {
427  DisplayListBuilder builder;
428 
429  DlPaint paint;
430  paint.setColor(DlColor::ARGB(0.1, 0.1, 0.1, 0.1));
431  builder.DrawPaint(paint);
432 
433  builder.Translate(100, 100);
434  builder.Scale(0.5, 0.5);
435 
436  // Blend the layer with the parent pass using kClear to expose the coverage.
437  paint.setBlendMode(DlBlendMode::kClear);
438  builder.SaveLayer(std::nullopt, &paint);
439  ASSERT_TRUE(RenderTextInCanvasSkia(
440  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
441  "Roboto-Regular.ttf"));
442  builder.Restore();
443 
444  // Render the text again over the cleared coverage rect.
445  ASSERT_TRUE(RenderTextInCanvasSkia(
446  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
447  "Roboto-Regular.ttf"));
448 
449  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
450 }
451 
452 TEST_P(AiksTest, CanRenderTextOutsideBoundaries) {
453  DisplayListBuilder builder;
454  builder.Translate(200, 150);
455 
456  // Construct the text blob.
457  auto mapping = flutter::testing::OpenFixtureAsSkData("wtf.otf");
458  ASSERT_NE(mapping, nullptr);
459 
460  Scalar font_size = 80;
461  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
462  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
463 
464  DlPaint text_paint;
465  text_paint.setColor(DlColor::kBlue().withAlpha(255 * 0.8));
466 
467  struct {
468  DlPoint position;
469  const char* text;
470  } text[] = {{DlPoint(0, 0), "0F0F0F0"},
471  {DlPoint(1, 2), "789"},
472  {DlPoint(1, 3), "456"},
473  {DlPoint(1, 4), "123"},
474  {DlPoint(0, 6), "0F0F0F0"}};
475  for (auto& t : text) {
476  builder.Save();
477  builder.Translate(t.position.x * font_size * 2,
478  t.position.y * font_size * 1.1);
479  {
480  auto blob = SkTextBlob::MakeFromString(t.text, sk_font);
481  ASSERT_NE(blob, nullptr);
482  auto frame = MakeTextFrameFromTextBlobSkia(blob);
483  builder.DrawText(DlTextImpeller::Make(frame), 0, 0, text_paint);
484  }
485  builder.Restore();
486  }
487 
488  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
489 }
490 
491 TEST_P(AiksTest, TextRotated) {
492  DisplayListBuilder builder;
493 
494  builder.Scale(GetContentScale().x, GetContentScale().y);
495  DlPaint paint;
496  paint.setColor(DlColor::ARGB(0.1, 0.1, 0.1, 1.0));
497  builder.DrawPaint(paint);
498 
499  builder.Transform(Matrix(0.25, -0.3, 0, -0.002, //
500  0, 0.5, 0, 0, //
501  0, 0, 0.3, 0, //
502  100, 100, 0, 1.3));
503  ASSERT_TRUE(RenderTextInCanvasSkia(
504  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
505  "Roboto-Regular.ttf"));
506 
507  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
508 }
509 
510 TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
511  DisplayListBuilder builder;
512 
513  Matrix matrix = Matrix(1.0, 0.0, 0.0, 0.0, //
514  0.0, 1.0, 0.0, 0.0, //
515  0.0, 0.0, 1.0, 0.01, //
516  0.0, 0.0, 0.0, 1.0) * //
517  Matrix::MakeRotationY({Degrees{10}});
518 
519  builder.Transform(matrix);
520 
521  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
522  "Roboto-Regular.ttf"));
523  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
524 }
525 
526 TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
527  DisplayListBuilder builder;
528 
529  Matrix matrix = Matrix(1.0, 0.0, 0.0, 0.0, //
530  0.0, 1.0, 0.0, 0.0, //
531  0.0, 0.0, 1.0, 0.01, //
532  0.0, 0.0, 0.0, 1.0) * //
533  Matrix::MakeRotationY({Degrees{10}});
534 
535  DlPaint save_paint;
536  DlRect window_bounds =
537  DlRect::MakeXYWH(0, 0, GetWindowSize().width, GetWindowSize().height);
538  // Note: bounds were not needed by the AIKS version, which may indicate a bug.
539  builder.SaveLayer(window_bounds, &save_paint);
540  builder.Transform(matrix);
541 
542  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
543  "Roboto-Regular.ttf"));
544 
545  builder.Restore();
546  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
547 }
548 
549 TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) {
550  // Verifies that text scales are clamped to work around
551  // https://github.com/flutter/flutter/issues/136112 .
552 
553  DisplayListBuilder builder;
554 
555  DlPaint save_paint;
556  builder.SaveLayer(std::nullopt, &save_paint);
557  builder.Transform(Matrix(2000, 0, 0, 0, //
558  0, 2000, 0, 0, //
559  0, 0, -1, 9000, //
560  0, 0, -1, 7000 //
561  ));
562 
563  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
564  "Roboto-Regular.ttf"));
565  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
566 }
567 
568 TEST_P(AiksTest, CanRenderTextWithPerspectiveTransformInSublist) {
569  DisplayListBuilder text_builder;
570  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), text_builder, "Hello world",
571  "Roboto-Regular.ttf"));
572  auto text_display_list = text_builder.Build();
573 
574  DisplayListBuilder builder;
575 
576  Matrix matrix = Matrix::MakeRow(2.0, 0.0, 0.0, 0.0, //
577  0.0, 2.0, 0.0, 0.0, //
578  0.0, 0.0, 1.0, 0.0, //
579  0.0, 0.002, 0.0, 1.0);
580 
581  DlPaint save_paint;
582  DlRect window_bounds =
583  DlRect::MakeXYWH(0, 0, GetWindowSize().width, GetWindowSize().height);
584  builder.SaveLayer(window_bounds, &save_paint);
585  builder.Transform(matrix);
586  builder.DrawDisplayList(text_display_list, 1.0f);
587  builder.Restore();
588 
589  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
590 }
591 
592 // This currently renders solid blue, as the support for text color sources was
593 // moved into DLDispatching. Path data requires the SkTextBlobs which are not
594 // used in impeller::TextFrames.
595 TEST_P(AiksTest, TextForegroundShaderWithTransform) {
596  auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
597  ASSERT_NE(mapping, nullptr);
598 
599  Scalar font_size = 100;
600  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
601  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
602 
603  DlPaint text_paint;
604  text_paint.setColor(DlColor::kBlue());
605 
606  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
607  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
608  std::vector<Scalar> stops = {
609  0.0,
610  1.0,
611  };
612  text_paint.setColorSource(DlColorSource::MakeLinear(
613  /*start_point=*/DlPoint(0, 0), //
614  /*end_point=*/DlPoint(100, 100), //
615  /*stop_count=*/2, //
616  /*colors=*/colors.data(), //
617  /*stops=*/stops.data(), //
618  /*tile_mode=*/DlTileMode::kRepeat //
619  ));
620 
621  DisplayListBuilder builder;
622  builder.Translate(100, 100);
623  builder.Rotate(45);
624 
625  auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
626  ASSERT_NE(blob, nullptr);
627  auto frame = MakeTextFrameFromTextBlobSkia(blob);
628  builder.DrawText(DlTextImpeller::Make(frame), 0, 0, text_paint);
629 
630  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
631 }
632 
633 // Regression test for https://github.com/flutter/flutter/issues/157885.
634 TEST_P(AiksTest, DifferenceClipsMustRenderIdenticallyAcrossBackends) {
635  DisplayListBuilder builder;
636 
637  DlPaint paint;
638  DlColor clear_color(1.0, 0.5, 0.5, 0.5, DlColorSpace::kSRGB);
639  paint.setColor(clear_color);
640  builder.DrawPaint(paint);
641 
642  DlMatrix identity = {
643  1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
644  0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
645  };
646  builder.Save();
647  builder.Transform(identity);
648 
649  DlRect frame = DlRect::MakeLTRB(1.0, 1.0, 1278.0, 763.0);
650  DlColor white(1.0, 1.0, 1.0, 1.0, DlColorSpace::kSRGB);
651  paint.setColor(white);
652  builder.DrawRect(frame, paint);
653 
654  builder.Save();
655  builder.ClipRect(frame, DlClipOp::kIntersect);
656 
657  DlMatrix rect_xform = {
658  0.8241262, 0.56640625, 0.0, 0.0, -0.56640625, 0.8241262, 0.0, 0.0,
659  0.0, 0.0, 1.0, 0.0, 271.1137, 489.4733, 0.0, 1.0,
660  };
661  builder.Save();
662  builder.Transform(rect_xform);
663 
664  DlRect rect = DlRect::MakeLTRB(0.0, 0.0, 100.0, 100.0);
665  DlColor bluish(1.0, 0.184, 0.501, 0.929, DlColorSpace::kSRGB);
666  paint.setColor(bluish);
667  DlRoundRect rrect = DlRoundRect::MakeRectRadius(rect, 18.0);
668  builder.DrawRoundRect(rrect, paint);
669 
670  builder.Save();
671  builder.ClipRect(rect, DlClipOp::kIntersect);
672  builder.Restore();
673 
674  builder.Restore();
675 
676  DlMatrix path_xform = {
677  1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
678  0.0, 0.0, 1.0, 0.0, 675.0, 279.5, 0.0, 1.0,
679  };
680  builder.Save();
681  builder.Transform(path_xform);
682 
683  DlPathBuilder path_builder;
684  path_builder.MoveTo(DlPoint(87.5, 349.5));
685  path_builder.LineTo(DlPoint(25.0, 29.5));
686  path_builder.LineTo(DlPoint(150.0, 118.0));
687  path_builder.LineTo(DlPoint(25.0, 118.0));
688  path_builder.LineTo(DlPoint(150.0, 29.5));
689  path_builder.Close();
690  DlPath path = path_builder.TakePath();
691 
692  DlColor fill_color(1.0, 1.0, 0.0, 0.0, DlColorSpace::kSRGB);
693  DlColor stroke_color(1.0, 0.0, 0.0, 0.0, DlColorSpace::kSRGB);
694  paint.setColor(fill_color);
695  paint.setDrawStyle(DlDrawStyle::kFill);
696  builder.DrawPath(path, paint);
697 
698  paint.setColor(stroke_color);
699  paint.setStrokeWidth(2.0);
700  paint.setDrawStyle(DlDrawStyle::kStroke);
701  builder.DrawPath(path, paint);
702 
703  builder.Restore();
704  builder.Restore();
705  builder.Restore();
706 
707  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
708 }
709 
710 TEST_P(AiksTest, TextContentsMismatchedTransformTest) {
711  AiksContext aiks_context(GetContext(),
712  std::make_shared<TypographerContextSkia>());
713 
714  // Verifies that TextContents only use the scale/transform that is
715  // computed during preroll.
716  constexpr const char* font_fixture = "Roboto-Regular.ttf";
717 
718  // Construct the text blob.
719  auto c_font_fixture = std::string(font_fixture);
720  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
721  ASSERT_TRUE(mapping);
722 
723  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
724  SkFont sk_font(font_mgr->makeFromData(mapping), 16);
725 
726  auto blob = SkTextBlob::MakeFromString("Hello World", sk_font);
727  ASSERT_TRUE(blob);
728 
729  auto text_frame = MakeTextFrameFromTextBlobSkia(blob);
730 
731  // Simulate recording the text frame during preroll.
732  Matrix preroll_matrix =
733  Matrix::MakeTranslateScale({1.5, 1.5, 1}, {100, 50, 0});
734  Point preroll_point = Point{23, 45};
735  {
736  aiks_context.GetContentContext().GetLazyGlyphAtlas()->AddTextFrame(
737  text_frame, //
738  preroll_point, //
739  preroll_matrix, //
740  GlyphProperties{} //
741  );
742  }
743 
744  // Now simulate rendering with a slightly different scale factor.
745  RenderTarget render_target =
746  aiks_context.GetContentContext()
748  ->CreateOffscreenMSAA(*aiks_context.GetContext(), {100, 100}, 1);
749 
750  TextContents text_contents;
751  text_contents.SetTextFrame(text_frame);
752  text_contents.SetPosition(preroll_point);
753  text_contents.SetScreenTransform(preroll_matrix);
754  text_contents.SetColor(Color::Aqua());
755 
756  Matrix not_preroll_matrix =
757  preroll_matrix * Matrix::MakeScale({2.0f, 2.0f, 1.0f});
758 
759  Entity entity;
760  entity.SetTransform(not_preroll_matrix);
761 
762  std::shared_ptr<CommandBuffer> command_buffer =
763  aiks_context.GetContext()->CreateCommandBuffer();
764  std::shared_ptr<RenderPass> render_pass =
765  command_buffer->CreateRenderPass(render_target);
766 
767  EXPECT_TRUE(text_contents.Render(aiks_context.GetContentContext(), entity,
768  *render_pass));
769 }
770 
771 TEST_P(AiksTest, CanRenderTextFrameWithThinLightAndDarkColors) {
772  DisplayListBuilder builder;
773  DlPaint paint;
774  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
775  builder.DrawPaint(paint);
776 
777  auto mapping =
778  flutter::testing::OpenFixtureAsSkData("RobotoSlab-VariableFont_wght.ttf");
779  ASSERT_TRUE(mapping);
780  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
781 
782  // Set the variation axis for weight to 100 (typically "Thin").
783  SkFontArguments::VariationPosition::Coordinate weight_coord{
784  SkSetFourByteTag('w', 'g', 'h', 't'), 100.0f};
785  SkFontArguments args;
786  args.setVariationDesignPosition({&weight_coord, 1});
787 
788  SkFont thin_font(font_mgr->makeFromData(mapping)->makeClone(args), 25);
789 
790  // Render light text
791  ASSERT_TRUE(RenderTextInCanvasSkia(
792  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
793  "RobotoSlab-VariableFont_wght.ttf",
794  TextRenderOptions{.color = DlColor::kWhite(),
795  .position = DlPoint(100, 200)},
796  thin_font));
797 
798  // Render dark text on a light background
799  DlPaint dart_text_background_paint;
800  dart_text_background_paint.setColor(DlColor::ARGB(1, 0.9, 0.9, 0.9));
801  builder.DrawRect(DlRect::MakeXYWH(50, 250, 900, 100),
802  dart_text_background_paint);
803  ASSERT_TRUE(RenderTextInCanvasSkia(
804  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
805  "RobotoSlab-VariableFont_wght.ttf",
806  TextRenderOptions{.color = DlColor::kDarkGreen(),
807  .position = DlPoint(100, 300)},
808  thin_font));
809 
810  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
811 }
812 
813 TEST_P(AiksTest, TextWithShadowCache) {
814  DisplayListBuilder builder;
815  builder.Scale(GetContentScale().x, GetContentScale().y);
816  DlPaint paint;
817  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
818  builder.DrawPaint(paint);
819 
820  AiksContext aiks_context(GetContext(),
821  std::make_shared<TypographerContextSkia>());
822  // Cache empty
823  EXPECT_EQ(aiks_context.GetContentContext()
826  0u);
827 
828  ASSERT_TRUE(RenderTextInCanvasSkia(
829  GetContext(), builder, "Hello World", kFontFixture,
831  .color = DlColor::kBlue(),
832  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
833 
834  DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
835 
836  // Text should be cached.
837  EXPECT_EQ(aiks_context.GetContentContext()
840  1u);
841 }
842 
843 TEST_P(AiksTest, MultipleTextWithShadowCache) {
844  DisplayListBuilder builder;
845  builder.Scale(GetContentScale().x, GetContentScale().y);
846  DlPaint paint;
847  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
848  builder.DrawPaint(paint);
849 
850  AiksContext aiks_context(GetContext(),
851  std::make_shared<TypographerContextSkia>());
852  // Cache empty
853  EXPECT_EQ(aiks_context.GetContentContext()
856  0u);
857 
858  for (auto i = 0; i < 5; i++) {
859  ASSERT_TRUE(RenderTextInCanvasSkia(
860  GetContext(), builder, "Hello World", kFontFixture,
862  .color = DlColor::kBlue(),
863  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
864  }
865 
866  DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
867 
868  // Text should be cached. Each text gets its own entry as we don't analyze the
869  // strings.
870  EXPECT_EQ(aiks_context.GetContentContext()
873  5u);
874 }
875 
876 TEST_P(AiksTest, MultipleColorWithShadowCache) {
877  DisplayListBuilder builder;
878  builder.Scale(GetContentScale().x, GetContentScale().y);
879  DlPaint paint;
880  paint.setColor(DlColor::kWhite());
881  builder.DrawPaint(paint);
882 
883  AiksContext aiks_context(GetContext(),
884  std::make_shared<TypographerContextSkia>());
885  // Cache empty
886  EXPECT_EQ(aiks_context.GetContentContext()
889  0u);
890 
891  SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
892 
893  std::array<DlColor, 4> colors{DlColor::kRed(), DlColor::kGreen(),
894  DlColor::kBlue(), DlColor::kRed()};
895  for (const auto& color : colors) {
896  ASSERT_TRUE(RenderTextInCanvasSkia(
897  GetContext(), builder, "A", kFontFixture,
899  .color = color,
900  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)},
901  sk_font));
902  }
903 
904  DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
905 
906  // The count of cache entries should match the number of distinct colors
907  // in the list. Repeated usage of a color should not add to the cache.
908  EXPECT_EQ(aiks_context.GetContentContext()
911  3u);
912 }
913 
914 TEST_P(AiksTest, SingleIconShadowTest) {
915  DisplayListBuilder builder;
916  builder.Scale(GetContentScale().x, GetContentScale().y);
917  DlPaint paint;
918  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
919  builder.DrawPaint(paint);
920 
921  AiksContext aiks_context(GetContext(),
922  std::make_shared<TypographerContextSkia>());
923  // Cache empty
924  EXPECT_EQ(aiks_context.GetContentContext()
927  0u);
928 
929  // Create font instance outside loop so all draws use identical font instance.
930  auto c_font_fixture = std::string(kFontFixture);
931  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
932  ASSERT_TRUE(mapping);
933  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
934  SkFont sk_font(font_mgr->makeFromData(mapping), 50);
935 
936  for (auto i = 0; i < 10; i++) {
937  ASSERT_TRUE(RenderTextInCanvasSkia(
938  GetContext(), builder, "A", kFontFixture,
940  .color = DlColor::kBlue(),
941  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)},
942  sk_font));
943  }
944 
945  DisplayListToTexture(builder.Build(), {400, 400}, aiks_context);
946 
947  // Text should be cached. All 10 glyphs use the same cache entry.
948  EXPECT_EQ(aiks_context.GetContentContext()
951  1u);
952 }
953 
954 TEST_P(AiksTest, VarietyOfTextScalesShowingRasterAndPath) {
955  DisplayListBuilder builder;
956  DlPaint paint;
957  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
958  builder.DrawPaint(paint);
959  builder.Scale(GetContentScale().x, GetContentScale().y);
960 
961  std::vector<Scalar> scales = {4, 8, 16, 24, 32};
962  std::vector<Scalar> spacing = {8, 8, 8, 8, 8};
963  Scalar space = 16;
964  Scalar x = 0;
965  for (auto i = 0u; i < scales.size(); i++) {
966  builder.Save();
967  builder.Scale(scales[i], scales[i]);
969  GetContext(), builder, "lo", "Roboto-Regular.ttf",
970  TextRenderOptions{.font_size = 16, .position = DlPoint(x, space)});
971  space += spacing[i];
972  if (i == 3) {
973  x = 10;
974  space = 16;
975  }
976  builder.Restore();
977  }
978  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
979 }
980 
981 namespace {
982 std::shared_ptr<TextFrame> MakeDefaultTextFrame(const std::string& text,
983  Scalar font_size) {
984  // Construct the text blob.
985  auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
986  if (mapping == nullptr) {
987  return nullptr;
988  }
989 
990  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
991  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
992  sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString("Hi", sk_font);
993 
994  std::shared_ptr<TextFrame> text_frame = MakeTextFrameFromTextBlobSkia(blob);
995  return text_frame;
996 }
997 
998 void DrawTextFramesMultipleScalesWithReuse(AiksTest* test,
999  Scalar first_scale,
1000  Scalar second_scale) {
1001  DisplayListBuilder builder;
1002  builder.Scale(test->GetContentScale().x, test->GetContentScale().y);
1003  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1004 
1005  std::shared_ptr<TextFrame> reuse_frame = MakeDefaultTextFrame("Hi", 20.0f);
1006  ASSERT_NE(reuse_frame, nullptr);
1007 
1008  builder.Save();
1009  builder.Translate(100, 100);
1010  builder.Scale(first_scale, first_scale);
1011  builder.DrawText(DlTextImpeller::Make(reuse_frame), 0, 0,
1012  DlPaint(DlColor::kBlue()));
1013  builder.Restore();
1014 
1015  builder.Save();
1016  builder.Translate(400, 100);
1017  builder.Scale(second_scale, second_scale);
1018  builder.DrawText(DlTextImpeller::Make(reuse_frame), 0, 0,
1019  DlPaint(DlColor::kPurple()));
1020  builder.Restore();
1021 
1022  builder.Save();
1023  builder.Translate(100, 400);
1024  builder.Scale(first_scale, first_scale);
1025  std::shared_ptr<TextFrame> single_use_frame1 =
1026  MakeDefaultTextFrame("Hi", 20.0f);
1027  builder.DrawText(DlTextImpeller::Make(single_use_frame1), 0, 0,
1028  DlPaint(DlColor::kBlue()));
1029  builder.Restore();
1030 
1031  builder.Save();
1032  builder.Translate(400, 400);
1033  builder.Scale(second_scale, second_scale);
1034  std::shared_ptr<TextFrame> single_use_frame2 =
1035  MakeDefaultTextFrame("Hi", 20.0f);
1036  builder.DrawText(DlTextImpeller::Make(single_use_frame2), 0, 0,
1037  DlPaint(DlColor::kPurple()));
1038  builder.Restore();
1039 
1040  ASSERT_TRUE(test->OpenPlaygroundHere(builder.Build()));
1041 }
1042 } // namespace
1043 
1044 TEST_P(AiksTest, TextFramesDoNotShareRenderDataBigSmall) {
1045  DrawTextFramesMultipleScalesWithReuse(this, //
1046  /*first_scale=*/4.0f, //
1047  /*second_scale=*/0.5f);
1048 }
1049 
1050 TEST_P(AiksTest, TextFramesDoNotShareRenderDataSmallBig) {
1051  DrawTextFramesMultipleScalesWithReuse(this, //
1052  /*first_scale=*/0.5f, //
1053  /*second_scale=*/4.0f);
1054 }
1055 
1056 // Verifies that non-uniform (anisotropic) scaling uses bilinear filtering
1057 // to avoid jagged/aliased text, while uniform scaling remains pixel-perfect.
1058 // Regression test for https://github.com/flutter/flutter/issues/182143
1059 TEST_P(AiksTest, TextWithNonUniformScale) {
1060  DisplayListBuilder builder;
1061 
1062  DlPaint paint;
1063  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
1064  builder.DrawPaint(paint);
1065 
1066  // Row 1: Uniform scale (should use nearest-neighbor, pixel-perfect).
1067  builder.Save();
1068  builder.Scale(2, 2);
1069  ASSERT_TRUE(RenderTextInCanvasSkia(
1070  GetContext(), builder, "Uniform 2x2", "Roboto-Regular.ttf",
1071  TextRenderOptions{.font_size = 30, .position = DlPoint(20, 40)}));
1072  builder.Restore();
1073 
1074  // Row 2: Non-uniform scale Y-only (ratio 2.0, triggers bilinear).
1075  builder.Save();
1076  builder.Scale(1, 2);
1077  ASSERT_TRUE(RenderTextInCanvasSkia(
1078  GetContext(), builder, "ScaleY 1x2", "Roboto-Regular.ttf",
1079  TextRenderOptions{.font_size = 30, .position = DlPoint(20, 140)}));
1080  builder.Restore();
1081 
1082  // Row 3: Non-uniform scale X-only (ratio 3.0, triggers bilinear).
1083  builder.Save();
1084  builder.Scale(3, 1);
1085  ASSERT_TRUE(RenderTextInCanvasSkia(
1086  GetContext(), builder, "ScaleX 3x1", "Roboto-Regular.ttf",
1087  TextRenderOptions{.font_size = 30, .position = DlPoint(20, 300)}));
1088  builder.Restore();
1089 
1090  // Row 4: Slightly non-uniform (ratio 1.1, below threshold, nearest).
1091  builder.Save();
1092  builder.Scale(2, 2.2);
1093  ASSERT_TRUE(RenderTextInCanvasSkia(
1094  GetContext(), builder, "Near-uniform 2x2.2", "Roboto-Regular.ttf",
1095  TextRenderOptions{.font_size = 30, .position = DlPoint(20, 200)}));
1096  builder.Restore();
1097 
1098  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1099 }
1100 
1101 TEST_P(AiksTest, TextGammaCorrectionGoldenTest) {
1102  constexpr const char* font_fixture = "Roboto-Regular.ttf";
1103  auto c_font_fixture = std::string(font_fixture);
1104  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
1105  ASSERT_TRUE(mapping);
1106 
1107  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
1108  SkFont sk_font(/*typeface=*/font_mgr->makeFromData(mapping), /*size=*/60);
1109  sk_font.setSubpixel(true);
1110 
1111  auto blob_corrected =
1112  SkTextBlob::MakeFromString("Gamma Corrected (true)", sk_font);
1113  ASSERT_TRUE(blob_corrected);
1114  auto text_frame_corrected = MakeTextFrameFromTextBlobSkia(blob_corrected);
1115  text_frame_corrected->SetEnableGammaCorrection(true);
1116 
1117  auto blob_uncorrected =
1118  SkTextBlob::MakeFromString("Gamma Corrected (false)", sk_font);
1119  ASSERT_TRUE(blob_uncorrected);
1120  auto text_frame_uncorrected = MakeTextFrameFromTextBlobSkia(blob_uncorrected);
1121  text_frame_uncorrected->SetEnableGammaCorrection(false);
1122 
1123  auto callback = [&]() -> sk_sp<flutter::DisplayList> {
1124  DisplayListBuilder builder;
1125 
1126  DlPaint bg_paint;
1127  bg_paint.setColor(DlColor::ARGB(1.0, 0.1, 0.1, 0.1));
1128  builder.DrawPaint(bg_paint);
1129 
1130  DlPaint text_paint;
1131  text_paint.setColor(DlColor::kWhite());
1132 
1133  builder.DrawText(/*text=*/DlTextImpeller::Make(text_frame_corrected),
1134  /*x=*/50, /*y=*/100, /*paint=*/text_paint);
1135  builder.DrawText(/*text=*/DlTextImpeller::Make(text_frame_uncorrected),
1136  /*x=*/50, /*y=*/200, /*paint=*/text_paint);
1137 
1138  builder.DrawText(/*text=*/DlTextImpeller::Make(text_frame_corrected),
1139  /*x=*/50, /*y=*/300, /*paint=*/text_paint);
1140  DlPaint diff_paint = text_paint;
1141  diff_paint.setBlendMode(DlBlendMode::kDifference);
1142  builder.DrawText(/*text=*/DlTextImpeller::Make(text_frame_uncorrected),
1143  /*x=*/50, /*y=*/300, /*paint=*/diff_paint);
1144 
1145  return builder.Build();
1146  };
1147 
1148  ASSERT_TRUE(OpenPlaygroundHere(callback));
1149 }
1150 
1151 TEST_P(AiksTest, TextWithShadowAndPosition) {
1152  DisplayListBuilder builder;
1153  builder.Scale(GetContentScale().x, GetContentScale().y);
1154  builder.Clear(DlColor::kWhite());
1155 
1156  auto frame = MakeDefaultTextFrame("Hello", 25.0f);
1157  auto text = DlTextImpeller::Make(frame);
1158  DlPaint paint = DlPaint().setColor(DlColor::kMagenta());
1159  DlPaint shadow_paint_ctm = DlPaint().setMaskFilter(
1160  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 5.0f, true));
1161  DlPaint shadow_paint_no_ctm = DlPaint().setMaskFilter(
1162  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 5.0f, false));
1163 
1164  builder.Translate(100, 100);
1165  builder.Scale(4, 4);
1166  for (int x = 10; x <= 100; x += 30) {
1167  builder.DrawText(text, x, 20, shadow_paint_ctm);
1168  builder.DrawText(text, x, 20, paint);
1169 
1170  builder.DrawText(text, x, 50, shadow_paint_no_ctm);
1171  builder.DrawText(text, x, 50, paint);
1172  }
1173 
1174  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1175 }
1176 
1177 } // namespace testing
1178 } // namespace impeller
static std::shared_ptr< DlTextImpeller > Make(const std::shared_ptr< impeller::TextFrame > &frame)
ContentContext & GetContentContext() const
Definition: aiks_context.cc:42
std::shared_ptr< Context > GetContext() const
Definition: aiks_context.cc:38
const std::shared_ptr< LazyGlyphAtlas > & GetLazyGlyphAtlas() const
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
TextShadowCache & GetTextShadowCache() const
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
void SetPosition(Point position)
void SetScreenTransform(const Matrix &transform)
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
void SetTextFrame(const std::shared_ptr< TextFrame > &frame)
void SetColor(Color color)
size_t GetCacheSizeForTesting() const
int32_t x
AiksPlayground AiksTest
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, DisplayListBuilder &canvas, const std::string &text, const std::string_view &font_fixture, const TextRenderOptions &options={}, const std::optional< SkFont > &font=std::nullopt)
TEST_P(AiksTest, TextWithShadowAndPosition)
static constexpr std::string_view kFontFixture
constexpr float k2Pi
Definition: constants.h:29
flutter::DlRect DlRect
Definition: dl_dispatcher.h:30
float Scalar
Definition: scalar.h:19
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips, std::optional< PixelFormat > target_pixel_format)
Render the provided display list to a texture with the given size.
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:32
TPoint< Scalar > Point
Definition: point.h:426
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:29
flutter::DlPath DlPath
Definition: dl_dispatcher.h:34
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
std::shared_ptr< ContextGLES > context
std::shared_ptr< RenderPass > render_pass
std::shared_ptr< CommandBuffer > command_buffer
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:631
std::shared_ptr< DlMaskFilter > filter
Scalar font_size
bool is_subpixel