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

Commit 8e5f86b

Browse files
author
Jonah Williams
authored
[Impeller] Use Skia software renderer to draw stroked text. (#53198)
We need to match the rounding of glyphs to fix flutter/flutter#138670 . We also don't have sufficient AA quality with 4x MSAA for high quality strokes. Fixes flutter/flutter#138670 Fixes flutter/flutter#136688
1 parent 33bd4b4 commit 8e5f86b

23 files changed

+324
-114
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "impeller/aiks/color_filter.h"
1919
#include "impeller/aiks/image.h"
2020
#include "impeller/aiks/image_filter.h"
21-
#include "impeller/aiks/paint_pass_delegate.h"
2221
#include "impeller/aiks/testing/context_spy.h"
2322
#include "impeller/core/device_buffer.h"
2423
#include "impeller/entity/contents/solid_color_contents.h"
@@ -632,6 +631,7 @@ TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
632631
}
633632

634633
struct TextRenderOptions {
634+
bool stroke = false;
635635
Scalar font_size = 50;
636636
Color color = Color::Yellow();
637637
Point position = Vector2(100, 200);
@@ -671,6 +671,9 @@ bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
671671
Paint text_paint;
672672
text_paint.color = options.color;
673673
text_paint.mask_blur_descriptor = options.mask_blur_descriptor;
674+
text_paint.stroke_width = 1;
675+
text_paint.style =
676+
options.stroke ? Paint::Style::kStroke : Paint::Style::kFill;
674677
canvas.DrawTextFrame(frame, options.position, text_paint);
675678
return true;
676679
}
@@ -714,6 +717,18 @@ TEST_P(AiksTest, CanRenderTextFrame) {
714717
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
715718
}
716719

720+
TEST_P(AiksTest, CanRenderStrokedTextFrame) {
721+
Canvas canvas;
722+
canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
723+
ASSERT_TRUE(RenderTextInCanvasSkia(
724+
GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?",
725+
"Roboto-Regular.ttf",
726+
{
727+
.stroke = true,
728+
}));
729+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
730+
}
731+
717732
TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
718733
Canvas canvas;
719734
canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});

impeller/aiks/canvas.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,9 +889,16 @@ void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
889889

890890
auto text_contents = std::make_shared<TextContents>();
891891
text_contents->SetTextFrame(text_frame);
892-
text_contents->SetColor(paint.color);
893892
text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
894893
text_contents->SetOffset(position);
894+
text_contents->SetColor(paint.color);
895+
text_contents->SetTextProperties(paint.color, //
896+
paint.style == Paint::Style::kStroke, //
897+
paint.stroke_width, //
898+
paint.stroke_cap, //
899+
paint.stroke_join, //
900+
paint.stroke_miter //
901+
);
895902

896903
entity.SetTransform(GetCurrentTransform() *
897904
Matrix::MakeTranslation(position));

