SkMatrixConvolutionImageFilter.cpp revision 8b0e8ac5f582de80356019406e2975079bf0829d
1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)/*
2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) * Copyright 2012 The Android Open Source Project
3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) *
4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) * Use of this source code is governed by a BSD-style license that can be
568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * found in the LICENSE file.
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) */
7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "SkMatrixConvolutionImageFilter.h"
923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "SkBitmap.h"
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "SkColorPriv.h"
1103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "SkReadBuffer.h"
12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "SkWriteBuffer.h"
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "SkRect.h"
1423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "SkUnPreMultiply.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#if SK_SUPPORT_GPU
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "gl/GrGLEffect.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "effects/GrSingleTextureEffect.h"
1903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "GrTBackendEffectFactory.h"
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "GrTexture.h"
2103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "SkMatrix.h"
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#endif
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic bool tile_mode_is_valid(SkMatrixConvolutionImageFilter::TileMode tileMode) {
25d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    switch (tileMode) {
2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case SkMatrixConvolutionImageFilter::kClamp_TileMode:
271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return true;
3003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    default:
31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        break;
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
33d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return false;
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const SkISize& kernelSize,
38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const SkScalar* kernel,
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    SkScalar gain,
406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    SkScalar bias,
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const SkIPoint& target,
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    TileMode tileMode,
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    bool convolveAlpha,
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    SkImageFilter* input,
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const CropRect* cropRect)
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  : INHERITED(input, cropRect),
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fKernelSize(kernelSize),
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fGain(gain),
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fBias(bias),
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fTarget(target),
516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fTileMode(tileMode),
526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fConvolveAlpha(convolveAlpha) {
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    fKernel = SkNEW_ARRAY(SkScalar, size);
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    memcpy(fKernel, kernel, size * sizeof(SkScalar));
5603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkReadBuffer& buffer)
6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    : INHERITED(1, buffer) {
636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // We need to be able to read at most SK_MaxS32 bytes, so divide that
646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // by the size of a scalar to know how many scalars we can read.
656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    static const int32_t kMaxSize = SK_MaxS32 / sizeof(SkScalar);
666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fKernelSize.fWidth = buffer.readInt();
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fKernelSize.fHeight = buffer.readInt();
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if ((fKernelSize.fWidth >= 1) && (fKernelSize.fHeight >= 1) &&
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // Make sure size won't be larger than a signed int,
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // which would still be extremely large for a kernel,
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // but we don't impose a hard limit for kernel size
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        (kMaxSize / fKernelSize.fWidth >= fKernelSize.fHeight)) {
736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        size_t size = fKernelSize.fWidth * fKernelSize.fHeight;
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        fKernel = SkNEW_ARRAY(SkScalar, size);
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        SkDEBUGCODE(bool success =) buffer.readScalarArray(fKernel, size);
766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        SkASSERT(success);
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    } else {
786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        fKernel = 0;
7923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    fGain = buffer.readScalar();
8123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    fBias = buffer.readScalar();
82d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    fTarget.fX = buffer.readInt();
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    fTarget.fY = buffer.readInt();
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    fTileMode = (TileMode) buffer.readInt();
85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    fConvolveAlpha = buffer.readBool();
86d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    buffer.validate((fKernel != 0) &&
8723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                    SkScalarIsFinite(fGain) &&
8823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                    SkScalarIsFinite(fBias) &&
8923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                    tile_mode_is_valid(fTileMode));
9023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
9123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
9323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    this->INHERITED::flatten(buffer);
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    buffer.writeInt(fKernelSize.fWidth);
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    buffer.writeInt(fKernelSize.fHeight);
9623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
9703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    buffer.writeScalar(fGain);
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    buffer.writeScalar(fBias);
9903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    buffer.writeInt(fTarget.fX);
10023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    buffer.writeInt(fTarget.fY);
10123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    buffer.writeInt((int) fTileMode);
10223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    buffer.writeBool(fConvolveAlpha);
10323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
10423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
10523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
10623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    delete[] fKernel;
10703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
10823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
10923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)class UncheckedPixelFetcher {
11023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)public:
11123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
11223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        return *src.getAddr32(x, y);
11323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
114010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)};
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class ClampPixelFetcher {
11723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)public:
118010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
11923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        x = SkPin32(x, bounds.fLeft, bounds.fRight - 1);
12023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        y = SkPin32(y, bounds.fTop, bounds.fBottom - 1);
12146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        return *src.getAddr32(x, y);
12223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
12323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)};
12423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
12523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)class RepeatPixelFetcher {
12623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)public:
12723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
12823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        x = (x - bounds.left()) % bounds.width() + bounds.left();
12923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        y = (y - bounds.top()) % bounds.height() + bounds.top();
13023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        if (x < bounds.left()) {
13123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)            x += bounds.width();
13223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        }
13303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        if (y < bounds.top()) {
13423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)            y += bounds.height();
13523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        }
13623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        return *src.getAddr32(x, y);
13723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
13923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
14023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)class ClampToBlackPixelFetcher {
14123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)public:
1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            return 0;
14523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        } else {
146010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            return *src.getAddr32(x, y);
14723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        }
14823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
14946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)};
15023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
15123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)template<class PixelFetcher, bool convolveAlpha>
15223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
15323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                                  SkBitmap* result,
15423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                                  const SkIRect& rect,
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                                  const SkIRect& bounds) {
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    for (int y = rect.fTop; y < rect.fBottom; ++y) {
15723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
158010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (int x = rect.fLeft; x < rect.fRight; ++x) {
159010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
160010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
161010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
162010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    SkPMColor s = PixelFetcher::fetch(src,
163010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                      x + cx - fTarget.fX,
164010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                      y + cy - fTarget.fY,
1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                                      bounds);
16603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    if (convolveAlpha) {
16803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                        sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
169010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    }
170010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
17203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                }
174010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
17523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)            int a = convolveAlpha
17623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                  ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  : 255;
1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
179d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
180d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
181d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            if (!convolveAlpha) {
182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
18323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                *dptr++ = SkPreMultiplyARGB(a, r, g, b);
184d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            } else {
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                *dptr++ = SkPackARGB32(a, r, g, b);
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            }
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
188d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
190116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
191d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)template<class PixelFetcher>
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
193d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                  SkBitmap* result,
19403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                                  const SkIRect& rect,
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                                  const SkIRect& bounds) {
19603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (fConvolveAlpha) {
197d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        filterPixels<PixelFetcher, true>(src, result, rect, bounds);
198d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    } else {
199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        filterPixels<PixelFetcher, false>(src, result, rect, bounds);
2001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
2011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
202d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
203d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
20403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                                          SkBitmap* result,
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                          const SkIRect& rect,
206d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                          const SkIRect& bounds) {
207d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
2095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
21046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
21146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                                        SkBitmap* result,
2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                                        const SkIRect& rect,
21346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                                        const SkIRect& bounds) {
21446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    switch (fTileMode) {
21546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        case kClamp_TileMode:
21623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)            filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
217d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            break;
218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        case kRepeat_TileMode:
219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
220d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            break;
2211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        case kClampToBlack_TileMode:
222010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            break;
2241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
225010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
227d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// FIXME:  This should be refactored to SkImageFilterUtils for
228010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// use by other filters.  For now, we assume the input is always
22903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// premultiplied and unpremultiply it
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
23168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles){
23268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    SkAutoLockPixels alp(src);
23368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!src.getPixels()) {
23468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        return SkBitmap();
23568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    SkBitmap result;
237d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    result.setConfig(src.config(), src.width(), src.height());
238d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    result.allocPixels();
239d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!result.getPixels()) {
2404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return SkBitmap();
2414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
242d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    for (int y = 0; y < src.height(); ++y) {
243d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        const uint32_t* srcRow = src.getAddr32(0, y);
244d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        uint32_t* dstRow = result.getAddr32(0, y);
24503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        for (int x = 0; x < src.width(); ++x) {
246d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
247d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
248d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
249d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return result;
2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
2511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
25223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
253d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                   const SkBitmap& source,
254d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                                   const SkMatrix& matrix,
2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                                   SkBitmap* result,
25646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                                   SkIPoint* offset) {
2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    SkBitmap src = source;
25846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    SkIPoint srcOffset = SkIPoint::Make(0, 0);
2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset)) {
26046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        return false;
261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
263d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (src.config() != SkBitmap::kARGB_8888_Config) {
2641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return false;
265010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
266d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
267d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    SkIRect bounds;
2681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src.getBounds(&bounds);
269010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    bounds.offset(srcOffset);
270d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!this->applyCropRect(&bounds, matrix)) {
271d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return false;
272d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
273010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
27403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (!fConvolveAlpha && !src.isOpaque()) {
27546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        src = unpremultiplyBitmap(src);
276d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
277d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SkAutoLockPixels alp(src);
27923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    if (!src.getPixels()) {
280f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return false;
2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
283f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    result->setConfig(src.config(), bounds.width(), bounds.height());
284f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    result->allocPixels();
28523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    if (!result->getPixels()) {
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return false;
287f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
288f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
289f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    offset->fX = bounds.fLeft;
29023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    offset->fY = bounds.fTop;
291f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bounds.offset(-srcOffset);
29203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fTarget.fX,
2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                         bounds.top() + fTarget.fY,
29403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                         bounds.width() - fKernelSize.fWidth + 1,
2955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                         bounds.height() - fKernelSize.fHeight + 1);
2965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top());
2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(),
2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       bounds.right(), bounds.bottom());
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(),
3005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                     interior.left(), interior.bottom());
3015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
3025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                      bounds.right(), interior.bottom());
3035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    filterBorderPixels(src, result, top, bounds);
3045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    filterBorderPixels(src, result, left, bounds);
305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    filterInteriorPixels(src, result, interior, bounds);
306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    filterBorderPixels(src, result, right, bounds);
307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    filterBorderPixels(src, result, bottom, bounds);
308f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
311f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#if SK_SUPPORT_GPU
312f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
314f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
315f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class GrGLMatrixConvolutionEffect;
31603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class GrMatrixConvolutionEffect : public GrSingleTextureEffect {
318f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)public:
319f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
320f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    static GrEffectRef* Create(GrTexture* texture,
3215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                               const SkIRect& bounds,
32246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                               const SkISize& kernelSize,
323f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                               const SkScalar* kernel,
32446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                               SkScalar gain,
32546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                               SkScalar bias,
32646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                               const SkIPoint& target,
32723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                               TileMode tileMode,
328f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                               bool convolveAlpha) {
329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        AutoEffectUnref effect(SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                                                      bounds,
331f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                                                      kernelSize,
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                                      kernel,
333010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                                      gain,
334f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                                                      bias,
335f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                                                      target,
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                                      tileMode,
337010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                                      convolveAlpha)));
338f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return CreateEffectRef(effect);
339f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
340f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    virtual ~GrMatrixConvolutionEffect();
341f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
34246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    virtual void getConstantColorComponents(GrColor* color,
343f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                            uint32_t* validFlags) const SK_OVERRIDE {
344f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        // TODO: Try to do better?
345f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        *validFlags = 0;
346f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
347f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    static const char* Name() { return "MatrixConvolution"; }
349f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const SkIRect& bounds() const { return fBounds; }
350f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const SkISize& kernelSize() const { return fKernelSize; }
351f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const float* target() const { return fTarget; }
35203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const float* kernel() const { return fKernel; }
353f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    float gain() const { return fGain; }
354f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    float bias() const { return fBias; }
355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    TileMode tileMode() const { return fTileMode; }
356f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bool convolveAlpha() const { return fConvolveAlpha; }
3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    typedef GrGLMatrixConvolutionEffect GLEffect;
35923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuprivate:
36346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    GrMatrixConvolutionEffect(GrTexture*,
3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              const SkIRect& bounds,
36546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                              const SkISize& kernelSize,
3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              const SkScalar* kernel,
36746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                              SkScalar gain,
368cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                              SkScalar bias,
369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                              const SkIPoint& target,
370f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                              TileMode tileMode,
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              bool convolveAlpha);
372010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
373f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
374f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SkIRect  fBounds;
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    SkISize  fKernelSize;
377010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    float   *fKernel;
378f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    float    fGain;
379f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    float    fBias;
380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    float    fTarget[2];
381f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    TileMode fTileMode;
382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bool     fConvolveAlpha;
38346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    GR_DECLARE_EFFECT_TEST;
385f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
386    typedef GrSingleTextureEffect INHERITED;
387};
388
389class GrGLMatrixConvolutionEffect : public GrGLEffect {
390public:
391    GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
392                                const GrDrawEffect& effect);
393    virtual void emitCode(GrGLShaderBuilder*,
394                          const GrDrawEffect&,
395                          EffectKey,
396                          const char* outputColor,
397                          const char* inputColor,
398                          const TransformedCoordsArray&,
399                          const TextureSamplerArray&) SK_OVERRIDE;
400
401    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
402
403    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
404
405private:
406    typedef GrGLUniformManager::UniformHandle        UniformHandle;
407    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
408    SkISize             fKernelSize;
409    TileMode            fTileMode;
410    bool                fConvolveAlpha;
411
412    UniformHandle       fBoundsUni;
413    UniformHandle       fKernelUni;
414    UniformHandle       fImageIncrementUni;
415    UniformHandle       fTargetUni;
416    UniformHandle       fGainUni;
417    UniformHandle       fBiasUni;
418
419    typedef GrGLEffect INHERITED;
420};
421
422GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
423                                                         const GrDrawEffect& drawEffect)
424    : INHERITED(factory) {
425    const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
426    fKernelSize = m.kernelSize();
427    fTileMode = m.tileMode();
428    fConvolveAlpha = m.convolveAlpha();
429}
430
431static void appendTextureLookup(GrGLShaderBuilder* builder,
432                                const GrGLShaderBuilder::TextureSampler& sampler,
433                                const char* coord,
434                                const char* bounds,
435                                SkMatrixConvolutionImageFilter::TileMode tileMode) {
436    SkString clampedCoord;
437    switch (tileMode) {
438        case SkMatrixConvolutionImageFilter::kClamp_TileMode:
439            clampedCoord.printf("clamp(%s, %s.xy, %s.zw)", coord, bounds, bounds);
440            coord = clampedCoord.c_str();
441            break;
442        case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
443            clampedCoord.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy", coord, bounds, bounds, bounds, bounds);
444            coord = clampedCoord.c_str();
445            break;
446        case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
447            builder->fsCodeAppendf("clamp(%s, %s.xy, %s.zw) != %s ? vec4(0, 0, 0, 0) : ", coord, bounds, bounds, coord);
448            break;
449    }
450    builder->fsAppendTextureLookup(sampler, coord);
451}
452
453void GrGLMatrixConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
454                                           const GrDrawEffect&,
455                                           EffectKey key,
456                                           const char* outputColor,
457                                           const char* inputColor,
458                                           const TransformedCoordsArray& coords,
459                                           const TextureSamplerArray& samplers) {
460    sk_ignore_unused_variable(inputColor);
461    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
462    fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
463                                     kVec4f_GrSLType, "Bounds");
464    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
465                                             kVec2f_GrSLType, "ImageIncrement");
466    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
467                                             kFloat_GrSLType,
468                                             "Kernel",
469                                             fKernelSize.width() * fKernelSize.height());
470    fTargetUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
471                                             kVec2f_GrSLType, "Target");
472    fGainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
473                                   kFloat_GrSLType, "Gain");
474    fBiasUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
475                                   kFloat_GrSLType, "Bias");
476
477    const char* bounds = builder->getUniformCStr(fBoundsUni);
478    const char* target = builder->getUniformCStr(fTargetUni);
479    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
480    const char* kernel = builder->getUniformCStr(fKernelUni);
481    const char* gain = builder->getUniformCStr(fGainUni);
482    const char* bias = builder->getUniformCStr(fBiasUni);
483    int kWidth = fKernelSize.width();
484    int kHeight = fKernelSize.height();
485
486    builder->fsCodeAppend("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
487    builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords2D.c_str(), target, imgInc);
488    builder->fsCodeAppendf("\t\tfor (int y = 0; y < %d; y++) {\n", kHeight);
489    builder->fsCodeAppendf("\t\t\tfor (int x = 0; x < %d; x++) {\n", kWidth);
490    builder->fsCodeAppendf("\t\t\t\tfloat k = %s[y * %d + x];\n", kernel, kWidth);
491    builder->fsCodeAppendf("\t\t\t\tvec2 coord2 = coord + vec2(x, y) * %s;\n", imgInc);
492    builder->fsCodeAppend("\t\t\t\tvec4 c = ");
493    appendTextureLookup(builder, samplers[0], "coord2", bounds, fTileMode);
494    builder->fsCodeAppend(";\n");
495    if (!fConvolveAlpha) {
496        builder->fsCodeAppend("\t\t\t\tc.rgb /= c.a;\n");
497    }
498    builder->fsCodeAppend("\t\t\t\tsum += c * k;\n");
499    builder->fsCodeAppend("\t\t\t}\n");
500    builder->fsCodeAppend("\t\t}\n");
501    if (fConvolveAlpha) {
502        builder->fsCodeAppendf("\t\t%s = sum * %s + %s;\n", outputColor, gain, bias);
503        builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n",
504            outputColor, outputColor, outputColor);
505    } else {
506        builder->fsCodeAppend("\t\tvec4 c = ");
507        appendTextureLookup(builder, samplers[0], coords2D.c_str(), bounds, fTileMode);
508        builder->fsCodeAppend(";\n");
509        builder->fsCodeAppendf("\t\t%s.a = c.a;\n", outputColor);
510        builder->fsCodeAppendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
511        builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
512    }
513}
514
515namespace {
516
517int encodeXY(int x, int y) {
518    SkASSERT(x >= 1 && y >= 1 && x * y <= 32);
519    if (y < x)
520        return 0x40 | encodeXY(y, x);
521    else
522        return (0x40 >> x) | (y - x);
523}
524
525};
526
527GrGLEffect::EffectKey GrGLMatrixConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
528                                                          const GrGLCaps&) {
529    const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
530    EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
531    key |= m.tileMode() << 7;
532    key |= m.convolveAlpha() ? 1 << 9 : 0;
533    return key;
534}
535
536void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
537                                          const GrDrawEffect& drawEffect) {
538    const GrMatrixConvolutionEffect& conv = drawEffect.castEffect<GrMatrixConvolutionEffect>();
539    GrTexture& texture = *conv.texture(0);
540    // the code we generated was for a specific kernel size
541    SkASSERT(conv.kernelSize() == fKernelSize);
542    SkASSERT(conv.tileMode() == fTileMode);
543    float imageIncrement[2];
544    float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
545    imageIncrement[0] = 1.0f / texture.width();
546    imageIncrement[1] = ySign / texture.height();
547    uman.set2fv(fImageIncrementUni, 1, imageIncrement);
548    uman.set2fv(fTargetUni, 1, conv.target());
549    uman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
550    uman.set1f(fGainUni, conv.gain());
551    uman.set1f(fBiasUni, conv.bias());
552    const SkIRect& bounds = conv.bounds();
553    float left = (float) bounds.left() / texture.width();
554    float top = (float) bounds.top() / texture.height();
555    float right = (float) bounds.right() / texture.width();
556    float bottom = (float) bounds.bottom() / texture.height();
557    if (texture.origin() == kBottomLeft_GrSurfaceOrigin) {
558        uman.set4f(fBoundsUni, left, 1.0f - bottom, right, 1.0f - top);
559    } else {
560        uman.set4f(fBoundsUni, left, top, right, bottom);
561    }
562}
563
564GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
565                                                     const SkIRect& bounds,
566                                                     const SkISize& kernelSize,
567                                                     const SkScalar* kernel,
568                                                     SkScalar gain,
569                                                     SkScalar bias,
570                                                     const SkIPoint& target,
571                                                     TileMode tileMode,
572                                                     bool convolveAlpha)
573  : INHERITED(texture, MakeDivByTextureWHMatrix(texture)),
574    fBounds(bounds),
575    fKernelSize(kernelSize),
576    fGain(SkScalarToFloat(gain)),
577    fBias(SkScalarToFloat(bias) / 255.0f),
578    fTileMode(tileMode),
579    fConvolveAlpha(convolveAlpha) {
580    fKernel = new float[kernelSize.width() * kernelSize.height()];
581    for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
582        fKernel[i] = SkScalarToFloat(kernel[i]);
583    }
584    fTarget[0] = static_cast<float>(target.x());
585    fTarget[1] = static_cast<float>(target.y());
586    this->setWillNotUseInputColor();
587}
588
589GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
590    delete[] fKernel;
591}
592
593const GrBackendEffectFactory& GrMatrixConvolutionEffect::getFactory() const {
594    return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
595}
596
597bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
598    const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
599    return this->texture(0) == s.texture(0) &&
600           fKernelSize == s.kernelSize() &&
601           !memcmp(fKernel, s.kernel(),
602                   fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
603           fGain == s.gain() &&
604           fBias == s.bias() &&
605           fTarget == s.target() &&
606           fTileMode == s.tileMode() &&
607           fConvolveAlpha == s.convolveAlpha();
608}
609
610GR_DEFINE_EFFECT_TEST(GrMatrixConvolutionEffect);
611
612// A little bit less than the minimum # uniforms required by DX9SM2 (32).
613// Allows for a 5x5 kernel (or 25x1, for that matter).
614#define MAX_KERNEL_SIZE 25
615
616GrEffectRef* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
617                                                   GrContext* context,
618                                                   const GrDrawTargetCaps&,
619                                                   GrTexture* textures[]) {
620    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
621                                      GrEffectUnitTest::kAlphaTextureIdx;
622    int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
623    int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
624    SkISize kernelSize = SkISize::Make(width, height);
625    SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
626    for (int i = 0; i < width * height; i++) {
627        kernel.get()[i] = random->nextSScalar1();
628    }
629    SkScalar gain = random->nextSScalar1();
630    SkScalar bias = random->nextSScalar1();
631    SkIPoint target = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
632                                     random->nextRangeU(0, kernelSize.height()));
633    SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
634                                       random->nextRangeU(0, textures[texIdx]->height()),
635                                       random->nextRangeU(0, textures[texIdx]->width()),
636                                       random->nextRangeU(0, textures[texIdx]->height()));
637    TileMode tileMode = static_cast<TileMode>(random->nextRangeU(0, 2));
638    bool convolveAlpha = random->nextBool();
639    return GrMatrixConvolutionEffect::Create(textures[texIdx],
640                                             bounds,
641                                             kernelSize,
642                                             kernel.get(),
643                                             gain,
644                                             bias,
645                                             target,
646                                             tileMode,
647                                             convolveAlpha);
648}
649
650bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffectRef** effect,
651                                                 GrTexture* texture,
652                                                 const SkMatrix&,
653                                                 const SkIRect& bounds
654                                                 ) const {
655    if (!effect) {
656        return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
657    }
658    SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE);
659    *effect = GrMatrixConvolutionEffect::Create(texture,
660                                                bounds,
661                                                fKernelSize,
662                                                fKernel,
663                                                fGain,
664                                                fBias,
665                                                fTarget,
666                                                fTileMode,
667                                                fConvolveAlpha);
668    return true;
669}
670
671///////////////////////////////////////////////////////////////////////////////
672
673#endif
674