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