impeller/aiks/experimental_canvas.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,16 @@ void ExperimentalCanvas::DrawTextFrame(
388388

389389
auto text_contents = std::make_shared<TextContents>();
390390
text_contents->SetTextFrame(text_frame);
391-
text_contents->SetColor(paint.color);
392391
text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
393392
text_contents->SetScale(GetCurrentTransform().GetMaxBasisLengthXY());
393+
text_contents->SetColor(paint.color);
394+
text_contents->SetTextProperties(paint.color, //
395+
paint.style == Paint::Style::kStroke, //
396+
paint.stroke_width, //
397+
paint.stroke_cap, //
398+
paint.stroke_join, //
399+
paint.stroke_miter //
400+
);
394401

395402
entity.SetTransform(GetCurrentTransform() *
396403
Matrix::MakeTranslation(position));

impeller/display_list/dl_dispatcher.cc

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "impeller/geometry/path_builder.h"
2929
#include "impeller/geometry/scalar.h"
3030
#include "impeller/geometry/sigma.h"
31+
#include "impeller/typographer/font_glyph_pair.h"
3132

3233
#if IMPELLER_ENABLE_3D
3334
#include "impeller/entity/contents/scene_contents.h"
@@ -1266,8 +1267,22 @@ void TextFrameDispatcher::drawTextFrame(
12661267
const std::shared_ptr<impeller::TextFrame>& text_frame,
12671268
SkScalar x,
12681269
SkScalar y) {
1269-
renderer_.GetLazyGlyphAtlas()->AddTextFrame(
1270-
*text_frame, matrix_.GetMaxBasisLengthXY(), Point(x, y));
1270+
GlyphProperties properties;
1271+
if (paint_.style == Paint::Style::kStroke) {
1272+
properties.stroke = true;
1273+
properties.stroke_cap = paint_.stroke_cap;
1274+
properties.stroke_join = paint_.stroke_join;
1275+
properties.stroke_miter = paint_.stroke_miter;
1276+
properties.stroke_width = paint_.stroke_width;
1277+
}
1278+
if (text_frame->HasColor()) {
1279+
properties.color = paint_.color;
1280+
}
1281+
renderer_.GetLazyGlyphAtlas()->AddTextFrame(*text_frame, //
1282+
matrix_.GetMaxBasisLengthXY(), //
1283+
Point(x, y), //
1284+
properties //
1285+
);
12711286
}
12721287

12731288
void TextFrameDispatcher::drawDisplayList(
@@ -1280,4 +1295,59 @@ void TextFrameDispatcher::drawDisplayList(
12801295
FML_DCHECK(stack_depth == stack_.size());
12811296
}
12821297

1298+
// |flutter::DlOpReceiver|
1299+
void TextFrameDispatcher::setDrawStyle(flutter::DlDrawStyle style) {
1300+
paint_.style = ToStyle(style);
1301+
}
1302+
1303+
// |flutter::DlOpReceiver|
1304+
void TextFrameDispatcher::setColor(flutter::DlColor color) {
1305+
paint_.color = {
1306+
color.getRedF(),
1307+
color.getGreenF(),
1308+
color.getBlueF(),
1309+
color.getAlphaF(),
1310+
};
1311+
}
1312+
1313+
// |flutter::DlOpReceiver|
1314+
void TextFrameDispatcher::setStrokeWidth(SkScalar width) {
1315+
paint_.stroke_width = width;
1316+
}
1317+
1318+
// |flutter::DlOpReceiver|
1319+
void TextFrameDispatcher::setStrokeMiter(SkScalar limit) {
1320+
paint_.stroke_miter = limit;
1321+
}
1322+
1323+
// |flutter::DlOpReceiver|
1324+
void TextFrameDispatcher::setStrokeCap(flutter::DlStrokeCap cap) {
1325+
switch (cap) {
1326+
case flutter::DlStrokeCap::kButt:
1327+
paint_.stroke_cap = Cap::kButt;
1328+
break;
1329+
case flutter::DlStrokeCap::kRound:
1330+
paint_.stroke_cap = Cap::kRound;
1331+
break;
1332+
case flutter::DlStrokeCap::kSquare:
1333+
paint_.stroke_cap = Cap::kSquare;
1334+
break;
1335+
}
1336+
}
1337+
1338+
// |flutter::DlOpReceiver|
1339+
void TextFrameDispatcher::setStrokeJoin(flutter::DlStrokeJoin join) {
1340+
switch (join) {
1341+
case flutter::DlStrokeJoin::kMiter:
1342+
paint_.stroke_join = Join::kMiter;
1343+
break;
1344+
case flutter::DlStrokeJoin::kRound:
1345+
paint_.stroke_join = Join::kRound;
1346+
break;
1347+
case flutter::DlStrokeJoin::kBevel:
1348+
paint_.stroke_join = Join::kBevel;
1349+
break;
1350+
}
1351+
}
1352+
12831353
} // namespace impeller

impeller/display_list/dl_dispatcher.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,29 @@ class TextFrameDispatcher : public flutter::IgnoreAttributeDispatchHelper,
368368
void drawDisplayList(const sk_sp<flutter::DisplayList> display_list,
369369
SkScalar opacity) override;
370370

371+
// |flutter::DlOpReceiver|
372+
void setDrawStyle(flutter::DlDrawStyle style) override;
373+
374+
// |flutter::DlOpReceiver|
375+
void setColor(flutter::DlColor color) override;
376+
377+
// |flutter::DlOpReceiver|
378+
void setStrokeWidth(SkScalar width) override;
379+
380+
// |flutter::DlOpReceiver|
381+
void setStrokeMiter(SkScalar limit) override;
382+
383+
// |flutter::DlOpReceiver|
384+
void setStrokeCap(flutter::DlStrokeCap cap) override;
385+
386+
// |flutter::DlOpReceiver|
387+
void setStrokeJoin(flutter::DlStrokeJoin join) override;
388+
371389
private:
372390
const ContentContext& renderer_;
373391
Matrix matrix_;
374392
std::vector<Matrix> stack_;
393+
Paint paint_;
375394
};
376395

