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/GrStyle.h"
9 #include "src/utils/SkDashPathPriv.h"
10 
KeySize(const GrStyle & style,Apply apply,uint32_t flags)11 int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
12     GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
13     int size = 0;
14     if (style.isDashed()) {
15         // One scalar for scale, one for dash phase, and one for each dash value.
16         size += 2 + style.dashIntervalCnt();
17     } else if (style.pathEffect()) {
18         // No key for a generic path effect.
19         return -1;
20     }
21 
22     if (Apply::kPathEffectOnly == apply) {
23         return size;
24     }
25 
26     if (style.strokeRec().needToApply()) {
27         // One for res scale, one for style/cap/join, one for miter limit, and one for width.
28         size += 4;
29     }
30     return size;
31 }
32 
WriteKey(uint32_t * key,const GrStyle & style,Apply apply,SkScalar scale,uint32_t flags)33 void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale,
34                        uint32_t flags) {
35     SkASSERT(key);
36     SkASSERT(KeySize(style, apply) >= 0);
37     GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
38 
39     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     if (style.isDashed()) {
47         GR_STATIC_ASSERT(sizeof(style.dashPhase()) == sizeof(uint32_t));
48         SkScalar phase = style.dashPhase();
49         memcpy(&key[i++], &scale, sizeof(SkScalar));
50         memcpy(&key[i++], &phase, sizeof(SkScalar));
51 
52         int32_t count = style.dashIntervalCnt();
53         // Dash count should always be even.
54         SkASSERT(0 == (count & 0x1));
55         const SkScalar *intervals = style.dashIntervals();
56         int intervalByteCnt = count * sizeof(SkScalar);
57         memcpy(&key[i], intervals, intervalByteCnt);
58         i += count;
59     } else {
60         SkASSERT(!style.pathEffect());
61     }
62 
63     if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) {
64         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         SkPaint::Cap cap = SkPaint::kDefault_Cap;
79         if (!(flags & kClosed_KeyFlag) || style.pathEffect()) {
80             cap = style.strokeRec().getCap();
81         }
82         SkScalar miter = -1.f;
83         SkPaint::Join join = SkPaint::kDefault_Join;
84 
85         // Dashing will not insert joins but other path effects may.
86         if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) {
87             join = style.strokeRec().getJoin();
88             // Miter limit only affects miter joins
89             if (SkPaint::kMiter_Join == join) {
90                 miter = style.strokeRec().getMiter();
91             }
92         }
93 
94         key[i++] = style.strokeRec().getStyle() |
95                    join << kJoinShift |
96                    cap << kCapShift;
97 
98         memcpy(&key[i++], &miter, sizeof(miter));
99 
100         SkScalar width = style.strokeRec().getWidth();
101         memcpy(&key[i++], &width, sizeof(width));
102     }
103     SkASSERT(KeySize(style, apply) == i);
104 }
105 
initPathEffect(sk_sp<SkPathEffect> pe)106 void GrStyle::initPathEffect(sk_sp<SkPathEffect> pe) {
107     SkASSERT(!fPathEffect);
108     SkASSERT(SkPathEffect::kNone_DashType == fDashInfo.fType);
109     SkASSERT(0 == fDashInfo.fIntervals.count());
110     if (!pe) {
111         return;
112     }
113     SkPathEffect::DashInfo info;
114     if (SkPathEffect::kDash_DashType == pe->asADash(&info)) {
115         SkStrokeRec::Style recStyle = fStrokeRec.getStyle();
116         if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) {
117             fDashInfo.fType = SkPathEffect::kDash_DashType;
118             fDashInfo.fIntervals.reset(info.fCount);
119             fDashInfo.fPhase = info.fPhase;
120             info.fIntervals = fDashInfo.fIntervals.get();
121             pe->asADash(&info);
122             fPathEffect = std::move(pe);
123         }
124     } else {
125         fPathEffect = std::move(pe);
126     }
127 }
128 
applyPathEffect(SkPath * dst,SkStrokeRec * strokeRec,const SkPath & src) const129 bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const {
130     if (!fPathEffect) {
131         return false;
132     }
133     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         SkScalar phase = fDashInfo.fPhase;
138         const SkScalar* intervals = fDashInfo.fIntervals.get();
139         int intervalCnt = fDashInfo.fIntervals.count();
140         SkScalar initialLength;
141         int initialIndex;
142         SkScalar intervalLength;
143         SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength,
144                                        &initialIndex, &intervalLength);
145         if (!SkDashPath::InternalFilter(dst, src, strokeRec,
146                                         nullptr, intervals, intervalCnt,
147                                         initialLength, initialIndex, intervalLength,
148                                         SkDashPath::StrokeRecApplication::kDisallow)) {
149             return false;
150         }
151     } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) {
152         return false;
153     }
154     dst->setIsVolatile(true);
155     return true;
156 }
157 
applyPathEffectToPath(SkPath * dst,SkStrokeRec * remainingStroke,const SkPath & src,SkScalar resScale) const158 bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke,
159                                     const SkPath &src, SkScalar resScale) const {
160     SkASSERT(dst);
161     SkStrokeRec strokeRec = fStrokeRec;
162     strokeRec.setResScale(resScale);
163     if (!this->applyPathEffect(dst, &strokeRec, src)) {
164         return false;
165     }
166     *remainingStroke = strokeRec;
167     return true;
168 }
169 
applyToPath(SkPath * dst,SkStrokeRec::InitStyle * style,const SkPath & src,SkScalar resScale) const170 bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src,
171                           SkScalar resScale) const {
172     SkASSERT(style);
173     SkASSERT(dst);
174     SkStrokeRec strokeRec = fStrokeRec;
175     strokeRec.setResScale(resScale);
176     const SkPath* pathForStrokeRec = &src;
177     if (this->applyPathEffect(dst, &strokeRec, src)) {
178         pathForStrokeRec = dst;
179     } else if (fPathEffect) {
180         return false;
181     }
182     if (strokeRec.needToApply()) {
183         if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) {
184             return false;
185         }
186         dst->setIsVolatile(true);
187         *style = SkStrokeRec::kFill_InitStyle;
188     } else if (!fPathEffect) {
189         // Nothing to do for path effect or stroke, fail.
190         return false;
191     } else {
192         SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() ||
193                  SkStrokeRec::kHairline_Style == strokeRec.getStyle());
194         *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style
195                  ? SkStrokeRec::kFill_InitStyle
196                  : SkStrokeRec::kHairline_InitStyle;
197     }
198     return true;
199 }
200