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