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