aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrStyle.h
blob: 9f7f630a4f7a2365f2784af52f008f5cff73d8a6 (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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrStyle_DEFINED
#define GrStyle_DEFINED

#include "GrTypes.h"
#include "SkPathEffect.h"
#include "SkStrokeRec.h"
#include "SkTemplates.h"

/**
 * Represents the various ways that a GrShape can be styled. It has fill/stroking information
 * as well as an optional path effect. If the path effect represents dashing, the dashing
 * information is extracted from the path effect and stored explicitly.
 *
 * This will replace GrStrokeInfo as GrShape is deployed.
 */
class GrStyle {
public:
    /**
     * A style object that represents a fill with no path effect.
     * TODO: constexpr with C++14
     */
    static const GrStyle& SimpleFill() {
        static const GrStyle kFill(SkStrokeRec::kFill_InitStyle);
        return kFill;
        }

    /**
     * A style object that represents a hairline stroke with no path effect.
     * TODO: constexpr with C++14
     */
    static const GrStyle& SimpleHairline() {
        static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle);
        return kHairline;
    }

    enum class Apply {
        kPathEffectOnly,
        kPathEffectAndStrokeRec
    };

    /**
     * Optional flags for computing keys that may remove unnecessary variation in the key due to
     * style settings that don't affect particular classes of geometry.
     */
    enum KeyFlags {
        // The shape being styled has no open contours.
        kClosed_KeyFlag = 0x1,
        // The shape being styled doesn't have any joins and so isn't affected by join type.
        kNoJoins_KeyFlag = 0x2
    };

    /**
     * Computes the key length for a GrStyle. The return will be negative if it cannot be turned
     * into a key. This occurs when there is a path effect that is not a dash. The key can
     * either reflect just the path effect (if one) or the path effect and the strokerec. Note
     * that a simple fill has a zero sized key.
     */
    static int KeySize(const GrStyle&, Apply, uint32_t flags = 0);

    /**
     * Writes a unique key for the style into the provided buffer. This function assumes the buffer
     * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative
     * value for the combination of GrStyle, Apply and flags params. This is written so that the key
     * for just dash application followed by the key for the remaining SkStrokeRec is the same as
     * the key for applying dashing and SkStrokeRec all at once.
     */
    static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0);

    GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {}

    explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {}

    GrStyle(const SkStrokeRec& strokeRec, sk_sp<SkPathEffect> pe) : fStrokeRec(strokeRec) {
        this->initPathEffect(std::move(pe));
    }

    GrStyle(const GrStyle& that) = default;

    explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) {
        this->initPathEffect(paint.refPathEffect());
    }

    explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle)
            : fStrokeRec(paint, overrideStyle) {
        this->initPathEffect(paint.refPathEffect());
    }

    GrStyle& operator=(const GrStyle& that) {
        fPathEffect = that.fPathEffect;
        fDashInfo = that.fDashInfo;
        fStrokeRec = that.fStrokeRec;
        return *this;
    }

    void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) {
        fDashInfo.reset();
        fPathEffect.reset(nullptr);
        if (SkStrokeRec::kFill_InitStyle == fillOrHairline) {
            fStrokeRec.setFillStyle();
        } else {
            fStrokeRec.setHairlineStyle();
        }
    }

    /** Is this style a fill with no path effect? */
    bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; }

    /** Is this style a hairline with no path effect? */
    bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; }

    SkPathEffect* pathEffect() const { return fPathEffect.get(); }
    sk_sp<SkPathEffect> refPathEffect() const { return fPathEffect; }

    bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }

    bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); }

    bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; }
    SkScalar dashPhase() const {
        SkASSERT(this->isDashed());
        return fDashInfo.fPhase;
    }
    int dashIntervalCnt() const {
        SkASSERT(this->isDashed());
        return fDashInfo.fIntervals.count();
    }
    const SkScalar* dashIntervals() const {
        SkASSERT(this->isDashed());
        return fDashInfo.fIntervals.get();
    }

    const SkStrokeRec& strokeRec() const { return fStrokeRec; }

    /** Hairline or fill styles without path effects make no alterations to a geometry. */
    bool applies() const {
        return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle());
    }

    static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) {
        // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale
        // factor of 1. This isn't necessarily a good choice and in the future we might consider
        // taking a bounds here for the perspective case.
        return SkScalarAbs(matrix.getMaxScale());
    }
    /**
     * Applies just the path effect and returns remaining stroke information. This will fail if
     * there is no path effect. dst may or may not have been overwritten on failure. Scale controls
     * geometric approximations made by the path effect. It is typically computed from the view
     * matrix.
     */
    bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke,
                                                     const SkPath& src, SkScalar scale) const;

    /**
     * If this succeeds then the result path should be filled or hairlined as indicated by the
     * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the
     * strokerec doesn't change the geometry. When this fails the outputs may or may not have
     * been overwritten. Scale controls geometric approximations made by the path effect and
     * stroker. It is typically computed from the view matrix.
     */
    bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline,
                                           const SkPath& src, SkScalar scale) const;

    /** Given bounds of a path compute the bounds of path with the style applied. */
    void adjustBounds(SkRect* dst, const SkRect& src) const {
        if (this->pathEffect()) {
            this->pathEffect()->computeFastBounds(dst, src);
            // This may not be the correct SkStrokeRec to use. skbug.com/5299
            // It happens to work for dashing.
            SkScalar radius = fStrokeRec.getInflationRadius();
            dst->outset(radius, radius);
        } else {
            SkScalar radius = fStrokeRec.getInflationRadius();
            *dst = src.makeOutset(radius, radius);
        }
    }

private:
    void initPathEffect(sk_sp<SkPathEffect> pe);

    struct DashInfo {
        DashInfo() : fType(SkPathEffect::kNone_DashType) {}
        DashInfo(const DashInfo& that) { *this = that; }
        DashInfo& operator=(const DashInfo& that) {
            fType = that.fType;
            fPhase = that.fPhase;
            fIntervals.reset(that.fIntervals.count());
            sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(),
                              sizeof(SkScalar) * that.fIntervals.count());
            return *this;
        }
        void reset() {
            fType = SkPathEffect::kNone_DashType;
            fIntervals.reset(0);
        }
        SkPathEffect::DashType      fType;
        SkScalar                    fPhase;
        SkAutoSTArray<4, SkScalar>  fIntervals;
    };

    bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;

    SkStrokeRec         fStrokeRec;
    sk_sp<SkPathEffect> fPathEffect;
    DashInfo            fDashInfo;
};

#endif