377396
} // namespace impeller

impeller/entity/contents/text_contents.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,30 @@ std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
6666
void TextContents::PopulateGlyphAtlas(
6767
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
6868
Scalar scale) {
69-
lazy_glyph_atlas->AddTextFrame(*frame_, scale, offset_);
69+
lazy_glyph_atlas->AddTextFrame(*frame_, scale, offset_, properties_);
7070
scale_ = scale;
7171
}
7272

73+
void TextContents::SetTextProperties(Color color,
74+
bool stroke,
75+
Scalar stroke_width,
76+
Cap stroke_cap,
77+
Join stroke_join,
78+
Scalar stroke_miter) {
79+
if (frame_->HasColor()) {
80+
// Alpha is always applied when rendering, remove it here so
81+
// we do not double-apply the alpha.
82+
properties_.color = color.WithAlpha(1.0);
83+
}
84+
if (stroke) {
85+
properties_.stroke = true;
86+
properties_.stroke_width = stroke_width;
87+
properties_.stroke_cap = stroke_cap;
88+
properties_.stroke_join = stroke_join;
89+
properties_.stroke_miter = stroke_miter;
90+
}
91+
}
92+
7393
bool TextContents::Render(const ContentContext& renderer,
7494
const Entity& entity,
7595
RenderPass& pass) const {
@@ -171,7 +191,7 @@ bool TextContents::Render(const ContentContext& renderer,
171191
Scalar rounded_scale = TextFrame::RoundScaledFontSize(
172192
scale_, font.GetMetrics().point_size);
173193
const FontGlyphAtlas* font_atlas =
174-
atlas->GetFontGlyphAtlas(font, rounded_scale, frame_->GetColor());
194+
atlas->GetFontGlyphAtlas(font, rounded_scale);
175195
if (!font_atlas) {
176196
VALIDATION_LOG << "Could not find font in the atlas.";
177197
continue;
@@ -203,7 +223,7 @@ bool TextContents::Render(const ContentContext& renderer,
203223
glyph_position, font.GetAxisAlignment(), offset_, scale_);
204224
std::optional<std::pair<Rect, Rect>> maybe_atlas_glyph_bounds =
205225
font_atlas->FindGlyphBounds(
206-
SubpixelGlyph{glyph_position.glyph, subpixel});
226+
SubpixelGlyph{glyph_position.glyph, subpixel, properties_});
207227
if (!maybe_atlas_glyph_bounds.has_value()) {
208228
VALIDATION_LOG << "Could not find glyph position in the atlas.";
209229
continue;

impeller/entity/contents/text_contents.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include "impeller/entity/contents/contents.h"
1111
#include "impeller/geometry/color.h"
12-
#include "impeller/typographer/glyph_atlas.h"
12+
#include "impeller/typographer/font_glyph_pair.h"
1313
#include "impeller/typographer/text_frame.h"
1414

1515
namespace impeller {
@@ -33,6 +33,14 @@ class TextContents final : public Contents {
3333
/// This is used to ensure that mask blurs work correctly on emoji.
3434
void SetForceTextColor(bool value);
3535

36+
/// Must be set after text frame.
37+
void SetTextProperties(Color color,
38+
bool stroke,
39+
Scalar stroke_width,
40+
Cap stroke_cap,
41+
Join stroke_join,
42+
Scalar stroke_miter);
43+
3644
Color GetColor() const;
3745

3846
// |Contents|
@@ -64,10 +72,11 @@ class TextContents final : public Contents {
6472
private:
6573
std::shared_ptr<TextFrame> frame_;
6674
Scalar scale_ = 1.0;
67-
Color color_;
6875
Scalar inherited_opacity_ = 1.0;
6976
Vector2 offset_;
7077
bool force_text_color_ = false;
78+
Color color_;
79+
GlyphProperties properties_;
7180

7281
TextContents(const TextContents&) = delete;
7382

impeller/typographer/backends/skia/text_frame_skia.cc

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
#include <vector>
88

9-
#include "display_list/dl_color.h"
109
#include "flutter/fml/logging.h"
1110
#include "impeller/typographer/backends/skia/typeface_skia.h"
1211
#include "impeller/typographer/font.h"
@@ -39,15 +38,6 @@ static AxisAlignment ToAxisAligment(SkAxisAlignment aligment) {
3938
FML_UNREACHABLE();
4039
}
4140

42-
static Color ToColor(const flutter::DlColor& color) {
43-
return {
44-
static_cast<Scalar>(color.getRedF()), //
45-
static_cast<Scalar>(color.getGreenF()), //
46-
static_cast<Scalar>(color.getBlueF()), //
47-
static_cast<Scalar>(color.getAlphaF()) //
48-
};
49-
}
50-
5141
static Font ToFont(const SkTextBlobRunIterator& run, AxisAlignment alignment) {
5242
auto& font = run.font();
5343
auto typeface = std::make_shared<TypefaceSkia>(font.refTypeface());
@@ -69,10 +59,8 @@ static Rect ToRect(const SkRect& rect) {
6959
}
7060

7161
std::shared_ptr<TextFrame> MakeTextFrameFromTextBlobSkia(
72-
const sk_sp<SkTextBlob>& blob,
73-
flutter::DlColor dl_color) {
62+
const sk_sp<SkTextBlob>& blob) {
7463
bool has_color = false;
75-
Color color = ToColor(dl_color);
7664
std::vector<TextRun> runs;
7765
for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) {
7866
// TODO(jonahwilliams): ask Skia for a public API to look this up.
@@ -114,8 +102,7 @@ std::shared_ptr<TextFrame> MakeTextFrameFromTextBlobSkia(
114102
continue;
115103
}
116104
}
117-
return std::make_shared<TextFrame>(runs, ToRect(blob->bounds()), has_color,
118-
has_color ? color : Color::Black());
105+
return std::make_shared<TextFrame>(runs, ToRect(blob->bounds()), has_color);
119106
}
120107

121108
} // namespace impeller

impeller/typographer/backends/skia/text_frame_skia.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@
55
#ifndef FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_SKIA_TEXT_FRAME_SKIA_H_
66
#define FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_SKIA_TEXT_FRAME_SKIA_H_
77

8-
#include "display_list/dl_color.h"
98
#include "impeller/typographer/text_frame.h"
109

1110
#include "third_party/skia/include/core/SkTextBlob.h"
1211

1312
namespace impeller {
1413

1514
std::shared_ptr<impeller::TextFrame> MakeTextFrameFromTextBlobSkia(
16-
const sk_sp<SkTextBlob>& blob,
17-
flutter::DlColor color = flutter::DlColor::kBlack());
15+
const sk_sp<SkTextBlob>& blob);
1816

1917
} // namespace impeller
2018

0 commit comments

Comments
 (0)