Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] Directly tessellate stroked circles. #48586

Merged
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
99 changes: 98 additions & 1 deletion impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2060,7 +2060,7 @@ TEST_P(AiksTest, DrawLinesRenderCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, FillCirclesRenderCorrectly) {
TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
Canvas canvas;
canvas.Scale(GetContentScale());
Paint paint;
Expand All @@ -2071,6 +2071,9 @@ TEST_P(AiksTest, FillCirclesRenderCorrectly) {
Color::Crimson(),
};

paint.color = Color::White();
canvas.DrawPaint(paint);

int c_index = 0;
int radius = 600;
while (radius > 0) {
Expand All @@ -2083,6 +2086,100 @@ TEST_P(AiksTest, FillCirclesRenderCorrectly) {
}
}

std::vector<Color> gradient_colors = {
Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
std::vector<Scalar> stops = {
0.0,
(1.0 / 6.0) * 1,
(1.0 / 6.0) * 2,
(1.0 / 6.0) * 3,
(1.0 / 6.0) * 4,
(1.0 / 6.0) * 5,
1.0,
};
auto texture = CreateTextureForFixture("airplane.jpg",
/*enable_mipmapping=*/true);

paint.color_source = ColorSource::MakeRadialGradient(
{500, 600}, 75, std::move(gradient_colors), std::move(stops),
Entity::TileMode::kMirror, {});
canvas.DrawCircle({500, 600}, 100, paint);

paint.color_source = ColorSource::MakeImage(
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
Matrix::MakeTranslation({700, 200}));
canvas.DrawCircle({800, 300}, 100, paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
Canvas canvas;
canvas.Scale(GetContentScale());
Paint paint;
const int color_count = 3;
Color colors[color_count] = {
Color::Blue(),
Color::Green(),
Color::Crimson(),
};

paint.color = Color::White();
canvas.DrawPaint(paint);

int c_index = 0;

auto draw = [&paint, &colors, &c_index](Canvas& canvas, Point center,
Scalar r, Scalar dr, int n) {
for (int i = 0; i < n; i++) {
paint.color = colors[(c_index++) % color_count];
canvas.DrawCircle(center, r, paint);
r += dr;
}
};

paint.style = Paint::Style::kStroke;
paint.stroke_width = 1;
draw(canvas, {10, 10}, 2, 2, 14); // r = [2, 28], covers [1,29]
paint.stroke_width = 5;
draw(canvas, {10, 10}, 35, 10, 56); // r = [35, 585], covers [30,590]

std::vector<Color> gradient_colors = {
Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
std::vector<Scalar> stops = {
0.0,
(1.0 / 6.0) * 1,
(1.0 / 6.0) * 2,
(1.0 / 6.0) * 3,
(1.0 / 6.0) * 4,
(1.0 / 6.0) * 5,
1.0,
};
auto texture = CreateTextureForFixture("airplane.jpg",
/*enable_mipmapping=*/true);

paint.color_source = ColorSource::MakeRadialGradient(
{500, 600}, 75, std::move(gradient_colors), std::move(stops),
Entity::TileMode::kMirror, {});
draw(canvas, {500, 600}, 5, 10, 10);

paint.color_source = ColorSource::MakeImage(
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
Matrix::MakeTranslation({700, 200}));
draw(canvas, {800, 300}, 5, 10, 10);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

Expand Down
20 changes: 6 additions & 14 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -287,18 +287,6 @@ void Canvas::DrawRRect(Rect rect, Point corner_radii, const Paint& paint) {
}

void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
if (paint.style == Paint::Style::kStroke) {
auto circle_path =
PathBuilder{}
.AddCircle(center, radius)
.SetConvexity(Convexity::kConvex)
.SetBounds(Rect::MakeLTRB(center.x - radius, center.y - radius,
center.x + radius, center.y + radius))
.TakePath();
DrawPath(circle_path, paint);
return;
}

Size half_size(radius, radius);
if (AttemptDrawBlurredRRect(
Rect::MakeOriginSize(center - half_size, half_size * 2), radius,
Expand All @@ -310,8 +298,12 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
entity.SetTransform(GetCurrentTransform());
entity.SetClipDepth(GetClipDepth());
entity.SetBlendMode(paint.blend_mode);
entity.SetContents(paint.WithFilters(
paint.CreateContentsForGeometry(Geometry::MakeCircle(center, radius))));
auto geometry =
paint.style == Paint::Style::kStroke
? Geometry::MakeStrokedCircle(center, radius, paint.stroke_width)
: Geometry::MakeCircle(center, radius);
entity.SetContents(
paint.WithFilters(paint.CreateContentsForGeometry(geometry)));

GetCurrentPass().AddEntity(entity);
}
Expand Down
137 changes: 104 additions & 33 deletions impeller/entity/geometry/ellipse_geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/entity/geometry/ellipse_geometry.h"
#include <algorithm>

#include "flutter/impeller/entity/geometry/ellipse_geometry.h"

#include "flutter/impeller/entity/geometry/line_geometry.h"
#include "flutter/impeller/tessellator/circle_tessellator.h"

namespace impeller {

EllipseGeometry::EllipseGeometry(Point center, Scalar radius)
: center_(center), radius_(radius) {
: center_(center), radius_(radius), stroke_width_(-1.0) {
FML_DCHECK(radius >= 0);
}

EllipseGeometry::EllipseGeometry(Point center,
Scalar radius,
Scalar stroke_width)
: center_(center),
radius_(radius),
stroke_width_(std::max(stroke_width, 0.0f)) {
FML_DCHECK(radius >= 0);
FML_DCHECK(stroke_width >= 0);
}

GeometryResult EllipseGeometry::GetPositionBuffer(
Expand All @@ -20,24 +33,52 @@ GeometryResult EllipseGeometry::GetPositionBuffer(
auto& host_buffer = pass.GetTransientsBuffer();
using VT = SolidFillVertexShader::PerVertexData;

Scalar radius = radius_;
Scalar half_width = stroke_width_ < 0
? 0.0
: LineGeometry::ComputePixelHalfWidth(
entity.GetTransform(), stroke_width_);
Scalar outer_radius = radius_ + half_width;
Scalar inner_radius = half_width <= 0 ? 0.0 : radius_ - half_width;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that this might be easier to read as an if/else setting the values more directly in each code block than a series of ternary ?: operations.


const Point& center = center_;
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
radius_);
size_t count = circle_tessellator.GetCircleVertexCount();
auto vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &center, radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices](const Point& p) { //
*vertices++ = {
.position = p,
};
},
center, radius);
});
outer_radius);

BufferView vertex_buffer;
size_t count;
if (inner_radius > 0) {
count = circle_tessellator.GetStrokedCircleVertexCount();
vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &count, &center, outer_radius,
inner_radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateStrokedCircleTriangleStrip(
[&vertices](const Point& p) { //
*vertices++ = {
.position = p,
};
},
center, outer_radius, inner_radius);
FML_DCHECK(vertices == reinterpret_cast<VT*>(buffer) + count);
});
} else {
count = circle_tessellator.GetCircleVertexCount();
vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &count, &center, outer_radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices](const Point& p) { //
*vertices++ = {
.position = p,
};
},
center, outer_radius);
FML_DCHECK(vertices == reinterpret_cast<VT*>(buffer) + count);
});
}

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
Expand Down Expand Up @@ -65,25 +106,55 @@ GeometryResult EllipseGeometry::GetPositionUVBuffer(
auto uv_transform =
texture_coverage.GetNormalizingTransform() * effect_transform;

Scalar radius = radius_;
Scalar half_width = stroke_width_ < 0
? 0.0
: LineGeometry::ComputePixelHalfWidth(
entity.GetTransform(), stroke_width_);
Scalar outer_radius = radius_ + half_width;
Scalar inner_radius = half_width <= 0 ? 0.0 : radius_ - half_width;

const Point& center = center_;
std::shared_ptr<Tessellator> tessellator = renderer.GetTessellator();
CircleTessellator circle_tessellator(tessellator, entity.GetTransform(),
radius_);
size_t count = circle_tessellator.GetCircleVertexCount();
auto vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &uv_transform, &center, radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices, &uv_transform](const Point& p) { //
*vertices++ = {
.position = p,
.texture_coords = uv_transform * p,
};
},
center, radius);
});
outer_radius);

