| 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
5 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
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
18 #include "impeller/base/validation.h"
19 #include "impeller/core/allocator.h"
20 #include "impeller/core/buffer_view.h"
21 #include "impeller/core/formats.h"
22 #include "impeller/core/host_buffer.h"
23 #include "impeller/core/platform.h"
24 #include "impeller/core/texture_descriptor.h"
25 #include "impeller/geometry/rect.h"
26 #include "impeller/geometry/size.h"
27 #include "impeller/renderer/command_buffer.h"
28 #include "impeller/renderer/render_pass.h"
29 #include "impeller/renderer/render_target.h"
30 #include "impeller/typographer/backends/skia/typeface_skia.h"
31 #include "impeller/typographer/font_glyph_pair.h"
32 #include "impeller/typographer/glyph.h"
33 #include "impeller/typographer/glyph_atlas.h"
34 #include "impeller/typographer/rectangle_packer.h"
35 #include "impeller/typographer/typographer_context.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
81 TypographerContextSkia::TypographerContextSkia() = default;
82
83 TypographerContextSkia::~TypographerContextSkia() = default;
84
85 std::shared_ptr<GlyphAtlasContext>
86 TypographerContextSkia::CreateGlyphAtlasContext(GlyphAtlas::Type type) const {
87 return std::make_shared<GlyphAtlasContext>(type);
88 }
89
90 static SkImageInfo GetImageInfo(const GlyphAtlas& atlas, Size size) {
91 switch (atlas.GetType()) {
92 case GlyphAtlas::Type::kAlphaBitmap:
93 return SkImageInfo::MakeA8(SkISize{static_cast<int32_t>(size.width),
94 static_cast<int32_t>(size.height)});
95 case GlyphAtlas::Type::kColorBitmap:
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
167 static ISize ComputeNextAtlasSize(
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 Point SubpixelPositionToPoint(SubpixelPosition pos) {
209 return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
210 }
211
212 static void DrawGlyph(SkCanvas* canvas,
213 const SkPoint position,
214 const ScaledFont& scaled_font,
215 const SubpixelGlyph& glyph,
216 const Rect& scaled_bounds,
217 const std::optional<GlyphProperties>& prop,
218 bool has_color) {
219 const auto& metrics = scaled_font.font.GetMetrics();
220 SkGlyphID glyph_id = glyph.glyph.index;
221
222 SkFont sk_font(
223 TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
224 metrics.point_size, metrics.scaleX, metrics.skewX);
225 sk_font.setEdging(SkFont::Edging::kAntiAlias);
226 sk_font.setHinting(SkFontHinting::kSlight);
227 sk_font.setEmbolden(metrics.embolden);
228 sk_font.setSubpixel(true);
229 sk_font.setSize(sk_font.getSize() * static_cast<Scalar>(scaled_font.scale));
230
231 auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK;
232
233 SkPaint glyph_paint;
234 glyph_paint.setColor(glyph_color);
235 glyph_paint.setBlendMode(SkBlendMode::kSrc);
236 if (prop.has_value() && prop->stroke) {
237 glyph_paint.setStroke(true);
238 glyph_paint.setStrokeWidth(prop->stroke_width *
239 static_cast<Scalar>(scaled_font.scale));
240 glyph_paint.setStrokeCap(ToSkiaCap(prop->stroke_cap));
241 glyph_paint.setStrokeJoin(ToSkiaJoin(prop->stroke_join));
242 glyph_paint.setStrokeMiter(prop->stroke_miter);
243 }
244 canvas->save();
245 Point subpixel_offset = SubpixelPositionToPoint(glyph.subpixel_offset);
246 canvas->translate(subpixel_offset.x, subpixel_offset.y);
247 canvas->drawGlyphs(1u, // count
248 &glyph_id, // glyphs
249 &position, // positions
250 SkPoint::Make(-scaled_bounds.GetLeft(),
251 -scaled_bounds.GetTop()), // origin
252 sk_font, // font
253 glyph_paint // paint
254 );
255 canvas->restore();
256 }
257
258 /// @brief Batch render to a single surface.
259 ///
260 /// This is only safe for use when updating a fresh texture.
261 static bool BulkUpdateAtlasBitmap(const GlyphAtlas& atlas,
262 std::shared_ptr<BlitPass>& blit_pass,
263 HostBuffer& host_buffer,
264 const std::shared_ptr<Texture>& texture,
265 const std::vector<FontGlyphPair>& new_pairs,
266 size_t start_index,
267 size_t end_index) {
268 TRACE_EVENT0("impeller", __FUNCTION__);
269
270 bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
271
272 SkBitmap bitmap;
273 bitmap.setInfo(GetImageInfo(atlas, Size(texture->GetSize())));
274 if (!bitmap.tryAllocPixels()) {
275 return false;
276 }
277
278 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
279 if (!surface) {
280 return false;
281 }
282 auto canvas = surface->getCanvas();
283 if (!canvas) {
284 return false;
285 }
286
287 for (size_t i = start_index; i < end_index; i++) {
288 const FontGlyphPair& pair = new_pairs[i];
289 auto data = atlas.FindFontGlyphBounds(pair);
290 if (!data.has_value()) {
291 continue;
292 }
293 auto [pos, bounds, placeholder] = data.value();
294 FML_DCHECK(!placeholder);
295 Size size = pos.GetSize();
296 if (size.IsEmpty()) {
297 continue;
298 }
299
300 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
301 pair.scaled_font, pair.glyph, bounds, pair.glyph.properties,
302 has_color);
303 }
304
305 // Writing to a malloc'd buffer and then copying to the staging buffers
306 // benchmarks as substantially faster on a number of Android devices.
307 BufferView buffer_view = host_buffer.Emplace(
308 bitmap.getAddr(0, 0),
309 texture->GetSize().Area() *
310 BytesPerPixelForPixelFormat(
311 atlas.GetTexture()->GetTextureDescriptor().format),
312 DefaultUniformAlignment());
313
314 return blit_pass->AddCopy(std::move(buffer_view), //
315 texture, //
316 IRect::MakeXYWH(0, 0, texture->GetSize().width,
317 texture->GetSize().height));
318 }
319
320 static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
321 std::shared_ptr<BlitPass>& blit_pass,
322 HostBuffer& host_buffer,
323 const std::shared_ptr<Texture>& texture,
324 const std::vector<FontGlyphPair>& new_pairs,
325 size_t start_index,
326 size_t end_index) {
327 TRACE_EVENT0("impeller", __FUNCTION__);
328
329 bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
330
331 for (size_t i = start_index; i < end_index; i++) {
332 const FontGlyphPair& pair = new_pairs[i];
333 auto data = atlas.FindFontGlyphBounds(pair);
334 if (!data.has_value()) {
335 continue;
336 }
337 auto [pos, bounds, placeholder] = data.value();
338 FML_DCHECK(!placeholder);
339
340 Size size = pos.GetSize();
341 if (size.IsEmpty()) {
342 continue;
343 }
344 // The uploaded bitmap is expanded by 1px of padding
345 // on each side.
346 size.width += 2;
347 size.height += 2;
348
349 SkBitmap bitmap;
350 bitmap.setInfo(GetImageInfo(atlas, size));
351 if (!bitmap.tryAllocPixels()) {
352 return false;
353 }
354
355 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
356 if (!surface) {
357 return false;
358 }
359 auto canvas = surface->getCanvas();
360 if (!canvas) {
361 return false;
362 }
363
364 DrawGlyph(canvas, SkPoint::Make(1, 1), pair.scaled_font, pair.glyph, bounds,
365 pair.glyph.properties, has_color);
366
367 // Writing to a malloc'd buffer and then copying to the staging buffers
368 // benchmarks as substantially faster on a number of Android devices.
369 BufferView buffer_view = host_buffer.Emplace(
370 bitmap.getAddr(0, 0),
371 size.Area() * BytesPerPixelForPixelFormat(
372 atlas.GetTexture()->GetTextureDescriptor().format),
373 DefaultUniformAlignment());
374
375 // convert_to_read is set to false so that the texture remains in a transfer
376 // dst layout until we finish writing to it below. This only has an impact
377 // on Vulkan where we are responsible for managing image layouts.
378 if (!blit_pass->AddCopy(std::move(buffer_view), //
379 texture, //
380 IRect::MakeXYWH(pos.GetLeft() - 1, pos.GetTop() - 1,
381 size.width, size.height), //
382 /*label=*/"", //
383 /*mip_level=*/0, //
384 /*slice=*/0, //
385 /*convert_to_read=*/false //
386 )) {
387 return false;
388 }
389 }
390 return blit_pass->ConvertTextureToShaderRead(texture);
391 }
392
393 static Rect ComputeGlyphSize(const SkFont& font,
394 const SubpixelGlyph& glyph,
395 Scalar scale) {
396 SkRect scaled_bounds;
397 SkPaint glyph_paint;
398 if (glyph.properties.has_value() && glyph.properties->stroke) {
399 glyph_paint.setStroke(true);
400 glyph_paint.setStrokeWidth(glyph.properties->stroke_width * scale);
401 glyph_paint.setStrokeCap(ToSkiaCap(glyph.properties->stroke_cap));
402 glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.properties->stroke_join));
403 glyph_paint.setStrokeMiter(glyph.properties->stroke_miter);
404 }
405 font.getBounds(&glyph.glyph.index, 1, &scaled_bounds, &glyph_paint);
406
407 // Expand the bounds of glyphs at subpixel offsets by 2 in the x direction.
408 Scalar adjustment = 0.0;
409 if (glyph.subpixel_offset != SubpixelPosition::kSubpixel00) {
410 adjustment = 1.0;
411 }
412 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
413 scaled_bounds.fRight + adjustment,
414 scaled_bounds.fBottom);
415 };
416
417 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
418 TypographerContextSkia::CollectNewGlyphs(
419 const std::shared_ptr<GlyphAtlas>& atlas,
420 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
421 std::vector<FontGlyphPair> new_glyphs;
422 std::vector<Rect> glyph_sizes;
423 size_t generation_id = atlas->GetAtlasGeneration();
424 intptr_t atlas_id = reinterpret_cast<intptr_t>(atlas.get());
425 for (const auto& frame : text_frames) {
426 // TODO(jonahwilliams): determine how to re-enable this. See
427 // https://github.com/flutter/flutter/issues/163730 for example. This can
428 // happen when the Aiks/Typographer context are re-created, but the last
429 // DisplayList is re-used. The "atlas_id" check is not reliable, perhaps
430 // because it may end up with the same memory?
431 #if false
432 auto [frame_generation_id, frame_atlas_id] =
433 frame->GetAtlasGenerationAndID();
434 if (atlas->IsValid() && frame->IsFrameComplete() &&
435 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
436 !frame->GetFrameBounds(0).is_placeholder) {
437 continue;
438 }
439 #endif // false
440 frame->ClearFrameBounds();
441 frame->SetAtlasGeneration(generation_id, atlas_id);
442
443 for (const auto& run : frame->GetRuns()) {
444 auto metrics = run.GetFont().GetMetrics();
445
446 auto rounded_scale = TextFrame::RoundScaledFontSize(frame->GetScale());
447 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
448
449 FontGlyphAtlas* font_glyph_atlas =
450 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
451 FML_DCHECK(!!font_glyph_atlas);
452
453 SkFont sk_font(
454 TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(),
455 metrics.point_size, metrics.scaleX, metrics.skewX);
456 sk_font.setEdging(SkFont::Edging::kAntiAlias);
457 sk_font.setHinting(SkFontHinting::kSlight);
458 sk_font.setEmbolden(metrics.embolden);
459 // Rather than computing the bounds at the requested point size and
460 // scaling up the bounds, we scale up the font size and request the
461 // bounds. This seems to give more accurate bounds information.
462 sk_font.setSize(sk_font.getSize() *
463 static_cast<Scalar>(scaled_font.scale));
464 sk_font.setSubpixel(true);
465
466 for (const auto& glyph_position : run.GetGlyphPositions()) {
467 SubpixelPosition subpixel = TextFrame::ComputeSubpixelPosition(
468 glyph_position, scaled_font.font.GetAxisAlignment(),
469 frame->GetOffsetTransform());
470 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
471 frame->GetProperties());
472 const auto& font_glyph_bounds =
473 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
474
475 if (!font_glyph_bounds.has_value()) {
476 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
477 auto glyph_bounds = ComputeGlyphSize(
478 sk_font, subpixel_glyph, static_cast<Scalar>(scaled_font.scale));
479 glyph_sizes.push_back(glyph_bounds);
480
481 auto frame_bounds = FrameBounds{
482 Rect::MakeLTRB(0, 0, 0, 0), //
483 glyph_bounds, //
484 /*placeholder=*/true //
485 };
486
487 frame->AppendFrameBounds(frame_bounds);
488 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
489 } else {
490 frame->AppendFrameBounds(font_glyph_bounds.value());
491 }
492 }
493 }
494 }
495 return {std::move(new_glyphs), std::move(glyph_sizes)};
496 }
497
498 std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
499 Context& context,
500 GlyphAtlas::Type type,
501 HostBuffer& host_buffer,
502 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
503 const std::vector<std::shared_ptr<TextFrame>>& text_frames) const {
504 TRACE_EVENT0("impeller", __FUNCTION__);
505 if (!IsValid()) {
506 return nullptr;
507 }
508 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
509 FML_DCHECK(last_atlas->GetType() == type);
510
511 if (text_frames.empty()) {
512 return last_atlas;
513 }
514
515 // ---------------------------------------------------------------------------
516 // Step 1: Determine if the atlas type and font glyph pairs are compatible
517 // with the current atlas and reuse if possible. For each new font and
518 // glyph pair, compute the glyph size at scale.
519 // ---------------------------------------------------------------------------
520 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
521 if (new_glyphs.size() == 0) {
522 return last_atlas;
523 }
524
525 // ---------------------------------------------------------------------------
526 // Step 2: Determine if the additional missing glyphs can be appended to the
527 // existing bitmap without recreating the atlas.
528 // ---------------------------------------------------------------------------
529 std::vector<Rect> glyph_positions;
530 glyph_positions.reserve(new_glyphs.size());
531 size_t first_missing_index = 0;
532
533 if (last_atlas->GetTexture()) {
534 // Append all glyphs that fit into the current atlas.
535 first_missing_index = AppendToExistingAtlas(
536 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
537 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
538 atlas_context->GetRectPacker());
539
540 // ---------------------------------------------------------------------------
541 // Step 3a: Record the positions in the glyph atlas of the newly added
542 // glyphs.
543 // ---------------------------------------------------------------------------
544 for (size_t i = 0; i < first_missing_index; i++) {
545 last_atlas->AddTypefaceGlyphPositionAndBounds(
546 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
547 }
548
549 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
550 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
551
552 fml::ScopedCleanupClosure closure([&]() {
553 blit_pass->EncodeCommands();
554 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
555 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
556 }
557 });
558
559 // ---------------------------------------------------------------------------
560 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
561 // the uploads into the blit pass.
562 // ---------------------------------------------------------------------------
563 if (!UpdateAtlasBitmap(*last_atlas, blit_pass, host_buffer,
564 last_atlas->GetTexture(), new_glyphs, 0,
565 first_missing_index)) {
566 return nullptr;
567 }
568
569 // If all glyphs fit, just return the old atlas.
570 if (first_missing_index == new_glyphs.size()) {
571 return last_atlas;
572 }
573 }
574
575 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
576 const int64_t max_texture_height =
577 context.GetResourceAllocator()->GetMaxTextureSizeSupported().height;
578
579 // IF the current atlas size is as big as it can get, then "GC" and create an
580 // atlas with only the required glyphs. OpenGLES cannot reliably perform the
581 // blit required here, as 1) it requires attaching textures as read and write
582 // framebuffers which has substantially smaller size limits that max textures
583 // and 2) is missing a GLES 2.0 implementation and cap check.
584 bool blit_old_atlas = true;
585 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
586 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
587 context.GetBackendType() == Context::BackendType::kOpenGLES) {
588 blit_old_atlas = false;
589 new_atlas = std::make_shared<GlyphAtlas>(
590 type, /*initial_generation=*/last_atlas->GetAtlasGeneration() + 1);
591
592 auto [update_glyphs, update_sizes] =
593 CollectNewGlyphs(new_atlas, text_frames);
594 new_glyphs = std::move(update_glyphs);
595 glyph_sizes = std::move(update_sizes);
596
597 glyph_positions.clear();
598 glyph_positions.reserve(new_glyphs.size());
599 first_missing_index = 0;
600
601 height_adjustment = 0;
602 atlas_context->UpdateRectPacker(nullptr);
603 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
604 }
605
606 // A new glyph atlas must be created.
607 ISize atlas_size = ComputeNextAtlasSize(atlas_context, //
608 new_glyphs, //
609 glyph_positions, //
610 glyph_sizes, //
611 first_missing_index, //
612 max_texture_height //
613 );
614
615 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
616 if (atlas_size.IsEmpty()) {
617 return nullptr;
618 }
619 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
620
621 TextureDescriptor descriptor;
622 switch (type) {
623 case GlyphAtlas::Type::kAlphaBitmap:
624 descriptor.format =
625 context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
626 break;
627 case GlyphAtlas::Type::kColorBitmap:
628 descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
629 break;
630 }
631 descriptor.size = atlas_size;
632 descriptor.storage_mode = StorageMode::kDevicePrivate;
633 descriptor.usage = TextureUsage::kShaderRead;
634 std::shared_ptr<Texture> new_texture =
635 context.GetResourceAllocator()->CreateTexture(descriptor);
636 if (!new_texture) {
637 return nullptr;
638 }
639
640 new_texture->SetLabel("GlyphAtlas");
641
642 std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
643 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
644
645 fml::ScopedCleanupClosure closure([&]() {
646 blit_pass->EncodeCommands();
647 if (!context.EnqueueCommandBuffer(std::move(cmd_buffer))) {
648 VALIDATION_LOG << "Failed to submit glyph atlas command buffer";
649 }
650 });
651
652 // Now append all remaining glyphs. This should never have any missing data...
653 auto old_texture = new_atlas->GetTexture();
654 new_atlas->SetTexture(std::move(new_texture));
655
656 // ---------------------------------------------------------------------------
657 // Step 3a: Record the positions in the glyph atlas of the newly added
658 // glyphs.
659 // ---------------------------------------------------------------------------
660 for (size_t i = first_missing_index; i < glyph_positions.size(); i++) {
661 new_atlas->AddTypefaceGlyphPositionAndBounds(
662 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
663 }
664
665 // ---------------------------------------------------------------------------
666 // Step 4a: Draw new font-glyph pairs into the a host buffer and encode
667 // the uploads into the blit pass.
668 // ---------------------------------------------------------------------------
669 if (!BulkUpdateAtlasBitmap(*new_atlas, blit_pass, host_buffer,
670 new_atlas->GetTexture(), new_glyphs,
671 first_missing_index, new_glyphs.size())) {
672 return nullptr;
673 }
674
675 // Blit the old texture to the top left of the new atlas.
676 if (blit_old_atlas && old_texture) {
677 blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
678 IRect::MakeSize(new_atlas->GetTexture()->GetSize()),
679 {0, 0});
680 }
681
682 // ---------------------------------------------------------------------------
683 // Step 8b: Record the texture in the glyph atlas.
684 // ---------------------------------------------------------------------------
685
686 return new_atlas;
687 }
688
689 } // namespace impeller
allocator.h
type
GLenum type
Definition: blit_command_gles.cc:151
buffer_view
BufferView buffer_view
Definition: blit_command_gles.cc:152
buffer_view.h
impeller::BackendCast< TypefaceSkia, Typeface >::Cast
static TypefaceSkia & Cast(Typeface &base)
Definition: backend_cast.h:13
impeller::Context
To do anything rendering related with Impeller, you need a context.
Definition: context.h:47
impeller::Context::CreateCommandBuffer
virtual std::shared_ptr< CommandBuffer > CreateCommandBuffer() const =0
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
impeller::Context::GetCapabilities
virtual const std::shared_ptr< const Capabilities > & GetCapabilities() const =0
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
impeller::Context::GetBackendType
virtual BackendType GetBackendType() const =0
Get the graphics backend of an Impeller context.
impeller::Context::GetResourceAllocator
virtual std::shared_ptr< Allocator > GetResourceAllocator() const =0
Returns the allocator used to create textures and buffers on the device.
impeller::Context::BackendType::kOpenGLES
@ kOpenGLES
impeller::Context::EnqueueCommandBuffer
virtual bool EnqueueCommandBuffer(std::shared_ptr< CommandBuffer > command_buffer)
Enqueue command_buffer for submission by the end of the frame.
Definition: context.cc:19
impeller::Font::GetTypeface
const std::shared_ptr< Typeface > & GetTypeface() const
The typeface whose intrinsic properties this font modifies.
Definition: font.cc:27
impeller::Font::GetMetrics
const Metrics & GetMetrics() const
Definition: font.cc:45
impeller::GlyphAtlas
A texture containing the bitmap representation of glyphs in different fonts along with the ability to...
Definition: glyph_atlas.h:70
impeller::GlyphAtlas::FindFontGlyphBounds
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
impeller::GlyphAtlas::Type
Type
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.h:74
impeller::GlyphAtlas::Type::kAlphaBitmap
@ kAlphaBitmap
impeller::GlyphAtlas::Type::kColorBitmap
@ kColorBitmap
impeller::GlyphAtlas::GetType
Type GetType() const
Describes how the glyphs are represented in the texture.
Definition: glyph_atlas.cc:58
impeller::GlyphAtlas::GetTexture
const std::shared_ptr< Texture > & GetTexture() const
Get the texture for the glyph atlas.
Definition: glyph_atlas.cc:62
impeller::HostBuffer
Definition: host_buffer.h:28
impeller::HostBuffer::Emplace
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
impeller::RectanglePacker::Factory
static std::shared_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
Definition: rectangle_packer.cc:175
impeller::TextFrame::RoundScaledFontSize
static Rational RoundScaledFontSize(Scalar scale)
Definition: text_frame.cc:55
impeller::TextFrame::ComputeSubpixelPosition
static SubpixelPosition ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, const Matrix &transform)
Definition: text_frame.cc:94
impeller::TypefaceSkia::GetSkiaTypeface
const sk_sp< SkTypeface > & GetSkiaTypeface() const
Definition: typeface_skia.cc:31
impeller::TypographerContext::IsValid
virtual bool IsValid() const
Definition: typographer_context.cc:17
impeller::TypographerContextSkia::TypographerContextSkia
TypographerContextSkia()
impeller::TypographerContextSkia::~TypographerContextSkia
~TypographerContextSkia() override
impeller::TypographerContextSkia::CreateGlyphAtlasContext
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
Definition: typographer_context_skia.cc:86
impeller::TypographerContextSkia::Make
static std::shared_ptr< TypographerContext > Make()
Definition: typographer_context_skia.cc:77
impeller::TypographerContextSkia::CreateGlyphAtlas
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
Definition: typographer_context_skia.cc:498
command_buffer.h
formats.h
font_glyph_pair.h
glyph.h
glyph_atlas.h
host_buffer.h
impeller
Definition: allocation.cc:12
impeller::BytesPerPixelForPixelFormat
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition: formats.h:466
impeller::Join
Join
Definition: path.h:26
impeller::Join::kRound
@ kRound
impeller::Join::kMiter
@ kMiter
impeller::Join::kBevel
@ kBevel
impeller::ComputeGlyphSize
static Rect ComputeGlyphSize(const SkFont &font, const SubpixelGlyph &glyph, Scalar scale)
Definition: typographer_context_skia.cc:393
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::PairsFitInAtlasOfSize
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)
Definition: typographer_context_skia.cc:137
impeller::DrawGlyph
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)
Definition: typographer_context_skia.cc:212
impeller::SubpixelPositionToPoint
static Point SubpixelPositionToPoint(SubpixelPosition pos)
Definition: typographer_context_skia.cc:208
impeller::GetImageInfo
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size)
Definition: typographer_context_skia.cc:90
impeller::BulkUpdateAtlasBitmap
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.
Definition: typographer_context_skia.cc:261
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::Cap
Cap
Definition: path.h:20
impeller::Cap::kRound
@ kRound
impeller::Cap::kButt
@ kButt
impeller::Cap::kSquare
@ kSquare
impeller::AppendToExistingAtlas
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)
Definition: typographer_context_skia.cc:103
impeller::ComputeNextAtlasSize
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)
Definition: typographer_context_skia.cc:167
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::Size
TSize< Scalar > Size
Definition: size.h:159
impeller::SubpixelPosition
SubpixelPosition
Definition: font_glyph_pair.h:61
impeller::kSubpixel00
@ kSubpixel00
Definition: font_glyph_pair.h:63
impeller::ISize
ISize64 ISize
Definition: size.h:162
impeller::UpdateAtlasBitmap
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)
Definition: typographer_context_skia.cc:320
impeller::DefaultUniformAlignment
constexpr size_t DefaultUniformAlignment()
Definition: platform.h:14
impeller::kPadding
constexpr auto kPadding
Definition: typographer_context_skia.cc:49
impeller::TextureUsage::kShaderRead
@ kShaderRead
platform.h
rect.h
rectangle_packer.h
render_pass.h
render_target.h
size.h
scale
const Scalar scale
Definition: stroke_path_geometry.cc:319
impeller::BufferView
Definition: buffer_view.h:19
impeller::FontGlyphPair
A font along with a glyph in that font rendered at a particular scale and subpixel position.
Definition: font_glyph_pair.h:138
impeller::FontGlyphPair::glyph
SubpixelGlyph glyph
Definition: font_glyph_pair.h:142
impeller::FontGlyphPair::scaled_font
ScaledFont scaled_font
Definition: font_glyph_pair.h:141
impeller::Glyph::index
uint16_t index
Definition: glyph.h:22
impeller::IPoint16
Definition: rectangle_packer.h:16
impeller::IPoint16::y
int16_t y() const
Definition: rectangle_packer.h:18
impeller::IPoint16::x
int16_t x() const
Definition: rectangle_packer.h:17
impeller::ScaledFont
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
Definition: font_glyph_pair.h:41
impeller::ScaledFont::scale
Rational scale
Definition: font_glyph_pair.h:43
impeller::ScaledFont::font
Font font
Definition: font_glyph_pair.h:42
impeller::SubpixelGlyph
A glyph and its subpixel position.
Definition: font_glyph_pair.h:90
impeller::SubpixelGlyph::properties
std::optional< GlyphProperties > properties
Definition: font_glyph_pair.h:93
impeller::SubpixelGlyph::subpixel_offset
SubpixelPosition subpixel_offset
Definition: font_glyph_pair.h:92
impeller::SubpixelGlyph::glyph
Glyph glyph
Definition: font_glyph_pair.h:91
impeller::TPoint< Scalar >
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TRect< Scalar >
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:357
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:355
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize< Scalar >
impeller::TSize::Area
constexpr Type Area() const
Definition: size.h:120
impeller::TSize::height
Type height
Definition: size.h:29
impeller::TSize::width
Type width
Definition: size.h:28
impeller::TSize::Ceil
constexpr TSize Ceil() const
Definition: size.h:114
impeller::TSize::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:123
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:44
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
texture_descriptor.h
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:67
typeface_skia.h
typographer_context.h
typographer_context_skia.h
validation.h