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