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
11int 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
33void 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
106void 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
129bool 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
158bool 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
170bool 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