SkMatrixConvolutionImageFilter.cpp revision 5faa2dc266ec933b3961f985e5718236f1ecbe47
1/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkMatrixConvolutionImageFilter.h"
9#include "SkBitmap.h"
10#include "SkColorPriv.h"
11#include "SkFlattenableBuffers.h"
12#include "SkRect.h"
13
14SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, SkImageFilter* input)
15  : INHERITED(input),
16    fKernelSize(kernelSize),
17    fGain(gain),
18    fBias(bias),
19    fTarget(target),
20    fTileMode(tileMode) {
21    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
22    fKernel = SkNEW_ARRAY(SkScalar, size);
23    memcpy(fKernel, kernel, size * sizeof(SkScalar));
24    SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
25    SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
26}
27
28SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
29    fKernelSize.fWidth = buffer.readInt();
30    fKernelSize.fHeight = buffer.readInt();
31    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
32    fKernel = SkNEW_ARRAY(SkScalar, size);
33    uint32_t readSize = buffer.readScalarArray(fKernel);
34    SkASSERT(readSize == size);
35    fGain = buffer.readScalar();
36    fBias = buffer.readScalar();
37    fTarget.fX = buffer.readScalar();
38    fTarget.fY = buffer.readScalar();
39    fTileMode = (TileMode) buffer.readInt();
40}
41
42void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
43    this->INHERITED::flatten(buffer);
44    buffer.writeInt(fKernelSize.fWidth);
45    buffer.writeInt(fKernelSize.fHeight);
46    buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
47    buffer.writeScalar(fGain);
48    buffer.writeScalar(fBias);
49    buffer.writeScalar(fTarget.fX);
50    buffer.writeScalar(fTarget.fY);
51    buffer.writeInt((int) fTileMode);
52}
53
54SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
55    delete[] fKernel;
56}
57
58class UncheckedPixelFetcher {
59public:
60    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
61        return *src.getAddr32(x, y);
62    }
63};
64
65class ClampPixelFetcher {
66public:
67    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
68        x = SkClampMax(x, src.width() - 1);
69        y = SkClampMax(y, src.height() - 1);
70        return *src.getAddr32(x, y);
71    }
72};
73
74class RepeatPixelFetcher {
75public:
76    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
77        x %= src.width();
78        y %= src.height();
79        if (x < 0) {
80            x += src.width();
81        }
82        if (y < 0) {
83            y += src.height();
84        }
85        return *src.getAddr32(x, y);
86    }
87};
88
89class ClampToBlackPixelFetcher {
90public:
91    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
92        if (x < 0 || x >= src.width() || y < 0 || y >= src.height()) {
93            return 0;
94        } else {
95            return *src.getAddr32(x, y);
96        }
97    }
98};
99
100template<class PixelFetcher>
101void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
102    for (int y = rect.fTop; y < rect.fBottom; ++y) {
103        SkPMColor* dptr = result->getAddr32(rect.fLeft, y);
104        for (int x = rect.fLeft; x < rect.fRight; ++x) {
105            SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
106            for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
107                for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
108                    SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY);
109                    SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
110                    sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
111                    sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
112                    sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
113                    sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
114                }
115            }
116            int a = SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias);
117            int r = SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias);
118            int g = SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias);
119            int b = SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias);
120            *dptr++ = SkPackARGB32(SkClampMax(a, 255),
121                                   SkClampMax(r, 255),
122                                   SkClampMax(g, 255),
123                                   SkClampMax(b, 255));
124        }
125    }
126}
127
128void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
129    filterPixels<UncheckedPixelFetcher>(src, result, rect);
130}
131
132void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
133    switch (fTileMode) {
134        case kClamp_TileMode:
135            filterPixels<ClampPixelFetcher>(src, result, rect);
136            break;
137        case kRepeat_TileMode:
138            filterPixels<RepeatPixelFetcher>(src, result, rect);
139            break;
140        case kClampToBlack_TileMode:
141            filterPixels<ClampToBlackPixelFetcher>(src, result, rect);
142            break;
143    }
144}
145
146bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
147                                                   const SkBitmap& source,
148                                                   const SkMatrix& matrix,
149                                                   SkBitmap* result,
150                                                   SkIPoint* loc) {
151    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
152    if (src.config() != SkBitmap::kARGB_8888_Config) {
153        return false;
154    }
155
156    SkAutoLockPixels alp(src);
157    if (!src.getPixels()) {
158        return false;
159    }
160
161    result->setConfig(src.config(), src.width(), src.height());
162    result->allocPixels();
163
164    SkIRect interior = SkIRect::MakeXYWH(fTarget.fX, fTarget.fY,
165                                         src.width() - fKernelSize.fWidth + 1,
166                                         src.height() - fKernelSize.fHeight + 1);
167    SkIRect top = SkIRect::MakeWH(src.width(), fTarget.fY);
168    SkIRect bottom = SkIRect::MakeLTRB(0, interior.bottom(),
169                                       src.width(), src.height());
170    SkIRect left = SkIRect::MakeXYWH(0, interior.top(),
171                                     fTarget.fX, interior.height());
172    SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
173                                      src.width(), interior.bottom());
174    filterBorderPixels(src, result, top);
175    filterBorderPixels(src, result, left);
176    filterInteriorPixels(src, result, interior);
177    filterBorderPixels(src, result, right);
178    filterBorderPixels(src, result, bottom);
179    return true;
180}
181
182SK_DEFINE_FLATTENABLE_REGISTRAR(SkMatrixConvolutionImageFilter)
183
184