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 "GrCoordTransform.h" 12 #include "GrFragmentProcessor.h" 13 #include "glsl/GrGLSLFragmentProcessor.h" 14 #include "glsl/GrGLSLProgramDataManager.h" 15 16 class GrGLProgramBuilder; 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 half4 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, 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(GrTextureProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1); 57 58 GrTextureDomain(const GrTextureDomain&) = default; 59 domain()60 const SkRect& domain() const { return fDomain; } modeX()61 Mode modeX() const { return fModeX; } modeY()62 Mode modeY() const { return fModeY; } 63 64 /* 65 * Computes a domain that bounds all the texels in texelRect, possibly insetting by half a pixel 66 * depending on the mode. The mode is used for both axes. 67 */ MakeTexelDomain(const SkIRect & texelRect,Mode mode)68 static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode mode) { 69 return MakeTexelDomain(texelRect, mode, mode); 70 } 71 MakeTexelDomain(const SkIRect & texelRect,Mode modeX,Mode modeY)72 static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode modeX, Mode modeY) { 73 // For Clamp and decal modes, inset by half a texel 74 SkScalar insetX = ((modeX == kClamp_Mode || modeX == kDecal_Mode) && texelRect.width() > 0) 75 ? SK_ScalarHalf : 0; 76 SkScalar insetY = ((modeY == kClamp_Mode || modeY == kDecal_Mode) && texelRect.height() > 0) 77 ? SK_ScalarHalf : 0; 78 return SkRect::MakeLTRB(texelRect.fLeft + insetX, texelRect.fTop + insetY, 79 texelRect.fRight - insetX, texelRect.fBottom - insetY); 80 } 81 82 // Convenience to determine if any axis of a texture uses an explicit decal mode or the hardware 83 // clamp to border decal mode. IsDecalSampled(GrSamplerState::WrapMode wrapX,GrSamplerState::WrapMode wrapY,Mode modeX,Mode modeY)84 static bool IsDecalSampled(GrSamplerState::WrapMode wrapX, GrSamplerState::WrapMode wrapY, 85 Mode modeX, Mode modeY) { 86 return wrapX == GrSamplerState::WrapMode::kClampToBorder || 87 wrapY == GrSamplerState::WrapMode::kClampToBorder || 88 modeX == kDecal_Mode || 89 modeY == kDecal_Mode; 90 } 91 IsDecalSampled(const GrSamplerState::WrapMode wraps[2],Mode modeX,Mode modeY)92 static bool IsDecalSampled(const GrSamplerState::WrapMode wraps[2], Mode modeX, Mode modeY) { 93 return IsDecalSampled(wraps[0], wraps[1], modeX, modeY); 94 } 95 IsDecalSampled(const GrSamplerState & sampler,Mode modeX,Mode modeY)96 static bool IsDecalSampled(const GrSamplerState& sampler, Mode modeX, Mode modeY) { 97 return IsDecalSampled(sampler.wrapModeX(), sampler.wrapModeY(), modeX, modeY); 98 } 99 100 bool operator==(const GrTextureDomain& that) const { 101 return fModeX == that.fModeX && fModeY == that.fModeY && 102 (kIgnore_Mode == fModeX || (fDomain.fLeft == that.fDomain.fLeft && 103 fDomain.fRight == that.fDomain.fRight)) && 104 (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop && 105 fDomain.fBottom == that.fDomain.fBottom)); 106 } 107 108 /** 109 * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses 110 * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces 111 * the part of the effect key that reflects the texture domain code, and performs the uniform 112 * uploads necessary for texture domains. 113 */ 114 class GLDomain { 115 public: GLDomain()116 GLDomain() { 117 for (int i = 0; i < kPrevDomainCount; i++) { 118 fPrevDomain[i] = SK_FloatNaN; 119 } 120 } 121 122 /** 123 * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the 124 * domain and mode. 125 * 126 * @param outcolor name of half4 variable to hold the sampled color. 127 * @param inCoords name of float2 variable containing the coords to be used with the domain. 128 * It is assumed that this is a variable and not an expression. 129 * @param inModulateColor if non-nullptr the sampled color will be modulated with this 130 * expression before being written to outColor. 131 */ 132 void sampleTexture(GrGLSLShaderBuilder* builder, 133 GrGLSLUniformHandler* uniformHandler, 134 const GrShaderCaps* shaderCaps, 135 const GrTextureDomain& textureDomain, 136 const char* outColor, 137 const SkString& inCoords, 138 GrGLSLFragmentProcessor::SamplerHandle sampler, 139 const char* inModulateColor = nullptr); 140 141 /** 142 * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the 143 * texture domain. The rectangle is automatically adjusted to account for the texture's 144 * origin. 145 */ 146 void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*, 147 const GrSamplerState& sampler); 148 149 enum { 150 kModeBits = 2, // See DomainKey(). 151 kDomainKeyBits = 4 152 }; 153 154 /** 155 * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's 156 * computed key. The returned will be limited to the lower kDomainKeyBits bits. 157 */ DomainKey(const GrTextureDomain & domain)158 static uint32_t DomainKey(const GrTextureDomain& domain) { 159 GR_STATIC_ASSERT(kModeCount <= (1 << kModeBits)); 160 return domain.modeX() | (domain.modeY() << kModeBits); 161 } 162 163 private: 164 static const int kPrevDomainCount = 4; 165 SkDEBUGCODE(Mode fModeX;) 166 SkDEBUGCODE(Mode fModeY;) 167 SkDEBUGCODE(bool fHasMode = false;) 168 GrGLSLProgramDataManager::UniformHandle fDomainUni; 169 SkString fDomainName; 170 171 // Only initialized if the domain has at least one decal axis 172 GrGLSLProgramDataManager::UniformHandle fDecalUni; 173 SkString fDecalName; 174 175 float fPrevDomain[kPrevDomainCount]; 176 }; 177 178 protected: 179 Mode fModeX; 180 Mode fModeY; 181 SkRect fDomain; 182 int fIndex; 183 }; 184 185 /** 186 * A basic texture effect that uses GrTextureDomain. 187 */ 188 class GrTextureDomainEffect : public GrFragmentProcessor { 189 public: 190 static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>, 191 const SkMatrix&, 192 const SkRect& domain, 193 GrTextureDomain::Mode mode, 194 GrSamplerState::Filter filterMode); 195 196 static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>, 197 const SkMatrix&, 198 const SkRect& domain, 199 GrTextureDomain::Mode modeX, 200 GrTextureDomain::Mode modeY, 201 const GrSamplerState& sampler); 202 name()203 const char* name() const override { return "TextureDomain"; } 204 clone()205 std::unique_ptr<GrFragmentProcessor> clone() const override { 206 return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this)); 207 } 208 209 #ifdef SK_DEBUG dumpInfo()210 SkString dumpInfo() const override { 211 SkString str; 212 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]", 213 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 214 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom); 215 str.append(INHERITED::dumpInfo()); 216 return str; 217 } 218 #endif 219 220 private: 221 GrCoordTransform fCoordTransform; 222 GrTextureDomain fTextureDomain; 223 TextureSampler fTextureSampler; 224 225 GrTextureDomainEffect(sk_sp<GrTextureProxy>, 226 const SkMatrix&, 227 const SkRect& domain, 228 GrTextureDomain::Mode modeX, 229 GrTextureDomain::Mode modeY, 230 const GrSamplerState&); 231 232 explicit GrTextureDomainEffect(const GrTextureDomainEffect&); 233 234 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 235 236 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; 237 238 bool onIsEqual(const GrFragmentProcessor&) const override; 239 onTextureSampler(int)240 const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; } 241 242 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 243 244 typedef GrFragmentProcessor INHERITED; 245 }; 246 247 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor { 248 public: 249 static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>, 250 const SkIRect& subset, 251 const SkIPoint& deviceSpaceOffset); 252 name()253 const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; } 254 255 #ifdef SK_DEBUG dumpInfo()256 SkString dumpInfo() const override { 257 SkString str; 258 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]", 259 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 260 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom, 261 fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY); 262 str.append(INHERITED::dumpInfo()); 263 return str; 264 } 265 #endif 266 267 std::unique_ptr<GrFragmentProcessor> clone() const override; 268 269 private: 270 TextureSampler fTextureSampler; 271 GrTextureDomain fTextureDomain; 272 SkIPoint fDeviceSpaceOffset; 273 274 GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>, 275 const SkIRect&, const SkIPoint&); 276 GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&); 277 278 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 279 280 // Since we always use decal mode, there is no need for key data. onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *)281 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} 282 283 bool onIsEqual(const GrFragmentProcessor& fp) const override; 284 onTextureSampler(int)285 const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; } 286 287 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 288 289 typedef GrFragmentProcessor INHERITED; 290 }; 291 #endif 292