SkMatrixConvolutionImageFilter.cpp revision 8640d5024d57da5508bdf7585849e3b1f1cb365b
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#include "SkUnPreMultiply.h"
14
15SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input)
16  : INHERITED(input),
17    fKernelSize(kernelSize),
18    fGain(gain),
19    fBias(bias),
20    fTarget(target),
21    fTileMode(tileMode),
22    fConvolveAlpha(convolveAlpha) {
23    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
24    fKernel = SkNEW_ARRAY(SkScalar, size);
25    memcpy(fKernel, kernel, size * sizeof(SkScalar));
26    SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
27    SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
28}
29
30SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
31    fKernelSize.fWidth = buffer.readInt();
32    fKernelSize.fHeight = buffer.readInt();
33    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
34    fKernel = SkNEW_ARRAY(SkScalar, size);
35    uint32_t readSize = buffer.readScalarArray(fKernel);
36    SkASSERT(readSize == size);
37    fGain = buffer.readScalar();
38    fBias = buffer.readScalar();
39    fTarget.fX = buffer.readScalar();
40    fTarget.fY = buffer.readScalar();
41    fTileMode = (TileMode) buffer.readInt();
42    fConvolveAlpha = buffer.readBool();
43}
44
45void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
46    this->INHERITED::flatten(buffer);
47    buffer.writeInt(fKernelSize.fWidth);
48    buffer.writeInt(fKernelSize.fHeight);
49    buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
50    buffer.writeScalar(fGain);
51    buffer.writeScalar(fBias);
52    buffer.writeScalar(fTarget.fX);
53    buffer.writeScalar(fTarget.fY);
54    buffer.writeInt((int) fTileMode);
55    buffer.writeBool(fConvolveAlpha);
56}
57
58SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
59    delete[] fKernel;
60}
61
62class UncheckedPixelFetcher {
63public:
64    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
65        return *src.getAddr32(x, y);
66    }
67};
68
69class ClampPixelFetcher {
70public:
71    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
72        x = SkClampMax(x, src.width() - 1);
73        y = SkClampMax(y, src.height() - 1);
74        return *src.getAddr32(x, y);
75    }
76};
77
78class RepeatPixelFetcher {
79public:
80    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
81        x %= src.width();
82        y %= src.height();
83        if (x < 0) {
84            x += src.width();
85        }
86        if (y < 0) {
87            y += src.height();
88        }
89        return *src.getAddr32(x, y);
90    }
91};
92
93class ClampToBlackPixelFetcher {
94public:
95    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
96        if (x < 0 || x >= src.width() || y < 0 || y >= src.height()) {
97            return 0;
98        } else {
99            return *src.getAddr32(x, y);
100        }
101    }
102};
103
104template<class PixelFetcher, bool convolveAlpha>
105void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
106    for (int y = rect.fTop; y < rect.fBottom; ++y) {
107        SkPMColor* dptr = result->getAddr32(rect.fLeft, y);
108        for (int x = rect.fLeft; x < rect.fRight; ++x) {
109            SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
110            for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
111                for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
112                    SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY);
113                    SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
114                    if (convolveAlpha) {
115                        sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
116                    }
117                    sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
118                    sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
119                    sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
120                }
121            }
122            int a = convolveAlpha
123                  ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
124                  : SkGetPackedA32(PixelFetcher::fetch(src, x, y));
125            int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
126            int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
127            int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
128            if (!convolveAlpha) {
129                *dptr++ = SkPreMultiplyARGB(a, r, g, b);
130            } else {
131                *dptr++ = SkPackARGB32(a, r, g, b);
132            }
133        }
134    }
135}
136
137template<class PixelFetcher>
138void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
139    if (fConvolveAlpha) {
140        filterPixels<PixelFetcher, true>(src, result, rect);
141    } else {
142        filterPixels<PixelFetcher, false>(src, result, rect);
143    }
144}
145
146void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
147    filterPixels<UncheckedPixelFetcher>(src, result, rect);
148}
149
150void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
151    switch (fTileMode) {
152        case kClamp_TileMode:
153            filterPixels<ClampPixelFetcher>(src, result, rect);
154            break;
155        case kRepeat_TileMode:
156            filterPixels<RepeatPixelFetcher>(src, result, rect);
157            break;
158        case kClampToBlack_TileMode:
159            filterPixels<ClampToBlackPixelFetcher>(src, result, rect);
160            break;
161    }
162}
163
164// FIXME:  This should be refactored to SkSingleInputImageFilter for
165// use by other filters.  For now, we assume the input is always
166// premultiplied and unpremultiply it
167static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
168{
169    SkAutoLockPixels alp(src);
170    if (!src.getPixels()) {
171        return SkBitmap();
172    }
173    SkBitmap result;
174    result.setConfig(src.config(), src.width(), src.height());
175    result.allocPixels();
176    if (!result.getPixels()) {
177        return SkBitmap();
178    }
179    for (int y = 0; y < src.height(); ++y) {
180        const uint32_t* srcRow = src.getAddr32(0, y);
181        uint32_t* dstRow = result.getAddr32(0, y);
182        for (int x = 0; x < src.width(); ++x) {
183            dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
184        }
185    }
186    return result;
187}
188
189bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
190                                                   const SkBitmap& source,
191                                                   const SkMatrix& matrix,
192                                                   SkBitmap* result,
193                                                   SkIPoint* loc) {
194    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
195    if (src.config() != SkBitmap::kARGB_8888_Config) {
196        return false;
197    }
198
199    if (!fConvolveAlpha && !src.isOpaque()) {
200        src = unpremultiplyBitmap(src);
201    }
202
203    SkAutoLockPixels alp(src);
204    if (!src.getPixels()) {
205        return false;
206    }
207
208    result->setConfig(src.config(), src.width(), src.height());
209    result->allocPixels();
210
211    SkIRect interior = SkIRect::MakeXYWH(fTarget.fX, fTarget.fY,
212                                         src.width() - fKernelSize.fWidth + 1,
213                                         src.height() - fKernelSize.fHeight + 1);
214    SkIRect top = SkIRect::MakeWH(src.width(), fTarget.fY);
215    SkIRect bottom = SkIRect::MakeLTRB(0, interior.bottom(),
216                                       src.width(), src.height());
217    SkIRect left = SkIRect::MakeXYWH(0, interior.top(),
218                                     fTarget.fX, interior.height());
219    SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
220                                      src.width(), interior.bottom());
221    filterBorderPixels(src, result, top);
222    filterBorderPixels(src, result, left);
223    filterInteriorPixels(src, result, interior);
224    filterBorderPixels(src, result, right);
225    filterBorderPixels(src, result, bottom);
226    return true;
227}
228