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