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