2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrDistanceFieldGeoProc.h"
9 #include "GrInvariantOutput.h"
10 #include "GrTexture.h"
12 #include "SkDistanceFieldGen.h"
14 #include "glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "glsl/GrGLSLGeometryProcessor.h"
16 #include "glsl/GrGLSLProgramBuilder.h"
17 #include "glsl/GrGLSLProgramDataManager.h"
18 #include "glsl/GrGLSLUtil.h"
19 #include "glsl/GrGLSLVarying.h"
20 #include "glsl/GrGLSLVertexShaderBuilder.h"
22 // Assuming a radius of a little less than the diagonal of the fragment
23 #define SK_DistanceFieldAAFactor "0.65"
25 class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor {
27 GrGLDistanceFieldA8TextGeoProc()
28 : fViewMatrix(SkMatrix::InvalidMatrix())
29 , fColor(GrColor_ILLEGAL)
30 #ifdef SK_GAMMA_APPLY_TO_A8
31 , fDistanceAdjust(-1.0f)
35 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
36 const GrDistanceFieldA8TextGeoProc& dfTexEffect =
37 args.fGP.cast<GrDistanceFieldA8TextGeoProc>();
38 GrGLSLGPBuilder* pb = args.fPB;
39 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
40 SkAssertResult(fragBuilder->enableFeature(
41 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
43 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
44 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
47 varyingHandler->emitAttributes(dfTexEffect);
49 #ifdef SK_GAMMA_APPLY_TO_A8
50 // adjust based on gamma
51 const char* distanceAdjustUniName = nullptr;
52 // width, height, 1/(3*width)
53 fDistanceAdjustUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
54 kFloat_GrSLType, kDefault_GrSLPrecision,
55 "DistanceAdjust", &distanceAdjustUniName);
58 // Setup pass through color
59 if (!dfTexEffect.colorIgnored()) {
60 if (dfTexEffect.hasVertexColor()) {
61 varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
63 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
68 this->setupPosition(pb,
71 dfTexEffect.inPosition()->fName,
72 dfTexEffect.viewMatrix(),
76 this->emitTransforms(pb,
80 dfTexEffect.inPosition()->fName,
85 GrGLSLVertToFrag recipScale(kFloat_GrSLType);
86 GrGLSLVertToFrag st(kVec2f_GrSLType);
87 bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
88 varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
89 vertBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
91 // compute numbers to be hardcoded to convert texture coordinates from int to float
92 SkASSERT(dfTexEffect.numTextures() == 1);
93 GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture();
94 SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
95 SkScalar recipWidth = 1.0f / atlas->width();
96 SkScalar recipHeight = 1.0f / atlas->height();
98 GrGLSLVertToFrag uv(kVec2f_GrSLType);
99 varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
100 vertBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", uv.vsOut(),
101 GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth,
102 GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight,
103 dfTexEffect.inTextureCoords()->fName);
105 // Use highp to work around aliasing issues
106 fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps,
107 kHigh_GrSLPrecision));
108 fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
110 fragBuilder->codeAppend("\tfloat texColor = ");
111 fragBuilder->appendTextureLookup(args.fSamplers[0],
114 fragBuilder->codeAppend(".r;\n");
115 fragBuilder->codeAppend("\tfloat distance = "
116 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
117 #ifdef SK_GAMMA_APPLY_TO_A8
118 // adjust width based on gamma
119 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
122 fragBuilder->codeAppend("float afwidth;");
124 // For uniform scale, we adjust for the effect of the transformation on the distance
125 // by using the length of the gradient of the texture coordinates. We use st coordinates
126 // to ensure we're mapping 1:1 from texel space to pixel space.
128 // this gives us a smooth step across approximately one fragment
129 // we use y to work around a Mali400 bug in the x direction
130 fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));",
133 // For general transforms, to determine the amount of correction we multiply a unit
134 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
135 // (which is the inverse transform for this fragment) and take the length of the result.
136 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
137 // the length of the gradient may be 0, so we need to check for this
138 // this also compensates for the Adreno, which likes to drop tiles on division by 0
139 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
140 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
141 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
142 fragBuilder->codeAppend("} else {");
143 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
144 fragBuilder->codeAppend("}");
146 fragBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn());
147 fragBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn());
148 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
149 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
151 // this gives us a smooth step across approximately one fragment
152 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
154 fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
156 fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
159 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc) override {
160 #ifdef SK_GAMMA_APPLY_TO_A8
161 const GrDistanceFieldA8TextGeoProc& dfTexEffect = proc.cast<GrDistanceFieldA8TextGeoProc>();
162 float distanceAdjust = dfTexEffect.getDistanceAdjust();
163 if (distanceAdjust != fDistanceAdjust) {
164 pdman.set1f(fDistanceAdjustUni, distanceAdjust);
165 fDistanceAdjust = distanceAdjust;
168 const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>();
170 if (!dfa8gp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfa8gp.viewMatrix())) {
171 fViewMatrix = dfa8gp.viewMatrix();
172 float viewMatrix[3 * 3];
173 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
174 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
177 if (dfa8gp.color() != fColor && !dfa8gp.hasVertexColor()) {
179 GrColorToRGBAFloat(dfa8gp.color(), c);
180 pdman.set4fv(fColorUniform, 1, c);
181 fColor = dfa8gp.color();
185 static inline void GenKey(const GrGeometryProcessor& gp,
187 GrProcessorKeyBuilder* b) {
188 const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>();
189 uint32_t key = dfTexEffect.getFlags();
190 key |= dfTexEffect.hasVertexColor() << 16;
191 key |= dfTexEffect.colorIgnored() << 17;
192 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
195 // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
196 SkASSERT(gp.numTextures() == 1);
197 GrTexture* atlas = gp.textureAccess(0).getTexture();
199 b->add32(atlas->width());
200 b->add32(atlas->height());
204 SkMatrix fViewMatrix;
206 UniformHandle fColorUniform;
207 UniformHandle fViewMatrixUniform;
208 #ifdef SK_GAMMA_APPLY_TO_A8
209 float fDistanceAdjust;
210 UniformHandle fDistanceAdjustUni;
213 typedef GrGLSLGeometryProcessor INHERITED;
216 ///////////////////////////////////////////////////////////////////////////////
218 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrColor color,
219 const SkMatrix& viewMatrix,
221 const GrTextureParams& params,
222 #ifdef SK_GAMMA_APPLY_TO_A8
223 float distanceAdjust,
226 bool usesLocalCoords)
228 , fViewMatrix(viewMatrix)
229 , fTextureAccess(texture, params)
230 #ifdef SK_GAMMA_APPLY_TO_A8
231 , fDistanceAdjust(distanceAdjust)
233 , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
235 , fUsesLocalCoords(usesLocalCoords) {
236 SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
237 this->initClassID<GrDistanceFieldA8TextGeoProc>();
238 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
239 kHigh_GrSLPrecision));
240 if (flags & kColorAttr_DistanceFieldEffectFlag) {
241 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
243 fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
244 kVec2s_GrVertexAttribType));
245 this->addTextureAccess(&fTextureAccess);
248 void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
249 GrProcessorKeyBuilder* b) const {
250 GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b);
253 GrGLSLPrimitiveProcessor* GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
254 return new GrGLDistanceFieldA8TextGeoProc();
257 ///////////////////////////////////////////////////////////////////////////////
259 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
261 const GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
262 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
263 GrProcessorUnitTest::kAlphaTextureIdx;
264 static const SkShader::TileMode kTileModes[] = {
265 SkShader::kClamp_TileMode,
266 SkShader::kRepeat_TileMode,
267 SkShader::kMirror_TileMode,
269 SkShader::TileMode tileModes[] = {
270 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
271 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
273 GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode :
274 GrTextureParams::kNone_FilterMode);
276 return GrDistanceFieldA8TextGeoProc::Create(GrRandomColor(d->fRandom),
277 GrTest::TestMatrix(d->fRandom),
278 d->fTextures[texIdx], params,
279 #ifdef SK_GAMMA_APPLY_TO_A8
282 d->fRandom->nextBool() ?
283 kSimilarity_DistanceFieldEffectFlag : 0,
284 d->fRandom->nextBool());
287 ///////////////////////////////////////////////////////////////////////////////
289 class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
291 GrGLDistanceFieldPathGeoProc()
292 : fViewMatrix(SkMatrix::InvalidMatrix())
293 , fColor(GrColor_ILLEGAL)
294 , fTextureSize(SkISize::Make(-1, -1)) {}
296 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
297 const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>();
299 GrGLSLGPBuilder* pb = args.fPB;
300 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
301 SkAssertResult(fragBuilder->enableFeature(
302 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
304 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
305 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
308 varyingHandler->emitAttributes(dfTexEffect);
310 GrGLSLVertToFrag v(kVec2f_GrSLType);
311 varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
313 // setup pass through color
314 if (!dfTexEffect.colorIgnored()) {
315 if (dfTexEffect.hasVertexColor()) {
316 varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
318 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
321 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
324 this->setupPosition(pb,
327 dfTexEffect.inPosition()->fName,
328 dfTexEffect.viewMatrix(),
329 &fViewMatrixUniform);
332 this->emitTransforms(pb,
335 gpArgs->fPositionVar,
336 dfTexEffect.inPosition()->fName,
338 args.fTransformsOut);
340 const char* textureSizeUniName = nullptr;
341 fTextureSizeUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
342 kVec2f_GrSLType, kDefault_GrSLPrecision,
343 "TextureSize", &textureSizeUniName);
345 // Use highp to work around aliasing issues
346 fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps,
347 kHigh_GrSLPrecision));
348 fragBuilder->codeAppendf("vec2 uv = %s;", v.fsIn());
350 fragBuilder->codeAppend("float texColor = ");
351 fragBuilder->appendTextureLookup(args.fSamplers[0],
354 fragBuilder->codeAppend(".r;");
355 fragBuilder->codeAppend("float distance = "
356 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
358 fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps,
359 kHigh_GrSLPrecision));
360 fragBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName);
361 fragBuilder->codeAppend("float afwidth;");
362 if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) {
363 // For uniform scale, we adjust for the effect of the transformation on the distance
364 // by using the length of the gradient of the texture coordinates. We use st coordinates
365 // to ensure we're mapping 1:1 from texel space to pixel space.
367 // this gives us a smooth step across approximately one fragment
368 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));");
370 // For general transforms, to determine the amount of correction we multiply a unit
371 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
372 // (which is the inverse transform for this fragment) and take the length of the result.
373 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
374 // the length of the gradient may be 0, so we need to check for this
375 // this also compensates for the Adreno, which likes to drop tiles on division by 0
376 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
377 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
378 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
379 fragBuilder->codeAppend("} else {");
380 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
381 fragBuilder->codeAppend("}");
383 fragBuilder->codeAppend("vec2 Jdx = dFdx(st);");
384 fragBuilder->codeAppend("vec2 Jdy = dFdy(st);");
385 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
386 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
388 // this gives us a smooth step across approximately one fragment
389 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
391 fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
393 fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
396 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc) override {
397 SkASSERT(fTextureSizeUni.isValid());
399 GrTexture* texture = proc.texture(0);
400 if (texture->width() != fTextureSize.width() ||
401 texture->height() != fTextureSize.height()) {
402 fTextureSize = SkISize::Make(texture->width(), texture->height());
403 pdman.set2f(fTextureSizeUni,
404 SkIntToScalar(fTextureSize.width()),
405 SkIntToScalar(fTextureSize.height()));
408 const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
410 if (!dfpgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfpgp.viewMatrix())) {
411 fViewMatrix = dfpgp.viewMatrix();
412 float viewMatrix[3 * 3];
413 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
414 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
417 if (dfpgp.color() != fColor) {
419 GrColorToRGBAFloat(dfpgp.color(), c);
420 pdman.set4fv(fColorUniform, 1, c);
421 fColor = dfpgp.color();
425 static inline void GenKey(const GrGeometryProcessor& gp,
427 GrProcessorKeyBuilder* b) {
428 const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>();
430 uint32_t key = dfTexEffect.getFlags();
431 key |= dfTexEffect.colorIgnored() << 16;
432 key |= dfTexEffect.hasVertexColor() << 17;
433 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
438 UniformHandle fColorUniform;
439 UniformHandle fTextureSizeUni;
440 UniformHandle fViewMatrixUniform;
441 SkMatrix fViewMatrix;
443 SkISize fTextureSize;
445 typedef GrGLSLGeometryProcessor INHERITED;
448 ///////////////////////////////////////////////////////////////////////////////
450 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
452 const SkMatrix& viewMatrix,
454 const GrTextureParams& params,
456 bool usesLocalCoords)
458 , fViewMatrix(viewMatrix)
459 , fTextureAccess(texture, params)
460 , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
462 , fUsesLocalCoords(usesLocalCoords) {
463 SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
464 this->initClassID<GrDistanceFieldPathGeoProc>();
465 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
466 kHigh_GrSLPrecision));
467 if (flags & kColorAttr_DistanceFieldEffectFlag) {
468 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
470 fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
471 kVec2f_GrVertexAttribType));
472 this->addTextureAccess(&fTextureAccess);
475 void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
476 GrProcessorKeyBuilder* b) const {
477 GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b);
480 GrGLSLPrimitiveProcessor* GrDistanceFieldPathGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
481 return new GrGLDistanceFieldPathGeoProc();
484 ///////////////////////////////////////////////////////////////////////////////
486 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
488 const GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
489 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
490 : GrProcessorUnitTest::kAlphaTextureIdx;
491 static const SkShader::TileMode kTileModes[] = {
492 SkShader::kClamp_TileMode,
493 SkShader::kRepeat_TileMode,
494 SkShader::kMirror_TileMode,
496 SkShader::TileMode tileModes[] = {
497 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
498 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
500 GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode
501 : GrTextureParams::kNone_FilterMode);
503 return GrDistanceFieldPathGeoProc::Create(GrRandomColor(d->fRandom),
504 GrTest::TestMatrix(d->fRandom),
505 d->fTextures[texIdx],
507 d->fRandom->nextBool() ?
508 kSimilarity_DistanceFieldEffectFlag : 0,
509 d->fRandom->nextBool());
512 ///////////////////////////////////////////////////////////////////////////////
514 class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
516 GrGLDistanceFieldLCDTextGeoProc()
517 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {
518 fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
521 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
522 const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
523 args.fGP.cast<GrDistanceFieldLCDTextGeoProc>();
524 GrGLSLGPBuilder* pb = args.fPB;
526 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
527 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
530 varyingHandler->emitAttributes(dfTexEffect);
532 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
534 // setup pass through color
535 if (!dfTexEffect.colorIgnored()) {
536 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
540 this->setupPosition(pb,
543 dfTexEffect.inPosition()->fName,
544 dfTexEffect.viewMatrix(),
545 &fViewMatrixUniform);
548 this->emitTransforms(pb,
551 gpArgs->fPositionVar,
552 dfTexEffect.inPosition()->fName,
554 args.fTransformsOut);
557 bool isUniformScale = SkToBool(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask);
558 GrGLSLVertToFrag recipScale(kFloat_GrSLType);
559 GrGLSLVertToFrag st(kVec2f_GrSLType);
560 varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
561 vertBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
563 // compute numbers to be hardcoded to convert texture coordinates from int to float
564 SkASSERT(dfTexEffect.numTextures() == 1);
565 GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture();
566 SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
567 SkScalar recipWidth = 1.0f / atlas->width();
568 SkScalar recipHeight = 1.0f / atlas->height();
570 GrGLSLVertToFrag uv(kVec2f_GrSLType);
571 varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
572 vertBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", uv.vsOut(),
573 GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth,
574 GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight,
575 dfTexEffect.inTextureCoords()->fName);
577 // add frag shader code
579 SkAssertResult(fragBuilder->enableFeature(
580 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
582 // create LCD offset adjusted by inverse of transform
583 // Use highp to work around aliasing issues
584 fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps,
585 kHigh_GrSLPrecision));
586 fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
587 fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps,
588 kHigh_GrSLPrecision));
590 SkScalar lcdDelta = 1.0f / (3.0f * atlas->width());
591 if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
592 fragBuilder->codeAppendf("float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta);
594 fragBuilder->codeAppendf("float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta);
596 if (isUniformScale) {
597 fragBuilder->codeAppendf("float dy = abs(dFdy(%s.y));", st.fsIn());
598 fragBuilder->codeAppend("vec2 offset = vec2(dy*delta, 0.0);");
600 fragBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn());
602 fragBuilder->codeAppend("vec2 Jdx = dFdx(st);");
603 fragBuilder->codeAppend("vec2 Jdy = dFdy(st);");
604 fragBuilder->codeAppend("vec2 offset = delta*Jdx;");
607 // green is distance to uv center
608 fragBuilder->codeAppend("\tvec4 texColor = ");
609 fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType);
610 fragBuilder->codeAppend(";\n");
611 fragBuilder->codeAppend("\tvec3 distance;\n");
612 fragBuilder->codeAppend("\tdistance.y = texColor.r;\n");
613 // red is distance to left offset
614 fragBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n");
615 fragBuilder->codeAppend("\ttexColor = ");
616 fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
617 fragBuilder->codeAppend(";\n");
618 fragBuilder->codeAppend("\tdistance.x = texColor.r;\n");
619 // blue is distance to right offset
620 fragBuilder->codeAppend("\tuv_adjusted = uv + offset;\n");
621 fragBuilder->codeAppend("\ttexColor = ");
622 fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
623 fragBuilder->codeAppend(";\n");
624 fragBuilder->codeAppend("\tdistance.z = texColor.r;\n");
626 fragBuilder->codeAppend("\tdistance = "
627 "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));");
629 // adjust width based on gamma
630 const char* distanceAdjustUniName = nullptr;
631 fDistanceAdjustUni = pb->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
632 kVec3f_GrSLType, kDefault_GrSLPrecision,
633 "DistanceAdjust", &distanceAdjustUniName);
634 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
636 // To be strictly correct, we should compute the anti-aliasing factor separately
637 // for each color component. However, this is only important when using perspective
638 // transformations, and even then using a single factor seems like a reasonable
639 // trade-off between quality and speed.
640 fragBuilder->codeAppend("float afwidth;");
641 if (isUniformScale) {
642 // For uniform scale, we adjust for the effect of the transformation on the distance
643 // by using the length of the gradient of the texture coordinates. We use st coordinates
644 // to ensure we're mapping 1:1 from texel space to pixel space.
646 // this gives us a smooth step across approximately one fragment
647 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*dy;");
649 // For general transforms, to determine the amount of correction we multiply a unit
650 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
651 // (which is the inverse transform for this fragment) and take the length of the result.
652 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));");
653 // the length of the gradient may be 0, so we need to check for this
654 // this also compensates for the Adreno, which likes to drop tiles on division by 0
655 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
656 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
657 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
658 fragBuilder->codeAppend("} else {");
659 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
660 fragBuilder->codeAppend("}");
661 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
662 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
664 // this gives us a smooth step across approximately one fragment
665 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
668 fragBuilder->codeAppend(
669 "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
670 // set alpha to be max of rgb coverage
671 fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);");
673 fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage);
676 void setData(const GrGLSLProgramDataManager& pdman,
677 const GrPrimitiveProcessor& processor) override {
678 SkASSERT(fDistanceAdjustUni.isValid());
680 const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>();
681 GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust();
682 if (wa != fDistanceAdjust) {
683 pdman.set3f(fDistanceAdjustUni,
687 fDistanceAdjust = wa;
690 if (!dflcd.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dflcd.viewMatrix())) {
691 fViewMatrix = dflcd.viewMatrix();
692 float viewMatrix[3 * 3];
693 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
694 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
697 if (dflcd.color() != fColor) {
699 GrColorToRGBAFloat(dflcd.color(), c);
700 pdman.set4fv(fColorUniform, 1, c);
701 fColor = dflcd.color();
705 static inline void GenKey(const GrGeometryProcessor& gp,
707 GrProcessorKeyBuilder* b) {
708 const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>();
710 uint32_t key = dfTexEffect.getFlags();
711 key |= dfTexEffect.colorIgnored() << 16;
712 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
715 // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
716 SkASSERT(gp.numTextures() == 1);
717 GrTexture* atlas = gp.textureAccess(0).getTexture();
719 b->add32(atlas->width());
720 b->add32(atlas->height());
724 SkMatrix fViewMatrix;
726 UniformHandle fViewMatrixUniform;
727 UniformHandle fColorUniform;
728 GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
729 UniformHandle fDistanceAdjustUni;
731 typedef GrGLSLGeometryProcessor INHERITED;
734 ///////////////////////////////////////////////////////////////////////////////
736 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
737 GrColor color, const SkMatrix& viewMatrix,
738 GrTexture* texture, const GrTextureParams& params,
739 DistanceAdjust distanceAdjust,
740 uint32_t flags, bool usesLocalCoords)
742 , fViewMatrix(viewMatrix)
743 , fTextureAccess(texture, params)
744 , fDistanceAdjust(distanceAdjust)
745 , fFlags(flags & kLCD_DistanceFieldEffectMask)
746 , fUsesLocalCoords(usesLocalCoords) {
747 SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
748 this->initClassID<GrDistanceFieldLCDTextGeoProc>();
749 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
750 kHigh_GrSLPrecision));
751 fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
752 kVec2s_GrVertexAttribType));
753 this->addTextureAccess(&fTextureAccess);
756 void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrGLSLCaps& caps,
757 GrProcessorKeyBuilder* b) const {
758 GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b);
761 GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrGLSLCaps&) const {
762 return new GrGLDistanceFieldLCDTextGeoProc();
765 ///////////////////////////////////////////////////////////////////////////////
767 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
769 const GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
770 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
771 GrProcessorUnitTest::kAlphaTextureIdx;
772 static const SkShader::TileMode kTileModes[] = {
773 SkShader::kClamp_TileMode,
774 SkShader::kRepeat_TileMode,
775 SkShader::kMirror_TileMode,
777 SkShader::TileMode tileModes[] = {
778 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
779 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
781 GrTextureParams params(tileModes, d->fRandom->nextBool() ? GrTextureParams::kBilerp_FilterMode :
782 GrTextureParams::kNone_FilterMode);
783 DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
784 uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
785 flags |= d->fRandom->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0;
786 flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
787 return GrDistanceFieldLCDTextGeoProc::Create(GrRandomColor(d->fRandom),
788 GrTest::TestMatrix(d->fRandom),
789 d->fTextures[texIdx], params,
792 d->fRandom->nextBool());