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 "GrTextureDomain.h"
9
10 #include "GrProxyProvider.h"
11 #include "GrShaderCaps.h"
12 #include "GrSimpleTextureEffect.h"
13 #include "GrSurfaceProxyPriv.h"
14 #include "GrTexture.h"
15 #include "SkFloatingPoint.h"
16 #include "glsl/GrGLSLFragmentProcessor.h"
17 #include "glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "glsl/GrGLSLProgramDataManager.h"
19 #include "glsl/GrGLSLShaderBuilder.h"
20 #include "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(abs(clampedCoord.x - origCoord.x) * %s.x, "
155 "abs(clampedCoord.y - origCoord.y) * %s.y);",
156 fDecalName.c_str(), fDecalName.c_str());
157 } else if (decalX) {
158 builder->codeAppendf("half err = abs(clampedCoord.x - origCoord.x) * %s.x;",
159 fDecalName.c_str());
160 } else {
161 SkASSERT(decalY);
162 builder->codeAppendf("half err = 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->height());
224 SkASSERT(values[1] >= 0.0f && values[1] <= proxy->height());
225 SkASSERT(values[2] >= 0.0f && values[2] <= proxy->height());
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,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 const SkMatrix& matrix,
256 const SkRect& domain,
257 GrTextureDomain::Mode mode,
258 GrSamplerState::Filter filterMode) {
259 return Make(std::move(proxy), matrix, domain, mode, mode,
260 GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode));
261 }
262
Make(sk_sp<GrTextureProxy> proxy,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode modeX,GrTextureDomain::Mode modeY,const GrSamplerState & sampler)263 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
264 sk_sp<GrTextureProxy> proxy,
265 const SkMatrix& matrix,
266 const SkRect& domain,
267 GrTextureDomain::Mode modeX,
268 GrTextureDomain::Mode modeY,
269 const GrSamplerState& sampler) {
270 // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
271 // entirely Technically, we could also use the simple texture effect if the domain modes agree
272 // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for
273 // calling code to detect these cases and handle it themselves.
274 return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
275 std::move(proxy), matrix, domain, modeX, modeY, sampler));
276 }
277
GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode modeX,GrTextureDomain::Mode modeY,const GrSamplerState & sampler)278 GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,
279 const SkMatrix& matrix,
280 const SkRect& domain,
281 GrTextureDomain::Mode modeX,
282 GrTextureDomain::Mode modeY,
283 const GrSamplerState& sampler)
284 : INHERITED(kGrTextureDomainEffect_ClassID,
285 ModulateForSamplerOptFlags(proxy->config(),
286 GrTextureDomain::IsDecalSampled(sampler, modeX, modeY)))
287 , fCoordTransform(matrix, proxy.get())
288 , fTextureDomain(proxy.get(), domain, modeX, modeY)
289 , fTextureSampler(std::move(proxy), sampler) {
290 SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) ||
291 sampler.filter() == GrSamplerState::Filter::kNearest);
292 this->addCoordTransform(&fCoordTransform);
293 this->setTextureSamplerCnt(1);
294 }
295
GrTextureDomainEffect(const GrTextureDomainEffect & that)296 GrTextureDomainEffect::GrTextureDomainEffect(const GrTextureDomainEffect& that)
297 : INHERITED(kGrTextureDomainEffect_ClassID, that.optimizationFlags())
298 , fCoordTransform(that.fCoordTransform)
299 , fTextureDomain(that.fTextureDomain)
300 , fTextureSampler(that.fTextureSampler) {
301 this->addCoordTransform(&fCoordTransform);
302 this->setTextureSamplerCnt(1);
303 }
304
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const305 void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
306 GrProcessorKeyBuilder* b) const {
307 b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
308 }
309
onCreateGLSLInstance() const310 GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const {
311 class GLSLProcessor : public GrGLSLFragmentProcessor {
312 public:
313 void emitCode(EmitArgs& args) override {
314 const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
315 const GrTextureDomain& domain = tde.fTextureDomain;
316
317 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
318 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
319
320 fGLDomain.sampleTexture(fragBuilder,
321 args.fUniformHandler,
322 args.fShaderCaps,
323 domain,
324 args.fOutputColor,
325 coords2D,
326 args.fTexSamplers[0],
327 args.fInputColor);
328 }
329
330 protected:
331 void onSetData(const GrGLSLProgramDataManager& pdman,
332 const GrFragmentProcessor& fp) override {
333 const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
334 const GrTextureDomain& domain = tde.fTextureDomain;
335 GrTextureProxy* proxy = tde.textureSampler(0).proxy();
336
337 fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState());
338 }
339
340 private:
341 GrTextureDomain::GLDomain fGLDomain;
342 };
343
344 return new GLSLProcessor;
345 }
346
onIsEqual(const GrFragmentProcessor & sBase) const347 bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
348 const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
349 return this->fTextureDomain == s.fTextureDomain;
350 }
351
352 ///////////////////////////////////////////////////////////////////////////////
353
354 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
355
356 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)357 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
358 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
359 : GrProcessorUnitTest::kAlphaTextureIdx;
360 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
361 SkRect domain;
362 domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
363 domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
364 domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
365 domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
366 GrTextureDomain::Mode modeX =
367 (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
368 GrTextureDomain::Mode modeY =
369 (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
370 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
371 bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ?
372 d->fRandom->nextBool() : false;
373 return GrTextureDomainEffect::Make(
374 std::move(proxy),
375 matrix,
376 domain,
377 modeX,
378 modeY,
379 GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ?
380 GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest));
381 }
382 #endif
383
384 ///////////////////////////////////////////////////////////////////////////////
Make(sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)385 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
386 sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
387 return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
388 std::move(proxy), subset, deviceSpaceOffset));
389 }
390
GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)391 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
392 sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
393 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
394 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
395 , fTextureSampler(proxy, GrSamplerState::ClampNearest())
396 , fTextureDomain(proxy.get(),
397 GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
398 GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
399 this->setTextureSamplerCnt(1);
400 fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
401 fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
402 }
403
GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor & that)404 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
405 const GrDeviceSpaceTextureDecalFragmentProcessor& that)
406 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
407 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
408 , fTextureSampler(that.fTextureSampler)
409 , fTextureDomain(that.fTextureDomain)
410 , fDeviceSpaceOffset(that.fDeviceSpaceOffset) {
411 this->setTextureSamplerCnt(1);
412 }
413
clone() const414 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const {
415 return std::unique_ptr<GrFragmentProcessor>(
416 new GrDeviceSpaceTextureDecalFragmentProcessor(*this));
417 }
418
onCreateGLSLInstance() const419 GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const {
420 class GLSLProcessor : public GrGLSLFragmentProcessor {
421 public:
422 void emitCode(EmitArgs& args) override {
423 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
424 args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
425 const char* scaleAndTranslateName;
426 fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
427 kHalf4_GrSLType,
428 "scaleAndTranslate",
429 &scaleAndTranslateName);
430 args.fFragBuilder->codeAppendf("half2 coords = sk_FragCoord.xy * %s.xy + %s.zw;",
431 scaleAndTranslateName, scaleAndTranslateName);
432 fGLDomain.sampleTexture(args.fFragBuilder,
433 args.fUniformHandler,
434 args.fShaderCaps,
435 dstdfp.fTextureDomain,
436 args.fOutputColor,
437 SkString("coords"),
438 args.fTexSamplers[0],
439 args.fInputColor);
440 }
441
442 protected:
443 void onSetData(const GrGLSLProgramDataManager& pdman,
444 const GrFragmentProcessor& fp) override {
445 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
446 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
447 GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy();
448 GrTexture* texture = proxy->peekTexture();
449
450 fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
451 dstdfp.textureSampler(0).samplerState());
452 float iw = 1.f / texture->width();
453 float ih = 1.f / texture->height();
454 float scaleAndTransData[4] = {
455 iw, ih,
456 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
457 };
458 if (proxy->origin() == kBottomLeft_GrSurfaceOrigin) {
459 scaleAndTransData[1] = -scaleAndTransData[1];
460 scaleAndTransData[3] = 1 - scaleAndTransData[3];
461 }
462 pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
463 }
464
465 private:
466 GrTextureDomain::GLDomain fGLDomain;
467 UniformHandle fScaleAndTranslateUni;
468 };
469
470 return new GLSLProcessor;
471 }
472
onIsEqual(const GrFragmentProcessor & fp) const473 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
474 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
475 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
476 return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() ==
477 fTextureSampler.proxy()->underlyingUniqueID() &&
478 dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
479 dstdfp.fTextureDomain == fTextureDomain;
480 }
481
482 ///////////////////////////////////////////////////////////////////////////////
483
484 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
485
486 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)487 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
488 GrProcessorTestData* d) {
489 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
490 : GrProcessorUnitTest::kAlphaTextureIdx;
491 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
492 SkIRect subset;
493 subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
494 subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
495 subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
496 subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
497 SkIPoint pt;
498 pt.fX = d->fRandom->nextULessThan(2048);
499 pt.fY = d->fRandom->nextULessThan(2048);
500 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt);
501 }
502 #endif
503