Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrDistanceFieldGeoProc.h"
      9 #include "GrInvariantOutput.h"
     10 #include "GrTexture.h"
     11 
     12 #include "SkDistanceFieldGen.h"
     13 
     14 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     15 #include "glsl/GrGLSLGeometryProcessor.h"
     16 #include "glsl/GrGLSLProgramDataManager.h"
     17 #include "glsl/GrGLSLUniformHandler.h"
     18 #include "glsl/GrGLSLUtil.h"
     19 #include "glsl/GrGLSLVarying.h"
     20 #include "glsl/GrGLSLVertexShaderBuilder.h"
     21 
     22 // Assuming a radius of a little less than the diagonal of the fragment
     23 #define SK_DistanceFieldAAFactor     "0.65"
     24 
     25 class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor {
     26 public:
     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 
    187 private:
    188     SkMatrix      fViewMatrix;
    189     UniformHandle fViewMatrixUniform;
    190 #ifdef SK_GAMMA_APPLY_TO_A8
    191     float         fDistanceAdjust;
    192     UniformHandle fDistanceAdjustUni;
    193 #endif
    194 
    195     typedef GrGLSLGeometryProcessor INHERITED;
    196 };
    197 
    198 ///////////////////////////////////////////////////////////////////////////////
    199 
    200 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrColor color,
    201                                                            const SkMatrix& viewMatrix,
    202                                                            GrTexture* texture,
    203                                                            const GrTextureParams& params,
    204 #ifdef SK_GAMMA_APPLY_TO_A8
    205                                                            float distanceAdjust,
    206 #endif
    207                                                            uint32_t flags,
    208                                                            bool usesLocalCoords)
    209     : fColor(color)
    210     , fViewMatrix(viewMatrix)
    211     , fTextureAccess(texture, params)
    212 #ifdef SK_GAMMA_APPLY_TO_A8
    213     , fDistanceAdjust(distanceAdjust)
    214 #endif
    215     , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
    216     , fInColor(nullptr)
    217     , fUsesLocalCoords(usesLocalCoords) {
    218     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
    219     this->initClassID<GrDistanceFieldA8TextGeoProc>();
    220     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
    221                                                    kHigh_GrSLPrecision));
    222     fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    223     fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
    224                                                         kVec2us_GrVertexAttribType,
    225                                                         kHigh_GrSLPrecision));
    226     this->addTextureAccess(&fTextureAccess);
    227 }
    228 
    229 void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
    230                                                        GrProcessorKeyBuilder* b) const {
    231     GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b);
    232 }
    233 
    234 GrGLSLPrimitiveProcessor* GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
    235     return new GrGLDistanceFieldA8TextGeoProc();
    236 }
    237 
    238 ///////////////////////////////////////////////////////////////////////////////
    239 
    240 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
    241 
    242 const GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
    243     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    244                                           GrProcessorUnitTest::kAlphaTextureIdx;
    245     static const SkShader::TileMode kTileModes[] = {
    246         SkShader::kClamp_TileMode,
    247         SkShader::kRepeat_TileMode,
    248         SkShader::kMirror_TileMode,
    249     };
    250     SkShader::TileMode tileModes[] = {
    251         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    252         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    253     };
    254     GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode :
    255                                                            GrTextureParams::kNone_FilterMode);
    256 
    257     return GrDistanceFieldA8TextGeoProc::Create(GrRandomColor(d->fRandom),
    258                                                 GrTest::TestMatrix(d->fRandom),
    259                                                 d->fTextures[texIdx], params,
    260 #ifdef SK_GAMMA_APPLY_TO_A8
    261                                                 d->fRandom->nextF(),
    262 #endif
    263                                                 d->fRandom->nextBool() ?
    264                                                     kSimilarity_DistanceFieldEffectFlag : 0,
    265                                                     d->fRandom->nextBool());
    266 }
    267 
    268 ///////////////////////////////////////////////////////////////////////////////
    269 
    270 class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
    271 public:
    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 
    405 private:
    406     UniformHandle fTextureSizeUni;
    407     UniformHandle fViewMatrixUniform;
    408     SkMatrix      fViewMatrix;
    409     SkISize       fTextureSize;
    410 
    411     typedef GrGLSLGeometryProcessor INHERITED;
    412 };
    413 
    414 ///////////////////////////////////////////////////////////////////////////////
    415 
    416 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
    417         GrColor color,
    418         const SkMatrix& viewMatrix,
    419         GrTexture* texture,
    420         const GrTextureParams& params,
    421         uint32_t flags,
    422         bool usesLocalCoords)
    423     : fColor(color)
    424     , fViewMatrix(viewMatrix)
    425     , fTextureAccess(texture, params)
    426     , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
    427     , fInColor(nullptr)
    428     , fUsesLocalCoords(usesLocalCoords) {
    429     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
    430     this->initClassID<GrDistanceFieldPathGeoProc>();
    431     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
    432                                                    kHigh_GrSLPrecision));
    433     fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    434     fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
    435                                                         kVec2f_GrVertexAttribType));
    436     this->addTextureAccess(&fTextureAccess);
    437 }
    438 
    439 void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
    440                                                      GrProcessorKeyBuilder* b) const {
    441     GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b);
    442 }
    443 
    444 GrGLSLPrimitiveProcessor* GrDistanceFieldPathGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
    445     return new GrGLDistanceFieldPathGeoProc();
    446 }
    447 
    448 ///////////////////////////////////////////////////////////////////////////////
    449 
    450 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
    451 
    452 const GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
    453     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
    454                                         : GrProcessorUnitTest::kAlphaTextureIdx;
    455     static const SkShader::TileMode kTileModes[] = {
    456         SkShader::kClamp_TileMode,
    457         SkShader::kRepeat_TileMode,
    458         SkShader::kMirror_TileMode,
    459     };
    460     SkShader::TileMode tileModes[] = {
    461         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    462         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    463     };
    464     GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode
    465                                                              : GrTextureParams::kNone_FilterMode);
    466 
    467     return GrDistanceFieldPathGeoProc::Create(GrRandomColor(d->fRandom),
    468                                               GrTest::TestMatrix(d->fRandom),
    469                                               d->fTextures[texIdx],
    470                                               params,
    471                                               d->fRandom->nextBool() ?
    472                                                       kSimilarity_DistanceFieldEffectFlag : 0,
    473                                                       d->fRandom->nextBool());
    474 }
    475 
    476 ///////////////////////////////////////////////////////////////////////////////
    477 
    478 class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
    479 public:
    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 
    677 private:
    678     SkMatrix                                     fViewMatrix;
    679     UniformHandle                                fViewMatrixUniform;
    680     UniformHandle                                fColorUniform;
    681     GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
    682     UniformHandle                                fDistanceAdjustUni;
    683 
    684     typedef GrGLSLGeometryProcessor INHERITED;
    685 };
    686 
    687 ///////////////////////////////////////////////////////////////////////////////
    688 
    689 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
    690                                                   GrColor color, const SkMatrix& viewMatrix,
    691                                                   GrTexture* texture, const GrTextureParams& params,
    692                                                   DistanceAdjust distanceAdjust,
    693                                                   uint32_t flags, bool usesLocalCoords)
    694     : fColor(color)
    695     , fViewMatrix(viewMatrix)
    696     , fTextureAccess(texture, params)
    697     , fDistanceAdjust(distanceAdjust)
    698     , fFlags(flags & kLCD_DistanceFieldEffectMask)
    699     , fUsesLocalCoords(usesLocalCoords) {
    700     SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
    701     this->initClassID<GrDistanceFieldLCDTextGeoProc>();
    702     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
    703                                                    kHigh_GrSLPrecision));
    704     fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
    705     fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
    706                                                         kVec2us_GrVertexAttribType,
    707                                                         kHigh_GrSLPrecision));
    708     this->addTextureAccess(&fTextureAccess);
    709 }
    710 
    711 void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
    712                                                         GrProcessorKeyBuilder* b) const {
    713     GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b);
    714 }
    715 
    716 GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
    717     return new GrGLDistanceFieldLCDTextGeoProc();
    718 }
    719 
    720 ///////////////////////////////////////////////////////////////////////////////
    721 
    722 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
    723 
    724 const GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
    725     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    726                                           GrProcessorUnitTest::kAlphaTextureIdx;
    727     static const SkShader::TileMode kTileModes[] = {
    728         SkShader::kClamp_TileMode,
    729         SkShader::kRepeat_TileMode,
    730         SkShader::kMirror_TileMode,
    731     };
    732     SkShader::TileMode tileModes[] = {
    733         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    734         kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    735     };
    736     GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode :
    737                            GrTextureParams::kNone_FilterMode);
    738     DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
    739     uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
    740     flags |= d->fRandom->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0;
    741     flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
    742     return GrDistanceFieldLCDTextGeoProc::Create(GrRandomColor(d->fRandom),
    743                                                  GrTest::TestMatrix(d->fRandom),
    744                                                  d->fTextures[texIdx], params,
    745                                                  wa,
    746                                                  flags,
    747                                                  d->fRandom->nextBool());
    748 }
    749