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 "GrResourceProvider.h"
11 #include "GrShaderCaps.h"
12 #include "GrSimpleTextureEffect.h"
13 #include "GrSurfaceProxyPriv.h"
14 #include "SkFloatingPoint.h"
15 #include "glsl/GrGLSLColorSpaceXformHelper.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
can_ignore_rect(GrTextureProxy * proxy,const SkRect & domain)22 static bool can_ignore_rect(GrTextureProxy* proxy, const SkRect& domain) {
23 if (GrResourceProvider::IsFunctionallyExact(proxy)) {
24 const SkIRect kFullRect = SkIRect::MakeWH(proxy->width(), proxy->height());
25
26 return domain.contains(kFullRect);
27 }
28
29 return false;
30 }
31
can_ignore_rect(GrTexture * tex,const SkRect & domain)32 static bool can_ignore_rect(GrTexture* tex, const SkRect& domain) {
33 // This logic is relying on the instantiated size of 'tex'. In the deferred world it
34 // will have to change so this logic only fires for kExact texture proxies. This shouldn't
35 // change the actual behavior of Ganesh since shaders shouldn't be accessing pixels outside
36 // of the content rectangle.
37 const SkIRect kFullRect = SkIRect::MakeWH(tex->width(), tex->height());
38
39 return domain.contains(kFullRect);
40 }
41
GrTextureDomain(GrTexture * tex,const SkRect & domain,Mode mode,int index)42 GrTextureDomain::GrTextureDomain(GrTexture* tex, const SkRect& domain, Mode mode, int index)
43 : fMode(mode)
44 , fIndex(index) {
45
46 if (kIgnore_Mode == fMode) {
47 return;
48 }
49
50 if (kClamp_Mode == mode && can_ignore_rect(tex, domain)) {
51 fMode = kIgnore_Mode;
52 return;
53 }
54
55 const SkRect kFullRect = SkRect::MakeIWH(tex->width(), tex->height());
56
57 // We don't currently handle domains that are empty or don't intersect the texture.
58 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
59 // handle rects that do not intersect the [0..1]x[0..1] rect.
60 SkASSERT(domain.fLeft <= domain.fRight);
61 SkASSERT(domain.fTop <= domain.fBottom);
62 fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
63 fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
64 fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
65 fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
66 SkASSERT(fDomain.fLeft <= fDomain.fRight);
67 SkASSERT(fDomain.fTop <= fDomain.fBottom);
68 }
69
GrTextureDomain(GrTextureProxy * proxy,const SkRect & domain,Mode mode,int index)70 GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode mode, int index)
71 : fMode(mode)
72 , fIndex(index) {
73
74 if (kIgnore_Mode == fMode) {
75 return;
76 }
77
78 if (kClamp_Mode == mode && can_ignore_rect(proxy, domain)) {
79 fMode = kIgnore_Mode;
80 return;
81 }
82
83 const SkRect kFullRect = SkRect::MakeIWH(proxy->width(), proxy->height());
84
85 // We don't currently handle domains that are empty or don't intersect the texture.
86 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
87 // handle rects that do not intersect the [0..1]x[0..1] rect.
88 SkASSERT(domain.fLeft <= domain.fRight);
89 SkASSERT(domain.fTop <= domain.fBottom);
90 fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
91 fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
92 fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
93 fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
94 SkASSERT(fDomain.fLeft <= fDomain.fRight);
95 SkASSERT(fDomain.fTop <= fDomain.fBottom);
96 }
97
98 //////////////////////////////////////////////////////////////////////////////
99
sampleTexture(GrGLSLShaderBuilder * builder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps * shaderCaps,const GrTextureDomain & textureDomain,const char * outColor,const SkString & inCoords,GrGLSLFragmentProcessor::SamplerHandle sampler,const char * inModulateColor,GrGLSLColorSpaceXformHelper * colorXformHelper)100 void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
101 GrGLSLUniformHandler* uniformHandler,
102 const GrShaderCaps* shaderCaps,
103 const GrTextureDomain& textureDomain,
104 const char* outColor,
105 const SkString& inCoords,
106 GrGLSLFragmentProcessor::SamplerHandle sampler,
107 const char* inModulateColor,
108 GrGLSLColorSpaceXformHelper* colorXformHelper) {
109 SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode);
110 SkDEBUGCODE(fMode = textureDomain.mode();)
111
112 if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) {
113 const char* name;
114 SkString uniName("TexDom");
115 if (textureDomain.fIndex >= 0) {
116 uniName.appendS32(textureDomain.fIndex);
117 }
118 fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
119 kVec4f_GrSLType, kDefault_GrSLPrecision,
120 uniName.c_str(), &name);
121 fDomainName = name;
122 }
123
124 switch (textureDomain.mode()) {
125 case kIgnore_Mode: {
126 builder->codeAppendf("%s = ", outColor);
127 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
128 kVec2f_GrSLType, colorXformHelper);
129 builder->codeAppend(";");
130 break;
131 }
132 case kClamp_Mode: {
133 SkString clampedCoords;
134 clampedCoords.appendf("clamp(%s, %s.xy, %s.zw)",
135 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
136
137 builder->codeAppendf("%s = ", outColor);
138 builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
139 kVec2f_GrSLType, colorXformHelper);
140 builder->codeAppend(";");
141 break;
142 }
143 case kDecal_Mode: {
144 // Add a block since we're going to declare variables.
145 GrGLSLShaderBuilder::ShaderBlock block(builder);
146
147 const char* domain = fDomainName.c_str();
148 if (!shaderCaps->canUseAnyFunctionInShader()) {
149 // On the NexusS and GalaxyNexus, the other path (with the 'any'
150 // call) causes the compilation error "Calls to any function that
151 // may require a gradient calculation inside a conditional block
152 // may return undefined results". This appears to be an issue with
153 // the 'any' call since even the simple "result=black; if (any())
154 // result=white;" code fails to compile.
155 builder->codeAppend("vec4 outside = vec4(0.0, 0.0, 0.0, 0.0);");
156 builder->codeAppend("vec4 inside = ");
157 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
158 kVec2f_GrSLType, colorXformHelper);
159 builder->codeAppend(";");
160
161 builder->codeAppendf("highp float x = (%s).x;", inCoords.c_str());
162 builder->codeAppendf("highp float y = (%s).y;", inCoords.c_str());
163
164 builder->codeAppendf("x = abs(2.0*(x - %s.x)/(%s.z - %s.x) - 1.0);",
165 domain, domain, domain);
166 builder->codeAppendf("y = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);",
167 domain, domain, domain);
168 builder->codeAppend("float blend = step(1.0, max(x, y));");
169 builder->codeAppendf("%s = mix(inside, outside, blend);", outColor);
170 } else {
171 builder->codeAppend("bvec4 outside;\n");
172 builder->codeAppendf("outside.xy = lessThan(%s, %s.xy);", inCoords.c_str(),
173 domain);
174 builder->codeAppendf("outside.zw = greaterThan(%s, %s.zw);", inCoords.c_str(),
175 domain);
176 builder->codeAppendf("%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ",
177 outColor);
178 builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
179 kVec2f_GrSLType, colorXformHelper);
180 builder->codeAppend(";");
181 }
182 break;
183 }
184 case kRepeat_Mode: {
185 SkString clampedCoords;
186 clampedCoords.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy",
187 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(),
188 fDomainName.c_str(), fDomainName.c_str());
189
190 builder->codeAppendf("%s = ", outColor);
191 builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
192 kVec2f_GrSLType, colorXformHelper);
193 builder->codeAppend(";");
194 break;
195 }
196 }
197 }
198
setData(const GrGLSLProgramDataManager & pdman,const GrTextureDomain & textureDomain,GrTexture * tex)199 void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
200 const GrTextureDomain& textureDomain,
201 GrTexture* tex) {
202 SkASSERT(textureDomain.mode() == fMode);
203 if (kIgnore_Mode != textureDomain.mode()) {
204 SkScalar wInv = SK_Scalar1 / tex->width();
205 SkScalar hInv = SK_Scalar1 / tex->height();
206
207 float values[kPrevDomainCount] = {
208 SkScalarToFloat(textureDomain.domain().fLeft * wInv),
209 SkScalarToFloat(textureDomain.domain().fTop * hInv),
210 SkScalarToFloat(textureDomain.domain().fRight * wInv),
211 SkScalarToFloat(textureDomain.domain().fBottom * hInv)
212 };
213
214 SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f);
215 SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f);
216 SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f);
217 SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f);
218
219 // vertical flip if necessary
220 if (kBottomLeft_GrSurfaceOrigin == tex->origin()) {
221 values[1] = 1.0f - values[1];
222 values[3] = 1.0f - values[3];
223 // The top and bottom were just flipped, so correct the ordering
224 // of elements so that values = (l, t, r, b).
225 SkTSwap(values[1], values[3]);
226 }
227 if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) {
228 pdman.set4fv(fDomainUni, 1, values);
229 memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float));
230 }
231 }
232 }
233
234 ///////////////////////////////////////////////////////////////////////////////
OptFlags(GrPixelConfig config,GrTextureDomain::Mode mode)235 inline GrFragmentProcessor::OptimizationFlags GrTextureDomainEffect::OptFlags(
236 GrPixelConfig config, GrTextureDomain::Mode mode) {
237 if (mode == GrTextureDomain::kDecal_Mode || !GrPixelConfigIsOpaque(config)) {
238 return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag;
239 } else {
240 return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag |
241 GrFragmentProcessor::kPreservesOpaqueInput_OptimizationFlag;
242 }
243 }
244
Make(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode mode,GrSamplerParams::FilterMode filterMode)245 sk_sp<GrFragmentProcessor> GrTextureDomainEffect::Make(GrResourceProvider* resourceProvider,
246 sk_sp<GrTextureProxy> proxy,
247 sk_sp<GrColorSpaceXform> colorSpaceXform,
248 const SkMatrix& matrix,
249 const SkRect& domain,
250 GrTextureDomain::Mode mode,
251 GrSamplerParams::FilterMode filterMode) {
252 if (GrTextureDomain::kIgnore_Mode == mode ||
253 (GrTextureDomain::kClamp_Mode == mode && can_ignore_rect(proxy.get(), domain))) {
254 return GrSimpleTextureEffect::Make(resourceProvider, std::move(proxy),
255 std::move(colorSpaceXform), matrix, filterMode);
256 } else {
257 return sk_sp<GrFragmentProcessor>(
258 new GrTextureDomainEffect(resourceProvider, std::move(proxy),
259 std::move(colorSpaceXform),
260 matrix, domain, mode, filterMode));
261 }
262 }
263
GrTextureDomainEffect(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & matrix,const SkRect & domain,GrTextureDomain::Mode mode,GrSamplerParams::FilterMode filterMode)264 GrTextureDomainEffect::GrTextureDomainEffect(GrResourceProvider* resourceProvider,
265 sk_sp<GrTextureProxy> proxy,
266 sk_sp<GrColorSpaceXform> colorSpaceXform,
267 const SkMatrix& matrix,
268 const SkRect& domain,
269 GrTextureDomain::Mode mode,
270 GrSamplerParams::FilterMode filterMode)
271 : GrSingleTextureEffect(resourceProvider, OptFlags(proxy->config(), mode), proxy,
272 std::move(colorSpaceXform), matrix, filterMode)
273 , fTextureDomain(proxy.get(), domain, mode) {
274 SkASSERT(mode != GrTextureDomain::kRepeat_Mode ||
275 filterMode == GrSamplerParams::kNone_FilterMode);
276 this->initClassID<GrTextureDomainEffect>();
277 }
278
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const279 void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
280 GrProcessorKeyBuilder* b) const {
281 b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
282 b->add32(GrColorSpaceXform::XformKey(this->colorSpaceXform()));
283 }
284
onCreateGLSLInstance() const285 GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const {
286 class GLSLProcessor : public GrGLSLFragmentProcessor {
287 public:
288 void emitCode(EmitArgs& args) override {
289 const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
290 const GrTextureDomain& domain = tde.fTextureDomain;
291
292 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
293 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
294
295 fColorSpaceHelper.emitCode(args.fUniformHandler, tde.colorSpaceXform());
296 fGLDomain.sampleTexture(fragBuilder,
297 args.fUniformHandler,
298 args.fShaderCaps,
299 domain,
300 args.fOutputColor,
301 coords2D,
302 args.fTexSamplers[0],
303 args.fInputColor,
304 &fColorSpaceHelper);
305 }
306
307 protected:
308 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& fp) override {
309 const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
310 const GrTextureDomain& domain = tde.fTextureDomain;
311 fGLDomain.setData(pdman, domain, tde.textureSampler(0).texture());
312 if (SkToBool(tde.colorSpaceXform())) {
313 fColorSpaceHelper.setData(pdman, tde.colorSpaceXform());
314 }
315 }
316
317 private:
318 GrTextureDomain::GLDomain fGLDomain;
319 GrGLSLColorSpaceXformHelper fColorSpaceHelper;
320 };
321
322 return new GLSLProcessor;
323 }
324
onIsEqual(const GrFragmentProcessor & sBase) const325 bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
326 const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
327 return this->fTextureDomain == s.fTextureDomain;
328 }
329
330 ///////////////////////////////////////////////////////////////////////////////
331
332 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
333
334 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)335 sk_sp<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
336 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
337 : GrProcessorUnitTest::kAlphaTextureIdx;
338 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
339 SkRect domain;
340 domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
341 domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
342 domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
343 domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
344 GrTextureDomain::Mode mode =
345 (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
346 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
347 bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? d->fRandom->nextBool() : false;
348 sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom);
349 return GrTextureDomainEffect::Make(d->resourceProvider(),
350 std::move(proxy),
351 std::move(colorSpaceXform),
352 matrix,
353 domain,
354 mode,
355 bilerp ? GrSamplerParams::kBilerp_FilterMode
356 : GrSamplerParams::kNone_FilterMode);
357 }
358 #endif
359
360 ///////////////////////////////////////////////////////////////////////////////
Make(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)361 sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
362 GrResourceProvider* resourceProvider,
363 sk_sp<GrTextureProxy> proxy,
364 const SkIRect& subset,
365 const SkIPoint& deviceSpaceOffset) {
366 return sk_sp<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
367 resourceProvider, std::move(proxy), subset, deviceSpaceOffset));
368 }
369
GrDeviceSpaceTextureDecalFragmentProcessor(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,const SkIRect & subset,const SkIPoint & deviceSpaceOffset)370 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
371 GrResourceProvider* resourceProvider,
372 sk_sp<GrTextureProxy> proxy,
373 const SkIRect& subset,
374 const SkIPoint& deviceSpaceOffset)
375 : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag)
376 , fTextureSampler(resourceProvider, proxy, GrSamplerParams::ClampNoFilter())
377 , fTextureDomain(proxy.get(), GrTextureDomain::MakeTexelDomain(subset),
378 GrTextureDomain::kDecal_Mode) {
379 this->addTextureSampler(&fTextureSampler);
380 fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
381 fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
382 this->initClassID<GrDeviceSpaceTextureDecalFragmentProcessor>();
383 }
384
onCreateGLSLInstance() const385 GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const {
386 class GLSLProcessor : public GrGLSLFragmentProcessor {
387 public:
388 void emitCode(EmitArgs& args) override {
389 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
390 args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
391 const char* scaleAndTranslateName;
392 fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
393 kVec4f_GrSLType,
394 kDefault_GrSLPrecision,
395 "scaleAndTranslate",
396 &scaleAndTranslateName);
397 args.fFragBuilder->codeAppendf("vec2 coords = sk_FragCoord.xy * %s.xy + %s.zw;",
398 scaleAndTranslateName, scaleAndTranslateName);
399 fGLDomain.sampleTexture(args.fFragBuilder,
400 args.fUniformHandler,
401 args.fShaderCaps,
402 dstdfp.fTextureDomain,
403 args.fOutputColor,
404 SkString("coords"),
405 args.fTexSamplers[0],
406 args.fInputColor);
407 }
408
409 protected:
410 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& fp) override {
411 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
412 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
413 GrTexture* texture = dstdfp.textureSampler(0).texture();
414 fGLDomain.setData(pdman, dstdfp.fTextureDomain, texture);
415 float iw = 1.f / texture->width();
416 float ih = 1.f / texture->height();
417 float scaleAndTransData[4] = {
418 iw, ih,
419 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
420 };
421 if (texture->origin() == kBottomLeft_GrSurfaceOrigin) {
422 scaleAndTransData[1] = -scaleAndTransData[1];
423 scaleAndTransData[3] = 1 - scaleAndTransData[3];
424 }
425 pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
426 }
427
428 private:
429 GrTextureDomain::GLDomain fGLDomain;
430 UniformHandle fScaleAndTranslateUni;
431 };
432
433 return new GLSLProcessor;
434 }
435
onIsEqual(const GrFragmentProcessor & fp) const436 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
437 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
438 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
439 return dstdfp.fTextureSampler.texture() == fTextureSampler.texture() &&
440 dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
441 dstdfp.fTextureDomain == fTextureDomain;
442 }
443
444 ///////////////////////////////////////////////////////////////////////////////
445
446 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
447
448 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)449 sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
450 GrProcessorTestData* d) {
451 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
452 : GrProcessorUnitTest::kAlphaTextureIdx;
453 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
454 SkIRect subset;
455 subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
456 subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
457 subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
458 subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
459 SkIPoint pt;
460 pt.fX = d->fRandom->nextULessThan(2048);
461 pt.fY = d->fRandom->nextULessThan(2048);
462 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(d->resourceProvider(),
463 std::move(proxy), subset, pt);
464 }
465 #endif
466