Home | History | Annotate | Download | only in effects
      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 
     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 
     58     const SkRect& domain() const { return fDomain; }
     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. */
     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 
     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:
    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          */
    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 
    178     const char* name() const override { return "TextureDomain"; }
    179 
    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 
    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