Skip to content

[Impeller] Avoid allocating path for dashed lines #169881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 9, 2025
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
4 changes: 4 additions & 0 deletions engine/src/flutter/ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -51471,6 +51471,8 @@ ORIGIN: ../../../flutter/impeller/geometry/color.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/color.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/constants.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/constants.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/dashed_line_path_source.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/dashed_line_path_source.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/geometry_asserts.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/geometry_benchmarks.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/gradient.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -54488,6 +54490,8 @@ FILE: ../../../flutter/impeller/geometry/color.cc
FILE: ../../../flutter/impeller/geometry/color.h
FILE: ../../../flutter/impeller/geometry/constants.cc
FILE: ../../../flutter/impeller/geometry/constants.h
FILE: ../../../flutter/impeller/geometry/dashed_line_path_source.cc
FILE: ../../../flutter/impeller/geometry/dashed_line_path_source.h
FILE: ../../../flutter/impeller/geometry/geometry_asserts.h
FILE: ../../../flutter/impeller/geometry/geometry_benchmarks.cc
FILE: ../../../flutter/impeller/geometry/gradient.cc
Expand Down
26 changes: 26 additions & 0 deletions engine/src/flutter/impeller/display_list/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,32 @@ void Canvas::DrawLine(const Point& p0,
}
}

void Canvas::DrawDashedLine(const Point& p0,
const Point& p1,
Scalar on_length,
Scalar off_length,
const Paint& paint) {
// Reasons to defer to regular DrawLine:
// - performance for degenerate and "regular line" cases
// - length is non-positive - DrawLine will draw appropriate "dot"
// - off_length is non-positive - no gaps, DrawLine will draw it solid
// - on_length is negative - invalid dashing
//
// Note that a 0 length "on" dash will draw "dot"s every "off" distance
// apart so we proceed with the dashing process in that case.
Scalar length = p0.GetDistance(p1);
if (length > 0.0f && on_length >= 0.0f && off_length > 0.0f) {
Entity entity;
entity.SetTransform(GetCurrentTransform());
entity.SetBlendMode(paint.blend_mode);

StrokeDashedLineGeometry geom(p0, p1, on_length, off_length, paint.stroke);
AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
} else {
DrawLine(p0, p1, paint);
}
}

void Canvas::DrawRect(const Rect& rect, const Paint& paint) {
if (AttemptDrawBlurredRRect(rect, {}, paint)) {
return;
Expand Down
6 changes: 6 additions & 0 deletions engine/src/flutter/impeller/display_list/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ class Canvas {
const Paint& paint,
bool reuse_depth = false);

void DrawDashedLine(const Point& p0,
const Point& p1,
Scalar on_length,
Scalar off_length,
const Paint& paint);

void DrawRect(const Rect& rect, const Paint& paint);

void DrawOval(const Rect& rect, const Paint& paint);
Expand Down
37 changes: 2 additions & 35 deletions engine/src/flutter/impeller/display_list/dl_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

#include "display_list/dl_sampling_options.h"
#include "display_list/effects/dl_image_filter.h"
#include "display_list/geometry/dl_path_builder.h"
#include "flutter/fml/logging.h"
#include "fml/closure.h"
#include "impeller/core/formats.h"
Expand Down Expand Up @@ -547,46 +546,14 @@ void DlDispatcherBase::drawLine(const DlPoint& p0, const DlPoint& p1) {
GetCanvas().DrawLine(p0, p1, paint_);
}

// |flutter::DlOpReceiver|
void DlDispatcherBase::drawDashedLine(const DlPoint& p0,
const DlPoint& p1,
DlScalar on_length,
DlScalar off_length) {
AUTO_DEPTH_WATCHER(1u);

Scalar length = p0.GetDistance(p1);
// Reasons to defer to regular DrawLine:
// length is non-positive - drawLine will draw appropriate "dot"
// off_length is non-positive - no gaps, drawLine will draw it solid
// on_length is negative - invalid dashing
// Note that a 0 length "on" dash will draw "dot"s every "off" distance
// apart
if (length > 0.0f && on_length >= 0.0f && off_length > 0.0f) {
Point delta = (p1 - p0) / length; // length > 0 already tested
flutter::DlPathBuilder builder;

Scalar consumed = 0.0f;
while (consumed < length) {
builder.MoveTo(p0 + delta * consumed);

Scalar dash_end = consumed + on_length;
if (dash_end < length) {
builder.LineTo(p0 + delta * dash_end);
} else {
builder.LineTo(p1);
// Should happen anyway due to the math, but let's make it explicit
// in case of bit errors. We're done with this line.
break;
}

consumed = dash_end + off_length;
}

Paint stroke_paint = paint_;
stroke_paint.style = Paint::Style::kStroke;
GetCanvas().DrawPath(builder.TakePath(), stroke_paint);
} else {
drawLine(p0, p1);
}
GetCanvas().DrawDashedLine(p0, p1, on_length, off_length, paint_);
}

// |flutter::DlOpReceiver|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,4 +846,17 @@ const PathSource& StrokeDiffRoundRectGeometry::GetSource() const {
return source_;
}

StrokeDashedLineGeometry::StrokeDashedLineGeometry(
Point p0,
Point p1,
Scalar on_length,
Scalar off_length,
const StrokeParameters& parameters)
: StrokePathSourceGeometry(parameters),
source_(p0, p1, on_length, off_length) {}

const PathSource& StrokeDashedLineGeometry::GetSource() const {
return source_;
}

} // namespace impeller
20 changes: 20 additions & 0 deletions engine/src/flutter/impeller/entity/geometry/stroke_path_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_IMPELLER_ENTITY_GEOMETRY_STROKE_PATH_GEOMETRY_H_