BufferView vertex_buffer;
size_t count;
if (inner_radius > 0) {
count = circle_tessellator.GetStrokedCircleVertexCount();
vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &uv_transform, &count, &center, outer_radius,
inner_radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateStrokedCircleTriangleStrip(
[&vertices, &uv_transform](const Point& p) { //
*vertices++ = {
.position = p,
.texture_coords = uv_transform * p,
};
},
center, outer_radius, inner_radius);
FML_DCHECK(vertices == reinterpret_cast<VT*>(buffer) + count);
});
} else {
count = circle_tessellator.GetCircleVertexCount();
vertex_buffer = host_buffer.Emplace(
count * sizeof(VT), alignof(VT),
[&circle_tessellator, &uv_transform, &count, &center,
outer_radius](uint8_t* buffer) {
auto vertices = reinterpret_cast<VT*>(buffer);
circle_tessellator.GenerateCircleTriangleStrip(
[&vertices, &uv_transform](const Point& p) { //
*vertices++ = {
.position = p,
.texture_coords = uv_transform * p,
};
},
center, outer_radius);
FML_DCHECK(vertices == reinterpret_cast<VT*>(buffer) + count);
});
}

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/geometry/ellipse_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace impeller {
class EllipseGeometry final : public Geometry {
public:
explicit EllipseGeometry(Point center, Scalar radius);
explicit EllipseGeometry(Point center, Scalar radius, Scalar stroke_width);

~EllipseGeometry() = default;

Expand Down Expand Up @@ -59,6 +60,7 @@ class EllipseGeometry final : public Geometry {

Point center_;
Scalar radius_;
Scalar stroke_width_;

EllipseGeometry(const EllipseGeometry&) = delete;

Expand Down
6 changes: 6 additions & 0 deletions impeller/entity/geometry/geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ std::shared_ptr<Geometry> Geometry::MakeCircle(Point center, Scalar radius) {
return std::make_shared<EllipseGeometry>(center, radius);
}

std::shared_ptr<Geometry> Geometry::MakeStrokedCircle(Point center,
Scalar radius,
Scalar stroke_width) {
return std::make_shared<EllipseGeometry>(center, radius, stroke_width);
}

bool Geometry::CoversArea(const Matrix& transform, const Rect& rect) const {
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions impeller/entity/geometry/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class Geometry {

static std::shared_ptr<Geometry> MakeCircle(Point center, Scalar radius);

static std::shared_ptr<Geometry> MakeStrokedCircle(Point center,
Scalar radius,
Scalar stroke_width);

static std::shared_ptr<Geometry> MakePointField(std::vector<Point> points,
Scalar radius,
bool round);
Expand Down
Loading