1 /*
2  * Copyright 2013 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 "GrDistanceFieldGeoProc.h"
9 
10 #include "GrTexture.h"
11 #include "SkDistanceFieldGen.h"
12 #include "glsl/GrGLSLFragmentShaderBuilder.h"
13 #include "glsl/GrGLSLGeometryProcessor.h"
14 #include "glsl/GrGLSLProgramDataManager.h"
15 #include "glsl/GrGLSLUniformHandler.h"
16 #include "glsl/GrGLSLUtil.h"
17 #include "glsl/GrGLSLVarying.h"
18 #include "glsl/GrGLSLVertexShaderBuilder.h"
19 
20 // Assuming a radius of a little less than the diagonal of the fragment
21 #define SK_DistanceFieldAAFactor     "0.65"
22 
23 class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor {
24 public:
GrGLDistanceFieldA8TextGeoProc()25     GrGLDistanceFieldA8TextGeoProc()
26         : fViewMatrix(SkMatrix::InvalidMatrix())
27 #ifdef SK_GAMMA_APPLY_TO_A8
28         , fDistanceAdjust(-1.0f)
29 #endif
30         {}
31 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)32     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
33         const GrDistanceFieldA8TextGeoProc& dfTexEffect =
34                 args.fGP.cast<GrDistanceFieldA8TextGeoProc>();
35         GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
36 
37         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
38         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
39         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
40 
41         // emit attributes
42         varyingHandler->emitAttributes(dfTexEffect);
43 
44 #ifdef SK_GAMMA_APPLY_TO_A8
45         // adjust based on gamma
46         const char* distanceAdjustUniName = nullptr;
47         // width, height, 1/(3*width)
48         fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
49                                                         kFloat_GrSLType, kDefault_GrSLPrecision,
50                                                         "DistanceAdjust", &distanceAdjustUniName);
51 #endif
52 
53         // Setup pass through color
54         varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
55 
56         // Setup position
57         this->setupPosition(vertBuilder,
58                             uniformHandler,
59                             gpArgs,
60                             dfTexEffect.inPosition()->fName,
61                             dfTexEffect.viewMatrix(),
62                             &fViewMatrixUniform);
63 
64         // emit transforms
65         this->emitTransforms(vertBuilder,
66                              varyingHandler,
67                              uniformHandler,
68                              gpArgs->fPositionVar,
69                              dfTexEffect.inPosition()->fName,
70                              args.fFPCoordTransformHandler);
71 
72         // add varyings
73         GrGLSLVertToFrag recipScale(kFloat_GrSLType);
74         GrGLSLVertToFrag uv(kVec2f_GrSLType);
75         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
76                               kUniformScale_DistanceFieldEffectMask;
77         bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
78         bool isGammaCorrect =
79             SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
80         varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
81         vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName);
82 
83         // compute numbers to be hardcoded to convert texture coordinates from float to int
84         SkASSERT(dfTexEffect.numTextureSamplers() == 1);
85         GrTexture* atlas = dfTexEffect.textureSampler(0).texture();
86         SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
87 
88         GrGLSLVertToFrag st(kVec2f_GrSLType);
89         varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
90         vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(),
91                                  atlas->width(), atlas->height(),
92                                  dfTexEffect.inTextureCoords()->fName);
93 
94         // Use highp to work around aliasing issues
95         fragBuilder->codeAppendf("highp vec2 uv = %s;\n", uv.fsIn());
96 
97         fragBuilder->codeAppend("\tfloat texColor = ");
98         fragBuilder->appendTextureLookup(args.fTexSamplers[0],
99                                          "uv",
100                                          kVec2f_GrSLType);
101         fragBuilder->codeAppend(".r;\n");
102         fragBuilder->codeAppend("\tfloat distance = "
103                        SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
104 #ifdef SK_GAMMA_APPLY_TO_A8
105         // adjust width based on gamma
106         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
107 #endif
108 
109         fragBuilder->codeAppend("float afwidth;");
110         if (isUniformScale) {
111             // For uniform scale, we adjust for the effect of the transformation on the distance
112             // by using the length of the gradient of the t coordinate in the y direction.
113             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
114 
115             // this gives us a smooth step across approximately one fragment
116 #ifdef SK_VULKAN
117             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));",
118                                      st.fsIn());
119 #else
120             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
121             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));",
122                                      st.fsIn());
123 #endif
124         } else if (isSimilarity) {
125             // For similarity transform, we adjust the effect of the transformation on the distance
126             // by using the length of the gradient of the texture coordinates. We use st coordinates
127             // to ensure we're mapping 1:1 from texel space to pixel space.
128             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
129 
130             // this gives us a smooth step across approximately one fragment
131 #ifdef SK_VULKAN
132             fragBuilder->codeAppendf("float st_grad_len = length(dFdx(%s));", st.fsIn());
133 #else
134             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
135             fragBuilder->codeAppendf("float st_grad_len = length(dFdy(%s));", st.fsIn());
136 #endif
137             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
138         } else {
139             // For general transforms, to determine the amount of correction we multiply a unit
140             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
141             // (which is the inverse transform for this fragment) and take the length of the result.
142             fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
143             // the length of the gradient may be 0, so we need to check for this
144             // this also compensates for the Adreno, which likes to drop tiles on division by 0
145             fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
146             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
147             fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
148             fragBuilder->codeAppend("} else {");
149             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
150             fragBuilder->codeAppend("}");
151 
152             fragBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn());
153             fragBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn());
154             fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
155             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
156 
157             // this gives us a smooth step across approximately one fragment
158             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
159         }
160 
161         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
162         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
163         // mapped linearly to coverage, so use a linear step:
164         if (isGammaCorrect) {
165             fragBuilder->codeAppend(
166                 "float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);");
167         } else {
168             fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
169         }
170 
171         fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
172     }
173 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,FPCoordTransformIter && transformIter)174     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
175                  FPCoordTransformIter&& transformIter) override {
176 #ifdef SK_GAMMA_APPLY_TO_A8
177         const GrDistanceFieldA8TextGeoProc& dfTexEffect = proc.cast<GrDistanceFieldA8TextGeoProc>();
178         float distanceAdjust = dfTexEffect.getDistanceAdjust();
179         if (distanceAdjust != fDistanceAdjust) {
180             pdman.set1f(fDistanceAdjustUni, distanceAdjust);
181             fDistanceAdjust = distanceAdjust;
182         }
183 #endif
184         const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>();
185 
186         if (!dfa8gp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfa8gp.viewMatrix())) {
187             fViewMatrix = dfa8gp.viewMatrix();
188             float viewMatrix[3 * 3];
189             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
190             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
191         }
192         this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
193     }
194 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)195     static inline void GenKey(const GrGeometryProcessor& gp,
196                               const GrShaderCaps&,
197                               GrProcessorKeyBuilder* b) {
198         const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>();
199         uint32_t key = dfTexEffect.getFlags();
200         key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
201         b->add32(key);
202 
203         // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
204         SkASSERT(gp.numTextureSamplers() == 1);
205         GrTexture* atlas = gp.textureSampler(0).texture();
206         SkASSERT(atlas);
207         b->add32(atlas->width());
208         b->add32(atlas->height());
209     }
210 
211 private:
212     SkMatrix      fViewMatrix;
213     UniformHandle fViewMatrixUniform;
214 #ifdef SK_GAMMA_APPLY_TO_A8
215     float         fDistanceAdjust;
216     UniformHandle fDistanceAdjustUni;
217 #endif
218 
219     typedef GrGLSLGeometryProcessor INHERITED;
220 };
221 
222 ///////////////////////////////////////////////////////////////////////////////
223 
GrDistanceFieldA8TextGeoProc(GrResourceProvider * resourceProvider,GrColor color,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,const GrSamplerParams & params,float distanceAdjust,uint32_t flags,bool usesLocalCoords)224 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrResourceProvider* resourceProvider,
225                                                            GrColor color,
226                                                            const SkMatrix& viewMatrix,
227                                                            sk_sp<GrTextureProxy> proxy,
228                                                            const GrSamplerParams& params,
229 #ifdef SK_GAMMA_APPLY_TO_A8
230                                                            float distanceAdjust,
231 #endif
232                                                            uint32_t flags,
233                                                            bool usesLocalCoords)
234     : fColor(color)
235     , fViewMatrix(viewMatrix)
236     , fTextureSampler(resourceProvider, std::move(proxy), params)
237 #ifdef SK_GAMMA_APPLY_TO_A8
238     , fDistanceAdjust(distanceAdjust)
239 #endif
240     , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
241     , fInColor(nullptr)
242     , fUsesLocalCoords(usesLocalCoords) {
243     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
244     this->initClassID<GrDistanceFieldA8TextGeoProc>();
245     fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
246                                          kHigh_GrSLPrecision);
247     fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
248     fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType,
249                                               kHigh_GrSLPrecision);
250     this->addTextureSampler(&fTextureSampler);
251 }
252 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const253 void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
254                                                        GrProcessorKeyBuilder* b) const {
255     GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b);
256 }
257 
258 GrGLSLPrimitiveProcessor*
createGLSLInstance(const GrShaderCaps &) const259 GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrShaderCaps&) const {
260     return new GrGLDistanceFieldA8TextGeoProc();
261 }
262 
263 ///////////////////////////////////////////////////////////////////////////////
264 
265 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
266 
267 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)268 sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
269     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
270                                         : GrProcessorUnitTest::kAlphaTextureIdx;
271     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
272 
273     static const SkShader::TileMode kTileModes[] = {
274         SkShader::kClamp_TileMode,
275         SkShader::kRepeat_TileMode,
276         SkShader::kMirror_TileMode,
277     };
278     SkShader::TileMode tileModes[] = {
279         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
280         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
281     };
282     GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
283                                                              : GrSamplerParams::kNone_FilterMode);
284 
285     uint32_t flags = 0;
286     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
287     if (flags & kSimilarity_DistanceFieldEffectFlag) {
288         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
289     }
290 
291     return GrDistanceFieldA8TextGeoProc::Make(d->resourceProvider(),
292                                               GrRandomColor(d->fRandom),
293                                               GrTest::TestMatrix(d->fRandom),
294                                               std::move(proxy), params,
295 #ifdef SK_GAMMA_APPLY_TO_A8
296                                               d->fRandom->nextF(),
297 #endif
298                                               flags,
299                                               d->fRandom->nextBool());
300 }
301 #endif
302 
303 ///////////////////////////////////////////////////////////////////////////////
304 
305 class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
306 public:
GrGLDistanceFieldPathGeoProc()307     GrGLDistanceFieldPathGeoProc()
308         : fViewMatrix(SkMatrix::InvalidMatrix())
309         , fTextureSize(SkISize::Make(-1, -1)) {}
310 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)311     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
312         const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>();
313 
314         GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
315 
316         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
317         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
318         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
319 
320         // emit attributes
321         varyingHandler->emitAttributes(dfTexEffect);
322 
323         GrGLSLVertToFrag v(kVec2f_GrSLType);
324         varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
325 
326         // setup pass through color
327         varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
328         vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
329 
330         // Setup position
331         this->setupPosition(vertBuilder,
332                             uniformHandler,
333                             gpArgs,
334                             dfTexEffect.inPosition()->fName,
335                             dfTexEffect.viewMatrix(),
336                             &fViewMatrixUniform);
337 
338         // emit transforms
339         this->emitTransforms(vertBuilder,
340                              varyingHandler,
341                              uniformHandler,
342                              gpArgs->fPositionVar,
343                              dfTexEffect.inPosition()->fName,
344                              args.fFPCoordTransformHandler);
345 
346         const char* textureSizeUniName = nullptr;
347         fTextureSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
348                                                      kVec2f_GrSLType, kDefault_GrSLPrecision,
349                                                      "TextureSize", &textureSizeUniName);
350 
351         // Use highp to work around aliasing issues
352         fragBuilder->codeAppendf("highp vec2 uv = %s;", v.fsIn());
353 
354         fragBuilder->codeAppend("float texColor = ");
355         fragBuilder->appendTextureLookup(args.fTexSamplers[0],
356                                          "uv",
357                                          kVec2f_GrSLType);
358         fragBuilder->codeAppend(".r;");
359         fragBuilder->codeAppend("float distance = "
360             SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
361 
362         fragBuilder->codeAppendf("highp vec2 st = uv*%s;", textureSizeUniName);
363         fragBuilder->codeAppend("float afwidth;");
364         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
365                                kUniformScale_DistanceFieldEffectMask;
366         bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
367         bool isGammaCorrect =
368             SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
369         if (isUniformScale) {
370             // For uniform scale, we adjust for the effect of the transformation on the distance
371             // by using the length of the gradient of the t coordinate in the y direction.
372             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
373 
374             // this gives us a smooth step across approximately one fragment
375 #ifdef SK_VULKAN
376             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(st.x));");
377 #else
378             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
379             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));");
380 #endif
381         } else if (isSimilarity) {
382             // For similarity transform, we adjust the effect of the transformation on the distance
383             // by using the length of the gradient of the texture coordinates. We use st coordinates
384             // to ensure we're mapping 1:1 from texel space to pixel space.
385 
386             // this gives us a smooth step across approximately one fragment
387 #ifdef SK_VULKAN
388             fragBuilder->codeAppend("float st_grad_len = length(dFdx(st));");
389 #else
390             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
391             fragBuilder->codeAppend("float st_grad_len = length(dFdy(st));");
392 #endif
393             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
394         } else {
395             // For general transforms, to determine the amount of correction we multiply a unit
396             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
397             // (which is the inverse transform for this fragment) and take the length of the result.
398             fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
399             // the length of the gradient may be 0, so we need to check for this
400             // this also compensates for the Adreno, which likes to drop tiles on division by 0
401             fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
402             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
403             fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
404             fragBuilder->codeAppend("} else {");
405             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
406             fragBuilder->codeAppend("}");
407 
408             fragBuilder->codeAppend("vec2 Jdx = dFdx(st);");
409             fragBuilder->codeAppend("vec2 Jdy = dFdy(st);");
410             fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
411             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
412 
413             // this gives us a smooth step across approximately one fragment
414             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
415         }
416         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
417         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
418         // mapped linearly to coverage, so use a linear step:
419         if (isGammaCorrect) {
420             fragBuilder->codeAppend(
421                 "float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);");
422         } else {
423             fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
424         }
425 
426         fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
427     }
428 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,FPCoordTransformIter && transformIter)429     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
430                  FPCoordTransformIter&& transformIter) override {
431         SkASSERT(fTextureSizeUni.isValid());
432 
433         GrTexture* texture = proc.textureSampler(0).texture();
434         if (texture->width() != fTextureSize.width() ||
435             texture->height() != fTextureSize.height()) {
436             fTextureSize = SkISize::Make(texture->width(), texture->height());
437             pdman.set2f(fTextureSizeUni,
438                         SkIntToScalar(fTextureSize.width()),
439                         SkIntToScalar(fTextureSize.height()));
440         }
441 
442         const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
443 
444         if (!dfpgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfpgp.viewMatrix())) {
445             fViewMatrix = dfpgp.viewMatrix();
446             float viewMatrix[3 * 3];
447             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
448             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
449         }
450         this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
451     }
452 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)453     static inline void GenKey(const GrGeometryProcessor& gp,
454                               const GrShaderCaps&,
455                               GrProcessorKeyBuilder* b) {
456         const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>();
457 
458         uint32_t key = dfTexEffect.getFlags();
459         key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
460         b->add32(key);
461     }
462 
463 private:
464     UniformHandle fTextureSizeUni;
465     UniformHandle fViewMatrixUniform;
466     SkMatrix      fViewMatrix;
467     SkISize       fTextureSize;
468 
469     typedef GrGLSLGeometryProcessor INHERITED;
470 };
471 
472 ///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldPathGeoProc(GrResourceProvider * resourceProvider,GrColor color,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,const GrSamplerParams & params,uint32_t flags,bool usesLocalCoords)473 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
474         GrResourceProvider* resourceProvider,
475         GrColor color,
476         const SkMatrix& viewMatrix,
477         sk_sp<GrTextureProxy> proxy,
478         const GrSamplerParams& params,
479         uint32_t flags,
480         bool usesLocalCoords)
481     : fColor(color)
482     , fViewMatrix(viewMatrix)
483     , fTextureSampler(resourceProvider, std::move(proxy), params)
484     , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
485     , fInColor(nullptr)
486     , fUsesLocalCoords(usesLocalCoords) {
487     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
488     this->initClassID<GrDistanceFieldPathGeoProc>();
489     fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
490                                          kHigh_GrSLPrecision);
491     fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
492     fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType);
493     this->addTextureSampler(&fTextureSampler);
494 }
495 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const496 void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
497                                                      GrProcessorKeyBuilder* b) const {
498     GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b);
499 }
500 
501 GrGLSLPrimitiveProcessor*
createGLSLInstance(const GrShaderCaps &) const502 GrDistanceFieldPathGeoProc::createGLSLInstance(const GrShaderCaps&) const {
503     return new GrGLDistanceFieldPathGeoProc();
504 }
505 
506 ///////////////////////////////////////////////////////////////////////////////
507 
508 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
509 
510 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)511 sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
512     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
513                                         : GrProcessorUnitTest::kAlphaTextureIdx;
514     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
515 
516     static const SkShader::TileMode kTileModes[] = {
517         SkShader::kClamp_TileMode,
518         SkShader::kRepeat_TileMode,
519         SkShader::kMirror_TileMode,
520     };
521     SkShader::TileMode tileModes[] = {
522         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
523         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
524     };
525     GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
526                                                              : GrSamplerParams::kNone_FilterMode);
527 
528     uint32_t flags = 0;
529     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
530     if (flags & kSimilarity_DistanceFieldEffectFlag) {
531         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
532     }
533 
534     return GrDistanceFieldPathGeoProc::Make(d->resourceProvider(),
535                                             GrRandomColor(d->fRandom),
536                                             GrTest::TestMatrix(d->fRandom),
537                                             std::move(proxy),
538                                             params,
539                                             flags,
540                                             d->fRandom->nextBool());
541 }
542 #endif
543 
544 ///////////////////////////////////////////////////////////////////////////////
545 
546 class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
547 public:
GrGLDistanceFieldLCDTextGeoProc()548     GrGLDistanceFieldLCDTextGeoProc()
549         : fViewMatrix(SkMatrix::InvalidMatrix()) {
550         fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
551     }
552 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)553     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
554         const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
555                 args.fGP.cast<GrDistanceFieldLCDTextGeoProc>();
556 
557         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
558         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
559         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
560 
561         // emit attributes
562         varyingHandler->emitAttributes(dfTexEffect);
563 
564         GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
565 
566         // setup pass through color
567         varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
568 
569         // Setup position
570         this->setupPosition(vertBuilder,
571                             uniformHandler,
572                             gpArgs,
573                             dfTexEffect.inPosition()->fName,
574                             dfTexEffect.viewMatrix(),
575                             &fViewMatrixUniform);
576 
577         // emit transforms
578         this->emitTransforms(vertBuilder,
579                              varyingHandler,
580                              uniformHandler,
581                              gpArgs->fPositionVar,
582                              dfTexEffect.inPosition()->fName,
583                              args.fFPCoordTransformHandler);
584 
585         // set up varyings
586         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
587                               kUniformScale_DistanceFieldEffectMask;
588         bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
589         bool isGammaCorrect =
590             SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
591         GrGLSLVertToFrag recipScale(kFloat_GrSLType);
592         GrGLSLVertToFrag uv(kVec2f_GrSLType);
593         varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
594         vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName);
595 
596         // compute numbers to be hardcoded to convert texture coordinates from float to int
597         SkASSERT(dfTexEffect.numTextureSamplers() == 1);
598         GrTexture* atlas = dfTexEffect.textureSampler(0).texture();
599         SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
600 
601         GrGLSLVertToFrag st(kVec2f_GrSLType);
602         varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
603         vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(),
604                                  atlas->width(), atlas->height(),
605                                  dfTexEffect.inTextureCoords()->fName);
606 
607         // add frag shader code
608 
609         // create LCD offset adjusted by inverse of transform
610         // Use highp to work around aliasing issues
611         fragBuilder->codeAppendf("highp vec2 uv = %s;\n", uv.fsIn());
612 
613         SkScalar lcdDelta = 1.0f / (3.0f * atlas->width());
614         if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
615             fragBuilder->codeAppendf("highp float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta);
616         } else {
617             fragBuilder->codeAppendf("highp float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta);
618         }
619         if (isUniformScale) {
620 #ifdef SK_VULKAN
621             fragBuilder->codeAppendf("float st_grad_len = abs(dFdx(%s.x));", st.fsIn());
622 #else
623             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
624             fragBuilder->codeAppendf("float st_grad_len = abs(dFdy(%s.y));", st.fsIn());
625 #endif
626             fragBuilder->codeAppend("vec2 offset = vec2(st_grad_len*delta, 0.0);");
627         } else if (isSimilarity) {
628             // For a similarity matrix with rotation, the gradient will not be aligned
629             // with the texel coordinate axes, so we need to calculate it.
630 #ifdef SK_VULKAN
631             fragBuilder->codeAppendf("vec2 st_grad = dFdx(%s);", st.fsIn());
632             fragBuilder->codeAppend("vec2 offset = delta*st_grad;");
633 #else
634             // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to
635             // get the gradient in the x direction.
636             fragBuilder->codeAppendf("vec2 st_grad = dFdy(%s);", st.fsIn());
637             fragBuilder->codeAppend("vec2 offset = delta*vec2(st_grad.y, -st_grad.x);");
638 #endif
639             fragBuilder->codeAppend("float st_grad_len = length(st_grad);");
640         } else {
641             fragBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn());
642 
643             fragBuilder->codeAppend("vec2 Jdx = dFdx(st);");
644             fragBuilder->codeAppend("vec2 Jdy = dFdy(st);");
645             fragBuilder->codeAppend("vec2 offset = delta*Jdx;");
646         }
647 
648         // green is distance to uv center
649         fragBuilder->codeAppend("\tvec4 texColor = ");
650         fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv", kVec2f_GrSLType);
651         fragBuilder->codeAppend(";\n");
652         fragBuilder->codeAppend("\tvec3 distance;\n");
653         fragBuilder->codeAppend("\tdistance.y = texColor.r;\n");
654         // red is distance to left offset
655         fragBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n");
656         fragBuilder->codeAppend("\ttexColor = ");
657         fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv_adjusted", kVec2f_GrSLType);
658         fragBuilder->codeAppend(";\n");
659         fragBuilder->codeAppend("\tdistance.x = texColor.r;\n");
660         // blue is distance to right offset
661         fragBuilder->codeAppend("\tuv_adjusted = uv + offset;\n");
662         fragBuilder->codeAppend("\ttexColor = ");
663         fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv_adjusted", kVec2f_GrSLType);
664         fragBuilder->codeAppend(";\n");
665         fragBuilder->codeAppend("\tdistance.z = texColor.r;\n");
666 
667         fragBuilder->codeAppend("\tdistance = "
668            "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));");
669 
670         // adjust width based on gamma
671         const char* distanceAdjustUniName = nullptr;
672         fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
673                                                         kVec3f_GrSLType, kDefault_GrSLPrecision,
674                                                         "DistanceAdjust", &distanceAdjustUniName);
675         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
676 
677         // To be strictly correct, we should compute the anti-aliasing factor separately
678         // for each color component. However, this is only important when using perspective
679         // transformations, and even then using a single factor seems like a reasonable
680         // trade-off between quality and speed.
681         fragBuilder->codeAppend("float afwidth;");
682         if (isSimilarity) {
683             // For similarity transform (uniform scale-only is a subset of this), we adjust for the
684             // effect of the transformation on the distance by using the length of the gradient of
685             // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel
686             // space to pixel space.
687 
688             // this gives us a smooth step across approximately one fragment
689             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;");
690         } else {
691             // For general transforms, to determine the amount of correction we multiply a unit
692             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
693             // (which is the inverse transform for this fragment) and take the length of the result.
694             fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));");
695             // the length of the gradient may be 0, so we need to check for this
696             // this also compensates for the Adreno, which likes to drop tiles on division by 0
697             fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
698             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
699             fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
700             fragBuilder->codeAppend("} else {");
701             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
702             fragBuilder->codeAppend("}");
703             fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
704             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
705 
706             // this gives us a smooth step across approximately one fragment
707             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
708         }
709 
710         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
711         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
712         // mapped linearly to coverage, so use a linear step:
713         if (isGammaCorrect) {
714             fragBuilder->codeAppend("vec4 val = "
715                 "vec4(clamp(distance + vec3(afwidth) / vec3(2.0 * afwidth), 0.0, 1.0), 1.0);");
716         } else {
717             fragBuilder->codeAppend(
718                 "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
719         }
720 
721         // set alpha to be max of rgb coverage
722         fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);");
723 
724         fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage);
725     }
726 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & processor,FPCoordTransformIter && transformIter)727     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor,
728                  FPCoordTransformIter&& transformIter) override {
729         SkASSERT(fDistanceAdjustUni.isValid());
730 
731         const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>();
732         GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust();
733         if (wa != fDistanceAdjust) {
734             pdman.set3f(fDistanceAdjustUni,
735                         wa.fR,
736                         wa.fG,
737                         wa.fB);
738             fDistanceAdjust = wa;
739         }
740 
741         if (!dflcd.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dflcd.viewMatrix())) {
742             fViewMatrix = dflcd.viewMatrix();
743             float viewMatrix[3 * 3];
744             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
745             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
746         }
747         this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
748     }
749 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)750     static inline void GenKey(const GrGeometryProcessor& gp,
751                               const GrShaderCaps&,
752                               GrProcessorKeyBuilder* b) {
753         const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>();
754 
755         uint32_t key = dfTexEffect.getFlags();
756         key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
757         b->add32(key);
758 
759         // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
760         SkASSERT(gp.numTextureSamplers() == 1);
761         GrTexture* atlas = gp.textureSampler(0).texture();
762         SkASSERT(atlas);
763         b->add32(atlas->width());
764         b->add32(atlas->height());
765     }
766 
767 private:
768     SkMatrix                                     fViewMatrix;
769     UniformHandle                                fViewMatrixUniform;
770     UniformHandle                                fColorUniform;
771     GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
772     UniformHandle                                fDistanceAdjustUni;
773 
774     typedef GrGLSLGeometryProcessor INHERITED;
775 };
776 
777 ///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldLCDTextGeoProc(GrResourceProvider * resourceProvider,GrColor color,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,const GrSamplerParams & params,DistanceAdjust distanceAdjust,uint32_t flags,bool usesLocalCoords)778 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
779                                                   GrResourceProvider* resourceProvider,
780                                                   GrColor color, const SkMatrix& viewMatrix,
781                                                   sk_sp<GrTextureProxy> proxy,
782                                                   const GrSamplerParams& params,
783                                                   DistanceAdjust distanceAdjust,
784                                                   uint32_t flags, bool usesLocalCoords)
785     : fColor(color)
786     , fViewMatrix(viewMatrix)
787     , fTextureSampler(resourceProvider, std::move(proxy), params)
788     , fDistanceAdjust(distanceAdjust)
789     , fFlags(flags & kLCD_DistanceFieldEffectMask)
790     , fUsesLocalCoords(usesLocalCoords) {
791     SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
792     this->initClassID<GrDistanceFieldLCDTextGeoProc>();
793     fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
794                                          kHigh_GrSLPrecision);
795     fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
796     fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType,
797                                               kHigh_GrSLPrecision);
798     this->addTextureSampler(&fTextureSampler);
799 }
800 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const801 void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
802                                                         GrProcessorKeyBuilder* b) const {
803     GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b);
804 }
805 
createGLSLInstance(const GrShaderCaps &) const806 GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrShaderCaps&) const {
807     return new GrGLDistanceFieldLCDTextGeoProc();
808 }
809 
810 ///////////////////////////////////////////////////////////////////////////////
811 
812 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
813 
814 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)815 sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
816     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
817                                           GrProcessorUnitTest::kAlphaTextureIdx;
818     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
819 
820     static const SkShader::TileMode kTileModes[] = {
821         SkShader::kClamp_TileMode,
822         SkShader::kRepeat_TileMode,
823         SkShader::kMirror_TileMode,
824     };
825     SkShader::TileMode tileModes[] = {
826         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
827         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
828     };
829     GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
830                                                              : GrSamplerParams::kNone_FilterMode);
831     DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
832     uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
833     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
834     if (flags & kSimilarity_DistanceFieldEffectFlag) {
835         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
836     }
837     flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
838     return GrDistanceFieldLCDTextGeoProc::Make(d->resourceProvider(),
839                                                GrRandomColor(d->fRandom),
840                                                GrTest::TestMatrix(d->fRandom),
841                                                std::move(proxy), params,
842                                                wa,
843                                                flags,
844                                                d->fRandom->nextBool());
845 }
846 #endif
847