aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkNormalBevelSource.cpp
blob: 4a08728a9819bf6616d73e02c21a52722bdaf425 (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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/*
 * 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 "SkNormalBevelSource.h"

#include "SkNormalSource.h"
#include "SkNormalSourcePriv.h"
#include "SkPoint3.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"

#if SK_SUPPORT_GPU
#include "GrInvariantOutput.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "SkGr.h"

/** \class NormalBevelFP
 *
 *  Fragment processor for the SkNormalBevelSource.
 *
 *  @param bevelType    type of the bevel
 *  @param bevelWidth   width of the bevel in device space
 *  @param bevelHeight  height of the bevel in device space
 */
class NormalBevelFP : public GrFragmentProcessor {
public:
    NormalBevelFP(SkNormalSource::BevelType bevelType, SkScalar bevelWidth, SkScalar bevelHeight)
        : fBevelType(bevelType)
        , fBevelWidth(bevelWidth)
        , fBevelHeight(bevelHeight) {
        this->initClassID<NormalBevelFP>();

        fUsesDistanceVectorField = true;
    }

    class GLSLNormalBevelFP : public GLSLNormalFP {
    public:
        GLSLNormalBevelFP() {
            fPrevWidth = SkFloatToScalar(0.0f);
            fPrevHeight = SkFloatToScalar(0.0f);
        }

        void onEmitCode(EmitArgs& args) override {
            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
            const NormalBevelFP& fp = args.fFp.cast<NormalBevelFP>();
            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

            // Determining necessary uniforms and initializing them
            bool needWidth = true;
            bool needHeight = (fp.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
                               fp.fBevelType == SkNormalSource::BevelType::kRoundedIn);
            bool needNormalized = (fp.fBevelType == SkNormalSource::BevelType::kLinear);

            const char *widthUniName = nullptr;
            if (needWidth) {
                fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
                                                       kDefault_GrSLPrecision, "Width",
                                                       &widthUniName);
            }

            const char* heightUniName = nullptr;
            if (needHeight) {
                fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
                                                        kDefault_GrSLPrecision, "Height",
                                                        &heightUniName);
            }

            const char* normalizedWidthUniName = nullptr;
            const char* normalizedHeightUniName = nullptr;
            if (needNormalized) {
                fNormalizedWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
                                                                 kFloat_GrSLType,
                                                                 kDefault_GrSLPrecision,
                                                                 "NormalizedWidth",
                                                                 &normalizedWidthUniName);
                fNormalizedHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
                                                                  kFloat_GrSLType,
                                                                  kDefault_GrSLPrecision,
                                                                  "NormalizedHeight",
                                                                  &normalizedHeightUniName);
            }

            // Here we are splitting the distance vector into length and normalized direction
            fragBuilder->codeAppendf("float dv_length = %s.z;",
                                     fragBuilder->distanceVectorName());
            fragBuilder->codeAppendf("vec2 dv_norm = %s.xy;",
                                     fragBuilder->distanceVectorName());

            // Asserting presence of necessary uniforms
            SkASSERT(widthUniName);

            fragBuilder->codeAppend( "vec3 normal;");
            fragBuilder->codeAppendf("if (dv_length >= %s) {", widthUniName);
            fragBuilder->codeAppend( "    normal = vec3(0.0, 0.0, 1.0);");
            fragBuilder->codeAppend( "} else {");
            this->emitMath(fragBuilder, fp.fBevelType, widthUniName, heightUniName,
                           normalizedWidthUniName, normalizedHeightUniName);
            fragBuilder->codeAppend( "}");
            fragBuilder->codeAppendf("%s = vec4(normal, 0.0);", args.fOutputColor);
        }

        static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
                           GrProcessorKeyBuilder* b) {
            const NormalBevelFP& fp = proc.cast<NormalBevelFP>();
            b->add32(static_cast<int>(fp.fBevelType));
        }

    protected:
        void setNormalData(const GrGLSLProgramDataManager& pdman,
                           const GrProcessor& proc) override {
            const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();

            // Updating uniform if bevel type requires it and data has changed

            bool needWidth = true;
            bool needHeight = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
                               normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedIn);
            bool needNormalized = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kLinear);

            bool dirtyWidth = (fPrevWidth  != normalBevelFP.fBevelWidth);
            bool dirtyHeight = (fPrevHeight != normalBevelFP.fBevelHeight);
            bool dirtyNormalized = (dirtyHeight || dirtyWidth);


            if (needWidth && dirtyWidth) {
                pdman.set1f(fWidthUni, normalBevelFP.fBevelWidth);
                fPrevWidth = normalBevelFP.fBevelWidth;
            }
            if (needHeight && dirtyHeight) {
                pdman.set1f(fHeightUni, normalBevelFP.fBevelHeight);
                fPrevHeight = normalBevelFP.fBevelHeight;
            }
            if (needNormalized && dirtyNormalized) {
                SkScalar height = normalBevelFP.fBevelHeight;
                SkScalar width  = normalBevelFP.fBevelWidth;

                SkScalar length = SkScalarSqrt(SkScalarSquare(height) + SkScalarSquare(width));
                pdman.set1f(fNormalizedHeightUni, height/length);
                pdman.set1f(fNormalizedWidthUni, width/length);
            }
        }

        // This method emits the code that calculates the normal orthgonal to the simulated beveled
        // surface. In the comments inside the function, the math involved is described. For this
        // purpose, the d-axis is defined to be the axis co-linear to the distance vector, where the
        // origin is the end of the bevel inside the shape.
        void emitMath(GrGLSLFPFragmentBuilder* fb, SkNormalSource::BevelType type,
                      const char* width, const char* height, const char* normalizedWidth,
                      const char* normalizedHeight) {
            switch (type) {
                case SkNormalSource::BevelType::kLinear:
                    // Asserting presence of necessary uniforms
                    SkASSERT(normalizedHeight);
                    SkASSERT(normalizedWidth);

                    // Because the slope of the bevel is -height/width, the vector
                    // normalized(vec2(height, width)) is the d- and z-components of the normal
                    // vector that is orthogonal to the linear bevel. Multiplying the d-component
                    // to the normalized distance vector splits it into x- and y-components.
                    fb->codeAppendf("normal = vec3(%s * dv_norm, %s);",
                                    normalizedHeight, normalizedWidth);
                    break;
                case SkNormalSource::BevelType::kRoundedOut:
                    // Fall through
                case SkNormalSource::BevelType::kRoundedIn:
                    // Asserting presence of necessary uniforms
                    SkASSERT(height);
                    SkASSERT(width);

                    // Setting the current position in the d-axis to the distance from the end of
                    // the bevel as opposed to the beginning if the bevel is rounded in, essentially
                    // flipping the bevel calculations.
                    if ( type == SkNormalSource::BevelType::kRoundedIn ) {
                        fb->codeAppendf("float currentPos_d = %s - dv_length;", width);
                    } else if (type == SkNormalSource::BevelType::kRoundedOut) {
                        fb->codeAppendf("float currentPos_d = dv_length;");
                    }

                    fb->codeAppendf("float rootDOverW = sqrt(currentPos_d/%s);", width);

                    // Calculating the d- and z-components of the normal, where 'd' is the axis
                    // co-linear to the distance vector. Equation was derived from the formula for
                    // a bezier curve by solving the parametric equation for d(t) and z(t), then
                    // with those, calculate d'(t), z'(t) and t(d), and from these, d'(d) and z'(d).
                    // z'(d)/d'(d) results in the slope of the bevel at d, so we construct an
                    // orthogonal vector of slope -d'(d)/z'(d) and length 1.
                    fb->codeAppendf("vec2 unnormalizedNormal_dz = vec2(%s*(1.0-rootDOverW), "
                                                                       "%s*rootDOverW);",
                                    height, width);
                    fb->codeAppendf("vec2 normal_dz = normalize(unnormalizedNormal_dz);");

                    // Multiplying the d-component to the normalized distance vector splits it into
                    // x- and y-components.
                    fb->codeAppendf("normal = vec3(normal_dz.x*dv_norm, normal_dz.y);");

                    break;
                default:
                    SkDEBUGFAIL("Invalid bevel type passed to emitMath");
            }
        }

    private:
        SkScalar fPrevWidth;
        GrGLSLProgramDataManager::UniformHandle fWidthUni;

        SkScalar fPrevHeight;
        GrGLSLProgramDataManager::UniformHandle fHeightUni;

        // width / length(<width,height>)
        GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni;
        // height / length(<width,height>)
        GrGLSLProgramDataManager::UniformHandle fNormalizedHeightUni;
    };

    void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
        GLSLNormalBevelFP::GenKey(*this, caps, b);
    }

    const char* name() const override { return "NormalBevelFP"; }

    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
        inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput);
    }

