Line data Source code
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 :
22 0 : static bool can_ignore_rect(GrTextureProxy* proxy, const SkRect& domain) {
23 0 : if (GrResourceProvider::IsFunctionallyExact(proxy)) {
24 0 : const SkIRect kFullRect = SkIRect::MakeWH(proxy->width(), proxy->height());
25 :
26 0 : return domain.contains(kFullRect);
27 : }
28 :
29 0 : return false;
30 : }
31 :
32 0 : 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 0 : const SkIRect kFullRect = SkIRect::MakeWH(tex->width(), tex->height());
38 :
39 0 : return domain.contains(kFullRect);
40 : }
41 :
42 0 : GrTextureDomain::GrTextureDomain(GrTexture* tex, const SkRect& domain, Mode mode, int index)
43 : : fMode(mode)
44 0 : , fIndex(index) {
45 :
46 0 : if (kIgnore_Mode == fMode) {
47 0 : return;
48 : }
49 :
50 0 : if (kClamp_Mode == mode && can_ignore_rect(tex, domain)) {
51 0 : fMode = kIgnore_Mode;
52 0 : return;
53 : }
54 :
55 0 : 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 0 : SkASSERT(domain.fLeft <= domain.fRight);
61 0 : SkASSERT(domain.fTop <= domain.fBottom);
62 0 : fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
63 0 : fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
64 0 : fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
65 0 : fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
66 0 : SkASSERT(fDomain.fLeft <= fDomain.fRight);
67 0 : SkASSERT(fDomain.fTop <= fDomain.fBottom);
68 : }
69 :
70 0 : GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode mode, int index)
71 : : fMode(mode)
72 0 : , fIndex(index) {
73 :
74 0 : if (kIgnore_Mode == fMode) {
75 0 : return;
76 : }
77 :
78 0 : if (kClamp_Mode == mode && can_ignore_rect(proxy, domain)) {
79 0 : fMode = kIgnore_Mode;
80 0 : return;
81 : }
82 :
83 0 : 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 0 : SkASSERT(domain.fLeft <= domain.fRight);
89 0 : SkASSERT(domain.fTop <= domain.fBottom);
90 0 : fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
91 0 : fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
92 0 : fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
93 0 : fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
94 0 : SkASSERT(fDomain.fLeft <= fDomain.fRight);
95 0 : SkASSERT(fDomain.fTop <= fDomain.fBottom);
96 : }
97 :
98 : //////////////////////////////////////////////////////////////////////////////
99 :
100 0 : 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 0 : SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode);
110 0 : SkDEBUGCODE(fMode = textureDomain.mode();)
111 :
112 0 : if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) {
113 : const char* name;
114 0 : SkString uniName("TexDom");
115 0 : if (textureDomain.fIndex >= 0) {
116 0 : uniName.appendS32(textureDomain.fIndex);
117 : }
118 : fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
119 : kVec4f_GrSLType, kDefault_GrSLPrecision,
120 0 : uniName.c_str(), &name);
121 0 : fDomainName = name;
122 : }
123 :
124 0 : switch (textureDomain.mode()) {
125 : case kIgnore_Mode: {
126 0 : builder->codeAppendf("%s = ", outColor);
127 0 : builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
128 0 : kVec2f_GrSLType, colorXformHelper);
129 0 : builder->codeAppend(";");
130 0 : break;
131 : }
132 : case kClamp_Mode: {
133 0 : SkString clampedCoords;
134 0 : clampedCoords.appendf("clamp(%s, %s.xy, %s.zw)",
135 0 : inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
136 :
137 0 : builder->codeAppendf("%s = ", outColor);
138 0 : builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
139 0 : kVec2f_GrSLType, colorXformHelper);
140 0 : builder->codeAppend(";");
141 0 : break;
142 : }
143 : case kDecal_Mode: {
144 : // Add a block since we're going to declare variables.
145 0 : GrGLSLShaderBuilder::ShaderBlock block(builder);
146 :
147 0 : const char* domain = fDomainName.c_str();
148 0 : 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 0 : builder->codeAppend("vec4 outside = vec4(0.0, 0.0, 0.0, 0.0);");
156 0 : builder->codeAppend("vec4 inside = ");
157 0 : builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
158 0 : kVec2f_GrSLType, colorXformHelper);
159 0 : builder->codeAppend(";");
160 :
161 0 : builder->codeAppendf("highp float x = (%s).x;", inCoords.c_str());
162 0 : 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 0 : domain, domain, domain);
166 : builder->codeAppendf("y = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);",
167 0 : domain, domain, domain);
168 0 : builder->codeAppend("float blend = step(1.0, max(x, y));");
169 0 : builder->codeAppendf("%s = mix(inside, outside, blend);", outColor);
170 : } else {
171 0 : builder->codeAppend("bvec4 outside;\n");
172 0 : builder->codeAppendf("outside.xy = lessThan(%s, %s.xy);", inCoords.c_str(),
173 0 : domain);
174 0 : builder->codeAppendf("outside.zw = greaterThan(%s, %s.zw);", inCoords.c_str(),
175 0 : domain);
176 : builder->codeAppendf("%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ",
177 0 : outColor);
178 0 : builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
179 0 : kVec2f_GrSLType, colorXformHelper);
180 0 : builder->codeAppend(";");
181 : }
182 0 : break;
183 : }
184 : case kRepeat_Mode: {
185 0 : SkString clampedCoords;
186 0 : clampedCoords.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy",
187 : inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(),
188 0 : fDomainName.c_str(), fDomainName.c_str());
189 :
190 0 : builder->codeAppendf("%s = ", outColor);
191 0 : builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
192 0 : kVec2f_GrSLType, colorXformHelper);
193 0 : builder->codeAppend(";");
194 0 : break;
195 : }
196 : }
197 0 : }
198 :
199 0 : void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
200 : const GrTextureDomain& textureDomain,
201 : GrTexture* tex) {
202 0 : SkASSERT(textureDomain.mode() == fMode);
203 0 : if (kIgnore_Mode != textureDomain.mode()) {
204 0 : SkScalar wInv = SK_Scalar1 / tex->width();
205 0 : SkScalar hInv = SK_Scalar1 / tex->height();
206 :
207 : float values[kPrevDomainCount] = {
208 0 : SkScalarToFloat(textureDomain.domain().fLeft * wInv),
209 0 : SkScalarToFloat(textureDomain.domain().fTop * hInv),
210 0 : SkScalarToFloat(textureDomain.domain().fRight * wInv),
211 0 : SkScalarToFloat(textureDomain.domain().fBottom * hInv)
212 0 : };
213 :
214 0 : SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f);
215 0 : SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f);
216 0 : SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f);
217 0 : SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f);
218 :
219 : // vertical flip if necessary
220 0 : if (kBottomLeft_GrSurfaceOrigin == tex->origin()) {
221 0 : values[1] = 1.0f - values[1];
222 0 : 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 0 : SkTSwap(values[1], values[3]);
226 : }
227 0 : if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) {
228 0 : pdman.set4fv(fDomainUni, 1, values);
229 0 : memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float));
230 : }
231 : }
232 0 : }
233 :
234 : ///////////////////////////////////////////////////////////////////////////////
235 0 : inline GrFragmentProcessor::OptimizationFlags GrTextureDomainEffect::OptFlags(
236 : GrPixelConfig config, GrTextureDomain::Mode mode) {
237 0 : if (mode == GrTextureDomain::kDecal_Mode || !GrPixelConfigIsOpaque(config)) {
238 0 : return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag;
239 : } else {
240 : return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag |
241 0 : GrFragmentProcessor::kPreservesOpaqueInput_OptimizationFlag;
242 : }
243 : }
244 :
245 0 : 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 0 : if (GrTextureDomain::kIgnore_Mode == mode ||
253 0 : (GrTextureDomain::kClamp_Mode == mode && can_ignore_rect(proxy.get(), domain))) {
254 0 : return GrSimpleTextureEffect::Make(resourceProvider, std::move(proxy),
255 0 : std::move(colorSpaceXform), matrix, filterMode);
256 : } else {
257 : return sk_sp<GrFragmentProcessor>(
258 0 : new GrTextureDomainEffect(resourceProvider, std::move(proxy),
259 0 : std::move(colorSpaceXform),
260 0 : matrix, domain, mode, filterMode));
261 : }
262 : }
263 :
264 0 : 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 0 : GrSamplerParams::FilterMode filterMode)
271 0 : : GrSingleTextureEffect(resourceProvider, OptFlags(proxy->config(), mode), proxy,
272 0 : std::move(colorSpaceXform), matrix, filterMode)
273 0 : , fTextureDomain(proxy.get(), domain, mode) {
274 0 : SkASSERT(mode != GrTextureDomain::kRepeat_Mode ||
275 : filterMode == GrSamplerParams::kNone_FilterMode);
276 0 : this->initClassID<GrTextureDomainEffect>();
277 0 : }
278 :
279 0 : void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
280 : GrProcessorKeyBuilder* b) const {
281 0 : b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
282 0 : b->add32(GrColorSpaceXform::XformKey(this->colorSpaceXform()));
283 0 : }
284 :
285 0 : GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const {
286 0 : class GLSLProcessor : public GrGLSLFragmentProcessor {
287 : public:
288 0 : void emitCode(EmitArgs& args) override {
289 0 : const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
290 0 : const GrTextureDomain& domain = tde.fTextureDomain;
291 :
292 0 : GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
293 0 : SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
294 :
295 0 : fColorSpaceHelper.emitCode(args.fUniformHandler, tde.colorSpaceXform());
296 0 : fGLDomain.sampleTexture(fragBuilder,
297 : args.fUniformHandler,
298 : args.fShaderCaps,
299 : domain,
300 : args.fOutputColor,
301 : coords2D,
302 0 : args.fTexSamplers[0],
303 : args.fInputColor,
304 0 : &fColorSpaceHelper);
305 0 : }
306 :
307 : protected:
308 0 : void onSetData(const GrGLSLProgramDataManager& pdman,
309 : const GrFragmentProcessor& fp) override {
310 0 : const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
311 0 : const GrTextureDomain& domain = tde.fTextureDomain;
312 0 : fGLDomain.setData(pdman, domain, tde.textureSampler(0).texture());
313 0 : if (SkToBool(tde.colorSpaceXform())) {
314 0 : fColorSpaceHelper.setData(pdman, tde.colorSpaceXform());
315 : }
316 0 : }
317 :
318 : private:
319 : GrTextureDomain::GLDomain fGLDomain;
320 : GrGLSLColorSpaceXformHelper fColorSpaceHelper;
321 : };
322 :
323 0 : return new GLSLProcessor;
324 : }
325 :
326 0 : bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
327 0 : const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
328 0 : return this->fTextureDomain == s.fTextureDomain;
329 : }
330 :
331 : ///////////////////////////////////////////////////////////////////////////////
332 :
333 : GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
334 :
335 : #if GR_TEST_UTILS
336 0 : sk_sp<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
337 0 : int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
338 0 : : GrProcessorUnitTest::kAlphaTextureIdx;
339 0 : sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
340 : SkRect domain;
341 0 : domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
342 0 : domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
343 0 : domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
344 0 : domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
345 : GrTextureDomain::Mode mode =
346 0 : (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
347 0 : const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
348 0 : bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? d->fRandom->nextBool() : false;
349 0 : sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom);
350 : return GrTextureDomainEffect::Make(d->resourceProvider(),
351 0 : std::move(proxy),
352 0 : std::move(colorSpaceXform),
353 : matrix,
354 : domain,
355 : mode,
356 : bilerp ? GrSamplerParams::kBilerp_FilterMode
357 0 : : GrSamplerParams::kNone_FilterMode);
358 : }
359 : #endif
360 :
361 : ///////////////////////////////////////////////////////////////////////////////
362 0 : sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
363 : GrResourceProvider* resourceProvider,
364 : sk_sp<GrTextureProxy> proxy,
365 : const SkIRect& subset,
366 : const SkIPoint& deviceSpaceOffset) {
367 : return sk_sp<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
368 0 : resourceProvider, std::move(proxy), subset, deviceSpaceOffset));
369 : }
370 :
371 0 : GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
372 : GrResourceProvider* resourceProvider,
373 : sk_sp<GrTextureProxy> proxy,
374 : const SkIRect& subset,
375 0 : const SkIPoint& deviceSpaceOffset)
376 : : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag)
377 : , fTextureSampler(resourceProvider, proxy, GrSamplerParams::ClampNoFilter())
378 0 : , fTextureDomain(proxy.get(), GrTextureDomain::MakeTexelDomain(subset),
379 0 : GrTextureDomain::kDecal_Mode) {
380 0 : this->addTextureSampler(&fTextureSampler);
381 0 : fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
382 0 : fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
383 0 : this->initClassID<GrDeviceSpaceTextureDecalFragmentProcessor>();
384 0 : }
385 :
386 0 : GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const {
387 0 : class GLSLProcessor : public GrGLSLFragmentProcessor {
388 : public:
389 0 : void emitCode(EmitArgs& args) override {
390 : const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
391 0 : args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
392 : const char* scaleAndTranslateName;
393 0 : fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
394 : kVec4f_GrSLType,
395 : kDefault_GrSLPrecision,
396 : "scaleAndTranslate",
397 0 : &scaleAndTranslateName);
398 0 : args.fFragBuilder->codeAppendf("vec2 coords = sk_FragCoord.xy * %s.xy + %s.zw;",
399 0 : scaleAndTranslateName, scaleAndTranslateName);
400 0 : fGLDomain.sampleTexture(args.fFragBuilder,
401 : args.fUniformHandler,
402 : args.fShaderCaps,
403 : dstdfp.fTextureDomain,
404 : args.fOutputColor,
405 0 : SkString("coords"),
406 0 : args.fTexSamplers[0],
407 0 : args.fInputColor);
408 0 : }
409 :
410 : protected:
411 0 : void onSetData(const GrGLSLProgramDataManager& pdman,
412 : const GrFragmentProcessor& fp) override {
413 : const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
414 0 : fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
415 0 : GrTexture* texture = dstdfp.textureSampler(0).texture();
416 0 : fGLDomain.setData(pdman, dstdfp.fTextureDomain, texture);
417 0 : float iw = 1.f / texture->width();
418 0 : float ih = 1.f / texture->height();
419 : float scaleAndTransData[4] = {
420 : iw, ih,
421 0 : -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
422 0 : };
423 0 : if (texture->origin() == kBottomLeft_GrSurfaceOrigin) {
424 0 : scaleAndTransData[1] = -scaleAndTransData[1];
425 0 : scaleAndTransData[3] = 1 - scaleAndTransData[3];
426 : }
427 0 : pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
428 0 : }
429 :
430 : private:
431 : GrTextureDomain::GLDomain fGLDomain;
432 : UniformHandle fScaleAndTranslateUni;
433 : };
434 :
435 0 : return new GLSLProcessor;
436 : }
437 :
438 0 : bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
439 : const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
440 0 : fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
441 0 : return dstdfp.fTextureSampler.texture() == fTextureSampler.texture() &&
442 0 : dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
443 0 : dstdfp.fTextureDomain == fTextureDomain;
444 : }
445 :
446 : ///////////////////////////////////////////////////////////////////////////////
447 :
448 : GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
449 :
450 : #if GR_TEST_UTILS
451 0 : sk_sp<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
452 : GrProcessorTestData* d) {
453 0 : int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
454 0 : : GrProcessorUnitTest::kAlphaTextureIdx;
455 0 : sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
456 : SkIRect subset;
457 0 : subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
458 0 : subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
459 0 : subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
460 0 : subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
461 : SkIPoint pt;
462 0 : pt.fX = d->fRandom->nextULessThan(2048);
463 0 : pt.fY = d->fRandom->nextULessThan(2048);
464 : return GrDeviceSpaceTextureDecalFragmentProcessor::Make(d->resourceProvider(),
465 0 : std::move(proxy), subset, pt);
466 : }
467 : #endif
|