// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef FLUTTER_IMPELLER_GEOMETRY_PATH_BUILDER_H_ #define FLUTTER_IMPELLER_GEOMETRY_PATH_BUILDER_H_ #include "impeller/geometry/path.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" namespace impeller { class PathBuilder { public: /// Used for approximating quarter circle arcs with cubic curves. This is the /// control point distance which results in the smallest possible unit circle /// integration for a right angle arc. It can be used to approximate arcs less /// than 90 degrees to great effect by simply reducing it proportionally to /// the angle. However, accuracy rapidly diminishes if magnified for obtuse /// angle arcs, and so multiple cubic curves should be used when approximating /// arcs greater than 90 degrees. constexpr static const Scalar kArcApproximationMagic = 0.551915024494f; PathBuilder(); ~PathBuilder(); Path CopyPath(FillType fill = FillType::kNonZero); Path TakePath(FillType fill = FillType::kNonZero); /// @brief Reserve [point_size] points and [verb_size] verbs in the underlying /// path buffer. void Reserve(size_t point_size, size_t verb_size); PathBuilder& SetConvexity(Convexity value); PathBuilder& MoveTo(Point point, bool relative = false); PathBuilder& Close(); /// @brief Insert a line from the current position to `point`. /// /// If `relative` is true, then `point` is relative to the current location. PathBuilder& LineTo(Point point, bool relative = false); PathBuilder& HorizontalLineTo(Scalar x, bool relative = false); PathBuilder& VerticalLineTo(Scalar y, bool relative = false); /// @brief Insert a quadradic curve from the current position to `point` using /// the control point `controlPoint`. /// /// If `relative` is true the `point` and `controlPoint` are relative to /// current location. PathBuilder& QuadraticCurveTo(Point controlPoint, Point point, bool relative = false); /// @brief Insert a cubic curve from the curren position to `point` using the /// control points `controlPoint1` and `controlPoint2`. /// /// If `relative` is true the `point`, `controlPoint1`, and `controlPoint2` /// are relative to current location. PathBuilder& CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, bool relative = false); PathBuilder& AddRect(Rect rect); PathBuilder& AddCircle(const Point& center, Scalar radius); PathBuilder& AddArc(const Rect& oval_bounds, Radians start, Radians sweep, bool use_center = false); PathBuilder& AddOval(const Rect& rect); /// @brief Move to point `p1`, then insert a line from `p1` to `p2`. PathBuilder& AddLine(const Point& p1, const Point& p2); /// @brief Move to point `p1`, then insert a quadradic curve from `p1` to `p2` /// with the control point `cp`. PathBuilder& AddQuadraticCurve(Point p1, Point cp, Point p2); /// @brief Move to point `p1`, then insert a cubic curve from `p1` to `p2` /// with control points `cp1` and `cp2`. PathBuilder& AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2); /// @brief Transform the existing path segments and contours by the given /// `offset`. PathBuilder& Shift(Point offset); /// @brief Set the bounding box that will be used by `Path.GetBoundingBox` in /// place of performing the computation. /// /// When Impeller recieves Skia Path objects, many of these already /// have computed bounds. This method is used to avoid needlessly /// recomputing these bounds. PathBuilder& SetBounds(Rect bounds); struct RoundingRadii { Point top_left; Point bottom_left; Point top_right; Point bottom_right; RoundingRadii() = default; RoundingRadii(Scalar p_top_left, Scalar p_bottom_left, Scalar p_top_right, Scalar p_bottom_right) : top_left(p_top_left, p_top_left), bottom_left(p_bottom_left, p_bottom_left), top_right(p_top_right, p_top_right), bottom_right(p_bottom_right, p_bottom_right) {} explicit RoundingRadii(Scalar radius) : top_left(radius, radius), bottom_left(radius, radius), top_right(radius, radius), bottom_right(radius, radius) {} explicit RoundingRadii(Point radii) : top_left(radii), bottom_left(radii), top_right(radii), bottom_right(radii) {} explicit RoundingRadii(Size radii) : top_left(radii), bottom_left(radii), top_right(radii), bottom_right(radii) {} bool AreAllZero() const { return top_left.IsZero() && // bottom_left.IsZero() && // top_right.IsZero() && // bottom_right.IsZero(); } }; PathBuilder& AddRoundedRect(Rect rect, RoundingRadii radii); PathBuilder& AddRoundedRect(Rect rect, Size radii); PathBuilder& AddRoundedRect(Rect rect, Scalar radius); PathBuilder& AddPath(const Path& path); private: Point subpath_start_; Point current_; size_t current_contour_location_ = 0u; Path::Data prototype_; PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii); PathBuilder& AddRoundedRectTopRight(Rect rect, RoundingRadii radii); PathBuilder& AddRoundedRectBottomRight(Rect rect, RoundingRadii radii); PathBuilder& AddRoundedRectBottomLeft(Rect rect, RoundingRadii radii); void AddContourComponent(const Point& destination, bool is_closed = false); void SetContourClosed(bool is_closed); void AddLinearComponent(const Point& p1, const Point& p2); void AddQuadraticComponent(const Point& p1, const Point& cp, const Point& p2); void AddCubicComponent(const Point& p1, const Point& cp1, const Point& cp2, const Point& p2); /// Compute the bounds of the path unless they are already computed or /// set by an external source, such as |SetBounds|. Any call which mutates /// the path data can invalidate the computed/set bounds. void UpdateBounds(); std::optional> GetMinMaxCoveragePoints() const; PathBuilder(const PathBuilder&) = delete; PathBuilder& operator=(const PathBuilder&&) = delete; }; } // namespace impeller #endif // FLUTTER_IMPELLER_GEOMETRY_PATH_BUILDER_H_