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