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 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 
     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 
     60     const SkRect& domain() const { return fDomain; }
     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. */
     65     static const SkRect MakeTexelDomain(const SkIRect& texelRect) {
     66         return SkRect::Make(texelRect);
     67     }
     68 
     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:
     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          */
    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 
    164     const char* name() const override { return "TextureDomain"; }
    165 
    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 
    205     const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; }
    206 
    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.
    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