1818
1919#include < algorithm>
2020#include < numeric>
21+ #include " fml/logging.h"
2122
2223namespace txt {
2324
@@ -45,27 +46,29 @@ txt::FontStyle GetTxtFontStyle(SkFontStyle::Slant font_slant) {
4546
4647class DisplayListParagraphPainter : public skt ::ParagraphPainter {
4748 public:
49+ // ----------------------------------------------------------------------------
50+ // / @brief Creates a |skt::ParagraphPainter| that draws to DisplayList.
51+ // /
52+ // / @param builder The display list builder.
53+ // / @param[in] dl_paints The paints referenced by ID in the `drawX` methods.
54+ // / @param[in] draw_path_effect If true, draw path effects directly by
55+ // / drawing multiple lines instead of providing
56+ // a path effect to the paint.
57+ // /
58+ // / @note Impeller does not (and will not) support path effects, but the
59+ // / Skia backend does. That means that if we want to draw dashed
60+ // / and dotted lines, we need to draw them directly using the
61+ // / `drawLine` API instead of using a path effect.
62+ // /
63+ // / See https://github.com/flutter/flutter/issues/126673. It
64+ // / probably makes sense to eventually make this a compile-time
65+ // / decision (i.e. with `#ifdef`) instead of a runtime option.
4866 DisplayListParagraphPainter (DisplayListBuilder* builder,
49- const std::vector<DlPaint>& dl_paints)
50- : builder_(builder), dl_paints_(dl_paints) {}
51-
52- DlPaint toDlPaint (const DecorationStyle& decor_style,
53- DlDrawStyle draw_style = DlDrawStyle::kStroke ) {
54- DlPaint paint;
55- paint.setDrawStyle (draw_style);
56- paint.setAntiAlias (true );
57- paint.setColor (decor_style.getColor ());
58- paint.setStrokeWidth (decor_style.getStrokeWidth ());
59- std::optional<DashPathEffect> dash_path_effect =
60- decor_style.getDashPathEffect ();
61- if (dash_path_effect) {
62- std::array<SkScalar, 2 > intervals{dash_path_effect->fOnLength ,
63- dash_path_effect->fOffLength };
64- paint.setPathEffect (
65- DlDashPathEffect::Make (intervals.data (), intervals.size (), 0 ));
66- }
67- return paint;
68- }
67+ const std::vector<DlPaint>& dl_paints,
68+ bool draw_path_effect)
69+ : builder_(builder),
70+ dl_paints_ (dl_paints),
71+ draw_path_effect_(draw_path_effect) {}
6972
7073 void drawTextBlob (const sk_sp<SkTextBlob>& blob,
7174 SkScalar x,
@@ -118,8 +121,25 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
118121 SkScalar x1,
119122 SkScalar y1,
120123 const DecorationStyle& decor_style) override {
121- builder_->DrawLine (SkPoint::Make (x0, y0), SkPoint::Make (x1, y1),
122- toDlPaint (decor_style));
124+ // We only support horizontal lines.
125+ FML_DCHECK (y0 == y1);
126+
127+ // This function is called for both solid and dashed lines. If we're drawing
128+ // a dashed line, and we're using the Impeller backend, then we need to draw
129+ // the line directly using the `drawLine` API instead of using a path effect
130+ // (because Impeller does not support path effects).
131+ auto dash_path_effect = decor_style.getDashPathEffect ();
132+ if (draw_path_effect_ && dash_path_effect) {
133+ auto path = dashedLine (x0, x1, y0, *dash_path_effect);
134+ builder_->DrawPath (path, toDlPaint (decor_style));
135+ return ;
136+ }
137+
138+ auto paint = toDlPaint (decor_style);
139+ if (dash_path_effect) {
140+ setPathEffect (paint, *dash_path_effect);
141+ }
142+ builder_->DrawLine (SkPoint::Make (x0, y0), SkPoint::Make (x1, y1), paint);
123143 }
124144
125145 void clipRect (const SkRect& rect) override {
@@ -135,15 +155,64 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter {
135155 void restore () override { builder_->Restore (); }
136156
137157 private:
158+ SkPath dashedLine (SkScalar x0,
159+ SkScalar x1,
160+ SkScalar y0,
161+ const DashPathEffect& dash_path_effect) {
162+ auto dx = 0.0 ;
163+ auto path = SkPath ();
164+ auto on = true ;
165+ auto length = x1 - x0;
166+ while (dx < length) {
167+ if (on) {
168+ // Draw the on part of the dash.
169+ path.moveTo (x0 + dx, y0);
170+ dx += dash_path_effect.fOnLength ;
171+ path.lineTo (x0 + dx, y0);
172+ } else {
173+ // Skip the off part of the dash.
174+ dx += dash_path_effect.fOffLength ;
175+ }
176+ on = !on;
177+ }
178+
179+ path.close ();
180+ return path;
181+ }
182+
183+ DlPaint toDlPaint (const DecorationStyle& decor_style,
184+ DlDrawStyle draw_style = DlDrawStyle::kStroke ) {
185+ DlPaint paint;
186+ paint.setDrawStyle (draw_style);
187+ paint.setAntiAlias (true );
188+ paint.setColor (decor_style.getColor ());
189+ paint.setStrokeWidth (decor_style.getStrokeWidth ());
190+ return paint;
191+ }
192+
193+ void setPathEffect (DlPaint& paint, const DashPathEffect& dash_path_effect) {
194+ // Impeller does not support path effects, so we should never be setting.
195+ FML_DCHECK (!draw_path_effect_);
196+
197+ std::array<SkScalar, 2 > intervals{dash_path_effect.fOnLength ,
198+ dash_path_effect.fOffLength };
199+ auto effect = DlDashPathEffect::Make (intervals.data (), intervals.size (), 0 );
200+ paint.setPathEffect (effect);
201+ }
202+
138203 DisplayListBuilder* builder_;
139204 const std::vector<DlPaint>& dl_paints_;
205+ bool draw_path_effect_;
140206};
141207
142208} // anonymous namespace
143209
144210ParagraphSkia::ParagraphSkia (std::unique_ptr<skt::Paragraph> paragraph,
145- std::vector<flutter::DlPaint>&& dl_paints)
146- : paragraph_(std::move(paragraph)), dl_paints_(dl_paints) {}
211+ std::vector<flutter::DlPaint>&& dl_paints,
212+ bool impeller_enabled)
213+ : paragraph_(std::move(paragraph)),
214+ dl_paints_(dl_paints),
215+ impeller_enabled_(impeller_enabled) {}
147216
148217double ParagraphSkia::GetMaxWidth () {
149218 return SkScalarToDouble (paragraph_->getMaxWidth ());
@@ -223,7 +292,7 @@ void ParagraphSkia::Layout(double width) {
223292}
224293
225294bool ParagraphSkia::Paint (DisplayListBuilder* builder, double x, double y) {
226- DisplayListParagraphPainter painter (builder, dl_paints_);
295+ DisplayListParagraphPainter painter (builder, dl_paints_, impeller_enabled_ );
227296 paragraph_->paint (&painter, x, y);
228297 return true ;
229298}
0 commit comments