blob: 108ec78740821b34ef9e54a28ea0e9eb4d10b756 [file] [log] [blame]
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +00001/*
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#include "GrSimpleTextureEffect.h"
10#include "GrTBackendEffectFactory.h"
11#include "gl/GrGLEffect.h"
bsalomon848faf02014-07-11 10:01:02 -070012#include "gl/GrGLShaderBuilder.h"
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000013#include "SkFloatingPoint.h"
14
15
16GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index)
17 : fIndex(index) {
18
19 static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
commit-bot@chromium.org26632632014-03-25 15:13:18 +000020 if (domain.contains(kFullRect) && kClamp_Mode == mode) {
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000021 fMode = kIgnore_Mode;
22 } else {
23 fMode = mode;
24 }
25
26 if (fMode != kIgnore_Mode) {
27 // We don't currently handle domains that are empty or don't intersect the texture.
28 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
29 // handle rects that do not intersect the [0..1]x[0..1] rect.
30 SkASSERT(domain.fLeft <= domain.fRight);
31 SkASSERT(domain.fTop <= domain.fBottom);
32 fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
33 fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
34 fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
35 fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
36 SkASSERT(fDomain.fLeft <= fDomain.fRight);
37 SkASSERT(fDomain.fTop <= fDomain.fBottom);
38 }
39}
40
41//////////////////////////////////////////////////////////////////////////////
42
43void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder,
44 const GrTextureDomain& textureDomain,
45 const char* outColor,
46 const SkString& inCoords,
47 const GrGLEffect::TextureSampler sampler,
48 const char* inModulateColor) {
reed@google.comd7b1af62013-12-09 20:31:50 +000049 SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000050 SkDEBUGCODE(fMode = textureDomain.mode();)
51
52 if (kIgnore_Mode == textureDomain.mode()) {
53 builder->fsCodeAppendf("\t%s = ", outColor);
54 builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler,
55 inCoords.c_str());
56 builder->fsCodeAppend(";\n");
57 return;
58 }
59
60 if (!fDomainUni.isValid()) {
61 const char* name;
62 SkString uniName("TexDom");
63 if (textureDomain.fIndex >= 0) {
64 uniName.appendS32(textureDomain.fIndex);
65 }
66 fDomainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
67 kVec4f_GrSLType, uniName.c_str(), &name);
68 fDomainName = name;
69 }
70 if (kClamp_Mode == textureDomain.mode()) {
71 SkString clampedCoords;
72 clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)",
73 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
74
75 builder->fsCodeAppendf("\t%s = ", outColor);
76 builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str());
77 builder->fsCodeAppend(";\n");
78 } else {
79 SkASSERT(GrTextureDomain::kDecal_Mode == textureDomain.mode());
80 // Add a block since we're going to declare variables.
81 GrGLShaderBuilder::FSBlock block(builder);
82
83 const char* domain = fDomainName.c_str();
84 if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) {
85 // On the NexusS and GalaxyNexus, the other path (with the 'any'
86 // call) causes the compilation error "Calls to any function that
87 // may require a gradient calculation inside a conditional block
88 // may return undefined results". This appears to be an issue with
89 // the 'any' call since even the simple "result=black; if (any())
90 // result=white;" code fails to compile.
91 builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
92 builder->fsCodeAppend("\tvec4 inside = ");
93 builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str());
94 builder->fsCodeAppend(";\n");
95
96 builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n",
97 inCoords.c_str(), domain, domain, domain);
98 builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n",
99 inCoords.c_str(), domain, domain, domain);
100 builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n");
101 builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outColor);
102 } else {
103 builder->fsCodeAppend("\tbvec4 outside;\n");
104 builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(),
105 domain);
106 builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(),
107 domain);
108 builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outColor);
109 builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str());
110 builder->fsCodeAppend(";\n");
111 }
112 }
113}
114
115void GrTextureDomain::GLDomain::setData(const GrGLUniformManager& uman,
116 const GrTextureDomain& textureDomain,
117 GrSurfaceOrigin textureOrigin) {
118 SkASSERT(textureDomain.mode() == fMode);
119 if (kIgnore_Mode != textureDomain.mode()) {
120 GrGLfloat values[4] = {
121 SkScalarToFloat(textureDomain.domain().left()),
122 SkScalarToFloat(textureDomain.domain().top()),
123 SkScalarToFloat(textureDomain.domain().right()),
124 SkScalarToFloat(textureDomain.domain().bottom())
125 };
126 // vertical flip if necessary
127 if (kBottomLeft_GrSurfaceOrigin == textureOrigin) {
128 values[1] = 1.0f - values[1];
129 values[3] = 1.0f - values[3];
130 // The top and bottom were just flipped, so correct the ordering
131 // of elements so that values = (l, t, r, b).
132 SkTSwap(values[1], values[3]);
133 }
134 if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
135 uman.set4fv(fDomainUni, 1, values);
136 memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
137 }
138 }
139}
140
141
142//////////////////////////////////////////////////////////////////////////////
143
144class GrGLTextureDomainEffect : public GrGLEffect {
145public:
146 GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
147
148 virtual void emitCode(GrGLShaderBuilder*,
149 const GrDrawEffect&,
150 EffectKey,
151 const char* outputColor,
152 const char* inputColor,
153 const TransformedCoordsArray&,
154 const TextureSamplerArray&) SK_OVERRIDE;
155
156 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
157
158 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
159
160private:
161 GrTextureDomain::GLDomain fGLDomain;
162 typedef GrGLEffect INHERITED;
163};
164
165GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
166 const GrDrawEffect&)
167 : INHERITED(factory) {
168}
169
170void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder,
171 const GrDrawEffect& drawEffect,
172 EffectKey key,
173 const char* outputColor,
174 const char* inputColor,
175 const TransformedCoordsArray& coords,
176 const TextureSamplerArray& samplers) {
177 const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>();
178 const GrTextureDomain& domain = effect.textureDomain();
179
180 SkString coords2D = builder->ensureFSCoords2D(coords, 0);
181 fGLDomain.sampleTexture(builder, domain, outputColor, coords2D, samplers[0], inputColor);
182}
183
184void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman,
185 const GrDrawEffect& drawEffect) {
186 const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>();
187 const GrTextureDomain& domain = effect.textureDomain();
188 fGLDomain.setData(uman, domain, effect.texture(0)->origin());
189}
190
191GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect,
192 const GrGLCaps&) {
193 const GrTextureDomain& domain = drawEffect.castEffect<GrTextureDomainEffect>().textureDomain();
194 return GrTextureDomain::GLDomain::DomainKey(domain);
195}
196
197
198///////////////////////////////////////////////////////////////////////////////
199
bsalomon83d081a2014-07-08 09:56:10 -0700200GrEffect* GrTextureDomainEffect::Create(GrTexture* texture,
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000201 const SkMatrix& matrix,
202 const SkRect& domain,
203 GrTextureDomain::Mode mode,
204 GrTextureParams::FilterMode filterMode,
205 GrCoordSet coordSet) {
206 static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
207 if (GrTextureDomain::kIgnore_Mode == mode ||
208 (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) {
209 return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
210 } else {
211
bsalomon55fad7a2014-07-08 07:34:20 -0700212 return SkNEW_ARGS(GrTextureDomainEffect, (texture,
213 matrix,
214 domain,
215 mode,
216 filterMode,
217 coordSet));
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000218 }
219}
220
221GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
222 const SkMatrix& matrix,
223 const SkRect& domain,
224 GrTextureDomain::Mode mode,
225 GrTextureParams::FilterMode filterMode,
226 GrCoordSet coordSet)
227 : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
228 , fTextureDomain(domain, mode) {
229}
230
231GrTextureDomainEffect::~GrTextureDomainEffect() {
232
233}
234
235const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const {
236 return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
237}
238
239bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
240 const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
241 return this->hasSameTextureParamsMatrixAndSourceCoords(s) &&
242 this->fTextureDomain == s.fTextureDomain;
243}
244
245void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
246 if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper
247 *validFlags = 0;
248 } else {
249 this->updateConstantColorComponentsForModulation(color, validFlags);
250 }
251}
252
253///////////////////////////////////////////////////////////////////////////////
254
255GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect);
256
bsalomon83d081a2014-07-08 09:56:10 -0700257GrEffect* GrTextureDomainEffect::TestCreate(SkRandom* random,
258 GrContext*,
259 const GrDrawTargetCaps&,
260 GrTexture* textures[]) {
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000261 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
262 GrEffectUnitTest::kAlphaTextureIdx;
263 SkRect domain;
264 domain.fLeft = random->nextUScalar1();
265 domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
266 domain.fTop = random->nextUScalar1();
267 domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
268 GrTextureDomain::Mode mode =
269 (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount);
270 const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
271 bool bilerp = random->nextBool();
272 GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
273 return GrTextureDomainEffect::Create(textures[texIdx],
274 matrix,
275 domain,
276 mode,
277 bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
278 coords);
279}