Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
808 changes: 449 additions & 359 deletions impeller/entity/geometry/stroke_path_geometry.cc

Large diffs are not rendered by default.

95 changes: 68 additions & 27 deletions impeller/entity/geometry/stroke_path_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

namespace impeller {

class VertexWriter {
public:
virtual void AppendVertex(const Point& point) = 0;
};

/// @brief A geometry that is created from a stroked path object.
class StrokePathGeometry final : public Geometry {
public:
Expand All @@ -28,23 +33,21 @@ class StrokePathGeometry final : public Geometry {

Join GetStrokeJoin() const;

using CapProc = std::function<void(VertexWriter& vtx_builder,
const Point& position,
const Point& offset,
Scalar scale,
bool reverse)>;
using JoinProc = std::function<void(VertexWriter& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset,
Scalar miter_limit,
Scalar scale)>;

private:
using VS = SolidFillVertexShader;

using CapProc =
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
const Point& position,
const Point& offset,
Scalar scale,
bool reverse)>;
using JoinProc =
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset,
Scalar miter_limit,
Scalar scale)>;

// |Geometry|
GeometryResult GetPositionBuffer(const ContentContext& renderer,
const Entity& entity,
Expand All @@ -65,23 +68,61 @@ class StrokePathGeometry final : public Geometry {

bool SkipRendering() const;

static Scalar CreateBevelAndGetDirection(
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset);
static Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset);

static VertexBufferBuilder<SolidFillVertexShader::PerVertexData>
CreateSolidStrokeVertices(const Path& path,
Scalar stroke_width,
Scalar scaled_miter_limit,
const JoinProc& join_proc,
const CapProc& cap_proc,
Scalar scale);
static void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
const Path::Polyline& path,
Scalar stroke_width,
Scalar scaled_miter_limit,
const JoinProc& join_proc,
const CapProc& cap_proc,
Scalar scale);

static StrokePathGeometry::JoinProc GetJoinProc(Join stroke_join);

static StrokePathGeometry::CapProc GetCapProc(Cap stroke_cap);
static StrokePathGeometry::CapProc GetCapProc(Cap cap);

static void CreateButtCap(VertexWriter& vtx_builder,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused - these don't seem to be used in this PR, is there a reason they were added to the headers?

(these = all the static void CreateXCap methods)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are used, see https://github.com/flutter/engine/pull/50379/files#diff-c75818e02ee25c31e8f8a6d5cac9a4f80b33edf3066d39104cd805268310625dR267

Previous this used to pass around closures, but I updated these to be static functions, since that is what they are anyway.

const Point& position,
const Point& offset,
Scalar scale,
bool reverse);

static void CreateRoundCap(VertexWriter& vtx_builder,
const Point& position,
const Point& offset,
Scalar scale,
bool reverse);

static void CreateSquareCap(VertexWriter& vtx_builder,
const Point& position,
const Point& offset,
Scalar scale,
bool reverse);

static void CreateMiterJoin(VertexWriter& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset,
Scalar miter_limit,
Scalar scale);

static void CreateRoundJoin(VertexWriter& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset,
Scalar miter_limit,
Scalar scale);

static void CreateBevelJoin(VertexWriter& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset,
Scalar miter_limit,
Scalar scale);

Path path_;
Scalar stroke_width_;
Expand Down
1 change: 0 additions & 1 deletion impeller/geometry/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include <functional>
#include <optional>
#include <set>
#include <tuple>
#include <vector>

Expand Down
30 changes: 17 additions & 13 deletions impeller/geometry/path_component.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ static Scalar ApproximateParabolaIntegral(Scalar x) {
void QuadraticPathComponent::AppendPolylinePoints(
Scalar scale_factor,
std::vector<Point>& points) const {
ToLinearPathComponents(scale_factor, [&points](const Point& point) {
points.emplace_back(point);
});
}

void QuadraticPathComponent::ToLinearPathComponents(
Scalar scale_factor,
const PointProc& proc) const {
auto tolerance = kDefaultCurveTolerance / scale_factor;
auto sqrt_tolerance = sqrt(tolerance);

Expand Down Expand Up @@ -141,9 +149,9 @@ void QuadraticPathComponent::AppendPolylinePoints(
auto u = i * step;
auto a = a0 + (a2 - a0) * u;
auto t = (ApproximateParabolaIntegral(a) - u0) * uscale;
points.emplace_back(Solve(t));
proc(Solve(t));
}
points.emplace_back(p2);
proc(p2);
}

std::vector<Point> QuadraticPathComponent::Extrema() const {
Expand Down Expand Up @@ -188,10 +196,8 @@ Point CubicPathComponent::SolveDerivative(Scalar time) const {
void CubicPathComponent::AppendPolylinePoints(
Scalar scale,
std::vector<Point>& points) const {
auto quads = ToQuadraticPathComponents(.1);
for (const auto& quad : quads) {
quad.AppendPolylinePoints(scale, points);
}
ToLinearPathComponents(
scale, [&points](const Point& point) { points.emplace_back(point); });
}

inline QuadraticPathComponent CubicPathComponent::Lower() const {
Expand All @@ -209,9 +215,9 @@ CubicPathComponent CubicPathComponent::Subsegment(Scalar t0, Scalar t1) const {
return CubicPathComponent(p0, p1, p2, p3);
}

std::vector<QuadraticPathComponent>
CubicPathComponent::ToQuadraticPathComponents(Scalar accuracy) const {
std::vector<QuadraticPathComponent> quads;
void CubicPathComponent::ToLinearPathComponents(Scalar scale,
const PointProc& proc) const {
constexpr Scalar accuracy = 0.1;
// The maximum error, as a vector from the cubic to the best approximating
// quadratic, is proportional to the third derivative, which is constant
// across the segment. Thus, the error scales down as the third power of
Expand All @@ -229,17 +235,15 @@ CubicPathComponent::ToQuadraticPathComponents(Scalar accuracy) const {
auto p = p2x2 - p1x2;
auto err = p.Dot(p);
auto quad_count = std::max(1., ceil(pow(err / max_hypot2, 1. / 6.0)));
quads.reserve(quad_count);
for (size_t i = 0; i < quad_count; i++) {
auto t0 = i / quad_count;
auto t1 = (i + 1) / quad_count;
auto seg = Subsegment(t0, t1);
auto p1x2 = 3.0 * seg.cp1 - seg.p1;
auto p2x2 = 3.0 * seg.cp2 - seg.p2;
quads.emplace_back(
QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2));
QuadraticPathComponent(seg.p1, ((p1x2 + p2x2) / 4.0), seg.p2)
.ToLinearPathComponents(scale, proc);
}
return quads;
}

static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) {
Expand Down
10 changes: 8 additions & 2 deletions impeller/geometry/path_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
#define FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_

#include <functional>
#include <type_traits>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -79,6 +80,10 @@ struct QuadraticPathComponent {
void AppendPolylinePoints(Scalar scale_factor,
std::vector<Point>& points) const;

using PointProc = std::function<void(const Point& point)>;

void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const;

std::vector<Point> Extrema() const;

bool operator==(const QuadraticPathComponent& other) const {
Expand Down Expand Up @@ -125,8 +130,9 @@ struct CubicPathComponent {

std::vector<Point> Extrema() const;

std::vector<QuadraticPathComponent> ToQuadraticPathComponents(
Scalar accuracy) const;
using PointProc = std::function<void(const Point& point)>;

void ToLinearPathComponents(Scalar scale, const PointProc& proc) const;

CubicPathComponent Subsegment(Scalar t0, Scalar t1) const;

Expand Down
14 changes: 13 additions & 1 deletion impeller/tessellator/tessellator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,22 @@ Tessellator::Result Tessellator::Tessellate(const Path& path,
return Result::kSuccess;
}

Path::Polyline Tessellator::CreateTempPolyline(const Path& path,
Scalar tolerance) {
FML_DCHECK(point_buffer_);
point_buffer_->clear();
auto polyline =
path.CreatePolyline(tolerance, std::move(point_buffer_),
[this](Path::Polyline::PointBufferPtr point_buffer) {
point_buffer_ = std::move(point_buffer);
});
return polyline;
}

std::vector<Point> Tessellator::TessellateConvex(const Path& path,
Scalar tolerance) {
FML_DCHECK(point_buffer_);
std::vector<Point> output;

point_buffer_->clear();
auto polyline =
path.CreatePolyline(tolerance, std::move(point_buffer_),
Expand Down
9 changes: 8 additions & 1 deletion impeller/tessellator/tessellator.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <memory>
#include <vector>

#include "flutter/fml/macros.h"
#include "impeller/core/formats.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/point.h"
Expand Down Expand Up @@ -213,6 +212,14 @@ class Tessellator {
///
std::vector<Point> TessellateConvex(const Path& path, Scalar tolerance);

//----------------------------------------------------------------------------
/// @brief Create a temporary polyline. Only one per-process can exist at
/// a time.
///
/// The tessellator itself is not a thread safe class and should
/// only be used from the raster thread.
Path::Polyline CreateTempPolyline(const Path& path, Scalar tolerance);

/// @brief The pixel tolerance used by the algorighm to determine how
/// many divisions to create for a circle.
///
Expand Down
13 changes: 13 additions & 0 deletions impeller/tessellator/tessellator_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -484,5 +484,18 @@ TEST(TessellatorTest, FilledRoundRectTessellationVertices) {
Rect::MakeXYWH(5000, 10000, 2000, 3000), {50, 70});
}

#if !NDEBUG
TEST(TessellatorTest, ChecksConcurrentPolylineUsage) {
auto tessellator = std::make_shared<Tessellator>();
PathBuilder builder;
builder.AddLine({0, 0}, {100, 100});
auto path = builder.TakePath();

auto polyline = tessellator->CreateTempPolyline(path, 0.1);
EXPECT_DEBUG_DEATH(tessellator->CreateTempPolyline(path, 0.1),
"point_buffer_");
}
#endif // NDEBUG

} // namespace testing
} // namespace impeller