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

Commit 980e4d2

Browse files
author
Jonah Williams
authored
[Impeller] geometry changes to support line/point style. (#56340)
Split off from #55230 . Adds getter that tracks if a Path is a single contour. ################################ flutter/flutter#152702
1 parent 6376c86 commit 980e4d2

File tree

11 files changed

+331
-8
lines changed

11 files changed

+331
-8
lines changed

impeller/entity/entity_unittests.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "gtest/gtest.h"
1515
#include "impeller/core/device_buffer.h"
1616
#include "impeller/core/formats.h"
17+
#include "impeller/core/host_buffer.h"
1718
#include "impeller/core/texture_descriptor.h"
1819
#include "impeller/entity/contents/clip_contents.h"
1920
#include "impeller/entity/contents/conical_gradient_contents.h"
@@ -2425,6 +2426,59 @@ TEST_P(EntityTest, GiantStrokePathAllocation) {
24252426
EXPECT_NEAR(point.y, expected[4].y, 0.1);
24262427
}
24272428

2429+
TEST_P(EntityTest, GiantLineStripPathAllocation) {
2430+
PathBuilder builder{};
2431+
for (int i = 0; i < 10000; i++) {
2432+
builder.LineTo(Point(i, i));
2433+
}
2434+
Path path = builder.TakePath();
2435+
2436+
ContentContext content_context(GetContext(), /*typographer_context=*/nullptr);
2437+
Entity entity;
2438+
2439+
auto host_buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
2440+
GetContext()->GetIdleWaiter());
2441+
auto tessellator = Tessellator();
2442+
2443+
auto vertex_buffer = tessellator.GenerateLineStrip(path, *host_buffer, 1.0);
2444+
2445+
// Validate the buffer data overflowed the small buffer
2446+
EXPECT_GT(vertex_buffer.vertex_count, kPointArenaSize);
2447+
2448+
// Validate that there are no uninitialized points near the gap.
2449+
Point* written_data = reinterpret_cast<Point*>(
2450+
(vertex_buffer.vertex_buffer.GetBuffer()->OnGetContents() +
2451+
vertex_buffer.vertex_buffer.GetRange().offset));
2452+
2453+
std::vector<Point> expected = {
2454+
Point(4093, 4093), //
2455+
Point(4094, 4094), //
2456+
Point(4095, 4095), //
2457+
Point(4096, 4096), //
2458+
Point(4097, 4097) //
2459+
};
2460+
2461+
Point point = written_data[kPointArenaSize - 2];
2462+
EXPECT_NEAR(point.x, expected[0].x, 0.1);
2463+
EXPECT_NEAR(point.y, expected[0].y, 0.1);
2464+
2465+
point = written_data[kPointArenaSize - 1];
2466+
EXPECT_NEAR(point.x, expected[1].x, 0.1);
2467+
EXPECT_NEAR(point.y, expected[1].y, 0.1);
2468+
2469+
point = written_data[kPointArenaSize];
2470+
EXPECT_NEAR(point.x, expected[2].x, 0.1);
2471+
EXPECT_NEAR(point.y, expected[2].y, 0.1);
2472+
2473+
point = written_data[kPointArenaSize + 1];
2474+
EXPECT_NEAR(point.x, expected[3].x, 0.1);
2475+
EXPECT_NEAR(point.y, expected[3].y, 0.1);
2476+
2477+
point = written_data[kPointArenaSize + 2];
2478+
EXPECT_NEAR(point.x, expected[4].x, 0.1);
2479+
EXPECT_NEAR(point.y, expected[4].y, 0.1);
2480+
}
2481+
24282482
} // namespace testing
24292483
} // namespace impeller
24302484

impeller/entity/geometry/stroke_path_geometry.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class PositionWriter {
4141

4242
bool HasOversizedBuffer() const { return !oversized_.empty(); }
4343

44-
const std::vector<Point>& GetOveriszedBuffer() const { return oversized_; }
44+
const std::vector<Point>& GetOversizedBuffer() const { return oversized_; }
4545

4646
private:
4747
std::vector<Point>& points_;
@@ -618,7 +618,7 @@ GeometryResult StrokePathGeometry::GetPositionBuffer(
618618
.mode = GeometryResult::Mode::kPreventOverdraw};
619619
}
620620
const std::vector<Point>& oversized_data =
621-
position_writer.GetOveriszedBuffer();
621+
position_writer.GetOversizedBuffer();
622622
BufferView buffer_view = host_buffer.Emplace(
623623
/*buffer=*/nullptr, //
624624
(arena_length + oversized_length) * sizeof(Point), //

impeller/geometry/path.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ bool Path::IsEmpty() const {
5959
data_->components[0] == ComponentType::kContour);
6060
}
6161

