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