aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrStyle.cpp
blob: 153cade919ef94b5667356ddb60b977d673fca25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrStyle.h"
#include "SkDashPathPriv.h"

int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
    GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
    int size = 0;
    if (style.isDashed()) {
        // One scalar for scale, one for dash phase, and one for each dash value.
        size += 2 + style.dashIntervalCnt();
    } else if (style.pathEffect()) {
        // No key for a generic path effect.
        return -1;
    }

    if (Apply::kPathEffectOnly == apply) {
        return size;
    }

    if (style.strokeRec().needToApply()) {
        // One for res scale, one for style/cap/join, one for miter limit, and one for width.
        size += 4;
    }
    return size;
}

void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale,
                       uint32_t flags) {
    SkASSERT(key);
    SkASSERT(KeySize(style, apply) >= 0);
    GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));

    int i = 0;
    // The scale can influence both the path effect and stroking. We want to preserve the
    // property that the following two are equal:
    // 1. WriteKey with apply == kPathEffectAndStrokeRec
    // 2. WriteKey with apply == kPathEffectOnly followed by WriteKey of a GrStyle made
    //    from SkStrokeRec output by the the path effect (and no additional path effect).
    // Since the scale can affect both parts of 2 we write it into the key twice.
    if (style.isDashed()) {
        GR_STATIC_ASSERT(sizeof(style.dashPhase()) == sizeof(uint32_t));
        SkScalar phase = style.dashPhase();
        memcpy(&key[i++], &scale, sizeof(SkScalar));
        memcpy(&key[i++], &phase, sizeof(SkScalar));

        int32_t count = style.dashIntervalCnt();
        // Dash count should always be even.
        SkASSERT(0 == (count & 0x1));
        const SkScalar *intervals = style.dashIntervals();
        int intervalByteCnt = count * sizeof(SkScalar);
        memcpy(&key[i], intervals, intervalByteCnt);
        i += count;
    } else {
        SkASSERT(!style.pathEffect());
    }

    if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) {
        memcpy(&key[i++], &scale, sizeof(SkScalar));
        enum {
            kStyleBits = 2,
            kJoinBits = 2,
            kCapBits = 32 - kStyleBits - kJoinBits,

            kJoinShift = kStyleBits,
            kCapShift = kJoinShift + kJoinBits,
        };
        GR_STATIC_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits));
        GR_STATIC_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits));
        GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits));
        // The cap type only matters for unclosed shapes. However, a path effect could unclose
        // the shape before it is stroked.
        SkPaint::Cap cap = SkPaint::kDefault_Cap;
        if (!(flags & kClosed_KeyFlag) || style.pathEffect()) {
            cap = style.strokeRec().getCap();
        }
        SkScalar miter = -1.f;
        SkPaint::Join join = SkPaint::kDefault_Join;

        // Dashing will not insert joins but other path effects may.
        if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) {
            join = style.strokeRec().getJoin();
            // Miter limit only affects miter joins
            if (SkPaint::kMiter_Join == join) {
                miter = style.strokeRec().getMiter();
            }
        }

        key[i++] = style.strokeRec().getStyle() |
                   join << kJoinShift |
                   cap << kCapShift;

        memcpy(&key[i++], &miter, sizeof(miter));

        SkScalar width = style.strokeRec().getWidth();
        memcpy(&key[i++], &width, sizeof(width));
    }
    SkASSERT(KeySize(style, apply) == i);
}

void GrStyle::initPathEffect(SkPathEffect* pe) {
    SkASSERT(!fPathEffect);
    SkASSERT(SkPathEffect::kNone_DashType == fDashInfo.fType);
    SkASSERT(0 == fDashInfo.fIntervals.count());
    if (!pe) {
        return;
    }
    SkPathEffect::DashInfo info;
    if (SkPathEffect::kDash_DashType == pe->asADash(&info)) {
        SkStrokeRec::Style recStyle = fStrokeRec.getStyle();
        if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) {
            fDashInfo.fType = SkPathEffect::kDash_DashType;
            fDashInfo.fIntervals.reset(info.fCount);
            fDashInfo.fPhase = info.fPhase;
            info.fIntervals = fDashInfo.fIntervals.get();
            pe->asADash(&info);
            fPathEffect.reset(SkSafeRef(pe));
        }
    } else {
        fPathEffect.reset(SkSafeRef(pe));
    }
}

bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const {
    if (!fPathEffect) {
        return false;
    }
    if (SkPathEffect::kDash_DashType == fDashInfo.fType) {
        // We apply the dash ourselves here rather than using the path effect. This is so that
        // we can control whether the dasher applies the strokeRec for special cases. Our keying
        // depends on the strokeRec being applied separately.
        SkScalar phase = fDashInfo.fPhase;
        const SkScalar* intervals = fDashInfo.fIntervals.get();
        int intervalCnt = fDashInfo.fIntervals.count();
        SkScalar initialLength;
        int initialIndex;
        SkScalar intervalLength;
        SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength,
                                       &initialIndex, &intervalLength);
        if (!SkDashPath::InternalFilter(dst, src, strokeRec,
                                        nullptr, intervals, intervalCnt,
                                        initialLength, initialIndex, intervalLength,
                                        SkDashPath::StrokeRecApplication::kDisallow)) {
            return false;
        }
    } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) {
        return false;
    }
    dst->setIsVolatile(true);
    return true;
}

bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke,
                                    const SkPath &src, SkScalar resScale) const {
    SkASSERT(dst);
    SkStrokeRec strokeRec = fStrokeRec;
    strokeRec.setResScale(resScale);
    if (!this->applyPathEffect(dst, &strokeRec, src)) {
        return false;
    }
    *remainingStroke = strokeRec;
    return true;
}

bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src,
                          SkScalar resScale) const {
    SkASSERT(style);
    SkASSERT(dst);
    SkStrokeRec strokeRec = fStrokeRec;
    strokeRec.setResScale(resScale);
    const SkPath* pathForStrokeRec = &src;
    if (this->applyPathEffect(dst, &strokeRec, src)) {
        pathForStrokeRec = dst;
    } else if (fPathEffect) {
        return false;
    }
    if (strokeRec.needToApply()) {
        if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) {
            return false;
        }
        dst->setIsVolatile(true);
        *style = SkStrokeRec::kFill_InitStyle;
    } else if (!fPathEffect) {
        // Nothing to do for path effect or stroke, fail.
        return false;
    } else {
        SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() ||
                 SkStrokeRec::kHairline_Style == strokeRec.getStyle());
        *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style
                 ? SkStrokeRec::kFill_InitStyle
                 : SkStrokeRec::kHairline_InitStyle;
    }
    return true;
}