blob: e5acf563f332ff39fa2df991b62b46a54e1678fa [file] [log] [blame]
senorblanco@chromium.org5faa2dc2012-09-18 20:32:34 +00001/*
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 }
senorblanco@chromium.orgcc9471c2012-09-20 17:59:49 +0000116 int a = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255);
117 int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
118 int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
119 int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
120 *dptr++ = SkPackARGB32(a, r, g, b);
senorblanco@chromium.org5faa2dc2012-09-18 20:32:34 +0000121 }
122 }
123}
124
125void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
126 filterPixels<UncheckedPixelFetcher>(src, result, rect);
127}
128
129void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
130 switch (fTileMode) {
131 case kClamp_TileMode:
132 filterPixels<ClampPixelFetcher>(src, result, rect);
133 break;
134 case kRepeat_TileMode:
135 filterPixels<RepeatPixelFetcher>(src, result, rect);
136 break;
137 case kClampToBlack_TileMode:
138 filterPixels<ClampToBlackPixelFetcher>(src, result, rect);
139 break;
140 }
141}
142
143bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
144 const SkBitmap& source,
145 const SkMatrix& matrix,
146 SkBitmap* result,
147 SkIPoint* loc) {
148 SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
149 if (src.config() != SkBitmap::kARGB_8888_Config) {
150 return false;
151 }
152
153 SkAutoLockPixels alp(src);
154 if (!src.getPixels()) {
155 return false;
156 }
157
158 result->setConfig(src.config(), src.width(), src.height());
159 result->allocPixels();
160
161 SkIRect interior = SkIRect::MakeXYWH(fTarget.fX, fTarget.fY,
162 src.width() - fKernelSize.fWidth + 1,
163 src.height() - fKernelSize.fHeight + 1);
164 SkIRect top = SkIRect::MakeWH(src.width(), fTarget.fY);
165 SkIRect bottom = SkIRect::MakeLTRB(0, interior.bottom(),
166 src.width(), src.height());
167 SkIRect left = SkIRect::MakeXYWH(0, interior.top(),
168 fTarget.fX, interior.height());
169 SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
170 src.width(), interior.bottom());
171 filterBorderPixels(src, result, top);
172 filterBorderPixels(src, result, left);
173 filterInteriorPixels(src, result, interior);
174 filterBorderPixels(src, result, right);
175 filterBorderPixels(src, result, bottom);
176 return true;
177}
178
senorblanco@chromium.orgb4ca9df2012-09-19 22:50:21 +0000179SK_DEFINE_FLATTENABLE_REGISTRAR(SkMatrixConvolutionImageFilter)