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:
GrGLDistanceFieldA8TextGeoProc(const GrGeometryProcessor &,const GrBatchTracker &)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
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)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
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,const GrBatchTracker & bt)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
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)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
GrDistanceFieldA8TextGeoProc(GrColor color,const SkMatrix & viewMatrix,GrTexture * texture,const GrTextureParams & params,float distanceAdjust,uint32_t flags)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
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const219 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*
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const226 GrDistanceFieldA8TextGeoProc::createGLInstance(const GrBatchTracker& bt,
227 const GrGLSLCaps&) const {
228 return SkNEW_ARGS(GrGLDistanceFieldA8TextGeoProc, (*this, bt));
229 }
230
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const231 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
TestCreate(SkRandom * random,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])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:
GrGLDistanceFieldPathGeoProc(const GrGeometryProcessor &,const GrBatchTracker &)281 GrGLDistanceFieldPathGeoProc(const GrGeometryProcessor&,
282 const GrBatchTracker&)
283 : fColor(GrColor_ILLEGAL), fTextureSize(SkISize::Make(-1, -1)) {}
284
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)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
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,const GrBatchTracker & bt)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
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)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
GrDistanceFieldPathGeoProc(GrColor color,const SkMatrix & viewMatrix,GrTexture * texture,const GrTextureParams & params,uint32_t flags)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
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const444 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*
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const451 GrDistanceFieldPathGeoProc::createGLInstance(const GrBatchTracker& bt, const GrGLSLCaps&) const {
452 return SkNEW_ARGS(GrGLDistanceFieldPathGeoProc, (*this, bt));
453 }
454
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const455 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
TestCreate(SkRandom * random,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])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:
GrGLDistanceFieldLCDTextGeoProc(const GrGeometryProcessor &,const GrBatchTracker &)502 GrGLDistanceFieldLCDTextGeoProc(const GrGeometryProcessor&, const GrBatchTracker&)
503 : fColor(GrColor_ILLEGAL) {
504 fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
505 }
506
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)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
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & processor,const GrBatchTracker & bt)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
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)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
GrDistanceFieldLCDTextGeoProc(GrColor color,const SkMatrix & viewMatrix,GrTexture * texture,const GrTextureParams & params,DistanceAdjust distanceAdjust,uint32_t flags)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
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const709 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*
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const716 GrDistanceFieldLCDTextGeoProc::createGLInstance(const GrBatchTracker& bt,
717 const GrGLSLCaps&) const {
718 return SkNEW_ARGS(GrGLDistanceFieldLCDTextGeoProc, (*this, bt));
719 }
720
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const721 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
TestCreate(SkRandom * random,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])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