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); 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, int index = -1); 57 58 GrTextureDomain(const GrTextureDomain&) = default; 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 } 93 94 /** 95 * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the 96 * domain and mode. 97 * 98 * @param outcolor name of half4 variable to hold the sampled color. 99 * @param inCoords name of float2 variable containing the coords to be used with the domain. 100 * It is assumed that this is a variable and not an expression. 101 * @param inModulateColor if non-nullptr the sampled color will be modulated with this 102 * expression before being written to outColor. 103 */ 104 void sampleTexture(GrGLSLShaderBuilder* builder, 105 GrGLSLUniformHandler* uniformHandler, 106 const GrShaderCaps* shaderCaps, 107 const GrTextureDomain& textureDomain, 108 const char* outColor, 109 const SkString& inCoords, 110 GrGLSLFragmentProcessor::SamplerHandle sampler, 111 const char* inModulateColor = nullptr); 112 113 /** 114 * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the 115 * texture domain. The rectangle is automatically adjusted to account for the texture's 116 * origin. 117 */ 118 void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrSurfaceProxy*); 119 120 enum { 121 kDomainKeyBits = 2, // See DomainKey(). 122 }; 123 124 /** 125 * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's 126 * computed key. The returned will be limited to the lower kDomainKeyBits bits. 127 */ DomainKey(const GrTextureDomain & domain)128 static uint32_t DomainKey(const GrTextureDomain& domain) { 129 GR_STATIC_ASSERT(kModeCount <= (1 << kDomainKeyBits)); 130 return domain.mode(); 131 } 132 133 private: 134 static const int kPrevDomainCount = 4; 135 SkDEBUGCODE(Mode fMode;) 136 SkDEBUGCODE(bool fHasMode = false;) 137 GrGLSLProgramDataManager::UniformHandle fDomainUni; 138 SkString fDomainName; 139 float fPrevDomain[kPrevDomainCount]; 140 }; 141 142 protected: 143 Mode fMode; 144 SkRect fDomain; 145 int fIndex; 146 }; 147 148 /** 149 * A basic texture effect that uses GrTextureDomain. 150 */ 151 class GrTextureDomainEffect : public GrFragmentProcessor { 152 public: 153 static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>, 154 const SkMatrix&, 155 const SkRect& domain, 156 GrTextureDomain::Mode, 157 GrSamplerState::Filter filterMode); 158 name()159 const char* name() const override { return "TextureDomain"; } 160 clone()161 std::unique_ptr<GrFragmentProcessor> clone() const override { 162 return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this)); 163 } 164 dumpInfo()165 SkString dumpInfo() const override { 166 SkString str; 167 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]", 168 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 169 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom); 170 str.append(INHERITED::dumpInfo()); 171 return str; 172 } 173 174 private: 175 GrCoordTransform fCoordTransform; 176 GrTextureDomain fTextureDomain; 177 TextureSampler fTextureSampler; 178 179 GrTextureDomainEffect(sk_sp<GrTextureProxy>, 180 const SkMatrix&, 181 const SkRect& domain, 182 GrTextureDomain::Mode, 183 GrSamplerState::Filter); 184 185 explicit GrTextureDomainEffect(const GrTextureDomainEffect&); 186 187 static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode mode); 188 189 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 190 191 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; 192 193 bool onIsEqual(const GrFragmentProcessor&) const override; 194 195 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 196 197 typedef GrFragmentProcessor INHERITED; 198 }; 199 200 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor { 201 public: 202 static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>, 203 const SkIRect& subset, 204 const SkIPoint& deviceSpaceOffset); 205 name()206 const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; } 207 dumpInfo()208 SkString dumpInfo() const override { 209 SkString str; 210 str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]", 211 fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop, 212 fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom, 213 fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY); 214 str.append(INHERITED::dumpInfo()); 215 return str; 216 } 217 218 std::unique_ptr<GrFragmentProcessor> clone() const override; 219 220 private: 221 TextureSampler fTextureSampler; 222 GrTextureDomain fTextureDomain; 223 SkIPoint fDeviceSpaceOffset; 224 225 GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>, 226 const SkIRect&, const SkIPoint&); 227 GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&); 228 229 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 230 231 // Since we always use decal mode, there is no need for key data. onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *)232 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} 233 234 bool onIsEqual(const GrFragmentProcessor& fp) const override; 235 236 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 237 238 typedef GrFragmentProcessor INHERITED; 239 }; 240 #endif 241