#include "impeller/entity/geometry/geometry.h"
#include "impeller/geometry/dashed_line_path_source.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/path_source.h"
#include "impeller/geometry/stroke_parameters.h"
Expand Down Expand Up @@ -126,6 +127,25 @@ class StrokeDiffRoundRectGeometry final : public StrokePathSourceGeometry {
const DiffRoundRectPathSource source_;
};

/// @brief A Geometry that produces fillable vertices representing the
/// stroked outline of a |DlPath| object using the
/// |StrokePathSourceGeometry| base class and a |DlPath| object
/// to perform path iteration.
class StrokeDashedLineGeometry final : public StrokePathSourceGeometry {
public:
StrokeDashedLineGeometry(Point p0,
Point p1,
Scalar on_length,
Scalar off_length,
const StrokeParameters& parameters);

protected:
const PathSource& GetSource() const override;

private:
const DashedLinePathSource source_;
};

} // namespace impeller

#endif // FLUTTER_IMPELLER_ENTITY_GEOMETRY_STROKE_PATH_GEOMETRY_H_
2 changes: 2 additions & 0 deletions engine/src/flutter/impeller/geometry/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ impeller_component("geometry") {
"color.h",
"constants.cc",
"constants.h",
"dashed_line_path_source.cc",
"dashed_line_path_source.h",
"gradient.cc",
"gradient.h",
"half.h",
Expand Down
68 changes: 68 additions & 0 deletions engine/src/flutter/impeller/geometry/dashed_line_path_source.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/geometry/dashed_line_path_source.h"

namespace impeller {

DashedLinePathSource::DashedLinePathSource(Point p0,
Point p1,
Scalar on_length,
Scalar off_length)
: p0_(p0), p1_(p1), on_length_(on_length), off_length_(off_length) {}

DashedLinePathSource::~DashedLinePathSource() = default;

FillType DashedLinePathSource::GetFillType() const {
return FillType::kNonZero;
}

Rect DashedLinePathSource::GetBounds() const {
return Rect::MakeLTRB(p0_.x, p0_.y, p1_.x, p1_.y).GetPositive();
}

bool DashedLinePathSource::IsConvex() const {
return false;
}

void DashedLinePathSource::Dispatch(PathReceiver& receiver) const {
// Exceptional conditions:
// - length is non-positive - result will draw only a "dot"
// - off_length is non-positive - no gaps, result is a solid line
// - on_length is negative - invalid dashing
// Note that a 0 length "on" dash will draw "dot"s every "off" distance
// apart so we still generate the dashing for that case.
//
// Note that Canvas will detect these conditions and use its own DrawLine
// method directly for performance reasons for a single line, but in case
// someone uses this PathSource with these exceptional cases, we degenerate
// gracefully into a single line segment path description below.
Scalar length = p0_.GetDistance(p1_);
if (length > 0.0f && on_length_ >= 0.0f && off_length_ > 0.0f) {
Point delta = (p1_ - p0_) / length; // length > 0 already verified

Scalar consumed = 0.0f;
while (consumed < length) {
receiver.MoveTo(p0_ + delta * consumed, false);

Scalar dash_end = consumed + on_length_;
if (dash_end < length) {
receiver.LineTo(p0_ + delta * dash_end);
} else {
receiver.LineTo(p1_);
// Should happen anyway due to the math, but let's make it explicit
// in case of bit errors. We're done with this line.
break;
}

consumed = dash_end + off_length_;
}
} else {
receiver.MoveTo(p0_, false);
receiver.LineTo(p1_);
}
receiver.PathEnd();
}

} // namespace impeller
42 changes: 42 additions & 0 deletions engine/src/flutter/impeller/geometry/dashed_line_path_source.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_IMPELLER_GEOMETRY_DASHED_LINE_PATH_SOURCE_H_
#define FLUTTER_IMPELLER_GEOMETRY_DASHED_LINE_PATH_SOURCE_H_

#include "flutter/impeller/geometry/path_source.h"
#include "flutter/impeller/geometry/point.h"
#include "flutter/impeller/geometry/scalar.h"

namespace impeller {

/// @brief A PathSource that generates the various segments of a dashed line.
class DashedLinePathSource : public PathSource {
public:
DashedLinePathSource(Point p0, Point p1, Scalar on_length, Scalar off_length);

~DashedLinePathSource();

// |PathSource|
FillType GetFillType() const override;

// |PathSource|
Rect GetBounds() const override;

// |PathSource|
bool IsConvex() const override;

// |PathSource|
void Dispatch(PathReceiver& receiver) const override;

private:
const Point p0_;
const Point p1_;
const Scalar on_length_;
const Scalar off_length_;
};

} // namespace impeller

#endif // FLUTTER_IMPELLER_GEOMETRY_DASHED_LINE_PATH_SOURCE_H_
Loading