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 15GrTextureDomain::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 42void 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 114void 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 143class GrGLTextureDomainEffect : public GrGLEffect { 144public: 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 159private: 160 GrTextureDomain::GLDomain fGLDomain; 161 typedef GrGLEffect INHERITED; 162}; 163 164GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory, 165 const GrDrawEffect&) 166 : INHERITED(factory) { 167} 168 169void 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 183void 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 190GrGLEffect::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 199GrEffectRef* 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 222GrTextureDomainEffect::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 232GrTextureDomainEffect::~GrTextureDomainEffect() { 233 234} 235 236const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const { 237 return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance(); 238} 239 240bool 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 246void 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 256GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect); 257 258GrEffectRef* 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} 281