Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2014 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 #include "gl/builders/GrGLProgramBuilder.h"
      8 #include "GrMatrixConvolutionEffect.h"
      9 #include "gl/GrGLProcessor.h"
     10 #include "gl/GrGLSL.h"
     11 #include "gl/GrGLTexture.h"
     12 #include "GrTBackendProcessorFactory.h"
     13 
     14 class GrGLMatrixConvolutionEffect : public GrGLFragmentProcessor {
     15 public:
     16     GrGLMatrixConvolutionEffect(const GrBackendProcessorFactory& factory,
     17                                 const GrProcessor&);
     18     virtual void emitCode(GrGLProgramBuilder*,
     19                           const GrFragmentProcessor&,
     20                           const GrProcessorKey&,
     21                           const char* outputColor,
     22                           const char* inputColor,
     23                           const TransformedCoordsArray&,
     24                           const TextureSamplerArray&) SK_OVERRIDE;
     25 
     26     static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
     27 
     28     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
     29 
     30 private:
     31     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
     32     SkISize                     fKernelSize;
     33     bool                        fConvolveAlpha;
     34 
     35     UniformHandle               fBoundsUni;
     36     UniformHandle               fKernelUni;
     37     UniformHandle               fImageIncrementUni;
     38     UniformHandle               fKernelOffsetUni;
     39     UniformHandle               fGainUni;
     40     UniformHandle               fBiasUni;
     41     GrTextureDomain::GLDomain   fDomain;
     42 
     43     typedef GrGLFragmentProcessor INHERITED;
     44 };
     45 
     46 GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendProcessorFactory& factory,
     47                                                          const GrProcessor& processor)
     48     : INHERITED(factory) {
     49     const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
     50     fKernelSize = m.kernelSize();
     51     fConvolveAlpha = m.convolveAlpha();
     52 }
     53 
     54 void GrGLMatrixConvolutionEffect::emitCode(GrGLProgramBuilder* builder,
     55                                            const GrFragmentProcessor& fp,
     56                                            const GrProcessorKey& key,
     57                                            const char* outputColor,
     58                                            const char* inputColor,
     59                                            const TransformedCoordsArray& coords,
     60                                            const TextureSamplerArray& samplers) {
     61     sk_ignore_unused_variable(inputColor);
     62     const GrTextureDomain& domain = fp.cast<GrMatrixConvolutionEffect>().domain();
     63 
     64     fBoundsUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
     65                                      kVec4f_GrSLType, "Bounds");
     66     fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
     67                                              kVec2f_GrSLType, "ImageIncrement");
     68     fKernelUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
     69                                           kFloat_GrSLType,
     70                                           "Kernel",
     71                                           fKernelSize.width() * fKernelSize.height());
     72     fKernelOffsetUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
     73                                            kVec2f_GrSLType, "KernelOffset");
     74     fGainUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
     75                                    kFloat_GrSLType, "Gain");
     76     fBiasUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
     77                                    kFloat_GrSLType, "Bias");
     78 
     79     const char* kernelOffset = builder->getUniformCStr(fKernelOffsetUni);
     80     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
     81     const char* kernel = builder->getUniformCStr(fKernelUni);
     82     const char* gain = builder->getUniformCStr(fGainUni);
     83     const char* bias = builder->getUniformCStr(fBiasUni);
     84     int kWidth = fKernelSize.width();
     85     int kHeight = fKernelSize.height();
     86 
     87     GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     88     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     89     fsBuilder->codeAppend("vec4 sum = vec4(0, 0, 0, 0);");
     90     fsBuilder->codeAppendf("vec2 coord = %s - %s * %s;", coords2D.c_str(), kernelOffset,
     91                            imgInc);
     92     fsBuilder->codeAppend("vec4 c;");
     93 
     94     for (int y = 0; y < kHeight; y++) {
     95         for (int x = 0; x < kWidth; x++) {
     96             GrGLShaderBuilder::ShaderBlock block(fsBuilder);
     97             fsBuilder->codeAppendf("float k = %s[%d * %d + %d];", kernel, y, kWidth, x);
     98             SkString coord;
     99             coord.printf("coord + vec2(%d, %d) * %s", x, y, imgInc);
    100             fDomain.sampleTexture(fsBuilder, domain, "c", coord, samplers[0]);
    101             if (!fConvolveAlpha) {
    102                 fsBuilder->codeAppend("c.rgb /= c.a;");
    103             }
    104             fsBuilder->codeAppend("sum += c * k;");
    105         }
    106     }
    107     if (fConvolveAlpha) {
    108         fsBuilder->codeAppendf("%s = sum * %s + %s;", outputColor, gain, bias);
    109         fsBuilder->codeAppendf("%s.rgb = clamp(%s.rgb, 0.0, %s.a);",
    110                                outputColor, outputColor, outputColor);
    111     } else {
    112         fDomain.sampleTexture(fsBuilder, domain, "c", coords2D, samplers[0]);
    113         fsBuilder->codeAppendf("%s.a = c.a;", outputColor);
    114         fsBuilder->codeAppendf("%s.rgb = sum.rgb * %s + %s;", outputColor, gain, bias);
    115         fsBuilder->codeAppendf("%s.rgb *= %s.a;", outputColor, outputColor);
    116     }
    117 
    118     SkString modulate;
    119     GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    120     fsBuilder->codeAppend(modulate.c_str());
    121 }
    122 
    123 void GrGLMatrixConvolutionEffect::GenKey(const GrProcessor& processor,
    124                                          const GrGLCaps&, GrProcessorKeyBuilder* b) {
    125     const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
    126     SkASSERT(m.kernelSize().width() <= 0x7FFF && m.kernelSize().height() <= 0xFFFF);
    127     uint32_t key = m.kernelSize().width() << 16 | m.kernelSize().height();
    128     key |= m.convolveAlpha() ? 1 << 31 : 0;
    129     b->add32(key);
    130     b->add32(GrTextureDomain::GLDomain::DomainKey(m.domain()));
    131 }
    132 
    133 void GrGLMatrixConvolutionEffect::setData(const GrGLProgramDataManager& pdman,
    134                                           const GrProcessor& processor) {
    135     const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
    136     GrTexture& texture = *conv.texture(0);
    137     // the code we generated was for a specific kernel size
    138     SkASSERT(conv.kernelSize() == fKernelSize);
    139     float imageIncrement[2];
    140     float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    141     imageIncrement[0] = 1.0f / texture.width();
    142     imageIncrement[1] = ySign / texture.height();
    143     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
    144     pdman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset());
    145     pdman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
    146     pdman.set1f(fGainUni, conv.gain());
    147     pdman.set1f(fBiasUni, conv.bias());
    148     fDomain.setData(pdman, conv.domain(), texture.origin());
    149 }
    150 
    151 GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
    152                                                      const SkIRect& bounds,
    153                                                      const SkISize& kernelSize,
    154                                                      const SkScalar* kernel,
    155                                                      SkScalar gain,
    156                                                      SkScalar bias,
    157                                                      const SkIPoint& kernelOffset,
    158                                                      GrTextureDomain::Mode tileMode,
    159                                                      bool convolveAlpha)
    160   : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)),
    161     fKernelSize(kernelSize),
    162     fGain(SkScalarToFloat(gain)),
    163     fBias(SkScalarToFloat(bias) / 255.0f),
    164     fConvolveAlpha(convolveAlpha),
    165     fDomain(GrTextureDomain::MakeTexelDomain(texture, bounds), tileMode) {
    166     for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
    167         fKernel[i] = SkScalarToFloat(kernel[i]);
    168     }
    169     fKernelOffset[0] = static_cast<float>(kernelOffset.x());
    170     fKernelOffset[1] = static_cast<float>(kernelOffset.y());
    171 }
    172 
    173 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
    174 }
    175 
    176 const GrBackendFragmentProcessorFactory& GrMatrixConvolutionEffect::getFactory() const {
    177     return GrTBackendFragmentProcessorFactory<GrMatrixConvolutionEffect>::getInstance();
    178 }
    179 
    180 bool GrMatrixConvolutionEffect::onIsEqual(const GrProcessor& sBase) const {
    181     const GrMatrixConvolutionEffect& s = sBase.cast<GrMatrixConvolutionEffect>();
    182     return this->texture(0) == s.texture(0) &&
    183            fKernelSize == s.kernelSize() &&
    184            !memcmp(fKernel, s.kernel(),
    185                    fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
    186            fGain == s.gain() &&
    187            fBias == s.bias() &&
    188            fKernelOffset == s.kernelOffset() &&
    189            fConvolveAlpha == s.convolveAlpha() &&
    190            fDomain == s.domain();
    191 }
    192 
    193 // Static function to create a 2D convolution
    194 GrFragmentProcessor*
    195 GrMatrixConvolutionEffect::CreateGaussian(GrTexture* texture,
    196                                           const SkIRect& bounds,
    197                                           const SkISize& kernelSize,
    198                                           SkScalar gain,
    199                                           SkScalar bias,
    200                                           const SkIPoint& kernelOffset,
    201                                           GrTextureDomain::Mode tileMode,
    202                                           bool convolveAlpha,
    203                                           SkScalar sigmaX,
    204                                           SkScalar sigmaY) {
    205     float kernel[MAX_KERNEL_SIZE];
    206     int width = kernelSize.width();
    207     int height = kernelSize.height();
    208     SkASSERT(width * height <= MAX_KERNEL_SIZE);
    209     float sum = 0.0f;
    210     float sigmaXDenom = 1.0f / (2.0f * SkScalarToFloat(SkScalarSquare(sigmaX)));
    211     float sigmaYDenom = 1.0f / (2.0f * SkScalarToFloat(SkScalarSquare(sigmaY)));
    212     int xRadius = width / 2;
    213     int yRadius = height / 2;
    214     for (int x = 0; x < width; x++) {
    215       float xTerm = static_cast<float>(x - xRadius);
    216       xTerm = xTerm * xTerm * sigmaXDenom;
    217       for (int y = 0; y < height; y++) {
    218         float yTerm = static_cast<float>(y - yRadius);
    219         float xyTerm = sk_float_exp(-(xTerm + yTerm * yTerm * sigmaYDenom));
    220         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
    221        // is dropped here, since we renormalize the kernel below.
    222         kernel[y * width + x] = xyTerm;
    223         sum += xyTerm;
    224       }
    225     }
    226     // Normalize the kernel
    227     float scale = 1.0f / sum;
    228     for (int i = 0; i < width * height; ++i) {
    229         kernel[i] *= scale;
    230     }
    231     return SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
    232                                                   bounds,
    233                                                   kernelSize,
    234                                                   kernel,
    235                                                   gain,
    236                                                   bias,
    237                                                   kernelOffset,
    238                                                   tileMode,
    239                                                   convolveAlpha));
    240 }
    241 
    242 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMatrixConvolutionEffect);
    243 
    244 GrFragmentProcessor* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
    245                                                            GrContext* context,
    246                                                            const GrDrawTargetCaps&,
    247                                                            GrTexture* textures[]) {
    248     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    249                                       GrProcessorUnitTest::kAlphaTextureIdx;
    250     int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
    251     int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
    252     SkISize kernelSize = SkISize::Make(width, height);
    253     SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
    254     for (int i = 0; i < width * height; i++) {
    255         kernel.get()[i] = random->nextSScalar1();
    256     }
    257     SkScalar gain = random->nextSScalar1();
    258     SkScalar bias = random->nextSScalar1();
    259     SkIPoint kernelOffset = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
    260                                            random->nextRangeU(0, kernelSize.height()));
    261     SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
    262                                        random->nextRangeU(0, textures[texIdx]->height()),
    263                                        random->nextRangeU(0, textures[texIdx]->width()),
    264                                        random->nextRangeU(0, textures[texIdx]->height()));
    265     GrTextureDomain::Mode tileMode = static_cast<GrTextureDomain::Mode>(random->nextRangeU(0, 2));
    266     bool convolveAlpha = random->nextBool();
    267     return GrMatrixConvolutionEffect::Create(textures[texIdx],
    268                                              bounds,
    269                                              kernelSize,
    270                                              kernel.get(),
    271                                              gain,
    272                                              bias,
    273                                              kernelOffset,
    274                                              tileMode,
    275                                              convolveAlpha);
    276 }
    277