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 GrEffectRef&); 18 19 virtual void emitCode(GrGLShaderBuilder*, 20 const GrEffectStage&, 21 EffectKey, 22 const char* vertexCoords, 23 const char* outputColor, 24 const char* inputColor, 25 const TextureSamplerArray&) SK_OVERRIDE; 26 27 virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE; 28 29 static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&); 30 31 private: 32 GrGLUniformManager::UniformHandle fNameUni; 33 GrGLEffectMatrix fEffectMatrix; 34 GrGLfloat fPrevDomain[4]; 35 36 typedef GrGLEffect INHERITED; 37 }; 38 39 GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory, 40 const GrEffectRef&) 41 : INHERITED(factory) 42 , fNameUni(GrGLUniformManager::kInvalidUniformHandle) { 43 fPrevDomain[0] = SK_FloatNaN; 44 } 45 46 void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder, 47 const GrEffectStage& stage, 48 EffectKey key, 49 const char* vertexCoords, 50 const char* outputColor, 51 const char* inputColor, 52 const TextureSamplerArray& samplers) { 53 const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage); 54 55 const char* coords; 56 fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords); 57 const char* domain; 58 fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, 59 kVec4f_GrSLType, "TexDom", &domain); 60 if (GrTextureDomainEffect::kClamp_WrapMode == effect.wrapMode()) { 61 62 builder->fFSCode.appendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n", 63 coords, domain, domain); 64 65 builder->fFSCode.appendf("\t%s = ", outputColor); 66 builder->appendTextureLookupAndModulate(&builder->fFSCode, 67 inputColor, 68 samplers[0], 69 "clampCoord"); 70 builder->fFSCode.append(";\n"); 71 } else { 72 GrAssert(GrTextureDomainEffect::kDecal_WrapMode == effect.wrapMode()); 73 builder->fFSCode.append("\tbvec4 outside;\n"); 74 builder->fFSCode.appendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords, domain); 75 builder->fFSCode.appendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords, domain); 76 builder->fFSCode.appendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor); 77 builder->appendTextureLookupAndModulate(&builder->fFSCode, inputColor, samplers[0], coords); 78 builder->fFSCode.append(";\n"); 79 } 80 } 81 82 void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) { 83 const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage); 84 const GrRect& domain = effect.domain(); 85 86 float values[4] = { 87 SkScalarToFloat(domain.left()), 88 SkScalarToFloat(domain.top()), 89 SkScalarToFloat(domain.right()), 90 SkScalarToFloat(domain.bottom()) 91 }; 92 // vertical flip if necessary 93 if (kBottomLeft_GrSurfaceOrigin == effect.texture(0)->origin()) { 94 values[1] = 1.0f - values[1]; 95 values[3] = 1.0f - values[3]; 96 // The top and bottom were just flipped, so correct the ordering 97 // of elements so that values = (l, t, r, b). 98 SkTSwap(values[1], values[3]); 99 } 100 if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) { 101 uman.set4fv(fNameUni, 0, 1, values); 102 } 103 fEffectMatrix.setData(uman, 104 effect.getMatrix(), 105 stage.getCoordChangeMatrix(), 106 effect.texture(0)); 107 } 108 109 GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) { 110 const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage); 111 EffectKey key = effect.wrapMode(); 112 key <<= GrGLEffectMatrix::kKeyBits; 113 EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(), 114 stage.getCoordChangeMatrix(), 115 effect.texture(0)); 116 return key | matrixKey; 117 } 118 119 120 /////////////////////////////////////////////////////////////////////////////// 121 122 GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture, 123 const SkMatrix& matrix, 124 const GrRect& domain, 125 WrapMode wrapMode, 126 bool bilerp) { 127 static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; 128 if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) { 129 return GrSimpleTextureEffect::Create(texture, matrix, bilerp); 130 } else { 131 SkRect clippedDomain; 132 // We don't currently handle domains that are empty or don't intersect the texture. 133 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not 134 // handle rects that do not intersect the [0..1]x[0..1] rect. 135 GrAssert(domain.fLeft <= domain.fRight); 136 GrAssert(domain.fTop <= domain.fBottom); 137 clippedDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft); 138 clippedDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight); 139 clippedDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop); 140 clippedDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom); 141 GrAssert(clippedDomain.fLeft <= clippedDomain.fRight); 142 GrAssert(clippedDomain.fTop <= clippedDomain.fBottom); 143 144 AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture, 145 matrix, 146 clippedDomain, 147 wrapMode, 148 bilerp))); 149 return CreateEffectRef(effect); 150 151 } 152 } 153 154 GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture, 155 const SkMatrix& matrix, 156 const GrRect& domain, 157 WrapMode wrapMode, 158 bool bilerp) 159 : GrSingleTextureEffect(texture, matrix, bilerp) 160 , fWrapMode(wrapMode) 161 , fTextureDomain(domain) { 162 } 163 164 GrTextureDomainEffect::~GrTextureDomainEffect() { 165 166 } 167 168 const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const { 169 return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance(); 170 } 171 172 bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const { 173 const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase); 174 return this->hasSameTextureParamsAndMatrix(s) && this->fTextureDomain == s.fTextureDomain; 175 } 176 177 void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 178 if (kDecal_WrapMode == fWrapMode) { 179 *validFlags = 0; 180 } else { 181 this->updateConstantColorComponentsForModulation(color, validFlags); 182 } 183 } 184 185 /////////////////////////////////////////////////////////////////////////////// 186 187 GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect); 188 189 GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random, 190 GrContext* context, 191 GrTexture* textures[]) { 192 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : 193 GrEffectUnitTest::kAlphaTextureIdx; 194 GrRect domain; 195 domain.fLeft = random->nextUScalar1(); 196 domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1); 197 domain.fTop = random->nextUScalar1(); 198 domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1); 199 WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode; 200 const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random); 201 return GrTextureDomainEffect::Create(textures[texIdx], matrix, domain, wrapMode); 202 } 203