[Impeller] Render axis-aligned Caps for zero length lines in Impeller (flutter/engine#35298)

This commit is contained in:
Jim Graham 2022-08-11 14:49:47 -07:00 committed by GitHub
parent 8126a78906
commit fea6e19d60
5 changed files with 53 additions and 25 deletions

View File

@ -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;
}

View File

@ -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<flutter::DlStrokeCap> 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

View File

@ -72,10 +72,6 @@ static VertexBuffer CreateSolidStrokeVertices(
VertexBufferBuilder<VS::PerVertexData> 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

View File

@ -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;

View File

@ -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};
}