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