Flutter Impeller
impeller_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/fml/native_library.h"
6 #include "flutter/fml/string_conversion.h"
7 #include "flutter/testing/testing.h"
24 
26 
29 
30 // Just ensures that context can be subclassed.
31 class ContextSub : public hpp::Context {};
32 
33 TEST_P(InteropPlaygroundTest, CanCreateContext) {
34  auto context = CreateContext();
35  ASSERT_TRUE(context);
36 }
37 
38 TEST_P(InteropPlaygroundTest, CanCreateDisplayListBuilder) {
39  hpp::DisplayListBuilder builder;
40  ASSERT_TRUE(builder);
41  ASSERT_TRUE(ToImpellerType(builder.GetTransform()).IsIdentity());
42  ASSERT_EQ(builder.GetSaveCount(), 1u);
43  builder.Save();
44  ASSERT_EQ(builder.GetSaveCount(), 2u);
45  builder.Restore();
46  ASSERT_EQ(builder.GetSaveCount(), 1u);
47 }
48 
49 TEST_P(InteropPlaygroundTest, CanCreateSurface) {
50  if (GetBackend() != PlaygroundBackend::kOpenGLES &&
51  GetBackend() != PlaygroundBackend::kOpenGLESSDF) {
52  GTEST_SKIP()
53  << "This test checks wrapping FBOs which is an OpenGL ES only call.";
54  return;
55  }
56  auto context = CreateContext();
57  ASSERT_TRUE(context);
58  const auto window_size = GetWindowSize();
59  ImpellerISize size = {window_size.width, window_size.height};
60  auto surface = Adopt<Surface>(ImpellerSurfaceCreateWrappedFBONew(
61  context.GetC(), //
62  0u, //
64  &size) //
65  );
66  ASSERT_TRUE(surface);
67 }
68 
70  auto builder =
71  Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
72  auto paint = Adopt<Paint>(ImpellerPaintNew());
73  ImpellerColor color = {0.0, 0.0, 1.0, 1.0};
74  ImpellerPaintSetColor(paint.GetC(), &color);
75  ImpellerRect rect = {10, 20, 100, 200};
76  ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
77  color = {1.0, 0.0, 0.0, 1.0};
78  ImpellerPaintSetColor(paint.GetC(), &color);
79  ImpellerDisplayListBuilderTranslate(builder.GetC(), 110, 210);
80  ImpellerMatrix scale_transform = {
81  // clang-format off
82  2.0, 0.0, 0.0, 0.0, //
83  0.0, 2.0, 0.0, 0.0, //
84  0.0, 0.0, 1.0, 0.0, //
85  0.0, 0.0, 0.0, 1.0, //
86  // clang-format on
87  };
88  ImpellerDisplayListBuilderTransform(builder.GetC(), &scale_transform);
89  ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
90  auto dl = Adopt<DisplayList>(
92  ASSERT_TRUE(dl);
93  ASSERT_TRUE(
94  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
95  ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
96  return true;
97  }));
98 }
99 
101  auto compressed = LoadFixtureImageCompressed(
102  flutter::testing::OpenFixtureAsMapping("boston.jpg"));
103  ASSERT_NE(compressed, nullptr);
104  auto decompressed = std::make_shared<impeller::DecompressedImage>(
105  compressed->Decode().ConvertToRGBA());
106  ASSERT_TRUE(decompressed->IsValid());
107  auto mapping = std::make_unique<hpp::Mapping>(
108  decompressed->GetAllocation()->GetMapping(),
109  decompressed->GetAllocation()->GetSize(), [decompressed]() {
110  // Mapping will be dropped on the floor.
111  });
112 
113  auto context = GetHPPContext();
114  ImpellerTextureDescriptor desc = {};
116  desc.size = {decompressed->GetSize().width, decompressed->GetSize().height};
117  desc.mip_count = 1u;
118  auto texture = hpp::Texture::WithContents(context, desc, std::move(mapping));
119  ASSERT_TRUE(texture);
120 
121  auto dl = hpp::DisplayListBuilder{}
122  .DrawTexture(texture, {100, 100},
123  kImpellerTextureSamplingLinear, hpp::Paint{})
124  .Build();
125 
126  ASSERT_TRUE(
127  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
128  hpp::Surface window(surface.GetC());
129  window.Draw(dl);
130  return true;
131  }));
132 }
133 
134 TEST_P(InteropPlaygroundTest, CanCreateOpenGLImage) {
135  auto context = GetInteropContext();
136 
137  auto impeller_context = context->GetContext();
138 
139  if (impeller_context->GetBackendType() !=
141  GTEST_SKIP() << "This test works with OpenGL handles is only suitable for "
142  "that backend.";
143  return;
144  }
145 
146  const auto& gl_context = ContextGLES::Cast(*impeller_context);
147  const auto& gl = gl_context.GetReactor()->GetProcTable();
148 
149  constexpr ISize external_texture_size = {200, 300};
150 
151  Allocation texture_data;
152  ASSERT_TRUE(
153  texture_data.Truncate(Bytes{external_texture_size.Area() * 4u}, false));
154 
155  const auto kClearColor = Color::Fuchsia().ToR8G8B8A8();
156 
157  for (size_t i = 0; i < external_texture_size.Area() * 4u; i += 4u) {
158  memcpy(texture_data.GetBuffer() + i, kClearColor.data(), 4);
159  }
160 
161  GLuint external_texture = GL_NONE;
162  gl.GenTextures(1u, &external_texture);
163  ASSERT_NE(external_texture, 0u);
164  gl.BindTexture(GL_TEXTURE_2D, external_texture);
165  gl.TexImage2D(GL_TEXTURE_2D, //
166  0, //
167  GL_RGBA, //
168  external_texture_size.width, //
169  external_texture_size.height, //
170  0, //
171  GL_RGBA, //
172  GL_UNSIGNED_BYTE, //
173  texture_data.GetBuffer() //
174  );
175 
176  ImpellerTextureDescriptor desc = {};
178  desc.size = {external_texture_size.width, external_texture_size.height};
179  desc.mip_count = 1u;
180  auto texture = Adopt<Texture>(ImpellerTextureCreateWithOpenGLTextureHandleNew(
181  context.GetC(), //
182  &desc, //
183  external_texture //
184  ));
185  ASSERT_TRUE(texture);
186 
187  ASSERT_EQ(ImpellerTextureGetOpenGLHandle(texture.GetC()), external_texture);
188 
189  auto builder =
190  Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
191  ImpellerPoint point = {100, 100};
192  ImpellerDisplayListBuilderDrawTexture(builder.GetC(), texture.GetC(), &point,
194  nullptr);
195  auto dl = Adopt<DisplayList>(
197  ASSERT_TRUE(
198  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
199  ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
200  return true;
201  }));
202 }
203 
204 TEST_P(InteropPlaygroundTest, ClearsOpenGLStancilStateAfterTransition) {
205  auto context = GetInteropContext();
206  auto impeller_context = context->GetContext();
207  if (impeller_context->GetBackendType() !=
209  GTEST_SKIP() << "This test works with OpenGL handles is only suitable for "
210  "that backend.";
211  return;
212  }
213  const auto& gl_context = ContextGLES::Cast(*impeller_context);
214  const auto& gl = gl_context.GetReactor()->GetProcTable();
215  auto builder =
216  Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
217  auto paint = Adopt<Paint>(ImpellerPaintNew());
218  ImpellerColor color = {0.0, 0.0, 1.0, 1.0};
219  ImpellerPaintSetColor(paint.GetC(), &color);
220  ImpellerRect rect = {10, 20, 100, 200};
221  ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
222  color = {1.0, 0.0, 0.0, 1.0};
223  ImpellerPaintSetColor(paint.GetC(), &color);
224  ImpellerDisplayListBuilderTranslate(builder.GetC(), 110, 210);
225  ImpellerDisplayListBuilderClipRect(builder.GetC(), &rect,
227  ImpellerDisplayListBuilderDrawRect(builder.GetC(), &rect, paint.GetC());
228  auto dl = Adopt<DisplayList>(
230  ASSERT_TRUE(dl);
231  ASSERT_TRUE(
232  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
233  ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
234  // OpenGL state is reset even though the operations above enable a
235  // stencil check.
236  GLboolean stencil_enabled = true;
237  gl.GetBooleanv(GL_STENCIL_TEST, &stencil_enabled);
238  return stencil_enabled == GL_FALSE;
239  }));
240 }
241 
242 TEST_P(InteropPlaygroundTest, CanCreateParagraphs) {
243  // Create a typography context.
244  hpp::TypographyContext type_context;
245  ASSERT_TRUE(type_context);
246 
247  // Create a builder.
248  hpp::ParagraphBuilder builder(type_context);
249  ASSERT_TRUE(builder);
250 
251  // Create a paragraph style with the font size and foreground and background
252  // colors.
253  hpp::ParagraphStyle style;
254  ASSERT_TRUE(style);
255  style.SetFontSize(150.0f);
256  style.SetHeight(2.0f);
257 
258  {
259  hpp::Paint paint;
260  ASSERT_TRUE(paint);
261  paint.SetColor({1.0, 0.0, 0.0, 1.0});
262  style.SetForeground(paint);
263  }
264 
265  {
266  hpp::Paint paint;
267  paint.SetColor({1.0, 1.0, 1.0, 1.0});
268  style.SetBackground(paint);
269  }
270 
271  // Push the style onto the style stack.
272  builder.PushStyle(style);
273  std::string text = "the ⚡️ quick ⚡️ brown 🦊 fox jumps over the lazy dog 🐶.";
274 
275  // Add the paragraph text data.
276  builder.AddText(text);
277 
278  // Layout and build the paragraph.
279  auto paragraph = builder.Build(1200.0f);
280  ASSERT_TRUE(paragraph);
281 
282  // Create a display list with just the paragraph drawn into it.
283  hpp::DisplayListBuilder dl_builder;
284  dl_builder.DrawParagraph(paragraph, {20, 20});
285 
286  // Build the display list.
287  auto dl = dl_builder.Build();
288 
289  ASSERT_TRUE(
290  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
291  hpp::Surface window(surface.GetC());
292  window.Draw(dl);
293  return true;
294  }));
295 }
296 
297 TEST_P(InteropPlaygroundTest, CanCreateDecorations) {
298  hpp::TypographyContext context;
299  auto para =
300  hpp::ParagraphBuilder(context)
301  .PushStyle(
302  hpp::ParagraphStyle{}
303  .SetForeground(hpp::Paint{}.SetColor({1.0, 0.0, 0.0, 1.0}))
304  .SetFontSize(150.0f)
305  .SetTextDecoration(ImpellerTextDecoration{
308  .color = ImpellerColor{0.0, 1.0, 0.0, 0.75},
310  .thickness_multiplier = 1.5,
311  }))
312  .AddText(std::string{"Holy text decorations Batman!"})
313  .Build(900);
314  auto dl = hpp::DisplayListBuilder{}.DrawParagraph(para, {100, 100}).Build();
315  ASSERT_TRUE(
316  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
317  hpp::Surface window(surface.GetC());
318  window.Draw(dl);
319  return true;
320  }));
321 }
322 
323 TEST_P(InteropPlaygroundTest, CanCreateShapes) {
324  hpp::DisplayListBuilder builder;
325 
326  hpp::Paint red_paint;
327  red_paint.SetColor({1.0, 0.0, 0.0, 1.0});
328  red_paint.SetStrokeWidth(10.0);
329 
330  builder.Translate(10, 10);
331  builder.DrawRect({0, 0, 100, 100}, red_paint);
332  builder.Translate(100, 100);
333  builder.DrawOval({0, 0, 100, 100}, red_paint);
334  builder.Translate(100, 100);
335  builder.DrawLine({0, 0}, {100, 100}, red_paint);
336 
337  builder.Translate(100, 100);
338  ImpellerRoundingRadii radii = {};
339  radii.top_left = {10, 10};
340  radii.bottom_right = {10, 10};
341  builder.DrawRoundedRect({0, 0, 100, 100}, radii, red_paint);
342 
343  builder.Translate(100, 100);
344  builder.DrawPath(hpp::PathBuilder{}.AddOval({0, 0, 100, 100}).Build(),
345  red_paint);
346 
347  auto dl = builder.Build();
348  ASSERT_TRUE(
349  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
350  hpp::Surface window(surface.GetC());
351  window.Draw(dl);
352  return true;
353  }));
354 }
355 
356 TEST_P(InteropPlaygroundTest, CanCreateParagraphsWithCustomFont) {
357  // Create a typography context.
358  auto type_context = Adopt<TypographyContext>(ImpellerTypographyContextNew());
359  ASSERT_TRUE(type_context);
360 
361  // Open the custom font file.
362  std::unique_ptr<fml::Mapping> font_data =
363  flutter::testing::OpenFixtureAsMapping("wtf.otf");
364  ASSERT_NE(font_data, nullptr);
365  ASSERT_GT(font_data->GetSize(), 0u);
366  ImpellerMapping font_data_mapping = {
367  .data = font_data->GetMapping(),
368  .length = font_data->GetSize(),
369  .on_release = [](auto ctx) {
370  delete reinterpret_cast<fml::Mapping*>(ctx);
371  }};
372  auto registered =
373  ImpellerTypographyContextRegisterFont(type_context.GetC(), //
374  &font_data_mapping, //
375  font_data.release(), //
376  nullptr //
377  );
378  ASSERT_TRUE(registered);
379 
380  // Create a builder.
381  auto builder =
382  Adopt<ParagraphBuilder>(ImpellerParagraphBuilderNew(type_context.GetC()));
383  ASSERT_TRUE(builder);
384 
385  // Create a paragraph style with the font size and foreground and background
386  // colors.
387  auto style = Adopt<ParagraphStyle>(ImpellerParagraphStyleNew());
388  ASSERT_TRUE(style);
389  ImpellerParagraphStyleSetFontSize(style.GetC(), 150.0f);
390  ImpellerParagraphStyleSetFontFamily(style.GetC(), "WhatTheFlutter");
391 
392  {
393  auto paint = Adopt<Paint>(ImpellerPaintNew());
394  ASSERT_TRUE(paint);
395  ImpellerColor color = {0.0, 1.0, 1.0, 1.0};
396  ImpellerPaintSetColor(paint.GetC(), &color);
397  ImpellerParagraphStyleSetForeground(style.GetC(), paint.GetC());
398  }
399 
400  // Push the style onto the style stack.
401  ImpellerParagraphBuilderPushStyle(builder.GetC(), style.GetC());
402  std::string text = "0F0F0F0";
403 
404  // Add the paragraph text data.
405  ImpellerParagraphBuilderAddText(builder.GetC(),
406  reinterpret_cast<const uint8_t*>(text.data()),
407  text.size());
408 
409  // Layout and build the paragraph.
410  auto paragraph = Adopt<Paragraph>(
411  ImpellerParagraphBuilderBuildParagraphNew(builder.GetC(), 1200.0f));
412  ASSERT_TRUE(paragraph);
413 
414  // Create a display list with just the paragraph drawn into it.
415  auto dl_builder =
416  Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
417  ImpellerPoint point = {20, 20};
418  ImpellerDisplayListBuilderDrawParagraph(dl_builder.GetC(), paragraph.GetC(),
419  &point);
420  auto dl = Adopt<DisplayList>(
422 
423  ASSERT_TRUE(
424  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
425  ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
426  return true;
427  }));
428 } // namespace impeller::interop::testing
429 
430 static void DrawTextFrame(const hpp::TypographyContext& tc,
431  hpp::DisplayListBuilder& builder,
432  hpp::ParagraphStyle& p_style,
433  const hpp::Paint& bg,
434  ImpellerColor color,
435  ImpellerTextAlignment align,
436  float x_offset) {
437  const char text[] =
438  "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
439 
440  hpp::Paint fg;
441 
442  // Draw a box.
443  fg.SetColor(color);
444  fg.SetDrawStyle(kImpellerDrawStyleStroke);
445  ImpellerRect box_rect = {10 + x_offset, 10, 200, 200};
446  builder.DrawRect(box_rect, fg);
447 
448  // Draw text.
449  fg.SetDrawStyle(kImpellerDrawStyleFill);
450  p_style.SetForeground(fg);
451  p_style.SetBackground(bg);
452  p_style.SetTextAlignment(align);
453 
454  hpp::ParagraphBuilder p_builder(tc);
455  p_builder.PushStyle(p_style);
456  p_builder.AddText(reinterpret_cast<const uint8_t*>(text), sizeof(text));
457 
458  auto left_p = p_builder.Build(box_rect.width - 20.0);
459  ImpellerPoint pt = {20.0f + x_offset, 20.0f};
460  float w = left_p.GetMaxWidth();
461  float h = left_p.GetHeight();
462  builder.DrawParagraph(left_p, pt);
463  fg.SetDrawStyle(kImpellerDrawStyleStroke);
464 
465  // Draw an inner box around the paragraph layout.
466  ImpellerRect inner_box_rect = {pt.x, pt.y, w, h};
467  builder.DrawRect(inner_box_rect, fg);
468 }
469 
470 TEST_P(InteropPlaygroundTest, CanRenderTextAlignments) {
471  hpp::TypographyContext tc;
472 
473  hpp::DisplayListBuilder builder;
474  hpp::Paint bg;
475  hpp::ParagraphStyle p_style;
476  p_style.SetFontFamily("Roboto");
477  p_style.SetFontSize(24.0);
478  p_style.SetFontWeight(kImpellerFontWeight400);
479 
480  // Clear the background to a white color.
481  ImpellerColor clear_color = {1.0, 1.0, 1.0, 1.0};
482  bg.SetColor(clear_color);
483  builder.DrawPaint(bg);
484 
485  // Draw red, left-aligned text.
486  ImpellerColor red = {1.0, 0.0, 0.0, 1.0};
487  DrawTextFrame(tc, builder, p_style, bg, red, kImpellerTextAlignmentLeft, 0.0);
488 
489  // Draw green, centered text.
490  ImpellerColor green = {0.0, 1.0, 0.0, 1.0};
491  DrawTextFrame(tc, builder, p_style, bg, green, kImpellerTextAlignmentCenter,
492  220.0);
493 
494  // Draw blue, right-aligned text.
495  ImpellerColor blue = {0.0, 0.0, 1.0, 1.0};
496  DrawTextFrame(tc, builder, p_style, bg, blue, kImpellerTextAlignmentRight,
497  440.0);
498 
499  auto dl = builder.Build();
500 
501  ASSERT_TRUE(
502  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
503  hpp::Surface window(surface.GetC());
504  window.Draw(dl);
505  return true;
506  }));
507 }
508 
509 TEST_P(InteropPlaygroundTest, CanRenderShadows) {
510  hpp::DisplayListBuilder builder;
511  {
512  builder.DrawRect(ImpellerRect{0, 0, 400, 400},
513  hpp::Paint{}.SetColor(ImpellerColor{
514  0.0, 1.0, 0.0, 1.0, kImpellerColorSpaceSRGB}));
515  }
516  ImpellerRect box = {100, 100, 100, 100};
517  {
518  hpp::PathBuilder path_builder;
519  path_builder.AddRect(box);
520  ImpellerColor shadow_color = {0.0, 0.0, 0.0, 1.0, kImpellerColorSpaceSRGB};
521  builder.DrawShadow(path_builder.Build(), shadow_color, 4.0f, false, 1.0f);
522  }
523  {
524  hpp::Paint red_paint;
525  red_paint.SetColor(
526  ImpellerColor{1.0, 0.0, 0.0, 1.0, kImpellerColorSpaceSRGB});
527  builder.DrawRect(box, red_paint);
528  }
529  auto dl = builder.Build();
530  ASSERT_TRUE(
531  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
532  hpp::Surface window(surface.GetC());
533  window.Draw(dl);
534  return true;
535  }));
536 }
537 
538 TEST_P(InteropPlaygroundTest, CanMeasureText) {
539  hpp::TypographyContext type_context;
540  hpp::ParagraphBuilder paragraph_builder(type_context);
541  hpp::ParagraphStyle paragraph_style;
542  paragraph_style.SetFontSize(50);
543  paragraph_builder.PushStyle(paragraph_style);
544  const std::string text =
545  "🏁 Can 👨‍👨‍👦‍👦 Measure 🔍 Text\nAnd this is line "
546  "two.\nWhoa! Three lines. How high does this go?\r\nI stopped counting.";
547  const auto u16text = fml::Utf8ToUtf16(text);
548  ASSERT_NE(text.size(), u16text.size());
549  paragraph_builder.AddText(reinterpret_cast<const uint8_t*>(text.data()),
550  text.size());
551  hpp::DisplayListBuilder builder;
552  // Don't rely on implicit line breaks in this test to make it less brittle to
553  // different fonts being picked.
554  hpp::Paragraph paragraph = paragraph_builder.Build(FLT_MAX);
555  const auto line_count = paragraph.GetLineCount();
556  ASSERT_EQ(line_count, 4u);
557 
558  // Line Metrics.
559  {
560  auto metrics = paragraph.GetLineMetrics();
561  ASSERT_GT(metrics.GetAscent(0), 0.0);
562  ASSERT_GT(metrics.GetUnscaledAscent(0), 0.0);
563  ASSERT_GT(metrics.GetDescent(0), 0.0);
564  ASSERT_GT(metrics.GetBaseline(0), 0.0);
565  ASSERT_TRUE(metrics.IsHardbreak(0));
566  ASSERT_DOUBLE_EQ(metrics.GetLeft(0), 0.0);
567  ASSERT_EQ(metrics.GetCodeUnitStartIndex(0), 0u);
568  ASSERT_EQ(metrics.GetCodeUnitEndIndexIncludingNewline(0),
569  metrics.GetCodeUnitEndIndex(0) + 1u);
570  ASSERT_GT(metrics.GetCodeUnitStartIndex(1), 0u);
571  // Last line should cover the entire range.
572  ASSERT_EQ(metrics.GetCodeUnitEndIndex(3), u16text.size());
573  }
574 
575  // Glyph info by code point.
576  {
577  auto glyph = paragraph.GlyphInfoAtCodeUnitIndex(0u);
578  ASSERT_TRUE(glyph);
579  ASSERT_EQ(glyph.GetGraphemeClusterCodeUnitRangeBegin(), 0u);
580  ASSERT_EQ(glyph.GetGraphemeClusterCodeUnitRangeEnd(),
581  fml::Utf8ToUtf16("🏁").size());
582  auto bounds = glyph.GetGraphemeClusterBounds();
583  ASSERT_GT(bounds.width, 0.0);
584  ASSERT_GT(bounds.height, 0.0);
585  ASSERT_FALSE(glyph.IsEllipsis());
586  ASSERT_EQ(glyph.GetTextDirection(), kImpellerTextDirectionLTR);
587 
588  ImpellerRect bounds2 = {};
589  ImpellerGlyphInfoGetGraphemeClusterBounds(glyph.Get(), &bounds2);
590  ASSERT_EQ(bounds.width, bounds2.width);
591  ASSERT_EQ(bounds.height, bounds2.height);
592  }
593 
594  // Glyph info by coordinates.
595  {
596  auto glyph = paragraph.GlyphInfoAtParagraphCoordinates(0.0, 0.0);
597  ASSERT_TRUE(glyph);
598  ASSERT_EQ(glyph.GetGraphemeClusterCodeUnitRangeEnd(),
599  fml::Utf8ToUtf16("🏁").size());
600  }
601 
602  // Glyph Figure out word boundaries.
603  {
604  auto glyph = paragraph.GlyphInfoAtCodeUnitIndex(0u);
605  ASSERT_TRUE(glyph);
606  auto range =
607  paragraph.GetWordBoundary(glyph.GetGraphemeClusterCodeUnitRangeEnd());
608  ASSERT_GT(range.end, 0u);
609  ImpellerRange range2 = {};
611  paragraph.Get(), glyph.GetGraphemeClusterCodeUnitRangeEnd(), &range2);
612  ASSERT_EQ(range.start, range2.start);
613  ASSERT_EQ(range.end, range2.end);
614  }
615 
616  builder.DrawParagraph(paragraph, ImpellerPoint{100, 100});
617  auto dl = builder.Build();
618  ASSERT_TRUE(
619  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
620  hpp::Surface window(surface.GetC());
621  window.Draw(dl);
622  return true;
623  }));
624 }
625 
626 TEST_P(InteropPlaygroundTest, CanGetPathBounds) {
627  const auto path =
628  hpp::PathBuilder{}.MoveTo({100, 100}).LineTo({200, 200}).Build();
629  const auto bounds = path.GetBounds();
630  ASSERT_EQ(bounds.x, 100);
631  ASSERT_EQ(bounds.y, 100);
632  ASSERT_EQ(bounds.width, 100);
633  ASSERT_EQ(bounds.height, 100);
634 }
635 
636 TEST_P(InteropPlaygroundTest, CanControlEllipses) {
637  hpp::TypographyContext context;
638  auto style = hpp::ParagraphStyle{};
639  style.SetFontSize(50);
640  style.SetForeground(hpp::Paint{}.SetColor({.red = 1.0, .alpha = 1.0}));
641  const auto text = std::string{"The quick brown fox jumped over the lazy dog"};
642  style.SetEllipsis("🐶");
643  auto para1 =
644  hpp::ParagraphBuilder{context}.PushStyle(style).AddText(text).Build(250);
645  style.SetForeground(hpp::Paint{}.SetColor({.green = 1.0, .alpha = 1.0}));
646  style.SetEllipsis(nullptr);
647  auto para2 =
648  hpp::ParagraphBuilder{context}.PushStyle(style).AddText(text).Build(250);
649  auto dl = hpp::DisplayListBuilder{}
650  .DrawParagraph(para1, {100, 100})
651  .DrawParagraph(para2, {100, 200})
652  .Build();
653  ASSERT_TRUE(
654  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
655  hpp::Surface window(surface.GetC());
656  window.Draw(dl);
657  return true;
658  }));
659 }
660 
661 TEST_P(InteropPlaygroundTest, CanCreateFragmentProgramColorFilters) {
662  auto iplr = OpenAssetAsHPPMapping("interop_runtime_stage_cs.frag.iplr");
663  ASSERT_TRUE(!!iplr);
664  auto program = hpp::FragmentProgram::WithData(std::move(iplr));
665  ASSERT_TRUE(program);
666  auto context = GetHPPContext();
667  auto filter =
668  hpp::ImageFilter::FragmentProgram(context, program, {}, nullptr);
669  ASSERT_TRUE(filter);
670  auto bay_bridge = OpenAssetAsHPPTexture("bay_bridge.jpg");
671  ASSERT_TRUE(bay_bridge);
672 
673  float size_data[4] = {500, 500};
674  auto uniform_data = hpp::Mapping{reinterpret_cast<const uint8_t*>(&size_data),
675  sizeof(size_data), nullptr};
676 
677  auto dl = hpp::DisplayListBuilder{}
678  .DrawRect({10, 10, 500, 500},
679  hpp::Paint{}
680  .SetColor({1.0, 1.0, 1.0, 1.0})
681  .SetColorSource(hpp::ColorSource::FragmentProgram(
682  context, //
683  program, //
684  {bay_bridge.Get()}, // samplers
685  &uniform_data // uniform data
686  )))
687  .Build();
688  ASSERT_TRUE(
689  OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
690  hpp::Surface window(surface.GetC());
691  window.Draw(dl);
692  return true;
693  }));
694 }
695 
696 TEST_P(InteropPlaygroundTest, MappingsReleaseTheirDataOnDestruction) {
697  bool deleted = false;
698  {
699  hpp::Mapping mapping(nullptr, 0, [&deleted]() { deleted = true; });
700  }
701  ASSERT_TRUE(deleted);
702 }
703 
704 } // namespace impeller::interop::testing
Describes an allocation on the heap.
Definition: allocation.h:23
uint8_t * GetBuffer() const
Gets the pointer to the start of the allocation.
Definition: allocation.cc:20
bool Truncate(Bytes length, bool npot=true)
Resize the underlying allocation to at least given number of bytes.
Definition: allocation.cc:32
static ContextGLES & Cast(Context &base)
Definition: backend_cast.h:13
@ kImpellerTextDirectionLTR
Definition: impeller.h:481
@ kImpellerTextureSamplingLinear
Definition: impeller.h:430
@ kImpellerTextDecorationTypeLineThrough
Definition: impeller.h:488
@ kImpellerTextDecorationTypeUnderline
Definition: impeller.h:486
@ kImpellerTextDecorationStyleWavy
Definition: impeller.h:496
@ kImpellerFontWeight400
Definition: impeller.h:457
@ kImpellerDrawStyleStroke
Definition: impeller.h:408
@ kImpellerDrawStyleFill
Definition: impeller.h:407
@ kImpellerColorSpaceSRGB
Definition: impeller.h:448
ImpellerTextAlignment
Definition: impeller.h:470
@ kImpellerTextAlignmentLeft
Definition: impeller.h:471
@ kImpellerTextAlignmentCenter
Definition: impeller.h:473
@ kImpellerTextAlignmentRight
Definition: impeller.h:472
@ kImpellerClipOperationDifference
Definition: impeller.h:370
@ kImpellerPixelFormatRGBA8888
Definition: impeller.h:425
std::shared_ptr< ContextVK > CreateContext()
TEST_P(InteropPlaygroundTest, CanCreateContext)
static void DrawTextFrame(const hpp::TypographyContext &tc, hpp::DisplayListBuilder &builder, hpp::ParagraphStyle &p_style, const hpp::Paint &bg, ImpellerColor color, ImpellerTextAlignment align, float x_offset)
INSTANTIATE_PLAYGROUND_SUITE(InteropPlaygroundTest)
IMPELLER_EXTERN_C void ImpellerParagraphGetWordBoundary(ImpellerParagraph paragraph, size_t code_unit_index, ImpellerRange *out_range)
Definition: impeller.cc:1364
IMPELLER_EXTERN_C uint64_t ImpellerTextureGetOpenGLHandle(ImpellerTexture texture)
Definition: impeller.cc:691
IMPELLER_EXTERN_C ImpellerSurface ImpellerSurfaceCreateWrappedFBONew(ImpellerContext context, uint64_t fbo, ImpellerPixelFormat format, const ImpellerISize *size)
Definition: impeller.cc:732
IMPELLER_EXTERN_C ImpellerDisplayList ImpellerDisplayListBuilderCreateDisplayListNew(ImpellerDisplayListBuilder builder)
Definition: impeller.cc:714
IMPELLER_EXTERN_C ImpellerParagraphBuilder ImpellerParagraphBuilderNew(ImpellerTypographyContext context)
Definition: impeller.cc:1257
IMPELLER_EXTERN_C ImpellerDisplayListBuilder ImpellerDisplayListBuilderNew(const ImpellerRect *cull_rect)
Definition: impeller.cc:229
IMPELLER_EXTERN_C ImpellerPaint ImpellerPaintNew()
Definition: impeller.cc:465
IMPELLER_EXTERN_C ImpellerParagraph ImpellerParagraphBuilderBuildParagraphNew(ImpellerParagraphBuilder paragraph_builder, float width)
Definition: impeller.cc:1307
IMPELLER_EXTERN_C void ImpellerParagraphBuilderPushStyle(ImpellerParagraphBuilder paragraph_builder, ImpellerParagraphStyle style)
Definition: impeller.cc:1281
IMPELLER_EXTERN_C ImpellerTypographyContext ImpellerTypographyContextNew()
Definition: impeller.cc:1371
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderClipRect(ImpellerDisplayListBuilder builder, const ImpellerRect *rect, ImpellerClipOperation op)
Definition: impeller.cc:432
constexpr Matrix ToImpellerType(const ImpellerMatrix &m)
Definition: formats.h:213
IMPELLER_EXTERN_C void ImpellerGlyphInfoGetGraphemeClusterBounds(ImpellerGlyphInfo glyph_info, ImpellerRect *out_bounds)
Definition: impeller.cc:1541
IMPELLER_EXTERN_C ImpellerTexture ImpellerTextureCreateWithOpenGLTextureHandleNew(ImpellerContext context, const ImpellerTextureDescriptor *descriptor, uint64_t external_gl_handle)
Definition: impeller.cc:642
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderTransform(ImpellerDisplayListBuilder builder, const ImpellerMatrix *transform)
Definition: impeller.cc:291
IMPELLER_EXTERN_C bool ImpellerTypographyContextRegisterFont(ImpellerTypographyContext context, const ImpellerMapping *contents, void *contents_on_release_user_data, const char *family_name_alias)
Definition: impeller.cc:1391
IMPELLER_EXTERN_C void ImpellerParagraphStyleSetFontSize(ImpellerParagraphStyle paragraph_style, float size)
Definition: impeller.cc:1184
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderDrawParagraph(ImpellerDisplayListBuilder builder, ImpellerParagraph paragraph, const ImpellerPoint *point)
Definition: impeller.cc:1235
IMPELLER_EXTERN_C void ImpellerParagraphStyleSetFontFamily(ImpellerParagraphStyle paragraph_style, const char *family_name)
Definition: impeller.cc:1178
IMPELLER_EXTERN_C bool ImpellerSurfaceDrawDisplayList(ImpellerSurface surface, ImpellerDisplayList display_list)
Definition: impeller.cc:778
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderTranslate(ImpellerDisplayListBuilder builder, float x_translation, float y_translation)
Definition: impeller.cc:278
IMPELLER_EXTERN_C void ImpellerParagraphStyleSetForeground(ImpellerParagraphStyle paragraph_style, ImpellerPaint paint)
Definition: impeller.cc:1147
IMPELLER_EXTERN_C ImpellerParagraphStyle ImpellerParagraphStyleNew()
Definition: impeller.cc:1132
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderDrawTexture(ImpellerDisplayListBuilder builder, ImpellerTexture texture, const ImpellerPoint *point, ImpellerTextureSampling sampling, ImpellerPaint paint)
Definition: impeller.cc:789
IMPELLER_EXTERN_C void ImpellerDisplayListBuilderDrawRect(ImpellerDisplayListBuilder builder, const ImpellerRect *rect, ImpellerPaint paint)
Definition: impeller.cc:548
IMPELLER_EXTERN_C void ImpellerPaintSetColor(ImpellerPaint paint, const ImpellerColor *color)
Definition: impeller.cc:480
IMPELLER_EXTERN_C void ImpellerParagraphBuilderAddText(ImpellerParagraphBuilder paragraph_builder, const uint8_t *data, uint32_t length)
Definition: impeller.cc:1294
flutter::DlPathBuilder PathBuilder
Definition: tessellator.h:22
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
std::shared_ptr< ContextGLES > context
int64_t width
Definition: impeller.h:520
const uint8_t *IMPELLER_NONNULL data
Definition: impeller.h:627
uint64_t end
Definition: impeller.h:526
uint64_t start
Definition: impeller.h:525
float width
Definition: impeller.h:505
float height
Definition: impeller.h:506
ImpellerPoint top_left
Definition: impeller.h:606
ImpellerPoint bottom_right
Definition: impeller.h:609
int types
A mask of ImpellerTextDecorationTypes to enable.
Definition: impeller.h:648
ImpellerPixelFormat pixel_format
Definition: impeller.h:621
ImpellerISize size
Definition: impeller.h:622
std::array< uint8_t, 4 > ToR8G8B8A8() const
Convert to R8G8B8A8 representation.
Definition: color.h:246
static constexpr Color Fuchsia()
Definition: color.h:471
constexpr bool IsIdentity() const
Definition: matrix.h:475