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