[Impeller] Reorganize the glyph atlas to improve efficiency when looking up runs of glyphs in the same font (#45191)
The atlas will now store a separate map of glyph positions for each font/scale pair. This avoids the cost of repeated hashing and copying of font objects for each glyph in a text run.
See https://github.com/flutter/flutter/issues/133201
diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc
index a93967c..9495556 100644
--- a/impeller/entity/contents/text_contents.cc
+++ b/impeller/entity/contents/text_contents.cc
@@ -166,13 +166,17 @@
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<Rect> 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 85bae56..9f8725a 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 <numeric>
#include <utility>
#include "flutter/fml/logging.h"
@@ -21,9 +22,6 @@
namespace impeller {
-using FontGlyphPairRefVector =
- std::vector<std::reference_wrapper<const FontGlyphPair>>;
-
// 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 @@
}
static size_t PairsFitInAtlasOfSize(
- const FontGlyphPair::Set& pairs,
+ const std::vector<FontGlyphPair>& pairs,
const ISize& atlas_size,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -58,7 +56,8 @@
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 bool CanAppendToExistingAtlas(
const std::shared_ptr<GlyphAtlas>& atlas,
- const FontGlyphPairRefVector& extra_pairs,
+ const std::vector<FontGlyphPair>& extra_pairs,
std::vector<Rect>& glyph_positions,
ISize atlas_size,
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -95,7 +94,8 @@
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 ISize OptimumAtlasSizeForFontGlyphPairs(
- const FontGlyphPair::Set& pairs,
+ const std::vector<FontGlyphPair>& pairs,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
GlyphAtlas::Type type) {
@@ -153,16 +153,17 @@
}
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 @@
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<SkBitmap>& bitmap,
- const FontGlyphPairRefVector& new_pairs) {
+ const std::vector<FontGlyphPair>& new_pairs) {
TRACE_EVENT0("impeller", __FUNCTION__);
FML_DCHECK(bitmap != nullptr);
@@ -207,7 +207,7 @@
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 @@
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 @@
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> 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 @@
auto& atlas_context_skia = GlyphAtlasContextSkia::Cast(*atlas_context);
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
- if (font_glyph_pairs.empty()) {
+ if (font_glyph_map.empty()) {
return last_atlas;
}
@@ -329,10 +330,21 @@
// 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<FontGlyphPair> 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 @@
// ---------------------------------------------------------------------------
// Step 3b: Get the optimum size of the texture atlas.
// ---------------------------------------------------------------------------
+ std::vector<FontGlyphPair> 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<GlyphAtlas>(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 3c1ebf5..f54592e 100644
--- a/impeller/typographer/backends/skia/typographer_context_skia.h
+++ b/impeller/typographer/backends/skia/typographer_context_skia.h
@@ -25,7 +25,7 @@
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> 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 5efdcf4..5669d7e 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 <numeric>
#include <utility>
#include "flutter/fml/logging.h"
@@ -23,9 +24,6 @@
namespace impeller {
-using FontGlyphPairRefVector =
- std::vector<std::reference_wrapper<const FontGlyphPair>>;
-
constexpr size_t kPadding = 1;
std::unique_ptr<TypographerContext> TypographerContextSTB::Make() {
@@ -44,7 +42,7 @@
// 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<FontGlyphPair>& pairs,
const ISize& atlas_size,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -58,15 +56,16 @@
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<TypefaceSTB> typeface_stb =
- std::reinterpret_pointer_cast<TypefaceSTB>(pair.font.GetTypeface());
+ std::reinterpret_pointer_cast<TypefaceSTB>(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 bool CanAppendToExistingAtlas(
const std::shared_ptr<GlyphAtlas>& atlas,
- const FontGlyphPairRefVector& extra_pairs,
+ const std::vector<FontGlyphPair>& extra_pairs,
std::vector<Rect>& glyph_positions,
ISize atlas_size,
const std::shared_ptr<RectanglePacker>& rect_packer) {
@@ -116,14 +115,15 @@
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<TypefaceSTB> typeface_stb =
- std::reinterpret_pointer_cast<TypefaceSTB>(pair.font.GetTypeface());
+ std::reinterpret_pointer_cast<TypefaceSTB>(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 ISize OptimumAtlasSizeForFontGlyphPairs(
- const FontGlyphPair::Set& pairs,
+ const std::vector<FontGlyphPair>& pairs,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
GlyphAtlas::Type type) {
@@ -196,13 +196,13 @@
}
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<TypefaceSTB> typeface_stb =
@@ -272,7 +272,7 @@
static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
const std::shared_ptr<BitmapSTB>& bitmap,
- const FontGlyphPairRefVector& new_pairs) {
+ const std::vector<FontGlyphPair>& new_pairs) {
TRACE_EVENT0("impeller", __FUNCTION__);
FML_DCHECK(bitmap != nullptr);
@@ -283,7 +283,8 @@
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 @@
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 @@
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> 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 @@
auto& atlas_context_stb = GlyphAtlasContextSTB::Cast(*atlas_context);
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
- if (font_glyph_pairs.empty()) {
+ if (font_glyph_map.empty()) {
return last_atlas;
}
@@ -391,10 +393,21 @@
// 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<FontGlyphPair> 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 @@
// ---------------------------------------------------------------------------
// Step 3b: Get the optimum size of the texture atlas.
// ---------------------------------------------------------------------------
+ std::vector<FontGlyphPair> 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<GlyphAtlas>(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 5496931..4254c01 100644
--- a/impeller/typographer/backends/stb/typographer_context_stb.h
+++ b/impeller/typographer/backends/stb/typographer_context_stb.h
@@ -27,7 +27,7 @@
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> 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 16eaecd..60be5b8 100644
--- a/impeller/typographer/font_glyph_pair.h
+++ b/impeller/typographer/font_glyph_pair.h
@@ -16,43 +16,40 @@
namespace impeller {
//------------------------------------------------------------------------------
+/// @brief A font and a scale. Used as a key that represents a typeface
+/// within a glyph atlas.
+///
+struct ScaledFont {
+ Font font;
+ Scalar scale;
+};
+
+using FontGlyphMap = std::unordered_map<ScaledFont, std::unordered_set<Glyph>>;
+
+//------------------------------------------------------------------------------
/// @brief A font along with a glyph in that font rendered at a particular
-/// scale. Used in glyph atlases as keys.
+/// scale.
///
struct FontGlyphPair {
- struct Hash;
- struct Equal;
-
- using Set = std::unordered_set<FontGlyphPair, Hash, Equal>;
-
- Font font;
- Glyph glyph;
- Scalar scale;
-
- 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<size_t>(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<const uint32_t*>(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;
- }
- };
+ 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<impeller::ScaledFont> {
+ constexpr std::size_t operator()(const impeller::ScaledFont& sf) const {
+ return fml::HashCombine(sf.font.GetHash(), sf.scale);
+ }
+};
+
+template <>
+struct std::equal_to<impeller::ScaledFont> {
+ 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 d379756..c9aad01 100644
--- a/impeller/typographer/glyph.h
+++ b/impeller/typographer/glyph.h
@@ -48,7 +48,9 @@
template <>
struct std::hash<impeller::Glyph> {
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<size_t>(g.type) << 16) | g.index;
}
};
diff --git a/impeller/typographer/glyph_atlas.cc b/impeller/typographer/glyph_atlas.cc
index 21a30ad..1f9bfaf 100644
--- a/impeller/typographer/glyph_atlas.cc
+++ b/impeller/typographer/glyph_atlas.cc
@@ -4,6 +4,7 @@
#include "impeller/typographer/glyph_atlas.h"
+#include <numeric>
#include <utility>
namespace impeller {
@@ -59,37 +60,60 @@
void GlyphAtlas::AddTypefaceGlyphPosition(const FontGlyphPair& pair,
Rect rect) {
- positions_[pair] = rect;
+ font_atlas_map_[pair.scaled_font].positions_[pair.glyph] = rect;
}
std::optional<Rect> 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<bool(const FontGlyphPair& pair, const Rect& rect)>&
- iterator) const {
+ const std::function<bool(const ScaledFont& scaled_font,
+ const Glyph& glyph,
+ const Rect& rect)>& 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<Rect> 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 c7d4ea4..03d428a 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 @@
/// @return The number of glyphs iterated over.
///
size_t IterateGlyphs(
- const std::function<bool(const FontGlyphPair& pair, const Rect& rect)>&
- iterator) const;
+ const std::function<bool(const ScaledFont& scaled_font,
+ const Glyph& glyph,
+ const Rect& rect)>& iterator) const;
//----------------------------------------------------------------------------
/// @brief Find the location of a specific font-glyph pair in the atlas.
@@ -106,19 +109,29 @@
/// @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<Rect> 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> texture_;
- std::unordered_map<FontGlyphPair,
- Rect,
- FontGlyphPair::Hash,
- FontGlyphPair::Equal>
- positions_;
+ std::unordered_map<ScaledFont, FontGlyphAtlas> font_atlas_map_;
FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlas);
};
@@ -159,4 +172,29 @@
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<Rect> FindGlyphBounds(const Glyph& glyph) const;
+
+ private:
+ friend class GlyphAtlas;
+ std::unordered_map<Glyph, Rect> 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 bf45acd..fd5cf27 100644
--- a/impeller/typographer/lazy_glyph_atlas.cc
+++ b/impeller/typographer/lazy_glyph_atlas.cc
@@ -26,15 +26,15 @@
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 @@
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 3794a7d..b6e8d04 100644
--- a/impeller/typographer/lazy_glyph_atlas.h
+++ b/impeller/typographer/lazy_glyph_atlas.h
@@ -32,8 +32,8 @@
private:
std::shared_ptr<TypographerContext> typographer_context_;
- FontGlyphPair::Set alpha_set_;
- FontGlyphPair::Set color_set_;
+ FontGlyphMap alpha_glyph_map_;
+ FontGlyphMap color_glyph_map_;
std::shared_ptr<GlyphAtlasContext> alpha_context_;
std::shared_ptr<GlyphAtlasContext> color_context_;
mutable std::unordered_map<GlyphAtlas::Type, std::shared_ptr<GlyphAtlas>>
diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc
index 9c02a73..3f82c1d 100644
--- a/impeller/typographer/text_frame.cc
+++ b/impeller/typographer/text_frame.cc
@@ -67,12 +67,13 @@
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 @@
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 3531635..d0d2d03 100644
--- a/impeller/typographer/text_frame.h
+++ b/impeller/typographer/text_frame.h
@@ -24,7 +24,7 @@
~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 8ed74ce..1ca4d4a 100644
--- a/impeller/typographer/typographer_context.h
+++ b/impeller/typographer/typographer_context.h
@@ -36,7 +36,7 @@
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> 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 fd41229..a996bdf 100644
--- a/impeller/typographer/typographer_unittests.cc
+++ b/impeller/typographer/typographer_unittests.cc
@@ -29,10 +29,10 @@
Scalar scale,
const std::shared_ptr<GlyphAtlasContext>& 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 @@
ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
ASSERT_EQ(atlas->GetGlyphCount(), 4llu);
- std::optional<FontGlyphPair> first_pair;
+ std::optional<impeller::ScaledFont> first_scaled_font;
+ std::optional<impeller::Glyph> 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<SkData> OpenFixtureAsSkData(const char* fixture_name) {
@@ -189,25 +194,26 @@
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<uint16_t> unique_glyphs;
std::vector<uint16_t> 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 @@
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<SkFontMgr> font_mgr = SkFontMgr::RefDefault();
sk_sp<SkTypeface> typeface =