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