22
22
23
23
namespace impeller {
24
24
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
+
25
30
TextRenderContextSkia::TextRenderContextSkia (std::shared_ptr<Context> context)
26
31
: TextRenderContext(std::move(context)) {}
27
32
@@ -58,33 +63,27 @@ static FontGlyphPair::Vector CollectUniqueFontGlyphPairs(
58
63
return vector;
59
64
}
60
65
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) {
64
71
if (atlas_size.IsEmpty ()) {
65
72
return false ;
66
73
}
67
74
68
- auto rect_packer = std::unique_ptr<GrRectanizer>(
69
- GrRectanizer::Factory (atlas_size.width , atlas_size.height ));
70
-
71
75
glyph_positions.clear ();
72
76
glyph_positions.reserve (pairs.size ());
73
77
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
-
79
78
for (size_t i = 0 ; i < pairs.size (); i++) {
80
79
const auto & pair = pairs[i];
81
80
82
81
const auto glyph_size =
83
82
ISize::Ceil ((pair.glyph .bounds * pair.font .GetMetrics ().scale ).size );
84
83
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 //
88
87
)) {
89
88
return pairs.size () - i;
90
89
}
@@ -98,9 +97,48 @@ static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
98
97
return 0 ;
99
98
}
100
99
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
+
101
138
static ISize OptimumAtlasSizeForFontGlyphPairs (
102
139
const FontGlyphPair::Vector& pairs,
103
- std::vector<Rect >& glyph_positions) {
140
+ std::vector<Rect >& glyph_positions,
141
+ const std::shared_ptr<GlyphAtlasContext>& atlas_context) {
104
142
static constexpr auto kMinAtlasSize = 8u ;
105
143
static constexpr auto kMaxAtlasSize = 4096u ;
106
144
@@ -109,9 +147,13 @@ static ISize OptimumAtlasSizeForFontGlyphPairs(
109
147
ISize current_size (kMinAtlasSize , kMinAtlasSize );
110
148
size_t total_pairs = pairs.size () + 1 ;
111
149
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);
114
155
if (remaining_pairs == 0 ) {
156
+ atlas_context->UpdateRectPacker (rect_packer);
115
157
return current_size;
116
158
} else if (remaining_pairs < std::ceil (total_pairs / 2 )) {
117
159
current_size = ISize::MakeWH (
@@ -243,6 +285,59 @@ static void ConvertBitmapToSignedDistanceField(uint8_t* pixels,
243
285
#undef nearestpt
244
286
}
245
287
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
+
246
341
static std::shared_ptr<SkBitmap> CreateAtlasBitmap (const GlyphAtlas& atlas,
247
342
const ISize& atlas_size) {
248
343
TRACE_EVENT0 (" impeller" , __FUNCTION__);
@@ -263,6 +358,7 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
263
358
if (!bitmap->tryAllocPixels (image_info)) {
264
359
return nullptr ;
265
360
}
361
+
266
362
auto surface = SkSurface::MakeRasterDirect (bitmap->pixmap ());
267
363
if (!surface) {
268
364
return nullptr ;
@@ -272,37 +368,31 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
272
368
return nullptr ;
273
369
}
274
370
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
+ });
302
376
303
377
return bitmap;
304
378
}
305
379
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
+
306
396
static std::shared_ptr<Texture> UploadGlyphTextureAtlas (
307
397
const std::shared_ptr<Allocator>& allocator,
308
398
std::shared_ptr<SkBitmap> bitmap,
@@ -367,26 +457,61 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
367
457
// Step 2: Determine if the atlas type and font glyph pairs are compatible
368
458
// with the current atlas and reuse if possible.
369
459
// ---------------------------------------------------------------------------
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 ) {
372
462
return last_atlas;
373
463
}
374
464
375
- auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
376
- atlas_context->UpdateGlyphAtlas (glyph_atlas);
377
-
378
465
// ---------------------------------------------------------------------------
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.
380
468
// ---------------------------------------------------------------------------
381
469
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);
384
510
if (atlas_size.IsEmpty ()) {
385
511
return nullptr ;
386
512
}
387
-
388
513
// ---------------------------------------------------------------------------
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
390
515
// the last step. So no need to do create another rect packer. But just do a
391
516
// sanity check of counts. This could also be just an assertion as only a
392
517
// construction issue would cause such a failure.
@@ -396,23 +521,24 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
396
521
}
397
522
398
523
// ---------------------------------------------------------------------------
399
- // Step 5 : Record the positions in the glyph atlas.
524
+ // Step 6 : Record the positions in the glyph atlas.
400
525
// ---------------------------------------------------------------------------
401
526
for (size_t i = 0 , count = glyph_positions.size (); i < count; i++) {
402
527
glyph_atlas->AddTypefaceGlyphPosition (font_glyph_pairs[i],
403
528
glyph_positions[i]);
404
529
}
405
530
406
531
// ---------------------------------------------------------------------------
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.
408
533
// ---------------------------------------------------------------------------
409
534
auto bitmap = CreateAtlasBitmap (*glyph_atlas, atlas_size);
410
535
if (!bitmap) {
411
536
return nullptr ;
412
537
}
538
+ atlas_context->UpdateBitmap (bitmap);
413
539
414
540
// ---------------------------------------------------------------------------
415
- // Step 7 : Upload the atlas as a texture.
541
+ // Step 8 : Upload the atlas as a texture.
416
542
// ---------------------------------------------------------------------------
417
543
PixelFormat format;
418
544
switch (type) {
@@ -434,7 +560,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
434
560
}
435
561
436
562
// ---------------------------------------------------------------------------
437
- // Step 8 : Record the texture in the glyph atlas.
563
+ // Step 9 : Record the texture in the glyph atlas.
438
564
// ---------------------------------------------------------------------------
439
565
glyph_atlas->SetTexture (std::move (texture));
440
566
0 commit comments