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

[Impeller] Implement TextDecoration (i.e. dashed lines) #45041

Merged
merged 21 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/ui/text/paragraph_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,9 @@ ParagraphBuilder::ParagraphBuilder(
->client()
->GetFontCollection();

auto impeller_enabled = UIDartState::Current()->IsImpellerEnabled();
m_paragraphBuilder = txt::ParagraphBuilder::CreateSkiaBuilder(
style, font_collection.GetFontCollection());
style, font_collection.GetFontCollection(), impeller_enabled);
}

ParagraphBuilder::~ParagraphBuilder() = default;
Expand Down
4 changes: 4 additions & 0 deletions third_party/txt/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ if (enable_unittests) {

sources = [
"tests/font_collection_tests.cc",
"tests/paragraph_unittests.cc",
"tests/txt_run_all_unittests.cc",
]

Expand All @@ -154,7 +155,10 @@ if (enable_unittests) {
":txt",
":txt_fixtures",
"//flutter/fml",
"//flutter/runtime:test_font",
"//flutter/testing:skia",
"//flutter/testing:testing_lib",
"//third_party/skia/modules/skparagraph:skparagraph",
]

# This is needed for //third_party/googletest for linking zircon symbols.
Expand Down
9 changes: 5 additions & 4 deletions third_party/txt/src/skia/paragraph_builder_skia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ SkFontStyle MakeSkFontStyle(txt::FontWeight font_weight,

ParagraphBuilderSkia::ParagraphBuilderSkia(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection)
: base_style_(style.GetTextStyle()) {
std::shared_ptr<FontCollection> font_collection,
const bool impeller_enabled)
: base_style_(style.GetTextStyle()), impeller_enabled_(impeller_enabled) {
builder_ = skt::ParagraphBuilder::make(
TxtToSkia(style), font_collection->CreateSktFontCollection());
}
Expand Down Expand Up @@ -85,8 +86,8 @@ void ParagraphBuilderSkia::AddPlaceholder(PlaceholderRun& span) {
}

std::unique_ptr<Paragraph> ParagraphBuilderSkia::Build() {
return std::make_unique<ParagraphSkia>(builder_->Build(),
std::move(dl_paints_));
return std::make_unique<ParagraphSkia>(
builder_->Build(), std::move(dl_paints_), impeller_enabled_);
}

skt::ParagraphPainter::PaintID ParagraphBuilderSkia::CreatePaintID(
Expand Down
17 changes: 15 additions & 2 deletions third_party/txt/src/skia/paragraph_builder_skia.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@

namespace txt {

// Implementation of ParagraphBuilder based on Skia's text layout module.
//------------------------------------------------------------------------------
/// @brief ParagraphBuilder implementation using Skia's text layout module.
///
/// @note Despite the suffix "Skia", this class is not specific to Skia
/// and is also used with the Impeller backend.
class ParagraphBuilderSkia : public ParagraphBuilder {
public:
ParagraphBuilderSkia(const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
std::shared_ptr<FontCollection> font_collection,
const bool impeller_enabled);

virtual ~ParagraphBuilderSkia();

Expand All @@ -47,6 +52,14 @@ class ParagraphBuilderSkia : public ParagraphBuilder {

std::shared_ptr<skia::textlayout::ParagraphBuilder> builder_;
TextStyle base_style_;

/// @brief Whether Impeller is enabled in the runtime.
///
/// @note As of the time of this writing, this is used to draw text
/// decorations (i.e. dashed and dotted lines) directly using the
/// `drawLine` API, because Impeller's path rendering does not
/// support dashed and dotted lines (but Skia's does).
const bool impeller_enabled_;
std::stack<TextStyle> txt_style_stack_;
std::vector<flutter::DlPaint> dl_paints_;
};
Expand Down
119 changes: 94 additions & 25 deletions third_party/txt/src/skia/paragraph_skia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <algorithm>
#include <numeric>
#include "fml/logging.h"

namespace txt {

Expand Down Expand Up @@ -45,27 +46,29 @@ txt::FontStyle GetTxtFontStyle(SkFontStyle::Slant font_slant) {

class DisplayListParagraphPainter : public skt::ParagraphPainter {
public:
//----------------------------------------------------------------------------
/// @brief Creates a |skt::ParagraphPainter| that draws to DisplayList.
///
/// @param builder The display list builder.
/// @param[in] dl_paints The paints referenced by ID in the `drawX` methods.
/// @param[in] draw_path_effect If true, draw path effects directly by
/// drawing multiple lines instead of providing
// a path effect to the paint.
///
/// @note Impeller does not (and will not) support path effects, but the
/// Skia backend does. That means that if we want to draw dashed
/// and dotted lines, we need to draw them directly using the
/// `drawLine` API instead of using a path effect.
///
/// See https://github.com/flutter/flutter/issues/126673. It
/// probably makes sense to eventually make this a compile-time
/// decision (i.e. with `#ifdef`) instead of a runtime option.
DisplayListParagraphPainter(DisplayListBuilder* builder,
const std::vector<DlPaint>& dl_paints)
: builder_(builder), dl_paints_(dl_paints) {}

DlPaint toDlPaint(const DecorationStyle& decor_style,
DlDrawStyle draw_style = DlDrawStyle::kStroke) {
DlPaint paint;
paint.setDrawStyle(draw_style);
paint.setAntiAlias(true);
paint.setColor(decor_style.getColor());
paint.setStrokeWidth(decor_style.getStrokeWidth());
std::optional<DashPathEffect> dash_path_effect =
decor_style.getDashPathEffect();
if (dash_path_effect) {
std::array<SkScalar, 2> intervals{dash_path_effect->fOnLength,
dash_path_effect->fOffLength};
paint.setPathEffect(
DlDashPathEffect::Make(intervals.data(), intervals.size(), 0));
}
return paint;
}
const std::vector<DlPaint>& dl_paints,
bool draw_path_effect)
: builder_(builder),
dl_paints_(dl_paints),
draw_path_effect_(draw_path_effect) {}

void drawTextBlob(const sk_sp<SkTextBlob>& blob,
SkScalar x,
Expand Down Expand Up @@ -118,8 +121,25 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
SkScalar x1,
SkScalar y1,
const DecorationStyle& decor_style) override {
builder_->DrawLine(SkPoint::Make(x0, y0), SkPoint::Make(x1, y1),
toDlPaint(decor_style));
// We only support horizontal lines.
FML_DCHECK(y0 == y1);

// This function is called for both solid and dashed lines. If we're drawing
// a dashed line, and we're using the Impeller backend, then we need to draw
// the line directly using the `drawLine` API instead of using a path effect
// (because Impeller does not support path effects).
auto dash_path_effect = decor_style.getDashPathEffect();
if (draw_path_effect_ && dash_path_effect) {
auto path = dashedLine(x0, x1, y0, *dash_path_effect);
builder_->DrawPath(path, toDlPaint(decor_style));
return;
}

auto paint = toDlPaint(decor_style);
if (dash_path_effect) {
setPathEffect(paint, *dash_path_effect);
}
builder_->DrawLine(SkPoint::Make(x0, y0), SkPoint::Make(x1, y1), paint);
}

void clipRect(const SkRect& rect) override {
Expand All @@ -135,15 +155,64 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
void restore() override { builder_->Restore(); }

private:
SkPath dashedLine(SkScalar x0,
SkScalar x1,
SkScalar y0,
const DashPathEffect& dash_path_effect) {
auto dx = 0.0;
auto path = SkPath();
auto on = true;
auto length = x1 - x0;
while (dx < length) {
if (on) {
// Draw the on part of the dash.
path.moveTo(x0 + dx, y0);
dx += dash_path_effect.fOnLength;
path.lineTo(x0 + dx, y0);
} else {
// Skip the off part of the dash.
dx += dash_path_effect.fOffLength;
}
on = !on;
}

path.close();
return path;
}

DlPaint toDlPaint(const DecorationStyle& decor_style,
DlDrawStyle draw_style = DlDrawStyle::kStroke) {
DlPaint paint;
paint.setDrawStyle(draw_style);
paint.setAntiAlias(true);
paint.setColor(decor_style.getColor());
paint.setStrokeWidth(decor_style.getStrokeWidth());
return paint;
}

void setPathEffect(DlPaint& paint, const DashPathEffect& dash_path_effect) {
// Impeller does not support path effects, so we should never be setting.
FML_DCHECK(!draw_path_effect_);

std::array<SkScalar, 2> intervals{dash_path_effect.fOnLength,
dash_path_effect.fOffLength};
auto effect = DlDashPathEffect::Make(intervals.data(), intervals.size(), 0);
paint.setPathEffect(effect);
}

DisplayListBuilder* builder_;
const std::vector<DlPaint>& dl_paints_;
bool draw_path_effect_;
};

} // anonymous namespace

ParagraphSkia::ParagraphSkia(std::unique_ptr<skt::Paragraph> paragraph,
std::vector<flutter::DlPaint>&& dl_paints)
: paragraph_(std::move(paragraph)), dl_paints_(dl_paints) {}
std::vector<flutter::DlPaint>&& dl_paints,
bool impeller_enabled)
: paragraph_(std::move(paragraph)),
dl_paints_(dl_paints),
impeller_enabled_(impeller_enabled) {}

double ParagraphSkia::GetMaxWidth() {
return SkScalarToDouble(paragraph_->getMaxWidth());
Expand Down Expand Up @@ -223,7 +292,7 @@ void ParagraphSkia::Layout(double width) {
}

bool ParagraphSkia::Paint(DisplayListBuilder* builder, double x, double y) {
DisplayListParagraphPainter painter(builder, dl_paints_);
DisplayListParagraphPainter painter(builder, dl_paints_, impeller_enabled_);
paragraph_->paint(&painter, x, y);
return true;
}
Expand Down
4 changes: 3 additions & 1 deletion third_party/txt/src/skia/paragraph_skia.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ namespace txt {
class ParagraphSkia : public Paragraph {
public:
ParagraphSkia(std::unique_ptr<skia::textlayout::Paragraph> paragraph,
std::vector<flutter::DlPaint>&& dl_paints);
std::vector<flutter::DlPaint>&& dl_paints,
bool impeller_enabled);

virtual ~ParagraphSkia() = default;

Expand Down Expand Up @@ -75,6 +76,7 @@ class ParagraphSkia : public Paragraph {
std::vector<flutter::DlPaint> dl_paints_;
std::optional<std::vector<LineMetrics>> line_metrics_;
std::vector<TextStyle> line_metrics_styles_;
const bool impeller_enabled_;
};

} // namespace txt
Expand Down
12 changes: 10 additions & 2 deletions third_party/txt/src/txt/paragraph_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@

namespace txt {

//------------------------------------------------------------------------------
/// @brief Creates a |ParagraphBuilder| based on Skia's text layout module.
///
/// @param[in] style The style to use for the paragraph.
/// @param[in] font_collection The font collection to use for the paragraph.
/// @param[in] impeller_enabled Whether Impeller is enabled in the runtime.
std::unique_ptr<ParagraphBuilder> ParagraphBuilder::CreateSkiaBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection) {
return std::make_unique<ParagraphBuilderSkia>(style, font_collection);
std::shared_ptr<FontCollection> font_collection,
const bool impeller_enabled) {
return std::make_unique<ParagraphBuilderSkia>(style, font_collection,
impeller_enabled);
}

} // namespace txt
3 changes: 2 additions & 1 deletion third_party/txt/src/txt/paragraph_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class ParagraphBuilder {
public:
static std::unique_ptr<ParagraphBuilder> CreateSkiaBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
std::shared_ptr<FontCollection> font_collection,
const bool impeller_enabled);

virtual ~ParagraphBuilder() = default;

Expand Down
Loading