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