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
25class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor {
26public:
27    GrGLDistanceFieldA8TextGeoProc()
28        : fViewMatrix(SkMatrix::InvalidMatrix())
29#ifdef SK_GAMMA_APPLY_TO_A8
30        , fDistanceAdjust(-1.0f)
31#endif
32        {}
33
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
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
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
187private:
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
200GrDistanceFieldA8TextGeoProc::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
229void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
230                                                       GrProcessorKeyBuilder* b) const {
231    GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b);
232}
233
234GrGLSLPrimitiveProcessor* GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
235    return new GrGLDistanceFieldA8TextGeoProc();
236}
237
238///////////////////////////////////////////////////////////////////////////////
239
240GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
241
242const 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
270class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
271public:
272    GrGLDistanceFieldPathGeoProc()
273        : fViewMatrix(SkMatrix::InvalidMatrix())
274        , fTextureSize(SkISize::Make(-1, -1)) {}
275
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
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
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
405private:
406    UniformHandle fTextureSizeUni;
407    UniformHandle fViewMatrixUniform;
408    SkMatrix      fViewMatrix;
409    SkISize       fTextureSize;
410
411    typedef GrGLSLGeometryProcessor INHERITED;
412};
413
414///////////////////////////////////////////////////////////////////////////////
415
416GrDistanceFieldPathGeoProc::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
439void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
440                                                     GrProcessorKeyBuilder* b) const {
441    GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b);
442}
443
444GrGLSLPrimitiveProcessor* GrDistanceFieldPathGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
445    return new GrGLDistanceFieldPathGeoProc();
446}
447
448///////////////////////////////////////////////////////////////////////////////
449
450GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
451
452const 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
478class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
479public:
480    GrGLDistanceFieldLCDTextGeoProc()
481        : fViewMatrix(SkMatrix::InvalidMatrix()) {
482        fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
483    }
484
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
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
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
677private:
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
689GrDistanceFieldLCDTextGeoProc::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
711void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
712                                                        GrProcessorKeyBuilder* b) const {
713    GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b);
714}
715
716GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
717    return new GrGLDistanceFieldLCDTextGeoProc();
718}
719
720///////////////////////////////////////////////////////////////////////////////
721
722GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
723
724const 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