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