Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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"
16#include "fml/closure.h"
17
35
36#include "third_party/abseil-cpp/absl/status/statusor.h"
37#include "third_party/skia/include/core/SkBitmap.h"
38#include "third_party/skia/include/core/SkBlendMode.h"
39#include "third_party/skia/include/core/SkCanvas.h"
40#include "third_party/skia/include/core/SkColor.h"
41#include "third_party/skia/include/core/SkFont.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
46namespace impeller {
47
48constexpr auto kPadding = 2;
49
50namespace {
51SkPaint::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 }
61}
62
63SkPaint::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 }
73}
74
75bool HasLightGlyphs(const GlyphAtlas& atlas,
76 const std::vector<FontGlyphPair>& new_pairs,
77 size_t start_index,
78 size_t end_index) {
80 return false;
81 }
82 for (size_t i = start_index; i < end_index; i++) {
83 if (new_pairs[i].glyph.properties.tone_or_color ==
85 return true;
86 }
87 }
88 return false;
89}
90
91// Create an A8 bitmap from an color bitmap.
92absl::StatusOr<SkBitmap> ToA8Bitmap(const SkBitmap& src) {
93 FML_DCHECK(src.colorType() == kRGBA_8888_SkColorType);
94
95 SkBitmap a8_bitmap;
96 a8_bitmap.setInfo(SkImageInfo::MakeA8(src.width(), src.height()));
97 if (!a8_bitmap.tryAllocPixels()) {
98 return absl::Status(absl::StatusCode::kInternal,
99 "Failed to allocate pixels for A8 bitmap");
100 }
101 if (!src.readPixels(a8_bitmap.pixmap())) {
102 return absl::Status(absl::StatusCode::kInternal,
103 "Failed to read pixels into A8 bitmap");
104 }
105 return a8_bitmap;
106}
107
108} // namespace
109
110std::shared_ptr<TypographerContext> TypographerContextSkia::Make() {
111 return std::make_shared<TypographerContextSkia>();
112}
113
115
117
118std::shared_ptr<GlyphAtlasContext>
120 return std::make_shared<GlyphAtlasContext>(type);
121}
122
124 Size size,
125 bool support_light_glyphs) {
126 SkISize skia_size = {static_cast<int32_t>(size.width),
127 static_cast<int32_t>(size.height)};
128
129 switch (atlas.GetType()) {
131 return support_light_glyphs
132 ? SkImageInfo::Make(skia_size, kRGBA_8888_SkColorType,
133 kPremul_SkAlphaType)
134 : SkImageInfo::MakeA8(skia_size);
136 return SkImageInfo::Make(skia_size, kRGBA_8888_SkColorType,
137 kPremul_SkAlphaType);
138 }
140}
141
142/// Append as many glyphs to the texture as will fit, and return the first index
143/// of [extra_pairs] that did not fit.
145 const std::shared_ptr<GlyphAtlas>& atlas,
146 const std::vector<FontGlyphPair>& extra_pairs,
147 std::vector<Rect>& glyph_positions,
148 const std::vector<Rect>& glyph_sizes,
149 ISize atlas_size,
150 int64_t height_adjustment,
151 const std::shared_ptr<RectanglePacker>& rect_packer) {
152 TRACE_EVENT0("impeller", __FUNCTION__);
153 if (!rect_packer || atlas_size.IsEmpty()) {
154 return 0;
155 }
156
157 for (size_t i = 0; i < extra_pairs.size(); i++) {
158 ISize glyph_size = ISize::Ceil(glyph_sizes[i].GetSize());
159 IPoint16 location_in_atlas;
160 if (!rect_packer->AddRect(glyph_size.width + kPadding, //
161 glyph_size.height + kPadding, //
162 &location_in_atlas //
163 )) {
164 return i;
165 }
166 // Position the glyph in the center of the 1px padding.
167 glyph_positions.push_back(Rect::MakeXYWH(
168 location_in_atlas.x() + 1, //
169 location_in_atlas.y() + height_adjustment + 1, //
170 glyph_size.width, //
171 glyph_size.height //
172 ));
173 }
174
175 return extra_pairs.size();
176}
177
179 const std::vector<FontGlyphPair>& pairs,
180 const ISize& atlas_size,
181 std::vector<Rect>& glyph_positions,
182 const std::vector<Rect>& glyph_sizes,
183 int64_t height_adjustment,
184 const std::shared_ptr<RectanglePacker>& rect_packer,
185 size_t start_index) {
186 FML_DCHECK(!atlas_size.IsEmpty());
187
188 for (size_t i = start_index; i < pairs.size(); i++) {
189 ISize glyph_size = ISize::Ceil(glyph_sizes[i].GetSize());
190 IPoint16 location_in_atlas;
191 if (!rect_packer->AddRect(glyph_size.width + kPadding, //
192 glyph_size.height + kPadding, //
193 &location_in_atlas //
194 )) {
195 return i;
196 }
197 glyph_positions.push_back(Rect::MakeXYWH(
198 location_in_atlas.x() + 1, //
199 location_in_atlas.y() + height_adjustment + 1, //
200 glyph_size.width, //
201 glyph_size.height //
202 ));
203 }
204
205 return pairs.size();
206}
207
209 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
210 const std::vector<FontGlyphPair>& extra_pairs,
211 std::vector<Rect>& glyph_positions,
212 const std::vector<Rect>& glyph_sizes,
213 size_t glyph_index_start,
214 int64_t max_texture_height) {
215 // Because we can't grow the skyline packer horizontally, pick a reasonable
216 // large width for all atlases.
217 static constexpr int64_t kAtlasWidth = 4096;
218 static constexpr int64_t kMinAtlasHeight = 1024;
219
220 ISize current_size = ISize(kAtlasWidth, kMinAtlasHeight);
221 if (atlas_context->GetAtlasSize().height > current_size.height) {
222 current_size.height = atlas_context->GetAtlasSize().height * 2;
223 }
224
225 auto height_adjustment = atlas_context->GetAtlasSize().height;
226 while (current_size.height <= max_texture_height) {
227 std::shared_ptr<RectanglePacker> rect_packer;
228 if (atlas_context->GetRectPacker() || glyph_index_start) {
229 rect_packer = RectanglePacker::Factory(
230 kAtlasWidth,
231 current_size.height - atlas_context->GetAtlasSize().height);
232 } else {
233 rect_packer = RectanglePacker::Factory(kAtlasWidth, current_size.height);
234 }
235 glyph_positions.erase(glyph_positions.begin() + glyph_index_start,
236 glyph_positions.end());
237 atlas_context->UpdateRectPacker(rect_packer);
238 auto next_index = PairsFitInAtlasOfSize(
239 extra_pairs, current_size, glyph_positions, glyph_sizes,
240 height_adjustment, rect_packer, glyph_index_start);
241 if (next_index == extra_pairs.size()) {
242 return current_size;
243 }
244 current_size = ISize(current_size.width, current_size.height * 2);
245 }
246 return {};
247}
248
250 return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
251}
252
253static void DrawGlyph(SkCanvas* canvas,
254 const SkPoint position,
255 const ScaledFont& scaled_font,
256 const SubpixelGlyph& glyph,
257 const Rect& scaled_bounds,
258 const GlyphProperties& prop) {
259 const auto& metrics = scaled_font.font.GetMetrics();
260 SkGlyphID glyph_id = glyph.glyph.index;
261
262 SkFont sk_font(
264 metrics.point_size, metrics.scaleX, metrics.skewX);
265 sk_font.setEdging(SkFont::Edging::kAntiAlias);
266 sk_font.setHinting(SkFontHinting::kSlight);
267 sk_font.setEmbolden(metrics.embolden);
268 sk_font.setSubpixel(true);
269 sk_font.setSize(sk_font.getSize() * static_cast<Scalar>(scaled_font.scale));
270
271 SkColor glyph_color;
273 glyph_color = SK_ColorBLACK;
274 } else if (prop.tone_or_color == GlyphProperties::kLightTone) {
275 glyph_color = SK_ColorWHITE;
276 } else {
277 FML_DCHECK(std::holds_alternative<Color>(prop.tone_or_color));
278 glyph_color = std::get<Color>(prop.tone_or_color).ToARGB();
279 }
280
281 SkPaint glyph_paint;
282 glyph_paint.setColor(glyph_color);
283 glyph_paint.setBlendMode(SkBlendMode::kSrc);
284 if (prop.stroke.has_value()) {
285 auto stroke = prop.stroke;
286 glyph_paint.setStroke(true);
287 glyph_paint.setStrokeWidth(stroke->width *
288 static_cast<Scalar>(scaled_font.scale));
289 glyph_paint.setStrokeCap(ToSkiaCap(stroke->cap));
290 glyph_paint.setStrokeJoin(ToSkiaJoin(stroke->join));
291 glyph_paint.setStrokeMiter(stroke->miter_limit);
292 }
293 canvas->save();
294 Point subpixel_offset = SubpixelPositionToPoint(glyph.subpixel_offset);
295 canvas->translate(subpixel_offset.x, subpixel_offset.y);
296 // Draw a single glyph in the bounds
297 canvas->drawGlyphs({&glyph_id, 1u}, // glyphs
298 {&position, 1u}, // positions
299 SkPoint::Make(-scaled_bounds.GetLeft(),
300 -scaled_bounds.GetTop()), // origin
301 sk_font, // font
302 glyph_paint // paint
303 );
304 canvas->restore();
305}
306
307/// @brief Batch render to a single surface.
308///
309/// This is only safe for use when updating a fresh texture.
310static bool BulkUpdateAtlasBitmap(const GlyphAtlas& atlas,
311 std::shared_ptr<BlitPass>& blit_pass,
312 HostBuffer& data_host_buffer,
313 const std::shared_ptr<Texture>& texture,
314 const std::vector<FontGlyphPair>& new_pairs,
315 size_t start_index,
316 size_t end_index) {
317 TRACE_EVENT0("impeller", __FUNCTION__);
318
319 bool has_light_glyphs =
320 HasLightGlyphs(atlas, new_pairs, start_index, end_index);
321
322 SkBitmap bitmap;
324 atlas, Size(texture->GetSize()), has_light_glyphs));
325 if (!bitmap.tryAllocPixels()) {
326 return false;
327 }
328
329 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
330 if (!surface) {
331 return false;
332 }
333 auto canvas = surface->getCanvas();
334 if (!canvas) {
335 return false;
336 }
337
338 for (size_t i = start_index; i < end_index; i++) {
339 const FontGlyphPair& pair = new_pairs[i];
340 auto data = atlas.FindFontGlyphBounds(pair);
341 if (!data.has_value()) {
342 continue;
343 }
344 auto [pos, bounds, placeholder] = data.value();
345 FML_DCHECK(!placeholder);
346 Size size = pos.GetSize();
347 if (size.IsEmpty()) {
348 continue;
349 }
350
351 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
352 pair.scaled_font, pair.glyph, bounds, pair.glyph.properties);
353 }
354
355 if (has_light_glyphs) {
356 auto a8_bitmap_status = ToA8Bitmap(bitmap);
357 if (!a8_bitmap_status.ok()) {
358 VALIDATION_LOG << a8_bitmap_status.status().message();
359 return false;
360 }
361 bitmap = a8_bitmap_status.value();
362 }
363
364 // Writing to a malloc'd buffer and then copying to the staging buffers
365 // benchmarks as substantially faster on a number of Android devices.
366 BufferView buffer_view = data_host_buffer.Emplace(
367 bitmap.getAddr(0, 0),
368 texture->GetSize().Area() *
370 atlas.GetTexture()->GetTextureDescriptor().format),
371 data_host_buffer.GetMinimumUniformAlignment());
372
373 return blit_pass->AddCopy(std::move(buffer_view), //
374 texture, //
375 IRect::MakeXYWH(0, 0, texture->GetSize().width,
376 texture->GetSize().height));
377}
378
379static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
380 std::shared_ptr<BlitPass>& blit_pass,
381 HostBuffer& data_host_buffer,
382 const std::shared_ptr<Texture>& texture,
383 const std::vector<FontGlyphPair>& new_pairs,
384 size_t start_index,
385 size_t end_index) {
386 TRACE_EVENT0("impeller", __FUNCTION__);
387
388 for (size_t i = start_index; i < end_index; i++) {
389 const FontGlyphPair& pair = new_pairs[i];
390 auto data = atlas.FindFontGlyphBounds(pair);
391 if (!data.has_value()) {
392 continue;
393 }
394 auto [pos, bounds, placeholder] = data.value();
395 FML_DCHECK(!placeholder);
396
397 Size size = pos.GetSize();
398 if (size.IsEmpty()) {
399 continue;
400 }
401 // The uploaded bitmap is expanded by 1px of padding
402 // on each side.
403 size.width += 2;
404 size.height += 2;
405
406 SkBitmap bitmap;
407 bool is_light_glyph =
409
410 bitmap.setInfo(
411 TypographerContextSkia::GetImageInfo(atlas, size, is_light_glyph));
412 if (!bitmap.tryAllocPixels()) {
413 return false;
414 }
415
416 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
417 if (!surface) {
418 return false;
419 }
420 auto canvas = surface->getCanvas();
421 if (!canvas) {
422 return false;
423 }
424
425 DrawGlyph(canvas, SkPoint::Make(1, 1), pair.scaled_font, pair.glyph, bounds,
426 pair.glyph.properties);
427
428 if (is_light_glyph) {
429 auto a8_bitmap_status = ToA8Bitmap(bitmap);
430 if (!a8_bitmap_status.ok()) {
431 VALIDATION_LOG << a8_bitmap_status.status().message();
432 return false;
433 }
434 bitmap = a8_bitmap_status.value();
435 }
436
437 // Writing to a malloc'd buffer and then copying to the staging buffers
438 // benchmarks as substantially faster on a number of Android devices.
439 BufferView buffer_view = data_host_buffer.Emplace(
440 bitmap.getAddr(0, 0),
441 size.Area() * BytesPerPixelForPixelFormat(
442 atlas.GetTexture()->GetTextureDescriptor().format),
443 data_host_buffer.GetMinimumUniformAlignment());
444
445 // convert_to_read is set to false so that the texture remains in a transfer
446 // dst layout until we finish writing to it below. This only has an impact
447 // on Vulkan where we are responsible for managing image layouts.
448 if (!blit_pass->AddCopy(std::move(buffer_view), //
449 texture, //
450 IRect::MakeXYWH(pos.GetLeft() - 1, pos.GetTop() - 1,
451 size.width, size.height), //
452 /*label=*/"", //
453 /*mip_level=*/0, //
454 /*slice=*/0, //
455 /*convert_to_read=*/false //
456 )) {
457 return false;
458 }
459 }
460 return blit_pass->ConvertTextureToShaderRead(texture);
461}
462
463static Rect ComputeGlyphSize(const SkFont& font,
464 const SubpixelGlyph& glyph,
465 Scalar scale) {
466 SkRect scaled_bounds;
467 SkPaint glyph_paint;
468 if (glyph.properties.stroke.has_value()) {
469 glyph_paint.setStroke(true);
470 glyph_paint.setStrokeWidth(glyph.properties.stroke->width * scale);
471 glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties.stroke->cap));
472 glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties.stroke->join));
473 glyph_paint.setStrokeMiter(glyph.properties.stroke->miter_limit);
474 }
475 // Get bounds for a single glyph
476 font.getBounds({&glyph.glyph.index, 1}, {&scaled_bounds, 1}, &glyph_paint);
477
478 // Expand the bounds of glyphs at subpixel offsets by 2 in the x direction.
479 Scalar adjustment = 0.0;
481 adjustment = 1.0;
482 }
483 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
484 scaled_bounds.fRight + adjustment,
485 scaled_bounds.fBottom);
486};
487
488std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
489TypographerContextSkia::CollectNewGlyphs(
490 const std::shared_ptr<GlyphAtlas>& atlas,
491 const std::vector<RenderableText>& renderable_texts) {
492 std::vector<FontGlyphPair> new_glyphs;
493 std::vector<Rect> glyph_sizes;
494 for (const auto& frame : renderable_texts) {
495 Rational rounded_scale = TextFrame::RoundScaledFontSize(
496 frame.origin_transform.GetMaxBasisLengthXY());
497 for (const auto& run : frame.text_frame->GetRuns()) {
498 auto metrics = run.GetFont().GetMetrics();
499
500 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
501
502 FontGlyphAtlas* font_glyph_atlas =
503 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
504 FML_DCHECK(!!font_glyph_atlas);
505
506 SkFont sk_font(
507 TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
508 metrics.point_size, metrics.scaleX, metrics.skewX);
509 sk_font.setEdging(SkFont::Edging::kAntiAlias);
510 sk_font.setHinting(SkFontHinting::kSlight);
511 sk_font.setEmbolden(metrics.embolden);
512 // Rather than computing the bounds at the requested point size and
513 // scaling up the bounds, we scale up the font size and request the
514 // bounds. This seems to give more accurate bounds information.
515 sk_font.setSize(sk_font.getSize() *
516 static_cast<Scalar>(scaled_font.scale));
517 sk_font.setSubpixel(true);
518
519 for (const auto& glyph_position : run.GetGlyphPositions()) {
521 glyph_position, scaled_font.font.GetAxisAlignment(),
522 frame.origin_transform);
523 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
524 frame.properties);
525 const auto& font_glyph_bounds =
526 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
527
528 if (!font_glyph_bounds.has_value()) {
529 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
530 auto glyph_bounds = ComputeGlyphSize(
531 sk_font, subpixel_glyph, static_cast<Scalar>(scaled_font.scale));
532 glyph_sizes.push_back(glyph_bounds);
533
534 auto frame_bounds = FrameBounds{
535 Rect::MakeLTRB(0, 0, 0, 0), //
536 glyph_bounds, //
537 /*placeholder=*/true //
538 };
539
540 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
541 }
542 }
543 }
544 }
545 return {std::move(new_glyphs), std::move(glyph_sizes)};
546}
547
549 Context& context,
551 HostBuffer& data_host_buffer,
552 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
553 const std::vector<RenderableText>& renderable_texts) const {
554 TRACE_EVENT0("impeller", __FUNCTION__);
555 if (!IsValid()) {
556 return nullptr;
557 }
558 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
559 FML_DCHECK(last_atlas->GetType() == type);
560
561 if (renderable_texts.empty()) {
562 return last_atlas;
563 }
564
565 // ---------------------------------------------------------------------------
566 // Step 1: Determine if the atlas type and font glyph pairs are compatible
567 // with the current atlas and reuse if possible. For each new font and
568 // glyph pair, compute the glyph size at scale.
569 // ---------------------------------------------------------------------------
570 auto [new_glyphs, glyph_sizes] =
571 CollectNewGlyphs(last_atlas, renderable_texts);
572 if (new_glyphs.size() == 0) {
573 return last_atlas;
574 }
575
576 // ---------------------------------------------------------------------------
577 // Step 2: Determine if the additional missing glyphs can be appended to the
578 // existing bitmap without recreating the atlas.
579 // ---------------------------------------------------------------------------
580 std::vector<Rect> glyph_positions;
581 glyph_positions.reserve(new_glyphs.size());
582 size_t first_missing_index = 0;
583
584 if (last_atlas->GetTexture()) {
585 // Append all glyphs that fit into the current atlas.
586 first_missing_index = AppendToExistingAtlas(
587 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
588 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
589 atlas_context->GetRectPacker());
590
591 // ---------------------------------------------------------------------------
592 // Step 3a: Record the positions in the glyph atlas of the newly added
593 // glyphs.
594 // ---------------------------------------------------------------------------
595 for (size_t i = 0; i < first_missing_index; i++) {
596 last_atlas->AddTypefaceGlyphPositionAndBounds(
597 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
598 }
599
600 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
601 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
602
603 fml::ScopedCleanupClosure closure([&]() {
604 blit_pass->EncodeCommands();
605 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
606 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
607 }
608 });
609
610 // ---------------------------------------------------------------------------
611 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
612 // the uploads into the blit pass.
613 // ---------------------------------------------------------------------------
614 if (!UpdateAtlasBitmap(*last_atlas, blit_pass, data_host_buffer,
615 last_atlas->GetTexture(), new_glyphs, 0,
616 first_missing_index)) {
617 return nullptr;
618 }
619
620 // If all glyphs fit, just return the old atlas.
621 if (first_missing_index == new_glyphs.size()) {
622 return last_atlas;
623 }
624 }
625
626 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
627 const int64_t max_texture_height =
628 context.GetResourceAllocator()->GetMaxTextureSizeSupported().height;
629
630 // IF the current atlas size is as big as it can get, then "GC" and create an
631 // atlas with only the required glyphs. OpenGLES cannot reliably perform the
632 // blit required here, as 1) it requires attaching textures as read and write
633 // framebuffers which has substantially smaller size limits that max textures
634 // and 2) is missing a GLES 2.0 implementation and cap check.
635 bool blit_old_atlas = true;
636 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
637 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
639 blit_old_atlas = false;
640 new_atlas = std::make_shared<GlyphAtlas>(
641 type, /*initial_generation=*/last_atlas->GetAtlasGeneration() + 1);
642
643 auto [update_glyphs, update_sizes] =
644 CollectNewGlyphs(new_atlas, renderable_texts);
645 new_glyphs = std::move(update_glyphs);
646 glyph_sizes = std::move(update_sizes);
647
648 glyph_positions.clear();
649 glyph_positions.reserve(new_glyphs.size());
650 first_missing_index = 0;
651
652 height_adjustment = 0;
653 atlas_context->UpdateRectPacker(nullptr);
654 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
655 }
656
657 // A new glyph atlas must be created.
658 ISize atlas_size = ComputeNextAtlasSize(atlas_context, //
659 new_glyphs, //
660 glyph_positions, //
661 glyph_sizes, //
662 first_missing_index, //
663 max_texture_height //
664 );
665
666 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
667 if (atlas_size.IsEmpty()) {
668 return nullptr;
669 }
670 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
671
672 TextureDescriptor descriptor;
673 switch (type) {
675 descriptor.format =
676 context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
677 break;
680 break;
681 }
682 descriptor.size = atlas_size;
684 descriptor.usage = TextureUsage::kShaderRead;
685 std::shared_ptr<Texture> new_texture =
686 context.GetResourceAllocator()->CreateTexture(descriptor);
687 if (!new_texture) {
688 return nullptr;
689 }
690
691 new_texture->SetLabel("GlyphAtlas");
692
693 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
694 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
695
696 fml::ScopedCleanupClosure closure([&]() {
697 blit_pass->EncodeCommands();
698 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
699 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
700 }
701 });
702
703 // Now append all remaining glyphs. This should never have any missing data...
704 auto old_texture = new_atlas->GetTexture();
705 new_atlas->SetTexture(std::move(new_texture));
706
707 // ---------------------------------------------------------------------------
708 // Step 3a: Record the positions in the glyph atlas of the newly added
709 // glyphs.
710 // ---------------------------------------------------------------------------
711 for (size_t i = first_missing_index; i < glyph_positions.size(); i++) {
712 new_atlas->AddTypefaceGlyphPositionAndBounds(
713 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
714 }
715
716 // ---------------------------------------------------------------------------
717 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
718 // the uploads into the blit pass.
719 // ---------------------------------------------------------------------------
720 if (!BulkUpdateAtlasBitmap(*new_atlas, blit_pass, data_host_buffer,
721 new_atlas->GetTexture(), new_glyphs,
722 first_missing_index, new_glyphs.size())) {
723 return nullptr;
724 }
725
726 // Blit the old texture to the top left of the new atlas.
727 if (blit_old_atlas && old_texture) {
728 blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
729 IRect::MakeSize(new_atlas->GetTexture()->GetSize()),
730 {0, 0});
731 }
732
733 // ---------------------------------------------------------------------------
734 // Step 8b: Record the texture in the glyph atlas.
735 // ---------------------------------------------------------------------------
736
737 return new_atlas;
738}
739
740} // namespace impeller
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
static TypefaceSkia & Cast(Typeface &base)
To do anything rendering related with Impeller, you need a context.
Definition context.h:69
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< CommandBuffer > CreateCommandBuffer() const =0
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
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.
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.
const std::shared_ptr< Texture > & GetTexture() const
Get the texture for the glyph atlas.
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.
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
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size, bool support_light_glyphs)
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
VkSurfaceKHR surface
Definition main.cc:65
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
std::shared_ptr< SkBitmap > bitmap
FlTexture * texture
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 GlyphProperties &prop)
static Point SubpixelPositionToPoint(SubpixelPosition pos)
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
impeller::ShaderType type
A font along with a glyph in that font rendered at a particular scale and subpixel position.
uint16_t index
Definition glyph.h:22
static constexpr ToneOrColor kLightTone
static constexpr ToneOrColor kDarkTone
std::optional< StrokeParameters > stroke
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.
SubpixelPosition subpixel_offset
constexpr auto GetTop() const
Definition rect.h:387
constexpr auto GetLeft() const
Definition rect.h:385
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
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...
#define TRACE_EVENT0(category_group, name)
#define VALIDATION_LOG
Definition validation.h:91