Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 767f2fb

Browse files
authored
[Impeller] Improve resolution of text scaling. (#43533)
This patch does a few things: - Changes ownership of the LazyGlyphAtlas to ContentContext from Canvas. This means that drawings with multiple canvases (e.g. calls to DrawPicture) share the same lazy atlas. - Moves the scale property from Font::Metrics to FontGlyphPair. Font::Metrics contains properties related to the font, whereas scale has to do with the properties of the drawing at render time and is independent of the font. - Makes the lazy atlas manage FontGlyphPair::Set rather than vectors of TextFrames. - Makes the determination of font scaling at EntityPass::Render rather than Canvas::DrawTextFrame. The scaling may be altered by calls to Canvas::DrawPicture by render time and otherwise get missed. This is done via a new method: Contents::PopulateGlyphAtlas, which is only implemented by TextContents in this patch. - Fixes up some miscelleaneous bugs in Font::Metrics hashing and unused/dead code. This improves over prior attempts: LazyGlyphAtlas ownership is now unambiguous, and glyph scaling happens correctly for all rendered glyphs regardless of the order of canvas operations in Aiks. Fixes flutter/flutter#130142
1 parent 153d9e1 commit 767f2fb

28 files changed

+250
-201
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,6 +2851,35 @@ TEST_P(AiksTest, CanCanvasDrawPicture) {
28512851
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
28522852
}
28532853

2854+
TEST_P(AiksTest, DrawPictureWithText) {
2855+
Canvas subcanvas;
2856+
ASSERT_TRUE(RenderTextInCanvas(
2857+
GetContext(), subcanvas,
2858+
"the quick brown fox jumped over the lazy dog!.?", "Roboto-Regular.ttf"));
2859+
subcanvas.Translate({0, 10});
2860+
subcanvas.Scale(Vector2(3, 3));
2861+
ASSERT_TRUE(RenderTextInCanvas(
2862+
GetContext(), subcanvas,
2863+
"the quick brown fox jumped over the very big lazy dog!.?",
2864+
"Roboto-Regular.ttf"));
2865+
auto picture = subcanvas.EndRecordingAsPicture();
2866+
2867+
Canvas canvas;
2868+
canvas.Scale(Vector2(.2, .2));
2869+
canvas.Save();
2870+
canvas.Translate({200, 200});
2871+
canvas.Scale(Vector2(3.5, 3.5)); // The text must not be blurry after this.
2872+
canvas.DrawPicture(picture);
2873+
canvas.Restore();
2874+
2875+
canvas.Scale(Vector2(1.5, 1.5));
2876+
ASSERT_TRUE(RenderTextInCanvas(
2877+
GetContext(), canvas,
2878+
"the quick brown fox jumped over the smaller lazy dog!.?",
2879+
"Roboto-Regular.ttf"));
2880+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
2881+
}
2882+
28542883
TEST_P(AiksTest, MatrixBackdropFilter) {
28552884
Canvas canvas;
28562885
canvas.SaveLayer({}, std::nullopt,

impeller/aiks/canvas.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ void Canvas::Initialize(std::optional<Rect> cull_rect) {
4242
base_pass_ = std::make_unique<EntityPass>();
4343
current_pass_ = base_pass_.get();
4444
xformation_stack_.emplace_back(CanvasStackEntry{.cull_rect = cull_rect});
45-
lazy_glyph_atlas_ = std::make_shared<LazyGlyphAtlas>();
4645
FML_DCHECK(GetSaveCount() == 1u);
4746
FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
4847
}
@@ -51,7 +50,6 @@ void Canvas::Reset() {
5150
base_pass_ = nullptr;
5251
current_pass_ = nullptr;
5352
xformation_stack_ = {};
54-
lazy_glyph_atlas_ = nullptr;
5553
}
5654

5755
void Canvas::Save() {
@@ -516,15 +514,12 @@ void Canvas::SaveLayer(const Paint& paint,
516514
void Canvas::DrawTextFrame(const TextFrame& text_frame,
517515
Point position,
518516
const Paint& paint) {
519-
lazy_glyph_atlas_->AddTextFrame(text_frame);
520-
521517
Entity entity;
522518
entity.SetStencilDepth(GetStencilDepth());
523519
entity.SetBlendMode(paint.blend_mode);
524520

525521
auto text_contents = std::make_shared<TextContents>();
526522
text_contents->SetTextFrame(text_frame);
527-
text_contents->SetGlyphAtlas(lazy_glyph_atlas_);
528523

529524
if (paint.color_source.GetType() != ColorSource::Type::kColor) {
530525
auto color_text_contents = std::make_shared<ColorSourceTextContents>();

impeller/aiks/canvas.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ class Canvas {
162162
std::unique_ptr<EntityPass> base_pass_;
163163
EntityPass* current_pass_ = nullptr;
164164
std::deque<CanvasStackEntry> xformation_stack_;
165-
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
166165
std::optional<Rect> initial_cull_rect_;
167166

168167
void Initialize(std::optional<Rect> cull_rect);

impeller/display_list/dl_dispatcher.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,10 +1104,9 @@ void DlDispatcher::drawDisplayList(
11041104
void DlDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
11051105
SkScalar x,
11061106
SkScalar y) {
1107-
Scalar scale = canvas_.GetCurrentTransformation().GetMaxBasisLengthXY();
1108-
canvas_.DrawTextFrame(TextFrameFromTextBlob(blob, scale), //
1109-
impeller::Point{x, y}, //
1110-
paint_ //
1107+
canvas_.DrawTextFrame(TextFrameFromTextBlob(blob), //
1108+
impeller::Point{x, y}, //
1109+
paint_ //
11111110
);
11121111
}
11131112

impeller/entity/contents/color_source_text_contents.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "impeller/entity/contents/color_source_text_contents.h"
66

7+
#include "color_source_text_contents.h"
78
#include "impeller/entity/contents/content_context.h"
89
#include "impeller/entity/contents/texture_contents.h"
910
#include "impeller/renderer/render_pass.h"
@@ -33,6 +34,12 @@ void ColorSourceTextContents::SetTextPosition(Point position) {
3334
position_ = position;
3435
}
3536

37+
void ColorSourceTextContents::PopulateGlyphAtlas(
38+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
39+
Scalar scale) const {
40+
text_contents_->PopulateGlyphAtlas(lazy_glyph_atlas, scale);
41+
}
42+
3643
bool ColorSourceTextContents::Render(const ContentContext& renderer,
3744
const Entity& entity,
3845
RenderPass& pass) const {

impeller/entity/contents/color_source_text_contents.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class ColorSourceTextContents final : public Contents {
3232
// |Contents|
3333
std::optional<Rect> GetCoverage(const Entity& entity) const override;
3434

35+
// |Contents|
36+
void PopulateGlyphAtlas(
37+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
38+
Scalar scale) const override;
39+
3540
// |Contents|
3641
bool Render(const ContentContext& renderer,
3742
const Entity& entity,

impeller/entity/contents/content_context.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,18 @@ class ContentContext {
696696
const SubpassCallback& subpass_callback,
697697
bool msaa_enabled = true) const;
698698

699+
void SetLazyGlyphAtlas(
700+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas) {
701+
lazy_glyph_atlas_ = lazy_glyph_atlas;
702+
}
703+
704+
std::shared_ptr<LazyGlyphAtlas> GetLazyGlyphAtlas() const {
705+
return lazy_glyph_atlas_;
706+
}
707+
699708
private:
700709
std::shared_ptr<Context> context_;
710+
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
701711

702712
template <class T>
703713
using Variants = std::unordered_map<ContentContextOptions,

impeller/entity/contents/contents.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "impeller/geometry/color.h"
1515
#include "impeller/geometry/rect.h"
1616
#include "impeller/renderer/snapshot.h"
17+
#include "impeller/typographer/lazy_glyph_atlas.h"
1718

1819
namespace impeller {
1920

@@ -49,6 +50,11 @@ class Contents {
4950

5051
virtual ~Contents();
5152

53+
/// @brief Add any text data to the specified lazy atlas.
54+
virtual void PopulateGlyphAtlas(
55+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
56+
Scalar scale) const {}
57+
5258
virtual bool Render(const ContentContext& renderer,
5359
const Entity& entity,
5460
RenderPass& pass) const = 0;

impeller/entity/contents/text_contents.cc

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,15 @@ void TextContents::SetTextFrame(const TextFrame& frame) {
2929
frame_ = frame;
3030
}
3131

32-
void TextContents::SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas) {
33-
lazy_atlas_ = std::move(atlas);
34-
}
35-
3632
std::shared_ptr<GlyphAtlas> TextContents::ResolveAtlas(
3733
GlyphAtlas::Type type,
34+
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
3835
std::shared_ptr<GlyphAtlasContext> atlas_context,
3936
std::shared_ptr<Context> context) const {
40-
FML_DCHECK(lazy_atlas_);
41-
if (lazy_atlas_) {
42-
return lazy_atlas_->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
43-
std::move(context));
37+
FML_DCHECK(lazy_atlas);
38+
if (lazy_atlas) {
39+
return lazy_atlas->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
40+
std::move(context));
4441
}
4542

4643
return nullptr;
@@ -78,14 +75,43 @@ std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
7875
return bounds->TransformBounds(entity.GetTransformation());
7976
}
8077

81-
static bool CommonRender(const ContentContext& renderer,
82-
const Entity& entity,
83-
RenderPass& pass,
84-
const Color& color,
85-
const TextFrame& frame,
86-
Vector2 offset,
87-
const std::shared_ptr<GlyphAtlas>& atlas,
88-
Command& cmd) {
78+
void TextContents::PopulateGlyphAtlas(
79+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
80+
Scalar scale) const {
81+
lazy_glyph_atlas->AddTextFrame(frame_, scale);
82+
}
83+
84+
bool TextContents::Render(const ContentContext& renderer,
85+
const Entity& entity,
86+
RenderPass& pass) const {
87+
auto color = GetColor();
88+
if (color.IsTransparent()) {
89+
return true;
90+
}
91+
92+
auto type = frame_.GetAtlasType();
93+
auto scale = entity.DeriveTextScale();
94+
auto atlas =
95+
ResolveAtlas(type, renderer.GetLazyGlyphAtlas(),
96+
renderer.GetGlyphAtlasContext(type), renderer.GetContext());
97+
98+
if (!atlas || !atlas->IsValid()) {
99+
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
100+
return false;
101+
}
102+
103+
// Information shared by all glyph draw calls.
104+
Command cmd;
105+
cmd.label = "TextFrame";
106+
auto opts = OptionsFromPassAndEntity(pass, entity);
107+
opts.primitive_type = PrimitiveType::kTriangle;
108+
if (type == GlyphAtlas::Type::kAlphaBitmap) {
109+
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
110+
} else {
111+
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
112+
}
113+
cmd.stencil_reference = entity.GetStencilDepth();
114+
89115
using VS = GlyphAtlasPipeline::VertexShader;
90116
using FS = GlyphAtlasPipeline::FragmentShader;
91117

@@ -95,7 +121,7 @@ static bool CommonRender(const ContentContext& renderer,
95121
frame_info.atlas_size =
96122
Vector2{static_cast<Scalar>(atlas->GetTexture()->GetSize().width),
97123
static_cast<Scalar>(atlas->GetTexture()->GetSize().height)};
98-
frame_info.offset = offset;
124+
frame_info.offset = offset_;
99125
frame_info.is_translation_scale =
100126
entity.GetTransformation().IsTranslationScaleOnly();
101127
frame_info.entity_transform = entity.GetTransformation();
@@ -141,7 +167,7 @@ static bool CommonRender(const ContentContext& renderer,
141167

142168
auto& host_buffer = pass.GetTransientsBuffer();
143169
size_t vertex_count = 0;
144-
for (const auto& run : frame.GetRuns()) {
170+
for (const auto& run : frame_.GetRuns()) {
145171
vertex_count += run.GetGlyphPositions().size();
146172
}
147173
vertex_count *= 6;
@@ -151,10 +177,10 @@ static bool CommonRender(const ContentContext& renderer,
151177
[&](uint8_t* contents) {
152178
VS::PerVertexData vtx;
153179
size_t vertex_offset = 0;
154-
for (const auto& run : frame.GetRuns()) {
180+
for (const auto& run : frame_.GetRuns()) {
155181
const Font& font = run.GetFont();
156182
for (const auto& glyph_position : run.GetGlyphPositions()) {
157-
FontGlyphPair font_glyph_pair{font, glyph_position.glyph};
183+
FontGlyphPair font_glyph_pair{font, glyph_position.glyph, scale};
158184
auto maybe_atlas_glyph_bounds =
159185
atlas->FindFontGlyphBounds(font_glyph_pair);
160186
if (!maybe_atlas_glyph_bounds.has_value()) {
@@ -191,37 +217,4 @@ static bool CommonRender(const ContentContext& renderer,
191217
return pass.AddCommand(cmd);
192218
}
193219

194-
bool TextContents::Render(const ContentContext& renderer,
195-
const Entity& entity,
196-
RenderPass& pass) const {
197-
auto color = GetColor();
198-
if (color.IsTransparent()) {
199-
return true;
200-
}
201-
202-
auto type = frame_.GetAtlasType();
203-
auto atlas = ResolveAtlas(type, renderer.GetGlyphAtlasContext(type),
204-
renderer.GetContext());
205-
206-
if (!atlas || !atlas->IsValid()) {
207-
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
208-
return false;
209-
}
210-
211-
// Information shared by all glyph draw calls.
212-
Command cmd;
213-
cmd.label = "TextFrame";
214-
auto opts = OptionsFromPassAndEntity(pass, entity);
215-
opts.primitive_type = PrimitiveType::kTriangle;
216-
if (type == GlyphAtlas::Type::kAlphaBitmap) {
217-
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
218-
} else {
219-
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
220-
}
221-
cmd.stencil_reference = entity.GetStencilDepth();
222-
223-
return CommonRender(renderer, entity, pass, color, frame_, offset_, atlas,
224-
cmd);
225-
}
226-
227220
} // namespace impeller

impeller/entity/contents/text_contents.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ class TextContents final : public Contents {
2828

2929
void SetTextFrame(const TextFrame& frame);
3030

31-
void SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas);
32-
3331
void SetColor(Color color);
3432

3533
Color GetColor() const;
3634

35+
// |Contents|
3736
bool CanInheritOpacity(const Entity& entity) const override;
3837

38+
// |Contents|
3939
void SetInheritedOpacity(Scalar opacity) override;
4040

4141
void SetOffset(Vector2 offset);
@@ -45,6 +45,11 @@ class TextContents final : public Contents {
4545
// |Contents|
4646
std::optional<Rect> GetCoverage(const Entity& entity) const override;
4747

48+
// |Contents|
49+
void PopulateGlyphAtlas(
50+
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
51+
Scalar scale) const override;
52+
4853
// |Contents|
4954
bool Render(const ContentContext& renderer,
5055
const Entity& entity,
@@ -54,11 +59,11 @@ class TextContents final : public Contents {
5459
TextFrame frame_;
5560
Color color_;
5661
Scalar inherited_opacity_ = 1.0;
57-
mutable std::shared_ptr<LazyGlyphAtlas> lazy_atlas_;
5862
Vector2 offset_;
5963

6064
std::shared_ptr<GlyphAtlas> ResolveAtlas(
6165
GlyphAtlas::Type type,
66+
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
6267
std::shared_ptr<GlyphAtlasContext> atlas_context,
6368
std::shared_ptr<Context> context) const;
6469

impeller/entity/entity.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,8 @@ bool Entity::Render(const ContentContext& renderer,
166166
return contents_->Render(renderer, *this, parent_pass);
167167
}
168168

169+
Scalar Entity::DeriveTextScale() const {
170+
return GetTransformation().GetMaxBasisLengthXY();
171+
}
172+
169173
} // namespace impeller

impeller/entity/entity.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class Entity {
9494

9595
std::optional<Color> AsBackgroundColor(ISize target_size) const;
9696

97+
Scalar DeriveTextScale() const;
98+
9799
private:
98100
Matrix transformation_;
99101
std::shared_ptr<Contents> contents_;

0 commit comments

Comments
 (0)