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