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