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 GrGLSLColorSpaceXformHelper; 17 class GrGLSLShaderBuilder; 18 class GrInvariantOutput; 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 GrTextureDomain gDomain((GrTextureProxy*)nullptr, 48 SkRect::MakeEmpty(), 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(GrTexture*, const SkRect& domain, Mode, int index = -1); 57 58 GrTextureDomain(GrTextureProxy*, const SkRect& domain, Mode, int index = -1); 59 domain()60 const SkRect& domain() const { return fDomain; } mode()61 Mode mode() const { return fMode; } 62 63 /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled 64 texels neighboring the domain may be read. */ MakeTexelDomain(const SkIRect & texelRect)65 static const SkRect MakeTexelDomain(const SkIRect& texelRect) { 66 return SkRect::Make(texelRect); 67 } 68 MakeTexelDomainForMode(const SkIRect & texelRect,Mode mode)69 static const SkRect MakeTexelDomainForMode(const SkIRect& texelRect, Mode mode) { 70 // For Clamp mode, inset by half a texel. 71 SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0; 72 return SkRect::MakeLTRB(texelRect.fLeft + inset, texelRect.fTop + inset, 73 texelRect.fRight - inset, texelRect.fBottom - inset); 74 } 75 76 bool operator==(const GrTextureDomain& that) const { 77 return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain); 78 } 79 80 /** 81 * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses 82 * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces 83 * the part of the effect key that reflects the texture domain code, and performs the uniform 84 * uploads necessary for texture domains. 85 */ 86 class GLDomain { 87 public: GLDomain()88 GLDomain() { 89 for (int i = 0; i < kPrevDomainCount; i++) { 90 fPrevDomain[i] = SK_FloatNaN; 91 } 92 SkDEBUGCODE(fMode = (Mode) -1;) 93 } 94 95 /** 96 * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the 97 * domain and mode. 98 * 99 * @param outcolor name of vec4 variable to hold the sampled color. 100 * @param inCoords name of vec2 variable containing the coords to be used with the domain. 101 * It is assumed that this is a variable and not an expression. 102 * @param inModulateColor if non-nullptr the sampled color will be modulated with this 103 * expression before being written to outColor. 104 */ 105 void sampleTexture(GrGLSLShaderBuilder* builder, 106 GrGLSLUniformHandler* uniformHandler, 107 const GrShaderCaps* shaderCaps, 108 const GrTextureDomain& textureDomain, 109 const char* outColor, 110 const SkString& inCoords, 111 GrGLSLFragmentProcessor::SamplerHandle sampler, 112 const char* inModulateColor = nullptr, 113 GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr); 114 115 /** 116 * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the 117 * texture domain. The rectangle is automatically adjusted to account for the texture's 118 * origin. 119 */ 120 void setData(const GrGLSLProgramDataManager& pdman, const GrTextureDomain& textureDomain, 121 GrTexture* texure); 122 123 enum { 124 kDomainKeyBits = 2, // See DomainKey(). 125 }; 126 127 /** 128 * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's 129 * computed key. The returned will be limited to the lower kDomainKeyBits bits. 130 */ DomainKey(const GrTextureDomain & domain)131 static uint32_t DomainKey(const GrTextureDomain& domain) { 132 GR_STATIC_ASSERT(kModeCount <= (1 << kDomainKeyBits)); 133 return domain.mode(); 134 } 135 136 private: 137 static const int kPrevDomainCount = 4; 138 SkDEBUGCODE(Mode fMode;) 139 GrGLSLProgramDataManager::UniformHandle fDomainUni; 140 SkString fDomainName; 141 float fPrevDomain[kPrevDomainCount]; 142 }; 143 144 protected: 145 Mode fMode; 146 SkRect fDomain; 147 int fIndex; 148 }; 149 150 /** 151 * A basic texture effect that uses GrTextureDomain. 152 */ 153 class GrTextureDomainEffect : public GrSingleTextureEffect { 154 155 public: 156 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider*, 157 sk_sp<GrTextureProxy>, 158 sk_sp<GrColorSpaceXform>, 159 const SkMatrix&, 160 const SkRect& domain, 161 GrTextureDomain::Mode, 162 GrSamplerParams::FilterMode filterMode); 163 name()164 const char* name() const override { return "TextureDomain"; } 165 dumpInfo()166 SkString dumpInfo() const override { 167 SkString str; 168 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]", 169 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 170 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom); 171 str.append(INHERITED::dumpInfo()); 172 return str; 173 } 174 175 private: 176 GrTextureDomain fTextureDomain; 177 178 GrTextureDomainEffect(GrResourceProvider*, 179 sk_sp<GrTextureProxy>, 180 sk_sp<GrColorSpaceXform>, 181 const SkMatrix&, 182 const SkRect& domain, 183 GrTextureDomain::Mode, 184 GrSamplerParams::FilterMode); 185 186 static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode mode); 187 188 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 189 190 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; 191 192 bool onIsEqual(const GrFragmentProcessor&) const override; 193 194 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 195 196 typedef GrSingleTextureEffect INHERITED; 197 }; 198 199 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor { 200 public: 201 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider*, sk_sp<GrTextureProxy>, 202 const SkIRect& subset, 203 const SkIPoint& deviceSpaceOffset); 204 name()205 const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; } 206 dumpInfo()207 SkString dumpInfo() const override { 208 SkString str; 209 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]", 210 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 211 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom, 212 fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY); 213 str.append(INHERITED::dumpInfo()); 214 return str; 215 } 216 217 private: 218 TextureSampler fTextureSampler; 219 GrTextureDomain fTextureDomain; 220 SkIPoint fDeviceSpaceOffset; 221 222 GrDeviceSpaceTextureDecalFragmentProcessor(GrResourceProvider*, sk_sp<GrTextureProxy>, 223 const SkIRect&, const SkIPoint&); 224 225 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 226 227 // Since we always use decal mode, there is no need for key data. onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *)228 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} 229 230 bool onIsEqual(const GrFragmentProcessor& fp) const override; 231 232 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 233 234 typedef GrFragmentProcessor INHERITED; 235 }; 236 #endif 237