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