/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrTextureDomainEffect_DEFINED #define GrTextureDomainEffect_DEFINED #include "GrSingleTextureEffect.h" #include "gl/GrGLEffect.h" class GrGLProgramBuilder; class GrGLShaderBuilder; struct SkRect; /** * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the * domain to affect the read value unless the caller considers this when calculating the domain. */ class GrTextureDomain { public: enum Mode { // Ignore the texture domain rectangle. kIgnore_Mode, // Clamp texture coords to the domain rectangle. kClamp_Mode, // Treat the area outside the domain rectangle as fully transparent. kDecal_Mode, // Wrap texture coordinates. NOTE: filtering may not work as expected because Bilerp will // read texels outside of the domain. We could perform additional texture reads and filter // in the shader, but are not currently doing this for performance reasons kRepeat_Mode, kLastMode = kRepeat_Mode }; static const int kModeCount = kLastMode + 1; static const GrTextureDomain& IgnoredDomain() { static const SkRect gDummyRect = {0, 0, 0, 0}; static const GrTextureDomain gDomain(gDummyRect, kIgnore_Mode); return gDomain; } /** * @param index Pass a value >= 0 if using multiple texture domains in the same effect. * It is used to keep inserted variables from causing name collisions. */ GrTextureDomain(const SkRect& domain, Mode, int index = -1); const SkRect& domain() const { return fDomain; } Mode mode() const { return fMode; } /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled texels neighboring the domain may be read. */ static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) { SkScalar wInv = SK_Scalar1 / texture->width(); SkScalar hInv = SK_Scalar1 / texture->height(); SkRect result = { texelRect.fLeft * wInv, texelRect.fTop * hInv, texelRect.fRight * wInv, texelRect.fBottom * hInv }; return result; } bool operator== (const GrTextureDomain& that) const { return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain); } /** * A GrGLEffect subclass that corresponds to a GrEffect subclass that uses GrTextureDomain * should include this helper. It generates the texture domain GLSL, produces the part of the * effect key that reflects the texture domain code, and performs the uniform uploads necessary * for texture domains. */ class GLDomain { public: GLDomain() { fPrevDomain[0] = SK_FloatNaN; SkDEBUGCODE(fMode = (Mode) -1;) } /** * Call this from GrGLEffect::emitCode() to sample the texture W.R.T. the domain and mode. * * @param outcolor name of vec4 variable to hold the sampled color. * @param inCoords name of vec2 variable containing the coords to be used with the domain. * It is assumed that this is a variable and not an expression. * @param inModulateColor if non-NULL the sampled color will be modulated with this * expression before being written to outColor. */ void sampleTexture(GrGLShaderBuilder* builder, const GrTextureDomain& textureDomain, const char* outColor, const SkString& inCoords, const GrGLEffect::TextureSampler sampler, const char* inModulateColor = NULL); /** * Call this from GrGLEffect::setData() to upload uniforms necessary for the texture domain. * The rectangle is automatically adjusted to account for the texture's origin. */ void setData(const GrGLProgramDataManager& pdman, const GrTextureDomain& textureDomain, GrSurfaceOrigin textureOrigin); enum { kDomainKeyBits = 2, // See DomainKey(). }; /** * GrGLEffect::GenKey() must call this and include the returned value in it's computed key. * The returned will be limited to the lower kDomainKeyBits bits. */ static uint32_t DomainKey(const GrTextureDomain& domain) { GR_STATIC_ASSERT(kModeCount <= 4); return domain.mode(); } private: SkDEBUGCODE(Mode fMode;) GrGLProgramDataManager::UniformHandle fDomainUni; SkString fDomainName; GrGLfloat fPrevDomain[4]; }; protected: Mode fMode; SkRect fDomain; int fIndex; typedef GrSingleTextureEffect INHERITED; }; class GrGLTextureDomainEffect; /** * A basic texture effect that uses GrTextureDomain. */ class GrTextureDomainEffect : public GrSingleTextureEffect { public: static GrEffect* Create(GrTexture*, const SkMatrix&, const SkRect& domain, GrTextureDomain::Mode, GrTextureParams::FilterMode filterMode, GrCoordSet = kLocal_GrCoordSet); virtual ~GrTextureDomainEffect(); static const char* Name() { return "TextureDomain"; } typedef GrGLTextureDomainEffect GLEffect; virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; const GrTextureDomain& textureDomain() const { return fTextureDomain; } protected: GrTextureDomain fTextureDomain; private: GrTextureDomainEffect(GrTexture*, const SkMatrix&, const SkRect& domain, GrTextureDomain::Mode, GrTextureParams::FilterMode, GrCoordSet); virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; GR_DECLARE_EFFECT_TEST; typedef GrSingleTextureEffect INHERITED; }; #endif