diff --git a/engine/src/flutter/impeller/display_list/display_list_dispatcher.cc b/engine/src/flutter/impeller/display_list/display_list_dispatcher.cc index 4ec8572f770..68739c5b98a 100644 --- a/engine/src/flutter/impeller/display_list/display_list_dispatcher.cc +++ b/engine/src/flutter/impeller/display_list/display_list_dispatcher.cc @@ -888,7 +888,6 @@ void DisplayListDispatcher::drawArc(const SkRect& oval_bounds, void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, uint32_t count, const SkPoint points[]) { - // auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); Paint paint = paint_; paint.style = Paint::Style::kStroke; switch (mode) { @@ -897,29 +896,28 @@ void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, paint.stroke_cap = SolidStrokeContents::Cap::kSquare; } for (uint32_t i = 0; i < count; i++) { - SkPoint p0 = points[i]; - // kEhCloseEnough works around a bug where Impeller does not draw - // anything for zero-length lines. - // See: https://github.com/flutter/flutter/issues/109077 - SkPoint p1 = points[i] + SkPoint{kEhCloseEnough, 0.0}; - auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); + Point p0 = ToPoint(points[i]); + auto path = PathBuilder{}.AddLine(p0, p0).TakePath(); canvas_.DrawPath(std::move(path), paint); } break; case SkCanvas::kLines_PointMode: for (uint32_t i = 1; i < count; i += 2) { - SkPoint p0 = points[i - 1]; - SkPoint p1 = points[i]; - auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); + Point p0 = ToPoint(points[i - 1]); + Point p1 = ToPoint(points[i]); + auto path = PathBuilder{}.AddLine(p0, p1).TakePath(); canvas_.DrawPath(std::move(path), paint); } break; case SkCanvas::kPolygon_PointMode: - for (uint32_t i = 1; i < count; i++) { - SkPoint p0 = points[i - 1]; - SkPoint p1 = points[i]; - auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); - canvas_.DrawPath(std::move(path), paint); + if (count > 1) { + Point p0 = ToPoint(points[0]); + for (uint32_t i = 1; i < count; i++) { + Point p1 = ToPoint(points[i]); + auto path = PathBuilder{}.AddLine(p0, p1).TakePath(); + canvas_.DrawPath(std::move(path), paint); + p0 = p1; + } } break; } diff --git a/engine/src/flutter/impeller/display_list/display_list_unittests.cc b/engine/src/flutter/impeller/display_list/display_list_unittests.cc index 18bd21af984..08e4bb352d2 100644 --- a/engine/src/flutter/impeller/display_list/display_list_unittests.cc +++ b/engine/src/flutter/impeller/display_list/display_list_unittests.cc @@ -451,11 +451,13 @@ TEST_P(DisplayListTest, CanDrawPoints) { flutter::DlStrokeCap::kRound, flutter::DlStrokeCap::kSquare, }; - flutter::DlPaint paint = flutter::DlPaint().setStrokeWidth(20); + flutter::DlPaint paint = + flutter::DlPaint() // + .setColor(flutter::DlColor::kYellow().withAlpha(127)) // + .setStrokeWidth(20); builder.translate(50, 50); for (auto cap : caps) { paint.setStrokeCap(cap); - paint.setColor(flutter::DlColor::kYellow().withAlpha(127)); builder.save(); builder.drawPoints(SkCanvas::kPoints_PointMode, 7, points, paint); builder.translate(150, 0); @@ -468,5 +470,28 @@ TEST_P(DisplayListTest, CanDrawPoints) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_P(DisplayListTest, CanDrawZeroLengthLine) { + flutter::DisplayListBuilder builder; + std::vector caps = { + flutter::DlStrokeCap::kButt, + flutter::DlStrokeCap::kRound, + flutter::DlStrokeCap::kSquare, + }; + flutter::DlPaint paint = + flutter::DlPaint() // + .setColor(flutter::DlColor::kYellow().withAlpha(127)) // + .setDrawStyle(flutter::DlDrawStyle::kStroke) // + .setStrokeCap(flutter::DlStrokeCap::kButt) // + .setStrokeWidth(20); + SkPath path = SkPath().addPoly({{150, 50}, {150, 50}}, false); + for (auto cap : caps) { + paint.setStrokeCap(cap); + builder.drawLine({50, 50}, {50, 50}, paint); + builder.drawPath(path, paint); + builder.translate(0, 150); + } + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/solid_stroke_contents.cc b/engine/src/flutter/impeller/entity/contents/solid_stroke_contents.cc index c332b966c5b..326b90f3428 100644 --- a/engine/src/flutter/impeller/entity/contents/solid_stroke_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/solid_stroke_contents.cc @@ -72,10 +72,6 @@ static VertexBuffer CreateSolidStrokeVertices( VertexBufferBuilder vtx_builder; auto polyline = path.CreatePolyline(); - if (polyline.points.size() < 2) { - return {}; // Nothing to render. - } - VS::PerVertexData vtx; // Normal state. @@ -95,8 +91,17 @@ static VertexBuffer CreateSolidStrokeVertices( std::tie(contour_start_point_i, contour_end_point_i) = polyline.GetContourPointBounds(contour_i); - if (contour_end_point_i - contour_start_point_i < 2) { - continue; // This contour has no renderable content. + switch (contour_end_point_i - contour_start_point_i) { + case 1: { + Point p = polyline.points[contour_start_point_i]; + cap_proc(vtx_builder, p, {-1, 0}, smoothing); + cap_proc(vtx_builder, p, {1, 0}, smoothing); + continue; + } + case 0: + continue; // This contour has no renderable content. + default: + break; } // The first point's normal is always the same as diff --git a/engine/src/flutter/impeller/geometry/path.cc b/engine/src/flutter/impeller/geometry/path.cc index 69b1cd46a49..72e9488e4f0 100644 --- a/engine/src/flutter/impeller/geometry/path.cc +++ b/engine/src/flutter/impeller/geometry/path.cc @@ -237,7 +237,7 @@ Path::Polyline Path::CreatePolyline( for (const auto& point : collection) { if (previous_contour_point.has_value() && previous_contour_point.value() == point) { - // Slip over duplicate points in the same contour. + // Skip over duplicate points in the same contour. continue; } previous_contour_point = point; diff --git a/engine/src/flutter/impeller/geometry/point.h b/engine/src/flutter/impeller/geometry/point.h index 7b511ed9d64..5fd01ea9a28 100644 --- a/engine/src/flutter/impeller/geometry/point.h +++ b/engine/src/flutter/impeller/geometry/point.h @@ -189,7 +189,7 @@ struct TPoint { constexpr TPoint Normalize() const { const auto length = GetLength(); if (length == 0) { - return {}; + return {1, 0}; } return {x / length, y / length}; }