Flutter Engine
The Flutter Engine
paragraph_skia.cc
Go to the documentation of this file.
1/*
2 * Copyright 2019 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "paragraph_skia.h"
18
19#include <algorithm>
20#include <numeric>
22#include "fml/logging.h"
25
26namespace txt {
27
28namespace skt = skia::textlayout;
30
31using namespace flutter;
32
33namespace {
34
35// Convert SkFontStyle::Weight values (ranging from 100-900) to txt::FontWeight
36// values (ranging from 0-8).
37txt::FontWeight GetTxtFontWeight(int font_weight) {
38 int txt_weight = (font_weight - 100) / 100;
39 txt_weight = std::clamp(txt_weight, static_cast<int>(txt::FontWeight::w100),
40 static_cast<int>(txt::FontWeight::w900));
41 return static_cast<txt::FontWeight>(txt_weight);
42}
43
44txt::FontStyle GetTxtFontStyle(SkFontStyle::Slant font_slant) {
45 return font_slant == SkFontStyle::Slant::kUpright_Slant
48}
49
50class DisplayListParagraphPainter : public skt::ParagraphPainter {
51 public:
52 //----------------------------------------------------------------------------
53 /// @brief Creates a |skt::ParagraphPainter| that draws to DisplayList.
54 ///
55 /// @param builder The display list builder.
56 /// @param[in] dl_paints The paints referenced by ID in the `drawX` methods.
57 /// @param[in] draw_path_effect If true, draw path effects directly by
58 /// drawing multiple lines instead of providing
59 // a path effect to the paint.
60 ///
61 /// @note Impeller does not (and will not) support path effects, but the
62 /// Skia backend does. That means that if we want to draw dashed
63 /// and dotted lines, we need to draw them directly using the
64 /// `drawLine` API instead of using a path effect.
65 ///
66 /// See https://github.com/flutter/flutter/issues/126673. It
67 /// probably makes sense to eventually make this a compile-time
68 /// decision (i.e. with `#ifdef`) instead of a runtime option.
69 DisplayListParagraphPainter(DisplayListBuilder* builder,
70 const std::vector<DlPaint>& dl_paints,
71 bool impeller_enabled)
72 : builder_(builder),
73 dl_paints_(dl_paints),
74 impeller_enabled_(impeller_enabled) {}
75
76 void drawTextBlob(const sk_sp<SkTextBlob>& blob,
77 SkScalar x,
78 SkScalar y,
79 const SkPaintOrID& paint) override {
80 if (!blob) {
81 return;
82 }
83 size_t paint_id = std::get<PaintID>(paint);
84 FML_DCHECK(paint_id < dl_paints_.size());
85
86#ifdef IMPELLER_SUPPORTS_RENDERING
87 if (impeller_enabled_) {
88 if (ShouldRenderAsPath(dl_paints_[paint_id])) {
90 // If there is no path, this is an emoji and should be drawn as is,
91 // ignoring the color source.
92 if (path.isEmpty()) {
93 builder_->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(blob),
94 x, y, dl_paints_[paint_id]);
95
96 return;
97 }
98
99 auto transformed = path.makeTransform(SkMatrix::Translate(
100 x + blob->bounds().left(), y + blob->bounds().top()));
101 builder_->DrawPath(transformed, dl_paints_[paint_id]);
102 return;
103 }
104 builder_->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(blob), x,
105 y, dl_paints_[paint_id]);
106 return;
107 }
108#endif // IMPELLER_SUPPORTS_RENDERING
109 builder_->DrawTextBlob(blob, x, y, dl_paints_[paint_id]);
110 }
111
112 void drawTextShadow(const sk_sp<SkTextBlob>& blob,
113 SkScalar x,
114 SkScalar y,
116 SkScalar blur_sigma) override {
117 if (!blob) {
118 return;
119 }
121 paint.setColor(DlColor(color));
122 if (blur_sigma > 0.0) {
123 DlBlurMaskFilter filter(DlBlurStyle::kNormal, blur_sigma, false);
124 paint.setMaskFilter(&filter);
125 }
126 if (impeller_enabled_) {
127 builder_->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(blob), x,
128 y, paint);
129 return;
130 }
131 builder_->DrawTextBlob(blob, x, y, paint);
132 }
133
134 void drawRect(const SkRect& rect, const SkPaintOrID& paint) override {
135 size_t paint_id = std::get<PaintID>(paint);
136 FML_DCHECK(paint_id < dl_paints_.size());
137 builder_->DrawRect(rect, dl_paints_[paint_id]);
138 }
139
140 void drawFilledRect(const SkRect& rect,
141 const DecorationStyle& decor_style) override {
142 DlPaint paint = toDlPaint(decor_style, DlDrawStyle::kFill);
143 builder_->DrawRect(rect, paint);
144 }
145
146 void drawPath(const SkPath& path,
147 const DecorationStyle& decor_style) override {
148 builder_->DrawPath(path, toDlPaint(decor_style));
149 }
150
151 void drawLine(SkScalar x0,
152 SkScalar y0,
153 SkScalar x1,
154 SkScalar y1,
155 const DecorationStyle& decor_style) override {
156 auto dash_path_effect = decor_style.getDashPathEffect();
157 auto paint = toDlPaint(decor_style);
158
159 if (dash_path_effect) {
160 builder_->DrawDashedLine(DlPoint(x0, y0), DlPoint(x1, y1),
161 dash_path_effect->fOnLength,
162 dash_path_effect->fOffLength, paint);
163 } else {
164 builder_->DrawLine(SkPoint::Make(x0, y0), SkPoint::Make(x1, y1), paint);
165 }
166 }
167
168 void clipRect(const SkRect& rect) override {
169 builder_->ClipRect(rect, DlCanvas::ClipOp::kIntersect, false);
170 }
171
172 void translate(SkScalar dx, SkScalar dy) override {
173 builder_->Translate(dx, dy);
174 }
175
176 void save() override { builder_->Save(); }
177
178 void restore() override { builder_->Restore(); }
179
180 private:
181 bool ShouldRenderAsPath(const DlPaint& paint) const {
182 FML_DCHECK(impeller_enabled_);
183 // Text with non-trivial color sources should be rendered as a path when
184 // running on Impeller for correctness. These filters rely on having the
185 // glyph coverage, whereas regular text is drawn as rectangular texture
186 // samples.
187 return (paint.getColorSource() && !paint.getColorSource()->asColor());
188 }
189
190 DlPaint toDlPaint(const DecorationStyle& decor_style,
191 DlDrawStyle draw_style = DlDrawStyle::kStroke) {
193 paint.setDrawStyle(draw_style);
194 paint.setAntiAlias(true);
195 paint.setColor(DlColor(decor_style.getColor()));
196 paint.setStrokeWidth(decor_style.getStrokeWidth());
197 return paint;
198 }
199
200 DisplayListBuilder* builder_;
201 const std::vector<DlPaint>& dl_paints_;
202 const bool impeller_enabled_;
203};
204
205} // anonymous namespace
206
207ParagraphSkia::ParagraphSkia(std::unique_ptr<skt::Paragraph> paragraph,
208 std::vector<flutter::DlPaint>&& dl_paints,
209 bool impeller_enabled)
210 : paragraph_(std::move(paragraph)),
211 dl_paints_(dl_paints),
212 impeller_enabled_(impeller_enabled) {}
213
215 return SkScalarToDouble(paragraph_->getMaxWidth());
216}
217
219 return SkScalarToDouble(paragraph_->getHeight());
220}
221
223 return SkScalarToDouble(paragraph_->getLongestLine());
224}
225
226std::vector<LineMetrics>& ParagraphSkia::GetLineMetrics() {
227 if (!line_metrics_) {
228 std::vector<skt::LineMetrics> metrics;
229 paragraph_->getLineMetrics(metrics);
230
231 line_metrics_.emplace();
232 line_metrics_styles_.reserve(
233 std::accumulate(metrics.begin(), metrics.end(), 0,
234 [](const int a, const skt::LineMetrics& b) {
235 return a + b.fLineMetrics.size();
236 }));
237
238 for (const skt::LineMetrics& skm : metrics) {
239 LineMetrics& txtm = line_metrics_->emplace_back(
240 skm.fStartIndex, skm.fEndIndex, skm.fEndExcludingWhitespaces,
241 skm.fEndIncludingNewline, skm.fHardBreak);
242 txtm.ascent = skm.fAscent;
243 txtm.descent = skm.fDescent;
244 txtm.unscaled_ascent = skm.fUnscaledAscent;
245 txtm.height = skm.fHeight;
246 txtm.width = skm.fWidth;
247 txtm.left = skm.fLeft;
248 txtm.baseline = skm.fBaseline;
249 txtm.line_number = skm.fLineNumber;
250
251 for (const auto& sk_iter : skm.fLineMetrics) {
252 const skt::StyleMetrics& sk_style_metrics = sk_iter.second;
253 line_metrics_styles_.push_back(SkiaToTxt(*sk_style_metrics.text_style));
254 txtm.run_metrics.emplace(
255 std::piecewise_construct, std::forward_as_tuple(sk_iter.first),
256 std::forward_as_tuple(&line_metrics_styles_.back(),
257 sk_style_metrics.font_metrics));
258 }
259 }
260 }
261
262 return line_metrics_.value();
263}
264
266 skt::LineMetrics* lineMetrics) const {
267 return paragraph_->getLineMetricsAt(lineNumber, lineMetrics);
268};
269
271 return SkScalarToDouble(paragraph_->getMinIntrinsicWidth());
272}
273
275 return SkScalarToDouble(paragraph_->getMaxIntrinsicWidth());
276}
277
279 return SkScalarToDouble(paragraph_->getAlphabeticBaseline());
280}
281
283 return SkScalarToDouble(paragraph_->getIdeographicBaseline());
284}
285
287 return paragraph_->didExceedMaxLines();
288}
289
291 line_metrics_.reset();
292 line_metrics_styles_.clear();
293 paragraph_->layout(width);
294}
295
297 DisplayListParagraphPainter painter(builder, dl_paints_, impeller_enabled_);
298 paragraph_->paint(&painter, x, y);
299 return true;
300}
301
302std::vector<Paragraph::TextBox> ParagraphSkia::GetRectsForRange(
303 size_t start,
304 size_t end,
305 RectHeightStyle rect_height_style,
306 RectWidthStyle rect_width_style) {
307 std::vector<skt::TextBox> skia_boxes = paragraph_->getRectsForRange(
308 start, end, static_cast<skt::RectHeightStyle>(rect_height_style),
309 static_cast<skt::RectWidthStyle>(rect_width_style));
310
311 std::vector<Paragraph::TextBox> boxes;
312 for (const skt::TextBox& skia_box : skia_boxes) {
313 boxes.emplace_back(skia_box.rect,
314 static_cast<TextDirection>(skia_box.direction));
315 }
316
317 return boxes;
318}
319
320std::vector<Paragraph::TextBox> ParagraphSkia::GetRectsForPlaceholders() {
321 std::vector<skt::TextBox> skia_boxes = paragraph_->getRectsForPlaceholders();
322
323 std::vector<Paragraph::TextBox> boxes;
324 for (const skt::TextBox& skia_box : skia_boxes) {
325 boxes.emplace_back(skia_box.rect,
326 static_cast<TextDirection>(skia_box.direction));
327 }
328
329 return boxes;
330}
331
333 double dx,
334 double dy) {
336 paragraph_->getGlyphPositionAtCoordinate(dx, dy);
337
338 return ParagraphSkia::PositionWithAffinity(
339 skia_pos.position, static_cast<Affinity>(skia_pos.affinity));
340}
341
343 unsigned offset,
345 return paragraph_->getGlyphInfoAtUTF16Offset(offset, glyphInfo);
346}
347
349 double dx,
350 double dy,
352 return paragraph_->getClosestUTF16GlyphInfoAt(dx, dy, glyphInfo);
353};
354
356 skt::SkRange<size_t> range = paragraph_->getWordBoundary(offset);
357 return Paragraph::Range<size_t>(range.start, range.end);
358}
359
361 return paragraph_->lineNumber();
362}
363
364int ParagraphSkia::GetLineNumberAt(size_t codeUnitIndex) const {
365 return paragraph_->getLineNumberAtUTF16Offset(codeUnitIndex);
366}
367
368TextStyle ParagraphSkia::SkiaToTxt(const skt::TextStyle& skia) {
370
371 txt.color = skia.getColor();
372 txt.decoration = static_cast<TextDecoration>(skia.getDecorationType());
373 txt.decoration_color = skia.getDecorationColor();
374 txt.decoration_style =
375 static_cast<TextDecorationStyle>(skia.getDecorationStyle());
376 txt.decoration_thickness_multiplier =
377 SkScalarToDouble(skia.getDecorationThicknessMultiplier());
378 txt.font_weight = GetTxtFontWeight(skia.getFontStyle().weight());
379 txt.font_style = GetTxtFontStyle(skia.getFontStyle().slant());
380
381 txt.text_baseline = static_cast<TextBaseline>(skia.getTextBaseline());
382
383 for (const SkString& font_family : skia.getFontFamilies()) {
384 txt.font_families.emplace_back(font_family.c_str());
385 }
386
387 txt.font_size = SkScalarToDouble(skia.getFontSize());
388 txt.letter_spacing = SkScalarToDouble(skia.getLetterSpacing());
389 txt.word_spacing = SkScalarToDouble(skia.getWordSpacing());
390 txt.height = SkScalarToDouble(skia.getHeight());
391
392 txt.locale = skia.getLocale().c_str();
393 if (skia.hasBackground()) {
394 PaintID background_id = std::get<PaintID>(skia.getBackgroundPaintOrID());
395 txt.background = dl_paints_[background_id];
396 }
397 if (skia.hasForeground()) {
398 PaintID foreground_id = std::get<PaintID>(skia.getForegroundPaintOrID());
399 txt.foreground = dl_paints_[foreground_id];
400 }
401
402 txt.text_shadows.clear();
403 for (const skt::TextShadow& skia_shadow : skia.getShadows()) {
404 txt::TextShadow shadow;
405 shadow.offset = skia_shadow.fOffset;
406 shadow.blur_sigma = skia_shadow.fBlurSigma;
407 shadow.color = skia_shadow.fColor;
408 txt.text_shadows.emplace_back(shadow);
409 }
410
411 return txt;
412}
413
414} // namespace txt
static unsigned clamp(SkFixed fx, int max)
uint32_t SkColor
Definition: SkColor.h:37
#define SkScalarToDouble(x)
Definition: SkScalar.h:63
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
Definition: SkPath.h:59
const SkRect & bounds() const
Definition: SkTextBlob.h:53
T * get() const
Definition: SkRefCnt.h:303
static SkPath GetPath(SkTextBlob *textBlob)
const TextStyle * text_style
Definition: Metrics.h:17
SkFontMetrics font_metrics
Definition: Metrics.h:36
std::map< size_t, RunMetrics > run_metrics
Definition: line_metrics.h:68
double unscaled_ascent
Definition: line_metrics.h:50
double GetMaxIntrinsicWidth() override
bool DidExceedMaxLines() override
double GetHeight() override
ParagraphSkia(std::unique_ptr< skia::textlayout::Paragraph > paragraph, std::vector< flutter::DlPaint > &&dl_paints, bool impeller_enabled)
size_t GetNumberOfLines() const override
PositionWithAffinity GetGlyphPositionAtCoordinate(double dx, double dy) override
double GetAlphabeticBaseline() override
bool GetClosestGlyphInfoAtCoordinate(double dx, double dy, skia::textlayout::Paragraph::GlyphInfo *glyphInfo) const override
int GetLineNumberAt(size_t utf16Offset) const override
bool GetLineMetricsAt(int lineNumber, skia::textlayout::LineMetrics *lineMetrics) const override
double GetLongestLine() override
std::vector< LineMetrics > & GetLineMetrics() override
void Layout(double width) override
std::vector< TextBox > GetRectsForRange(size_t start, size_t end, RectHeightStyle rect_height_style, RectWidthStyle rect_width_style) override
std::vector< TextBox > GetRectsForPlaceholders() override
double GetMaxWidth() override
bool Paint(flutter::DisplayListBuilder *builder, double x, double y) override
bool GetGlyphInfoAt(unsigned offset, skia::textlayout::Paragraph::GlyphInfo *glyphInfo) const override
double GetMinIntrinsicWidth() override
Range< size_t > GetWordBoundary(size_t offset) override
double GetIdeographicBaseline() override
SkPoint offset
Definition: text_shadow.h:28
double blur_sigma
Definition: text_shadow.h:29
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
glong glong end
#define FML_DCHECK(condition)
Definition: logging.h:103
static void drawPath(SkPath &path, SkCanvas *canvas, SkColor color, const SkRect &clip, SkPaint::Cap cap, SkPaint::Join join, SkPaint::Style style, SkPathFillType fill, SkScalar strokeWidth)
Definition: linepaths.cpp:22
double y
double x
drawTextBlob(r.blob.get(), r.x, r.y, r.paint)) DRAW(DrawSlug
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DlDrawStyle
Definition: dl_paint.h:19
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
@ kNormal
fuzzy inside and outside
impeller::Point DlPoint
std::shared_ptr< TextFrame > MakeTextFrameFromTextBlobSkia(const sk_sp< SkTextBlob > &blob)
Definition: DartTypes.h:13
Definition: ref_ptr.h:256
flutter::DlColor DlColor
skt::ParagraphPainter::PaintID PaintID
TextDecoration
FontWeight
Definition: font_weight.h:22
FontStyle
Definition: font_style.h:22
TextDecorationStyle
TextBaseline
Definition: text_baseline.h:22
int32_t width
SeparatedVector2 offset
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741