private:
    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalBevelFP; }

    bool onIsEqual(const GrFragmentProcessor& proc) const override {
        const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
        return fBevelType   == normalBevelFP.fBevelType &&
               fBevelWidth  == normalBevelFP.fBevelWidth &&
               fBevelHeight == normalBevelFP.fBevelHeight;
    }

    SkNormalSource::BevelType fBevelType;
    SkScalar fBevelWidth;
    SkScalar fBevelHeight;
};

sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor(
        const SkShader::AsFPArgs& args) const {

    // This assumes a uniform scale. Anisotropic scaling might not be handled gracefully.
    SkScalar maxScale = args.fViewMatrix->getMaxScale();

    // Providing device-space width and height
    return sk_make_sp<NormalBevelFP>(fType, maxScale * fWidth, maxScale * fHeight);
}

#endif // SK_SUPPORT_GPU

////////////////////////////////////////////////////////////////////////////

SkNormalBevelSourceImpl::Provider::Provider() {}

SkNormalBevelSourceImpl::Provider::~Provider() {}

SkNormalSource::Provider* SkNormalBevelSourceImpl::asProvider(const SkShader::ContextRec &rec,
                                                              void *storage) const {
    return new (storage) Provider();
}

size_t SkNormalBevelSourceImpl::providerSize(const SkShader::ContextRec&) const {
    return sizeof(Provider);
}

