Flutter Engine
 
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#include "include/core/SkColor.h"
36#include "include/core/SkImageInfo.h"
37#include "include/core/SkPaint.h"
38#include "include/core/SkSize.h"
39
40#include "third_party/skia/include/core/SkBitmap.h"
41#include "third_party/skia/include/core/SkBlendMode.h"
42#include "third_party/skia/include/core/SkCanvas.h"
43#include "third_party/skia/include/core/SkFont.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} // namespace
75
76std::shared_ptr<TypographerContext> TypographerContextSkia::Make() {
77 return std::make_shared<TypographerContextSkia>();
78}
79
81
83
84std::shared_ptr<GlyphAtlasContext>
86 return std::make_shared<GlyphAtlasContext>(type);
87}
88
89static 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 }
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.
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
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
211static 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.
265static 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
324static 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),
375 size.Area() * BytesPerPixelForPixelFormat(
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
397static 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
422std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
423TypographerContextSkia::CollectNewGlyphs(
424 const std::shared_ptr<GlyphAtlas>& atlas,
425 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
426 std::vector<FontGlyphPair> new_glyphs;
427 std::vector<Rect> glyph_sizes;
428 size_t generation_id = atlas->GetAtlasGeneration();
429 intptr_t atlas_id = reinterpret_cast<intptr_t>(atlas.get());
430 for (const auto& frame : text_frames) {
431// TODO(jonahwilliams): determine how to re-enable this. See
432// https://github.com/flutter/flutter/issues/163730 for example. This can
433// happen when the Aiks/Typographer context are re-created, but the last
434// DisplayList is re-used. The "atlas_id" check is not reliable, perhaps
435// because it may end up with the same memory?
436#if false
437 auto [frame_generation_id, frame_atlas_id] =
438 frame->GetAtlasGenerationAndID();
439 if (atlas->IsValid() && frame->IsFrameComplete() &&
440 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
441 !frame->GetFrameBounds(0).is_placeholder) {
442 continue;
443 }
444#endif // false
445 frame->ClearFrameBounds();
446 frame->SetAtlasGeneration(generation_id, atlas_id);
447
448 for (const auto& run : frame->GetRuns()) {
449 auto metrics = run.GetFont().GetMetrics();
450
451 auto rounded_scale = TextFrame::RoundScaledFontSize(frame->GetScale());
452 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
453
454 FontGlyphAtlas* font_glyph_atlas =
455 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
456 FML_DCHECK(!!font_glyph_atlas);
457
458 SkFont sk_font(
459 TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
460 metrics.point_size, metrics.scaleX, metrics.skewX);
461 sk_font.setEdging(SkFont::Edging::kAntiAlias);
462 sk_font.setHinting(SkFontHinting::kSlight);
463 sk_font.setEmbolden(metrics.embolden);
464 // Rather than computing the bounds at the requested point size and
465 // scaling up the bounds, we scale up the font size and request the
466 // bounds. This seems to give more accurate bounds information.
467 sk_font.setSize(sk_font.getSize() *
468 static_cast<Scalar>(scaled_font.scale));
469 sk_font.setSubpixel(true);
470
471 for (const auto& glyph_position : run.GetGlyphPositions()) {
473 glyph_position, scaled_font.font.GetAxisAlignment(),
474 frame->GetOffsetTransform());
475 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
476 frame->GetProperties());
477 const auto& font_glyph_bounds =
478 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
479
480 if (!font_glyph_bounds.has_value()) {
481 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
482 auto glyph_bounds = ComputeGlyphSize(
483 sk_font, subpixel_glyph, static_cast<Scalar>(scaled_font.scale));
484 glyph_sizes.push_back(glyph_bounds);
485
486 auto frame_bounds = FrameBounds{
487 Rect::MakeLTRB(0, 0, 0, 0), //
488 glyph_bounds, //
489 /*placeholder=*/true //
490 };
491
492 frame->AppendFrameBounds(frame_bounds);
493 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
494 } else {
495 frame->AppendFrameBounds(font_glyph_bounds.value());
496 }
497 }
498 }
499 }
500 return {std::move(new_glyphs), std::move(glyph_sizes)};
501}
502
504 Context& context,
506 HostBuffer& data_host_buffer,
507 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
508 const std::vector<std::shared_ptr<TextFrame>>& text_frames) const {
509 TRACE_EVENT0("impeller", __FUNCTION__);
510 if (!IsValid()) {
511 return nullptr;
512 }
513 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
514 FML_DCHECK(last_atlas->GetType() == type);
515
516 if (text_frames.empty()) {
517 return last_atlas;
518 }
519
520 // ---------------------------------------------------------------------------
521 // Step 1: Determine if the atlas type and font glyph pairs are compatible
522 // with the current atlas and reuse if possible. For each new font and
523 // glyph pair, compute the glyph size at scale.
524 // ---------------------------------------------------------------------------
525 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
526 if (new_glyphs.size() == 0) {
527 return last_atlas;
528 }
529
530 // ---------------------------------------------------------------------------
531 // Step 2: Determine if the additional missing glyphs can be appended to the
532 // existing bitmap without recreating the atlas.
533 // ---------------------------------------------------------------------------
534 std::vector<Rect> glyph_positions;
535 glyph_positions.reserve(new_glyphs.size());
536 size_t first_missing_index = 0;
537
538 if (last_atlas->GetTexture()) {
539 // Append all glyphs that fit into the current atlas.
540 first_missing_index = AppendToExistingAtlas(
541 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
542 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
543 atlas_context->GetRectPacker());
544
545 // ---------------------------------------------------------------------------
546 // Step 3a: Record the positions in the glyph atlas of the newly added
547 // glyphs.
548 // ---------------------------------------------------------------------------
549 for (size_t i = 0; i < first_missing_index; i++) {
550 last_atlas->AddTypefaceGlyphPositionAndBounds(
551 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
552 }
553
554 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
555 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
556
557 fml::ScopedCleanupClosure closure([&]() {
558 blit_pass->EncodeCommands();
559 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
560 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
561 }
562 });
563
564 // ---------------------------------------------------------------------------
565 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
566 // the uploads into the blit pass.
567 // ---------------------------------------------------------------------------
568 if (!UpdateAtlasBitmap(*last_atlas, blit_pass, data_host_buffer,
569 last_atlas->GetTexture(), new_glyphs, 0,
570 first_missing_index)) {
571 return nullptr;
572 }
573
574 // If all glyphs fit, just return the old atlas.
575 if (first_missing_index == new_glyphs.size()) {
576 return last_atlas;
577 }
578 }
579
580 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
581 const int64_t max_texture_height =
582 context.GetResourceAllocator()->GetMaxTextureSizeSupported().height;
583
584 // IF the current atlas size is as big as it can get, then "GC" and create an
585 // atlas with only the required glyphs. OpenGLES cannot reliably perform the
586 // blit required here, as 1) it requires attaching textures as read and write
587 // framebuffers which has substantially smaller size limits that max textures
588 // and 2) is missing a GLES 2.0 implementation and cap check.
589 bool blit_old_atlas = true;
590 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
591 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
593 blit_old_atlas = false;
594 new_atlas = std::make_shared<GlyphAtlas>(
595 type, /*initial_generation=*/last_atlas->GetAtlasGeneration() + 1);
596
597 auto [update_glyphs, update_sizes] =
598 CollectNewGlyphs(new_atlas, text_frames);
599 new_glyphs = std::move(update_glyphs);
600 glyph_sizes = std::move(update_sizes);
601
602 glyph_positions.clear();
603 glyph_positions.reserve(new_glyphs.size());
604 first_missing_index = 0;
605
606 height_adjustment = 0;
607 atlas_context->UpdateRectPacker(nullptr);
608 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
609 }
610
611 // A new glyph atlas must be created.
612 ISize atlas_size = ComputeNextAtlasSize(atlas_context, //
613 new_glyphs, //
614 glyph_positions, //
615 glyph_sizes, //
616 first_missing_index, //
617 max_texture_height //
618 );
619
620 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
621 if (atlas_size.IsEmpty()) {
622 return nullptr;
623 }
624 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
625
626 TextureDescriptor descriptor;
627 switch (type) {
629 descriptor.format =
630 context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
631 break;
634 break;
635 }
636 descriptor.size = atlas_size;
638 descriptor.usage = TextureUsage::kShaderRead;
639 std::shared_ptr<Texture> new_texture =
640 context.GetResourceAllocator()->CreateTexture(descriptor);
641 if (!new_texture) {
642 return nullptr;
643 }
644
645 new_texture->SetLabel("GlyphAtlas");
646
647 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
648 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
649
650 fml::ScopedCleanupClosure closure([&]() {
651 blit_pass->EncodeCommands();
652 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
653 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
654 }
655 });
656
657 // Now append all remaining glyphs. This should never have any missing data...
658 auto old_texture = new_atlas->GetTexture();
659 new_atlas->SetTexture(std::move(new_texture));
660
661 // ---------------------------------------------------------------------------
662 // Step 3a: Record the positions in the glyph atlas of the newly added
663 // glyphs.
664 // ---------------------------------------------------------------------------
665 for (size_t i = first_missing_index; i < glyph_positions.size(); i++) {
666 new_atlas->AddTypefaceGlyphPositionAndBounds(
667 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
668 }
669
670 // ---------------------------------------------------------------------------
671 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
672 // the uploads into the blit pass.
673 // ---------------------------------------------------------------------------
674 if (!BulkUpdateAtlasBitmap(*new_atlas, blit_pass, data_host_buffer,
675 new_atlas->GetTexture(), new_glyphs,
676 first_missing_index, new_glyphs.size())) {
677 return nullptr;
678 }
679
680 // Blit the old texture to the top left of the new atlas.
681 if (blit_old_atlas && old_texture) {
682 blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
683 IRect::MakeSize(new_atlas->GetTexture()->GetSize()),
684 {0, 0});
685 }
686
687 // ---------------------------------------------------------------------------
688 // Step 8b: Record the texture in the glyph atlas.
689 // ---------------------------------------------------------------------------
690
691 return new_atlas;
692}
693
694} // namespace impeller
GLenum type
BufferView buffer_view
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:65
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< std::shared_ptr< TextFrame > > &text_frames) const override
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
FlTexture * texture
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition formats.h:466
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:327
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
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...
std::shared_ptr< const fml::Mapping > data
#define TRACE_EVENT0(category_group, name)