Flutter Impeller
typographer_context_skia.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 
6 
7 #include <cstddef>
8 #include <cstdint>
9 #include <memory>
10 #include <numeric>
11 #include <utility>
12 #include <vector>
13 
14 #include "flutter/fml/logging.h"
15 #include "flutter/fml/trace_event.h"
16 #include "fml/closure.h"
17 
21 #include "impeller/core/formats.h"
24 #include "impeller/geometry/rect.h"
25 #include "impeller/geometry/size.h"
35 
36 #include "third_party/skia/include/core/SkBitmap.h"
37 #include "third_party/skia/include/core/SkBlendMode.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "third_party/skia/include/core/SkColor.h"
40 #include "third_party/skia/include/core/SkFont.h"
41 #include "third_party/skia/include/core/SkImageInfo.h"
42 #include "third_party/skia/include/core/SkPaint.h"
43 #include "third_party/skia/include/core/SkSize.h"
44 #include "third_party/skia/include/core/SkSurface.h"
45 
46 namespace impeller {
47 
48 constexpr auto kPadding = 2;
49 
50 namespace {
51 SkPaint::Cap ToSkiaCap(Cap cap) {
52  switch (cap) {
53  case Cap::kButt:
54  return SkPaint::Cap::kButt_Cap;
55  case Cap::kRound:
56  return SkPaint::Cap::kRound_Cap;
57  case Cap::kSquare:
58  return SkPaint::Cap::kSquare_Cap;
59  }
60  FML_UNREACHABLE();
61 }
62 
63 SkPaint::Join ToSkiaJoin(Join join) {
64  switch (join) {
65  case Join::kMiter:
66  return SkPaint::Join::kMiter_Join;
67  case Join::kRound:
68  return SkPaint::Join::kRound_Join;
69  case Join::kBevel:
70  return SkPaint::Join::kBevel_Join;
71  }
72  FML_UNREACHABLE();
73 }
74 } // namespace
75 
76 std::shared_ptr<TypographerContext> TypographerContextSkia::Make() {
77  return std::make_shared<TypographerContextSkia>();
78 }
79 
81 
83 
84 std::shared_ptr<GlyphAtlasContext>
86  return std::make_shared<GlyphAtlasContext>(type);
87 }
88 
89 static SkImageInfo GetImageInfo(const GlyphAtlas& atlas, Size size) {
90  switch (atlas.GetType()) {
92  return SkImageInfo::MakeA8(SkISize{static_cast<int32_t>(size.width),
93  static_cast<int32_t>(size.height)});
95  return SkImageInfo::MakeN32Premul(size.width, size.height);
96  }
97  FML_UNREACHABLE();
98 }
99 
100 /// Append as many glyphs to the texture as will fit, and return the first index
101 /// of [extra_pairs] that did not fit.
102 static size_t AppendToExistingAtlas(
103  const std::shared_ptr<GlyphAtlas>& atlas,
104  const std::vector<FontGlyphPair>& extra_pairs,
105  std::vector<Rect>& glyph_positions,
106  const std::vector<Rect>& glyph_sizes,
107  ISize atlas_size,
108  int64_t height_adjustment,
109  const std::shared_ptr<RectanglePacker>& rect_packer) {
110  TRACE_EVENT0("impeller", __FUNCTION__);
111  if (!rect_packer || atlas_size.IsEmpty()) {
112  return 0;
113  }
114 
115  for (size_t i = 0; i < extra_pairs.size(); i++) {
116  ISize glyph_size = ISize::Ceil(glyph_sizes[i].GetSize());
117  IPoint16 location_in_atlas;
118  if (!rect_packer->AddRect(glyph_size.width + kPadding, //
119  glyph_size.height + kPadding, //
120  &location_in_atlas //
121  )) {
122  return i;
123  }
124  // Position the glyph in the center of the 1px padding.
125  glyph_positions.push_back(Rect::MakeXYWH(
126  location_in_atlas.x() + 1, //
127  location_in_atlas.y() + height_adjustment + 1, //
128  glyph_size.width, //
129  glyph_size.height //
130  ));
131  }
132 
133  return extra_pairs.size();
134 }
135 
136 static size_t PairsFitInAtlasOfSize(
137  const std::vector<FontGlyphPair>& pairs,
138  const ISize& atlas_size,
139  std::vector<Rect>& glyph_positions,
140  const std::vector<Rect>& glyph_sizes,
141  int64_t height_adjustment,
142  const std::shared_ptr<RectanglePacker>& rect_packer,
143  size_t start_index) {
144  FML_DCHECK(!atlas_size.IsEmpty());
145 
146  for (size_t i = start_index; i < pairs.size(); i++) {
147  ISize glyph_size = ISize::Ceil(glyph_sizes[i].GetSize());
148  IPoint16 location_in_atlas;
149  if (!rect_packer->AddRect(glyph_size.width + kPadding, //
150  glyph_size.height + kPadding, //
151  &location_in_atlas //
152  )) {
153  return i;
154  }
155  glyph_positions.push_back(Rect::MakeXYWH(
156  location_in_atlas.x() + 1, //
157  location_in_atlas.y() + height_adjustment + 1, //
158  glyph_size.width, //
159  glyph_size.height //
160  ));
161  }
162 
163  return pairs.size();
164 }
165 
167  const std::shared_ptr<GlyphAtlasContext>& atlas_context,
168  const std::vector<FontGlyphPair>& extra_pairs,
169  std::vector<Rect>& glyph_positions,
170  const std::vector<Rect>& glyph_sizes,
171  size_t glyph_index_start,
172  int64_t max_texture_height) {
173  // Because we can't grow the skyline packer horizontally, pick a reasonable
174  // large width for all atlases.
175  static constexpr int64_t kAtlasWidth = 4096;
176  static constexpr int64_t kMinAtlasHeight = 1024;
177 
178  ISize current_size = ISize(kAtlasWidth, kMinAtlasHeight);
179  if (atlas_context->GetAtlasSize().height > current_size.height) {
180  current_size.height = atlas_context->GetAtlasSize().height * 2;
181  }
182 
183  auto height_adjustment = atlas_context->GetAtlasSize().height;
184  while (current_size.height <= max_texture_height) {
185  std::shared_ptr<RectanglePacker> rect_packer;
186  if (atlas_context->GetRectPacker() || glyph_index_start) {
187  rect_packer = RectanglePacker::Factory(
188  kAtlasWidth,
189  current_size.height - atlas_context->GetAtlasSize().height);
190  } else {
191  rect_packer = RectanglePacker::Factory(kAtlasWidth, current_size.height);
192  }
193  glyph_positions.erase(glyph_positions.begin() + glyph_index_start,
194  glyph_positions.end());
195  atlas_context->UpdateRectPacker(rect_packer);
196  auto next_index = PairsFitInAtlasOfSize(
197  extra_pairs, current_size, glyph_positions, glyph_sizes,
198  height_adjustment, rect_packer, glyph_index_start);
199  if (next_index == extra_pairs.size()) {
200  return current_size;
201  }
202  current_size = ISize(current_size.width, current_size.height * 2);
203  }
204  return {};
205 }
206 
208  return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
209 }
210 
211 static void DrawGlyph(SkCanvas* canvas,
212  const SkPoint position,
213  const ScaledFont& scaled_font,
214  const SubpixelGlyph& glyph,
215  const Rect& scaled_bounds,
216  const std::optional<GlyphProperties>& prop,
217  bool has_color) {
218  const auto& metrics = scaled_font.font.GetMetrics();
219  SkGlyphID glyph_id = glyph.glyph.index;
220 
221  SkFont sk_font(
223  metrics.point_size, metrics.scaleX, metrics.skewX);
224  sk_font.setEdging(SkFont::Edging::kAntiAlias);
225  sk_font.setHinting(SkFontHinting::kSlight);
226  sk_font.setEmbolden(metrics.embolden);
227  sk_font.setSubpixel(true);
228  sk_font.setSize(sk_font.getSize() * static_cast<Scalar>(scaled_font.scale));
229 
230  auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK;
231 
232  SkPaint glyph_paint;
233  glyph_paint.setColor(glyph_color);
234  glyph_paint.setBlendMode(SkBlendMode::kSrc);
235  if (prop.has_value()) {
236  auto stroke = prop->stroke;
237  if (stroke.has_value()) {
238  glyph_paint.setStroke(true);
239  glyph_paint.setStrokeWidth(stroke->width *
240  static_cast<Scalar>(scaled_font.scale));
241  glyph_paint.setStrokeCap(ToSkiaCap(stroke->cap));
242  glyph_paint.setStrokeJoin(ToSkiaJoin(stroke->join));
243  glyph_paint.setStrokeMiter(stroke->miter_limit);
244  } else {
245  glyph_paint.setStroke(false);
246  }
247  }
248  canvas->save();
249  Point subpixel_offset = SubpixelPositionToPoint(glyph.subpixel_offset);
250  canvas->translate(subpixel_offset.x, subpixel_offset.y);
251  // Draw a single glyph in the bounds
252  canvas->drawGlyphs({&glyph_id, 1u}, // glyphs
253  {&position, 1u}, // positions
254  SkPoint::Make(-scaled_bounds.GetLeft(),
255  -scaled_bounds.GetTop()), // origin
256  sk_font, // font
257  glyph_paint // paint
258  );
259  canvas->restore();
260 }
261 
262 /// @brief Batch render to a single surface.
263 ///
264 /// This is only safe for use when updating a fresh texture.
265 static bool BulkUpdateAtlasBitmap(const GlyphAtlas& atlas,
266  std::shared_ptr<BlitPass>& blit_pass,
267  HostBuffer& data_host_buffer,
268  const std::shared_ptr<Texture>& texture,
269  const std::vector<FontGlyphPair>& new_pairs,
270  size_t start_index,
271  size_t end_index) {
272  TRACE_EVENT0("impeller", __FUNCTION__);
273 
274  bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
275 
276  SkBitmap bitmap;
277  bitmap.setInfo(GetImageInfo(atlas, Size(texture->GetSize())));
278  if (!bitmap.tryAllocPixels()) {
279  return false;
280  }
281 
282  auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
283  if (!surface) {
284  return false;
285  }
286  auto canvas = surface->getCanvas();
287  if (!canvas) {
288  return false;
289  }
290 
291  for (size_t i = start_index; i < end_index; i++) {
292  const FontGlyphPair& pair = new_pairs[i];
293  auto data = atlas.FindFontGlyphBounds(pair);
294  if (!data.has_value()) {
295  continue;
296  }
297  auto [pos, bounds, placeholder] = data.value();
298  FML_DCHECK(!placeholder);
299  Size size = pos.GetSize();
300  if (size.IsEmpty()) {
301  continue;
302  }
303 
304  DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
305  pair.scaled_font, pair.glyph, bounds, pair.glyph.properties,
306  has_color);
307  }
308 
309  // Writing to a malloc'd buffer and then copying to the staging buffers
310  // benchmarks as substantially faster on a number of Android devices.
311  BufferView buffer_view = data_host_buffer.Emplace(
312  bitmap.getAddr(0, 0),
313  texture->GetSize().Area() *
315  atlas.GetTexture()->GetTextureDescriptor().format),
316  data_host_buffer.GetMinimumUniformAlignment());
317 
318  return blit_pass->AddCopy(std::move(buffer_view), //
319  texture, //
320  IRect::MakeXYWH(0, 0, texture->GetSize().width,
321  texture->GetSize().height));
322 }
323 
324 static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
325  std::shared_ptr<BlitPass>& blit_pass,
326  HostBuffer& data_host_buffer,
327  const std::shared_ptr<Texture>& texture,
328  const std::vector<FontGlyphPair>& new_pairs,
329  size_t start_index,
330  size_t end_index) {
331  TRACE_EVENT0("impeller", __FUNCTION__);
332 
333  bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
334 
335  for (size_t i = start_index; i < end_index; i++) {
336  const FontGlyphPair& pair = new_pairs[i];
337  auto data = atlas.FindFontGlyphBounds(pair);
338  if (!data.has_value()) {
339  continue;
340  }
341  auto [pos, bounds, placeholder] = data.value();
342  FML_DCHECK(!placeholder);
343 
344  Size size = pos.GetSize();
345  if (size.IsEmpty()) {
346  continue;
347  }
348  // The uploaded bitmap is expanded by 1px of padding
349  // on each side.
350  size.width += 2;
351  size.height += 2;
352 
353  SkBitmap bitmap;
354  bitmap.setInfo(GetImageInfo(atlas, size));
355  if (!bitmap.tryAllocPixels()) {
356  return false;
357  }
358 
359  auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
360  if (!surface) {
361  return false;
362  }
363  auto canvas = surface->getCanvas();
364  if (!canvas) {
365  return false;
366  }
367 
368  DrawGlyph(canvas, SkPoint::Make(1, 1), pair.scaled_font, pair.glyph, bounds,
369  pair.glyph.properties, has_color);
370 
371  // Writing to a malloc'd buffer and then copying to the staging buffers
372  // benchmarks as substantially faster on a number of Android devices.
373  BufferView buffer_view = data_host_buffer.Emplace(
374  bitmap.getAddr(0, 0),
376  atlas.GetTexture()->GetTextureDescriptor().format),
377  data_host_buffer.GetMinimumUniformAlignment());
378 
379  // convert_to_read is set to false so that the texture remains in a transfer
380  // dst layout until we finish writing to it below. This only has an impact
381  // on Vulkan where we are responsible for managing image layouts.
382  if (!blit_pass->AddCopy(std::move(buffer_view), //
383  texture, //
384  IRect::MakeXYWH(pos.GetLeft() - 1, pos.GetTop() - 1,
385  size.width, size.height), //
386  /*label=*/"", //
387  /*mip_level=*/0, //
388  /*slice=*/0, //
389  /*convert_to_read=*/false //
390  )) {
391  return false;
392  }
393  }
394  return blit_pass->ConvertTextureToShaderRead(texture);
395 }
396 
397 static Rect ComputeGlyphSize(const SkFont& font,
398  const SubpixelGlyph& glyph,
399  Scalar scale) {
400  SkRect scaled_bounds;
401  SkPaint glyph_paint;
402  if (glyph.properties.has_value() && glyph.properties->stroke) {
403  glyph_paint.setStroke(true);
404  glyph_paint.setStrokeWidth(glyph.properties->stroke->width * scale);
405  glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties->stroke->cap));
406  glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties->stroke->join));
407  glyph_paint.setStrokeMiter(glyph.properties->stroke->miter_limit);
408  }
409  // Get bounds for a single glyph
410  font.getBounds({&glyph.glyph.index, 1}, {&scaled_bounds, 1}, &glyph_paint);
411 
412  // Expand the bounds of glyphs at subpixel offsets by 2 in the x direction.
413  Scalar adjustment = 0.0;
415  adjustment = 1.0;
416  }
417  return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
418  scaled_bounds.fRight + adjustment,
419  scaled_bounds.fBottom);
420 };
421 
422 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
423 TypographerContextSkia::CollectNewGlyphs(
424  const std::shared_ptr<GlyphAtlas>& atlas,
425  const std::vector<RenderableText>& renderable_texts) {
426  std::vector<FontGlyphPair> new_glyphs;
427  std::vector<Rect> glyph_sizes;
428  for (const auto& frame : renderable_texts) {
429  Rational rounded_scale = TextFrame::RoundScaledFontSize(
430  frame.origin_transform.GetMaxBasisLengthXY());
431  for (const auto& run : frame.text_frame->GetRuns()) {
432  auto metrics = run.GetFont().GetMetrics();
433 
434  ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
435 
436  FontGlyphAtlas* font_glyph_atlas =
437  atlas->GetOrCreateFontGlyphAtlas(scaled_font);
438  FML_DCHECK(!!font_glyph_atlas);
439 
440  SkFont sk_font(
441  TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
442  metrics.point_size, metrics.scaleX, metrics.skewX);
443  sk_font.setEdging(SkFont::Edging::kAntiAlias);
444  sk_font.setHinting(SkFontHinting::kSlight);
445  sk_font.setEmbolden(metrics.embolden);
446  // Rather than computing the bounds at the requested point size and
447  // scaling up the bounds, we scale up the font size and request the
448  // bounds. This seems to give more accurate bounds information.
449  sk_font.setSize(sk_font.getSize() *
450  static_cast<Scalar>(scaled_font.scale));
451  sk_font.setSubpixel(true);
452 
453  for (const auto& glyph_position : run.GetGlyphPositions()) {
455  glyph_position, scaled_font.font.GetAxisAlignment(),
456  frame.origin_transform);
457  SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
458  frame.properties);
459  const auto& font_glyph_bounds =
460  font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
461 
462  if (!font_glyph_bounds.has_value()) {
463  new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
464  auto glyph_bounds = ComputeGlyphSize(
465  sk_font, subpixel_glyph, static_cast<Scalar>(scaled_font.scale));
466  glyph_sizes.push_back(glyph_bounds);
467 
468  auto frame_bounds = FrameBounds{
469  Rect::MakeLTRB(0, 0, 0, 0), //
470  glyph_bounds, //
471  /*placeholder=*/true //
472  };
473 
474  font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
475  }
476  }
477  }
478  }
479  return {std::move(new_glyphs), std::move(glyph_sizes)};
480 }
481 
482 std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
483  Context& context,
484  GlyphAtlas::Type type,
485  HostBuffer& data_host_buffer,
486  const std::shared_ptr<GlyphAtlasContext>& atlas_context,
487  const std::vector<RenderableText>& renderable_texts) const {
488  TRACE_EVENT0("impeller", __FUNCTION__);
489  if (!IsValid()) {
490  return nullptr;
491  }
492  std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
493  FML_DCHECK(last_atlas->GetType() == type);
494 
495  if (renderable_texts.empty()) {
496  return last_atlas;
497  }
498 
499  // ---------------------------------------------------------------------------
500  // Step 1: Determine if the atlas type and font glyph pairs are compatible
501  // with the current atlas and reuse if possible. For each new font and
502  // glyph pair, compute the glyph size at scale.
503  // ---------------------------------------------------------------------------
504  auto [new_glyphs, glyph_sizes] =
505  CollectNewGlyphs(last_atlas, renderable_texts);
506  if (new_glyphs.size() == 0) {
507  return last_atlas;
508  }
509 
510  // ---------------------------------------------------------------------------
511  // Step 2: Determine if the additional missing glyphs can be appended to the
512  // existing bitmap without recreating the atlas.
513  // ---------------------------------------------------------------------------
514  std::vector<Rect> glyph_positions;
515  glyph_positions.reserve(new_glyphs.size());
516  size_t first_missing_index = 0;
517 
518  if (last_atlas->GetTexture()) {
519  // Append all glyphs that fit into the current atlas.
520  first_missing_index = AppendToExistingAtlas(
521  last_atlas, new_glyphs, glyph_positions, glyph_sizes,
522  atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
523  atlas_context->GetRectPacker());
524 
525  // ---------------------------------------------------------------------------
526  // Step 3a: Record the positions in the glyph atlas of the newly added
527  // glyphs.
528  // ---------------------------------------------------------------------------
529  for (size_t i = 0; i < first_missing_index; i++) {
530  last_atlas->AddTypefaceGlyphPositionAndBounds(
531  new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
532  }
533 
534  std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
535  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
536 
537  fml::ScopedCleanupClosure closure([&]() {
538  blit_pass->EncodeCommands();
539  if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
540  VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
541  }
542  });
543 
544  // ---------------------------------------------------------------------------
545  // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
546  // the uploads into the blit pass.
547  // ---------------------------------------------------------------------------
548  if (!UpdateAtlasBitmap(*last_atlas, blit_pass, data_host_buffer,
549  last_atlas->GetTexture(), new_glyphs, 0,
550  first_missing_index)) {
551  return nullptr;
552  }
553 
554  // If all glyphs fit, just return the old atlas.
555  if (first_missing_index == new_glyphs.size()) {
556  return last_atlas;
557  }
558  }
559 
560  int64_t height_adjustment = atlas_context->GetAtlasSize().height;
561  const int64_t max_texture_height =
562  context.GetResourceAllocator()->GetMaxTextureSizeSupported().height;
563 
564  // IF the current atlas size is as big as it can get, then "GC" and create an
565  // atlas with only the required glyphs. OpenGLES cannot reliably perform the
566  // blit required here, as 1) it requires attaching textures as read and write
567  // framebuffers which has substantially smaller size limits that max textures
568  // and 2) is missing a GLES 2.0 implementation and cap check.
569  bool blit_old_atlas = true;
570  std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
571  if (atlas_context->GetAtlasSize().height >= max_texture_height ||
573  blit_old_atlas = false;
574  new_atlas = std::make_shared<GlyphAtlas>(
575  type, /*initial_generation=*/last_atlas->GetAtlasGeneration() + 1);
576 
577  auto [update_glyphs, update_sizes] =
578  CollectNewGlyphs(new_atlas, renderable_texts);
579  new_glyphs = std::move(update_glyphs);
580  glyph_sizes = std::move(update_sizes);
581 
582  glyph_positions.clear();
583  glyph_positions.reserve(new_glyphs.size());
584  first_missing_index = 0;
585 
586  height_adjustment = 0;
587  atlas_context->UpdateRectPacker(nullptr);
588  atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
589  }
590 
591  // A new glyph atlas must be created.
592  ISize atlas_size = ComputeNextAtlasSize(atlas_context, //
593  new_glyphs, //
594  glyph_positions, //
595  glyph_sizes, //
596  first_missing_index, //
597  max_texture_height //
598  );
599 
600  atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
601  if (atlas_size.IsEmpty()) {
602  return nullptr;
603  }
604  FML_DCHECK(new_glyphs.size() == glyph_positions.size());
605 
606  TextureDescriptor descriptor;
607  switch (type) {
609  descriptor.format =
610  context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
611  break;
614  break;
615  }
616  descriptor.size = atlas_size;
618  descriptor.usage = TextureUsage::kShaderRead;
619  std::shared_ptr<Texture> new_texture =
620  context.GetResourceAllocator()->CreateTexture(descriptor);
621  if (!new_texture) {
622  return nullptr;
623  }
624 
625  new_texture->SetLabel("GlyphAtlas");
626 
627  std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
628  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
629 
630  fml::ScopedCleanupClosure closure([&]() {
631  blit_pass->EncodeCommands();
632  if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
633  VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
634  }
635  });
636 
637  // Now append all remaining glyphs. This should never have any missing data...
638  auto old_texture = new_atlas->GetTexture();
639  new_atlas->SetTexture(std::move(new_texture));
640 
641  // ---------------------------------------------------------------------------
642  // Step 3a: Record the positions in the glyph atlas of the newly added
643  // glyphs.
644  // ---------------------------------------------------------------------------
645  for (size_t i = first_missing_index; i < glyph_positions.size(); i++) {
646  new_atlas->AddTypefaceGlyphPositionAndBounds(
647  new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
648  }
649 
650  // ---------------------------------------------------------------------------
651  // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
652  // the uploads into the blit pass.
653  // ---------------------------------------------------------------------------
654  if (!BulkUpdateAtlasBitmap(*new_atlas, blit_pass, data_host_buffer,
655  new_atlas->GetTexture(), new_glyphs,
656  first_missing_index, new_glyphs.size())) {
657  return nullptr;
658  }
659 
660  // Blit the old texture to the top left of the new atlas.
661  if (blit_old_atlas && old_texture) {
662  blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
663  IRect::MakeSize(new_atlas->GetTexture()->GetSize()),
664  {0, 0});
665  }
666 
667  // ---------------------------------------------------------------------------
668  // Step 8b: Record the texture in the glyph atlas.
669  // ---------------------------------------------------------------------------
670 
671  return new_atlas;
672 }
673 
674 } // namespace impeller
static TypefaceSkia & Cast(Typeface &base)
Definition: backend_cast.h:13
To do anything rendering related with Impeller, you need a context.
Definition: context.h:65
virtual std::shared_ptr< CommandBuffer > CreateCommandBuffer() const =0
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
virtual const std::shared_ptr< const Capabilities > & GetCapabilities() const =0
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
virtual BackendType GetBackendType() const =0
Get the graphics backend of an Impeller context.
virtual std::shared_ptr< Allocator > GetResourceAllocator() const =0
Returns the allocator used to create textures and buffers on the device.
virtual bool EnqueueCommandBuffer(std::shared_ptr< CommandBuffer > command_buffer)
Enqueue command_buffer for submission by the end of the frame.
Definition: context.cc:33
const std::shared_ptr< Typeface > & GetTypeface() const
The typeface whose intrinsic properties this font modifies.
Definition: font.cc:27
const Metrics & GetMetrics() const
Definition: font.cc:45
A texture containing the bitmap representation of glyphs in different fonts along with the ability to...
Definition: glyph_atlas.h:37
std::optional< FrameBounds > FindFontGlyphBounds(const FontGlyphPair &pair) const
Find the location of a specific font-glyph pair in the atlas.
Definition: glyph_atlas.cc:87
Type
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.h:41
Type GetType() const
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.cc:58
const std::shared_ptr< Texture > & GetTexture() const
Get the texture for the glyph atlas.
Definition: glyph_atlas.cc:62
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:92
size_t GetMinimumUniformAlignment() const
Retrieve the minimum uniform buffer alignment in bytes.
Definition: host_buffer.cc:241
static std::shared_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
static Rational RoundScaledFontSize(Scalar scale)
Definition: text_frame.cc:55
static SubpixelPosition ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, const Matrix &transform)
Definition: text_frame.cc:94
const sk_sp< SkTypeface > & GetSkiaTypeface() const
std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, GlyphAtlas::Type type, HostBuffer &host_buffer, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< RenderableText > &renderable_texts) const override
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition: formats.h:469
Join
An enum that describes ways to join two segments of a path.
static bool UpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &data_host_buffer, const std::shared_ptr< Texture > &texture, const std::vector< FontGlyphPair > &new_pairs, size_t start_index, size_t end_index)
static Rect ComputeGlyphSize(const SkFont &font, const SubpixelGlyph &glyph, Scalar scale)
float Scalar
Definition: scalar.h:19
static size_t PairsFitInAtlasOfSize(const std::vector< FontGlyphPair > &pairs, const ISize &atlas_size, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, int64_t height_adjustment, const std::shared_ptr< RectanglePacker > &rect_packer, size_t start_index)
static void DrawGlyph(SkCanvas *canvas, const SkPoint position, const ScaledFont &scaled_font, const SubpixelGlyph &glyph, const Rect &scaled_bounds, const std::optional< GlyphProperties > &prop, bool has_color)
static Point SubpixelPositionToPoint(SubpixelPosition pos)
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size)
TPoint< Scalar > Point
Definition: point.h:426
Cap
An enum that describes ways to decorate the end of a path contour.
static size_t AppendToExistingAtlas(const std::shared_ptr< GlyphAtlas > &atlas, const std::vector< FontGlyphPair > &extra_pairs, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, ISize atlas_size, int64_t height_adjustment, const std::shared_ptr< RectanglePacker > &rect_packer)
static ISize ComputeNextAtlasSize(const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< FontGlyphPair > &extra_pairs, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, size_t glyph_index_start, int64_t max_texture_height)
static bool BulkUpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &data_host_buffer, const std::shared_ptr< Texture > &texture, const std::vector< FontGlyphPair > &new_pairs, size_t start_index, size_t end_index)
Batch render to a single surface.
TSize< Scalar > Size
Definition: size.h:159
ISize64 ISize
Definition: size.h:162
constexpr auto kPadding
A font along with a glyph in that font rendered at a particular scale and subpixel position.
uint16_t index
Definition: glyph.h:22
int16_t y() const
int16_t x() const
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
A glyph and its subpixel position.
std::optional< GlyphProperties > properties
SubpixelPosition subpixel_offset
constexpr auto GetTop() const
Definition: rect.h:353
constexpr auto GetLeft() const
Definition: rect.h:351
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
constexpr Type Area() const
Definition: size.h:120
Type height
Definition: size.h:29
Type width
Definition: size.h:28
constexpr TSize Ceil() const
Definition: size.h:114
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:123
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...