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 #include "gl/builders/GrGLProgramBuilder.h"
      9 #include "GrTextureDomain.h"
     10 #include "GrSimpleTextureEffect.h"
     11 #include "GrTBackendProcessorFactory.h"
     12 #include "gl/GrGLProcessor.h"
     13 #include "SkFloatingPoint.h"
     14 
     15 
     16 GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index)
     17     : fIndex(index) {
     18 
     19     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
     20     if (domain.contains(kFullRect) && kClamp_Mode == mode) {
     21         fMode = kIgnore_Mode;
     22     } else {
     23         fMode = mode;
     24     }
     25 
     26     if (fMode != kIgnore_Mode) {
     27         // We don't currently handle domains that are empty or don't intersect the texture.
     28         // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
     29         // handle rects that do not intersect the [0..1]x[0..1] rect.
     30         SkASSERT(domain.fLeft <= domain.fRight);
     31         SkASSERT(domain.fTop <= domain.fBottom);
     32         fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
     33         fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
     34         fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
     35         fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
     36         SkASSERT(fDomain.fLeft <= fDomain.fRight);
     37         SkASSERT(fDomain.fTop <= fDomain.fBottom);
     38     }
     39 }
     40 
     41 //////////////////////////////////////////////////////////////////////////////
     42 
     43 void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder,
     44                                               const GrTextureDomain& textureDomain,
     45                                               const char* outColor,
     46                                               const SkString& inCoords,
     47                                               const GrGLProcessor::TextureSampler sampler,
     48                                               const char* inModulateColor) {
     49     SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode);
     50     SkDEBUGCODE(fMode = textureDomain.mode();)
     51 
     52     GrGLProgramBuilder* program = builder->getProgramBuilder();
     53 
     54     if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) {
     55         const char* name;
     56         SkString uniName("TexDom");
     57         if (textureDomain.fIndex >= 0) {
     58             uniName.appendS32(textureDomain.fIndex);
     59         }
     60         fDomainUni = program->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType,
     61                 uniName.c_str(), &name);
     62         fDomainName = name;
     63     }
     64 
     65     switch (textureDomain.mode()) {
     66         case kIgnore_Mode: {
     67             builder->codeAppendf("\t%s = ", outColor);
     68             builder->appendTextureLookupAndModulate(inModulateColor, sampler,
     69                                                       inCoords.c_str());
     70             builder->codeAppend(";\n");
     71             break;
     72         }
     73         case kClamp_Mode: {
     74             SkString clampedCoords;
     75             clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)",
     76                                   inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
     77 
     78             builder->codeAppendf("\t%s = ", outColor);
     79             builder->appendTextureLookupAndModulate(inModulateColor, sampler,
     80                                                       clampedCoords.c_str());
     81             builder->codeAppend(";\n");
     82             break;
     83         }
     84         case kDecal_Mode: {
     85             // Add a block since we're going to declare variables.
     86             GrGLShaderBuilder::ShaderBlock block(builder);
     87 
     88             const char* domain = fDomainName.c_str();
     89             if (kImagination_GrGLVendor == program->ctxInfo().vendor()) {
     90                 // On the NexusS and GalaxyNexus, the other path (with the 'any'
     91                 // call) causes the compilation error "Calls to any function that
     92                 // may require a gradient calculation inside a conditional block
     93                 // may return undefined results". This appears to be an issue with
     94                 // the 'any' call since even the simple "result=black; if (any())
     95                 // result=white;" code fails to compile.
     96                 builder->codeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
     97                 builder->codeAppend("\tvec4 inside = ");
     98                 builder->appendTextureLookupAndModulate(inModulateColor, sampler,
     99                                                           inCoords.c_str());
    100                 builder->codeAppend(";\n");
    101                 builder->codeAppendf("\tfloat x = (%s).x;\n", inCoords.c_str());
    102                 builder->codeAppendf("\tfloat y = (%s).y;\n", inCoords.c_str());
    103 
    104                 builder->codeAppendf("\tx = abs(2.0*(x - %s.x)/(%s.z - %s.x) - 1.0);\n",
    105                                        domain, domain, domain);
    106                 builder->codeAppendf("\ty = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);\n",
    107                                        domain, domain, domain);
    108                 builder->codeAppend("\tfloat blend = step(1.0, max(x, y));\n");
    109                 builder->codeAppendf("\t%s = mix(inside, outside, blend);\n", outColor);
    110             } else {
    111                 builder->codeAppend("\tbvec4 outside;\n");
    112                 builder->codeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(),
    113                                        domain);
    114                 builder->codeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(),
    115                                        domain);
    116                 builder->codeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ",
    117                                        outColor);
    118                 builder->appendTextureLookupAndModulate(inModulateColor, sampler,
    119                                                           inCoords.c_str());
    120                 builder->codeAppend(";\n");
    121             }
    122             break;
    123         }
    124         case kRepeat_Mode: {
    125             SkString clampedCoords;
    126             clampedCoords.printf("\tmod(%s - %s.xy, %s.zw - %s.xy) + %s.xy",
    127                                  inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(),
    128                                  fDomainName.c_str(), fDomainName.c_str());
    129 
    130             builder->codeAppendf("\t%s = ", outColor);
    131             builder->appendTextureLookupAndModulate(inModulateColor, sampler,
    132                                                       clampedCoords.c_str());
    133             builder->codeAppend(";\n");
    134             break;
    135         }
    136     }
    137 }
    138 
    139 void GrTextureDomain::GLDomain::setData(const GrGLProgramDataManager& pdman,
    140                                         const GrTextureDomain& textureDomain,
    141                                         GrSurfaceOrigin textureOrigin) {
    142     SkASSERT(textureDomain.mode() == fMode);
    143     if (kIgnore_Mode != textureDomain.mode()) {
    144         GrGLfloat values[4] = {
    145             SkScalarToFloat(textureDomain.domain().left()),
    146             SkScalarToFloat(textureDomain.domain().top()),
    147             SkScalarToFloat(textureDomain.domain().right()),
    148             SkScalarToFloat(textureDomain.domain().bottom())
    149         };
    150         // vertical flip if necessary
    151         if (kBottomLeft_GrSurfaceOrigin == textureOrigin) {
    152             values[1] = 1.0f - values[1];
    153             values[3] = 1.0f - values[3];
    154             // The top and bottom were just flipped, so correct the ordering
    155             // of elements so that values = (l, t, r, b).
    156             SkTSwap(values[1], values[3]);
    157         }
    158         if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
    159             pdman.set4fv(fDomainUni, 1, values);
    160             memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
    161         }
    162     }
    163 }
    164 
    165 
    166 //////////////////////////////////////////////////////////////////////////////
    167 
    168 class GrGLTextureDomainEffect : public GrGLFragmentProcessor {
    169 public:
    170     GrGLTextureDomainEffect(const GrBackendProcessorFactory&, const GrProcessor&);
    171 
    172     virtual void emitCode(GrGLProgramBuilder*,
    173                           const GrFragmentProcessor&,
    174                           const GrProcessorKey&,
    175                           const char* outputColor,
    176                           const char* inputColor,
    177                           const TransformedCoordsArray&,
    178                           const TextureSamplerArray&) SK_OVERRIDE;
    179 
    180     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
    181 
    182     static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
    183 
    184 private:
    185     GrTextureDomain::GLDomain         fGLDomain;
    186     typedef GrGLFragmentProcessor INHERITED;
    187 };
    188 
    189 GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendProcessorFactory& factory,
    190                                                  const GrProcessor&)
    191     : INHERITED(factory) {
    192 }
    193 
    194 void GrGLTextureDomainEffect::emitCode(GrGLProgramBuilder* builder,
    195                                        const GrFragmentProcessor& fp,
    196                                        const GrProcessorKey& key,
    197                                        const char* outputColor,
    198                                        const char* inputColor,
    199                                        const TransformedCoordsArray& coords,
    200                                        const TextureSamplerArray& samplers) {
    201     const GrTextureDomainEffect& textureDomainEffect = fp.cast<GrTextureDomainEffect>();
    202     const GrTextureDomain& domain = textureDomainEffect.textureDomain();
    203 
    204     GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
    205     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
    206     fGLDomain.sampleTexture(fsBuilder, domain, outputColor, coords2D, samplers[0], inputColor);
    207 }
    208 
    209 void GrGLTextureDomainEffect::setData(const GrGLProgramDataManager& pdman,
    210                                       const GrProcessor& processor) {
    211     const GrTextureDomainEffect& textureDomainEffect = processor.cast<GrTextureDomainEffect>();
    212     const GrTextureDomain& domain = textureDomainEffect.textureDomain();
    213     fGLDomain.setData(pdman, domain, processor.texture(0)->origin());
    214 }
    215 
    216 void GrGLTextureDomainEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
    217                                      GrProcessorKeyBuilder* b) {
    218     const GrTextureDomain& domain = processor.cast<GrTextureDomainEffect>().textureDomain();
    219     b->add32(GrTextureDomain::GLDomain::DomainKey(domain));
    220 }
    221 
    222 
    223 ///////////////////////////////////////////////////////////////////////////////
    224 
    225 GrFragmentProcessor* GrTextureDomainEffect::Create(GrTexture* texture,
    226                                                    const SkMatrix& matrix,
    227                                                    const SkRect& domain,
    228                                                    GrTextureDomain::Mode mode,
    229                                                    GrTextureParams::FilterMode filterMode,
    230                                                    GrCoordSet coordSet) {
    231     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
    232     if (GrTextureDomain::kIgnore_Mode == mode ||
    233         (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) {
    234         return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
    235     } else {
    236 
    237         return SkNEW_ARGS(GrTextureDomainEffect, (texture,
    238                                                   matrix,
    239                                                   domain,
    240                                                   mode,
    241                                                   filterMode,
    242                                                   coordSet));
    243     }
    244 }
    245 
    246 GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
    247                                              const SkMatrix& matrix,
    248                                              const SkRect& domain,
    249                                              GrTextureDomain::Mode mode,
    250                                              GrTextureParams::FilterMode filterMode,
    251                                              GrCoordSet coordSet)
    252     : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
    253     , fTextureDomain(domain, mode) {
    254     SkASSERT(mode != GrTextureDomain::kRepeat_Mode ||
    255             filterMode == GrTextureParams::kNone_FilterMode);
    256 }
    257 
    258 GrTextureDomainEffect::~GrTextureDomainEffect() {
    259 
    260 }
    261 
    262 const GrBackendFragmentProcessorFactory& GrTextureDomainEffect::getFactory() const {
    263     return GrTBackendFragmentProcessorFactory<GrTextureDomainEffect>::getInstance();
    264 }
    265 
    266 bool GrTextureDomainEffect::onIsEqual(const GrProcessor& sBase) const {
    267     const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
    268     return this->hasSameTextureParamsMatrixAndSourceCoords(s) &&
    269            this->fTextureDomain == s.fTextureDomain;
    270 }
    271 
    272 void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    273     if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper
    274         *validFlags = 0;
    275     } else {
    276         this->updateConstantColorComponentsForModulation(color, validFlags);
    277     }
    278 }
    279 
    280 ///////////////////////////////////////////////////////////////////////////////
    281 
    282 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
    283 
    284 GrFragmentProcessor* GrTextureDomainEffect::TestCreate(SkRandom* random,
    285                                                        GrContext*,
    286                                                        const GrDrawTargetCaps&,
    287                                                        GrTexture* textures[]) {
    288     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    289                                       GrProcessorUnitTest::kAlphaTextureIdx;
    290     SkRect domain;
    291     domain.fLeft = random->nextUScalar1();
    292     domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
    293     domain.fTop = random->nextUScalar1();
    294     domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
    295     GrTextureDomain::Mode mode =
    296         (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount);
    297     const SkMatrix& matrix = GrProcessorUnitTest::TestMatrix(random);
    298     bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? random->nextBool() : false;
    299     GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
    300     return GrTextureDomainEffect::Create(textures[texIdx],
    301                                          matrix,
    302                                          domain,
    303                                          mode,
    304                                          bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
    305                                          coords);
    306 }
    307