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