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