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