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