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