62+
bool Path::IsSingleContour() const {
63+
return data_->single_countour;
64+
}
65+
6266
/// Determine required storage for points and indices.
6367
std::pair<size_t, size_t> Path::CountStorage(Scalar scale) const {
6468
size_t points = 0;

impeller/geometry/path.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ class Path {
153153

154154
bool IsEmpty() const;
155155

156+
/// @brief Whether the line contains a single contour.
157+
bool IsSingleContour() const;
158+
156159
bool GetLinearComponentAtIndex(size_t index,
157160
LinearPathComponent& linear) const;
158161

@@ -219,6 +222,7 @@ class Path {
219222

220223
FillType fill = FillType::kNonZero;
221224
Convexity convexity = Convexity::kUnknown;
225+
bool single_countour = true;
222226
std::optional<Rect> bounds;
223227
std::vector<Point> points;
224228
std::vector<ComponentType> components;

impeller/geometry/path_builder.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,22 @@ PathBuilder::~PathBuilder() = default;
1818

1919
Path PathBuilder::CopyPath(FillType fill) {
2020
prototype_.fill = fill;
21+
prototype_.single_countour =
22+
current_contour_location_ == 0u ||
23+
(contour_count_ == 2 &&
24+
prototype_.components.back() == Path::ComponentType::kContour);
2125
return Path(prototype_);
2226
}
2327

2428
Path PathBuilder::TakePath(FillType fill) {
2529
prototype_.fill = fill;
2630
UpdateBounds();
31+
prototype_.single_countour =
32+
current_contour_location_ == 0u ||
33+
(contour_count_ == 2 &&
34+
prototype_.components.back() == Path::ComponentType::kContour);
2735
current_contour_location_ = 0u;
36+
contour_count_ = 1;
2837
return Path(std::move(prototype_));
2938
}
3039

@@ -276,6 +285,7 @@ void PathBuilder::AddContourComponent(const Point& destination,
276285
points.push_back(destination);
277286
points.push_back(closed);
278287
components.push_back(Path::ComponentType::kContour);
288+
contour_count_ += 1;
279289
}
280290
prototype_.bounds.reset();
281291
}
@@ -450,6 +460,7 @@ PathBuilder& PathBuilder::AddPath(const Path& path) {
450460
for (auto component : path.data_->components) {
451461
if (component == Path::ComponentType::kContour) {
452462
current_contour_location_ = source_offset;
463+
contour_count_ += 1;
453464
}
454465
source_offset += Path::VerbToOffset(component);
455466
}

impeller/geometry/path_builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class PathBuilder {
111111
Point subpath_start_;
112112
Point current_;
113113
size_t current_contour_location_ = 0u;
114+
size_t contour_count_ = 0u;
114115
Path::Data prototype_;
115116

116117
PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii);

impeller/geometry/path_component.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "path_component.h"
66

77
#include <cmath>
8+
#include <utility>
89

910
#include "impeller/geometry/scalar.h"
1011
#include "impeller/geometry/wangs_formula.h"
@@ -77,6 +78,29 @@ void StripVertexWriter::Write(Point point) {
7778
point_buffer_[count_++] = point;
7879
}
7980

81+
/////////// LineStripVertexWriter ////////
82+
83+
LineStripVertexWriter::LineStripVertexWriter(std::vector<Point>& points)
84+
: points_(points) {}
85+
86+
void LineStripVertexWriter::EndContour() {}
87+
88+
void LineStripVertexWriter::Write(Point point) {
89+
if (offset_ >= points_.size()) {
90+
overflow_.push_back(point);
91+
} else {
92+
points_[offset_++] = point;
93+
}
94+
}
95+
96+
const std::vector<Point>& LineStripVertexWriter::GetOversizedBuffer() const {
97+
return overflow_;
98+
}
99+
100+
std::pair<size_t, size_t> LineStripVertexWriter::GetVertexCount() const {
101+
return std::make_pair(offset_, overflow_.size());
102+
}
103+
80104
/////////// GLESVertexWriter ///////////
81105

82106
GLESVertexWriter::GLESVertexWriter(std::vector<Point>& points,

impeller/geometry/path_component.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,27 @@ class StripVertexWriter : public VertexWriter {
6767
uint16_t* index_buffer_ = nullptr;
6868
};
6969

70+
/// @brief A vertex writer that generates a line strip topology.
71+
class LineStripVertexWriter : public VertexWriter {
72+
public:
73+
explicit LineStripVertexWriter(std::vector<Point>& points);
74+
75+
~LineStripVertexWriter() = default;
76+
77+
void EndContour() override;
78+
79+
void Write(Point point) override;
80+
81+
std::pair<size_t, size_t> GetVertexCount() const;
82+
83+
const std::vector<Point>& GetOversizedBuffer() const;
84+
85+
private:
86+
size_t offset_ = 0u;
87+
std::vector<Point>& points_;
88+
std::vector<Point> overflow_;
89+
};
90+
7091
/// @brief A vertex writer that has no hardware requirements.
7192
class GLESVertexWriter : public VertexWriter {
7293
public:

impeller/geometry/path_unittests.cc

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,142 @@ TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) {
5050
ASSERT_EQ(polyline.GetPoint(4).x, 50);
5151
}
5252

53+
TEST(PathTest, PathSingleContour) {
54+
// Closed shapes.
55+
{
56+
Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
57+
EXPECT_TRUE(path.IsSingleContour());
58+
}
59+
60+
{
61+
Path path =
62+
PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
63+
64+
EXPECT_TRUE(path.IsSingleContour());
65+
}
66+
67+
{
68+
Path path =
69+
PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
70+
71+
EXPECT_TRUE(path.IsSingleContour());
72+
}
73+
74+
{
75+
Path path = PathBuilder{}
76+
.AddRoundRect(RoundRect::MakeRectRadius(
77+
Rect::MakeXYWH(100, 100, 100, 100), 10))
78+
.TakePath();
79+
80+
EXPECT_TRUE(path.IsSingleContour());
81+
}
82+
83+
// Open shapes.
84+
{
85+
Point p(100, 100);
86+
Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
87+
88+
EXPECT_TRUE(path.IsSingleContour());
89+
}
90+
91+
{
92+
Path path =
93+
PathBuilder{}
94+
.AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
95+
.TakePath();
96+
97+
EXPECT_TRUE(path.IsSingleContour());
98+
}
99+
100+
{
101+
Path path = PathBuilder{}
102+
.AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
103+
.TakePath();
104+
105+
EXPECT_TRUE(path.IsSingleContour());
106+
}
107+
}
108+
109+
TEST(PathTest, PathSingleContourDoubleShapes) {
110+
// Closed shapes.
111+
{
112+
Path path = PathBuilder{}
113+
.AddCircle({100, 100}, 50)
114+
.AddCircle({100, 100}, 50)
115+
.TakePath();
116+
EXPECT_FALSE(path.IsSingleContour());
117+
}
118+
119+
{
120+
Path path = PathBuilder{}
121+
.AddOval(Rect::MakeXYWH(100, 100, 100, 100))
122+
.AddOval(Rect::MakeXYWH(100, 100, 100, 100))
123+
.TakePath();
124+
125+
EXPECT_FALSE(path.IsSingleContour());
126+
}
127+
128+
{
129+
Path path = PathBuilder{}
130+
.AddRect(Rect::MakeXYWH(100, 100, 100, 100))
131+
.AddRect(Rect::MakeXYWH(100, 100, 100, 100))
132+
.TakePath();
133+
134+
EXPECT_FALSE(path.IsSingleContour());
135+
}
136+
137+
{
138+
Path path = PathBuilder{}
139+
.AddRoundRect(RoundRect::MakeRectRadius(
140+
Rect::MakeXYWH(100, 100, 100, 100), 10))
141+
.AddRoundRect(RoundRect::MakeRectRadius(
142+
Rect::MakeXYWH(100, 100, 100, 100), 10))
143+
.TakePath();
144+
145+
EXPECT_FALSE(path.IsSingleContour());
146+
}
147+
148+
{
149+
Path path = PathBuilder{}
150+
.AddRoundRect(RoundRect::MakeRectXY(
151+
Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
152+
.AddRoundRect(RoundRect::MakeRectXY(
153+
Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
154+
.TakePath();
155+
156+
EXPECT_FALSE(path.IsSingleContour());
157+
}
158+
159+
// Open shapes.
160+
{
161+
Point p(100, 100);
162+
Path path =
163+
PathBuilder{}.AddLine(p, {200, 100}).AddLine(p, {200, 100}).TakePath();
164+
165+
EXPECT_FALSE(path.IsSingleContour());
166+
}
167+
168+
{
169+
Path path =
170+
PathBuilder{}
171+
.AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
172+
.AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
173+
.TakePath();
174+
175+
EXPECT_FALSE(path.IsSingleContour());
176+
}
177+
178+
{
179+
Path path = PathBuilder{}
180+
.AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
181+
.Close()
182+
.AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
183+
.TakePath();
184+
185+
EXPECT_FALSE(path.IsSingleContour());
186+
}
187+
}
188+
53189
TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
54190
// Closed shapes.
55191
{

0 commit comments

Comments
 (0)