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 #ifndef GrTextureDomainEffect_DEFINED 9 #define GrTextureDomainEffect_DEFINED 10 11 #include "GrSingleTextureEffect.h" 12 #include "glsl/GrGLSLFragmentProcessor.h" 13 #include "glsl/GrGLSLProgramDataManager.h" 14 15 class GrGLProgramBuilder; 16 class GrGLSLShaderBuilder; 17 class GrInvariantOutput; 18 class GrGLSLTextureSampler; 19 class GrGLSLUniformHandler; 20 struct SkRect; 21 22 /** 23 * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped 24 * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to 25 * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the 26 * domain to affect the read value unless the caller considers this when calculating the domain. 27 */ 28 class GrTextureDomain { 29 public: 30 enum Mode { 31 // Ignore the texture domain rectangle. 32 kIgnore_Mode, 33 // Clamp texture coords to the domain rectangle. 34 kClamp_Mode, 35 // Treat the area outside the domain rectangle as fully transparent. 36 kDecal_Mode, 37 // Wrap texture coordinates. NOTE: filtering may not work as expected because Bilerp will 38 // read texels outside of the domain. We could perform additional texture reads and filter 39 // in the shader, but are not currently doing this for performance reasons 40 kRepeat_Mode, 41 42 kLastMode = kRepeat_Mode 43 }; 44 static const int kModeCount = kLastMode + 1; 45 IgnoredDomain()46 static const GrTextureDomain& IgnoredDomain() { 47 static const SkRect gDummyRect = {0, 0, 0, 0}; 48 static const GrTextureDomain gDomain(gDummyRect, kIgnore_Mode); 49 return gDomain; 50 } 51 52 /** 53 * @param index Pass a value >= 0 if using multiple texture domains in the same effect. 54 * It is used to keep inserted variables from causing name collisions. 55 */ 56 GrTextureDomain(const SkRect& domain, Mode, int index = -1); 57 domain()58 const SkRect& domain() const { return fDomain; } mode()59 Mode mode() const { return fMode; } 60 61 /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled 62 texels neighboring the domain may be read. */ MakeTexelDomain(const GrTexture * texture,const SkIRect & texelRect)63 static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) { 64 SkScalar wInv = SK_Scalar1 / texture->width(); 65 SkScalar hInv = SK_Scalar1 / texture->height(); 66 SkRect result = { 67 texelRect.fLeft * wInv, 68 texelRect.fTop * hInv, 69 texelRect.fRight * wInv, 70 texelRect.fBottom * hInv 71 }; 72 return result; 73 } 74 MakeTexelDomainForMode(const GrTexture * texture,const SkIRect & texelRect,Mode mode)75 static const SkRect MakeTexelDomainForMode(const GrTexture* texture, const SkIRect& texelRect, Mode mode) { 76 // For Clamp mode, inset by half a texel. 77 SkScalar wInv = SK_Scalar1 / texture->width(); 78 SkScalar hInv = SK_Scalar1 / texture->height(); 79 SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0; 80 return SkRect::MakeLTRB( 81 (texelRect.fLeft + inset) * wInv, 82 (texelRect.fTop + inset) * hInv, 83 (texelRect.fRight - inset) * wInv, 84 (texelRect.fBottom - inset) * hInv 85 ); 86 } 87 88 bool operator== (const GrTextureDomain& that) const { 89 return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain); 90 } 91 92 /** 93 * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses 94 * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces 95 * the part of the effect key that reflects the texture domain code, and performs the uniform 96 * uploads necessary for texture domains. 97 */ 98 class GLDomain { 99 public: GLDomain()100 GLDomain() { 101 for (int i = 0; i < kPrevDomainCount; i++) { 102 fPrevDomain[i] = SK_FloatNaN; 103 } 104 SkDEBUGCODE(fMode = (Mode) -1;) 105 } 106 107 /** 108 * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the 109 * domain and mode. 110 * 111 * @param outcolor name of vec4 variable to hold the sampled color. 112 * @param inCoords name of vec2 variable containing the coords to be used with the domain. 113 * It is assumed that this is a variable and not an expression. 114 * @param inModulateColor if non-nullptr the sampled color will be modulated with this 115 * expression before being written to outColor. 116 */ 117 void sampleTexture(GrGLSLShaderBuilder* builder, 118 GrGLSLUniformHandler* uniformHandler, 119 const GrGLSLCaps* glslCaps, 120 const GrTextureDomain& textureDomain, 121 const char* outColor, 122 const SkString& inCoords, 123 const GrGLSLTextureSampler& sampler, 124 const char* inModulateColor = nullptr); 125 126 /** 127 * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the 128 * texture domain. The rectangle is automatically adjusted to account for the texture's 129 * origin. 130 */ 131 void setData(const GrGLSLProgramDataManager& pdman, const GrTextureDomain& textureDomain, 132 GrSurfaceOrigin textureOrigin); 133 134 enum { 135 kDomainKeyBits = 2, // See DomainKey(). 136 }; 137 138 /** 139 * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's 140 * computed key. The returned will be limited to the lower kDomainKeyBits bits. 141 */ DomainKey(const GrTextureDomain & domain)142 static uint32_t DomainKey(const GrTextureDomain& domain) { 143 GR_STATIC_ASSERT(kModeCount <= 4); 144 return domain.mode(); 145 } 146 147 private: 148 static const int kPrevDomainCount = 4; 149 SkDEBUGCODE(Mode fMode;) 150 GrGLSLProgramDataManager::UniformHandle fDomainUni; 151 SkString fDomainName; 152 float fPrevDomain[kPrevDomainCount]; 153 }; 154 155 protected: 156 Mode fMode; 157 SkRect fDomain; 158 int fIndex; 159 160 typedef GrSingleTextureEffect INHERITED; 161 }; 162 163 /** 164 * A basic texture effect that uses GrTextureDomain. 165 */ 166 class GrTextureDomainEffect : public GrSingleTextureEffect { 167 168 public: 169 static const GrFragmentProcessor* Create(GrTexture*, 170 const SkMatrix&, 171 const SkRect& domain, 172 GrTextureDomain::Mode, 173 GrTextureParams::FilterMode filterMode, 174 GrCoordSet = kLocal_GrCoordSet); 175 176 virtual ~GrTextureDomainEffect(); 177 name()178 const char* name() const override { return "TextureDomain"; } 179 dumpInfo()180 SkString dumpInfo() const override { 181 SkString str; 182 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] ", 183 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 184 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom); 185 str.append(INHERITED::dumpInfo()); 186 return str; 187 } 188 textureDomain()189 const GrTextureDomain& textureDomain() const { return fTextureDomain; } 190 191 protected: 192 GrTextureDomain fTextureDomain; 193 194 private: 195 GrTextureDomainEffect(GrTexture*, 196 const SkMatrix&, 197 const SkRect& domain, 198 GrTextureDomain::Mode, 199 GrTextureParams::FilterMode, 200 GrCoordSet); 201 202 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 203 204 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override; 205 206 bool onIsEqual(const GrFragmentProcessor&) const override; 207 208 void onComputeInvariantOutput(GrInvariantOutput* inout) const override; 209 210 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 211 212 typedef GrSingleTextureEffect INHERITED; 213 }; 214 215 #endif 216