diff options
author | senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-09-18 20:32:34 +0000 |
---|---|---|
committer | senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-09-18 20:32:34 +0000 |
commit | 5faa2dc266ec933b3961f985e5718236f1ecbe47 (patch) | |
tree | c8c50ad2e8667e89aacadb5f8d0f242ba888097b /src/effects | |
parent | d1688744d537d928699b6069f99c4470a0f6e772 (diff) |
Implements a matrix convolution filter (raster path only). The filtering loop
is templated on the tiling mode for speed: interior pixels are unconditionally
fetched; border pixels apply the appropriate tiling mode before fetching. It
handles target, bias, divisor (as gain), and edge modes (named to be more
skia-like). It does not handle the "preserveAlpha" semantics of
feConvolveMatrix, nor "kernelUnitLength".
git-svn-id: http://skia.googlecode.com/svn/trunk@5592 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/effects')
-rw-r--r-- | src/effects/SkMatrixConvolutionImageFilter.cpp | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp new file mode 100644 index 0000000000..3ff13d2236 --- /dev/null +++ b/src/effects/SkMatrixConvolutionImageFilter.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkMatrixConvolutionImageFilter.h" +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkFlattenableBuffers.h" +#include "SkRect.h" + +SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, SkImageFilter* input) + : INHERITED(input), + fKernelSize(kernelSize), + fGain(gain), + fBias(bias), + fTarget(target), + fTileMode(tileMode) { + uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight; + fKernel = SkNEW_ARRAY(SkScalar, size); + memcpy(fKernel, kernel, size * sizeof(SkScalar)); + SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth); + SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight); +} + +SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { + fKernelSize.fWidth = buffer.readInt(); + fKernelSize.fHeight = buffer.readInt(); + uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight; + fKernel = SkNEW_ARRAY(SkScalar, size); + uint32_t readSize = buffer.readScalarArray(fKernel); + SkASSERT(readSize == size); + fGain = buffer.readScalar(); + fBias = buffer.readScalar(); + fTarget.fX = buffer.readScalar(); + fTarget.fY = buffer.readScalar(); + fTileMode = (TileMode) buffer.readInt(); +} + +void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeInt(fKernelSize.fWidth); + buffer.writeInt(fKernelSize.fHeight); + buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight); + buffer.writeScalar(fGain); + buffer.writeScalar(fBias); + buffer.writeScalar(fTarget.fX); + buffer.writeScalar(fTarget.fY); + buffer.writeInt((int) fTileMode); +} + +SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() { + delete[] fKernel; +} + +class UncheckedPixelFetcher { +public: + static inline SkPMColor fetch(const SkBitmap& src, int x, int y) { + return *src.getAddr32(x, y); + } +}; + +class ClampPixelFetcher { +public: + static inline SkPMColor fetch(const SkBitmap& src, int x, int y) { + x = SkClampMax(x, src.width() - 1); + y = SkClampMax(y, src.height() - 1); + return *src.getAddr32(x, y); + } +}; + +class RepeatPixelFetcher { +public: + static inline SkPMColor fetch(const SkBitmap& src, int x, int y) { + x %= src.width(); + y %= src.height(); + if (x < 0) { + x += src.width(); + } + if (y < 0) { + y += src.height(); + } + return *src.getAddr32(x, y); + } +}; + +class ClampToBlackPixelFetcher { +public: + static inline SkPMColor fetch(const SkBitmap& src, int x, int y) { + if (x < 0 || x >= src.width() || y < 0 || y >= src.height()) { + return 0; + } else { + return *src.getAddr32(x, y); + } + } +}; + +template<class PixelFetcher> +void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { + for (int y = rect.fTop; y < rect.fBottom; ++y) { + SkPMColor* dptr = result->getAddr32(rect.fLeft, y); + for (int x = rect.fLeft; x < rect.fRight; ++x) { + SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0; + for (int cy = 0; cy < fKernelSize.fHeight; cy++) { + for (int cx = 0; cx < fKernelSize.fWidth; cx++) { + SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY); + SkScalar k = fKernel[cy * fKernelSize.fWidth + cx]; + sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k); + sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k); + sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k); + sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k); + } + } + int a = SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias); + int r = SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias); + int g = SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias); + int b = SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias); + *dptr++ = SkPackARGB32(SkClampMax(a, 255), + SkClampMax(r, 255), + SkClampMax(g, 255), + SkClampMax(b, 255)); + } + } +} + +void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { + filterPixels<UncheckedPixelFetcher>(src, result, rect); +} + +void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { + switch (fTileMode) { + case kClamp_TileMode: + filterPixels<ClampPixelFetcher>(src, result, rect); + break; + case kRepeat_TileMode: + filterPixels<RepeatPixelFetcher>(src, result, rect); + break; + case kClampToBlack_TileMode: + filterPixels<ClampToBlackPixelFetcher>(src, result, rect); + break; + } +} + +bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, + const SkBitmap& source, + const SkMatrix& matrix, + SkBitmap* result, + SkIPoint* loc) { + SkBitmap src = this->getInputResult(proxy, source, matrix, loc); + if (src.config() != SkBitmap::kARGB_8888_Config) { + return false; + } + + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return false; + } + + result->setConfig(src.config(), src.width(), src.height()); + result->allocPixels(); + + SkIRect interior = SkIRect::MakeXYWH(fTarget.fX, fTarget.fY, + src.width() - fKernelSize.fWidth + 1, + src.height() - fKernelSize.fHeight + 1); + SkIRect top = SkIRect::MakeWH(src.width(), fTarget.fY); + SkIRect bottom = SkIRect::MakeLTRB(0, interior.bottom(), + src.width(), src.height()); + SkIRect left = SkIRect::MakeXYWH(0, interior.top(), + fTarget.fX, interior.height()); + SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(), + src.width(), interior.bottom()); + filterBorderPixels(src, result, top); + filterBorderPixels(src, result, left); + filterInteriorPixels(src, result, interior); + filterBorderPixels(src, result, right); + filterBorderPixels(src, result, bottom); + return true; +} + +SK_DEFINE_FLATTENABLE_REGISTRAR(SkMatrixConvolutionImageFilter) + |