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 "SkNormalBevelSource.h"
9 
10 #include "SkArenaAlloc.h"
11 #include "SkNormalSource.h"
12 #include "SkNormalSourcePriv.h"
13 #include "SkPoint3.h"
14 #include "SkReadBuffer.h"
15 #include "SkWriteBuffer.h"
16 
17 #if SK_SUPPORT_GPU
18 #include "glsl/GrGLSLFragmentProcessor.h"
19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "SkGr.h"
21 
22 /** \class NormalBevelFP
23  *
24  *  Fragment processor for the SkNormalBevelSource.
25  *
26  *  @param bevelType    type of the bevel
27  *  @param bevelWidth   width of the bevel in device space
28  *  @param bevelHeight  height of the bevel in device space
29  */
30 class NormalBevelFP : public GrFragmentProcessor {
31 public:
NormalBevelFP(SkNormalSource::BevelType bevelType,SkScalar bevelWidth,SkScalar bevelHeight)32     NormalBevelFP(SkNormalSource::BevelType bevelType, SkScalar bevelWidth, SkScalar bevelHeight)
33             : INHERITED(kNone_OptimizationFlags)
34             , fBevelType(bevelType)
35             , fBevelWidth(bevelWidth)
36             , fBevelHeight(bevelHeight) {
37         this->initClassID<NormalBevelFP>();
38 
39         this->setWillUseDistanceVectorField();
40     }
41 
42     class GLSLNormalBevelFP : public GLSLNormalFP {
43     public:
GLSLNormalBevelFP()44         GLSLNormalBevelFP() {
45             fPrevWidth = SkFloatToScalar(0.0f);
46             fPrevHeight = SkFloatToScalar(0.0f);
47         }
48 
onEmitCode(EmitArgs & args)49         void onEmitCode(EmitArgs& args) override {
50             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
51             const NormalBevelFP& fp = args.fFp.cast<NormalBevelFP>();
52             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
53 
54             // Determining necessary uniforms and initializing them
55             bool needWidth = true;
56             bool needHeight = (fp.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
57                                fp.fBevelType == SkNormalSource::BevelType::kRoundedIn);
58             bool needNormalized = (fp.fBevelType == SkNormalSource::BevelType::kLinear);
59 
60             const char *widthUniName = nullptr;
61             if (needWidth) {
62                 fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
63                                                        kDefault_GrSLPrecision, "Width",
64                                                        &widthUniName);
65             }
66 
67             const char* heightUniName = nullptr;
68             if (needHeight) {
69                 fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
70                                                         kDefault_GrSLPrecision, "Height",
71                                                         &heightUniName);
72             }
73 
74             const char* normalizedWidthUniName = nullptr;
75             const char* normalizedHeightUniName = nullptr;
76             if (needNormalized) {
77                 fNormalizedWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
78                                                                  kFloat_GrSLType,
79                                                                  kDefault_GrSLPrecision,
80                                                                  "NormalizedWidth",
81                                                                  &normalizedWidthUniName);
82                 fNormalizedHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
83                                                                   kFloat_GrSLType,
84                                                                   kDefault_GrSLPrecision,
85                                                                   "NormalizedHeight",
86                                                                   &normalizedHeightUniName);
87             }
88 
89             // Here we are splitting the distance vector into length and normalized direction
90             fragBuilder->codeAppendf("float dv_length = %s.z;",
91                                      fragBuilder->distanceVectorName());
92             fragBuilder->codeAppendf("vec2 dv_norm = %s.xy;",
93                                      fragBuilder->distanceVectorName());
94 
95             // Asserting presence of necessary uniforms
96             SkASSERT(widthUniName);
97 
98             fragBuilder->codeAppend( "vec3 normal;");
99             fragBuilder->codeAppendf("if (dv_length >= %s) {", widthUniName);
100             fragBuilder->codeAppend( "    normal = vec3(0.0, 0.0, 1.0);");
101             fragBuilder->codeAppend( "} else {");
102             this->emitMath(fragBuilder, fp.fBevelType, widthUniName, heightUniName,
103                            normalizedWidthUniName, normalizedHeightUniName);
104             fragBuilder->codeAppend( "}");
105             fragBuilder->codeAppendf("%s = vec4(normal, 0.0);", args.fOutputColor);
106         }
107 
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)108         static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
109             const NormalBevelFP& fp = proc.cast<NormalBevelFP>();
110             b->add32(static_cast<int>(fp.fBevelType));
111         }
112 
113     protected:
setNormalData(const GrGLSLProgramDataManager & pdman,const GrProcessor & proc)114         void setNormalData(const GrGLSLProgramDataManager& pdman,
115                            const GrProcessor& proc) override {
116             const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
117 
118             // Updating uniform if bevel type requires it and data has changed
119 
120             bool needWidth = true;
121             bool needHeight = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
122                                normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedIn);
123             bool needNormalized = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kLinear);
124 
125             bool dirtyWidth = (fPrevWidth  != normalBevelFP.fBevelWidth);
126             bool dirtyHeight = (fPrevHeight != normalBevelFP.fBevelHeight);
127             bool dirtyNormalized = (dirtyHeight || dirtyWidth);
128 
129 
130             if (needWidth && dirtyWidth) {
131                 pdman.set1f(fWidthUni, normalBevelFP.fBevelWidth);
132                 fPrevWidth = normalBevelFP.fBevelWidth;
133             }
134             if (needHeight && dirtyHeight) {
135                 pdman.set1f(fHeightUni, normalBevelFP.fBevelHeight);
136                 fPrevHeight = normalBevelFP.fBevelHeight;
137             }
138             if (needNormalized && dirtyNormalized) {
139                 SkScalar height = normalBevelFP.fBevelHeight;
140                 SkScalar width  = normalBevelFP.fBevelWidth;
141 
142                 SkScalar length = SkScalarSqrt(SkScalarSquare(height) + SkScalarSquare(width));
143                 pdman.set1f(fNormalizedHeightUni, height/length);
144                 pdman.set1f(fNormalizedWidthUni, width/length);
145             }
146         }
147 
148         // This method emits the code that calculates the normal orthgonal to the simulated beveled
149         // surface. In the comments inside the function, the math involved is described. For this
150         // purpose, the d-axis is defined to be the axis co-linear to the distance vector, where the
151         // origin is the end of the bevel inside the shape.
emitMath(GrGLSLFPFragmentBuilder * fb,SkNormalSource::BevelType type,const char * width,const char * height,const char * normalizedWidth,const char * normalizedHeight)152         void emitMath(GrGLSLFPFragmentBuilder* fb, SkNormalSource::BevelType type,
153                       const char* width, const char* height, const char* normalizedWidth,
154                       const char* normalizedHeight) {
155             switch (type) {
156                 case SkNormalSource::BevelType::kLinear:
157                     // Asserting presence of necessary uniforms
158                     SkASSERT(normalizedHeight);
159                     SkASSERT(normalizedWidth);
160 
161                     // Because the slope of the bevel is -height/width, the vector
162                     // normalized(vec2(height, width)) is the d- and z-components of the normal
163                     // vector that is orthogonal to the linear bevel. Multiplying the d-component
164                     // to the normalized distance vector splits it into x- and y-components.
165                     fb->codeAppendf("normal = vec3(%s * dv_norm, %s);",
166                                     normalizedHeight, normalizedWidth);
167                     break;
168                 case SkNormalSource::BevelType::kRoundedOut:
169                     // Fall through
170                 case SkNormalSource::BevelType::kRoundedIn:
171                     // Asserting presence of necessary uniforms
172                     SkASSERT(height);
173                     SkASSERT(width);
174 
175                     // Setting the current position in the d-axis to the distance from the end of
176                     // the bevel as opposed to the beginning if the bevel is rounded in, essentially
177                     // flipping the bevel calculations.
178                     if ( type == SkNormalSource::BevelType::kRoundedIn ) {
179                         fb->codeAppendf("float currentPos_d = %s - dv_length;", width);
180                     } else if (type == SkNormalSource::BevelType::kRoundedOut) {
181                         fb->codeAppendf("float currentPos_d = dv_length;");
182                     }
183 
184                     fb->codeAppendf("float rootDOverW = sqrt(currentPos_d/%s);", width);
185 
186                     // Calculating the d- and z-components of the normal, where 'd' is the axis
187                     // co-linear to the distance vector. Equation was derived from the formula for
188                     // a bezier curve by solving the parametric equation for d(t) and z(t), then
189                     // with those, calculate d'(t), z'(t) and t(d), and from these, d'(d) and z'(d).
190                     // z'(d)/d'(d) results in the slope of the bevel at d, so we construct an
191                     // orthogonal vector of slope -d'(d)/z'(d) and length 1.
192                     fb->codeAppendf("vec2 unnormalizedNormal_dz = vec2(%s*(1.0-rootDOverW), "
193                                                                        "%s*rootDOverW);",
194                                     height, width);
195                     fb->codeAppendf("vec2 normal_dz = normalize(unnormalizedNormal_dz);");
196 
197                     // Multiplying the d-component to the normalized distance vector splits it into
198                     // x- and y-components.
199                     fb->codeAppendf("normal = vec3(normal_dz.x*dv_norm, normal_dz.y);");
200 
201                     break;
202                 default:
203                     SkDEBUGFAIL("Invalid bevel type passed to emitMath");
204             }
205         }
206 
207     private:
208         SkScalar fPrevWidth;
209         GrGLSLProgramDataManager::UniformHandle fWidthUni;
210 
211         SkScalar fPrevHeight;
212         GrGLSLProgramDataManager::UniformHandle fHeightUni;
213 
214         // width / length(<width,height>)
215         GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni;
216         // height / length(<width,height>)
217         GrGLSLProgramDataManager::UniformHandle fNormalizedHeightUni;
218     };
219 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const220     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
221         GLSLNormalBevelFP::GenKey(*this, caps, b);
222     }
223 
name() const224     const char* name() const override { return "NormalBevelFP"; }
225 
226 private:
onCreateGLSLInstance() const227     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalBevelFP; }
228 
onIsEqual(const GrFragmentProcessor & proc) const229     bool onIsEqual(const GrFragmentProcessor& proc) const override {
230         const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
231         return fBevelType   == normalBevelFP.fBevelType &&
232                fBevelWidth  == normalBevelFP.fBevelWidth &&
233                fBevelHeight == normalBevelFP.fBevelHeight;
234     }
235 
236     SkNormalSource::BevelType fBevelType;
237     SkScalar fBevelWidth;
238     SkScalar fBevelHeight;
239 
240     typedef GrFragmentProcessor INHERITED;
241 };
242 
asFragmentProcessor(const SkShader::AsFPArgs & args) const243 sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor(
244         const SkShader::AsFPArgs& args) const {
245 
246     // This assumes a uniform scale. Anisotropic scaling might not be handled gracefully.
247     SkScalar maxScale = args.fViewMatrix->getMaxScale();
248 
249     // Providing device-space width and height
250     return sk_make_sp<NormalBevelFP>(fType, maxScale * fWidth, maxScale * fHeight);
251 }
252 
253 #endif // SK_SUPPORT_GPU
254 
255 ////////////////////////////////////////////////////////////////////////////
256 
Provider()257 SkNormalBevelSourceImpl::Provider::Provider() {}
258 
~Provider()259 SkNormalBevelSourceImpl::Provider::~Provider() {}
260 
asProvider(const SkShader::ContextRec & rec,SkArenaAlloc * alloc) const261 SkNormalSource::Provider* SkNormalBevelSourceImpl::asProvider(const SkShader::ContextRec &rec,
262                                                               SkArenaAlloc* alloc) const {
263     return alloc->make<Provider>();
264 }
265 
266 // TODO Implement feature for the CPU pipeline
fillScanLine(int x,int y,SkPoint3 output[],int count) const267 void SkNormalBevelSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
268                                                      int count) const {
269     for (int i = 0; i < count; i++) {
270         output[i] = {0.0f, 0.0f, 1.0f};
271     }
272 }
273 
274 ////////////////////////////////////////////////////////////////////////////////
275 
CreateProc(SkReadBuffer & buf)276 sk_sp<SkFlattenable> SkNormalBevelSourceImpl::CreateProc(SkReadBuffer& buf) {
277 
278     auto type = static_cast<SkNormalSource::BevelType>(buf.readInt());
279     SkScalar width = buf.readScalar();
280     SkScalar height = buf.readScalar();
281 
282     return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
283 }
284 
flatten(SkWriteBuffer & buf) const285 void SkNormalBevelSourceImpl::flatten(SkWriteBuffer& buf) const {
286     this->INHERITED::flatten(buf);
287 
288     buf.writeInt(static_cast<int>(fType));
289     buf.writeScalar(fWidth);
290     buf.writeScalar(fHeight);
291 }
292 
293 ////////////////////////////////////////////////////////////////////////////
294 
MakeBevel(BevelType type,SkScalar width,SkScalar height)295 sk_sp<SkNormalSource> SkNormalSource::MakeBevel(BevelType type, SkScalar width, SkScalar height) {
296     /* TODO make sure these checks are tolerant enough to account for loss of conversion when GPUs
297        use 16-bit float types. We don't want to assume stuff is non-zero on the GPU and be wrong.*/
298     SkASSERT(width > 0.0f && !SkScalarNearlyZero(width));
299     if (SkScalarNearlyZero(height)) {
300         return SkNormalSource::MakeFlat();
301     }
302 
303     return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
304 }
305