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 "GrAtlasedShaderHelpers.h"
10 #include "GrCaps.h"
11 #include "GrShaderCaps.h"
12 #include "GrTexture.h"
13 #include "SkDistanceFieldGen.h"
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/GrGLSLVertexGeoBuilder.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:
27     GrGLDistanceFieldA8TextGeoProc() = default;
28 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)29     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
30         const GrDistanceFieldA8TextGeoProc& dfTexEffect =
31                 args.fGP.cast<GrDistanceFieldA8TextGeoProc>();
32         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
33 
34         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
35         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
36         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
37 
38         // emit attributes
39         varyingHandler->emitAttributes(dfTexEffect);
40 
41         const char* atlasSizeInvName;
42         fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag,
43                                                           kFloat2_GrSLType,
44                                                           kHigh_GrSLPrecision,
45                                                           "AtlasSizeInv",
46                                                           &atlasSizeInvName);
47 #ifdef SK_GAMMA_APPLY_TO_A8
48         // adjust based on gamma
49         const char* distanceAdjustUniName = nullptr;
50         // width, height, 1/(3*width)
51         fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
52                                                         "DistanceAdjust", &distanceAdjustUniName);
53 #endif
54 
55         // Setup pass through color
56         varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
57 
58         // Setup position
59         gpArgs->fPositionVar = dfTexEffect.inPosition().asShaderVar();
60 
61         // emit transforms
62         this->emitTransforms(vertBuilder,
63                              varyingHandler,
64                              uniformHandler,
65                              dfTexEffect.inPosition().asShaderVar(),
66                              dfTexEffect.localMatrix(),
67                              args.fFPCoordTransformHandler);
68 
69         // add varyings
70         GrGLSLVarying uv(kFloat2_GrSLType);
71         GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType;
72         GrGLSLVarying texIdx(texIdxType);
73         GrGLSLVarying st(kFloat2_GrSLType);
74         append_index_uv_varyings(args, dfTexEffect.inTextureCoords().name(), atlasSizeInvName, &uv,
75                                  &texIdx, &st);
76 
77         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
78                               kUniformScale_DistanceFieldEffectMask;
79         bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
80         bool isGammaCorrect =
81             SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
82         bool isAliased =
83             SkToBool(dfTexEffect.getFlags() & kAliased_DistanceFieldEffectFlag);
84 
85         // Use highp to work around aliasing issues
86         fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
87         fragBuilder->codeAppend("half4 texColor;");
88         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
89                                    texIdx, "uv", "texColor");
90 
91         fragBuilder->codeAppend("half distance = "
92                       SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");");
93 #ifdef SK_GAMMA_APPLY_TO_A8
94         // adjust width based on gamma
95         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
96 #endif
97 
98         fragBuilder->codeAppend("half afwidth;");
99         if (isUniformScale) {
100             // For uniform scale, we adjust for the effect of the transformation on the distance
101             // by using the length of the gradient of the t coordinate in the y direction.
102             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
103 
104             // this gives us a smooth step across approximately one fragment
105 #ifdef SK_VULKAN
106             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));",
107                                      st.fsIn());
108 #else
109             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
110             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));",
111                                      st.fsIn());
112 #endif
113         } else if (isSimilarity) {
114             // For similarity transform, we adjust the effect of the transformation on the distance
115             // by using the length of the gradient of the texture coordinates. We use st coordinates
116             // to ensure we're mapping 1:1 from texel space to pixel space.
117             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
118 
119             // this gives us a smooth step across approximately one fragment
120 #ifdef SK_VULKAN
121             fragBuilder->codeAppendf("half st_grad_len = length(dFdx(%s));", st.fsIn());
122 #else
123             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
124             fragBuilder->codeAppendf("half st_grad_len = length(dFdy(%s));", st.fsIn());
125 #endif
126             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
127         } else {
128             // For general transforms, to determine the amount of correction we multiply a unit
129             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
130             // (which is the inverse transform for this fragment) and take the length of the result.
131             fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), dFdy(distance));");
132             // the length of the gradient may be 0, so we need to check for this
133             // this also compensates for the Adreno, which likes to drop tiles on division by 0
134             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
135             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
136             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
137             fragBuilder->codeAppend("} else {");
138             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
139             fragBuilder->codeAppend("}");
140 
141             fragBuilder->codeAppendf("half2 Jdx = dFdx(%s);", st.fsIn());
142             fragBuilder->codeAppendf("half2 Jdy = dFdy(%s);", st.fsIn());
143             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
144             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
145 
146             // this gives us a smooth step across approximately one fragment
147             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
148         }
149 
150         if (isAliased) {
151             fragBuilder->codeAppend("half val = distance > 0 ? 1.0 : 0.0;");
152         } else if (isGammaCorrect) {
153             // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
154             // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want
155             // distance mapped linearly to coverage, so use a linear step:
156             fragBuilder->codeAppend(
157                 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
158         } else {
159             fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
160         }
161 
162         fragBuilder->codeAppendf("%s = half4(val);", args.fOutputCoverage);
163     }
164 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,FPCoordTransformIter && transformIter)165     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
166                  FPCoordTransformIter&& transformIter) override {
167         const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>();
168 
169 #ifdef SK_GAMMA_APPLY_TO_A8
170         float distanceAdjust = dfa8gp.getDistanceAdjust();
171         if (distanceAdjust != fDistanceAdjust) {
172             fDistanceAdjust = distanceAdjust;
173             pdman.set1f(fDistanceAdjustUni, distanceAdjust);
174         }
175 #endif
176 
177         const SkISize& atlasSize = dfa8gp.atlasSize();
178         SkASSERT(SkIsPow2(atlasSize.fWidth) && SkIsPow2(atlasSize.fHeight));
179 
180         if (fAtlasSize != atlasSize) {
181             pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlasSize.fWidth, 1.0f / atlasSize.fHeight);
182             fAtlasSize = atlasSize;
183         }
184         this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, &transformIter);
185     }
186 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)187     static inline void GenKey(const GrGeometryProcessor& gp,
188                               const GrShaderCaps&,
189                               GrProcessorKeyBuilder* b) {
190         const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>();
191         uint32_t key = dfTexEffect.getFlags();
192         b->add32(key);
193         b->add32(dfTexEffect.numTextureSamplers());
194     }
195 
196 private:
197 #ifdef SK_GAMMA_APPLY_TO_A8
198     float fDistanceAdjust = -1.f;
199     UniformHandle fDistanceAdjustUni;
200 #endif
201     SkISize fAtlasSize = {0, 0};
202     UniformHandle fAtlasSizeInvUniform;
203 
204     typedef GrGLSLGeometryProcessor INHERITED;
205 };
206 
207 ///////////////////////////////////////////////////////////////////////////////
208 
GrDistanceFieldA8TextGeoProc(const GrShaderCaps & caps,const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params,float distanceAdjust,uint32_t flags,const SkMatrix & localMatrix)209 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(const GrShaderCaps& caps,
210                                                            const sk_sp<GrTextureProxy>* proxies,
211                                                            int numProxies,
212                                                            const GrSamplerState& params,
213 #ifdef SK_GAMMA_APPLY_TO_A8
214                                                            float distanceAdjust,
215 #endif
216                                                            uint32_t flags,
217                                                            const SkMatrix& localMatrix)
218         : INHERITED(kGrDistanceFieldA8TextGeoProc_ClassID)
219         , fLocalMatrix(localMatrix)
220         , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
221 #ifdef SK_GAMMA_APPLY_TO_A8
222         , fDistanceAdjust(distanceAdjust)
223 #endif
224 {
225     SkASSERT(numProxies <= kMaxTextures);
226     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
227 
228     if (flags & kPerspective_DistanceFieldEffectFlag) {
229         fInPosition = {"inPosition", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
230     } else {
231         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
232     }
233     fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType };
234     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
235                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
236     this->setVertexAttributes(&fInPosition, 3);
237 
238     if (numProxies) {
239         fAtlasSize = proxies[0]->isize();
240     }
241     for (int i = 0; i < numProxies; ++i) {
242         SkASSERT(proxies[i]);
243         SkASSERT(proxies[i]->isize() == fAtlasSize);
244         fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
245     }
246     this->setTextureSamplerCnt(numProxies);
247 }
248 
addNewProxies(const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params)249 void GrDistanceFieldA8TextGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
250                                                  int numProxies,
251                                                  const GrSamplerState& params) {
252     SkASSERT(numProxies <= kMaxTextures);
253 
254     if (!fTextureSamplers[0].isInitialized()) {
255         fAtlasSize = proxies[0]->isize();
256     }
257 
258     for (int i = 0; i < numProxies; ++i) {
259         SkASSERT(proxies[i]);
260         SkASSERT(proxies[i]->isize() == fAtlasSize);
261         if (!fTextureSamplers[i].isInitialized()) {
262             fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
263         }
264     }
265     this->setTextureSamplerCnt(numProxies);
266 }
267 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const268 void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
269                                                        GrProcessorKeyBuilder* b) const {
270     GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b);
271 }
272 
273 GrGLSLPrimitiveProcessor*
createGLSLInstance(const GrShaderCaps &) const274 GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrShaderCaps&) const {
275     return new GrGLDistanceFieldA8TextGeoProc();
276 }
277 
278 ///////////////////////////////////////////////////////////////////////////////
279 
280 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
281 
282 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)283 sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
284     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
285                                         : GrProcessorUnitTest::kAlphaTextureIdx;
286     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
287         d->textureProxy(texIdx),
288         nullptr,
289         nullptr,
290         nullptr
291     };
292 
293     GrSamplerState::WrapMode wrapModes[2];
294     GrTest::TestWrapModes(d->fRandom, wrapModes);
295     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
296                                                    ? GrSamplerState::Filter::kBilerp
297                                                    : GrSamplerState::Filter::kNearest);
298 
299     uint32_t flags = 0;
300     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
301     if (flags & kSimilarity_DistanceFieldEffectFlag) {
302         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
303     }
304     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
305 #ifdef SK_GAMMA_APPLY_TO_A8
306     float lum = d->fRandom->nextF();
307 #endif
308     return GrDistanceFieldA8TextGeoProc::Make(*d->caps()->shaderCaps(),
309                                               proxies, 1,
310                                               samplerState,
311 #ifdef SK_GAMMA_APPLY_TO_A8
312                                               lum,
313 #endif
314                                               flags, localMatrix);
315 }
316 #endif
317 
318 ///////////////////////////////////////////////////////////////////////////////
319 
320 class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor {
321 public:
GrGLDistanceFieldPathGeoProc()322     GrGLDistanceFieldPathGeoProc()
323             : fMatrix(SkMatrix::InvalidMatrix())
324             , fAtlasSize({0,0}) {
325     }
326 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)327     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
328         const GrDistanceFieldPathGeoProc& dfPathEffect =
329                 args.fGP.cast<GrDistanceFieldPathGeoProc>();
330 
331         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
332 
333         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
334         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
335         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
336 
337         // emit attributes
338         varyingHandler->emitAttributes(dfPathEffect);
339 
340         const char* atlasSizeInvName;
341         fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag,
342                                                           kFloat2_GrSLType,
343                                                           kHigh_GrSLPrecision,
344                                                           "AtlasSizeInv",
345                                                           &atlasSizeInvName);
346 
347         GrGLSLVarying uv(kFloat2_GrSLType);
348         GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType;
349         GrGLSLVarying texIdx(texIdxType);
350         GrGLSLVarying st(kFloat2_GrSLType);
351         append_index_uv_varyings(args, dfPathEffect.inTextureCoords().name(), atlasSizeInvName, &uv,
352                                  &texIdx, &st);
353 
354         // setup pass through color
355         varyingHandler->addPassThroughAttribute(dfPathEffect.inColor(), args.fOutputColor);
356 
357         if (dfPathEffect.matrix().hasPerspective()) {
358             // Setup position
359             this->writeOutputPosition(vertBuilder,
360                                       uniformHandler,
361                                       gpArgs,
362                                       dfPathEffect.inPosition().name(),
363                                       dfPathEffect.matrix(),
364                                       &fMatrixUniform);
365 
366             // emit transforms
367             this->emitTransforms(vertBuilder,
368                                  varyingHandler,
369                                  uniformHandler,
370                                  dfPathEffect.inPosition().asShaderVar(),
371                                  args.fFPCoordTransformHandler);
372         } else {
373             // Setup position
374             this->writeOutputPosition(vertBuilder, gpArgs, dfPathEffect.inPosition().name());
375 
376             // emit transforms
377             this->emitTransforms(vertBuilder,
378                                  varyingHandler,
379                                  uniformHandler,
380                                  dfPathEffect.inPosition().asShaderVar(),
381                                  dfPathEffect.matrix(),
382                                  args.fFPCoordTransformHandler);
383         }
384 
385         // Use highp to work around aliasing issues
386         fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn());
387         fragBuilder->codeAppend("half4 texColor;");
388         append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv",
389                                    "texColor");
390 
391         fragBuilder->codeAppend("half distance = "
392             SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");");
393 
394         fragBuilder->codeAppend("half afwidth;");
395         bool isUniformScale = (dfPathEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
396                               kUniformScale_DistanceFieldEffectMask;
397         bool isSimilarity = SkToBool(dfPathEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
398         bool isGammaCorrect =
399                 SkToBool(dfPathEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
400         if (isUniformScale) {
401             // For uniform scale, we adjust for the effect of the transformation on the distance
402             // by using the length of the gradient of the t coordinate in the y direction.
403             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
404 
405             // this gives us a smooth step across approximately one fragment
406 #ifdef SK_VULKAN
407             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));",
408                                      st.fsIn());
409 #else
410             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
411             fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));",
412                                      st.fsIn());
413 #endif
414         } else if (isSimilarity) {
415             // For similarity transform, we adjust the effect of the transformation on the distance
416             // by using the length of the gradient of the texture coordinates. We use st coordinates
417             // to ensure we're mapping 1:1 from texel space to pixel space.
418 
419             // this gives us a smooth step across approximately one fragment
420 #ifdef SK_VULKAN
421             fragBuilder->codeAppendf("half st_grad_len = length(dFdx(%s));", st.fsIn());
422 #else
423             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
424             fragBuilder->codeAppendf("half st_grad_len = length(dFdy(%s));", st.fsIn());
425 #endif
426             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
427         } else {
428             // For general transforms, to determine the amount of correction we multiply a unit
429             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
430             // (which is the inverse transform for this fragment) and take the length of the result.
431             fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), dFdy(distance));");
432             // the length of the gradient may be 0, so we need to check for this
433             // this also compensates for the Adreno, which likes to drop tiles on division by 0
434             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
435             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
436             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
437             fragBuilder->codeAppend("} else {");
438             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
439             fragBuilder->codeAppend("}");
440 
441             fragBuilder->codeAppendf("half2 Jdx = dFdx(%s);", st.fsIn());
442             fragBuilder->codeAppendf("half2 Jdy = dFdy(%s);", st.fsIn());
443             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
444             fragBuilder->codeAppend("                   dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
445 
446             // this gives us a smooth step across approximately one fragment
447             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
448         }
449         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
450         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
451         // mapped linearly to coverage, so use a linear step:
452         if (isGammaCorrect) {
453             fragBuilder->codeAppend(
454                 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
455         } else {
456             fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
457         }
458 
459         fragBuilder->codeAppendf("%s = half4(val);", args.fOutputCoverage);
460     }
461 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc,FPCoordTransformIter && transformIter)462     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
463                  FPCoordTransformIter&& transformIter) override {
464 
465         const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
466 
467         if (dfpgp.matrix().hasPerspective() && !fMatrix.cheapEqualTo(dfpgp.matrix())) {
468             fMatrix = dfpgp.matrix();
469             float matrix[3 * 3];
470             GrGLSLGetMatrix<3>(matrix, fMatrix);
471             pdman.setMatrix3f(fMatrixUniform, matrix);
472         }
473 
474         const SkISize& atlasSize = dfpgp.atlasSize();
475         SkASSERT(SkIsPow2(atlasSize.fWidth) && SkIsPow2(atlasSize.fHeight));
476         if (fAtlasSize != atlasSize) {
477             pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlasSize.fWidth, 1.0f / atlasSize.fHeight);
478             fAtlasSize = atlasSize;
479         }
480 
481         if (dfpgp.matrix().hasPerspective()) {
482             this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
483         } else {
484             this->setTransformDataHelper(dfpgp.matrix(), pdman, &transformIter);
485         }
486     }
487 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)488     static inline void GenKey(const GrGeometryProcessor& gp,
489                               const GrShaderCaps&,
490                               GrProcessorKeyBuilder* b) {
491         const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>();
492 
493         uint32_t key = dfTexEffect.getFlags();
494         key |= ComputePosKey(dfTexEffect.matrix()) << 16;
495         b->add32(key);
496         b->add32(dfTexEffect.matrix().hasPerspective());
497         b->add32(dfTexEffect.numTextureSamplers());
498     }
499 
500 private:
501     SkMatrix      fMatrix;        // view matrix if perspective, local matrix otherwise
502     UniformHandle fMatrixUniform;
503 
504     SkISize       fAtlasSize;
505     UniformHandle fAtlasSizeInvUniform;
506 
507     typedef GrGLSLGeometryProcessor INHERITED;
508 };
509 
510 ///////////////////////////////////////////////////////////////////////////////
511 
GrDistanceFieldPathGeoProc(const GrShaderCaps & caps,const SkMatrix & matrix,bool wideColor,const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params,uint32_t flags)512 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(const GrShaderCaps& caps,
513                                                        const SkMatrix& matrix,
514                                                        bool wideColor,
515                                                        const sk_sp<GrTextureProxy>* proxies,
516                                                        int numProxies,
517                                                        const GrSamplerState& params,
518                                                        uint32_t flags)
519         : INHERITED(kGrDistanceFieldPathGeoProc_ClassID)
520         , fMatrix(matrix)
521         , fFlags(flags & kNonLCD_DistanceFieldEffectMask) {
522     SkASSERT(numProxies <= kMaxTextures);
523     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
524 
525     fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
526     fInColor = MakeColorAttribute("inColor", wideColor);
527     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
528                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
529     this->setVertexAttributes(&fInPosition, 3);
530 
531     if (numProxies) {
532         fAtlasSize = proxies[0]->isize();
533     }
534 
535     for (int i = 0; i < numProxies; ++i) {
536         SkASSERT(proxies[i]);
537         SkASSERT(proxies[i]->isize() == fAtlasSize);
538         fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
539     }
540     this->setTextureSamplerCnt(numProxies);
541 }
542 
addNewProxies(const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params)543 void GrDistanceFieldPathGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
544                                                int numProxies,
545                                                const GrSamplerState& params) {
546     SkASSERT(numProxies <= kMaxTextures);
547 
548     if (!fTextureSamplers[0].isInitialized()) {
549         fAtlasSize = proxies[0]->isize();
550     }
551 
552     for (int i = 0; i < numProxies; ++i) {
553         SkASSERT(proxies[i]);
554         SkASSERT(proxies[i]->isize() == fAtlasSize);
555 
556         if (!fTextureSamplers[i].isInitialized()) {
557             fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
558         }
559     }
560     this->setTextureSamplerCnt(numProxies);
561 }
562 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const563 void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
564                                                      GrProcessorKeyBuilder* b) const {
565     GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b);
566 }
567 
568 GrGLSLPrimitiveProcessor*
createGLSLInstance(const GrShaderCaps &) const569 GrDistanceFieldPathGeoProc::createGLSLInstance(const GrShaderCaps&) const {
570     return new GrGLDistanceFieldPathGeoProc();
571 }
572 
573 ///////////////////////////////////////////////////////////////////////////////
574 
575 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
576 
577 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)578 sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
579     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
580                                         : GrProcessorUnitTest::kAlphaTextureIdx;
581     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
582         d->textureProxy(texIdx),
583         nullptr,
584         nullptr,
585         nullptr
586     };
587 
588     GrSamplerState::WrapMode wrapModes[2];
589     GrTest::TestWrapModes(d->fRandom, wrapModes);
590     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
591                                                    ? GrSamplerState::Filter::kBilerp
592                                                    : GrSamplerState::Filter::kNearest);
593 
594     uint32_t flags = 0;
595     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
596     if (flags & kSimilarity_DistanceFieldEffectFlag) {
597         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
598     }
599 
600     return GrDistanceFieldPathGeoProc::Make(*d->caps()->shaderCaps(),
601                                             GrTest::TestMatrix(d->fRandom),
602                                             d->fRandom->nextBool(),
603                                             proxies, 1,
604                                             samplerState,
605                                             flags);
606 }
607 #endif
608 
609 ///////////////////////////////////////////////////////////////////////////////
610 
611 class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
612 public:
GrGLDistanceFieldLCDTextGeoProc()613     GrGLDistanceFieldLCDTextGeoProc() : fAtlasSize({0, 0}) {
614         fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
615     }
616 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)617     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
618         const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
619                 args.fGP.cast<GrDistanceFieldLCDTextGeoProc>();
620 
621         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
622         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
623         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
624 
625         // emit attributes
626         varyingHandler->emitAttributes(dfTexEffect);
627 
628         const char* atlasSizeInvName;
629         fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag,
630                                                           kFloat2_GrSLType,
631                                                           kHigh_GrSLPrecision,
632                                                           "AtlasSizeInv",
633                                                           &atlasSizeInvName);
634 
635         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
636 
637         // setup pass through color
638         varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
639 
640         // Setup position
641         gpArgs->fPositionVar = dfTexEffect.inPosition().asShaderVar();
642 
643         // emit transforms
644         this->emitTransforms(vertBuilder,
645                              varyingHandler,
646                              uniformHandler,
647                              dfTexEffect.inPosition().asShaderVar(),
648                              dfTexEffect.localMatrix(),
649                              args.fFPCoordTransformHandler);
650 
651         // set up varyings
652         GrGLSLVarying uv(kFloat2_GrSLType);
653         GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType;
654         GrGLSLVarying texIdx(texIdxType);
655         GrGLSLVarying st(kFloat2_GrSLType);
656         append_index_uv_varyings(args, dfTexEffect.inTextureCoords().name(), atlasSizeInvName, &uv,
657                                  &texIdx, &st);
658 
659         GrGLSLVarying delta(kFloat_GrSLType);
660         varyingHandler->addVarying("Delta", &delta);
661         if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
662             vertBuilder->codeAppendf("%s = -%s.x/3.0;", delta.vsOut(), atlasSizeInvName);
663         } else {
664             vertBuilder->codeAppendf("%s = %s.x/3.0;", delta.vsOut(), atlasSizeInvName);
665         }
666 
667         // add frag shader code
668         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
669                               kUniformScale_DistanceFieldEffectMask;
670         bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
671         bool isGammaCorrect =
672             SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
673 
674         // create LCD offset adjusted by inverse of transform
675         // Use highp to work around aliasing issues
676         fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
677 
678         if (isUniformScale) {
679 #ifdef SK_VULKAN
680             fragBuilder->codeAppendf("half st_grad_len = abs(dFdx(%s.x));", st.fsIn());
681 #else
682             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
683             fragBuilder->codeAppendf("half st_grad_len = abs(dFdy(%s.y));", st.fsIn());
684 #endif
685             fragBuilder->codeAppendf("half2 offset = half2(st_grad_len*%s, 0.0);", delta.fsIn());
686         } else if (isSimilarity) {
687             // For a similarity matrix with rotation, the gradient will not be aligned
688             // with the texel coordinate axes, so we need to calculate it.
689 #ifdef SK_VULKAN
690             fragBuilder->codeAppendf("half2 st_grad = dFdx(%s);", st.fsIn());
691             fragBuilder->codeAppendf("half2 offset = %s*st_grad;", delta.fsIn());
692 #else
693             // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to
694             // get the gradient in the x direction.
695             fragBuilder->codeAppendf("half2 st_grad = dFdy(%s);", st.fsIn());
696             fragBuilder->codeAppendf("half2 offset = %s*half2(st_grad.y, -st_grad.x);",
697                                      delta.fsIn());
698 #endif
699             fragBuilder->codeAppend("half st_grad_len = length(st_grad);");
700         } else {
701             fragBuilder->codeAppendf("half2 st = %s;\n", st.fsIn());
702 
703             fragBuilder->codeAppend("half2 Jdx = dFdx(st);");
704             fragBuilder->codeAppend("half2 Jdy = dFdy(st);");
705             fragBuilder->codeAppendf("half2 offset = %s*Jdx;", delta.fsIn());
706         }
707 
708         // sample the texture by index
709         fragBuilder->codeAppend("half4 texColor;");
710         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
711                                    texIdx, "uv", "texColor");
712 
713         // green is distance to uv center
714         fragBuilder->codeAppend("half3 distance;");
715         fragBuilder->codeAppend("distance.y = texColor.r;");
716         // red is distance to left offset
717         fragBuilder->codeAppend("half2 uv_adjusted = uv - offset;");
718         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
719                                    texIdx, "uv_adjusted", "texColor");
720         fragBuilder->codeAppend("distance.x = texColor.r;");
721         // blue is distance to right offset
722         fragBuilder->codeAppend("uv_adjusted = uv + offset;");
723         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
724                                    texIdx, "uv_adjusted", "texColor");
725         fragBuilder->codeAppend("distance.z = texColor.r;");
726 
727         fragBuilder->codeAppend("distance = "
728            "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));");
729 
730         // adjust width based on gamma
731         const char* distanceAdjustUniName = nullptr;
732         fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
733                                                         "DistanceAdjust", &distanceAdjustUniName);
734         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
735 
736         // To be strictly correct, we should compute the anti-aliasing factor separately
737         // for each color component. However, this is only important when using perspective
738         // transformations, and even then using a single factor seems like a reasonable
739         // trade-off between quality and speed.
740         fragBuilder->codeAppend("half afwidth;");
741         if (isSimilarity) {
742             // For similarity transform (uniform scale-only is a subset of this), we adjust for the
743             // effect of the transformation on the distance by using the length of the gradient of
744             // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel
745             // space to pixel space.
746 
747             // this gives us a smooth step across approximately one fragment
748             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;");
749         } else {
750             // For general transforms, to determine the amount of correction we multiply a unit
751             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
752             // (which is the inverse transform for this fragment) and take the length of the result.
753             fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance.r), dFdy(distance.r));");
754             // the length of the gradient may be 0, so we need to check for this
755             // this also compensates for the Adreno, which likes to drop tiles on division by 0
756             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
757             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
758             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
759             fragBuilder->codeAppend("} else {");
760             fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
761             fragBuilder->codeAppend("}");
762             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
763             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
764 
765             // this gives us a smooth step across approximately one fragment
766             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
767         }
768 
769         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
770         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
771         // mapped linearly to coverage, so use a linear step:
772         if (isGammaCorrect) {
773             fragBuilder->codeAppendf("%s = "
774                 "half4(saturate((distance + half3(afwidth)) / half3(2.0 * afwidth)), 1.0);",
775                 args.fOutputCoverage);
776         } else {
777             fragBuilder->codeAppendf(
778                 "%s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);",
779                 args.fOutputCoverage);
780         }
781     }
782 
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & processor,FPCoordTransformIter && transformIter)783     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor,
784                  FPCoordTransformIter&& transformIter) override {
785         SkASSERT(fDistanceAdjustUni.isValid());
786 
787         const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>();
788         GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust();
789         if (wa != fDistanceAdjust) {
790             pdman.set3f(fDistanceAdjustUni,
791                         wa.fR,
792                         wa.fG,
793                         wa.fB);
794             fDistanceAdjust = wa;
795         }
796 
797         const SkISize& atlasSize = dflcd.atlasSize();
798         SkASSERT(SkIsPow2(atlasSize.fWidth) && SkIsPow2(atlasSize.fHeight));
799         if (fAtlasSize != atlasSize) {
800             pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlasSize.fWidth, 1.0f / atlasSize.fHeight);
801             fAtlasSize = atlasSize;
802         }
803         this->setTransformDataHelper(dflcd.localMatrix(), pdman, &transformIter);
804     }
805 
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)806     static inline void GenKey(const GrGeometryProcessor& gp,
807                               const GrShaderCaps&,
808                               GrProcessorKeyBuilder* b) {
809         const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>();
810 
811         uint32_t key = dfTexEffect.getFlags();
812         b->add32(key);
813         b->add32(dfTexEffect.numTextureSamplers());
814     }
815 
816 private:
817     GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
818     UniformHandle                                 fDistanceAdjustUni;
819 
820     SkISize                                       fAtlasSize;
821     UniformHandle                                 fAtlasSizeInvUniform;
822 
823     typedef GrGLSLGeometryProcessor INHERITED;
824 };
825 
826 ///////////////////////////////////////////////////////////////////////////////
827 
GrDistanceFieldLCDTextGeoProc(const GrShaderCaps & caps,const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params,DistanceAdjust distanceAdjust,uint32_t flags,const SkMatrix & localMatrix)828 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(const GrShaderCaps& caps,
829                                                              const sk_sp<GrTextureProxy>* proxies,
830                                                              int numProxies,
831                                                              const GrSamplerState& params,
832                                                              DistanceAdjust distanceAdjust,
833                                                              uint32_t flags,
834                                                              const SkMatrix& localMatrix)
835         : INHERITED(kGrDistanceFieldLCDTextGeoProc_ClassID)
836         , fLocalMatrix(localMatrix)
837         , fDistanceAdjust(distanceAdjust)
838         , fFlags(flags & kLCD_DistanceFieldEffectMask) {
839     SkASSERT(numProxies <= kMaxTextures);
840     SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
841 
842     if (fFlags & kPerspective_DistanceFieldEffectFlag) {
843         fInPosition = {"inPosition", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
844     } else {
845         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
846     }
847     fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
848     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
849                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
850     this->setVertexAttributes(&fInPosition, 3);
851 
852     if (numProxies) {
853         fAtlasSize = proxies[0]->isize();
854     }
855 
856     for (int i = 0; i < numProxies; ++i) {
857         SkASSERT(proxies[i]);
858         SkASSERT(proxies[i]->isize() == fAtlasSize);
859         fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
860     }
861     this->setTextureSamplerCnt(numProxies);
862 }
863 
addNewProxies(const sk_sp<GrTextureProxy> * proxies,int numProxies,const GrSamplerState & params)864 void GrDistanceFieldLCDTextGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
865                                                   int numProxies,
866                                                   const GrSamplerState& params) {
867     SkASSERT(numProxies <= kMaxTextures);
868 
869     if (!fTextureSamplers[0].isInitialized()) {
870         fAtlasSize = proxies[0]->isize();
871     }
872 
873     for (int i = 0; i < numProxies; ++i) {
874         SkASSERT(proxies[i]);
875         SkASSERT(proxies[i]->isize() == fAtlasSize);
876 
877         if (!fTextureSamplers[i].isInitialized()) {
878             fTextureSamplers[i].reset(proxies[i]->textureType(), proxies[i]->config(), params);
879         }
880     }
881     this->setTextureSamplerCnt(numProxies);
882 }
883 
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const884 void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
885                                                         GrProcessorKeyBuilder* b) const {
886     GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b);
887 }
888 
createGLSLInstance(const GrShaderCaps &) const889 GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrShaderCaps&) const {
890     return new GrGLDistanceFieldLCDTextGeoProc();
891 }
892 
893 ///////////////////////////////////////////////////////////////////////////////
894 
895 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
896 
897 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)898 sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
899     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
900                                           GrProcessorUnitTest::kAlphaTextureIdx;
901     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
902         d->textureProxy(texIdx),
903         nullptr,
904         nullptr,
905         nullptr
906     };
907 
908     GrSamplerState::WrapMode wrapModes[2];
909     GrTest::TestWrapModes(d->fRandom, wrapModes);
910     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
911                                                    ? GrSamplerState::Filter::kBilerp
912                                                    : GrSamplerState::Filter::kNearest);
913     DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
914     uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
915     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
916     if (flags & kSimilarity_DistanceFieldEffectFlag) {
917         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
918     }
919     flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
920     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
921     return GrDistanceFieldLCDTextGeoProc::Make(*d->caps()->shaderCaps(), proxies, 1, samplerState,
922                                                wa, flags, localMatrix);
923 }
924 #endif
925