blob: 326f800442c9d2061102ffa2bcfc89b25a552ffc [file] [log] [blame]
bsalomon47cc7692016-04-26 12:56:00 -07001/*
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 *
bsalomon47cc7692016-04-26 12:56:00 -070021 * This will replace GrStrokeInfo as GrShape is deployed.
22 */
23class GrStyle {
24public:
bsalomonfb083272016-05-04 08:27:41 -070025 /**
26 * A style object that represents a fill with no path effect.
27 * TODO: constexpr with C++14
28 */
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 */
38 static const GrStyle& SimpleHairline() {
39 static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle);
40 return kHairline;
bsalomon47cc7692016-04-26 12:56:00 -070041 }
42
bsalomonfb083272016-05-04 08:27:41 -070043 enum class Apply {
44 kPathEffectOnly,
45 kPathEffectAndStrokeRec
46 };
47
48 /**
bsalomon06077562016-05-04 13:50:29 -070049 * 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 };
56
57 /**
bsalomonfb083272016-05-04 08:27:41 -070058 * Computes the key length for a GrStyle. The return will be negative if it cannot be turned
59 * into a key. This occurs when there is a path effect that is not a dash. The key can
60 * either reflect just the path effect (if one) or the path effect and the strokerec. Note
61 * that a simple fill has a zero sized key.
62 */
bsalomon97fd2d42016-05-09 13:02:01 -070063 static int KeySize(const GrStyle&, Apply, uint32_t flags = 0);
bsalomonfb083272016-05-04 08:27:41 -070064
65 /**
66 * Writes a unique key for the style into the provided buffer. This function assumes the buffer
67 * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative
bsalomon06077562016-05-04 13:50:29 -070068 * value for the combination of GrStyle, Apply and flags params. This is written so that the key
69 * for just dash application followed by the key for the remaining SkStrokeRec is the same as
70 * the key for applying dashing and SkStrokeRec all at once.
bsalomonfb083272016-05-04 08:27:41 -070071 */
bsalomon97fd2d42016-05-09 13:02:01 -070072 static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0);
bsalomonfb083272016-05-04 08:27:41 -070073
74 GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {}
75
76 explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {}
77
bsalomon308feba2016-05-03 10:11:55 -070078 GrStyle(const SkStrokeRec& strokeRec, SkPathEffect* pe) : fStrokeRec(strokeRec) {
bsalomon308feba2016-05-03 10:11:55 -070079 this->initPathEffect(pe);
bsalomon47cc7692016-04-26 12:56:00 -070080 }
81
bsalomonfb083272016-05-04 08:27:41 -070082 GrStyle(const GrStyle& that) : fStrokeRec(SkStrokeRec::kFill_InitStyle) { *this = that; }
bsalomon47cc7692016-04-26 12:56:00 -070083
84 explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) {
bsalomon308feba2016-05-03 10:11:55 -070085 this->initPathEffect(paint.getPathEffect());
bsalomon47cc7692016-04-26 12:56:00 -070086 }
87
bsalomon6663acf2016-05-10 09:14:17 -070088 explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle)
89 : fStrokeRec(paint, overrideStyle) {
90 this->initPathEffect(paint.getPathEffect());
91 }
92
bsalomon47cc7692016-04-26 12:56:00 -070093 GrStyle& operator=(const GrStyle& that) {
94 fPathEffect = that.fPathEffect;
95 fDashInfo = that.fDashInfo;
96 fStrokeRec = that.fStrokeRec;
97 return *this;
98 }
bsalomonfb083272016-05-04 08:27:41 -070099
100 void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) {
101 fDashInfo.reset();
102 fPathEffect.reset(nullptr);
103 if (SkStrokeRec::kFill_InitStyle == fillOrHairline) {
104 fStrokeRec.setFillStyle();
105 } else {
106 fStrokeRec.setHairlineStyle();
107 }
108 }
109
110 /** Is this style a fill with no path effect? */
111 bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; }
112
113 /** Is this style a hairline with no path effect? */
114 bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; }
115
bsalomon47cc7692016-04-26 12:56:00 -0700116 SkPathEffect* pathEffect() const { return fPathEffect.get(); }
117
bsalomonee295642016-06-06 14:01:25 -0700118 bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }
119
bsalomonfb083272016-05-04 08:27:41 -0700120 bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); }
121
bsalomon47cc7692016-04-26 12:56:00 -0700122 bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; }
123 SkScalar dashPhase() const {
124 SkASSERT(this->isDashed());
125 return fDashInfo.fPhase;
126 }
127 int dashIntervalCnt() const {
128 SkASSERT(this->isDashed());
129 return fDashInfo.fIntervals.count();
130 }
131 const SkScalar* dashIntervals() const {
132 SkASSERT(this->isDashed());
133 return fDashInfo.fIntervals.get();
134 }
135
136 const SkStrokeRec& strokeRec() const { return fStrokeRec; }
137
bsalomonfb083272016-05-04 08:27:41 -0700138 /** Hairline or fill styles without path effects make no alterations to a geometry. */
139 bool applies() const {
140 return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle());
141 }
142
bsalomon6663acf2016-05-10 09:14:17 -0700143 static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) {
144 // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale
145 // factor of 1. This isn't necessarily a good choice and in the future we might consider
146 // taking a bounds here for the perspective case.
147 return SkScalarAbs(matrix.getMaxScale());
148 }
bsalomonfb083272016-05-04 08:27:41 -0700149 /**
150 * Applies just the path effect and returns remaining stroke information. This will fail if
bsalomon97fd2d42016-05-09 13:02:01 -0700151 * there is no path effect. dst may or may not have been overwritten on failure. Scale controls
152 * geometric approximations made by the path effect. It is typically computed from the view
153 * matrix.
bsalomonfb083272016-05-04 08:27:41 -0700154 */
bsalomon1a0b9ed2016-05-06 11:07:03 -0700155 bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke,
bsalomon97fd2d42016-05-09 13:02:01 -0700156 const SkPath& src, SkScalar scale) const;
bsalomonfb083272016-05-04 08:27:41 -0700157
bsalomon97fd2d42016-05-09 13:02:01 -0700158 /**
159 * If this succeeds then the result path should be filled or hairlined as indicated by the
160 * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the
161 * strokerec doesn't change the geometry. When this fails the outputs may or may not have
162 * been overwritten. Scale controls geometric approximations made by the path effect and
163 * stroker. It is typically computed from the view matrix.
164 */
bsalomon1a0b9ed2016-05-06 11:07:03 -0700165 bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline,
bsalomon97fd2d42016-05-09 13:02:01 -0700166 const SkPath& src, SkScalar scale) const;
bsalomonfb083272016-05-04 08:27:41 -0700167
168 /** Given bounds of a path compute the bounds of path with the style applied. */
169 void adjustBounds(SkRect* dst, const SkRect& src) const {
170 if (this->pathEffect()) {
171 this->pathEffect()->computeFastBounds(dst, src);
bsalomon9fb42032016-05-13 09:23:38 -0700172 // This may not be the correct SkStrokeRec to use. skbug.com/5299
173 // It happens to work for dashing.
174 SkScalar radius = fStrokeRec.getInflationRadius();
175 dst->outset(radius, radius);
bsalomonfb083272016-05-04 08:27:41 -0700176 } else {
177 SkScalar radius = fStrokeRec.getInflationRadius();
178 *dst = src.makeOutset(radius, radius);
179 }
180 }
181
bsalomon47cc7692016-04-26 12:56:00 -0700182private:
bsalomon308feba2016-05-03 10:11:55 -0700183 void initPathEffect(SkPathEffect* pe);
bsalomon47cc7692016-04-26 12:56:00 -0700184
185 struct DashInfo {
bsalomonfb083272016-05-04 08:27:41 -0700186 DashInfo() : fType(SkPathEffect::kNone_DashType) {}
bsalomon47cc7692016-04-26 12:56:00 -0700187 DashInfo& operator=(const DashInfo& that) {
188 fType = that.fType;
189 fPhase = that.fPhase;
190 fIntervals.reset(that.fIntervals.count());
benjaminwagnerd9cca4a2016-05-04 11:06:19 -0700191 sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(),
192 sizeof(SkScalar) * that.fIntervals.count());
bsalomon47cc7692016-04-26 12:56:00 -0700193 return *this;
194 }
bsalomonfb083272016-05-04 08:27:41 -0700195 void reset() {
196 fType = SkPathEffect::kNone_DashType;
197 fIntervals.reset(0);
198 }
bsalomon47cc7692016-04-26 12:56:00 -0700199 SkPathEffect::DashType fType;
200 SkScalar fPhase;
201 SkAutoSTArray<4, SkScalar> fIntervals;
202 };
203
bsalomon398e3f42016-06-13 10:22:48 -0700204 bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;
205
bsalomon47cc7692016-04-26 12:56:00 -0700206 SkStrokeRec fStrokeRec;
207 sk_sp<SkPathEffect> fPathEffect;
208 DashInfo fDashInfo;
209};
210
211#endif