2222
2323namespace impeller {
2424
25+ // TODO(bdero): We might be able to remove this per-glyph padding if we fix
26+ // the underlying causes of the overlap.
27+ // https://github.com/flutter/flutter/issues/114563
28+ constexpr auto kPadding = 2 ;
29+
2530TextRenderContextSkia::TextRenderContextSkia (std::shared_ptr<Context> context)
2631 : TextRenderContext(std::move(context)) {}
2732
@@ -58,33 +63,27 @@ static FontGlyphPair::Vector CollectUniqueFontGlyphPairs(
5863 return vector;
5964}
6065
61- static size_t PairsFitInAtlasOfSize (const FontGlyphPair::Vector& pairs,
62- const ISize& atlas_size,
63- std::vector<Rect>& glyph_positions) {
66+ static size_t PairsFitInAtlasOfSize (
67+ const FontGlyphPair::Vector& pairs,
68+ const ISize& atlas_size,
69+ std::vector<Rect>& glyph_positions,
70+ const std::shared_ptr<GrRectanizer>& rect_packer) {
6471 if (atlas_size.IsEmpty ()) {
6572 return false ;
6673 }
6774
68- auto rect_packer = std::unique_ptr<GrRectanizer>(
69- GrRectanizer::Factory (atlas_size.width , atlas_size.height ));
70-
7175 glyph_positions.clear ();
7276 glyph_positions.reserve (pairs.size ());
7377
74- // TODO(bdero): We might be able to remove this per-glyph padding if we fix
75- // the underlying causes of the overlap.
76- // https://github.com/flutter/flutter/issues/114563
77- constexpr auto padding = 2 ;
78-
7978 for (size_t i = 0 ; i < pairs.size (); i++) {
8079 const auto & pair = pairs[i];
8180
8281 const auto glyph_size =
8382 ISize::Ceil ((pair.glyph .bounds * pair.font .GetMetrics ().scale ).size );
8483 SkIPoint16 location_in_atlas;
85- if (!rect_packer->addRect (glyph_size.width + padding , //
86- glyph_size.height + padding , //
87- &location_in_atlas //
84+ if (!rect_packer->addRect (glyph_size.width + kPadding , //
85+ glyph_size.height + kPadding , //
86+ &location_in_atlas //
8887 )) {
8988 return pairs.size () - i;
9089 }
@@ -98,9 +97,48 @@ static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
9897 return 0 ;
9998}
10099
100+ static bool CanAppendToExistingAtlas (
101+ const std::shared_ptr<GlyphAtlas>& atlas,
102+ const FontGlyphPair::Vector& extra_pairs,
103+ std::vector<Rect>& glyph_positions,
104+ ISize atlas_size,
105+ const std::shared_ptr<GrRectanizer>& rect_packer) {
106+ TRACE_EVENT0 (" impeller" , __FUNCTION__);
107+ if (!rect_packer || atlas_size.IsEmpty ()) {
108+ return false ;
109+ }
110+
111+ // We assume that all existing glyphs will fit. After all, they fit before.
112+ // The glyph_positions only contains the values for the additional glyphs
113+ // from extra_pairs.
114+ FML_DCHECK (glyph_positions.size () == 0 );
115+ glyph_positions.reserve (extra_pairs.size ());
116+ for (size_t i = 0 ; i < extra_pairs.size (); i++) {
117+ const auto & pair = extra_pairs[i];
118+
119+ const auto glyph_size =
120+ ISize::Ceil ((pair.glyph .bounds * pair.font .GetMetrics ().scale ).size );
121+ SkIPoint16 location_in_atlas;
122+ if (!rect_packer->addRect (glyph_size.width + kPadding , //
123+ glyph_size.height + kPadding , //
124+ &location_in_atlas //
125+ )) {
126+ return false ;
127+ }
128+ glyph_positions.emplace_back (Rect::MakeXYWH (location_in_atlas.x (), //
129+ location_in_atlas.y (), //
130+ glyph_size.width , //
131+ glyph_size.height //
132+ ));
133+ }
134+
135+ return true ;
136+ }
137+
101138static ISize OptimumAtlasSizeForFontGlyphPairs (
102139 const FontGlyphPair::Vector& pairs,
103- std::vector<Rect>& glyph_positions) {
140+ std::vector<Rect>& glyph_positions,
141+ const std::shared_ptr<GlyphAtlasContext>& atlas_context) {
104142 static constexpr auto kMinAtlasSize = 8u ;
105143 static constexpr auto kMaxAtlasSize = 4096u ;
106144
@@ -109,9 +147,13 @@ static ISize OptimumAtlasSizeForFontGlyphPairs(
109147 ISize current_size (kMinAtlasSize , kMinAtlasSize );
110148 size_t total_pairs = pairs.size () + 1 ;
111149 do {
112- auto remaining_pairs =
113- PairsFitInAtlasOfSize (pairs, current_size, glyph_positions);
150+ auto rect_packer = std::shared_ptr<GrRectanizer>(
151+ GrRectanizer::Factory (current_size.width , current_size.height ));
152+
153+ auto remaining_pairs = PairsFitInAtlasOfSize (pairs, current_size,
154+ glyph_positions, rect_packer);
114155 if (remaining_pairs == 0 ) {
156+ atlas_context->UpdateRectPacker (rect_packer);
115157 return current_size;
116158 } else if (remaining_pairs < std::ceil (total_pairs / 2 )) {
117159 current_size = ISize::MakeWH (
@@ -243,6 +285,59 @@ static void ConvertBitmapToSignedDistanceField(uint8_t* pixels,
243285#undef nearestpt
244286}
245287
288+ static void DrawGlyph (SkCanvas* canvas,
289+ const FontGlyphPair& font_glyph,
290+ const Rect& location) {
291+ const auto & metrics = font_glyph.font .GetMetrics ();
292+ const auto position = SkPoint::Make (location.origin .x / metrics.scale ,
293+ location.origin .y / metrics.scale );
294+ SkGlyphID glyph_id = font_glyph.glyph .index ;
295+
296+ SkFont sk_font (
297+ TypefaceSkia::Cast (*font_glyph.font .GetTypeface ()).GetSkiaTypeface (),
298+ metrics.point_size );
299+ auto glyph_color = SK_ColorWHITE;
300+
301+ SkPaint glyph_paint;
302+ glyph_paint.setColor (glyph_color);
303+ canvas->resetMatrix ();
304+ canvas->scale (metrics.scale , metrics.scale );
305+ canvas->drawGlyphs (
306+ 1u , // count
307+ &glyph_id, // glyphs
308+ &position, // positions
309+ SkPoint::Make (-font_glyph.glyph .bounds .GetLeft (),
310+ -font_glyph.glyph .bounds .GetTop ()), // origin
311+ sk_font, // font
312+ glyph_paint // paint
313+ );
314+ }
315+
316+ static bool UpdateAtlasBitmap (const GlyphAtlas& atlas,
317+ const std::shared_ptr<SkBitmap>& bitmap,
318+ const FontGlyphPair::Vector& new_pairs) {
319+ TRACE_EVENT0 (" impeller" , __FUNCTION__);
320+ FML_DCHECK (bitmap != nullptr );
321+
322+ auto surface = SkSurface::MakeRasterDirect (bitmap->pixmap ());
323+ if (!surface) {
324+ return false ;
325+ }
326+ auto canvas = surface->getCanvas ();
327+ if (!canvas) {
328+ return false ;
329+ }
330+
331+ for (const auto & pair : new_pairs) {
332+ auto pos = atlas.FindFontGlyphPosition (pair);
333+ if (!pos.has_value ()) {
334+ continue ;
335+ }
336+ DrawGlyph (canvas, pair, pos.value ());
337+ }
338+ return true ;
339+ }
340+
246341static std::shared_ptr<SkBitmap> CreateAtlasBitmap (const GlyphAtlas& atlas,
247342 const ISize& atlas_size) {
248343 TRACE_EVENT0 (" impeller" , __FUNCTION__);
@@ -263,6 +358,7 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
263358 if (!bitmap->tryAllocPixels (image_info)) {
264359 return nullptr ;
265360 }
361+
266362 auto surface = SkSurface::MakeRasterDirect (bitmap->pixmap ());
267363 if (!surface) {
268364 return nullptr ;
@@ -272,37 +368,31 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
272368 return nullptr ;
273369 }
274370
275- atlas.IterateGlyphs ([canvas](const FontGlyphPair& font_glyph,
276- const Rect& location) -> bool {
277- const auto & metrics = font_glyph.font .GetMetrics ();
278- const auto position = SkPoint::Make (location.origin .x / metrics.scale ,
279- location.origin .y / metrics.scale );
280- SkGlyphID glyph_id = font_glyph.glyph .index ;
281-
282- SkFont sk_font (
283- TypefaceSkia::Cast (*font_glyph.font .GetTypeface ()).GetSkiaTypeface (),
284- metrics.point_size );
285- auto glyph_color = SK_ColorWHITE;
286-
287- SkPaint glyph_paint;
288- glyph_paint.setColor (glyph_color);
289- canvas->resetMatrix ();
290- canvas->scale (metrics.scale , metrics.scale );
291- canvas->drawGlyphs (
292- 1u , // count
293- &glyph_id, // glyphs
294- &position, // positions
295- SkPoint::Make (-font_glyph.glyph .bounds .GetLeft (),
296- -font_glyph.glyph .bounds .GetTop ()), // origin
297- sk_font, // font
298- glyph_paint // paint
299- );
300- return true ;
301- });
371+ atlas.IterateGlyphs (
372+ [canvas](const FontGlyphPair& font_glyph, const Rect& location) -> bool {
373+ DrawGlyph (canvas, font_glyph, location);
374+ return true ;
375+ });
302376
303377 return bitmap;
304378}
305379
380+ static bool UpdateGlyphTextureAtlas (std::shared_ptr<SkBitmap> bitmap,
381+ const std::shared_ptr<Texture>& texture) {
382+ TRACE_EVENT0 (" impeller" , __FUNCTION__);
383+
384+ FML_DCHECK (bitmap != nullptr );
385+ auto texture_descriptor = texture->GetTextureDescriptor ();
386+
387+ auto mapping = std::make_shared<fml::NonOwnedMapping>(
388+ reinterpret_cast <const uint8_t *>(bitmap->getAddr (0 , 0 )), // data
389+ texture_descriptor.GetByteSizeOfBaseMipLevel (), // size
390+ [bitmap](auto , auto ) mutable { bitmap.reset (); } // proc
391+ );
392+
393+ return texture->SetContents (mapping);
394+ }
395+
306396static std::shared_ptr<Texture> UploadGlyphTextureAtlas (
307397 const std::shared_ptr<Allocator>& allocator,
308398 std::shared_ptr<SkBitmap> bitmap,
@@ -367,26 +457,61 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
367457 // Step 2: Determine if the atlas type and font glyph pairs are compatible
368458 // with the current atlas and reuse if possible.
369459 // ---------------------------------------------------------------------------
370- if ( last_atlas->GetType () == type &&
371- last_atlas->HasSamePairs (font_glyph_pairs) ) {
460+ auto new_glyphs = last_atlas->HasSamePairs (font_glyph_pairs);
461+ if ( last_atlas->GetType () == type && new_glyphs. size () == 0 ) {
372462 return last_atlas;
373463 }
374464
375- auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
376- atlas_context->UpdateGlyphAtlas (glyph_atlas);
377-
378465 // ---------------------------------------------------------------------------
379- // Step 3: Get the optimum size of the texture atlas.
466+ // Step 3: Determine if the additional missing glyphs can be appended to the
467+ // existing bitmap without recreating the atlas.
380468 // ---------------------------------------------------------------------------
381469 std::vector<Rect> glyph_positions;
382- const auto atlas_size =
383- OptimumAtlasSizeForFontGlyphPairs (font_glyph_pairs, glyph_positions);
470+ if (CanAppendToExistingAtlas (last_atlas, new_glyphs, glyph_positions,
471+ atlas_context->GetAtlasSize (),
472+ atlas_context->GetRectPacker ())) {
473+ // The old bitmap will be reused and only the additional glyphs will be
474+ // added.
475+
476+ // ---------------------------------------------------------------------------
477+ // Step 4: Record the positions in the glyph atlas of the newly added
478+ // glyphs.
479+ // ---------------------------------------------------------------------------
480+ for (size_t i = 0 , count = glyph_positions.size (); i < count; i++) {
481+ last_atlas->AddTypefaceGlyphPosition (new_glyphs[i], glyph_positions[i]);
482+ }
483+
484+ // ---------------------------------------------------------------------------
485+ // Step 5: Draw new font-glyph pairs into the existing bitmap.
486+ // ---------------------------------------------------------------------------
487+ auto bitmap = atlas_context->GetBitmap ();
488+ if (!UpdateAtlasBitmap (*last_atlas, bitmap, new_glyphs)) {
489+ return nullptr ;
490+ }
491+
492+ // ---------------------------------------------------------------------------
493+ // Step 6: Update the existing texture with the updated bitmap.
494+ // ---------------------------------------------------------------------------
495+ if (!UpdateGlyphTextureAtlas (bitmap, last_atlas->GetTexture ())) {
496+ return nullptr ;
497+ }
498+ return last_atlas;
499+ }
500+ // A new glyph atlas must be created.
501+
502+ // ---------------------------------------------------------------------------
503+ // Step 4: Get the optimum size of the texture atlas.
504+ // ---------------------------------------------------------------------------
505+ auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
506+ auto atlas_size = OptimumAtlasSizeForFontGlyphPairs (
507+ font_glyph_pairs, glyph_positions, atlas_context);
508+
509+ atlas_context->UpdateGlyphAtlas (glyph_atlas, atlas_size);
384510 if (atlas_size.IsEmpty ()) {
385511 return nullptr ;
386512 }
387-
388513 // ---------------------------------------------------------------------------
389- // Step 4 : Find location of font-glyph pairs in the atlas. We have this from
514+ // Step 5 : Find location of font-glyph pairs in the atlas. We have this from
390515 // the last step. So no need to do create another rect packer. But just do a
391516 // sanity check of counts. This could also be just an assertion as only a
392517 // construction issue would cause such a failure.
@@ -396,23 +521,24 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
396521 }
397522
398523 // ---------------------------------------------------------------------------
399- // Step 5 : Record the positions in the glyph atlas.
524+ // Step 6 : Record the positions in the glyph atlas.
400525 // ---------------------------------------------------------------------------
401526 for (size_t i = 0 , count = glyph_positions.size (); i < count; i++) {
402527 glyph_atlas->AddTypefaceGlyphPosition (font_glyph_pairs[i],
403528 glyph_positions[i]);
404529 }
405530
406531 // ---------------------------------------------------------------------------
407- // Step 6 : Draw font-glyph pairs in the correct spot in the atlas.
532+ // Step 7 : Draw font-glyph pairs in the correct spot in the atlas.
408533 // ---------------------------------------------------------------------------
409534 auto bitmap = CreateAtlasBitmap (*glyph_atlas, atlas_size);
410535 if (!bitmap) {
411536 return nullptr ;
412537 }
538+ atlas_context->UpdateBitmap (bitmap);
413539
414540 // ---------------------------------------------------------------------------
415- // Step 7 : Upload the atlas as a texture.
541+ // Step 8 : Upload the atlas as a texture.
416542 // ---------------------------------------------------------------------------
417543 PixelFormat format;
418544 switch (type) {
@@ -434,7 +560,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
434560 }
435561
436562 // ---------------------------------------------------------------------------
437- // Step 8 : Record the texture in the glyph atlas.
563+ // Step 9 : Record the texture in the glyph atlas.
438564 // ---------------------------------------------------------------------------
439565 glyph_atlas->SetTexture (std::move (texture));
440566
0 commit comments