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