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