// TODO Implement feature for the CPU pipeline
void SkNormalBevelSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
                                                     int count) const {
    for (int i = 0; i < count; i++) {
        output[i] = {0.0f, 0.0f, 1.0f};
    }
}

////////////////////////////////////////////////////////////////////////////////

sk_sp<SkFlattenable> SkNormalBevelSourceImpl::CreateProc(SkReadBuffer& buf) {

    auto type = static_cast<SkNormalSource::BevelType>(buf.readInt());
    SkScalar width = buf.readScalar();
    SkScalar height = buf.readScalar();

    return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
}

void SkNormalBevelSourceImpl::flatten(SkWriteBuffer& buf) const {
    this->INHERITED::flatten(buf);

    buf.writeInt(static_cast<int>(fType));
    buf.writeScalar(fWidth);
    buf.writeScalar(fHeight);
}

////////////////////////////////////////////////////////////////////////////

sk_sp<SkNormalSource> SkNormalSource::MakeBevel(BevelType type, SkScalar width, SkScalar height) {
    /* TODO make sure these checks are tolerant enough to account for loss of conversion when GPUs
       use 16-bit float types. We don't want to assume stuff is non-zero on the GPU and be wrong.*/
    SkASSERT(width > 0.0f && !SkScalarNearlyZero(width));
    if (SkScalarNearlyZero(height)) {
        return SkNormalSource::MakeFlat();
    }

    return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
}