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 #include "src/gpu/ganesh/GrStyle.h"
9
10 #include "include/core/SkPath.h"
11 #include "src/utils/SkDashPathPriv.h"
12
13 #include <cstring>
14
KeySize(const GrStyle & style,Apply apply,uint32_t flags)15 int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
16 static_assert(sizeof(uint32_t) == sizeof(SkScalar));
17 int size = 0;
18 if (style.isDashed()) {
19 // One scalar for scale, one for dash phase, and one for each dash value.
20 size += 2 + style.dashIntervalCnt();
21 } else if (style.pathEffect()) {
22 // No key for a generic path effect.
23 return -1;
24 }
25
26 if (Apply::kPathEffectOnly == apply) {
27 return size;
28 }
29
30 if (style.strokeRec().needToApply()) {
31 // One for res scale, one for style/cap/join, one for miter limit, and one for width.
32 size += 4;
33 }
34 return size;
35 }
36
WriteKey(uint32_t * key,const GrStyle & style,Apply apply,SkScalar scale,uint32_t flags)37 void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale,
38 uint32_t flags) {
39 SkASSERT(key);
40 SkASSERT(KeySize(style, apply) >= 0);
41 static_assert(sizeof(uint32_t) == sizeof(SkScalar));
42
43 int i = 0;
44 // The scale can influence both the path effect and stroking. We want to preserve the
45 // property that the following two are equal:
46 // 1. WriteKey with apply == kPathEffectAndStrokeRec
47 // 2. WriteKey with apply == kPathEffectOnly followed by WriteKey of a GrStyle made
48 // from SkStrokeRec output by the the path effect (and no additional path effect).
49 // Since the scale can affect both parts of 2 we write it into the key twice.
50 if (style.isDashed()) {
51 static_assert(sizeof(style.dashPhase()) == sizeof(uint32_t));
52 SkScalar phase = style.dashPhase();
53 memcpy(&key[i++], &scale, sizeof(SkScalar));
54 memcpy(&key[i++], &phase, sizeof(SkScalar));
55
56 int32_t count = style.dashIntervalCnt();
57 // Dash count should always be even.
58 SkASSERT(0 == (count & 0x1));
59 const SkScalar *intervals = style.dashIntervals();
60 int intervalByteCnt = count * sizeof(SkScalar);
61 memcpy(&key[i], intervals, intervalByteCnt);
62 i += count;
63 } else {
64 SkASSERT(!style.pathEffect());
65 }
66
67 if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) {
68 memcpy(&key[i++], &scale, sizeof(SkScalar));
69 static constexpr uint32_t kStyleBits = 2;
70 static constexpr uint32_t kJoinBits = 2;
71 static constexpr uint32_t kCapBits = 32 - kStyleBits - kJoinBits;
72 static constexpr uint32_t kJoinShift = kStyleBits;
73 static constexpr uint32_t kCapShift = kJoinShift + kJoinBits;
74
75 static_assert(SkStrokeRec::kStyleCount <= (1 << kStyleBits));
76 static_assert(SkPaint::kJoinCount <= (1 << kJoinBits));
77 static_assert(SkPaint::kCapCount <= (1 << kCapBits));
78 // The cap type only matters for unclosed shapes. However, a path effect could unclose
79 // the shape before it is stroked.
80 SkPaint::Cap cap = SkPaint::kDefault_Cap;
81 if (!(flags & kClosed_KeyFlag) || style.pathEffect()) {
82 cap = style.strokeRec().getCap();
83 }
84 SkScalar miter = -1.f;
85 SkPaint::Join join = SkPaint::kDefault_Join;
86
87 // Dashing will not insert joins but other path effects may.
88 if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) {
89 join = style.strokeRec().getJoin();
90 // Miter limit only affects miter joins
91 if (SkPaint::kMiter_Join == join) {
92 miter = style.strokeRec().getMiter();
93 }
94 }
95
96 key[i++] = style.strokeRec().getStyle() |
97 join << kJoinShift |
98 cap << kCapShift;
99
100 memcpy(&key[i++], &miter, sizeof(miter));
101
102 SkScalar width = style.strokeRec().getWidth();
103 memcpy(&key[i++], &width, sizeof(width));
104 }
105 SkASSERT(KeySize(style, apply) == i);
106 }
107
initPathEffect(sk_sp<SkPathEffect> pe)108 void GrStyle::initPathEffect(sk_sp<SkPathEffect> pe) {
109 SkASSERT(!fPathEffect);
110 SkASSERT(SkPathEffectBase::DashType::kNone == fDashInfo.fType);
111 SkASSERT(0 == fDashInfo.fIntervals.count());
112 if (!pe) {
113 return;
114 }
115 SkPathEffectBase::DashInfo info;
116 if (SkPathEffectBase::DashType::kDash == as_PEB(pe)->asADash(&info)) {
117 SkStrokeRec::Style recStyle = fStrokeRec.getStyle();
118 if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) {
119 fDashInfo.fType = SkPathEffectBase::DashType::kDash;
120 fDashInfo.fIntervals.reset(info.fCount);
121 fDashInfo.fPhase = info.fPhase;
122 info.fIntervals = fDashInfo.fIntervals.get();
123 as_PEB(pe)->asADash(&info);
124 fPathEffect = std::move(pe);
125 }
126 } else {
127 fPathEffect = std::move(pe);
128 }
129 }
130
applyPathEffect(SkPath * dst,SkStrokeRec * strokeRec,const SkPath & src) const131 bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const {
132 if (!fPathEffect) {
133 return false;
134 }
135
136 // TODO: [skbug.com/11957] Plumb CTM callers and pass it to filterPath().
137 SkASSERT(!fPathEffect->needsCTM());
138
139 if (SkPathEffectBase::DashType::kDash == fDashInfo.fType) {
140 // We apply the dash ourselves here rather than using the path effect. This is so that
141 // we can control whether the dasher applies the strokeRec for special cases. Our keying
142 // depends on the strokeRec being applied separately.
143 SkASSERT(!fPathEffect->needsCTM()); // Make sure specified PE doesn't need CTM
144 SkScalar phase = fDashInfo.fPhase;
145 const SkScalar* intervals = fDashInfo.fIntervals.get();
146 int intervalCnt = fDashInfo.fIntervals.count();
147 SkScalar initialLength;
148 int initialIndex;
149 SkScalar intervalLength;
150 SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength,
151 &initialIndex, &intervalLength);
152 if (!SkDashPath::InternalFilter(dst, src, strokeRec,
153 nullptr, intervals, intervalCnt,
154 initialLength, initialIndex, intervalLength, phase,
155 SkDashPath::StrokeRecApplication::kDisallow)) {
156 return false;
157 }
158 } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) {
159 return false;
160 }
161 dst->setIsVolatile(true);
162 return true;
163 }
164
applyPathEffectToPath(SkPath * dst,SkStrokeRec * remainingStroke,const SkPath & src,SkScalar resScale) const165 bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke,
166 const SkPath &src, SkScalar resScale) const {
167 SkASSERT(dst);
168 SkStrokeRec strokeRec = fStrokeRec;
169 strokeRec.setResScale(resScale);
170 if (!this->applyPathEffect(dst, &strokeRec, src)) {
171 return false;
172 }
173 *remainingStroke = strokeRec;
174 return true;
175 }
176
applyToPath(SkPath * dst,SkStrokeRec::InitStyle * style,const SkPath & src,SkScalar resScale) const177 bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src,
178 SkScalar resScale) const {
179 SkASSERT(style);
180 SkASSERT(dst);
181 SkStrokeRec strokeRec = fStrokeRec;
182 strokeRec.setResScale(resScale);
183 const SkPath* pathForStrokeRec = &src;
184 if (this->applyPathEffect(dst, &strokeRec, src)) {
185 pathForStrokeRec = dst;
186 } else if (fPathEffect) {
187 return false;
188 }
189 if (strokeRec.needToApply()) {
190 if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) {
191 return false;
192 }
193 dst->setIsVolatile(true);
194 *style = SkStrokeRec::kFill_InitStyle;
195 } else if (!fPathEffect) {
196 // Nothing to do for path effect or stroke, fail.
197 return false;
198 } else {
199 SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() ||
200 SkStrokeRec::kHairline_Style == strokeRec.getStyle());
201 *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style
202 ? SkStrokeRec::kFill_InitStyle
203 : SkStrokeRec::kHairline_InitStyle;
204 }
205 return true;
206 }
207