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