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,SkPathEffect * pe)80     GrStyle(const SkStrokeRec& strokeRec, SkPathEffect* pe) : fStrokeRec(strokeRec) {
81         this->initPathEffect(pe);
82     }
83 
GrStyle(const GrStyle & that)84     GrStyle(const GrStyle& that) : fStrokeRec(SkStrokeRec::kFill_InitStyle) { *this = that; }
85 
GrStyle(const SkPaint & paint)86     explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) {
87         this->initPathEffect(paint.getPathEffect());
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.getPathEffect());
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(); }
119 
hasPathEffect()120     bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }
121 
hasNonDashPathEffect()122     bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); }
123 
isDashed()124     bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; }
dashPhase()125     SkScalar dashPhase() const {
126         SkASSERT(this->isDashed());
127         return fDashInfo.fPhase;
128     }
dashIntervalCnt()129     int dashIntervalCnt() const {
130         SkASSERT(this->isDashed());
131         return fDashInfo.fIntervals.count();
132     }
dashIntervals()133     const SkScalar* dashIntervals() const {
134         SkASSERT(this->isDashed());
135         return fDashInfo.fIntervals.get();
136     }
137 
strokeRec()138     const SkStrokeRec& strokeRec() const { return fStrokeRec; }
139 
140     /** Hairline or fill styles without path effects make no alterations to a geometry. */
applies()141     bool applies() const {
142         return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle());
143     }
144 
MatrixToScaleFactor(const SkMatrix & matrix)145     static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) {
146         // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale
147         // factor of 1. This isn't necessarily a good choice and in the future we might consider
148         // taking a bounds here for the perspective case.
149         return SkScalarAbs(matrix.getMaxScale());
150     }
151     /**
152      * Applies just the path effect and returns remaining stroke information. This will fail if
153      * there is no path effect. dst may or may not have been overwritten on failure. Scale controls
154      * geometric approximations made by the path effect. It is typically computed from the view
155      * matrix.
156      */
157     bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke,
158                                                      const SkPath& src, SkScalar scale) const;
159 
160     /**
161      * If this succeeds then the result path should be filled or hairlined as indicated by the
162      * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the
163      * strokerec doesn't change the geometry. When this fails the outputs may or may not have
164      * been overwritten. Scale controls geometric approximations made by the path effect and
165      * stroker. It is typically computed from the view matrix.
166      */
167     bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline,
168                                            const SkPath& src, SkScalar scale) const;
169 
170     /** Given bounds of a path compute the bounds of path with the style applied. */
adjustBounds(SkRect * dst,const SkRect & src)171     void adjustBounds(SkRect* dst, const SkRect& src) const {
172         if (this->pathEffect()) {
173             this->pathEffect()->computeFastBounds(dst, src);
174             // This may not be the correct SkStrokeRec to use. skbug.com/5299
175             // It happens to work for dashing.
176             SkScalar radius = fStrokeRec.getInflationRadius();
177             dst->outset(radius, radius);
178         } else {
179             SkScalar radius = fStrokeRec.getInflationRadius();
180             *dst = src.makeOutset(radius, radius);
181         }
182     }
183 
184 private:
185     void initPathEffect(SkPathEffect* pe);
186 
187     struct DashInfo {
DashInfoDashInfo188         DashInfo() : fType(SkPathEffect::kNone_DashType) {}
189         DashInfo& operator=(const DashInfo& that) {
190             fType = that.fType;
191             fPhase = that.fPhase;
192             fIntervals.reset(that.fIntervals.count());
193             sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(),
194                               sizeof(SkScalar) * that.fIntervals.count());
195             return *this;
196         }
resetDashInfo197         void reset() {
198             fType = SkPathEffect::kNone_DashType;
199             fIntervals.reset(0);
200         }
201         SkPathEffect::DashType      fType;
202         SkScalar                    fPhase;
203         SkAutoSTArray<4, SkScalar>  fIntervals;
204     };
205 
206     bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;
207 
208     SkStrokeRec         fStrokeRec;
209     sk_sp<SkPathEffect> fPathEffect;
210     DashInfo            fDashInfo;
211 };
212 
213 #endif
214