diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index a93967c938061..9495556bef6f5 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -166,13 +166,17 @@ bool TextContents::Render(const ContentContext& renderer, const Font& font = run.GetFont(); Scalar rounded_scale = TextFrame::RoundScaledFontSize( scale_, font.GetMetrics().point_size); + const FontGlyphAtlas* font_atlas = + atlas->GetFontGlyphAtlas(font, rounded_scale); + if (!font_atlas) { + VALIDATION_LOG << "Could not find font in the atlas."; + continue; + } for (const TextRun::GlyphPosition& glyph_position : run.GetGlyphPositions()) { - FontGlyphPair font_glyph_pair{font, glyph_position.glyph, - rounded_scale}; std::optional maybe_atlas_glyph_bounds = - atlas->FindFontGlyphBounds(font_glyph_pair); + font_atlas->FindGlyphBounds(glyph_position.glyph); if (!maybe_atlas_glyph_bounds.has_value()) { VALIDATION_LOG << "Could not find glyph position in the atlas."; continue; diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc index 85bae56a15ecd..9f8725a67c81d 100644 --- a/impeller/typographer/backends/skia/typographer_context_skia.cc +++ b/impeller/typographer/backends/skia/typographer_context_skia.cc @@ -4,6 +4,7 @@ #include "impeller/typographer/backends/skia/typographer_context_skia.h" +#include #include #include "flutter/fml/logging.h" @@ -21,9 +22,6 @@ namespace impeller { -using FontGlyphPairRefVector = - std::vector>; - // TODO(bdero): We might be able to remove this per-glyph padding if we fix // the underlying causes of the overlap. // https://github.com/flutter/flutter/issues/114563 @@ -43,7 +41,7 @@ TypographerContextSkia::CreateGlyphAtlasContext() const { } static size_t PairsFitInAtlasOfSize( - const FontGlyphPair::Set& pairs, + const std::vector& pairs, const ISize& atlas_size, std::vector& glyph_positions, const std::shared_ptr& rect_packer) { @@ -58,7 +56,8 @@ static size_t PairsFitInAtlasOfSize( for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) { const auto& pair = *it; - const auto glyph_size = ISize::Ceil(pair.glyph.bounds.size * pair.scale); + const auto glyph_size = + ISize::Ceil(pair.glyph.bounds.size * pair.scaled_font.scale); IPoint16 location_in_atlas; if (!rect_packer->addRect(glyph_size.width + kPadding, // glyph_size.height + kPadding, // @@ -78,7 +77,7 @@ static size_t PairsFitInAtlasOfSize( static bool CanAppendToExistingAtlas( const std::shared_ptr& atlas, - const FontGlyphPairRefVector& extra_pairs, + const std::vector& extra_pairs, std::vector& glyph_positions, ISize atlas_size, const std::shared_ptr& rect_packer) { @@ -95,7 +94,8 @@ static bool CanAppendToExistingAtlas( for (size_t i = 0; i < extra_pairs.size(); i++) { const FontGlyphPair& pair = extra_pairs[i]; - const auto glyph_size = ISize::Ceil(pair.glyph.bounds.size * pair.scale); + const auto glyph_size = + ISize::Ceil(pair.glyph.bounds.size * pair.scaled_font.scale); IPoint16 location_in_atlas; if (!rect_packer->addRect(glyph_size.width + kPadding, // glyph_size.height + kPadding, // @@ -114,7 +114,7 @@ static bool CanAppendToExistingAtlas( } static ISize OptimumAtlasSizeForFontGlyphPairs( - const FontGlyphPair::Set& pairs, + const std::vector& pairs, std::vector& glyph_positions, const std::shared_ptr& atlas_context, GlyphAtlas::Type type) { @@ -153,16 +153,17 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( } static void DrawGlyph(SkCanvas* canvas, - const FontGlyphPair& font_glyph, + const ScaledFont& scaled_font, + const Glyph& glyph, const Rect& location, bool has_color) { - const auto& metrics = font_glyph.font.GetMetrics(); - const auto position = SkPoint::Make(location.origin.x / font_glyph.scale, - location.origin.y / font_glyph.scale); - SkGlyphID glyph_id = font_glyph.glyph.index; + const auto& metrics = scaled_font.font.GetMetrics(); + const auto position = SkPoint::Make(location.origin.x / scaled_font.scale, + location.origin.y / scaled_font.scale); + SkGlyphID glyph_id = glyph.index; SkFont sk_font( - TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(), + TypefaceSkia::Cast(*scaled_font.font.GetTypeface()).GetSkiaTypeface(), metrics.point_size, metrics.scaleX, metrics.skewX); sk_font.setEdging(SkFont::Edging::kAntiAlias); sk_font.setHinting(SkFontHinting::kSlight); @@ -173,21 +174,20 @@ static void DrawGlyph(SkCanvas* canvas, SkPaint glyph_paint; glyph_paint.setColor(glyph_color); canvas->resetMatrix(); - canvas->scale(font_glyph.scale, font_glyph.scale); - canvas->drawGlyphs( - 1u, // count - &glyph_id, // glyphs - &position, // positions - SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(), - -font_glyph.glyph.bounds.GetTop()), // origin - sk_font, // font - glyph_paint // paint + canvas->scale(scaled_font.scale, scaled_font.scale); + canvas->drawGlyphs(1u, // count + &glyph_id, // glyphs + &position, // positions + SkPoint::Make(-glyph.bounds.GetLeft(), + -glyph.bounds.GetTop()), // origin + sk_font, // font + glyph_paint // paint ); } static bool UpdateAtlasBitmap(const GlyphAtlas& atlas, const std::shared_ptr& bitmap, - const FontGlyphPairRefVector& new_pairs) { + const std::vector& new_pairs) { TRACE_EVENT0("impeller", __FUNCTION__); FML_DCHECK(bitmap != nullptr); @@ -207,7 +207,7 @@ static bool UpdateAtlasBitmap(const GlyphAtlas& atlas, if (!pos.has_value()) { continue; } - DrawGlyph(canvas, pair, pos.value(), has_color); + DrawGlyph(canvas, pair.scaled_font, pair.glyph, pos.value(), has_color); } return true; } @@ -243,9 +243,10 @@ static std::shared_ptr CreateAtlasBitmap(const GlyphAtlas& atlas, bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap; - atlas.IterateGlyphs([canvas, has_color](const FontGlyphPair& font_glyph, + atlas.IterateGlyphs([canvas, has_color](const ScaledFont& scaled_font, + const Glyph& glyph, const Rect& location) -> bool { - DrawGlyph(canvas, font_glyph, location, has_color); + DrawGlyph(canvas, scaled_font, glyph, location, has_color); return true; }); @@ -313,7 +314,7 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( Context& context, GlyphAtlas::Type type, std::shared_ptr atlas_context, - const FontGlyphPair::Set& font_glyph_pairs) const { + const FontGlyphMap& font_glyph_map) const { TRACE_EVENT0("impeller", __FUNCTION__); if (!IsValid()) { return nullptr; @@ -321,7 +322,7 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( auto& atlas_context_skia = GlyphAtlasContextSkia::Cast(*atlas_context); std::shared_ptr last_atlas = atlas_context->GetGlyphAtlas(); - if (font_glyph_pairs.empty()) { + if (font_glyph_map.empty()) { return last_atlas; } @@ -329,10 +330,21 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( // Step 1: Determine if the atlas type and font glyph pairs are compatible // with the current atlas and reuse if possible. // --------------------------------------------------------------------------- - FontGlyphPairRefVector new_glyphs; - for (const FontGlyphPair& pair : font_glyph_pairs) { - if (!last_atlas->FindFontGlyphBounds(pair).has_value()) { - new_glyphs.push_back(pair); + std::vector new_glyphs; + for (const auto& font_value : font_glyph_map) { + const ScaledFont& scaled_font = font_value.first; + const FontGlyphAtlas* font_glyph_atlas = + last_atlas->GetFontGlyphAtlas(scaled_font.font, scaled_font.scale); + if (font_glyph_atlas) { + for (const Glyph& glyph : font_value.second) { + if (!font_glyph_atlas->FindGlyphBounds(glyph)) { + new_glyphs.emplace_back(scaled_font, glyph); + } + } + } else { + for (const Glyph& glyph : font_value.second) { + new_glyphs.emplace_back(scaled_font, glyph); + } } } if (last_atlas->GetType() == type && new_glyphs.size() == 0) { @@ -381,6 +393,16 @@ std::shared_ptr TypographerContextSkia::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 3b: Get the optimum size of the texture atlas. // --------------------------------------------------------------------------- + std::vector font_glyph_pairs; + font_glyph_pairs.reserve(std::accumulate( + font_glyph_map.begin(), font_glyph_map.end(), 0, + [](const int a, const auto& b) { return a + b.second.size(); })); + for (const auto& font_value : font_glyph_map) { + const ScaledFont& scaled_font = font_value.first; + for (const Glyph& glyph : font_value.second) { + font_glyph_pairs.push_back({scaled_font, glyph}); + } + } auto glyph_atlas = std::make_shared(type); auto atlas_size = OptimumAtlasSizeForFontGlyphPairs( font_glyph_pairs, glyph_positions, atlas_context, type); diff --git a/impeller/typographer/backends/skia/typographer_context_skia.h b/impeller/typographer/backends/skia/typographer_context_skia.h index 3c1ebf503134a..f54592e330968 100644 --- a/impeller/typographer/backends/skia/typographer_context_skia.h +++ b/impeller/typographer/backends/skia/typographer_context_skia.h @@ -25,7 +25,7 @@ class TypographerContextSkia : public TypographerContext { Context& context, GlyphAtlas::Type type, std::shared_ptr atlas_context, - const FontGlyphPair::Set& font_glyph_pairs) const override; + const FontGlyphMap& font_glyph_map) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(TypographerContextSkia); diff --git a/impeller/typographer/backends/stb/typographer_context_stb.cc b/impeller/typographer/backends/stb/typographer_context_stb.cc index 5efdcf4c72b6c..5669d7eba7837 100644 --- a/impeller/typographer/backends/stb/typographer_context_stb.cc +++ b/impeller/typographer/backends/stb/typographer_context_stb.cc @@ -4,6 +4,7 @@ #include "impeller/typographer/backends/stb/typographer_context_stb.h" +#include #include #include "flutter/fml/logging.h" @@ -23,9 +24,6 @@ constexpr auto kColorFontBitsPerPixel = 4; namespace impeller { -using FontGlyphPairRefVector = - std::vector>; - constexpr size_t kPadding = 1; std::unique_ptr TypographerContextSTB::Make() { @@ -44,7 +42,7 @@ TypographerContextSTB::CreateGlyphAtlasContext() const { // Function returns the count of "remaining pairs" not packed into rect of given // size. static size_t PairsFitInAtlasOfSize( - const FontGlyphPair::Set& pairs, + const std::vector& pairs, const ISize& atlas_size, std::vector& glyph_positions, const std::shared_ptr& rect_packer) { @@ -58,15 +56,16 @@ static size_t PairsFitInAtlasOfSize( size_t i = 0; for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) { const auto& pair = *it; + const Font& font = pair.scaled_font.font; // We downcast to the correct typeface type to access `stb` specific // methods. std::shared_ptr typeface_stb = - std::reinterpret_pointer_cast(pair.font.GetTypeface()); + std::reinterpret_pointer_cast(font.GetTypeface()); // Conversion factor to scale font size in Points to pixels. // Note this assumes typical DPI. float text_size_pixels = - pair.font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels; + font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels; ISize glyph_size; { @@ -100,7 +99,7 @@ static size_t PairsFitInAtlasOfSize( static bool CanAppendToExistingAtlas( const std::shared_ptr& atlas, - const FontGlyphPairRefVector& extra_pairs, + const std::vector& extra_pairs, std::vector& glyph_positions, ISize atlas_size, const std::shared_ptr& rect_packer) { @@ -116,14 +115,15 @@ static bool CanAppendToExistingAtlas( glyph_positions.reserve(extra_pairs.size()); for (size_t i = 0; i < extra_pairs.size(); i++) { const FontGlyphPair& pair = extra_pairs[i]; + const Font& font = pair.scaled_font.font; // We downcast to the correct typeface type to access `stb` specific methods std::shared_ptr typeface_stb = - std::reinterpret_pointer_cast(pair.font.GetTypeface()); + std::reinterpret_pointer_cast(font.GetTypeface()); // Conversion factor to scale font size in Points to pixels. // Note this assumes typical DPI. float text_size_pixels = - pair.font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels; + font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels; ISize glyph_size; { @@ -157,7 +157,7 @@ static bool CanAppendToExistingAtlas( } static ISize OptimumAtlasSizeForFontGlyphPairs( - const FontGlyphPair::Set& pairs, + const std::vector& pairs, std::vector& glyph_positions, const std::shared_ptr& atlas_context, GlyphAtlas::Type type) { @@ -196,13 +196,13 @@ static ISize OptimumAtlasSizeForFontGlyphPairs( } static void DrawGlyph(BitmapSTB* bitmap, - const FontGlyphPair& font_glyph, + const ScaledFont& scaled_font, + const Glyph& glyph, const Rect& location, bool has_color) { - const auto& metrics = font_glyph.font.GetMetrics(); + const auto& metrics = scaled_font.font.GetMetrics(); - const impeller::Font& font = font_glyph.font; - const impeller::Glyph& glyph = font_glyph.glyph; + const impeller::Font& font = scaled_font.font; auto typeface = font.GetTypeface(); // We downcast to the correct typeface type to access `stb` specific methods std::shared_ptr typeface_stb = @@ -272,7 +272,7 @@ static void DrawGlyph(BitmapSTB* bitmap, static bool UpdateAtlasBitmap(const GlyphAtlas& atlas, const std::shared_ptr& bitmap, - const FontGlyphPairRefVector& new_pairs) { + const std::vector& new_pairs) { TRACE_EVENT0("impeller", __FUNCTION__); FML_DCHECK(bitmap != nullptr); @@ -283,7 +283,8 @@ static bool UpdateAtlasBitmap(const GlyphAtlas& atlas, if (!pos.has_value()) { continue; } - DrawGlyph(bitmap.get(), pair, pos.value(), has_color); + DrawGlyph(bitmap.get(), pair.scaled_font, pair.glyph, pos.value(), + has_color); } return true; } @@ -302,9 +303,10 @@ static std::shared_ptr CreateAtlasBitmap(const GlyphAtlas& atlas, bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap; - atlas.IterateGlyphs([&bitmap, has_color](const FontGlyphPair& font_glyph, + atlas.IterateGlyphs([&bitmap, has_color](const ScaledFont& scaled_font, + const Glyph& glyph, const Rect& location) -> bool { - DrawGlyph(bitmap.get(), font_glyph, location, has_color); + DrawGlyph(bitmap.get(), scaled_font, glyph, location, has_color); return true; }); @@ -375,7 +377,7 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( Context& context, GlyphAtlas::Type type, std::shared_ptr atlas_context, - const FontGlyphPair::Set& font_glyph_pairs) const { + const FontGlyphMap& font_glyph_map) const { TRACE_EVENT0("impeller", __FUNCTION__); if (!IsValid()) { return nullptr; @@ -383,7 +385,7 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( auto& atlas_context_stb = GlyphAtlasContextSTB::Cast(*atlas_context); std::shared_ptr last_atlas = atlas_context->GetGlyphAtlas(); - if (font_glyph_pairs.empty()) { + if (font_glyph_map.empty()) { return last_atlas; } @@ -391,10 +393,21 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( // Step 1: Determine if the atlas type and font glyph pairs are compatible // with the current atlas and reuse if possible. // --------------------------------------------------------------------------- - FontGlyphPairRefVector new_glyphs; - for (const FontGlyphPair& pair : font_glyph_pairs) { - if (!last_atlas->FindFontGlyphBounds(pair).has_value()) { - new_glyphs.push_back(pair); + std::vector new_glyphs; + for (const auto& font_value : font_glyph_map) { + const ScaledFont& scaled_font = font_value.first; + const FontGlyphAtlas* font_glyph_atlas = + last_atlas->GetFontGlyphAtlas(scaled_font.font, scaled_font.scale); + if (font_glyph_atlas) { + for (const Glyph& glyph : font_value.second) { + if (!font_glyph_atlas->FindGlyphBounds(glyph)) { + new_glyphs.emplace_back(scaled_font, glyph); + } + } + } else { + for (const Glyph& glyph : font_value.second) { + new_glyphs.emplace_back(scaled_font, glyph); + } } } if (last_atlas->GetType() == type && new_glyphs.size() == 0) { @@ -444,6 +457,16 @@ std::shared_ptr TypographerContextSTB::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 3b: Get the optimum size of the texture atlas. // --------------------------------------------------------------------------- + std::vector font_glyph_pairs; + font_glyph_pairs.reserve(std::accumulate( + font_glyph_map.begin(), font_glyph_map.end(), 0, + [](const int a, const auto& b) { return a + b.second.size(); })); + for (const auto& font_value : font_glyph_map) { + const ScaledFont& scaled_font = font_value.first; + for (const Glyph& glyph : font_value.second) { + font_glyph_pairs.push_back({scaled_font, glyph}); + } + } auto glyph_atlas = std::make_shared(type); auto atlas_size = OptimumAtlasSizeForFontGlyphPairs( font_glyph_pairs, glyph_positions, atlas_context, type); diff --git a/impeller/typographer/backends/stb/typographer_context_stb.h b/impeller/typographer/backends/stb/typographer_context_stb.h index 5496931363ed5..4254c01f98de0 100644 --- a/impeller/typographer/backends/stb/typographer_context_stb.h +++ b/impeller/typographer/backends/stb/typographer_context_stb.h @@ -27,7 +27,7 @@ class TypographerContextSTB : public TypographerContext { Context& context, GlyphAtlas::Type type, std::shared_ptr atlas_context, - const FontGlyphPair::Set& font_glyph_pairs) const override; + const FontGlyphMap& font_glyph_map) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(TypographerContextSTB); diff --git a/impeller/typographer/font_glyph_pair.h b/impeller/typographer/font_glyph_pair.h index 16eaecdc6606a..60be5b8f88293 100644 --- a/impeller/typographer/font_glyph_pair.h +++ b/impeller/typographer/font_glyph_pair.h @@ -16,43 +16,40 @@ namespace impeller { //------------------------------------------------------------------------------ -/// @brief A font along with a glyph in that font rendered at a particular -/// scale. Used in glyph atlases as keys. +/// @brief A font and a scale. Used as a key that represents a typeface +/// within a glyph atlas. /// -struct FontGlyphPair { - struct Hash; - struct Equal; - - using Set = std::unordered_set; - +struct ScaledFont { Font font; - Glyph glyph; Scalar scale; +}; + +using FontGlyphMap = std::unordered_map>; - struct Hash { - std::size_t operator()(const FontGlyphPair& p) const { - static_assert(sizeof(p.glyph.index) == 2); - static_assert(sizeof(p.glyph.type) == 1); - size_t index = p.glyph.index; - size_t type = static_cast(p.glyph.type); - // By packaging multiple values in a single size_t the hash function is - // more efficient without losing entropy. - if (sizeof(size_t) == 8 && sizeof(Scalar) == 4) { - const float* fScale = &p.scale; - size_t nScale = *reinterpret_cast(fScale); - size_t index_type_scale = nScale << 32 | index << 16 | type; - return fml::HashCombine(p.font.GetHash(), index_type_scale); - } - size_t index_type = index << 16 | type; - return fml::HashCombine(p.font.GetHash(), index_type, p.scale); - } - }; - struct Equal { - bool operator()(const FontGlyphPair& lhs, const FontGlyphPair& rhs) const { - return lhs.font.IsEqual(rhs.font) && lhs.glyph.index == rhs.glyph.index && - lhs.glyph.type == rhs.glyph.type && lhs.scale == rhs.scale; - } - }; +//------------------------------------------------------------------------------ +/// @brief A font along with a glyph in that font rendered at a particular +/// scale. +/// +struct FontGlyphPair { + FontGlyphPair(const ScaledFont& sf, const Glyph& g) + : scaled_font(sf), glyph(g) {} + const ScaledFont& scaled_font; + const Glyph& glyph; }; } // namespace impeller + +template <> +struct std::hash { + constexpr std::size_t operator()(const impeller::ScaledFont& sf) const { + return fml::HashCombine(sf.font.GetHash(), sf.scale); + } +}; + +template <> +struct std::equal_to { + constexpr bool operator()(const impeller::ScaledFont& lhs, + const impeller::ScaledFont& rhs) const { + return lhs.font.IsEqual(rhs.font) && lhs.scale == rhs.scale; + } +}; diff --git a/impeller/typographer/glyph.h b/impeller/typographer/glyph.h index d3797568fdaa2..c9aad01cba317 100644 --- a/impeller/typographer/glyph.h +++ b/impeller/typographer/glyph.h @@ -48,7 +48,9 @@ static_assert(sizeof(Glyph) == 20); template <> struct std::hash { constexpr std::size_t operator()(const impeller::Glyph& g) const { - return fml::HashCombine(g.index, g.type); + static_assert(sizeof(g.index) == 2); + static_assert(sizeof(g.type) == 1); + return (static_cast(g.type) << 16) | g.index; } }; diff --git a/impeller/typographer/glyph_atlas.cc b/impeller/typographer/glyph_atlas.cc index 21a30ad4d8f80..1f9bfaff8d7ae 100644 --- a/impeller/typographer/glyph_atlas.cc +++ b/impeller/typographer/glyph_atlas.cc @@ -4,6 +4,7 @@ #include "impeller/typographer/glyph_atlas.h" +#include #include namespace impeller { @@ -59,37 +60,60 @@ void GlyphAtlas::SetTexture(std::shared_ptr texture) { void GlyphAtlas::AddTypefaceGlyphPosition(const FontGlyphPair& pair, Rect rect) { - positions_[pair] = rect; + font_atlas_map_[pair.scaled_font].positions_[pair.glyph] = rect; } std::optional GlyphAtlas::FindFontGlyphBounds( const FontGlyphPair& pair) const { - const auto& found = positions_.find(pair); - if (found == positions_.end()) { + const auto& found = font_atlas_map_.find(pair.scaled_font); + if (found == font_atlas_map_.end()) { return std::nullopt; } - return found->second; + return found->second.FindGlyphBounds(pair.glyph); +} + +const FontGlyphAtlas* GlyphAtlas::GetFontGlyphAtlas(const Font& font, + Scalar scale) const { + const auto& found = font_atlas_map_.find({font, scale}); + if (found == font_atlas_map_.end()) { + return nullptr; + } + return &found->second; } size_t GlyphAtlas::GetGlyphCount() const { - return positions_.size(); + return std::accumulate(font_atlas_map_.begin(), font_atlas_map_.end(), 0, + [](const int a, const auto& b) { + return a + b.second.positions_.size(); + }); } size_t GlyphAtlas::IterateGlyphs( - const std::function& - iterator) const { + const std::function& iterator) const { if (!iterator) { return 0u; } size_t count = 0u; - for (const auto& position : positions_) { - count++; - if (!iterator(position.first, position.second)) { - return count; + for (const auto& font_value : font_atlas_map_) { + for (const auto& glyph_value : font_value.second.positions_) { + count++; + if (!iterator(font_value.first, glyph_value.first, glyph_value.second)) { + return count; + } } } return count; } +std::optional FontGlyphAtlas::FindGlyphBounds(const Glyph& glyph) const { + const auto& found = positions_.find(glyph); + if (found == positions_.end()) { + return std::nullopt; + } + return found->second; +} + } // namespace impeller diff --git a/impeller/typographer/glyph_atlas.h b/impeller/typographer/glyph_atlas.h index c7d4ea4acfcd4..03d428ac0401b 100644 --- a/impeller/typographer/glyph_atlas.h +++ b/impeller/typographer/glyph_atlas.h @@ -18,6 +18,8 @@ namespace impeller { +class FontGlyphAtlas; + //------------------------------------------------------------------------------ /// @brief A texture containing the bitmap representation of glyphs in /// different fonts along with the ability to query the location of @@ -97,8 +99,9 @@ class GlyphAtlas { /// @return The number of glyphs iterated over. /// size_t IterateGlyphs( - const std::function& - iterator) const; + const std::function& iterator) const; //---------------------------------------------------------------------------- /// @brief Find the location of a specific font-glyph pair in the atlas. @@ -106,19 +109,29 @@ class GlyphAtlas { /// @param[in] pair The font-glyph pair /// /// @return The location of the font-glyph pair in the atlas. - /// `std::nullopt` of the pair in not in the atlas. + /// `std::nullopt` if the pair is not in the atlas. /// std::optional FindFontGlyphBounds(const FontGlyphPair& pair) const; + //---------------------------------------------------------------------------- + /// @brief Obtain an interface for querying the location of glyphs in the + /// atlas for the given font and scale. This provides a more + /// efficient way to look up a run of glyphs in the same font. + /// + /// @param[in] font The font + /// @param[in] scale The scale + /// + /// @return A pointer to a FontGlyphAtlas, or nullptr if the font and + /// scale are not available in the atlas. The pointer is only + /// valid for the lifetime of the GlyphAtlas. + /// + const FontGlyphAtlas* GetFontGlyphAtlas(const Font& font, Scalar scale) const; + private: const Type type_; std::shared_ptr texture_; - std::unordered_map - positions_; + std::unordered_map font_atlas_map_; FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlas); }; @@ -159,4 +172,29 @@ class GlyphAtlasContext { FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlasContext); }; +//------------------------------------------------------------------------------ +/// @brief An object that can look up glyph locations within the GlyphAtlas +/// for a particular typeface. +/// +class FontGlyphAtlas { + public: + FontGlyphAtlas() = default; + + //---------------------------------------------------------------------------- + /// @brief Find the location of a glyph in the atlas. + /// + /// @param[in] glyph The glyph + /// + /// @return The location of the glyph in the atlas. + /// `std::nullopt` if the glyph is not in the atlas. + /// + std::optional FindGlyphBounds(const Glyph& glyph) const; + + private: + friend class GlyphAtlas; + std::unordered_map positions_; + + FML_DISALLOW_COPY_AND_ASSIGN(FontGlyphAtlas); +}; + } // namespace impeller diff --git a/impeller/typographer/lazy_glyph_atlas.cc b/impeller/typographer/lazy_glyph_atlas.cc index bf45acda95985..fd5cf27beddc3 100644 --- a/impeller/typographer/lazy_glyph_atlas.cc +++ b/impeller/typographer/lazy_glyph_atlas.cc @@ -26,15 +26,15 @@ LazyGlyphAtlas::~LazyGlyphAtlas() = default; void LazyGlyphAtlas::AddTextFrame(const TextFrame& frame, Scalar scale) { FML_DCHECK(atlas_map_.empty()); if (frame.GetAtlasType() == GlyphAtlas::Type::kAlphaBitmap) { - frame.CollectUniqueFontGlyphPairs(alpha_set_, scale); + frame.CollectUniqueFontGlyphPairs(alpha_glyph_map_, scale); } else { - frame.CollectUniqueFontGlyphPairs(color_set_, scale); + frame.CollectUniqueFontGlyphPairs(color_glyph_map_, scale); } } void LazyGlyphAtlas::ResetTextFrames() { - alpha_set_.clear(); - color_set_.clear(); + alpha_glyph_map_.clear(); + color_glyph_map_.clear(); atlas_map_.clear(); } @@ -59,11 +59,12 @@ std::shared_ptr LazyGlyphAtlas::CreateOrGetGlyphAtlas( return nullptr; } - auto& set = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_set_ : color_set_; + auto& glyph_map = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_glyph_map_ + : color_glyph_map_; auto atlas_context = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_context_ : color_context_; - auto atlas = - typographer_context_->CreateGlyphAtlas(context, type, atlas_context, set); + auto atlas = typographer_context_->CreateGlyphAtlas(context, type, + atlas_context, glyph_map); if (!atlas || !atlas->IsValid()) { VALIDATION_LOG << "Could not create valid atlas."; return nullptr; diff --git a/impeller/typographer/lazy_glyph_atlas.h b/impeller/typographer/lazy_glyph_atlas.h index 3794a7dcccac9..b6e8d04d72ac7 100644 --- a/impeller/typographer/lazy_glyph_atlas.h +++ b/impeller/typographer/lazy_glyph_atlas.h @@ -32,8 +32,8 @@ class LazyGlyphAtlas { private: std::shared_ptr typographer_context_; - FontGlyphPair::Set alpha_set_; - FontGlyphPair::Set color_set_; + FontGlyphMap alpha_glyph_map_; + FontGlyphMap color_glyph_map_; std::shared_ptr alpha_context_; std::shared_ptr color_context_; mutable std::unordered_map> diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc index 9c02a7393bd62..3f82c1d239a9d 100644 --- a/impeller/typographer/text_frame.cc +++ b/impeller/typographer/text_frame.cc @@ -67,12 +67,13 @@ Scalar TextFrame::RoundScaledFontSize(Scalar scale, Scalar point_size) { return std::round(scale * 100) / 100; } -void TextFrame::CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set, +void TextFrame::CollectUniqueFontGlyphPairs(FontGlyphMap& glyph_map, Scalar scale) const { for (const TextRun& run : GetRuns()) { const Font& font = run.GetFont(); auto rounded_scale = RoundScaledFontSize(scale, font.GetMetrics().point_size); + auto& set = glyph_map[{font, rounded_scale}]; for (const TextRun::GlyphPosition& glyph_position : run.GetGlyphPositions()) { #if false @@ -82,7 +83,7 @@ if (rounded_scale != scale) { FML_LOG(ERROR) << glyph_position.glyph.bounds.size * delta; } #endif - set.insert({font, glyph_position.glyph, rounded_scale}); + set.insert(glyph_position.glyph); } } } diff --git a/impeller/typographer/text_frame.h b/impeller/typographer/text_frame.h index 35316351e66f0..d0d2d03e8068c 100644 --- a/impeller/typographer/text_frame.h +++ b/impeller/typographer/text_frame.h @@ -24,7 +24,7 @@ class TextFrame { ~TextFrame(); - void CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set, Scalar scale) const; + void CollectUniqueFontGlyphPairs(FontGlyphMap& glyph_map, Scalar scale) const; static Scalar RoundScaledFontSize(Scalar scale, Scalar point_size); diff --git a/impeller/typographer/typographer_context.h b/impeller/typographer/typographer_context.h index 8ed74ce1fcbf1..1ca4d4a7687c3 100644 --- a/impeller/typographer/typographer_context.h +++ b/impeller/typographer/typographer_context.h @@ -36,7 +36,7 @@ class TypographerContext { Context& context, GlyphAtlas::Type type, std::shared_ptr atlas_context, - const FontGlyphPair::Set& font_glyph_pairs) const = 0; + const FontGlyphMap& font_glyph_map) const = 0; protected: //---------------------------------------------------------------------------- diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index fd412297b4941..a996bdf98d98d 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -29,10 +29,10 @@ static std::shared_ptr CreateGlyphAtlas( Scalar scale, const std::shared_ptr& atlas_context, const TextFrame& frame) { - FontGlyphPair::Set set; - frame.CollectUniqueFontGlyphPairs(set, scale); + FontGlyphMap font_glyph_map; + frame.CollectUniqueFontGlyphPairs(font_glyph_map, scale); return typographer_context->CreateGlyphAtlas(context, type, atlas_context, - set); + font_glyph_map); } TEST_P(TypographerTest, CanConvertTextBlob) { @@ -68,17 +68,22 @@ TEST_P(TypographerTest, CanCreateGlyphAtlas) { ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap); ASSERT_EQ(atlas->GetGlyphCount(), 4llu); - std::optional first_pair; + std::optional first_scaled_font; + std::optional first_glyph; Rect first_rect; - atlas->IterateGlyphs( - [&](const FontGlyphPair& pair, const Rect& rect) -> bool { - first_pair = pair; - first_rect = rect; - return false; - }); + atlas->IterateGlyphs([&](const ScaledFont& scaled_font, const Glyph& glyph, + const Rect& rect) -> bool { + first_scaled_font = scaled_font; + first_glyph = glyph; + first_rect = rect; + return false; + }); - ASSERT_TRUE(first_pair.has_value()); - ASSERT_TRUE(atlas->FindFontGlyphBounds(first_pair.value()).has_value()); + ASSERT_TRUE(first_scaled_font.has_value()); + ASSERT_TRUE(atlas + ->FindFontGlyphBounds( + {first_scaled_font.value(), first_glyph.value()}) + .has_value()); } static sk_sp OpenFixtureAsSkData(const char* fixture_name) { @@ -189,25 +194,26 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) { auto blob = SkTextBlob::MakeFromString(test_string, sk_font); ASSERT_TRUE(blob); - FontGlyphPair::Set set; + FontGlyphMap font_glyph_map; size_t size_count = 8; for (size_t index = 0; index < size_count; index += 1) { MakeTextFrameFromTextBlobSkia(blob).value().CollectUniqueFontGlyphPairs( - set, 0.6 * index); + font_glyph_map, 0.6 * index); }; auto atlas = context->CreateGlyphAtlas(*GetContext(), GlyphAtlas::Type::kAlphaBitmap, - std::move(atlas_context), set); + std::move(atlas_context), font_glyph_map); ASSERT_NE(atlas, nullptr); ASSERT_NE(atlas->GetTexture(), nullptr); std::set unique_glyphs; std::vector total_glyphs; - atlas->IterateGlyphs([&](const FontGlyphPair& pair, const Rect& rect) { - unique_glyphs.insert(pair.glyph.index); - total_glyphs.push_back(pair.glyph.index); - return true; - }); + atlas->IterateGlyphs( + [&](const ScaledFont& scaled_font, const Glyph& glyph, const Rect& rect) { + unique_glyphs.insert(glyph.index); + total_glyphs.push_back(glyph.index); + return true; + }); EXPECT_EQ(unique_glyphs.size() * size_count, atlas->GetGlyphCount()); EXPECT_EQ(total_glyphs.size(), atlas->GetGlyphCount()); @@ -284,24 +290,6 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) { ASSERT_NE(old_packer, new_packer); } -TEST_P(TypographerTest, FontGlyphPairTypeChangesHashAndEquals) { - Font font = Font(nullptr, {}); - FontGlyphPair pair_1 = { - .font = font, - .glyph = Glyph(0, Glyph::Type::kBitmap, Rect::MakeXYWH(0, 0, 1, 1))}; - // Same glyph same type. - FontGlyphPair pair_2 = { - .font = font, - .glyph = Glyph(0, Glyph::Type::kBitmap, Rect::MakeXYWH(0, 0, 1, 1))}; - // Same glyph different type. - FontGlyphPair pair_3 = { - .font = font, - .glyph = Glyph(0, Glyph::Type::kPath, Rect::MakeXYWH(0, 0, 1, 1))}; - - ASSERT_TRUE(FontGlyphPair::Equal{}(pair_1, pair_2)); - ASSERT_FALSE(FontGlyphPair::Equal{}(pair_1, pair_3)); -} - TEST_P(TypographerTest, MaybeHasOverlapping) { sk_sp font_mgr = SkFontMgr::RefDefault(); sk_sp typeface =