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 "display_list/display_list.h"
6 #include "display_list/dl_blend_mode.h"
7 #include "display_list/dl_tile_mode.h"
8 #include "display_list/effects/dl_color_source.h"
9 #include "display_list/effects/dl_mask_filter.h"
10 #include "flutter/display_list/dl_builder.h"
11 #include "flutter/display_list/dl_color.h"
12 #include "flutter/display_list/dl_paint.h"
13 #include "flutter/fml/build_config.h"
15 #include "flutter/testing/testing.h"
17 #include "impeller/entity/entity.h"
20 #include "include/core/SkMatrix.h"
21 #include "include/core/SkRect.h"
22 
24 #include "txt/platform.h"
25 
26 using namespace flutter;
27 /////////////////////////////////////////////////////
28 
29 namespace impeller {
30 namespace testing {
31 
33  bool stroke = false;
34  Scalar font_size = 50;
36  DlColor color = DlColor::kYellow();
37  SkPoint position = SkPoint::Make(100, 200);
38  std::shared_ptr<DlMaskFilter> filter;
39  bool is_subpixel = false;
40 };
41 
42 bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
43  DisplayListBuilder& canvas,
44  const std::string& text,
45  const std::string_view& font_fixture,
46  const TextRenderOptions& options = {}) {
47  // Draw the baseline.
48  DlPaint paint;
49  paint.setColor(DlColor::kAqua().withAlpha(255 * 0.25));
50  canvas.DrawRect(SkRect::MakeXYWH(options.position.x() - 50,
51  options.position.y(), 900, 10),
52  paint);
53 
54  // Mark the point at which the text is drawn.
55  paint.setColor(DlColor::kRed().withAlpha(255 * 0.25));
56  canvas.DrawCircle(options.position, 5.0, paint);
57 
58  // Construct the text blob.
59  auto c_font_fixture = std::string(font_fixture);
60  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
61  if (!mapping) {
62  return false;
63  }
64  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
65  SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
66  if (options.is_subpixel) {
67  sk_font.setSubpixel(true);
68  }
69  auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
70  if (!blob) {
71  return false;
72  }
73 
74  // Create the Impeller text frame and draw it at the designated baseline.
75  auto frame = MakeTextFrameFromTextBlobSkia(blob);
76 
77  DlPaint text_paint;
78  text_paint.setColor(options.color);
79  text_paint.setMaskFilter(options.filter);
80  text_paint.setStrokeWidth(options.stroke_width);
81  text_paint.setDrawStyle(options.stroke ? DlDrawStyle::kStroke
82  : DlDrawStyle::kFill);
83  canvas.DrawTextFrame(frame, options.position.x(), options.position.y(),
84  text_paint);
85  return true;
86 }
87 
88 TEST_P(AiksTest, CanRenderTextFrame) {
89  DisplayListBuilder builder;
90 
91  DlPaint paint;
92  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
93  builder.DrawPaint(paint);
94  ASSERT_TRUE(RenderTextInCanvasSkia(
95  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
96  "Roboto-Regular.ttf"));
97 
98  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
99 }
100 
101 TEST_P(AiksTest, CanRenderTextFrameWithInvertedTransform) {
102  DisplayListBuilder builder;
103 
104  DlPaint paint;
105  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
106  builder.DrawPaint(paint);
107  builder.Translate(1000, 0);
108  builder.Scale(-1, 1);
109 
110  ASSERT_TRUE(RenderTextInCanvasSkia(
111  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
112  "Roboto-Regular.ttf"));
113 
114  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
115 }
116 
117 TEST_P(AiksTest, CanRenderStrokedTextFrame) {
118  DisplayListBuilder builder;
119 
120  DlPaint paint;
121  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
122  builder.DrawPaint(paint);
123 
124  ASSERT_TRUE(RenderTextInCanvasSkia(
125  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
126  "Roboto-Regular.ttf",
127  {
128  .stroke = true,
129  }));
130  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
131 }
132 
133 TEST_P(AiksTest, CanRenderTextStrokeWidth) {
134  DisplayListBuilder builder;
135 
136  DlPaint paint;
137  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
138  builder.DrawPaint(paint);
139 
140  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "LMNOP VWXYZ",
141  "Roboto-Medium.ttf",
142  {
143  .stroke = true,
144  .stroke_width = 4,
145  }));
146  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
147 }
148 
149 TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
150  DisplayListBuilder builder;
151 
152  DlPaint paint;
153  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
154  builder.DrawPaint(paint);
155  builder.Scale(0.5, 0.5);
156 
157  ASSERT_TRUE(RenderTextInCanvasSkia(
158  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
159  "Roboto-Regular.ttf"));
160  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
161 }
162 
163 TEST_P(AiksTest, CanRenderTextFrameWithScalingOverflow) {
164  Scalar scale = 60.0;
165  Scalar offsetx = -500.0;
166  Scalar offsety = 700.0;
167  auto callback = [&]() -> sk_sp<DisplayList> {
168  if (AiksTest::ImGuiBegin("Controls", nullptr,
169  ImGuiWindowFlags_AlwaysAutoResize)) {
170  ImGui::SliderFloat("scale", &scale, 1.f, 300.f);
171  ImGui::SliderFloat("offsetx", &offsetx, -600.f, 100.f);
172  ImGui::SliderFloat("offsety", &offsety, 600.f, 2048.f);
173  ImGui::End();
174  }
175  DisplayListBuilder builder;
176  builder.Scale(GetContentScale().x, GetContentScale().y);
177  DlPaint paint;
178  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
179  builder.DrawPaint(paint);
180  builder.Scale(scale, scale);
181 
183  GetContext(), builder, "test", "Roboto-Regular.ttf",
185  .position = SkPoint::Make(offsetx / scale, offsety / scale),
186  });
187  return builder.Build();
188  };
189  ASSERT_TRUE(OpenPlaygroundHere(callback));
190 }
191 
192 TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
193  Scalar fine_scale = 0.f;
194  bool is_subpixel = false;
195  auto callback = [&]() -> sk_sp<DisplayList> {
196  if (AiksTest::ImGuiBegin("Controls", nullptr,
197  ImGuiWindowFlags_AlwaysAutoResize)) {
198  ImGui::SliderFloat("Fine Scale", &fine_scale, -1, 1);
199  ImGui::Checkbox("subpixel", &is_subpixel);
200  ImGui::End();
201  }
202 
203  DisplayListBuilder builder;
204  DlPaint paint;
205  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
206  builder.DrawPaint(paint);
207  Scalar scale = 2.625 + fine_scale;
208  builder.Scale(scale, scale);
209  RenderTextInCanvasSkia(GetContext(), builder,
210  "the quick brown fox jumped over the lazy dog!.?",
211  "Roboto-Regular.ttf",
212  TextRenderOptions{.is_subpixel = is_subpixel});
213  return builder.Build();
214  };
215 
216  ASSERT_TRUE(OpenPlaygroundHere(callback));
217 }
218 
219 // https://github.com/flutter/flutter/issues/164958
220 TEST_P(AiksTest, TextRotated180Degrees) {
221  float fpivot[2] = {200 + 30, 200 - 20};
222  float rotation = 180;
223  float foffset[2] = {200, 200};
224 
225  auto callback = [&]() -> sk_sp<DisplayList> {
226  if (AiksTest::ImGuiBegin("Controls", nullptr,
227  ImGuiWindowFlags_AlwaysAutoResize)) {
228  ImGui::SliderFloat("pivotx", &fpivot[0], 0, 300);
229  ImGui::SliderFloat("pivoty", &fpivot[1], 0, 300);
230  ImGui::SliderFloat("rotation", &rotation, 0, 360);
231  ImGui::SliderFloat("foffsetx", &foffset[0], 0, 300);
232  ImGui::SliderFloat("foffsety", &foffset[1], 0, 300);
233  ImGui::End();
234  }
235  DisplayListBuilder builder;
236  builder.Scale(GetContentScale().x, GetContentScale().y);
237  builder.DrawPaint(DlPaint().setColor(DlColor(0xffffeeff)));
238 
239  builder.Save();
240  DlPoint pivot = Point(fpivot[0], fpivot[1]);
241  builder.Translate(pivot.x, pivot.y);
242  builder.Rotate(rotation);
243  builder.Translate(-pivot.x, -pivot.y);
244 
246  GetContext(), builder, "test", "Roboto-Regular.ttf",
248  .color = DlColor::kBlack(),
249  .position = SkPoint::Make(foffset[0], foffset[1]),
250  });
251 
252  builder.Restore();
253  return builder.Build();
254  };
255  ASSERT_TRUE(OpenPlaygroundHere(callback));
256 }
257 
258 TEST_P(AiksTest, TextFrameSubpixelAlignment) {
259  // "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
260  std::array<Scalar, 20> phase_offsets = {
261  7.82637e-06, 0.131538, 0.755605, 0.45865, 0.532767,
262  0.218959, 0.0470446, 0.678865, 0.679296, 0.934693,
263  0.383502, 0.519416, 0.830965, 0.0345721, 0.0534616,
264  0.5297, 0.671149, 0.00769819, 0.383416, 0.0668422};
265  auto callback = [&]() -> sk_sp<DisplayList> {
266  static float font_size = 20;
267  static float phase_variation = 0.2;
268  static float speed = 0.5;
269  static float magnitude = 100;
270  if (AiksTest::ImGuiBegin("Controls", nullptr,
271  ImGuiWindowFlags_AlwaysAutoResize)) {
272  ImGui::SliderFloat("Font size", &font_size, 5, 50);
273  ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
274  ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
275  ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
276  ImGui::End();
277  }
278 
279  DisplayListBuilder builder;
280  builder.Scale(GetContentScale().x, GetContentScale().y);
281 
282  for (size_t i = 0; i < phase_offsets.size(); i++) {
283  SkPoint position = SkPoint::Make(
284  200 +
285  magnitude * std::sin((-phase_offsets[i] * k2Pi * phase_variation +
286  GetSecondsElapsed() * speed)), //
287  200 + i * font_size * 1.1 //
288  );
290  GetContext(), builder,
291  "the quick brown fox jumped over "
292  "the lazy dog!.?",
293  "Roboto-Regular.ttf",
294  {.font_size = font_size, .position = position})) {
295  return nullptr;
296  }
297  }
298  return builder.Build();
299  };
300 
301  ASSERT_TRUE(OpenPlaygroundHere(callback));
302 }
303 
304 TEST_P(AiksTest, CanRenderItalicizedText) {
305  DisplayListBuilder builder;
306 
307  DlPaint paint;
308  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
309  builder.DrawPaint(paint);
310 
311  ASSERT_TRUE(RenderTextInCanvasSkia(
312  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
313  "HomemadeApple.ttf"));
314  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
315 }
316 
317 static constexpr std::string_view kFontFixture =
318 #if FML_OS_MACOSX
319  "Apple Color Emoji.ttc";
320 #else
321  "NotoColorEmoji.ttf";
322 #endif
323 
324 TEST_P(AiksTest, CanRenderEmojiTextFrame) {
325  DisplayListBuilder builder;
326 
327  DlPaint paint;
328  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
329  builder.DrawPaint(paint);
330 
331  ASSERT_TRUE(RenderTextInCanvasSkia(
332  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture));
333  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
334 }
335 
336 TEST_P(AiksTest, CanRenderEmojiTextFrameWithBlur) {
337  DisplayListBuilder builder;
338 
339  builder.Scale(GetContentScale().x, GetContentScale().y);
340  DlPaint paint;
341  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
342  builder.DrawPaint(paint);
343 
344  ASSERT_TRUE(RenderTextInCanvasSkia(
345  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
347  .color = DlColor::kBlue(),
348  .filter = DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 4)}));
349  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
350 }
351 
352 TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
353  DisplayListBuilder builder;
354 
355  DlPaint paint;
356  paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
357  builder.DrawPaint(paint);
358 
359  ASSERT_TRUE(RenderTextInCanvasSkia(
360  GetContext(), builder, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊", kFontFixture,
361  {.color = DlColor::kBlack().modulateOpacity(0.5)}));
362  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
363 }
364 
365 TEST_P(AiksTest, CanRenderTextInSaveLayer) {
366  DisplayListBuilder builder;
367 
368  DlPaint paint;
369  paint.setColor(DlColor::ARGB(0.1, 0.1, 0.1, 0.1));
370  builder.DrawPaint(paint);
371 
372  builder.Translate(100, 100);
373  builder.Scale(0.5, 0.5);
374 
375  // Blend the layer with the parent pass using kClear to expose the coverage.
376  paint.setBlendMode(DlBlendMode::kClear);
377  builder.SaveLayer(nullptr, &paint);
378  ASSERT_TRUE(RenderTextInCanvasSkia(
379  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
380  "Roboto-Regular.ttf"));
381  builder.Restore();
382 
383  // Render the text again over the cleared coverage rect.
384  ASSERT_TRUE(RenderTextInCanvasSkia(
385  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
386  "Roboto-Regular.ttf"));
387 
388  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
389 }
390 
391 TEST_P(AiksTest, CanRenderTextOutsideBoundaries) {
392  DisplayListBuilder builder;
393  builder.Translate(200, 150);
394 
395  // Construct the text blob.
396  auto mapping = flutter::testing::OpenFixtureAsSkData("wtf.otf");
397  ASSERT_NE(mapping, nullptr);
398 
399  Scalar font_size = 80;
400  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
401  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
402 
403  DlPaint text_paint;
404  text_paint.setColor(DlColor::kBlue().withAlpha(255 * 0.8));
405 
406  struct {
407  SkPoint position;
408  const char* text;
409  } text[] = {{SkPoint::Make(0, 0), "0F0F0F0"},
410  {SkPoint::Make(1, 2), "789"},
411  {SkPoint::Make(1, 3), "456"},
412  {SkPoint::Make(1, 4), "123"},
413  {SkPoint::Make(0, 6), "0F0F0F0"}};
414  for (auto& t : text) {
415  builder.Save();
416  builder.Translate(t.position.x() * font_size * 2,
417  t.position.y() * font_size * 1.1);
418  {
419  auto blob = SkTextBlob::MakeFromString(t.text, sk_font);
420  ASSERT_NE(blob, nullptr);
421  auto frame = MakeTextFrameFromTextBlobSkia(blob);
422  builder.DrawTextFrame(frame, 0, 0, text_paint);
423  }
424  builder.Restore();
425  }
426 
427  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
428 }
429 
430 TEST_P(AiksTest, TextRotated) {
431  DisplayListBuilder builder;
432 
433  builder.Scale(GetContentScale().x, GetContentScale().y);
434  DlPaint paint;
435  paint.setColor(DlColor::ARGB(0.1, 0.1, 0.1, 1.0));
436  builder.DrawPaint(paint);
437 
438  builder.Transform(SkM44::ColMajor(Matrix(0.25, -0.3, 0, -0.002, //
439  0, 0.5, 0, 0, //
440  0, 0, 0.3, 0, //
441  100, 100, 0, 1.3)
442  .m));
443  ASSERT_TRUE(RenderTextInCanvasSkia(
444  GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
445  "Roboto-Regular.ttf"));
446 
447  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
448 }
449 
450 TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) {
451  DisplayListBuilder builder;
452 
453  Matrix matrix = Matrix(1.0, 0.0, 0.0, 0.0, //
454  0.0, 1.0, 0.0, 0.0, //
455  0.0, 0.0, 1.0, 0.01, //
456  0.0, 0.0, 0.0, 1.0) * //
457  Matrix::MakeRotationY({Degrees{10}});
458 
459  builder.Transform(SkM44::ColMajor(matrix.m));
460 
461  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
462  "Roboto-Regular.ttf"));
463  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
464 }
465 
466 TEST_P(AiksTest, DrawScaledTextWithPerspectiveSaveLayer) {
467  DisplayListBuilder builder;
468 
469  Matrix matrix = Matrix(1.0, 0.0, 0.0, 0.0, //
470  0.0, 1.0, 0.0, 0.0, //
471  0.0, 0.0, 1.0, 0.01, //
472  0.0, 0.0, 0.0, 1.0) * //
473  Matrix::MakeRotationY({Degrees{10}});
474 
475  DlPaint save_paint;
476  SkRect window_bounds =
477  SkRect::MakeXYWH(0, 0, GetWindowSize().width, GetWindowSize().height);
478  // Note: bounds were not needed by the AIKS version, which may indicate a bug.
479  builder.SaveLayer(&window_bounds, &save_paint);
480  builder.Transform(SkM44::ColMajor(matrix.m));
481 
482  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
483  "Roboto-Regular.ttf"));
484 
485  builder.Restore();
486  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
487 }
488 
489 TEST_P(AiksTest, CanRenderTextWithLargePerspectiveTransform) {
490  // Verifies that text scales are clamped to work around
491  // https://github.com/flutter/flutter/issues/136112 .
492 
493  DisplayListBuilder builder;
494 
495  DlPaint save_paint;
496  builder.SaveLayer(nullptr, &save_paint);
497  builder.Transform(SkM44::ColMajor(Matrix(2000, 0, 0, 0, //
498  0, 2000, 0, 0, //
499  0, 0, -1, 9000, //
500  0, 0, -1, 7000 //
501  )
502  .m));
503 
504  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), builder, "Hello world",
505  "Roboto-Regular.ttf"));
506  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
507 }
508 
509 TEST_P(AiksTest, CanRenderTextWithPerspectiveTransformInSublist) {
510  DisplayListBuilder text_builder;
511  ASSERT_TRUE(RenderTextInCanvasSkia(GetContext(), text_builder, "Hello world",
512  "Roboto-Regular.ttf"));
513  auto text_display_list = text_builder.Build();
514 
515  DisplayListBuilder builder;
516 
517  Matrix matrix = Matrix::MakeRow(2.0, 0.0, 0.0, 0.0, //
518  0.0, 2.0, 0.0, 0.0, //
519  0.0, 0.0, 1.0, 0.0, //
520  0.0, 0.002, 0.0, 1.0);
521 
522  DlPaint save_paint;
523  SkRect window_bounds =
524  SkRect::MakeXYWH(0, 0, GetWindowSize().width, GetWindowSize().height);
525  builder.SaveLayer(&window_bounds, &save_paint);
526  builder.Transform(SkM44::ColMajor(matrix.m));
527  builder.DrawDisplayList(text_display_list, 1.0f);
528  builder.Restore();
529 
530  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
531 }
532 
533 // This currently renders solid blue, as the support for text color sources was
534 // moved into DLDispatching. Path data requires the SkTextBlobs which are not
535 // used in impeller::TextFrames.
536 TEST_P(AiksTest, TextForegroundShaderWithTransform) {
537  auto mapping = flutter::testing::OpenFixtureAsSkData("Roboto-Regular.ttf");
538  ASSERT_NE(mapping, nullptr);
539 
540  Scalar font_size = 100;
541  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
542  SkFont sk_font(font_mgr->makeFromData(mapping), font_size);
543 
544  DlPaint text_paint;
545  text_paint.setColor(DlColor::kBlue());
546 
547  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
548  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
549  std::vector<Scalar> stops = {
550  0.0,
551  1.0,
552  };
553  text_paint.setColorSource(DlColorSource::MakeLinear(
554  /*start_point=*/DlPoint(0, 0), //
555  /*end_point=*/DlPoint(100, 100), //
556  /*stop_count=*/2, //
557  /*colors=*/colors.data(), //
558  /*stops=*/stops.data(), //
559  /*tile_mode=*/DlTileMode::kRepeat //
560  ));
561 
562  DisplayListBuilder builder;
563  builder.Translate(100, 100);
564  builder.Rotate(45);
565 
566  auto blob = SkTextBlob::MakeFromString("Hello", sk_font);
567  ASSERT_NE(blob, nullptr);
568  auto frame = MakeTextFrameFromTextBlobSkia(blob);
569  builder.DrawTextFrame(frame, 0, 0, text_paint);
570 
571  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
572 }
573 
574 // Regression test for https://github.com/flutter/flutter/issues/157885.
575 TEST_P(AiksTest, DifferenceClipsMustRenderIdenticallyAcrossBackends) {
576  DisplayListBuilder builder;
577 
578  DlPaint paint;
579  DlColor clear_color(1.0, 0.5, 0.5, 0.5, DlColorSpace::kSRGB);
580  paint.setColor(clear_color);
581  builder.DrawPaint(paint);
582 
583  DlMatrix identity = {
584  1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
585  0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
586  };
587  builder.Save();
588  builder.Transform(identity);
589 
590  DlRect frame = DlRect::MakeLTRB(1.0, 1.0, 1278.0, 763.0);
591  DlColor white(1.0, 1.0, 1.0, 1.0, DlColorSpace::kSRGB);
592  paint.setColor(white);
593  builder.DrawRect(frame, paint);
594 
595  builder.Save();
596  builder.ClipRect(frame, DlCanvas::ClipOp::kIntersect);
597 
598  DlMatrix rect_xform = {
599  0.8241262, 0.56640625, 0.0, 0.0, -0.56640625, 0.8241262, 0.0, 0.0,
600  0.0, 0.0, 1.0, 0.0, 271.1137, 489.4733, 0.0, 1.0,
601  };
602  builder.Save();
603  builder.Transform(rect_xform);
604 
605  DlRect rect = DlRect::MakeLTRB(0.0, 0.0, 100.0, 100.0);
606  DlColor bluish(1.0, 0.184, 0.501, 0.929, DlColorSpace::kSRGB);
607  paint.setColor(bluish);
608  DlRoundRect rrect = DlRoundRect::MakeRectRadius(rect, 18.0);
609  builder.DrawRoundRect(rrect, paint);
610 
611  builder.Save();
612  builder.ClipRect(rect, DlCanvas::ClipOp::kIntersect);
613  builder.Restore();
614 
615  builder.Restore();
616 
617  DlMatrix path_xform = {
618  1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
619  0.0, 0.0, 1.0, 0.0, 675.0, 279.5, 0.0, 1.0,
620  };
621  builder.Save();
622  builder.Transform(path_xform);
623 
624  SkPath path;
625  path.moveTo(87.5, 349.5);
626  path.lineTo(25.0, 29.5);
627  path.lineTo(150.0, 118.0);
628  path.lineTo(25.0, 118.0);
629  path.lineTo(150.0, 29.5);
630  path.close();
631 
632  DlColor fill_color(1.0, 1.0, 0.0, 0.0, DlColorSpace::kSRGB);
633  DlColor stroke_color(1.0, 0.0, 0.0, 0.0, DlColorSpace::kSRGB);
634  paint.setColor(fill_color);
635  paint.setDrawStyle(DlDrawStyle::kFill);
636  builder.DrawPath(DlPath(path), paint);
637 
638  paint.setColor(stroke_color);
639  paint.setStrokeWidth(2.0);
640  paint.setDrawStyle(DlDrawStyle::kStroke);
641  builder.DrawPath(path, paint);
642 
643  builder.Restore();
644  builder.Restore();
645  builder.Restore();
646 
647  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
648 }
649 
650 TEST_P(AiksTest, TextContentsMismatchedTransformTest) {
651  AiksContext aiks_context(GetContext(),
652  std::make_shared<TypographerContextSkia>());
653 
654  // Verifies that TextContents only use the scale/transform that is
655  // computed during preroll.
656  constexpr const char* font_fixture = "Roboto-Regular.ttf";
657 
658  // Construct the text blob.
659  auto c_font_fixture = std::string(font_fixture);
660  auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
661  ASSERT_TRUE(mapping);
662 
663  sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
664  SkFont sk_font(font_mgr->makeFromData(mapping), 16);
665 
666  auto blob = SkTextBlob::MakeFromString("Hello World", sk_font);
667  ASSERT_TRUE(blob);
668 
669  auto text_frame = MakeTextFrameFromTextBlobSkia(blob);
670 
671  // Simulate recording the text frame during preroll.
672  Matrix preroll_matrix =
673  Matrix::MakeTranslateScale({1.5, 1.5, 1}, {100, 50, 0});
674  Point preroll_point = Point{23, 45};
675  {
676  auto scale = TextFrame::RoundScaledFontSize(
677  (preroll_matrix * Matrix::MakeTranslation(preroll_point))
678  .GetMaxBasisLengthXY());
679 
680  aiks_context.GetContentContext().GetLazyGlyphAtlas()->AddTextFrame(
681  text_frame, //
682  scale, //
683  preroll_point, //
684  preroll_matrix,
685  std::nullopt //
686  );
687  }
688 
689  // Now simulate rendering with a slightly different scale factor.
690  RenderTarget render_target =
691  aiks_context.GetContentContext()
693  ->CreateOffscreenMSAA(*aiks_context.GetContext(), {100, 100}, 1);
694 
695  TextContents text_contents;
696  text_contents.SetTextFrame(text_frame);
697  text_contents.SetOffset(preroll_point);
698  text_contents.SetScale(1.6);
699  text_contents.SetColor(Color::Aqua());
700 
701  Matrix not_preroll_matrix =
702  Matrix::MakeTranslateScale({1.5, 1.5, 1}, {100, 50, 0});
703 
704  Entity entity;
705  entity.SetTransform(not_preroll_matrix);
706 
707  std::shared_ptr<CommandBuffer> command_buffer =
708  aiks_context.GetContext()->CreateCommandBuffer();
709  std::shared_ptr<RenderPass> render_pass =
710  command_buffer->CreateRenderPass(render_target);
711 
712  EXPECT_TRUE(text_contents.Render(aiks_context.GetContentContext(), entity,
713  *render_pass));
714 }
715 
716 } // namespace testing
717 } // namespace impeller
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
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:60
void SetOffset(Vector2 offset)
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
void SetTextFrame(const std::shared_ptr< TextFrame > &frame)
void SetScale(Scalar scale)
Definition: text_contents.h:58
void SetColor(Color color)
int32_t x
TEST_P(AiksTest, TextContentsMismatchedTransformTest)
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, DisplayListBuilder &canvas, const std::string &text, const std::string_view &font_fixture, const TextRenderOptions &options={})
static constexpr std::string_view kFontFixture
constexpr float k2Pi
Definition: constants.h:29
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
float Scalar
Definition: scalar.h:18
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
TPoint< Scalar > Point
Definition: point.h:327
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:28
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
const Scalar stroke_width
const Scalar scale
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
Scalar m[16]
Definition: matrix.h:39
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:543
std::shared_ptr< DlMaskFilter > filter