diff --git a/display_list/geometry/dl_path.cc b/display_list/geometry/dl_path.cc index 38fea72130ccf..e0d708999560d 100644 --- a/display_list/geometry/dl_path.cc +++ b/display_list/geometry/dl_path.cc @@ -6,6 +6,7 @@ #include "flutter/display_list/geometry/dl_geometry_types.h" #include "flutter/impeller/geometry/path_builder.h" +#include "impeller/geometry/path.h" namespace flutter { @@ -81,6 +82,9 @@ using FillType = impeller::FillType; using Convexity = impeller::Convexity; Path DlPath::ConvertToImpellerPath(const SkPath& path, const DlPoint& shift) { + if (path.isEmpty()) { + return impeller::Path{}; + } auto iterator = SkPath::Iter(path, false); struct PathData { diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 9db3663c09f89..7979f8399414e 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -5,7 +5,6 @@ #include "impeller/geometry/path.h" #include -#include #include "flutter/fml/logging.h" #include "impeller/geometry/path_component.h" @@ -36,12 +35,9 @@ size_t Path::GetComponentCount(std::optional type) const { return data_->components.size(); } auto type_value = type.value(); - if (type_value == ComponentType::kContour) { - return data_->contours.size(); - } size_t count = 0u; for (const auto& component : data_->components) { - if (component.type == type_value) { + if (component == type_value) { count++; } } @@ -57,50 +53,9 @@ bool Path::IsConvex() const { } bool Path::IsEmpty() const { - return data_->points.empty(); -} - -void Path::EnumerateComponents( - const Applier& linear_applier, - const Applier& quad_applier, - const Applier& cubic_applier, - const Applier& contour_applier) const { - auto& points = data_->points; - size_t currentIndex = 0; - for (const auto& component : data_->components) { - switch (component.type) { - case ComponentType::kLinear: - if (linear_applier) { - linear_applier(currentIndex, - LinearPathComponent(points[component.index], - points[component.index + 1])); - } - break; - case ComponentType::kQuadratic: - if (quad_applier) { - quad_applier(currentIndex, - QuadraticPathComponent(points[component.index], - points[component.index + 1], - points[component.index + 2])); - } - break; - case ComponentType::kCubic: - if (cubic_applier) { - cubic_applier(currentIndex, - CubicPathComponent(points[component.index], - points[component.index + 1], - points[component.index + 2], - points[component.index + 3])); - } - break; - case ComponentType::kContour: - if (contour_applier) { - contour_applier(currentIndex, data_->contours[component.index]); - } - break; - } - currentIndex++; - } + return data_->points.empty() || + (data_->components.size() == 1 && + data_->components[0] == ComponentType::kContour); } void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { @@ -109,14 +64,15 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { bool started_contour = false; bool first_point = true; + size_t storage_offset = 0u; for (size_t component_i = 0; component_i < path_components.size(); component_i++) { const auto& path_component = path_components[component_i]; - switch (path_component.type) { + switch (path_component) { case ComponentType::kLinear: { const LinearPathComponent* linear = reinterpret_cast( - &path_points[path_component.index]); + &path_points[storage_offset]); if (first_point) { writer.Write(linear->p1); first_point = false; @@ -127,7 +83,7 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { case ComponentType::kQuadratic: { const QuadraticPathComponent* quad = reinterpret_cast( - &path_points[path_component.index]); + &path_points[storage_offset]); if (first_point) { writer.Write(quad->p1); first_point = false; @@ -138,7 +94,7 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { case ComponentType::kCubic: { const CubicPathComponent* cubic = reinterpret_cast( - &path_points[path_component.index]); + &path_points[storage_offset]); if (first_point) { writer.Write(cubic->p1); first_point = false; @@ -146,7 +102,7 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { cubic->ToLinearPathComponents(scale, writer); break; } - case ComponentType::kContour: + case Path::ComponentType::kContour: if (component_i == path_components.size() - 1) { // If the last component is a contour, that means it's an empty // contour, so skip it. @@ -163,6 +119,7 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { started_contour = true; first_point = true; } + storage_offset += VerbToOffset(path_component); } if (started_contour) { writer.EndContour(); @@ -172,18 +129,18 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const { bool Path::GetLinearComponentAtIndex(size_t index, LinearPathComponent& linear) const { auto& components = data_->components; - - if (index >= components.size()) { + if (index >= components.size() || + components[index] != ComponentType::kLinear) { return false; } - if (components[index].type != ComponentType::kLinear) { - return false; + size_t storage_offset = 0u; + for (auto i = 0u; i < index; i++) { + storage_offset += VerbToOffset(components[i]); } - auto& points = data_->points; - auto point_index = components[index].index; - linear = LinearPathComponent(points[point_index], points[point_index + 1]); + linear = + LinearPathComponent(points[storage_offset], points[storage_offset + 1]); return true; } @@ -191,54 +148,58 @@ bool Path::GetQuadraticComponentAtIndex( size_t index, QuadraticPathComponent& quadratic) const { auto& components = data_->components; - - if (index >= components.size()) { + if (index >= components.size() || + components[index] != ComponentType::kQuadratic) { return false; } - if (components[index].type != ComponentType::kQuadratic) { - return false; + size_t storage_offset = 0u; + for (auto i = 0u; i < index; i++) { + storage_offset += VerbToOffset(components[i]); } - auto& points = data_->points; - auto point_index = components[index].index; - quadratic = QuadraticPathComponent( - points[point_index], points[point_index + 1], points[point_index + 2]); + + quadratic = + QuadraticPathComponent(points[storage_offset], points[storage_offset + 1], + points[storage_offset + 2]); return true; } bool Path::GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const { auto& components = data_->components; - - if (index >= components.size()) { + if (index >= components.size() || + components[index] != ComponentType::kCubic) { return false; } - if (components[index].type != ComponentType::kCubic) { - return false; + size_t storage_offset = 0u; + for (auto i = 0u; i < index; i++) { + storage_offset += VerbToOffset(components[i]); } - auto& points = data_->points; - auto point_index = components[index].index; - cubic = CubicPathComponent(points[point_index], points[point_index + 1], - points[point_index + 2], points[point_index + 3]); + + cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1], + points[storage_offset + 2], + points[storage_offset + 3]); return true; } bool Path::GetContourComponentAtIndex(size_t index, ContourComponent& move) const { auto& components = data_->components; - - if (index >= components.size()) { + if (index >= components.size() || + components[index] != ComponentType::kContour) { return false; } - if (components[index].type != ComponentType::kContour) { - return false; + size_t storage_offset = 0u; + for (auto i = 0u; i < index; i++) { + storage_offset += VerbToOffset(components[i]); } + auto& points = data_->points; - move = data_->contours[components[index].index]; + move = ContourComponent(points[storage_offset], points[storage_offset + 1]); return true; } @@ -261,144 +222,165 @@ Path::Polyline::~Polyline() { } } -Path::Polyline Path::CreatePolyline( - Scalar scale, - Path::Polyline::PointBufferPtr point_buffer, - Path::Polyline::ReclaimPointBufferCallback reclaim) const { - Polyline polyline(std::move(point_buffer), std::move(reclaim)); - +void Path::EndContour( + size_t storage_offset, + Polyline& polyline, + size_t component_index, + std::vector& poly_components) const { auto& path_components = data_->components; auto& path_points = data_->points; + // Whenever a contour has ended, extract the exact end direction from + // the last component. + if (polyline.contours.empty() || component_index == 0) { + return; + } - auto get_path_component = [&path_components, &path_points]( - size_t component_i) -> PathComponentVariant { - if (component_i >= path_components.size()) { - return std::monostate{}; - } - const auto& component = path_components[component_i]; - switch (component.type) { - case ComponentType::kLinear: - return reinterpret_cast( - &path_points[component.index]); - case ComponentType::kQuadratic: - return reinterpret_cast( - &path_points[component.index]); - case ComponentType::kCubic: - return reinterpret_cast( - &path_points[component.index]); - case ComponentType::kContour: - return std::monostate{}; - } - }; - - auto compute_contour_start_direction = - [&get_path_component](size_t current_path_component_index) { - size_t next_component_index = current_path_component_index + 1; - while (!std::holds_alternative( - get_path_component(next_component_index))) { - auto next_component = get_path_component(next_component_index); - auto maybe_vector = - std::visit(PathComponentStartDirectionVisitor(), next_component); - if (maybe_vector.has_value()) { - return maybe_vector.value(); - } else { - next_component_index++; - } - } - return Vector2(0, -1); - }; - - std::vector poly_components; - std::optional previous_path_component_index; - auto end_contour = [&polyline, &previous_path_component_index, - &get_path_component, &poly_components]() { - // Whenever a contour has ended, extract the exact end direction from - // the last component. - if (polyline.contours.empty()) { - return; - } + auto& contour = polyline.contours.back(); + contour.end_direction = Vector2(0, 1); + contour.components = poly_components; + poly_components.clear(); - if (!previous_path_component_index.has_value()) { - return; - } + size_t previous_index = component_index - 1; + storage_offset -= VerbToOffset(path_components[previous_index]); - auto& contour = polyline.contours.back(); - contour.end_direction = Vector2(0, 1); - contour.components = poly_components; - poly_components.clear(); - - size_t previous_index = previous_path_component_index.value(); - while (!std::holds_alternative( - get_path_component(previous_index))) { - auto previous_component = get_path_component(previous_index); - auto maybe_vector = - std::visit(PathComponentEndDirectionVisitor(), previous_component); - if (maybe_vector.has_value()) { - contour.end_direction = maybe_vector.value(); + while (previous_index >= 0 && storage_offset >= 0) { + const auto& path_component = path_components[previous_index]; + switch (path_component) { + case ComponentType::kLinear: { + auto* linear = reinterpret_cast( + &path_points[storage_offset]); + auto maybe_end = linear->GetEndDirection(); + if (maybe_end.has_value()) { + contour.end_direction = maybe_end.value(); + return; + } break; - } else { - if (previous_index == 0) { - break; + } + case ComponentType::kQuadratic: { + auto* quad = reinterpret_cast( + &path_points[storage_offset]); + auto maybe_end = quad->GetEndDirection(); + if (maybe_end.has_value()) { + contour.end_direction = maybe_end.value(); + return; + } + break; + } + case ComponentType::kCubic: { + auto* cubic = reinterpret_cast( + &path_points[storage_offset]); + auto maybe_end = cubic->GetEndDirection(); + if (maybe_end.has_value()) { + contour.end_direction = maybe_end.value(); + return; } - previous_index--; + break; } + case ComponentType::kContour: { + // Hit previous contour, return. + return; + }; } - }; + storage_offset -= VerbToOffset(path_component); + previous_index--; + } +}; - for (size_t component_i = 0; component_i < path_components.size(); - component_i++) { - const auto& path_component = path_components[component_i]; - switch (path_component.type) { - case ComponentType::kLinear: +Path::Polyline Path::CreatePolyline( + Scalar scale, + Path::Polyline::PointBufferPtr point_buffer, + Path::Polyline::ReclaimPointBufferCallback reclaim) const { + Polyline polyline(std::move(point_buffer), std::move(reclaim)); + + auto& path_components = data_->components; + auto& path_points = data_->points; + std::optional start_direction; + std::vector poly_components; + size_t storage_offset = 0u; + size_t component_i = 0; + + for (; component_i < path_components.size(); component_i++) { + auto path_component = path_components[component_i]; + switch (path_component) { + case ComponentType::kLinear: { poly_components.push_back({ .component_start_index = polyline.points->size() - 1, .is_curve = false, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(*polyline.points); - previous_path_component_index = component_i; + auto* linear = reinterpret_cast( + &path_points[storage_offset]); + linear->AppendPolylinePoints(*polyline.points); + if (!start_direction.has_value()) { + start_direction = linear->GetStartDirection(); + } break; - case ComponentType::kQuadratic: + } + case ComponentType::kQuadratic: { poly_components.push_back({ .component_start_index = polyline.points->size() - 1, .is_curve = true, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(scale, *polyline.points); - previous_path_component_index = component_i; + auto* quad = reinterpret_cast( + &path_points[storage_offset]); + quad->AppendPolylinePoints(scale, *polyline.points); + if (!start_direction.has_value()) { + start_direction = quad->GetStartDirection(); + } break; - case ComponentType::kCubic: + } + case ComponentType::kCubic: { poly_components.push_back({ .component_start_index = polyline.points->size() - 1, .is_curve = true, }); - reinterpret_cast( - &path_points[path_component.index]) - ->AppendPolylinePoints(scale, *polyline.points); - previous_path_component_index = component_i; + auto* cubic = reinterpret_cast( + &path_points[storage_offset]); + cubic->AppendPolylinePoints(scale, *polyline.points); + if (!start_direction.has_value()) { + start_direction = cubic->GetStartDirection(); + } break; + } case ComponentType::kContour: if (component_i == path_components.size() - 1) { // If the last component is a contour, that means it's an empty // contour, so skip it. - continue; + break; } - end_contour(); - - Vector2 start_direction = compute_contour_start_direction(component_i); - const auto& contour = data_->contours[path_component.index]; - polyline.contours.push_back({.start_index = polyline.points->size(), - .is_closed = contour.is_closed, - .start_direction = start_direction, - .components = poly_components}); + if (!polyline.contours.empty()) { + polyline.contours.back().start_direction = + start_direction.value_or(Vector2(0, -1)); + start_direction = std::nullopt; + } + EndContour(storage_offset, polyline, component_i, poly_components); + + auto* contour = reinterpret_cast( + &path_points[storage_offset]); + polyline.contours.push_back(PolylineContour{ + .start_index = polyline.points->size(), // + .is_closed = contour->IsClosed(), // + .start_direction = Vector2(0, -1), // + .components = poly_components // + }); - polyline.points->push_back(contour.destination); + polyline.points->push_back(contour->destination); break; } + storage_offset += VerbToOffset(path_component); + } + + // Subtract the last storage offset increment so that the storage lookup is + // correct, including potentially an empty contour as well. + if (component_i > 0 && path_components.back() == ComponentType::kContour) { + storage_offset -= VerbToOffset(ComponentType::kContour); + component_i--; + } + + if (!polyline.contours.empty()) { + polyline.contours.back().start_direction = + start_direction.value_or(Vector2(0, -1)); } - end_contour(); + EndContour(storage_offset, polyline, component_i, poly_components); return polyline; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 0eebfab5cfd1e..27a345ec082ab 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -58,6 +58,21 @@ class Path { kContour, }; + static constexpr size_t VerbToOffset(Path::ComponentType verb) { + switch (verb) { + case Path::ComponentType::kLinear: + return 2u; + case Path::ComponentType::kQuadratic: + return 3u; + case Path::ComponentType::kCubic: + return 4u; + case Path::ComponentType::kContour: + return 2u; + break; + } + FML_UNREACHABLE(); + } + struct PolylineContour { struct Component { size_t component_start_index; @@ -138,14 +153,6 @@ class Path { bool IsEmpty() const; - template - using Applier = std::function; - void EnumerateComponents( - const Applier& linear_applier, - const Applier& quad_applier, - const Applier& cubic_applier, - const Applier& contour_applier) const; - bool GetLinearComponentAtIndex(size_t index, LinearPathComponent& linear) const; @@ -169,6 +176,12 @@ class Path { std::make_unique>(), Polyline::ReclaimPointBufferCallback reclaim = nullptr) const; + void EndContour( + size_t storage_offset, + Polyline& polyline, + size_t component_index, + std::vector& poly_components) const; + std::optional GetBoundingBox() const; std::optional GetTransformedBoundingBox(const Matrix& transform) const; @@ -183,16 +196,6 @@ class Path { private: friend class PathBuilder; - struct ComponentIndexPair { - ComponentType type = ComponentType::kLinear; - size_t index = 0; - - ComponentIndexPair() {} - - ComponentIndexPair(ComponentType a_type, size_t a_index) - : type(a_type), index(a_index) {} - }; - // All of the data for the path is stored in this structure which is // held by a shared_ptr. Since they all share the structure, the // copy constructor for Path is very cheap and we don't need to deal @@ -213,11 +216,9 @@ class Path { FillType fill = FillType::kNonZero; Convexity convexity = Convexity::kUnknown; - std::vector components; - std::vector points; - std::vector contours; - std::optional bounds; + std::vector points; + std::vector components; }; explicit Path(Data data); diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index be591d92884c7..e6b8228f148d7 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -6,6 +6,8 @@ #include +#include "impeller/geometry/path_component.h" + namespace impeller { PathBuilder::PathBuilder() { @@ -22,6 +24,7 @@ Path PathBuilder::CopyPath(FillType fill) { Path PathBuilder::TakePath(FillType fill) { prototype_.fill = fill; UpdateBounds(); + current_contour_location_ = 0u; return Path(std::move(prototype_)); } @@ -264,24 +267,27 @@ PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect, void PathBuilder::AddContourComponent(const Point& destination, bool is_closed) { auto& components = prototype_.components; - auto& contours = prototype_.contours; + auto& points = prototype_.points; + auto closed = is_closed ? Point{0, 0} : Point{1, 1}; if (components.size() > 0 && - components.back().type == Path::ComponentType::kContour) { + components.back() == Path::ComponentType::kContour) { // Never insert contiguous contours. - contours.back() = ContourComponent(destination, is_closed); + points[current_contour_location_] = destination; + points[current_contour_location_ + 1] = closed; } else { - contours.emplace_back(ContourComponent(destination, is_closed)); - components.emplace_back(Path::ComponentType::kContour, contours.size() - 1); + current_contour_location_ = points.size(); + points.push_back(destination); + points.push_back(closed); + components.push_back(Path::ComponentType::kContour); } prototype_.bounds.reset(); } void PathBuilder::AddLinearComponent(const Point& p1, const Point& p2) { auto& points = prototype_.points; - auto index = points.size(); - points.emplace_back(p1); - points.emplace_back(p2); - prototype_.components.emplace_back(Path::ComponentType::kLinear, index); + points.push_back(p1); + points.push_back(p2); + prototype_.components.push_back(Path::ComponentType::kLinear); prototype_.bounds.reset(); } @@ -289,11 +295,10 @@ void PathBuilder::AddQuadraticComponent(const Point& p1, const Point& cp, const Point& p2) { auto& points = prototype_.points; - auto index = points.size(); - points.emplace_back(p1); - points.emplace_back(cp); - points.emplace_back(p2); - prototype_.components.emplace_back(Path::ComponentType::kQuadratic, index); + points.push_back(p1); + points.push_back(cp); + points.push_back(p2); + prototype_.components.push_back(Path::ComponentType::kQuadratic); prototype_.bounds.reset(); } @@ -302,17 +307,17 @@ void PathBuilder::AddCubicComponent(const Point& p1, const Point& cp2, const Point& p2) { auto& points = prototype_.points; - auto index = points.size(); - points.emplace_back(p1); - points.emplace_back(cp1); - points.emplace_back(cp2); - points.emplace_back(p2); - prototype_.components.emplace_back(Path::ComponentType::kCubic, index); + points.push_back(p1); + points.push_back(cp1); + points.push_back(cp2); + points.push_back(p2); + prototype_.components.push_back(Path::ComponentType::kCubic); prototype_.bounds.reset(); } void PathBuilder::SetContourClosed(bool is_closed) { - prototype_.contours.back().is_closed = is_closed; + prototype_.points[current_contour_location_ + 1] = + is_closed ? Point{0, 0} : Point{1, 1}; } PathBuilder& PathBuilder::AddArc(const Rect& oval_bounds, @@ -428,29 +433,60 @@ PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) { } PathBuilder& PathBuilder::AddPath(const Path& path) { - auto linear = [&](size_t index, const LinearPathComponent& l) { - AddLinearComponent(l.p1, l.p2); - }; - auto quadratic = [&](size_t index, const QuadraticPathComponent& q) { - AddQuadraticComponent(q.p1, q.cp, q.p2); - }; - auto cubic = [&](size_t index, const CubicPathComponent& c) { - AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2); - }; - auto move = [&](size_t index, const ContourComponent& m) { - AddContourComponent(m.destination); - }; - path.EnumerateComponents(linear, quadratic, cubic, move); + auto& points = prototype_.points; + auto& components = prototype_.components; + + points.insert(points.end(), path.data_->points.begin(), + path.data_->points.end()); + components.insert(components.end(), path.data_->components.begin(), + path.data_->components.end()); + + size_t source_offset = points.size(); + for (auto component : path.data_->components) { + if (component == Path::ComponentType::kContour) { + current_contour_location_ = source_offset; + } + source_offset += Path::VerbToOffset(component); + } return *this; } PathBuilder& PathBuilder::Shift(Point offset) { - for (auto& point : prototype_.points) { - point += offset; - } - for (auto& contour : prototype_.contours) { - contour.destination += offset; + auto& points = prototype_.points; + size_t storage_offset = 0u; + for (const auto& component : prototype_.components) { + switch (component) { + case Path::ComponentType::kLinear: { + auto* linear = + reinterpret_cast(&points[storage_offset]); + linear->p1 += offset; + linear->p2 += offset; + break; + } + case Path::ComponentType::kQuadratic: { + auto* quad = + reinterpret_cast(&points[storage_offset]); + quad->p1 += offset; + quad->p2 += offset; + quad->cp += offset; + } break; + case Path::ComponentType::kCubic: { + auto* cubic = + reinterpret_cast(&points[storage_offset]); + cubic->p1 += offset; + cubic->p2 += offset; + cubic->cp1 += offset; + cubic->cp2 += offset; + } break; + case Path::ComponentType::kContour: + auto* contour = + reinterpret_cast(&points[storage_offset]); + contour->destination += offset; + break; + } + storage_offset += Path::VerbToOffset(component); } + prototype_.bounds.reset(); return *this; } @@ -499,11 +535,12 @@ std::optional> PathBuilder::GetMinMaxCoveragePoints() } }; + size_t storage_offset = 0u; for (const auto& component : prototype_.components) { - switch (component.type) { + switch (component) { case Path::ComponentType::kLinear: { auto* linear = reinterpret_cast( - &points[component.index]); + &points[storage_offset]); clamp(linear->p1); clamp(linear->p2); break; @@ -511,14 +548,14 @@ std::optional> PathBuilder::GetMinMaxCoveragePoints() case Path::ComponentType::kQuadratic: for (const auto& extrema : reinterpret_cast( - &points[component.index]) + &points[storage_offset]) ->Extrema()) { clamp(extrema); } break; case Path::ComponentType::kCubic: for (const auto& extrema : reinterpret_cast( - &points[component.index]) + &points[storage_offset]) ->Extrema()) { clamp(extrema); } @@ -526,6 +563,7 @@ std::optional> PathBuilder::GetMinMaxCoveragePoints() case Path::ComponentType::kContour: break; } + storage_offset += Path::VerbToOffset(component); } if (!min.has_value() || !max.has_value()) { diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 0907b7b67f0f4..f4de96d18b704 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -156,6 +156,7 @@ class PathBuilder { private: Point subpath_start_; Point current_; + size_t current_contour_location_ = 0u; Path::Data prototype_; PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii); diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 6c3492fd2ade4..d990f37ed7f73 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -373,52 +373,4 @@ std::optional CubicPathComponent::GetEndDirection() const { return std::nullopt; } -std::optional PathComponentStartDirectionVisitor::operator()( - const LinearPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetStartDirection(); -} - -std::optional PathComponentStartDirectionVisitor::operator()( - const QuadraticPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetStartDirection(); -} - -std::optional PathComponentStartDirectionVisitor::operator()( - const CubicPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetStartDirection(); -} - -std::optional PathComponentEndDirectionVisitor::operator()( - const LinearPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetEndDirection(); -} - -std::optional PathComponentEndDirectionVisitor::operator()( - const QuadraticPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetEndDirection(); -} - -std::optional PathComponentEndDirectionVisitor::operator()( - const CubicPathComponent* component) { - if (!component) { - return std::nullopt; - } - return component->GetEndDirection(); -} - } // namespace impeller diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index dca03126bb0dc..b54735dba61f2 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "impeller/geometry/point.h" @@ -150,38 +149,19 @@ struct CubicPathComponent { struct ContourComponent { Point destination; - bool is_closed = false; - ContourComponent() {} + // 0, 0 for closed, anything else for open. + Point closed = Point(1, 1); - explicit ContourComponent(Point p, bool is_closed = false) - : destination(p), is_closed(is_closed) {} + ContourComponent() {} - bool operator==(const ContourComponent& other) const { - return destination == other.destination && is_closed == other.is_closed; - } -}; + constexpr bool IsClosed() const { return closed == Point{0, 0}; } -using PathComponentVariant = std::variant; - -struct PathComponentStartDirectionVisitor { - std::optional operator()(const LinearPathComponent* component); - std::optional operator()(const QuadraticPathComponent* component); - std::optional operator()(const CubicPathComponent* component); - std::optional operator()(std::monostate monostate) { - return std::nullopt; - } -}; + explicit ContourComponent(Point p, Point closed) + : destination(p), closed(closed) {} -struct PathComponentEndDirectionVisitor { - std::optional operator()(const LinearPathComponent* component); - std::optional operator()(const QuadraticPathComponent* component); - std::optional operator()(const CubicPathComponent* component); - std::optional operator()(std::monostate monostate) { - return std::nullopt; + bool operator==(const ContourComponent& other) const { + return destination == other.destination && IsClosed() == other.IsClosed(); } }; diff --git a/impeller/geometry/path_unittests.cc b/impeller/geometry/path_unittests.cc index 54f6900a0f9a2..f37689f5e8cf3 100644 --- a/impeller/geometry/path_unittests.cc +++ b/impeller/geometry/path_unittests.cc @@ -8,6 +8,7 @@ #include "impeller/geometry/geometry_asserts.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/path_component.h" namespace impeller { namespace testing { @@ -22,6 +23,13 @@ TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) { ASSERT_EQ(polyline.back().y, 40); } +TEST(PathTest, EmptyPathWithContour) { + PathBuilder builder; + auto path = builder.TakePath(); + + EXPECT_TRUE(path.IsEmpty()); +} + TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) { PathBuilder builder; builder.MoveTo({10, 10}); @@ -48,7 +56,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(100, 50)); - EXPECT_TRUE(contour.is_closed); + EXPECT_TRUE(contour.IsClosed()); } { @@ -57,7 +65,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(150, 100)); - EXPECT_TRUE(contour.is_closed); + EXPECT_TRUE(contour.IsClosed()); } { @@ -66,7 +74,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(100, 100)); - EXPECT_TRUE(contour.is_closed); + EXPECT_TRUE(contour.IsClosed()); } { @@ -76,7 +84,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(110, 100)); - EXPECT_TRUE(contour.is_closed); + EXPECT_TRUE(contour.IsClosed()); } { @@ -87,7 +95,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(110, 100)); - EXPECT_TRUE(contour.is_closed); + EXPECT_TRUE(contour.IsClosed()); } // Open shapes. @@ -97,7 +105,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); ASSERT_POINT_NEAR(contour.destination, p); - ASSERT_FALSE(contour.is_closed); + ASSERT_FALSE(contour.IsClosed()); } { @@ -108,7 +116,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); ASSERT_POINT_NEAR(contour.destination, Point(100, 100)); - ASSERT_FALSE(contour.is_closed); + ASSERT_FALSE(contour.IsClosed()); } { @@ -118,7 +126,7 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { ContourComponent contour; path.GetContourComponentAtIndex(0, contour); ASSERT_POINT_NEAR(contour.destination, Point(100, 100)); - ASSERT_FALSE(contour.is_closed); + ASSERT_FALSE(contour.IsClosed()); } } @@ -402,56 +410,74 @@ TEST(PathTest, SimplePath) { .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600}) .TakePath(); - ASSERT_EQ(path.GetComponentCount(), 6u); - ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u); - ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u); - ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u); - ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u); - - path.EnumerateComponents( - [](size_t index, const LinearPathComponent& linear) { - Point p1(0, 0); - Point p2(100, 100); - ASSERT_EQ(index, 1u); - ASSERT_EQ(linear.p1, p1); - ASSERT_EQ(linear.p2, p2); - }, - [](size_t index, const QuadraticPathComponent& quad) { - Point p1(100, 100); - Point cp(200, 200); - Point p2(300, 300); - ASSERT_EQ(index, 3u); - ASSERT_EQ(quad.p1, p1); - ASSERT_EQ(quad.cp, cp); - ASSERT_EQ(quad.p2, p2); - }, - [](size_t index, const CubicPathComponent& cubic) { - Point p1(300, 300); - Point cp1(400, 400); - Point cp2(500, 500); - Point p2(600, 600); - ASSERT_EQ(index, 5u); - ASSERT_EQ(cubic.p1, p1); - ASSERT_EQ(cubic.cp1, cp1); - ASSERT_EQ(cubic.cp2, cp2); - ASSERT_EQ(cubic.p2, p2); - }, - [](size_t index, const ContourComponent& contour) { - // There is an initial countour added for each curve. - if (index == 0u) { - Point p1(0, 0); - ASSERT_EQ(contour.destination, p1); - } else if (index == 2u) { - Point p1(100, 100); - ASSERT_EQ(contour.destination, p1); - } else if (index == 4u) { - Point p1(300, 300); - ASSERT_EQ(contour.destination, p1); - } else { - ASSERT_FALSE(true); - } - ASSERT_FALSE(contour.is_closed); - }); + EXPECT_EQ(path.GetComponentCount(), 6u); + EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u); + EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u); + EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u); + EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u); + + { + LinearPathComponent linear; + EXPECT_TRUE(path.GetLinearComponentAtIndex(1, linear)); + + Point p1(0, 0); + Point p2(100, 100); + EXPECT_EQ(linear.p1, p1); + EXPECT_EQ(linear.p2, p2); + } + + { + QuadraticPathComponent quad; + EXPECT_TRUE(path.GetQuadraticComponentAtIndex(3, quad)); + + Point p1(100, 100); + Point cp(200, 200); + Point p2(300, 300); + EXPECT_EQ(quad.p1, p1); + EXPECT_EQ(quad.cp, cp); + EXPECT_EQ(quad.p2, p2); + } + + { + CubicPathComponent cubic; + EXPECT_TRUE(path.GetCubicComponentAtIndex(5, cubic)); + + Point p1(300, 300); + Point cp1(400, 400); + Point cp2(500, 500); + Point p2(600, 600); + EXPECT_EQ(cubic.p1, p1); + EXPECT_EQ(cubic.cp1, cp1); + EXPECT_EQ(cubic.cp2, cp2); + EXPECT_EQ(cubic.p2, p2); + } + + { + ContourComponent contour; + EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour)); + + Point p1(0, 0); + EXPECT_EQ(contour.destination, p1); + EXPECT_FALSE(contour.IsClosed()); + } + + { + ContourComponent contour; + EXPECT_TRUE(path.GetContourComponentAtIndex(2, contour)); + + Point p1(100, 100); + EXPECT_EQ(contour.destination, p1); + EXPECT_FALSE(contour.IsClosed()); + } + + { + ContourComponent contour; + EXPECT_TRUE(path.GetContourComponentAtIndex(4, contour)); + + Point p1(300, 300); + EXPECT_EQ(contour.destination, p1); + EXPECT_FALSE(contour.IsClosed()); + } } TEST(PathTest, RepeatCloseDoesNotAddNewLines) { @@ -553,7 +579,7 @@ TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) { ContourComponent contour; EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour)) << label; EXPECT_EQ(contour.destination, offset + Point(10, 10)) << label; - EXPECT_EQ(contour.is_closed, is_closed) << label; + EXPECT_EQ(contour.IsClosed(), is_closed) << label; } { LinearPathComponent line;