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