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