xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrStyle.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrStyle_DEFINED
9 #define GrStyle_DEFINED
10 
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkPathEffect.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkStrokeRec.h"
18 #include "include/private/base/SkAssert.h"
19 #include "include/private/base/SkMalloc.h"
20 #include "include/private/base/SkTemplates.h"
21 #include "include/private/base/SkTo.h"
22 #include "src/core/SkPathEffectBase.h"
23 
24 #include <cstdint>
25 #include <utility>
26 
27 class SkPath;
28 
29 /**
30  * Represents the various ways that a GrStyledShape can be styled. It has fill/stroking information
31  * as well as an optional path effect. If the path effect represents dashing, the dashing
32  * information is extracted from the path effect and stored explicitly.
33  *
34  * This will replace GrStrokeInfo as GrStyledShape is deployed.
35  */
36 class GrStyle {
37 public:
38     /**
39      * A style object that represents a fill with no path effect.
40      * TODO: constexpr with C++14
41      */
SimpleFill()42     static const GrStyle& SimpleFill() {
43         static const GrStyle kFill(SkStrokeRec::kFill_InitStyle);
44         return kFill;
45         }
46 
47     /**
48      * A style object that represents a hairline stroke with no path effect.
49      * TODO: constexpr with C++14
50      */
SimpleHairline()51     static const GrStyle& SimpleHairline() {
52         static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle);
53         return kHairline;
54     }
55 
56     enum class Apply {
57         kPathEffectOnly,
58         kPathEffectAndStrokeRec
59     };
60 
61     /**
62      * Optional flags for computing keys that may remove unnecessary variation in the key due to
63      * style settings that don't affect particular classes of geometry.
64      */
65     enum KeyFlags {
66         // The shape being styled has no open contours.
67         kClosed_KeyFlag = 0x1,
68         // The shape being styled doesn't have any joins and so isn't affected by join type.
69         kNoJoins_KeyFlag = 0x2
70     };
71 
72     /**
73      * Computes the key length for a GrStyle. The return will be negative if it cannot be turned
74      * into a key. This occurs when there is a path effect that is not a dash. The key can
75      * either reflect just the path effect (if one) or the path effect and the strokerec. Note
76      * that a simple fill has a zero sized key.
77      */
78     static int KeySize(const GrStyle&, Apply, uint32_t flags = 0);
79 
80     /**
81      * Writes a unique key for the style into the provided buffer. This function assumes the buffer
82      * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative
83      * value for the combination of GrStyle, Apply and flags params. This is written so that the key
84      * for just dash application followed by the key for the remaining SkStrokeRec is the same as
85      * the key for applying dashing and SkStrokeRec all at once.
86      */
87     static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0);
88 
GrStyle()89     GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {}
90 
GrStyle(SkStrokeRec::InitStyle initStyle)91     explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {}
92 
GrStyle(const SkStrokeRec & strokeRec,sk_sp<SkPathEffect> pe)93     GrStyle(const SkStrokeRec& strokeRec, sk_sp<SkPathEffect> pe) : fStrokeRec(strokeRec) {
94         this->initPathEffect(std::move(pe));
95     }
96 
97     GrStyle(const GrStyle& that) = default;
98 
GrStyle(const SkPaint & paint)99     explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) {
100         this->initPathEffect(paint.refPathEffect());
101     }
102 
GrStyle(const SkPaint & paint,SkPaint::Style overrideStyle)103     explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle)
104             : fStrokeRec(paint, overrideStyle) {
105         this->initPathEffect(paint.refPathEffect());
106     }
107 
108     GrStyle& operator=(const GrStyle& that) {
109         fPathEffect = that.fPathEffect;
110         fDashInfo = that.fDashInfo;
111         fStrokeRec = that.fStrokeRec;
112         return *this;
113     }
114 
resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline)115     void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) {
116         fDashInfo.reset();
117         fPathEffect.reset(nullptr);
118         if (SkStrokeRec::kFill_InitStyle == fillOrHairline) {
119             fStrokeRec.setFillStyle();
120         } else {
121             fStrokeRec.setHairlineStyle();
122         }
123     }
124 
125     /** Is this style a fill with no path effect? */
isSimpleFill()126     bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; }
127 
128     /** Is this style a hairline with no path effect? */
isSimpleHairline()129     bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; }
130 
pathEffect()131     SkPathEffect* pathEffect() const { return fPathEffect.get(); }
refPathEffect()132     sk_sp<SkPathEffect> refPathEffect() const { return fPathEffect; }
133 
hasPathEffect()134     bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }
135 
hasNonDashPathEffect()136     bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); }
137 
isDashed()138     bool isDashed() const { return SkPathEffectBase::DashType::kDash == fDashInfo.fType; }
dashPhase()139     SkScalar dashPhase() const {
140         SkASSERT(this->isDashed());
141         return fDashInfo.fPhase;
142     }
dashIntervalCnt()143     int dashIntervalCnt() const {
144         SkASSERT(this->isDashed());
145         return fDashInfo.fIntervals.count();
146     }
dashIntervals()147     const SkScalar* dashIntervals() const {
148         SkASSERT(this->isDashed());
149         return fDashInfo.fIntervals.get();
150     }
151 
strokeRec()152     const SkStrokeRec& strokeRec() const { return fStrokeRec; }
153 
154     /** Hairline or fill styles without path effects make no alterations to a geometry. */
applies()155     bool applies() const {
156         return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle());
157     }
158 
MatrixToScaleFactor(const SkMatrix & matrix)159     static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) {
160         // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale
161         // factor of 1. This isn't necessarily a good choice and in the future we might consider
162         // taking a bounds here for the perspective case.
163         return SkScalarAbs(matrix.getMaxScale());
164     }
165     /**
166      * Applies just the path effect and returns remaining stroke information. This will fail if
167      * there is no path effect. dst may or may not have been overwritten on failure. Scale controls
168      * geometric approximations made by the path effect. It is typically computed from the view
169      * matrix.
170      */
171     [[nodiscard]] bool applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke,
172                                              const SkPath& src, SkScalar scale) const;
173 
174     /**
175      * If this succeeds then the result path should be filled or hairlined as indicated by the
176      * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the
177      * strokerec doesn't change the geometry. When this fails the outputs may or may not have
178      * been overwritten. Scale controls geometric approximations made by the path effect and
179      * stroker. It is typically computed from the view matrix.
180      */
181     [[nodiscard]] bool applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline,
182                                    const SkPath& src, SkScalar scale) const;
183 
184     /** Given bounds of a path compute the bounds of path with the style applied. */
adjustBounds(SkRect * dst,const SkRect & src)185     void adjustBounds(SkRect* dst, const SkRect& src) const {
186         *dst = src;
187         auto pe = as_PEB(this->pathEffect());
188         if (pe && !pe->computeFastBounds(dst)) {
189             // Restore dst == src since ComputeFastBounds leaves it undefined when returning false
190             *dst = src;
191         }
192 
193         // This may not be the correct SkStrokeRec to use if there's a path effect: skbug.com/5299
194         // It happens to work for dashing.
195         SkScalar radius = fStrokeRec.getInflationRadius();
196         dst->outset(radius, radius);
197     }
198 
199 private:
200     void initPathEffect(sk_sp<SkPathEffect> pe);
201 
202     struct DashInfo {
DashInfoDashInfo203         DashInfo() : fType(SkPathEffectBase::DashType::kNone) {}
DashInfoDashInfo204         DashInfo(const DashInfo& that) { *this = that; }
205         DashInfo& operator=(const DashInfo& that) {
206             fType = that.fType;
207             fPhase = that.fPhase;
208             fIntervals.reset(that.fIntervals.count());
209             sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(),
210                               sizeof(SkScalar) * that.fIntervals.count());
211             return *this;
212         }
resetDashInfo213         void reset() {
214             fType = SkPathEffectBase::DashType::kNone;
215             fIntervals.reset(0);
216         }
217         SkPathEffectBase::DashType      fType;
218         SkScalar                    fPhase{0};
219         skia_private::AutoSTArray<4, SkScalar>  fIntervals;
220     };
221 
222     bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;
223 
224     SkStrokeRec         fStrokeRec;
225     sk_sp<SkPathEffect> fPathEffect;
226     DashInfo            fDashInfo;
227 };
228 
229 #endif
230