Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / effects / SkMatrixConvolutionImageFilter.cpp
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 "SkReadBuffer.h"
12 #include "SkWriteBuffer.h"
13 #include "SkRect.h"
14 #include "SkUnPreMultiply.h"
15
16 #if SK_SUPPORT_GPU
17 #include "effects/GrMatrixConvolutionEffect.h"
18 #endif
19
20 static bool tile_mode_is_valid(SkMatrixConvolutionImageFilter::TileMode tileMode) {
21     switch (tileMode) {
22     case SkMatrixConvolutionImageFilter::kClamp_TileMode:
23     case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
24     case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
25         return true;
26     default:
27         break;
28     }
29     return false;
30 }
31
32 // We need to be able to read at most SK_MaxS32 bytes, so divide that
33 // by the size of a scalar to know how many scalars we can read.
34 static const int32_t gMaxKernelSize = SK_MaxS32 / sizeof(SkScalar);
35
36 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(
37     const SkISize& kernelSize,
38     const SkScalar* kernel,
39     SkScalar gain,
40     SkScalar bias,
41     const SkIPoint& kernelOffset,
42     TileMode tileMode,
43     bool convolveAlpha,
44     SkImageFilter* input,
45     const CropRect* cropRect)
46   : INHERITED(1, &input, cropRect),
47     fKernelSize(kernelSize),
48     fGain(gain),
49     fBias(bias),
50     fKernelOffset(kernelOffset),
51     fTileMode(tileMode),
52     fConvolveAlpha(convolveAlpha) {
53     size_t size = (size_t) sk_64_mul(fKernelSize.width(), fKernelSize.height());
54     fKernel = SkNEW_ARRAY(SkScalar, size);
55     memcpy(fKernel, kernel, size * sizeof(SkScalar));
56     SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
57     SkASSERT(kernelOffset.fX >= 0 && kernelOffset.fX < kernelSize.fWidth);
58     SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight);
59 }
60
61 SkMatrixConvolutionImageFilter* SkMatrixConvolutionImageFilter::Create(
62     const SkISize& kernelSize,
63     const SkScalar* kernel,
64     SkScalar gain,
65     SkScalar bias,
66     const SkIPoint& kernelOffset,
67     TileMode tileMode,
68     bool convolveAlpha,
69     SkImageFilter* input,
70     const CropRect* cropRect) {
71     if (kernelSize.width() < 1 || kernelSize.height() < 1) {
72         return NULL;
73     }
74     if (gMaxKernelSize / kernelSize.fWidth < kernelSize.fHeight) {
75         return NULL;
76     }
77     if (!kernel) {
78         return NULL;
79     }
80     return SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias,
81                                                        kernelOffset, tileMode, convolveAlpha,
82                                                        input, cropRect));
83 }
84
85 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkReadBuffer& buffer)
86     : INHERITED(1, buffer) {
87     fKernelSize.fWidth = buffer.readInt();
88     fKernelSize.fHeight = buffer.readInt();
89     if ((fKernelSize.fWidth >= 1) && (fKernelSize.fHeight >= 1) &&
90         // Make sure size won't be larger than a signed int,
91         // which would still be extremely large for a kernel,
92         // but we don't impose a hard limit for kernel size
93         (gMaxKernelSize / fKernelSize.fWidth >= fKernelSize.fHeight)) {
94         size_t size = fKernelSize.fWidth * fKernelSize.fHeight;
95         fKernel = SkNEW_ARRAY(SkScalar, size);
96         SkDEBUGCODE(bool success =) buffer.readScalarArray(fKernel, size);
97         SkASSERT(success);
98     } else {
99         fKernel = 0;
100     }
101     fGain = buffer.readScalar();
102     fBias = buffer.readScalar();
103     fKernelOffset.fX = buffer.readInt();
104     fKernelOffset.fY = buffer.readInt();
105     fTileMode = (TileMode) buffer.readInt();
106     fConvolveAlpha = buffer.readBool();
107     buffer.validate((fKernel != 0) &&
108                     SkScalarIsFinite(fGain) &&
109                     SkScalarIsFinite(fBias) &&
110                     tile_mode_is_valid(fTileMode) &&
111                     (fKernelOffset.fX >= 0) && (fKernelOffset.fX < fKernelSize.fWidth) &&
112                     (fKernelOffset.fY >= 0) && (fKernelOffset.fY < fKernelSize.fHeight));
113 }
114
115 void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
116     this->INHERITED::flatten(buffer);
117     buffer.writeInt(fKernelSize.fWidth);
118     buffer.writeInt(fKernelSize.fHeight);
119     buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
120     buffer.writeScalar(fGain);
121     buffer.writeScalar(fBias);
122     buffer.writeInt(fKernelOffset.fX);
123     buffer.writeInt(fKernelOffset.fY);
124     buffer.writeInt((int) fTileMode);
125     buffer.writeBool(fConvolveAlpha);
126 }
127
128 SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
129     delete[] fKernel;
130 }
131
132 class UncheckedPixelFetcher {
133 public:
134     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
135         return *src.getAddr32(x, y);
136     }
137 };
138
139 class ClampPixelFetcher {
140 public:
141     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
142         x = SkPin32(x, bounds.fLeft, bounds.fRight - 1);
143         y = SkPin32(y, bounds.fTop, bounds.fBottom - 1);
144         return *src.getAddr32(x, y);
145     }
146 };
147
148 class RepeatPixelFetcher {
149 public:
150     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
151         x = (x - bounds.left()) % bounds.width() + bounds.left();
152         y = (y - bounds.top()) % bounds.height() + bounds.top();
153         if (x < bounds.left()) {
154             x += bounds.width();
155         }
156         if (y < bounds.top()) {
157             y += bounds.height();
158         }
159         return *src.getAddr32(x, y);
160     }
161 };
162
163 class ClampToBlackPixelFetcher {
164 public:
165     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
166         if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
167             return 0;
168         } else {
169             return *src.getAddr32(x, y);
170         }
171     }
172 };
173
174 template<class PixelFetcher, bool convolveAlpha>
175 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
176                                                   SkBitmap* result,
177                                                   const SkIRect& r,
178                                                   const SkIRect& bounds) const {
179     SkIRect rect(r);
180     if (!rect.intersect(bounds)) {
181         return;
182     }
183     for (int y = rect.fTop; y < rect.fBottom; ++y) {
184         SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
185         for (int x = rect.fLeft; x < rect.fRight; ++x) {
186             SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
187             for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
188                 for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
189                     SkPMColor s = PixelFetcher::fetch(src,
190                                                       x + cx - fKernelOffset.fX,
191                                                       y + cy - fKernelOffset.fY,
192                                                       bounds);
193                     SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
194                     if (convolveAlpha) {
195                         sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
196                     }
197                     sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
198                     sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
199                     sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
200                 }
201             }
202             int a = convolveAlpha
203                   ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
204                   : 255;
205             int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
206             int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
207             int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
208             if (!convolveAlpha) {
209                 a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
210                 *dptr++ = SkPreMultiplyARGB(a, r, g, b);
211             } else {
212                 *dptr++ = SkPackARGB32(a, r, g, b);
213             }
214         }
215     }
216 }
217
218 template<class PixelFetcher>
219 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
220                                                   SkBitmap* result,
221                                                   const SkIRect& rect,
222                                                   const SkIRect& bounds) const {
223     if (fConvolveAlpha) {
224         filterPixels<PixelFetcher, true>(src, result, rect, bounds);
225     } else {
226         filterPixels<PixelFetcher, false>(src, result, rect, bounds);
227     }
228 }
229
230 void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
231                                                           SkBitmap* result,
232                                                           const SkIRect& rect,
233                                                           const SkIRect& bounds) const {
234     filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
235 }
236
237 void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
238                                                         SkBitmap* result,
239                                                         const SkIRect& rect,
240                                                         const SkIRect& bounds) const {
241     switch (fTileMode) {
242         case kClamp_TileMode:
243             filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
244             break;
245         case kRepeat_TileMode:
246             filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
247             break;
248         case kClampToBlack_TileMode:
249             filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
250             break;
251     }
252 }
253
254 // FIXME:  This should be refactored to SkImageFilterUtils for
255 // use by other filters.  For now, we assume the input is always
256 // premultiplied and unpremultiply it
257 static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
258 {
259     SkAutoLockPixels alp(src);
260     if (!src.getPixels()) {
261         return SkBitmap();
262     }
263     SkBitmap result;
264     if (!result.allocPixels(src.info())) {
265         return SkBitmap();
266     }
267     for (int y = 0; y < src.height(); ++y) {
268         const uint32_t* srcRow = src.getAddr32(0, y);
269         uint32_t* dstRow = result.getAddr32(0, y);
270         for (int x = 0; x < src.width(); ++x) {
271             dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
272         }
273     }
274     return result;
275 }
276
277 bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
278                                                    const SkBitmap& source,
279                                                    const Context& ctx,
280                                                    SkBitmap* result,
281                                                    SkIPoint* offset) const {
282     SkBitmap src = source;
283     SkIPoint srcOffset = SkIPoint::Make(0, 0);
284     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
285         return false;
286     }
287
288     if (src.colorType() != kN32_SkColorType) {
289         return false;
290     }
291
292     SkIRect bounds;
293     if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
294         return false;
295     }
296
297     if (!fConvolveAlpha && !src.isOpaque()) {
298         src = unpremultiplyBitmap(src);
299     }
300
301     SkAutoLockPixels alp(src);
302     if (!src.getPixels()) {
303         return false;
304     }
305
306     if (!result->allocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
307         return false;
308     }
309
310     offset->fX = bounds.fLeft;
311     offset->fY = bounds.fTop;
312     bounds.offset(-srcOffset);
313     SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fKernelOffset.fX,
314                                          bounds.top() + fKernelOffset.fY,
315                                          bounds.width() - fKernelSize.fWidth + 1,
316                                          bounds.height() - fKernelSize.fHeight + 1);
317     SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top());
318     SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(),
319                                        bounds.right(), bounds.bottom());
320     SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(),
321                                      interior.left(), interior.bottom());
322     SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
323                                       bounds.right(), interior.bottom());
324     filterBorderPixels(src, result, top, bounds);
325     filterBorderPixels(src, result, left, bounds);
326     filterInteriorPixels(src, result, interior, bounds);
327     filterBorderPixels(src, result, right, bounds);
328     filterBorderPixels(src, result, bottom, bounds);
329     return true;
330 }
331
332 bool SkMatrixConvolutionImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
333                                                     SkIRect* dst) const {
334     SkIRect bounds = src;
335     bounds.fRight += fKernelSize.width() - 1;
336     bounds.fBottom += fKernelSize.height() - 1;
337     bounds.offset(-fKernelOffset);
338     if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
339         return false;
340     }
341     *dst = bounds;
342     return true;
343 }
344
345 #if SK_SUPPORT_GPU
346
347 static GrTextureDomain::Mode convert_tilemodes(
348         SkMatrixConvolutionImageFilter::TileMode tileMode) {
349     switch (tileMode) {
350         case SkMatrixConvolutionImageFilter::kClamp_TileMode:
351             return GrTextureDomain::kClamp_Mode;
352         case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
353             return GrTextureDomain::kRepeat_Mode;
354         case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
355             return GrTextureDomain::kDecal_Mode;
356         default:
357             SkASSERT(false);
358     }
359     return GrTextureDomain::kIgnore_Mode;
360 }
361
362 bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffect** effect,
363                                                  GrTexture* texture,
364                                                  const SkMatrix&,
365                                                  const SkIRect& bounds) const {
366     if (!effect) {
367         return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
368     }
369     SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE);
370     *effect = GrMatrixConvolutionEffect::Create(texture,
371                                                 bounds,
372                                                 fKernelSize,
373                                                 fKernel,
374                                                 fGain,
375                                                 fBias,
376                                                 fKernelOffset,
377                                                 convert_tilemodes(fTileMode),
378                                                 fConvolveAlpha);
379     return true;
380 }
381 #endif