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