1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/gpu/effects/GrTextureEffect.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h"
11cb93a386Sopenharmony_ci#include "src/gpu/GrTexture.h"
12cb93a386Sopenharmony_ci#include "src/gpu/effects/GrMatrixEffect.h"
13cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
14cb93a386Sopenharmony_ci#include "src/sksl/SkSLUtil.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ciusing Wrap = GrSamplerState::WrapMode;
17cb93a386Sopenharmony_ciusing Filter = GrSamplerState::Filter;
18cb93a386Sopenharmony_ciusing MipmapMode = GrSamplerState::MipmapMode;
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_cistruct GrTextureEffect::Sampling {
21cb93a386Sopenharmony_ci    GrSamplerState fHWSampler;
22cb93a386Sopenharmony_ci    ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
23cb93a386Sopenharmony_ci    SkRect fShaderSubset = {0, 0, 0, 0};
24cb93a386Sopenharmony_ci    SkRect fShaderClamp  = {0, 0, 0, 0};
25cb93a386Sopenharmony_ci    float fBorder[4] = {0, 0, 0, 0};
26cb93a386Sopenharmony_ci    Sampling(Filter filter, MipmapMode mm) : fHWSampler(filter, mm) {}
27cb93a386Sopenharmony_ci    Sampling(const GrSurfaceProxy& proxy,
28cb93a386Sopenharmony_ci             GrSamplerState wrap,
29cb93a386Sopenharmony_ci             const SkRect&,
30cb93a386Sopenharmony_ci             const SkRect*,
31cb93a386Sopenharmony_ci             const float border[4],
32cb93a386Sopenharmony_ci             bool alwaysUseShaderTileMode,
33cb93a386Sopenharmony_ci             const GrCaps&,
34cb93a386Sopenharmony_ci             SkVector linearFilterInset = {0.5f, 0.5f});
35cb93a386Sopenharmony_ci    inline bool hasBorderAlpha() const;
36cb93a386Sopenharmony_ci};
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ciGrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
39cb93a386Sopenharmony_ci                                    GrSamplerState sampler,
40cb93a386Sopenharmony_ci                                    const SkRect& subset,
41cb93a386Sopenharmony_ci                                    const SkRect* domain,
42cb93a386Sopenharmony_ci                                    const float border[4],
43cb93a386Sopenharmony_ci                                    bool alwaysUseShaderTileMode,
44cb93a386Sopenharmony_ci                                    const GrCaps& caps,
45cb93a386Sopenharmony_ci                                    SkVector linearFilterInset) {
46cb93a386Sopenharmony_ci    struct Span {
47cb93a386Sopenharmony_ci        float fA = 0.f, fB = 0.f;
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci        Span makeInset(float o) const {
50cb93a386Sopenharmony_ci            Span r = {fA + o, fB - o};
51cb93a386Sopenharmony_ci            if (r.fA > r.fB) {
52cb93a386Sopenharmony_ci                r.fA = r.fB = (r.fA + r.fB) / 2;
53cb93a386Sopenharmony_ci            }
54cb93a386Sopenharmony_ci            return r;
55cb93a386Sopenharmony_ci        }
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci        bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
58cb93a386Sopenharmony_ci    };
59cb93a386Sopenharmony_ci    struct Result1D {
60cb93a386Sopenharmony_ci        ShaderMode fShaderMode;
61cb93a386Sopenharmony_ci        Span fShaderSubset;
62cb93a386Sopenharmony_ci        Span fShaderClamp;
63cb93a386Sopenharmony_ci        Wrap fHWWrap;
64cb93a386Sopenharmony_ci    };
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    auto type = proxy.asTextureProxy()->textureType();
67cb93a386Sopenharmony_ci    auto filter = sampler.filter();
68cb93a386Sopenharmony_ci    auto mm = sampler.mipmapMode();
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    auto resolve = [&](int size, Wrap wrap, Span subset, Span domain, float linearFilterInset) {
71cb93a386Sopenharmony_ci        Result1D r;
72cb93a386Sopenharmony_ci        bool canDoModeInHW = !alwaysUseShaderTileMode;
73cb93a386Sopenharmony_ci        // TODO: Use HW border color when available.
74cb93a386Sopenharmony_ci        if (wrap == Wrap::kClampToBorder &&
75cb93a386Sopenharmony_ci            (!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
76cb93a386Sopenharmony_ci            canDoModeInHW = false;
77cb93a386Sopenharmony_ci        } else if (wrap != Wrap::kClamp && !caps.npotTextureTileSupport() && !SkIsPow2(size)) {
78cb93a386Sopenharmony_ci            canDoModeInHW = false;
79cb93a386Sopenharmony_ci        } else if (type != GrTextureType::k2D &&
80cb93a386Sopenharmony_ci                   !(wrap == Wrap::kClamp || wrap == Wrap::kClampToBorder)) {
81cb93a386Sopenharmony_ci            canDoModeInHW = false;
82cb93a386Sopenharmony_ci        }
83cb93a386Sopenharmony_ci        if (canDoModeInHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
84cb93a386Sopenharmony_ci            r.fShaderMode = ShaderMode::kNone;
85cb93a386Sopenharmony_ci            r.fHWWrap = wrap;
86cb93a386Sopenharmony_ci            r.fShaderSubset = r.fShaderClamp = {0, 0};
87cb93a386Sopenharmony_ci            return r;
88cb93a386Sopenharmony_ci        }
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci        r.fShaderSubset = subset;
91cb93a386Sopenharmony_ci        bool domainIsSafe = false;
92cb93a386Sopenharmony_ci        if (filter == Filter::kNearest) {
93cb93a386Sopenharmony_ci            Span isubset{sk_float_floor(subset.fA), sk_float_ceil(subset.fB)};
94cb93a386Sopenharmony_ci            if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
95cb93a386Sopenharmony_ci                domainIsSafe = true;
96cb93a386Sopenharmony_ci            }
97cb93a386Sopenharmony_ci            // This inset prevents sampling neighboring texels that could occur when
98cb93a386Sopenharmony_ci            // texture coords fall exactly at texel boundaries (depending on precision
99cb93a386Sopenharmony_ci            // and GPU-specific snapping at the boundary).
100cb93a386Sopenharmony_ci            r.fShaderClamp = isubset.makeInset(0.5f);
101cb93a386Sopenharmony_ci        } else {
102cb93a386Sopenharmony_ci            r.fShaderClamp = subset.makeInset(linearFilterInset);
103cb93a386Sopenharmony_ci            if (r.fShaderClamp.contains(domain)) {
104cb93a386Sopenharmony_ci                domainIsSafe = true;
105cb93a386Sopenharmony_ci            }
106cb93a386Sopenharmony_ci        }
107cb93a386Sopenharmony_ci        if (!alwaysUseShaderTileMode && domainIsSafe) {
108cb93a386Sopenharmony_ci            // The domain of coords that will be used won't access texels outside of the subset.
109cb93a386Sopenharmony_ci            // So the wrap mode effectively doesn't matter. We use kClamp since it is always
110cb93a386Sopenharmony_ci            // supported.
111cb93a386Sopenharmony_ci            r.fShaderMode = ShaderMode::kNone;
112cb93a386Sopenharmony_ci            r.fHWWrap = Wrap::kClamp;
113cb93a386Sopenharmony_ci            r.fShaderSubset = r.fShaderClamp = {0, 0};
114cb93a386Sopenharmony_ci            return r;
115cb93a386Sopenharmony_ci        }
116cb93a386Sopenharmony_ci        r.fShaderMode = GetShaderMode(wrap, filter, mm);
117cb93a386Sopenharmony_ci        r.fHWWrap = Wrap::kClamp;
118cb93a386Sopenharmony_ci        return r;
119cb93a386Sopenharmony_ci    };
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_ci    SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    Span subsetX{subset.fLeft, subset.fRight};
124cb93a386Sopenharmony_ci    auto domainX = domain ? Span{domain->fLeft, domain->fRight}
125cb93a386Sopenharmony_ci                          : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
126cb93a386Sopenharmony_ci    auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX, linearFilterInset.fX);
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci    Span subsetY{subset.fTop, subset.fBottom};
129cb93a386Sopenharmony_ci    auto domainY = domain ? Span{domain->fTop, domain->fBottom}
130cb93a386Sopenharmony_ci                          : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
131cb93a386Sopenharmony_ci    auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY, linearFilterInset.fY);
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci    fHWSampler = {x.fHWWrap, y.fHWWrap, filter, mm};
134cb93a386Sopenharmony_ci    fShaderModes[0] = x.fShaderMode;
135cb93a386Sopenharmony_ci    fShaderModes[1] = y.fShaderMode;
136cb93a386Sopenharmony_ci    fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
137cb93a386Sopenharmony_ci                     x.fShaderSubset.fB, y.fShaderSubset.fB};
138cb93a386Sopenharmony_ci    fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
139cb93a386Sopenharmony_ci                    x.fShaderClamp.fB, y.fShaderClamp.fB};
140cb93a386Sopenharmony_ci    std::copy_n(border, 4, fBorder);
141cb93a386Sopenharmony_ci}
142cb93a386Sopenharmony_ci
143cb93a386Sopenharmony_cibool GrTextureEffect::Sampling::hasBorderAlpha() const {
144cb93a386Sopenharmony_ci    if (fHWSampler.wrapModeX() == Wrap::kClampToBorder ||
145cb93a386Sopenharmony_ci        fHWSampler.wrapModeY() == Wrap::kClampToBorder) {
146cb93a386Sopenharmony_ci        return true;
147cb93a386Sopenharmony_ci    }
148cb93a386Sopenharmony_ci    if (ShaderModeIsClampToBorder(fShaderModes[0]) || ShaderModeIsClampToBorder(fShaderModes[1])) {
149cb93a386Sopenharmony_ci        return fBorder[3] < 1.f;
150cb93a386Sopenharmony_ci    }
151cb93a386Sopenharmony_ci    return false;
152cb93a386Sopenharmony_ci}
153cb93a386Sopenharmony_ci
154cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
155cb93a386Sopenharmony_ci                                                           SkAlphaType alphaType,
156cb93a386Sopenharmony_ci                                                           const SkMatrix& matrix,
157cb93a386Sopenharmony_ci                                                           Filter filter,
158cb93a386Sopenharmony_ci                                                           MipmapMode mm) {
159cb93a386Sopenharmony_ci    Sampling sampling = Sampling(filter, mm);
160cb93a386Sopenharmony_ci    std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
161cb93a386Sopenharmony_ci                                                                alphaType,
162cb93a386Sopenharmony_ci                                                                sampling));
163cb93a386Sopenharmony_ci    return GrMatrixEffect::Make(matrix, std::move(te));
164cb93a386Sopenharmony_ci}
165cb93a386Sopenharmony_ci
166cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
167cb93a386Sopenharmony_ci                                                           SkAlphaType alphaType,
168cb93a386Sopenharmony_ci                                                           const SkMatrix& matrix,
169cb93a386Sopenharmony_ci                                                           GrSamplerState sampler,
170cb93a386Sopenharmony_ci                                                           const GrCaps& caps,
171cb93a386Sopenharmony_ci                                                           const float border[4]) {
172cb93a386Sopenharmony_ci    Sampling sampling(*view.proxy(),
173cb93a386Sopenharmony_ci                      sampler,
174cb93a386Sopenharmony_ci                      SkRect::Make(view.proxy()->dimensions()),
175cb93a386Sopenharmony_ci                      nullptr,
176cb93a386Sopenharmony_ci                      border,
177cb93a386Sopenharmony_ci                      false,
178cb93a386Sopenharmony_ci                      caps);
179cb93a386Sopenharmony_ci    std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
180cb93a386Sopenharmony_ci                                                                alphaType,
181cb93a386Sopenharmony_ci                                                                sampling));
182cb93a386Sopenharmony_ci    return GrMatrixEffect::Make(matrix, std::move(te));
183cb93a386Sopenharmony_ci}
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
186cb93a386Sopenharmony_ci                                                                 SkAlphaType alphaType,
187cb93a386Sopenharmony_ci                                                                 const SkMatrix& matrix,
188cb93a386Sopenharmony_ci                                                                 GrSamplerState sampler,
189cb93a386Sopenharmony_ci                                                                 const SkRect& subset,
190cb93a386Sopenharmony_ci                                                                 const GrCaps& caps,
191cb93a386Sopenharmony_ci                                                                 const float border[4],
192cb93a386Sopenharmony_ci                                                                 bool alwaysUseShaderTileMode) {
193cb93a386Sopenharmony_ci    Sampling sampling(*view.proxy(),
194cb93a386Sopenharmony_ci                      sampler,
195cb93a386Sopenharmony_ci                      subset,
196cb93a386Sopenharmony_ci                      nullptr,
197cb93a386Sopenharmony_ci                      border,
198cb93a386Sopenharmony_ci                      alwaysUseShaderTileMode,
199cb93a386Sopenharmony_ci                      caps);
200cb93a386Sopenharmony_ci    std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
201cb93a386Sopenharmony_ci                                                                alphaType,
202cb93a386Sopenharmony_ci                                                                sampling));
203cb93a386Sopenharmony_ci    return GrMatrixEffect::Make(matrix, std::move(te));
204cb93a386Sopenharmony_ci}
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
207cb93a386Sopenharmony_ci                                                                 SkAlphaType alphaType,
208cb93a386Sopenharmony_ci                                                                 const SkMatrix& matrix,
209cb93a386Sopenharmony_ci                                                                 GrSamplerState sampler,
210cb93a386Sopenharmony_ci                                                                 const SkRect& subset,
211cb93a386Sopenharmony_ci                                                                 const SkRect& domain,
212cb93a386Sopenharmony_ci                                                                 const GrCaps& caps,
213cb93a386Sopenharmony_ci                                                                 const float border[4]) {
214cb93a386Sopenharmony_ci    Sampling sampling(*view.proxy(), sampler, subset, &domain, border, false, caps);
215cb93a386Sopenharmony_ci    std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
216cb93a386Sopenharmony_ci                                                                alphaType,
217cb93a386Sopenharmony_ci                                                                sampling));
218cb93a386Sopenharmony_ci    return GrMatrixEffect::Make(matrix, std::move(te));
219cb93a386Sopenharmony_ci}
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeCustomLinearFilterInset(
222cb93a386Sopenharmony_ci        GrSurfaceProxyView view,
223cb93a386Sopenharmony_ci        SkAlphaType alphaType,
224cb93a386Sopenharmony_ci        const SkMatrix& matrix,
225cb93a386Sopenharmony_ci        Wrap wx,
226cb93a386Sopenharmony_ci        Wrap wy,
227cb93a386Sopenharmony_ci        const SkRect& subset,
228cb93a386Sopenharmony_ci        const SkRect* domain,
229cb93a386Sopenharmony_ci        SkVector inset,
230cb93a386Sopenharmony_ci        const GrCaps& caps,
231cb93a386Sopenharmony_ci        const float border[4]) {
232cb93a386Sopenharmony_ci    GrSamplerState sampler(wx, wy, Filter::kLinear);
233cb93a386Sopenharmony_ci    Sampling sampling(*view.proxy(), sampler, subset, domain, border, false, caps, inset);
234cb93a386Sopenharmony_ci    std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
235cb93a386Sopenharmony_ci                                                                alphaType,
236cb93a386Sopenharmony_ci                                                                sampling));
237cb93a386Sopenharmony_ci    return GrMatrixEffect::Make(matrix, std::move(te));
238cb93a386Sopenharmony_ci}
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ciSkMatrix GrTextureEffect::coordAdjustmentMatrix() const {
241cb93a386Sopenharmony_ci    SkMatrix m;
242cb93a386Sopenharmony_ci    GrTexture* texture = this->texture();
243cb93a386Sopenharmony_ci    SkISize d = texture->dimensions();
244cb93a386Sopenharmony_ci    if (this->matrixEffectShouldNormalize()) {
245cb93a386Sopenharmony_ci        if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
246cb93a386Sopenharmony_ci            m.setScaleTranslate(1.f / d.width(), -1.f / d.height(), 0, 1);
247cb93a386Sopenharmony_ci        } else {
248cb93a386Sopenharmony_ci            m.setScale(1.f / d.width(), 1.f / d.height());
249cb93a386Sopenharmony_ci        }
250cb93a386Sopenharmony_ci    } else {
251cb93a386Sopenharmony_ci        if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
252cb93a386Sopenharmony_ci            m.setScaleTranslate(1.f, -1.f, 0, d.height());
253cb93a386Sopenharmony_ci        }
254cb93a386Sopenharmony_ci    }
255cb93a386Sopenharmony_ci    return m;
256cb93a386Sopenharmony_ci}
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ciGrTextureEffect::ShaderMode GrTextureEffect::GetShaderMode(Wrap wrap,
259cb93a386Sopenharmony_ci                                                           Filter filter,
260cb93a386Sopenharmony_ci                                                           MipmapMode mm) {
261cb93a386Sopenharmony_ci    switch (wrap) {
262cb93a386Sopenharmony_ci        case Wrap::kMirrorRepeat:
263cb93a386Sopenharmony_ci            return ShaderMode::kMirrorRepeat;
264cb93a386Sopenharmony_ci        case Wrap::kClamp:
265cb93a386Sopenharmony_ci            return ShaderMode::kClamp;
266cb93a386Sopenharmony_ci        case Wrap::kRepeat:
267cb93a386Sopenharmony_ci            switch (mm) {
268cb93a386Sopenharmony_ci                case MipmapMode::kNone:
269cb93a386Sopenharmony_ci                    switch (filter) {
270cb93a386Sopenharmony_ci                        case Filter::kNearest: return ShaderMode::kRepeat_Nearest_None;
271cb93a386Sopenharmony_ci                        case Filter::kLinear:  return ShaderMode::kRepeat_Linear_None;
272cb93a386Sopenharmony_ci                    }
273cb93a386Sopenharmony_ci                    SkUNREACHABLE;
274cb93a386Sopenharmony_ci                case MipmapMode::kNearest:
275cb93a386Sopenharmony_ci                case MipmapMode::kLinear:
276cb93a386Sopenharmony_ci                    switch (filter) {
277cb93a386Sopenharmony_ci                        case Filter::kNearest: return ShaderMode::kRepeat_Nearest_Mipmap;
278cb93a386Sopenharmony_ci                        case Filter::kLinear:  return ShaderMode::kRepeat_Linear_Mipmap;
279cb93a386Sopenharmony_ci                    }
280cb93a386Sopenharmony_ci                    SkUNREACHABLE;
281cb93a386Sopenharmony_ci            }
282cb93a386Sopenharmony_ci            SkUNREACHABLE;
283cb93a386Sopenharmony_ci        case Wrap::kClampToBorder:
284cb93a386Sopenharmony_ci            return filter == Filter::kNearest ? ShaderMode::kClampToBorder_Nearest
285cb93a386Sopenharmony_ci                                              : ShaderMode::kClampToBorder_Filter;
286cb93a386Sopenharmony_ci    }
287cb93a386Sopenharmony_ci    SkUNREACHABLE;
288cb93a386Sopenharmony_ci}
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ciinline bool GrTextureEffect::ShaderModeIsClampToBorder(ShaderMode m) {
291cb93a386Sopenharmony_ci    return m == ShaderMode::kClampToBorder_Nearest || m == ShaderMode::kClampToBorder_Filter;
292cb93a386Sopenharmony_ci}
293cb93a386Sopenharmony_ci
294cb93a386Sopenharmony_cibool GrTextureEffect::ShaderModeRequiresUnormCoord(ShaderMode m) {
295cb93a386Sopenharmony_ci    switch (m) {
296cb93a386Sopenharmony_ci        case ShaderMode::kNone:                     return false;
297cb93a386Sopenharmony_ci        case ShaderMode::kClamp:                    return false;
298cb93a386Sopenharmony_ci        case ShaderMode::kRepeat_Nearest_None:      return false;
299cb93a386Sopenharmony_ci        case ShaderMode::kRepeat_Linear_None:       return true;
300cb93a386Sopenharmony_ci        case ShaderMode::kRepeat_Nearest_Mipmap:    return true;
301cb93a386Sopenharmony_ci        case ShaderMode::kRepeat_Linear_Mipmap:     return true;
302cb93a386Sopenharmony_ci        case ShaderMode::kMirrorRepeat:             return false;
303cb93a386Sopenharmony_ci        case ShaderMode::kClampToBorder_Nearest:    return true;
304cb93a386Sopenharmony_ci        case ShaderMode::kClampToBorder_Filter:     return true;
305cb93a386Sopenharmony_ci    }
306cb93a386Sopenharmony_ci    SkUNREACHABLE;
307cb93a386Sopenharmony_ci};
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_civoid GrTextureEffect::Impl::emitCode(EmitArgs& args) {
310cb93a386Sopenharmony_ci    using ShaderMode = GrTextureEffect::ShaderMode;
311cb93a386Sopenharmony_ci
312cb93a386Sopenharmony_ci    auto& te = args.fFp.cast<GrTextureEffect>();
313cb93a386Sopenharmony_ci    auto* fb = args.fFragBuilder;
314cb93a386Sopenharmony_ci
315cb93a386Sopenharmony_ci    if (te.fShaderModes[0] == ShaderMode::kNone &&
316cb93a386Sopenharmony_ci        te.fShaderModes[1] == ShaderMode::kNone) {
317cb93a386Sopenharmony_ci        fb->codeAppendf("return ");
318cb93a386Sopenharmony_ci        fb->appendTextureLookup(fSamplerHandle, args.fSampleCoord);
319cb93a386Sopenharmony_ci        fb->codeAppendf(";");
320cb93a386Sopenharmony_ci    } else {
321cb93a386Sopenharmony_ci        // Here is the basic flow of the various ShaderModes are implemented in a series of
322cb93a386Sopenharmony_ci        // steps. Not all the steps apply to all the modes. We try to emit only the steps
323cb93a386Sopenharmony_ci        // that are necessary for the given x/y shader modes.
324cb93a386Sopenharmony_ci        //
325cb93a386Sopenharmony_ci        // 0) Start with interpolated coordinates (unnormalize if doing anything
326cb93a386Sopenharmony_ci        //    complicated).
327cb93a386Sopenharmony_ci        // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
328cb93a386Sopenharmony_ci        //    through output of 0).
329cb93a386Sopenharmony_ci        // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
330cb93a386Sopenharmony_ci        //    MirrorRepeat always or ClampToBorder only when filtering] or pass through
331cb93a386Sopenharmony_ci        //    output of 1). The clamp rect collapses to a line or point it if the subset
332cb93a386Sopenharmony_ci        //    rect is less than one pixel wide/tall.
333cb93a386Sopenharmony_ci        // 3) Look up texture with output of 2) [All]
334cb93a386Sopenharmony_ci        // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
335cb93a386Sopenharmony_ci        //    ClampToBorder]. In the Repeat case this requires extra texture lookups on the
336cb93a386Sopenharmony_ci        //    other side of the subset (up to 3 more reads). Or if ClampToBorder and not
337cb93a386Sopenharmony_ci        //    filtering do a hard less than/greater than test with the subset rect.
338cb93a386Sopenharmony_ci
339cb93a386Sopenharmony_ci        // Convert possible projective texture coordinates into non-homogeneous half2.
340cb93a386Sopenharmony_ci        fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
341cb93a386Sopenharmony_ci
342cb93a386Sopenharmony_ci        const auto& m = te.fShaderModes;
343cb93a386Sopenharmony_ci
344cb93a386Sopenharmony_ci        const char* borderName = nullptr;
345cb93a386Sopenharmony_ci        if (te.hasClampToBorderShaderMode()) {
346cb93a386Sopenharmony_ci            fBorderUni = args.fUniformHandler->addUniform(
347cb93a386Sopenharmony_ci                    &te, kFragment_GrShaderFlag, kHalf4_GrSLType, "border", &borderName);
348cb93a386Sopenharmony_ci        }
349cb93a386Sopenharmony_ci        auto modeUsesSubset = [](ShaderMode m) {
350cb93a386Sopenharmony_ci          switch (m) {
351cb93a386Sopenharmony_ci              case ShaderMode::kNone:                     return false;
352cb93a386Sopenharmony_ci              case ShaderMode::kClamp:                    return false;
353cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Nearest_None:      return true;
354cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Linear_None:       return true;
355cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Nearest_Mipmap:    return true;
356cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Linear_Mipmap:     return true;
357cb93a386Sopenharmony_ci              case ShaderMode::kMirrorRepeat:             return true;
358cb93a386Sopenharmony_ci              case ShaderMode::kClampToBorder_Nearest:    return true;
359cb93a386Sopenharmony_ci              case ShaderMode::kClampToBorder_Filter:     return true;
360cb93a386Sopenharmony_ci          }
361cb93a386Sopenharmony_ci          SkUNREACHABLE;
362cb93a386Sopenharmony_ci        };
363cb93a386Sopenharmony_ci
364cb93a386Sopenharmony_ci        auto modeUsesClamp = [](ShaderMode m) {
365cb93a386Sopenharmony_ci          switch (m) {
366cb93a386Sopenharmony_ci              case ShaderMode::kNone:                     return false;
367cb93a386Sopenharmony_ci              case ShaderMode::kClamp:                    return true;
368cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Nearest_None:      return true;
369cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Linear_None:       return true;
370cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Nearest_Mipmap:    return true;
371cb93a386Sopenharmony_ci              case ShaderMode::kRepeat_Linear_Mipmap:     return true;
372cb93a386Sopenharmony_ci              case ShaderMode::kMirrorRepeat:             return true;
373cb93a386Sopenharmony_ci              case ShaderMode::kClampToBorder_Nearest:    return false;
374cb93a386Sopenharmony_ci              case ShaderMode::kClampToBorder_Filter:     return true;
375cb93a386Sopenharmony_ci          }
376cb93a386Sopenharmony_ci          SkUNREACHABLE;
377cb93a386Sopenharmony_ci        };
378cb93a386Sopenharmony_ci
379cb93a386Sopenharmony_ci        bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
380cb93a386Sopenharmony_ci        bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
381cb93a386Sopenharmony_ci
382cb93a386Sopenharmony_ci        const char* subsetName = nullptr;
383cb93a386Sopenharmony_ci        if (useSubset[0] || useSubset[1]) {
384cb93a386Sopenharmony_ci            fSubsetUni = args.fUniformHandler->addUniform(
385cb93a386Sopenharmony_ci                    &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "subset", &subsetName);
386cb93a386Sopenharmony_ci        }
387cb93a386Sopenharmony_ci
388cb93a386Sopenharmony_ci        const char* clampName = nullptr;
389cb93a386Sopenharmony_ci        if (useClamp[0] || useClamp[1]) {
390cb93a386Sopenharmony_ci            fClampUni = args.fUniformHandler->addUniform(
391cb93a386Sopenharmony_ci                    &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
392cb93a386Sopenharmony_ci        }
393cb93a386Sopenharmony_ci
394cb93a386Sopenharmony_ci        bool unormCoordsRequiredForShaderMode = ShaderModeRequiresUnormCoord(m[0]) ||
395cb93a386Sopenharmony_ci                                                ShaderModeRequiresUnormCoord(m[1]);
396cb93a386Sopenharmony_ci        // We should not pre-normalize the input coords with GrMatrixEffect if we're going to
397cb93a386Sopenharmony_ci        // operate on unnormalized coords and then normalize after the shader mode.
398cb93a386Sopenharmony_ci        SkASSERT(!(unormCoordsRequiredForShaderMode && te.matrixEffectShouldNormalize()));
399cb93a386Sopenharmony_ci        bool sampleCoordsMustBeNormalized =
400cb93a386Sopenharmony_ci                te.fView.asTextureProxy()->textureType() != GrTextureType::kRectangle;
401cb93a386Sopenharmony_ci
402cb93a386Sopenharmony_ci        const char* idims = nullptr;
403cb93a386Sopenharmony_ci        if (unormCoordsRequiredForShaderMode && sampleCoordsMustBeNormalized) {
404cb93a386Sopenharmony_ci            // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
405cb93a386Sopenharmony_ci            // always use?
406cb93a386Sopenharmony_ci            fIDimsUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
407cb93a386Sopenharmony_ci                                                         kFloat2_GrSLType, "idims", &idims);
408cb93a386Sopenharmony_ci        }
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_ci        // Generates a string to read at a coordinate, normalizing coords if necessary.
411cb93a386Sopenharmony_ci        auto read = [&](const char* coord) {
412cb93a386Sopenharmony_ci            SkString result;
413cb93a386Sopenharmony_ci            SkString normCoord;
414cb93a386Sopenharmony_ci            if (idims) {
415cb93a386Sopenharmony_ci                normCoord.printf("(%s) * %s", coord, idims);
416cb93a386Sopenharmony_ci            } else {
417cb93a386Sopenharmony_ci                normCoord = coord;
418cb93a386Sopenharmony_ci            }
419cb93a386Sopenharmony_ci            fb->appendTextureLookup(&result, fSamplerHandle, normCoord.c_str());
420cb93a386Sopenharmony_ci            return result;
421cb93a386Sopenharmony_ci        };
422cb93a386Sopenharmony_ci
423cb93a386Sopenharmony_ci        // Implements coord wrapping for kRepeat and kMirrorRepeat
424cb93a386Sopenharmony_ci        auto subsetCoord = [&](ShaderMode mode,
425cb93a386Sopenharmony_ci                               const char* coordSwizzle,
426cb93a386Sopenharmony_ci                               const char* subsetStartSwizzle,
427cb93a386Sopenharmony_ci                               const char* subsetStopSwizzle,
428cb93a386Sopenharmony_ci                               const char* extraCoord,
429cb93a386Sopenharmony_ci                               const char* coordWeight) {
430cb93a386Sopenharmony_ci            switch (mode) {
431cb93a386Sopenharmony_ci                // These modes either don't use the subset rect or don't need to map the
432cb93a386Sopenharmony_ci                // coords to be within the subset.
433cb93a386Sopenharmony_ci                case ShaderMode::kNone:
434cb93a386Sopenharmony_ci                case ShaderMode::kClampToBorder_Nearest:
435cb93a386Sopenharmony_ci                case ShaderMode::kClampToBorder_Filter:
436cb93a386Sopenharmony_ci                case ShaderMode::kClamp:
437cb93a386Sopenharmony_ci                    fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle, coordSwizzle);
438cb93a386Sopenharmony_ci                    break;
439cb93a386Sopenharmony_ci                case ShaderMode::kRepeat_Nearest_None:
440cb93a386Sopenharmony_ci                case ShaderMode::kRepeat_Linear_None:
441cb93a386Sopenharmony_ci                    fb->codeAppendf(
442cb93a386Sopenharmony_ci                            "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + %s.%s;",
443cb93a386Sopenharmony_ci                            coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle, subsetName,
444cb93a386Sopenharmony_ci                            subsetStopSwizzle, subsetName, subsetStartSwizzle, subsetName,
445cb93a386Sopenharmony_ci                            subsetStartSwizzle);
446cb93a386Sopenharmony_ci                    break;
447cb93a386Sopenharmony_ci                case ShaderMode::kRepeat_Nearest_Mipmap:
448cb93a386Sopenharmony_ci                case ShaderMode::kRepeat_Linear_Mipmap:
449cb93a386Sopenharmony_ci                    // The approach here is to generate two sets of texture coords that
450cb93a386Sopenharmony_ci                    // are both "moving" at the same speed (if not direction) as
451cb93a386Sopenharmony_ci                    // inCoords. We accomplish that by using two out of phase mirror
452cb93a386Sopenharmony_ci                    // repeat coords. We will always sample using both coords but the
453cb93a386Sopenharmony_ci                    // read from the upward sloping one is selected using a weight
454cb93a386Sopenharmony_ci                    // that transitions from one set to the other near the reflection
455cb93a386Sopenharmony_ci                    // point. Like the coords, the weight is a saw-tooth function,
456cb93a386Sopenharmony_ci                    // phase-shifted, vertically translated, and then clamped to 0..1.
457cb93a386Sopenharmony_ci                    // TODO: Skip this and use textureGrad() when available.
458cb93a386Sopenharmony_ci                    SkASSERT(extraCoord);
459cb93a386Sopenharmony_ci                    SkASSERT(coordWeight);
460cb93a386Sopenharmony_ci                    fb->codeAppend("{");
461cb93a386Sopenharmony_ci                    fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
462cb93a386Sopenharmony_ci                                    subsetName, subsetStartSwizzle);
463cb93a386Sopenharmony_ci                    fb->codeAppendf("float w2 = 2 * w;");
464cb93a386Sopenharmony_ci                    fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle, subsetName,
465cb93a386Sopenharmony_ci                                    subsetStartSwizzle);
466cb93a386Sopenharmony_ci                    fb->codeAppend("float m = mod(d, w2);");
467cb93a386Sopenharmony_ci                    fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
468cb93a386Sopenharmony_ci                    fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle, subsetName,
469cb93a386Sopenharmony_ci                                    subsetStartSwizzle);
470cb93a386Sopenharmony_ci                    fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
471cb93a386Sopenharmony_ci                                    subsetStartSwizzle);
472cb93a386Sopenharmony_ci                    // coordWeight is used as the third param of mix() to blend between a
473cb93a386Sopenharmony_ci                    // sample taken using subsetCoord and a sample at extraCoord.
474cb93a386Sopenharmony_ci                    fb->codeAppend("float hw = w/2;");
475cb93a386Sopenharmony_ci                    fb->codeAppend("float n = mod(d - hw, w2);");
476cb93a386Sopenharmony_ci                    fb->codeAppendf("%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + 0.5));",
477cb93a386Sopenharmony_ci                                    coordWeight);
478cb93a386Sopenharmony_ci                    fb->codeAppend("}");
479cb93a386Sopenharmony_ci                    break;
480cb93a386Sopenharmony_ci                case ShaderMode::kMirrorRepeat:
481cb93a386Sopenharmony_ci                    fb->codeAppend("{");
482cb93a386Sopenharmony_ci                    fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
483cb93a386Sopenharmony_ci                                    subsetName, subsetStartSwizzle);
484cb93a386Sopenharmony_ci                    fb->codeAppendf("float w2 = 2 * w;");
485cb93a386Sopenharmony_ci                    fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
486cb93a386Sopenharmony_ci                                    subsetName, subsetStartSwizzle);
487cb93a386Sopenharmony_ci                    fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
488cb93a386Sopenharmony_ci                                    coordSwizzle, subsetName, subsetStartSwizzle);
489cb93a386Sopenharmony_ci                    fb->codeAppend("}");
490cb93a386Sopenharmony_ci                    break;
491cb93a386Sopenharmony_ci            }
492cb93a386Sopenharmony_ci        };
493cb93a386Sopenharmony_ci
494cb93a386Sopenharmony_ci        auto clampCoord = [&](bool clamp,
495cb93a386Sopenharmony_ci                              const char* coordSwizzle,
496cb93a386Sopenharmony_ci                              const char* clampStartSwizzle,
497cb93a386Sopenharmony_ci                              const char* clampStopSwizzle) {
498cb93a386Sopenharmony_ci            if (clamp) {
499cb93a386Sopenharmony_ci                fb->codeAppendf("clampedCoord%s = clamp(subsetCoord%s, %s%s, %s%s);",
500cb93a386Sopenharmony_ci                                coordSwizzle, coordSwizzle, clampName, clampStartSwizzle, clampName,
501cb93a386Sopenharmony_ci                                clampStopSwizzle);
502cb93a386Sopenharmony_ci            } else {
503cb93a386Sopenharmony_ci                fb->codeAppendf("clampedCoord%s = subsetCoord%s;", coordSwizzle, coordSwizzle);
504cb93a386Sopenharmony_ci            }
505cb93a386Sopenharmony_ci        };
506cb93a386Sopenharmony_ci
507cb93a386Sopenharmony_ci        // Insert vars for extra coords and blending weights for repeat + mip map.
508cb93a386Sopenharmony_ci        const char* extraRepeatCoordX  = nullptr;
509cb93a386Sopenharmony_ci        const char* repeatCoordWeightX = nullptr;
510cb93a386Sopenharmony_ci        const char* extraRepeatCoordY  = nullptr;
511cb93a386Sopenharmony_ci        const char* repeatCoordWeightY = nullptr;
512cb93a386Sopenharmony_ci
513cb93a386Sopenharmony_ci        bool mipmapRepeatX = m[0] == ShaderMode::kRepeat_Nearest_Mipmap ||
514cb93a386Sopenharmony_ci                             m[0] == ShaderMode::kRepeat_Linear_Mipmap;
515cb93a386Sopenharmony_ci        bool mipmapRepeatY = m[1] == ShaderMode::kRepeat_Nearest_Mipmap ||
516cb93a386Sopenharmony_ci                             m[1] == ShaderMode::kRepeat_Linear_Mipmap;
517cb93a386Sopenharmony_ci
518cb93a386Sopenharmony_ci        if (mipmapRepeatX || mipmapRepeatY) {
519cb93a386Sopenharmony_ci            fb->codeAppend("float2 extraRepeatCoord;");
520cb93a386Sopenharmony_ci        }
521cb93a386Sopenharmony_ci        if (mipmapRepeatX) {
522cb93a386Sopenharmony_ci            fb->codeAppend("half repeatCoordWeightX;");
523cb93a386Sopenharmony_ci            extraRepeatCoordX   = "extraRepeatCoord.x";
524cb93a386Sopenharmony_ci            repeatCoordWeightX  = "repeatCoordWeightX";
525cb93a386Sopenharmony_ci        }
526cb93a386Sopenharmony_ci        if (mipmapRepeatY) {
527cb93a386Sopenharmony_ci            fb->codeAppend("half repeatCoordWeightY;");
528cb93a386Sopenharmony_ci            extraRepeatCoordY   = "extraRepeatCoord.y";
529cb93a386Sopenharmony_ci            repeatCoordWeightY  = "repeatCoordWeightY";
530cb93a386Sopenharmony_ci        }
531cb93a386Sopenharmony_ci
532cb93a386Sopenharmony_ci        // Apply subset rect and clamp rect to coords.
533cb93a386Sopenharmony_ci        fb->codeAppend("float2 subsetCoord;");
534cb93a386Sopenharmony_ci        subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX, repeatCoordWeightX);
535cb93a386Sopenharmony_ci        subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY, repeatCoordWeightY);
536cb93a386Sopenharmony_ci        fb->codeAppend("float2 clampedCoord;");
537cb93a386Sopenharmony_ci        if (useClamp[0] == useClamp[1]) {
538cb93a386Sopenharmony_ci            clampCoord(useClamp[0], "", ".xy", ".zw");
539cb93a386Sopenharmony_ci        } else {
540cb93a386Sopenharmony_ci            clampCoord(useClamp[0], ".x", ".x", ".z");
541cb93a386Sopenharmony_ci            clampCoord(useClamp[1], ".y", ".y", ".w");
542cb93a386Sopenharmony_ci        }
543cb93a386Sopenharmony_ci        // Additional clamping for the extra coords for kRepeat with mip maps.
544cb93a386Sopenharmony_ci        if (mipmapRepeatX && mipmapRepeatY) {
545cb93a386Sopenharmony_ci            fb->codeAppendf("extraRepeatCoord = clamp(extraRepeatCoord, %s.xy, %s.zw);",
546cb93a386Sopenharmony_ci                            clampName, clampName);
547cb93a386Sopenharmony_ci        } else if (mipmapRepeatX) {
548cb93a386Sopenharmony_ci            fb->codeAppendf("extraRepeatCoord.x = clamp(extraRepeatCoord.x, %s.x, %s.z);",
549cb93a386Sopenharmony_ci                            clampName, clampName);
550cb93a386Sopenharmony_ci        } else if (mipmapRepeatY) {
551cb93a386Sopenharmony_ci            fb->codeAppendf("extraRepeatCoord.y = clamp(extraRepeatCoord.y, %s.y, %s.w);",
552cb93a386Sopenharmony_ci                            clampName, clampName);
553cb93a386Sopenharmony_ci        }
554cb93a386Sopenharmony_ci
555cb93a386Sopenharmony_ci        // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
556cb93a386Sopenharmony_ci        // to blend between them. If neither direction is repeat or not using mip maps do a single
557cb93a386Sopenharmony_ci        // read at clampedCoord.
558cb93a386Sopenharmony_ci        if (mipmapRepeatX && mipmapRepeatY) {
559cb93a386Sopenharmony_ci            fb->codeAppendf(
560cb93a386Sopenharmony_ci                    "half4 textureColor ="
561cb93a386Sopenharmony_ci                    "   mix(mix(%s, %s, repeatCoordWeightX),"
562cb93a386Sopenharmony_ci                    "       mix(%s, %s, repeatCoordWeightX),"
563cb93a386Sopenharmony_ci                    "       repeatCoordWeightY);",
564cb93a386Sopenharmony_ci                    read("clampedCoord").c_str(),
565cb93a386Sopenharmony_ci                    read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str(),
566cb93a386Sopenharmony_ci                    read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str(),
567cb93a386Sopenharmony_ci                    read("float2(extraRepeatCoord.x, extraRepeatCoord.y)").c_str());
568cb93a386Sopenharmony_ci
569cb93a386Sopenharmony_ci        } else if (mipmapRepeatX) {
570cb93a386Sopenharmony_ci            fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
571cb93a386Sopenharmony_ci                            read("clampedCoord").c_str(),
572cb93a386Sopenharmony_ci                            read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str());
573cb93a386Sopenharmony_ci        } else if (mipmapRepeatY) {
574cb93a386Sopenharmony_ci            fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
575cb93a386Sopenharmony_ci                            read("clampedCoord").c_str(),
576cb93a386Sopenharmony_ci                            read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str());
577cb93a386Sopenharmony_ci        } else {
578cb93a386Sopenharmony_ci            fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
579cb93a386Sopenharmony_ci        }
580cb93a386Sopenharmony_ci
581cb93a386Sopenharmony_ci        // Strings for extra texture reads used only in kRepeatLinear
582cb93a386Sopenharmony_ci        SkString repeatLinearReadX;
583cb93a386Sopenharmony_ci        SkString repeatLinearReadY;
584cb93a386Sopenharmony_ci
585cb93a386Sopenharmony_ci        // Calculate the amount the coord moved for clamping. This will be used
586cb93a386Sopenharmony_ci        // to implement shader-based filtering for kClampToBorder and kRepeat.
587cb93a386Sopenharmony_ci        bool repeatLinearFilterX = m[0] == ShaderMode::kRepeat_Linear_None ||
588cb93a386Sopenharmony_ci                                   m[0] == ShaderMode::kRepeat_Linear_Mipmap;
589cb93a386Sopenharmony_ci        bool repeatLinearFilterY = m[1] == ShaderMode::kRepeat_Linear_None ||
590cb93a386Sopenharmony_ci                                   m[1] == ShaderMode::kRepeat_Linear_Mipmap;
591cb93a386Sopenharmony_ci        if (repeatLinearFilterX || m[0] == ShaderMode::kClampToBorder_Filter) {
592cb93a386Sopenharmony_ci            fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
593cb93a386Sopenharmony_ci            if (repeatLinearFilterX) {
594cb93a386Sopenharmony_ci                fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;",
595cb93a386Sopenharmony_ci                                clampName, clampName);
596cb93a386Sopenharmony_ci                repeatLinearReadX = read("float2(repeatCoordX, clampedCoord.y)");
597cb93a386Sopenharmony_ci            }
598cb93a386Sopenharmony_ci        }
599cb93a386Sopenharmony_ci        if (repeatLinearFilterY || m[1] == ShaderMode::kClampToBorder_Filter) {
600cb93a386Sopenharmony_ci            fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
601cb93a386Sopenharmony_ci            if (repeatLinearFilterY) {
602cb93a386Sopenharmony_ci                fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;",
603cb93a386Sopenharmony_ci                                clampName, clampName);
604cb93a386Sopenharmony_ci                repeatLinearReadY = read("float2(clampedCoord.x, repeatCoordY)");
605cb93a386Sopenharmony_ci            }
606cb93a386Sopenharmony_ci        }
607cb93a386Sopenharmony_ci
608cb93a386Sopenharmony_ci        // Add logic for kRepeat + linear filter. Do 1 or 3 more texture reads depending
609cb93a386Sopenharmony_ci        // on whether both modes are kRepeat and whether we're near a single subset edge
610cb93a386Sopenharmony_ci        // or a corner. Then blend the multiple reads using the err values calculated
611cb93a386Sopenharmony_ci        // above.
612cb93a386Sopenharmony_ci        const char* ifStr = "if";
613cb93a386Sopenharmony_ci        if (repeatLinearFilterX && repeatLinearFilterY) {
614cb93a386Sopenharmony_ci            auto repeatLinearReadXY = read("float2(repeatCoordX, repeatCoordY)");
615cb93a386Sopenharmony_ci            fb->codeAppendf(
616cb93a386Sopenharmony_ci                    "if (errX != 0 && errY != 0) {"
617cb93a386Sopenharmony_ci                    "    errX = abs(errX);"
618cb93a386Sopenharmony_ci                    "    textureColor = mix(mix(textureColor, %s, errX),"
619cb93a386Sopenharmony_ci                    "                       mix(%s, %s, errX),"
620cb93a386Sopenharmony_ci                    "                       abs(errY));"
621cb93a386Sopenharmony_ci                    "}",
622cb93a386Sopenharmony_ci                    repeatLinearReadX.c_str(), repeatLinearReadY.c_str(),
623cb93a386Sopenharmony_ci                    repeatLinearReadXY.c_str());
624cb93a386Sopenharmony_ci            ifStr = "else if";
625cb93a386Sopenharmony_ci        }
626cb93a386Sopenharmony_ci        if (repeatLinearFilterX) {
627cb93a386Sopenharmony_ci            fb->codeAppendf(
628cb93a386Sopenharmony_ci                    "%s (errX != 0) {"
629cb93a386Sopenharmony_ci                    "    textureColor = mix(textureColor, %s, abs(errX));"
630cb93a386Sopenharmony_ci                    "}",
631cb93a386Sopenharmony_ci                    ifStr, repeatLinearReadX.c_str());
632cb93a386Sopenharmony_ci        }
633cb93a386Sopenharmony_ci        if (repeatLinearFilterY) {
634cb93a386Sopenharmony_ci            fb->codeAppendf(
635cb93a386Sopenharmony_ci                    "%s (errY != 0) {"
636cb93a386Sopenharmony_ci                    "    textureColor = mix(textureColor, %s, abs(errY));"
637cb93a386Sopenharmony_ci                    "}",
638cb93a386Sopenharmony_ci                    ifStr, repeatLinearReadY.c_str());
639cb93a386Sopenharmony_ci        }
640cb93a386Sopenharmony_ci
641cb93a386Sopenharmony_ci        // Do soft edge shader filtering against border color for kClampToBorderFilter using
642cb93a386Sopenharmony_ci        // the err values calculated above.
643cb93a386Sopenharmony_ci        if (m[0] == ShaderMode::kClampToBorder_Filter) {
644cb93a386Sopenharmony_ci            fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));", borderName);
645cb93a386Sopenharmony_ci        }
646cb93a386Sopenharmony_ci        if (m[1] == ShaderMode::kClampToBorder_Filter) {
647cb93a386Sopenharmony_ci            fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));", borderName);
648cb93a386Sopenharmony_ci        }
649cb93a386Sopenharmony_ci
650cb93a386Sopenharmony_ci        // Do hard-edge shader transition to border color for kClampToBorderNearest at the
651cb93a386Sopenharmony_ci        // subset boundaries. Snap the input coordinates to nearest neighbor (with an
652cb93a386Sopenharmony_ci        // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
653cb93a386Sopenharmony_ci        if (m[0] == ShaderMode::kClampToBorder_Nearest) {
654cb93a386Sopenharmony_ci            fb->codeAppendf(
655cb93a386Sopenharmony_ci                    "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
656cb93a386Sopenharmony_ci                    "if (snappedX < %s.x || snappedX > %s.z) {"
657cb93a386Sopenharmony_ci                    "    textureColor = %s;"
658cb93a386Sopenharmony_ci                    "}",
659cb93a386Sopenharmony_ci                    subsetName, subsetName, borderName);
660cb93a386Sopenharmony_ci        }
661cb93a386Sopenharmony_ci        if (m[1] == ShaderMode::kClampToBorder_Nearest) {
662cb93a386Sopenharmony_ci            fb->codeAppendf(
663cb93a386Sopenharmony_ci                    "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
664cb93a386Sopenharmony_ci                    "if (snappedY < %s.y || snappedY > %s.w) {"
665cb93a386Sopenharmony_ci                    "    textureColor = %s;"
666cb93a386Sopenharmony_ci                    "}",
667cb93a386Sopenharmony_ci                    subsetName, subsetName, borderName);
668cb93a386Sopenharmony_ci        }
669cb93a386Sopenharmony_ci        fb->codeAppendf("return textureColor;");
670cb93a386Sopenharmony_ci    }
671cb93a386Sopenharmony_ci}
672cb93a386Sopenharmony_ci
673cb93a386Sopenharmony_civoid GrTextureEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdm,
674cb93a386Sopenharmony_ci                                      const GrFragmentProcessor& fp) {
675cb93a386Sopenharmony_ci    const auto& te = fp.cast<GrTextureEffect>();
676cb93a386Sopenharmony_ci
677cb93a386Sopenharmony_ci    const float w = te.texture()->width();
678cb93a386Sopenharmony_ci    const float h = te.texture()->height();
679cb93a386Sopenharmony_ci    const auto& s = te.fSubset;
680cb93a386Sopenharmony_ci    const auto& c = te.fClamp;
681cb93a386Sopenharmony_ci
682cb93a386Sopenharmony_ci    auto type = te.texture()->textureType();
683cb93a386Sopenharmony_ci
684cb93a386Sopenharmony_ci    float idims[2] = {1.f/w, 1.f/h};
685cb93a386Sopenharmony_ci
686cb93a386Sopenharmony_ci    if (fIDimsUni.isValid()) {
687cb93a386Sopenharmony_ci        pdm.set2fv(fIDimsUni, 1, idims);
688cb93a386Sopenharmony_ci        SkASSERT(type != GrTextureType::kRectangle);
689cb93a386Sopenharmony_ci    }
690cb93a386Sopenharmony_ci
691cb93a386Sopenharmony_ci    auto pushRect = [&](float rect[4], UniformHandle uni) {
692cb93a386Sopenharmony_ci        if (te.view().origin() == kBottomLeft_GrSurfaceOrigin) {
693cb93a386Sopenharmony_ci            rect[1] = h - rect[1];
694cb93a386Sopenharmony_ci            rect[3] = h - rect[3];
695cb93a386Sopenharmony_ci            std::swap(rect[1], rect[3]);
696cb93a386Sopenharmony_ci        }
697cb93a386Sopenharmony_ci        if (!fIDimsUni.isValid() && type != GrTextureType::kRectangle) {
698cb93a386Sopenharmony_ci            rect[0] *= idims[0];
699cb93a386Sopenharmony_ci            rect[2] *= idims[0];
700cb93a386Sopenharmony_ci            rect[1] *= idims[1];
701cb93a386Sopenharmony_ci            rect[3] *= idims[1];
702cb93a386Sopenharmony_ci        }
703cb93a386Sopenharmony_ci        pdm.set4fv(uni, 1, rect);
704cb93a386Sopenharmony_ci    };
705cb93a386Sopenharmony_ci
706cb93a386Sopenharmony_ci    if (fSubsetUni.isValid()) {
707cb93a386Sopenharmony_ci        float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
708cb93a386Sopenharmony_ci        pushRect(subset, fSubsetUni);
709cb93a386Sopenharmony_ci    }
710cb93a386Sopenharmony_ci    if (fClampUni.isValid()) {
711cb93a386Sopenharmony_ci        float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
712cb93a386Sopenharmony_ci        pushRect(subset, fClampUni);
713cb93a386Sopenharmony_ci    }
714cb93a386Sopenharmony_ci    if (fBorderUni.isValid()) {
715cb93a386Sopenharmony_ci        pdm.set4fv(fBorderUni, 1, te.fBorder);
716cb93a386Sopenharmony_ci    }
717cb93a386Sopenharmony_ci}
718cb93a386Sopenharmony_ci
719cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor::ProgramImpl> GrTextureEffect::onMakeProgramImpl() const {
720cb93a386Sopenharmony_ci    return std::make_unique<Impl>();
721cb93a386Sopenharmony_ci}
722cb93a386Sopenharmony_ci
723cb93a386Sopenharmony_ciSkString GrTextureEffect::getShaderDfxInfo() const
724cb93a386Sopenharmony_ci{
725cb93a386Sopenharmony_ci    SkString format;
726cb93a386Sopenharmony_ci    format.printf("ShaderDfx_GrTextureEffect_%d_%d", fShaderModes[0], fShaderModes[1]);
727cb93a386Sopenharmony_ci    return format;
728cb93a386Sopenharmony_ci}
729cb93a386Sopenharmony_ci
730cb93a386Sopenharmony_civoid GrTextureEffect::onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
731cb93a386Sopenharmony_ci    auto m0 = static_cast<uint32_t>(fShaderModes[0]);
732cb93a386Sopenharmony_ci    b->addBits(8, m0, "shaderMode0");
733cb93a386Sopenharmony_ci
734cb93a386Sopenharmony_ci    auto m1 = static_cast<uint32_t>(fShaderModes[1]);
735cb93a386Sopenharmony_ci    b->addBits(8, m1, "shaderMode1");
736cb93a386Sopenharmony_ci}
737cb93a386Sopenharmony_ci
738cb93a386Sopenharmony_cibool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
739cb93a386Sopenharmony_ci    auto& that = other.cast<GrTextureEffect>();
740cb93a386Sopenharmony_ci    if (fView != that.fView) {
741cb93a386Sopenharmony_ci        return false;
742cb93a386Sopenharmony_ci    }
743cb93a386Sopenharmony_ci    if (fSamplerState != that.fSamplerState) {
744cb93a386Sopenharmony_ci        return false;
745cb93a386Sopenharmony_ci    }
746cb93a386Sopenharmony_ci    if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
747cb93a386Sopenharmony_ci        return false;
748cb93a386Sopenharmony_ci    }
749cb93a386Sopenharmony_ci    if (fSubset != that.fSubset) {
750cb93a386Sopenharmony_ci        return false;
751cb93a386Sopenharmony_ci    }
752cb93a386Sopenharmony_ci    if (this->hasClampToBorderShaderMode() && !std::equal(fBorder, fBorder + 4, that.fBorder)) {
753cb93a386Sopenharmony_ci        return false;
754cb93a386Sopenharmony_ci    }
755cb93a386Sopenharmony_ci    return true;
756cb93a386Sopenharmony_ci}
757cb93a386Sopenharmony_ci
758cb93a386Sopenharmony_cibool GrTextureEffect::matrixEffectShouldNormalize() const {
759cb93a386Sopenharmony_ci    return fView.asTextureProxy()->textureType() != GrTextureType::kRectangle &&
760cb93a386Sopenharmony_ci           !ShaderModeRequiresUnormCoord(fShaderModes[0])                     &&
761cb93a386Sopenharmony_ci           !ShaderModeRequiresUnormCoord(fShaderModes[1]);
762cb93a386Sopenharmony_ci}
763cb93a386Sopenharmony_ci
764cb93a386Sopenharmony_ciGrTextureEffect::GrTextureEffect(GrSurfaceProxyView view,
765cb93a386Sopenharmony_ci                                 SkAlphaType alphaType,
766cb93a386Sopenharmony_ci                                 const Sampling& sampling)
767cb93a386Sopenharmony_ci        : GrFragmentProcessor(kGrTextureEffect_ClassID,
768cb93a386Sopenharmony_ci                              ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
769cb93a386Sopenharmony_ci        , fView(std::move(view))
770cb93a386Sopenharmony_ci        , fSamplerState(sampling.fHWSampler)
771cb93a386Sopenharmony_ci        , fSubset(sampling.fShaderSubset)
772cb93a386Sopenharmony_ci        , fClamp(sampling.fShaderClamp)
773cb93a386Sopenharmony_ci        , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]} {
774cb93a386Sopenharmony_ci    // We always compare the range even when it isn't used so assert we have canonical don't care
775cb93a386Sopenharmony_ci    // values.
776cb93a386Sopenharmony_ci    SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
777cb93a386Sopenharmony_ci    SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
778cb93a386Sopenharmony_ci    this->setUsesSampleCoordsDirectly();
779cb93a386Sopenharmony_ci    std::copy_n(sampling.fBorder, 4, fBorder);
780cb93a386Sopenharmony_ci}
781cb93a386Sopenharmony_ci
782cb93a386Sopenharmony_ciGrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
783cb93a386Sopenharmony_ci        : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
784cb93a386Sopenharmony_ci        , fView(src.fView)
785cb93a386Sopenharmony_ci        , fSamplerState(src.fSamplerState)
786cb93a386Sopenharmony_ci        , fSubset(src.fSubset)
787cb93a386Sopenharmony_ci        , fClamp(src.fClamp)
788cb93a386Sopenharmony_ci        , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
789cb93a386Sopenharmony_ci    std::copy_n(src.fBorder, 4, fBorder);
790cb93a386Sopenharmony_ci    this->setUsesSampleCoordsDirectly();
791cb93a386Sopenharmony_ci}
792cb93a386Sopenharmony_ci
793cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
794cb93a386Sopenharmony_ci    return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
795cb93a386Sopenharmony_ci}
796cb93a386Sopenharmony_ci
797cb93a386Sopenharmony_ciGR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect);
798cb93a386Sopenharmony_ci#if GR_TEST_UTILS
799cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
800cb93a386Sopenharmony_ci    auto [view, ct, at] = testData->randomView();
801cb93a386Sopenharmony_ci    Wrap wrapModes[2];
802cb93a386Sopenharmony_ci    GrTest::TestWrapModes(testData->fRandom, wrapModes);
803cb93a386Sopenharmony_ci
804cb93a386Sopenharmony_ci    Filter filter = testData->fRandom->nextBool() ? Filter::kLinear : Filter::kNearest;
805cb93a386Sopenharmony_ci    MipmapMode mm = MipmapMode::kNone;
806cb93a386Sopenharmony_ci    if (view.asTextureProxy()->mipmapped() == GrMipmapped::kYes) {
807cb93a386Sopenharmony_ci        mm = testData->fRandom->nextBool() ? MipmapMode::kLinear : MipmapMode::kNone;
808cb93a386Sopenharmony_ci    }
809cb93a386Sopenharmony_ci    GrSamplerState params(wrapModes, filter, mm);
810cb93a386Sopenharmony_ci
811cb93a386Sopenharmony_ci    const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
812cb93a386Sopenharmony_ci    return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
813cb93a386Sopenharmony_ci}
814cb93a386Sopenharmony_ci#endif
815