SkMatrixConvolutionImageFilter.cpp revision a34995e18b1f0a7d8c9f23451718bb30ff0105b0
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
15#if SK_SUPPORT_GPU
16#include "gl/GrGLEffect.h"
17#include "effects/GrSingleTextureEffect.h"
18#include "GrTBackendEffectFactory.h"
19#include "GrTexture.h"
20#include "SkMatrix.h"
21#endif
22
23SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(
24    const SkISize& kernelSize,
25    const SkScalar* kernel,
26    SkScalar gain,
27    SkScalar bias,
28    const SkIPoint& target,
29    TileMode tileMode,
30    bool convolveAlpha,
31    SkImageFilter* input,
32    const CropRect* cropRect)
33  : INHERITED(input, cropRect),
34    fKernelSize(kernelSize),
35    fGain(gain),
36    fBias(bias),
37    fTarget(target),
38    fTileMode(tileMode),
39    fConvolveAlpha(convolveAlpha) {
40    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
41    fKernel = SkNEW_ARRAY(SkScalar, size);
42    memcpy(fKernel, kernel, size * sizeof(SkScalar));
43    SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
44    SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
45    SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
46}
47
48SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer)
49    : INHERITED(buffer) {
50    fKernelSize.fWidth = buffer.readInt();
51    fKernelSize.fHeight = buffer.readInt();
52    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
53    fKernel = SkNEW_ARRAY(SkScalar, size);
54    SkDEBUGCODE(uint32_t readSize = )buffer.readScalarArray(fKernel);
55    SkASSERT(readSize == size);
56    fGain = buffer.readScalar();
57    fBias = buffer.readScalar();
58    fTarget.fX = buffer.readInt();
59    fTarget.fY = buffer.readInt();
60    fTileMode = (TileMode) buffer.readInt();
61    fConvolveAlpha = buffer.readBool();
62}
63
64void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
65    this->INHERITED::flatten(buffer);
66    buffer.writeInt(fKernelSize.fWidth);
67    buffer.writeInt(fKernelSize.fHeight);
68    buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
69    buffer.writeScalar(fGain);
70    buffer.writeScalar(fBias);
71    buffer.writeInt(fTarget.fX);
72    buffer.writeInt(fTarget.fY);
73    buffer.writeInt((int) fTileMode);
74    buffer.writeBool(fConvolveAlpha);
75}
76
77SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
78    delete[] fKernel;
79}
80
81class UncheckedPixelFetcher {
82public:
83    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
84        return *src.getAddr32(x, y);
85    }
86};
87
88class ClampPixelFetcher {
89public:
90    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
91        x = SkPin32(x, bounds.fLeft, bounds.fRight - 1);
92        y = SkPin32(y, bounds.fTop, bounds.fBottom - 1);
93        return *src.getAddr32(x, y);
94    }
95};
96
97class RepeatPixelFetcher {
98public:
99    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
100        x = (x - bounds.left()) % bounds.width() + bounds.left();
101        y = (y - bounds.top()) % bounds.height() + bounds.top();
102        if (x < bounds.left()) {
103            x += bounds.width();
104        }
105        if (y < bounds.top()) {
106            y += bounds.height();
107        }
108        return *src.getAddr32(x, y);
109    }
110};
111
112class ClampToBlackPixelFetcher {
113public:
114    static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
115        if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
116            return 0;
117        } else {
118            return *src.getAddr32(x, y);
119        }
120    }
121};
122
123template<class PixelFetcher, bool convolveAlpha>
124void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
125                                                  SkBitmap* result,
126                                                  const SkIRect& rect,
127                                                  const SkIRect& bounds) {
128    for (int y = rect.fTop; y < rect.fBottom; ++y) {
129        SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
130        for (int x = rect.fLeft; x < rect.fRight; ++x) {
131            SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
132            for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
133                for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
134                    SkPMColor s = PixelFetcher::fetch(src,
135                                                      x + cx - fTarget.fX,
136                                                      y + cy - fTarget.fY,
137                                                      bounds);
138                    SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
139                    if (convolveAlpha) {
140                        sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
141                    }
142                    sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
143                    sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
144                    sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
145                }
146            }
147            int a = convolveAlpha
148                  ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
149                  : 255;
150            int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
151            int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
152            int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
153            if (!convolveAlpha) {
154                a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
155                *dptr++ = SkPreMultiplyARGB(a, r, g, b);
156            } else {
157                *dptr++ = SkPackARGB32(a, r, g, b);
158            }
159        }
160    }
161}
162
163template<class PixelFetcher>
164void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
165                                                  SkBitmap* result,
166                                                  const SkIRect& rect,
167                                                  const SkIRect& bounds) {
168    if (fConvolveAlpha) {
169        filterPixels<PixelFetcher, true>(src, result, rect, bounds);
170    } else {
171        filterPixels<PixelFetcher, false>(src, result, rect, bounds);
172    }
173}
174
175void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
176                                                          SkBitmap* result,
177                                                          const SkIRect& rect,
178                                                          const SkIRect& bounds) {
179    filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
180}
181
182void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
183                                                        SkBitmap* result,
184                                                        const SkIRect& rect,
185                                                        const SkIRect& bounds) {
186    switch (fTileMode) {
187        case kClamp_TileMode:
188            filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
189            break;
190        case kRepeat_TileMode:
191            filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
192            break;
193        case kClampToBlack_TileMode:
194            filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
195            break;
196    }
197}
198
199// FIXME:  This should be refactored to SkImageFilterUtils for
200// use by other filters.  For now, we assume the input is always
201// premultiplied and unpremultiply it
202static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
203{
204    SkAutoLockPixels alp(src);
205    if (!src.getPixels()) {
206        return SkBitmap();
207    }
208    SkBitmap result;
209    result.setConfig(src.config(), src.width(), src.height());
210    result.allocPixels();
211    if (!result.getPixels()) {
212        return SkBitmap();
213    }
214    for (int y = 0; y < src.height(); ++y) {
215        const uint32_t* srcRow = src.getAddr32(0, y);
216        uint32_t* dstRow = result.getAddr32(0, y);
217        for (int x = 0; x < src.width(); ++x) {
218            dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
219        }
220    }
221    return result;
222}
223
224bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
225                                                   const SkBitmap& source,
226                                                   const SkMatrix& matrix,
227                                                   SkBitmap* result,
228                                                   SkIPoint* loc) {
229    SkBitmap src = source;
230    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
231        return false;
232    }
233
234    if (src.config() != SkBitmap::kARGB_8888_Config) {
235        return false;
236    }
237
238    SkIRect bounds;
239    src.getBounds(&bounds);
240    if (!this->applyCropRect(&bounds, matrix)) {
241        return false;
242    }
243
244    if (!fConvolveAlpha && !src.isOpaque()) {
245        src = unpremultiplyBitmap(src);
246    }
247
248    SkAutoLockPixels alp(src);
249    if (!src.getPixels()) {
250        return false;
251    }
252
253    result->setConfig(src.config(), bounds.width(), bounds.height());
254    result->allocPixels();
255
256    SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fTarget.fX,
257                                         bounds.top() + fTarget.fY,
258                                         bounds.width() - fKernelSize.fWidth + 1,
259                                         bounds.height() - fKernelSize.fHeight + 1);
260    SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top());
261    SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(),
262                                       bounds.right(), bounds.bottom());
263    SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(),
264                                     interior.left(), interior.bottom());
265    SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
266                                      bounds.right(), interior.bottom());
267    filterBorderPixels(src, result, top, bounds);
268    filterBorderPixels(src, result, left, bounds);
269    filterInteriorPixels(src, result, interior, bounds);
270    filterBorderPixels(src, result, right, bounds);
271    filterBorderPixels(src, result, bottom, bounds);
272    loc->fX += bounds.fLeft;
273    loc->fY += bounds.fTop;
274    return true;
275}
276
277#if SK_SUPPORT_GPU
278
279///////////////////////////////////////////////////////////////////////////////
280
281class GrGLMatrixConvolutionEffect;
282
283class GrMatrixConvolutionEffect : public GrSingleTextureEffect {
284public:
285    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
286    static GrEffectRef* Create(GrTexture* texture,
287                               const SkIRect& bounds,
288                               const SkISize& kernelSize,
289                               const SkScalar* kernel,
290                               SkScalar gain,
291                               SkScalar bias,
292                               const SkIPoint& target,
293                               TileMode tileMode,
294                               bool convolveAlpha) {
295        AutoEffectUnref effect(SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
296                                                                      bounds,
297                                                                      kernelSize,
298                                                                      kernel,
299                                                                      gain,
300                                                                      bias,
301                                                                      target,
302                                                                      tileMode,
303                                                                      convolveAlpha)));
304        return CreateEffectRef(effect);
305    }
306    virtual ~GrMatrixConvolutionEffect();
307
308    virtual void getConstantColorComponents(GrColor* color,
309                                            uint32_t* validFlags) const SK_OVERRIDE {
310        // TODO: Try to do better?
311        *validFlags = 0;
312    }
313
314    static const char* Name() { return "MatrixConvolution"; }
315    const SkIRect& bounds() const { return fBounds; }
316    const SkISize& kernelSize() const { return fKernelSize; }
317    const float* target() const { return fTarget; }
318    const float* kernel() const { return fKernel; }
319    float gain() const { return fGain; }
320    float bias() const { return fBias; }
321    TileMode tileMode() const { return fTileMode; }
322    bool convolveAlpha() const { return fConvolveAlpha; }
323
324    typedef GrGLMatrixConvolutionEffect GLEffect;
325
326    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
327
328private:
329    GrMatrixConvolutionEffect(GrTexture*,
330                              const SkIRect& bounds,
331                              const SkISize& kernelSize,
332                              const SkScalar* kernel,
333                              SkScalar gain,
334                              SkScalar bias,
335                              const SkIPoint& target,
336                              TileMode tileMode,
337                              bool convolveAlpha);
338
339    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
340
341    SkIRect  fBounds;
342    SkISize  fKernelSize;
343    float   *fKernel;
344    float    fGain;
345    float    fBias;
346    float    fTarget[2];
347    TileMode fTileMode;
348    bool     fConvolveAlpha;
349
350    GR_DECLARE_EFFECT_TEST;
351
352    typedef GrSingleTextureEffect INHERITED;
353};
354
355class GrGLMatrixConvolutionEffect : public GrGLEffect {
356public:
357    GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
358                                const GrDrawEffect& effect);
359    virtual void emitCode(GrGLShaderBuilder*,
360                          const GrDrawEffect&,
361                          EffectKey,
362                          const char* outputColor,
363                          const char* inputColor,
364                          const TransformedCoordsArray&,
365                          const TextureSamplerArray&) SK_OVERRIDE;
366
367    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
368
369    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
370
371private:
372    typedef GrGLUniformManager::UniformHandle        UniformHandle;
373    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
374    SkISize             fKernelSize;
375    TileMode            fTileMode;
376    bool                fConvolveAlpha;
377
378    UniformHandle       fBoundsUni;
379    UniformHandle       fKernelUni;
380    UniformHandle       fImageIncrementUni;
381    UniformHandle       fTargetUni;
382    UniformHandle       fGainUni;
383    UniformHandle       fBiasUni;
384
385    typedef GrGLEffect INHERITED;
386};
387
388GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
389                                                         const GrDrawEffect& drawEffect)
390    : INHERITED(factory) {
391    const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
392    fKernelSize = m.kernelSize();
393    fTileMode = m.tileMode();
394    fConvolveAlpha = m.convolveAlpha();
395}
396
397static void appendTextureLookup(GrGLShaderBuilder* builder,
398                                const GrGLShaderBuilder::TextureSampler& sampler,
399                                const char* coord,
400                                const char* bounds,
401                                SkMatrixConvolutionImageFilter::TileMode tileMode) {
402    SkString clampedCoord;
403    switch (tileMode) {
404        case SkMatrixConvolutionImageFilter::kClamp_TileMode:
405            clampedCoord.printf("clamp(%s, %s.xy, %s.zw)", coord, bounds, bounds);
406            coord = clampedCoord.c_str();
407            break;
408        case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
409            clampedCoord.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy", coord, bounds, bounds, bounds, bounds);
410            coord = clampedCoord.c_str();
411            break;
412        case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
413            builder->fsCodeAppendf("clamp(%s, %s.xy, %s.zw) != %s ? vec4(0, 0, 0, 0) : ", coord, bounds, bounds, coord);
414            break;
415    }
416    builder->fsAppendTextureLookup(sampler, coord);
417}
418
419void GrGLMatrixConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
420                                           const GrDrawEffect&,
421                                           EffectKey key,
422                                           const char* outputColor,
423                                           const char* inputColor,
424                                           const TransformedCoordsArray& coords,
425                                           const TextureSamplerArray& samplers) {
426    sk_ignore_unused_variable(inputColor);
427    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
428    fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
429                                     kVec4f_GrSLType, "Bounds");
430    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
431                                             kVec2f_GrSLType, "ImageIncrement");
432    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
433                                             kFloat_GrSLType,
434                                             "Kernel",
435                                             fKernelSize.width() * fKernelSize.height());
436    fTargetUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
437                                             kVec2f_GrSLType, "Target");
438    fGainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
439                                   kFloat_GrSLType, "Gain");
440    fBiasUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
441                                   kFloat_GrSLType, "Bias");
442
443    const char* bounds = builder->getUniformCStr(fBoundsUni);
444    const char* target = builder->getUniformCStr(fTargetUni);
445    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
446    const char* kernel = builder->getUniformCStr(fKernelUni);
447    const char* gain = builder->getUniformCStr(fGainUni);
448    const char* bias = builder->getUniformCStr(fBiasUni);
449    int kWidth = fKernelSize.width();
450    int kHeight = fKernelSize.height();
451
452    builder->fsCodeAppend("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
453    builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords2D.c_str(), target, imgInc);
454    builder->fsCodeAppendf("\t\tfor (int y = 0; y < %d; y++) {\n", kHeight);
455    builder->fsCodeAppendf("\t\t\tfor (int x = 0; x < %d; x++) {\n", kWidth);
456    builder->fsCodeAppendf("\t\t\t\tfloat k = %s[y * %d + x];\n", kernel, kWidth);
457    builder->fsCodeAppendf("\t\t\t\tvec2 coord2 = coord + vec2(x, y) * %s;\n", imgInc);
458    builder->fsCodeAppend("\t\t\t\tvec4 c = ");
459    appendTextureLookup(builder, samplers[0], "coord2", bounds, fTileMode);
460    builder->fsCodeAppend(";\n");
461    if (!fConvolveAlpha) {
462        builder->fsCodeAppend("\t\t\t\tc.rgb /= c.a;\n");
463    }
464    builder->fsCodeAppend("\t\t\t\tsum += c * k;\n");
465    builder->fsCodeAppend("\t\t\t}\n");
466    builder->fsCodeAppend("\t\t}\n");
467    if (fConvolveAlpha) {
468        builder->fsCodeAppendf("\t\t%s = sum * %s + %s;\n", outputColor, gain, bias);
469        builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n",
470            outputColor, outputColor, outputColor);
471    } else {
472        builder->fsCodeAppend("\t\tvec4 c = ");
473        appendTextureLookup(builder, samplers[0], coords2D.c_str(), bounds, fTileMode);
474        builder->fsCodeAppend(";\n");
475        builder->fsCodeAppendf("\t\t%s.a = c.a;\n", outputColor);
476        builder->fsCodeAppendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
477        builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
478    }
479}
480
481namespace {
482
483int encodeXY(int x, int y) {
484    SkASSERT(x >= 1 && y >= 1 && x * y <= 32);
485    if (y < x)
486        return 0x40 | encodeXY(y, x);
487    else
488        return (0x40 >> x) | (y - x);
489}
490
491};
492
493GrGLEffect::EffectKey GrGLMatrixConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
494                                                          const GrGLCaps&) {
495    const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
496    EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
497    key |= m.tileMode() << 7;
498    key |= m.convolveAlpha() ? 1 << 9 : 0;
499    return key;
500}
501
502void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
503                                          const GrDrawEffect& drawEffect) {
504    const GrMatrixConvolutionEffect& conv = drawEffect.castEffect<GrMatrixConvolutionEffect>();
505    GrTexture& texture = *conv.texture(0);
506    // the code we generated was for a specific kernel size
507    SkASSERT(conv.kernelSize() == fKernelSize);
508    SkASSERT(conv.tileMode() == fTileMode);
509    float imageIncrement[2];
510    float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
511    imageIncrement[0] = 1.0f / texture.width();
512    imageIncrement[1] = ySign / texture.height();
513    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
514    uman.set2fv(fTargetUni, 0, 1, conv.target());
515    uman.set1fv(fKernelUni, 0, fKernelSize.width() * fKernelSize.height(), conv.kernel());
516    uman.set1f(fGainUni, conv.gain());
517    uman.set1f(fBiasUni, conv.bias());
518    const SkIRect& bounds = conv.bounds();
519    float left = (float) bounds.left() / texture.width();
520    float top = (float) bounds.top() / texture.height();
521    float right = (float) bounds.right() / texture.width();
522    float bottom = (float) bounds.bottom() / texture.height();
523    if (texture.origin() == kBottomLeft_GrSurfaceOrigin) {
524        uman.set4f(fBoundsUni, left, 1.0f - bottom, right, 1.0f - top);
525    } else {
526        uman.set4f(fBoundsUni, left, top, right, bottom);
527    }
528}
529
530GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
531                                                     const SkIRect& bounds,
532                                                     const SkISize& kernelSize,
533                                                     const SkScalar* kernel,
534                                                     SkScalar gain,
535                                                     SkScalar bias,
536                                                     const SkIPoint& target,
537                                                     TileMode tileMode,
538                                                     bool convolveAlpha)
539  : INHERITED(texture, MakeDivByTextureWHMatrix(texture)),
540    fBounds(bounds),
541    fKernelSize(kernelSize),
542    fGain(SkScalarToFloat(gain)),
543    fBias(SkScalarToFloat(bias) / 255.0f),
544    fTileMode(tileMode),
545    fConvolveAlpha(convolveAlpha) {
546    fKernel = new float[kernelSize.width() * kernelSize.height()];
547    for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
548        fKernel[i] = SkScalarToFloat(kernel[i]);
549    }
550    fTarget[0] = static_cast<float>(target.x());
551    fTarget[1] = static_cast<float>(target.y());
552    this->setWillNotUseInputColor();
553}
554
555GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
556    delete[] fKernel;
557}
558
559const GrBackendEffectFactory& GrMatrixConvolutionEffect::getFactory() const {
560    return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
561}
562
563bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
564    const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
565    return this->texture(0) == s.texture(0) &&
566           fKernelSize == s.kernelSize() &&
567           !memcmp(fKernel, s.kernel(),
568                   fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
569           fGain == s.gain() &&
570           fBias == s.bias() &&
571           fTarget == s.target() &&
572           fTileMode == s.tileMode() &&
573           fConvolveAlpha == s.convolveAlpha();
574}
575
576GR_DEFINE_EFFECT_TEST(GrMatrixConvolutionEffect);
577
578// A little bit less than the minimum # uniforms required by DX9SM2 (32).
579// Allows for a 5x5 kernel (or 25x1, for that matter).
580#define MAX_KERNEL_SIZE 25
581
582GrEffectRef* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
583                                                   GrContext* context,
584                                                   const GrDrawTargetCaps&,
585                                                   GrTexture* textures[]) {
586    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
587                                      GrEffectUnitTest::kAlphaTextureIdx;
588    int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
589    int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
590    SkISize kernelSize = SkISize::Make(width, height);
591    SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
592    for (int i = 0; i < width * height; i++) {
593        kernel.get()[i] = random->nextSScalar1();
594    }
595    SkScalar gain = random->nextSScalar1();
596    SkScalar bias = random->nextSScalar1();
597    SkIPoint target = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
598                                     random->nextRangeU(0, kernelSize.height()));
599    SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
600                                       random->nextRangeU(0, textures[texIdx]->height()),
601                                       random->nextRangeU(0, textures[texIdx]->width()),
602                                       random->nextRangeU(0, textures[texIdx]->height()));
603    TileMode tileMode = static_cast<TileMode>(random->nextRangeU(0, 2));
604    bool convolveAlpha = random->nextBool();
605    return GrMatrixConvolutionEffect::Create(textures[texIdx],
606                                             bounds,
607                                             kernelSize,
608                                             kernel.get(),
609                                             gain,
610                                             bias,
611                                             target,
612                                             tileMode,
613                                             convolveAlpha);
614}
615
616bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffectRef** effect,
617                                                 GrTexture* texture,
618                                                 const SkMatrix&,
619                                                 const SkIRect& bounds
620                                                 ) const {
621    if (!effect) {
622        return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
623    }
624    SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE);
625    *effect = GrMatrixConvolutionEffect::Create(texture,
626                                                bounds,
627                                                fKernelSize,
628                                                fKernel,
629                                                fGain,
630                                                fBias,
631                                                fTarget,
632                                                fTileMode,
633                                                fConvolveAlpha);
634    return true;
635}
636
637///////////////////////////////////////////////////////////////////////////////
638
639#endif
640