1 /*
2  * Copyright 2012 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 "src/gpu/effects/GrTextureDomain.h"
9 
10 #include "include/gpu/GrTexture.h"
11 #include "include/private/SkFloatingPoint.h"
12 #include "src/gpu/GrProxyProvider.h"
13 #include "src/gpu/GrShaderCaps.h"
14 #include "src/gpu/GrSurfaceProxyPriv.h"
15 #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
16 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
17 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
19 #include "src/gpu/glsl/GrGLSLShaderBuilder.h"
20 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
21 
22 #include <utility>
23 
GrTextureDomain(GrTextureProxy * proxy,const SkRect & domain,Mode modeX,Mode modeY,int index)24 GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode modeX,
25                                  Mode modeY, int index)
26     : fModeX(modeX)
27     , fModeY(modeY)
28     , fIndex(index) {
29 
30     if (!proxy) {
31         SkASSERT(modeX == kIgnore_Mode && modeY == kIgnore_Mode);
32         return;
33     }
34 
35     const SkRect kFullRect = SkRect::MakeIWH(proxy->width(), proxy->height());
36 
37     // We don't currently handle domains that are empty or don't intersect the texture.
38     // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
39     // handle rects that do not intersect the [0..1]x[0..1] rect.
40     SkASSERT(domain.fLeft <= domain.fRight);
41     SkASSERT(domain.fTop <= domain.fBottom);
42     fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
43     fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
44     fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
45     fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
46     SkASSERT(fDomain.fLeft <= fDomain.fRight);
47     SkASSERT(fDomain.fTop <= fDomain.fBottom);
48 }
49 
50 //////////////////////////////////////////////////////////////////////////////
51 
clamp_expression(GrTextureDomain::Mode mode,const char * inCoord,const char * coordSwizzle,const char * domain,const char * minSwizzle,const char * maxSwizzle)52 static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
53                                  const char* coordSwizzle, const char* domain,
54                                  const char* minSwizzle, const char* maxSwizzle) {
55     SkString clampedExpr;
56     switch(mode) {
57         case GrTextureDomain::kIgnore_Mode:
58             clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle);
59             break;
60         case GrTextureDomain::kDecal_Mode:
61             // The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
62             // it's just that the post-processing will be different, so fall through
63         case GrTextureDomain::kClamp_Mode:
64             clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)",
65                                inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle);
66             break;
67         case GrTextureDomain::kRepeat_Mode:
68             clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s",
69                                inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle,
70                                domain, minSwizzle, domain, minSwizzle);
71             break;
72         default:
73             SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode);
74             break;
75     }
76     return clampedExpr;
77 }
78 
sampleTexture(GrGLSLShaderBuilder * builder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps * shaderCaps,const GrTextureDomain & textureDomain,const char * outColor,const SkString & inCoords,GrGLSLFragmentProcessor::SamplerHandle sampler,const char * inModulateColor)79 void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
80                                               GrGLSLUniformHandler* uniformHandler,
81                                               const GrShaderCaps* shaderCaps,
82                                               const GrTextureDomain& textureDomain,
83                                               const char* outColor,
84                                               const SkString& inCoords,
85                                               GrGLSLFragmentProcessor::SamplerHandle sampler,
86                                               const char* inModulateColor) {
87     SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
88     SkDEBUGCODE(fModeX = textureDomain.modeX();)
89     SkDEBUGCODE(fModeY = textureDomain.modeY();)
90     SkDEBUGCODE(fHasMode = true;)
91 
92     if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) &&
93         !fDomainUni.isValid()) {
94         // Must include the domain uniform since at least one axis uses it
95         const char* name;
96         SkString uniName("TexDom");
97         if (textureDomain.fIndex >= 0) {
98             uniName.appendS32(textureDomain.fIndex);
99         }
100         fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
101                                                 uniName.c_str(), &name);
102         fDomainName = name;
103     }
104 
105     bool decalX = textureDomain.modeX() == kDecal_Mode;
106     bool decalY = textureDomain.modeY() == kDecal_Mode;
107     if ((decalX || decalY) && !fDecalUni.isValid()) {
108         const char* name;
109         SkString uniName("DecalParams");
110         if (textureDomain.fIndex >= 0) {
111             uniName.appendS32(textureDomain.fIndex);
112         }
113         // Half3 since this will hold texture width, height, and then a step function control param
114         fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
115                                                uniName.c_str(), &name);
116         fDecalName = name;
117     }
118 
119     // Add a block so that we can declare variables
120     GrGLSLShaderBuilder::ShaderBlock block(builder);
121     // Always use a local variable for the input coordinates; often callers pass in an expression
122     // and we want to cache it across all of its references in the code below
123     builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
124     builder->codeAppend("float2 clampedCoord = ");
125     if (textureDomain.modeX() != textureDomain.modeY()) {
126         // The wrap modes differ on the two axes, so build up a coordinate that respects each axis'
127         // domain rule independently before sampling the texture.
128         SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x",
129                                         fDomainName.c_str(), "x", "z");
130         SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y",
131                                         fDomainName.c_str(), "y", "w");
132         builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str());
133     } else {
134         // Since the x and y axis wrap modes are the same, they can be calculated together using
135         // more efficient vector operations
136         SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy",
137                                        fDomainName.c_str(), "xy", "zw");
138         builder->codeAppend(tc.c_str());
139     }
140     builder->codeAppend(";");
141 
142     // Look up the texture sample at the clamped coordinate location
143     builder->codeAppend("half4 inside = ");
144     builder->appendTextureLookupAndModulate(inModulateColor, sampler, "clampedCoord",
145                                             kFloat2_GrSLType);
146     builder->codeAppend(";");
147 
148     // Apply decal mode's transparency interpolation if needed
149     if (decalX || decalY) {
150         // The decal err is the max absoluate value between the clamped coordinate and the original
151         // pixel coordinate. This will then be clamped to 1.f if it's greater than the control
152         // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1.
153         if (decalX && decalY) {
154             builder->codeAppendf("half err = max(half(abs(clampedCoord.x - origCoord.x) * %s.x), "
155                                                 "half(abs(clampedCoord.y - origCoord.y) * %s.y));",
156                                  fDecalName.c_str(), fDecalName.c_str());
157         } else if (decalX) {
158             builder->codeAppendf("half err = half(abs(clampedCoord.x - origCoord.x) * %s.x);",
159                                  fDecalName.c_str());
160         } else {
161             SkASSERT(decalY);
162             builder->codeAppendf("half err = half(abs(clampedCoord.y - origCoord.y) * %s.y);",
163                                  fDecalName.c_str());
164         }
165 
166         // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering
167         // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so
168         // this becomes a step function centered at .5 away from the clamped coordinate (but the
169         // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z
170         // is set to 1 and it becomes a simple linear blend between texture and transparent.
171         builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
172                              fDecalName.c_str(), fDecalName.c_str());
173         builder->codeAppendf("%s = mix(inside, half4(0, 0, 0, 0), err);", outColor);
174     } else {
175         // A simple look up
176         builder->codeAppendf("%s = inside;", outColor);
177     }
178 }
179 
setData(const GrGLSLProgramDataManager & pdman,const GrTextureDomain & textureDomain,GrTextureProxy * proxy,const GrSamplerState & sampler)180 void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
181                                         const GrTextureDomain& textureDomain,
182                                         GrTextureProxy* proxy,
183                                         const GrSamplerState& sampler) {
184     GrTexture* tex = proxy->peekTexture();
185     SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
186     if (kIgnore_Mode != textureDomain.modeX() || kIgnore_Mode != textureDomain.modeY()) {
187         bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
188                              textureDomain.modeY() == kDecal_Mode;
189 
190         // If the texture is using nearest filtering, then the decal filter weight should step from
191         // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
192         // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
193         // texture and transparent.
194         SkScalar decalFilterWeight = sampler.filter() == GrSamplerState::Filter::kNearest ?
195                 SK_ScalarHalf : 1.0f;
196         SkScalar wInv, hInv, h;
197         if (proxy->textureType() == GrTextureType::kRectangle) {
198             wInv = hInv = 1.f;
199             h = tex->height();
200 
201             // Don't do any scaling by texture size for decal filter rate, it's already in pixels
202             if (sendDecalData) {
203                 pdman.set3f(fDecalUni, 1.f, 1.f, decalFilterWeight);
204             }
205         } else {
206             wInv = SK_Scalar1 / tex->width();
207             hInv = SK_Scalar1 / tex->height();
208             h = 1.f;
209 
210             if (sendDecalData) {
211                 pdman.set3f(fDecalUni, tex->width(), tex->height(), decalFilterWeight);
212             }
213         }
214 
215         float values[kPrevDomainCount] = {
216             SkScalarToFloat(textureDomain.domain().fLeft * wInv),
217             SkScalarToFloat(textureDomain.domain().fTop * hInv),
218             SkScalarToFloat(textureDomain.domain().fRight * wInv),
219             SkScalarToFloat(textureDomain.domain().fBottom * hInv)
220         };
221 
222         if (proxy->textureType() == GrTextureType::kRectangle) {
223             SkASSERT(values[0] >= 0.0f && values[0] <= proxy->width());
224             SkASSERT(values[1] >= 0.0f && values[1] <= proxy->height());
225             SkASSERT(values[2] >= 0.0f && values[2] <= proxy->width());
226             SkASSERT(values[3] >= 0.0f && values[3] <= proxy->height());
227         } else {
228             SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f);
229             SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f);
230             SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f);
231             SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f);
232         }
233 
234         // vertical flip if necessary
235         if (kBottomLeft_GrSurfaceOrigin == proxy->origin()) {
236             values[1] = h - values[1];
237             values[3] = h - values[3];
238 
239             // The top and bottom were just flipped, so correct the ordering
240             // of elements so that values = (l, t, r, b).
241             using std::swap;
242             swap(values[1], values[3]);
243         }
244         if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) {
245             pdman.set4fv(fDomainUni, 1, values);
246             memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float));
247         }
248     }
249 }
250 
251 ///////////////////////////////////////////////////////////////////////////////
252 
Make(sk_sp<GrTextureProxy> proxy,GrColorType srcColorType,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode mode,GrSamplerState::Filter filterMode)253 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
254         sk_sp<GrTextureProxy> proxy,
255         GrColorType srcColorType,
256         const SkMatrix& matrix,
257         const SkRect& domain,
258         GrTextureDomain::Mode mode,
259         GrSamplerState::Filter filterMode) {
260     return Make(std::move(proxy), srcColorType, matrix, domain, mode, mode,
261                 GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode));
262 }
263 
Make(sk_sp<GrTextureProxy> proxy,GrColorType srcColorType,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode modeX,GrTextureDomain::Mode modeY,const GrSamplerState & sampler)264 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
265         sk_sp<GrTextureProxy> proxy,
266         GrColorType srcColorType,
267         const SkMatrix& matrix,
268         const SkRect& domain,
269         GrTextureDomain::Mode modeX,
270         GrTextureDomain::Mode modeY,
271         const GrSamplerState& sampler) {
272     // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
273     // entirely Technically, we could also use the simple texture effect if the domain modes agree
274     // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for
275     // calling code to detect these cases and handle it themselves.
276     return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
277             std::move(proxy), srcColorType, matrix, domain, modeX, modeY, sampler));
278 }
279 
GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,GrColorType srcColorType,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode modeX,GrTextureDomain::Mode modeY,const GrSamplerState & sampler)280 GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,
281                                              GrColorType srcColorType,
282                                              const SkMatrix& matrix,
283                                              const SkRect& domain,
284                                              GrTextureDomain::Mode modeX,
285                                              GrTextureDomain::Mode modeY,
286                                              const GrSamplerState& sampler)
287         : INHERITED(kGrTextureDomainEffect_ClassID,
288                     ModulateForSamplerOptFlags(srcColorType,
289                             GrTextureDomain::IsDecalSampled(sampler, modeX, modeY)))
290         , fCoordTransform(matrix, proxy.get())
291         , fTextureDomain(proxy.get(), domain, modeX, modeY)
292         , fTextureSampler(std::move(proxy), sampler) {
293     SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) ||
294              sampler.filter() == GrSamplerState::Filter::kNearest);
295     this->addCoordTransform(&fCoordTransform);
296     this->setTextureSamplerCnt(1);
297 }
298 
GrTextureDomainEffect(const GrTextureDomainEffect & that)299 GrTextureDomainEffect::GrTextureDomainEffect(const GrTextureDomainEffect& that)
300         : INHERITED(kGrTextureDomainEffect_ClassID, that.optimizationFlags())
301         , fCoordTransform(that.fCoordTransform)
302         , fTextureDomain(that.fTextureDomain)
303         , fTextureSampler(that.fTextureSampler) {
304     this->addCoordTransform(&fCoordTransform);
305     this->setTextureSamplerCnt(1);
306 }
307 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const308 void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
309                                                   GrProcessorKeyBuilder* b) const {
310     b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
311 }
312 
onCreateGLSLInstance() const313 GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const  {
314     class GLSLProcessor : public GrGLSLFragmentProcessor {
315     public:
316         void emitCode(EmitArgs& args) override {
317             const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
318             const GrTextureDomain& domain = tde.fTextureDomain;
319 
320             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
321             SkString coords2D =
322                               fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
323 
324             fGLDomain.sampleTexture(fragBuilder,
325                                     args.fUniformHandler,
326                                     args.fShaderCaps,
327                                     domain,
328                                     args.fOutputColor,
329                                     coords2D,
330                                     args.fTexSamplers[0],
331                                     args.fInputColor);
332         }
333 
334     protected:
335         void onSetData(const GrGLSLProgramDataManager& pdman,
336                        const GrFragmentProcessor& fp) override {
337             const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
338             const GrTextureDomain& domain = tde.fTextureDomain;
339             GrTextureProxy* proxy = tde.textureSampler(0).proxy();
340 
341             fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState());
342         }
343 
344     private:
345         GrTextureDomain::GLDomain         fGLDomain;
346     };
347 
348     return new GLSLProcessor;
349 }
350 
onIsEqual(const GrFragmentProcessor & sBase) const351 bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
352     const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
353     return this->fTextureDomain == s.fTextureDomain;
354 }
355 
356 ///////////////////////////////////////////////////////////////////////////////
357 
358 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
359 
360 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)361 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
362     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
363                                         : GrProcessorUnitTest::kAlphaTextureIdx;
364     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
365     SkRect domain;
366     domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
367     domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
368     domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
369     domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
370     GrTextureDomain::Mode modeX =
371         (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
372     GrTextureDomain::Mode modeY =
373         (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
374     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
375     bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ?
376             d->fRandom->nextBool() : false;
377     return GrTextureDomainEffect::Make(
378             std::move(proxy),
379             d->textureProxyColorType(texIdx),
380             matrix,
381             domain,
382             modeX,
383             modeY,
384             GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ?
385                            GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest));
386 }
387 #endif
388 
389 ///////////////////////////////////////////////////////////////////////////////
Make(sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)390 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
391         sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
392     return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
393             std::move(proxy), subset, deviceSpaceOffset));
394 }
395 
GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)396 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
397         sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
398         : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
399                     kCompatibleWithCoverageAsAlpha_OptimizationFlag)
400         , fTextureSampler(proxy, GrSamplerState::ClampNearest())
401         , fTextureDomain(proxy.get(),
402                          GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
403                          GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
404     this->setTextureSamplerCnt(1);
405     fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
406     fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
407 }
408 
GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor & that)409 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
410         const GrDeviceSpaceTextureDecalFragmentProcessor& that)
411         : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
412                     kCompatibleWithCoverageAsAlpha_OptimizationFlag)
413         , fTextureSampler(that.fTextureSampler)
414         , fTextureDomain(that.fTextureDomain)
415         , fDeviceSpaceOffset(that.fDeviceSpaceOffset) {
416     this->setTextureSamplerCnt(1);
417 }
418 
clone() const419 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const {
420     return std::unique_ptr<GrFragmentProcessor>(
421             new GrDeviceSpaceTextureDecalFragmentProcessor(*this));
422 }
423 
onCreateGLSLInstance() const424 GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const  {
425     class GLSLProcessor : public GrGLSLFragmentProcessor {
426     public:
427         void emitCode(EmitArgs& args) override {
428             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
429                     args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
430             const char* scaleAndTranslateName;
431             fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
432                                                                      kHalf4_GrSLType,
433                                                                      "scaleAndTranslate",
434                                                                      &scaleAndTranslateName);
435             args.fFragBuilder->codeAppendf("half2 coords = half2(sk_FragCoord.xy * %s.xy + %s.zw);",
436                                            scaleAndTranslateName, scaleAndTranslateName);
437             fGLDomain.sampleTexture(args.fFragBuilder,
438                                     args.fUniformHandler,
439                                     args.fShaderCaps,
440                                     dstdfp.fTextureDomain,
441                                     args.fOutputColor,
442                                     SkString("coords"),
443                                     args.fTexSamplers[0],
444                                     args.fInputColor);
445         }
446 
447     protected:
448         void onSetData(const GrGLSLProgramDataManager& pdman,
449                        const GrFragmentProcessor& fp) override {
450             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
451                     fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
452             GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy();
453             GrTexture* texture = proxy->peekTexture();
454 
455             fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
456                               dstdfp.textureSampler(0).samplerState());
457             float iw = 1.f / texture->width();
458             float ih = 1.f / texture->height();
459             float scaleAndTransData[4] = {
460                 iw, ih,
461                 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
462             };
463             if (proxy->origin() == kBottomLeft_GrSurfaceOrigin) {
464                 scaleAndTransData[1] = -scaleAndTransData[1];
465                 scaleAndTransData[3] = 1 - scaleAndTransData[3];
466             }
467             pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
468         }
469 
470     private:
471         GrTextureDomain::GLDomain   fGLDomain;
472         UniformHandle               fScaleAndTranslateUni;
473     };
474 
475     return new GLSLProcessor;
476 }
477 
onIsEqual(const GrFragmentProcessor & fp) const478 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
479     const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
480             fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
481     return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() ==
482                    fTextureSampler.proxy()->underlyingUniqueID() &&
483            dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
484            dstdfp.fTextureDomain == fTextureDomain;
485 }
486 
487 ///////////////////////////////////////////////////////////////////////////////
488 
489 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
490 
491 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)492 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
493         GrProcessorTestData* d) {
494     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
495                                         : GrProcessorUnitTest::kAlphaTextureIdx;
496     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
497     SkIRect subset;
498     subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
499     subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
500     subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
501     subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
502     SkIPoint pt;
503     pt.fX = d->fRandom->nextULessThan(2048);
504     pt.fY = d->fRandom->nextULessThan(2048);
505     return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt);
506 }
507 #endif
508