| /* | |
| * Copyright 2025 Google LLC. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkPathData_DEFINED | |
| #define SkPathData_DEFINED | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPathTypes.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRRect.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSpan.h" | |
| #include "include/private/SkPathRef.h" | |
| #include "src/core/SkPathEnums.h" | |
| #include <array> | |
| #include <atomic> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <optional> | |
| struct SkPathRaw; | |
| /* | |
| * Immutable container for path geometry data: points, verbs, bounds, segment masks. | |
| * | |
| * All of the public factories check for valid input | |
| * - valid verb sequence | |
| * - corresponding # points | |
| * - finite point and conic values | |
| * | |
| * If any of these checks fail, null is returned. | |
| * | |
| * A valid sequence of verbs (and corresponding points/conics) is: | |
| * | |
| * Any number of contours (0 or more) | |
| * ... Each contour must begin with a single Move verb | |
| * ... followed by any number of segments: lines, quads, conics, cubics (0 or more) | |
| * ... followed by 0 or 1 Close verb | |
| * | |
| * A minor exception to these rules, is that the last contour may end with a single | |
| * Move verb -- this will be ignored in the resulting PathData. | |
| * | |
| * Given a valid verb sequence, there must be the corresponding number of points and | |
| * conics to match. If there are more or fewer, null is returned. | |
| * | |
| * Verb points conic_weights | |
| * ----------------------------- | |
| * Move 1 0 | |
| * Line 1 0 | |
| * Quad 2 0 | |
| * Conic 2 1 | |
| * Cubic 3 0 | |
| * Close 0 0 | |
| */ | |
| class SkPathData : public SkNVRefCnt<SkPathData> { | |
| public: | |
| /* | |
| * Returns an empty pathdata. | |
| * | |
| * Since this is immutable, it may return the same object each time it is called. | |
| */ | |
| static sk_sp<SkPathData> Empty(); | |
| /* | |
| * Return SkPathData with a copy of these buffers, or nullptr if they are illegal. | |
| * Illegal = non-finite, or non-sensical verb sequences | |
| */ | |
| static sk_sp<SkPathData> Make(SkSpan<const SkPoint> pts, | |
| SkSpan<const SkPathVerb> verbs, | |
| SkSpan<const float> conics = {}); | |
| /* | |
| * Attempt to transform src by the matrix. On success, return a new SkPathData | |
| * with the result, else return {}. | |
| */ | |
| static sk_sp<SkPathData> MakeTransform(const SkPathRaw& src, const SkMatrix&); | |
| /* | |
| * When a factory takes a startIndex, this refers to the position of the first point | |
| * when constructing one of our simple shapes: rect, oval, rrect. | |
| * The index is the same as that passed to the equivalent factories in SkPath | |
| * and in the associated addRect/Oval/RRect methods on SkPathBuilder. | |
| */ | |
| static sk_sp<SkPathData> Rect(const SkRect&, | |
| SkPathDirection = SkPathDirection::kDefault, | |
| unsigned startIndex = 0); | |
| static sk_sp<SkPathData> Oval(const SkRect&, | |
| SkPathDirection = SkPathDirection::kDefault, | |
| unsigned startIndex = 1); | |
| static sk_sp<SkPathData> RRect(const SkRRect&, SkPathDirection, unsigned startIndex); | |
| static sk_sp<SkPathData> RRect(const SkRRect& rrect, | |
| SkPathDirection dir = SkPathDirection::kDefault) { | |
| return RRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7); | |
| } | |
| static sk_sp<SkPathData> Polygon(SkSpan<const SkPoint> pts, bool isClosed); | |
| static sk_sp<SkPathData> Line(SkPoint a, SkPoint b) { | |
| return Polygon({a, b}, false); | |
| } | |
| friend bool operator==(const SkPathData& a, const SkPathData& b); | |
| friend bool operator!=(const SkPathData& a, const SkPathData& b) { | |
| return !(a == b); | |
| } | |
| SkSpan<const SkPoint> points() const { return fPoints; } | |
| SkSpan<const SkPathVerb> verbs() const { return fVerbs; } | |
| SkSpan<const float> conics() const { return fConics; } | |
| SkRect bounds() const { return fBounds; } | |
| uint8_t segmentMask() const { return fSegmentMask; } | |
| SkPathRaw raw(SkPathFillType, SkResolveConvexity) const; | |
| /** | |
| * Return true if the path contains no points or verbs | |
| */ | |
| bool empty() const { return fVerbs.empty(); } | |
| SkRect computeTightBounds() const; | |
| /** | |
| * Returns true if the pathdata is convex. | |
| * Note: if necessary, it will first compute the convexity (and cache it). | |
| */ | |
| bool isConvex() const; | |
| /** | |
| * Returns two points if SkPath contains only one line. If not, return {}. | |
| */ | |
| std::optional<std::array<SkPoint, 2>> asLine() const; | |
| /** | |
| * If the pathdata is recognized as a rect, return it and its direction and open/closed. | |
| * If not, return {} | |
| */ | |
| std::optional<SkPathRectInfo> asRect() const; | |
| /** | |
| * If the path is recognized as an oval, return its bounds. If not, return {}. | |
| */ | |
| std::optional<SkPathOvalInfo> asOval() const; | |
| /** | |
| * If the path is recognized as a round-rect, return it. If not, return {}. | |
| */ | |
| std::optional<SkPathRRectInfo> asRRect() const; | |
| /** | |
| * Attempt to transform the pathdata by the matrix. If this succeeds, return a new | |
| * pathdata object: note, this may have different verbs / number-of-points, if the | |
| * matrix contained perspective. | |
| * | |
| * If the matrix has no effect on the coordinates (e.g. it is identity), then this may | |
| * return a ref to the same pathdata object. | |
| * | |
| * If the result of applying the matrix creates any non-finite coordinates, this returns | |
| * nullptr. | |
| */ | |
| sk_sp<SkPathData> makeTransform(const SkMatrix&) const; | |
| sk_sp<SkPathData> makeOffset(SkVector) const; | |
| bool contains(SkPoint, SkPathFillType) const; | |
| private: | |
| friend class SkNVRefCnt<SkPathData>; | |
| friend class SkPathPriv; | |
| SkSpan<SkPoint> fPoints; | |
| SkSpan<float> fConics; | |
| SkSpan<SkPathVerb> fVerbs; | |
| SkRect fBounds; | |
| /* | |
| * Convexity can be slow to compute, and (in theory) it can't always survive a matrix | |
| * transform (due to numeric instability). Therefore we will lazily compute it as | |
| * requested. Since we are technically always immutable, we have to store this field | |
| * in an atomic. | |
| */ | |
| mutable std::atomic<uint8_t> fConvexity; // SkPathConvexity | |
| uint8_t fSegmentMask; // SkPathSegmentMask | |
| SkPathIsAType fType; | |
| SkPathIsAData fIsA {}; | |
| // | |
| // Memory layout after this (assuming we're not empty) | |
| // | |
| // [point data] | |
| // [conic data] | |
| // [verb data] | |
| // | |
| SkPathData(size_t npts, size_t nvbs, size_t ncns); | |
| // Ensure the unsized delete is called (since we're manually allocating the storage) | |
| void operator delete(void* p); | |
| // internal finisher when building a PathData. | |
| // If the optional value is not present, it will be computed (else checked in debug mode). | |
| // | |
| // In particular, if bounds is not provided, it will be computed, and if it proves | |
| // to be non-finite, false will be returned. | |
| bool finishInit(std::optional<SkRect> bounds, std::optional<uint8_t> segmentMask); | |
| // If we know we're a special shape, call this after the normal initialization | |
| void setupIsA(SkPathIsAType, SkPathDirection dir, unsigned startIndex); | |
| SkPathConvexity getConvexityOrUnknown() const; // may return kUnknown | |
| SkPathConvexity getResolvedConvexity() const; // never returns kUnknown | |
| void setConvexity(SkPathConvexity) const; // const -- but convexity is mutable | |
| static sk_sp<SkPathData> Alloc(size_t npts, size_t nvbs, size_t ncns); | |
| static sk_sp<SkPathData> MakeNoCheck(SkSpan<const SkPoint> pts, | |
| SkSpan<const SkPathVerb> verbs, | |
| SkSpan<const float> conics, | |
| std::optional<SkRect> bounds, | |
| std::optional<unsigned> segmentMask); | |
| static sk_sp<SkPathData> MakeNoCheck(const SkPathRaw&); | |
| }; | |
| #endif |