Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
typographer_context_skia.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <numeric>
8#include <utility>
9
10#include "flutter/fml/logging.h"
11#include "flutter/fml/trace_event.h"
19#include "include/core/SkSize.h"
24
25namespace impeller {
26
27// TODO(bdero): We might be able to remove this per-glyph padding if we fix
28// the underlying causes of the overlap.
29// https://github.com/flutter/flutter/issues/114563
30constexpr auto kPadding = 2;
31
32std::shared_ptr<TypographerContext> TypographerContextSkia::Make() {
33 return std::make_shared<TypographerContextSkia>();
34}
35
37
39
40std::shared_ptr<GlyphAtlasContext>
42 return std::make_shared<GlyphAtlasContextSkia>();
43}
44
46 const std::vector<FontGlyphPair>& pairs,
47 const ISize& atlas_size,
48 std::vector<Rect>& glyph_positions,
49 const std::shared_ptr<RectanglePacker>& rect_packer) {
50 if (atlas_size.IsEmpty()) {
51 return false;
52 }
53
54 glyph_positions.clear();
55 glyph_positions.reserve(pairs.size());
56
57 size_t i = 0;
58 for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) {
59 const auto& pair = *it;
60
61 const auto glyph_size =
62 ISize::Ceil(pair.glyph.bounds.GetSize() * pair.scaled_font.scale);
63 IPoint16 location_in_atlas;
64 if (!rect_packer->addRect(glyph_size.width + kPadding, //
65 glyph_size.height + kPadding, //
66 &location_in_atlas //
67 )) {
68 return pairs.size() - i;
69 }
70 glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), //
71 location_in_atlas.y(), //
72 glyph_size.width, //
73 glyph_size.height //
74 ));
75 }
76
77 return 0;
78}
79
81 const std::shared_ptr<GlyphAtlas>& atlas,
82 const std::vector<FontGlyphPair>& extra_pairs,
83 std::vector<Rect>& glyph_positions,
84 ISize atlas_size,
85 const std::shared_ptr<RectanglePacker>& rect_packer) {
86 TRACE_EVENT0("impeller", __FUNCTION__);
87 if (!rect_packer || atlas_size.IsEmpty()) {
88 return false;
89 }
90
91 // We assume that all existing glyphs will fit. After all, they fit before.
92 // The glyph_positions only contains the values for the additional glyphs
93 // from extra_pairs.
94 FML_DCHECK(glyph_positions.size() == 0);
95 glyph_positions.reserve(extra_pairs.size());
96 for (size_t i = 0; i < extra_pairs.size(); i++) {
97 const FontGlyphPair& pair = extra_pairs[i];
98
99 const auto glyph_size =
101 IPoint16 location_in_atlas;
102 if (!rect_packer->addRect(glyph_size.width + kPadding, //
103 glyph_size.height + kPadding, //
104 &location_in_atlas //
105 )) {
106 return false;
107 }
108 glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), //
109 location_in_atlas.y(), //
110 glyph_size.width, //
111 glyph_size.height //
112 ));
113 }
114
115 return true;
116}
117
119 const std::vector<FontGlyphPair>& pairs,
120 std::vector<Rect>& glyph_positions,
121 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
123 const ISize& max_texture_size) {
124 static constexpr auto kMinAtlasSize = 8u;
125 static constexpr auto kMinAlphaBitmapSize = 1024u;
126
127 TRACE_EVENT0("impeller", __FUNCTION__);
128
130 ? ISize(kMinAlphaBitmapSize, kMinAlphaBitmapSize)
131 : ISize(kMinAtlasSize, kMinAtlasSize);
132 size_t total_pairs = pairs.size() + 1;
133 do {
134 auto rect_packer = std::shared_ptr<RectanglePacker>(
135 RectanglePacker::Factory(current_size.width, current_size.height));
136
137 auto remaining_pairs = PairsFitInAtlasOfSize(pairs, current_size,
138 glyph_positions, rect_packer);
139 if (remaining_pairs == 0) {
140 atlas_context->UpdateRectPacker(rect_packer);
141 return current_size;
142 } else if (remaining_pairs < std::ceil(total_pairs / 2)) {
143 current_size = ISize::MakeWH(
144 std::max(current_size.width, current_size.height),
146 std::min(current_size.width, current_size.height) + 1));
147 } else {
148 current_size = ISize::MakeWH(
149 Allocation::NextPowerOfTwoSize(current_size.width + 1),
150 Allocation::NextPowerOfTwoSize(current_size.height + 1));
151 }
152 } while (current_size.width <= max_texture_size.width &&
153 current_size.height <= max_texture_size.height);
154 return ISize{0, 0};
155}
156
157static void DrawGlyph(SkCanvas* canvas,
158 const ScaledFont& scaled_font,
159 const Glyph& glyph,
160 const Rect& location,
161 bool has_color) {
162 const auto& metrics = scaled_font.font.GetMetrics();
163 const auto position = SkPoint::Make(location.GetX() / scaled_font.scale,
164 location.GetY() / scaled_font.scale);
165 SkGlyphID glyph_id = glyph.index;
166
167 SkFont sk_font(
169 metrics.point_size, metrics.scaleX, metrics.skewX);
172 sk_font.setEmbolden(metrics.embolden);
173
174 auto glyph_color = has_color ? SK_ColorWHITE : SK_ColorBLACK;
175
176 SkPaint glyph_paint;
177 glyph_paint.setColor(glyph_color);
178 canvas->resetMatrix();
179 canvas->scale(scaled_font.scale, scaled_font.scale);
180 canvas->drawGlyphs(1u, // count
181 &glyph_id, // glyphs
182 &position, // positions
184 -glyph.bounds.GetTop()), // origin
185 sk_font, // font
186 glyph_paint // paint
187 );
188}
189
190static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
191 const std::shared_ptr<SkBitmap>& bitmap,
192 const std::vector<FontGlyphPair>& new_pairs) {
193 TRACE_EVENT0("impeller", __FUNCTION__);
194 FML_DCHECK(bitmap != nullptr);
195
196 auto surface = SkSurfaces::WrapPixels(bitmap->pixmap());
197 if (!surface) {
198 return false;
199 }
200 auto canvas = surface->getCanvas();
201 if (!canvas) {
202 return false;
203 }
204
205 bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
206
207 for (const FontGlyphPair& pair : new_pairs) {
208 auto pos = atlas.FindFontGlyphBounds(pair);
209 if (!pos.has_value()) {
210 continue;
211 }
212 DrawGlyph(canvas, pair.scaled_font, pair.glyph, pos.value(), has_color);
213 }
214 return true;
215}
216
217static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
218 const ISize& atlas_size) {
219 TRACE_EVENT0("impeller", __FUNCTION__);
220 auto bitmap = std::make_shared<SkBitmap>();
221 SkImageInfo image_info;
222
223 switch (atlas.GetType()) {
225 image_info =
226 SkImageInfo::MakeA8(SkISize{static_cast<int32_t>(atlas_size.width),
227 static_cast<int32_t>(atlas_size.height)});
228 break;
230 image_info =
231 SkImageInfo::MakeN32Premul(atlas_size.width, atlas_size.height);
232 break;
233 }
234
235 if (!bitmap->tryAllocPixels(image_info)) {
236 return nullptr;
237 }
238
239 auto surface = SkSurfaces::WrapPixels(bitmap->pixmap());
240 if (!surface) {
241 return nullptr;
242 }
243 auto canvas = surface->getCanvas();
244 if (!canvas) {
245 return nullptr;
246 }
247
248 bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
249
250 atlas.IterateGlyphs([canvas, has_color](const ScaledFont& scaled_font,
251 const Glyph& glyph,
252 const Rect& location) -> bool {
253 DrawGlyph(canvas, scaled_font, glyph, location, has_color);
254 return true;
255 });
256
257 return bitmap;
258}
259
260static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
261 const std::shared_ptr<Texture>& texture) {
262 TRACE_EVENT0("impeller", __FUNCTION__);
263
264 FML_DCHECK(bitmap != nullptr);
265 auto texture_descriptor = texture->GetTextureDescriptor();
266
267 auto mapping = std::make_shared<fml::NonOwnedMapping>(
268 reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
269 texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
270 [bitmap](auto, auto) mutable { bitmap.reset(); } // proc
271 );
272
273 return texture->SetContents(mapping);
274}
275
276static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
277 const std::shared_ptr<Allocator>& allocator,
278 std::shared_ptr<SkBitmap> bitmap,
279 const ISize& atlas_size,
281 TRACE_EVENT0("impeller", __FUNCTION__);
282 if (!allocator) {
283 return nullptr;
284 }
285
286 FML_DCHECK(bitmap != nullptr);
287 const auto& pixmap = bitmap->pixmap();
288
289 TextureDescriptor texture_descriptor;
290 texture_descriptor.storage_mode = StorageMode::kHostVisible;
291 texture_descriptor.format = format;
292 texture_descriptor.size = atlas_size;
293
294 if (pixmap.rowBytes() * pixmap.height() !=
295 texture_descriptor.GetByteSizeOfBaseMipLevel()) {
296 return nullptr;
297 }
298
299 auto texture = allocator->CreateTexture(texture_descriptor);
300 if (!texture || !texture->IsValid()) {
301 return nullptr;
302 }
303 texture->SetLabel("GlyphAtlas");
304
305 auto mapping = std::make_shared<fml::NonOwnedMapping>(
306 reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
307 texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
308 [bitmap](auto, auto) mutable { bitmap.reset(); } // proc
309 );
310
311 if (!texture->SetContents(mapping)) {
312 return nullptr;
313 }
314 return texture;
315}
316
318 Context& context,
320 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
321 const FontGlyphMap& font_glyph_map) const {
322 TRACE_EVENT0("impeller", __FUNCTION__);
323 if (!IsValid()) {
324 return nullptr;
325 }
326 auto& atlas_context_skia = GlyphAtlasContextSkia::Cast(*atlas_context);
327 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
328
329 if (font_glyph_map.empty()) {
330 return last_atlas;
331 }
332
333 // ---------------------------------------------------------------------------
334 // Step 1: Determine if the atlas type and font glyph pairs are compatible
335 // with the current atlas and reuse if possible.
336 // ---------------------------------------------------------------------------
337 std::vector<FontGlyphPair> new_glyphs;
338 for (const auto& font_value : font_glyph_map) {
339 const ScaledFont& scaled_font = font_value.first;
340 const FontGlyphAtlas* font_glyph_atlas =
341 last_atlas->GetFontGlyphAtlas(scaled_font.font, scaled_font.scale);
342 if (font_glyph_atlas) {
343 for (const Glyph& glyph : font_value.second) {
344 if (!font_glyph_atlas->FindGlyphBounds(glyph)) {
345 new_glyphs.emplace_back(scaled_font, glyph);
346 }
347 }
348 } else {
349 for (const Glyph& glyph : font_value.second) {
350 new_glyphs.emplace_back(scaled_font, glyph);
351 }
352 }
353 }
354 if (last_atlas->GetType() == type && new_glyphs.size() == 0) {
355 return last_atlas;
356 }
357
358 // ---------------------------------------------------------------------------
359 // Step 2: Determine if the additional missing glyphs can be appended to the
360 // existing bitmap without recreating the atlas. This requires that
361 // the type is identical.
362 // ---------------------------------------------------------------------------
363 std::vector<Rect> glyph_positions;
364 if (last_atlas->GetType() == type &&
365 CanAppendToExistingAtlas(last_atlas, new_glyphs, glyph_positions,
366 atlas_context->GetAtlasSize(),
367 atlas_context->GetRectPacker())) {
368 // The old bitmap will be reused and only the additional glyphs will be
369 // added.
370
371 // ---------------------------------------------------------------------------
372 // Step 3a: Record the positions in the glyph atlas of the newly added
373 // glyphs.
374 // ---------------------------------------------------------------------------
375 for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
376 last_atlas->AddTypefaceGlyphPosition(new_glyphs[i], glyph_positions[i]);
377 }
378
379 // ---------------------------------------------------------------------------
380 // Step 4a: Draw new font-glyph pairs into the existing bitmap.
381 // ---------------------------------------------------------------------------
382 auto bitmap = atlas_context_skia.GetBitmap();
383 if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
384 return nullptr;
385 }
386
387 // ---------------------------------------------------------------------------
388 // Step 5a: Update the existing texture with the updated bitmap.
389 // ---------------------------------------------------------------------------
390 if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
391 return nullptr;
392 }
393 return last_atlas;
394 }
395 // A new glyph atlas must be created.
396
397 // ---------------------------------------------------------------------------
398 // Step 3b: Get the optimum size of the texture atlas.
399 // ---------------------------------------------------------------------------
400 std::vector<FontGlyphPair> font_glyph_pairs;
401 font_glyph_pairs.reserve(std::accumulate(
402 font_glyph_map.begin(), font_glyph_map.end(), 0,
403 [](const int a, const auto& b) { return a + b.second.size(); }));
404 for (const auto& font_value : font_glyph_map) {
405 const ScaledFont& scaled_font = font_value.first;
406 for (const Glyph& glyph : font_value.second) {
407 font_glyph_pairs.push_back({scaled_font, glyph});
408 }
409 }
410 auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
411 auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
412 font_glyph_pairs, //
413 glyph_positions, //
414 atlas_context, //
415 type, //
416 context.GetResourceAllocator()->GetMaxTextureSizeSupported() //
417 );
418
419 atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size);
420 if (atlas_size.IsEmpty()) {
421 return nullptr;
422 }
423 // ---------------------------------------------------------------------------
424 // Step 4b: Find location of font-glyph pairs in the atlas. We have this from
425 // the last step. So no need to do create another rect packer. But just do a
426 // sanity check of counts. This could also be just an assertion as only a
427 // construction issue would cause such a failure.
428 // ---------------------------------------------------------------------------
429 if (glyph_positions.size() != font_glyph_pairs.size()) {
430 return nullptr;
431 }
432
433 // ---------------------------------------------------------------------------
434 // Step 5b: Record the positions in the glyph atlas.
435 // ---------------------------------------------------------------------------
436 {
437 size_t i = 0;
438 for (auto it = font_glyph_pairs.begin(); it != font_glyph_pairs.end();
439 ++i, ++it) {
440 glyph_atlas->AddTypefaceGlyphPosition(*it, glyph_positions[i]);
441 }
442 }
443
444 // ---------------------------------------------------------------------------
445 // Step 6b: Draw font-glyph pairs in the correct spot in the atlas.
446 // ---------------------------------------------------------------------------
447 auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
448 if (!bitmap) {
449 return nullptr;
450 }
451 atlas_context_skia.UpdateBitmap(bitmap);
452
453 // ---------------------------------------------------------------------------
454 // Step 7b: Upload the atlas as a texture.
455 // ---------------------------------------------------------------------------
457 switch (type) {
459 format = context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
460 break;
463 break;
464 }
466 atlas_size, format);
467 if (!texture) {
468 return nullptr;
469 }
470
471 // ---------------------------------------------------------------------------
472 // Step 8b: Record the texture in the glyph atlas.
473 // ---------------------------------------------------------------------------
474 glyph_atlas->SetTexture(std::move(texture));
475
476 return glyph_atlas;
477}
478
479} // namespace impeller
int count
SkPoint pos
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
@ kSlight
minimal modification to improve constrast
uint16_t SkGlyphID
Definition SkTypes.h:179
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], const uint32_t clusters[], int textByteCount, const char utf8text[], SkPoint origin, const SkFont &font, const SkPaint &paint)
void resetMatrix()
void scale(SkScalar sx, SkScalar sy)
void setEdging(Edging edging)
Definition SkFont.cpp:121
void setHinting(SkFontHinting hintingLevel)
Definition SkFont.cpp:125
@ kAntiAlias
may have transparent pixels on glyph edges
void setEmbolden(bool embolden)
Definition SkFont.cpp:115
void setColor(SkColor color)
Definition SkPaint.cpp:119
static uint32_t NextPowerOfTwoSize(uint32_t x)
Definition allocation.cc:41
static TypefaceSkia & Cast(Typeface &base)
To do anything rendering related with Impeller, you need a context.
Definition context.h:46
virtual const std::shared_ptr< const Capabilities > & GetCapabilities() const =0
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
virtual std::shared_ptr< Allocator > GetResourceAllocator() const =0
Returns the allocator used to create textures and buffers on the device.
An object that can look up glyph locations within the GlyphAtlas for a particular typeface.
std::optional< Rect > FindGlyphBounds(const Glyph &glyph) const
Find the location of a glyph in the atlas.
const std::shared_ptr< Typeface > & GetTypeface() const
The typeface whose intrinsic properties this font modifies.
Definition font.cc:23
const Metrics & GetMetrics() const
Definition font.cc:37
A texture containing the bitmap representation of glyphs in different fonts along with the ability to...
Definition glyph_atlas.h:28
Type
Describes how the glyphs are represented in the texture.
Definition glyph_atlas.h:32
static std::unique_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
const sk_sp< SkTypeface > & GetSkiaTypeface() const
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext() const override
std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, GlyphAtlas::Type type, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const FontGlyphMap &font_glyph_map) const override
static std::shared_ptr< TypographerContext > Make()
VkSurfaceKHR surface
Definition main.cc:49
static bool b
struct MyStruct a[10]
uint32_t uint32_t * format
#define FML_DCHECK(condition)
Definition logging.h:103
FlTexture * texture
SK_API sk_sp< SkSurface > WrapPixels(const SkImageInfo &imageInfo, void *pixels, size_t rowBytes, const SkSurfaceProps *surfaceProps=nullptr)
std::unordered_map< ScaledFont, std::unordered_set< Glyph > > FontGlyphMap
static std::shared_ptr< Texture > UploadGlyphTextureAtlas(const std::shared_ptr< Allocator > &allocator, std::shared_ptr< SkBitmap > bitmap, const ISize &atlas_size, PixelFormat format)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:100
constexpr auto kPadding
static std::shared_ptr< SkBitmap > CreateAtlasBitmap(const GlyphAtlas &atlas, const ISize &atlas_size)
TSize< int64_t > ISize
Definition size.h:138
static void DrawGlyph(SkCanvas *canvas, const ScaledFont &scaled_font, const Glyph &glyph, const Rect &location, bool has_color)
static ISize OptimumAtlasSizeForFontGlyphPairs(const std::vector< FontGlyphPair > &pairs, std::vector< Rect > &glyph_positions, const std::shared_ptr< GlyphAtlasContext > &atlas_context, GlyphAtlas::Type type, const ISize &max_texture_size)
static bool UpdateGlyphTextureAtlas(std::shared_ptr< SkBitmap > bitmap, const std::shared_ptr< Texture > &texture)
static size_t PairsFitInAtlasOfSize(const std::vector< FontGlyphPair > &pairs, const ISize &atlas_size, std::vector< Rect > &glyph_positions, const std::shared_ptr< RectanglePacker > &rect_packer)
static bool UpdateAtlasBitmap(const GlyphAtlas &atlas, const std::shared_ptr< SkBitmap > &bitmap, const std::vector< FontGlyphPair > &new_pairs)
static bool CanAppendToExistingAtlas(const std::shared_ptr< GlyphAtlas > &atlas, const std::vector< FontGlyphPair > &extra_pairs, std::vector< Rect > &glyph_positions, ISize atlas_size, const std::shared_ptr< RectanglePacker > &rect_packer)
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo MakeA8(int width, int height)
static constexpr SkPoint Make(float x, float y)
A font along with a glyph in that font rendered at a particular scale.
const ScaledFont & scaled_font
The glyph index in the typeface.
Definition glyph.h:20
uint16_t index
Definition glyph.h:26
Rect bounds
Visibility coverage of the glyph in text run space (relative to the baseline, no scaling applied).
Definition glyph.h:37
int16_t y() const
int16_t x() const
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition rect.h:304
constexpr auto GetTop() const
Definition rect.h:320
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition rect.h:294
constexpr auto GetLeft() const
Definition rect.h:318
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition rect.h:300
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
Type height
Definition size.h:23
Type width
Definition size.h:22
constexpr TSize Ceil() const
Definition size.h:96
static constexpr TSize MakeWH(Type width, Type height)
Definition size.h:34
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition size.h:105
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
constexpr size_t GetByteSizeOfBaseMipLevel() const
#define TRACE_EVENT0(category_group, name)