SkBlurImageFilter.cpp revision 27eec46d6925afc76c2241c6b3182ce9b3284c9e
1/* 2 * Copyright 2011 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 "SkBitmap.h" 9#include "SkBlurImageFilter.h" 10#include "SkColorPriv.h" 11#include "SkFlattenableBuffers.h" 12#include "SkGpuBlurUtils.h" 13#include "SkBlurImage_opts.h" 14#if SK_SUPPORT_GPU 15#include "GrContext.h" 16#include "SkImageFilterUtils.h" 17#endif 18 19SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer) 20 : INHERITED(buffer) { 21 fSigma.fWidth = buffer.readScalar(); 22 fSigma.fHeight = buffer.readScalar(); 23 buffer.validate(SkScalarIsFinite(fSigma.fWidth) && 24 SkScalarIsFinite(fSigma.fHeight) && 25 (fSigma.fWidth >= 0) && 26 (fSigma.fHeight >= 0)); 27} 28 29SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, 30 SkScalar sigmaY, 31 SkImageFilter* input, 32 const CropRect* cropRect) 33 : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) { 34 SkASSERT(sigmaX >= 0 && sigmaY >= 0); 35} 36 37void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { 38 this->INHERITED::flatten(buffer); 39 buffer.writeScalar(fSigma.fWidth); 40 buffer.writeScalar(fSigma.fHeight); 41} 42 43enum BlurDirection { 44 kX, kY 45}; 46 47/** 48 * 49 * In order to make memory accesses cache-friendly, we reorder the passes to 50 * use contiguous memory reads wherever possible. 51 * 52 * For example, the 6 passes of the X-and-Y blur case are rewritten as 53 * follows. Instead of 3 passes in X and 3 passes in Y, we perform 54 * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, 55 * then 1 pass in X transposed to Y on write. 56 * 57 * +----+ +----+ +----+ +---+ +---+ +---+ +----+ 58 * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | 59 * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ 60 * +---+ +---+ +---+ 61 * 62 * In this way, two of the y-blurs become x-blurs applied to transposed 63 * images, and all memory reads are contiguous. 64 */ 65 66template<BlurDirection srcDirection, BlurDirection dstDirection> 67static void boxBlur(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize, 68 int leftOffset, int rightOffset, int width, int height) 69{ 70 int rightBorder = SkMin32(rightOffset + 1, width); 71 int srcStrideX = srcDirection == kX ? 1 : srcStride; 72 int dstStrideX = dstDirection == kX ? 1 : height; 73 int srcStrideY = srcDirection == kX ? srcStride : 1; 74 int dstStrideY = dstDirection == kX ? width : 1; 75#ifndef SK_DISABLE_BLUR_DIVISION_OPTIMIZATION 76 uint32_t scale = (1 << 24) / kernelSize; 77 uint32_t half = 1 << 23; 78#endif 79 for (int y = 0; y < height; ++y) { 80 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 81 const SkPMColor* p = src; 82 for (int i = 0; i < rightBorder; ++i) { 83 sumA += SkGetPackedA32(*p); 84 sumR += SkGetPackedR32(*p); 85 sumG += SkGetPackedG32(*p); 86 sumB += SkGetPackedB32(*p); 87 p += srcStrideX; 88 } 89 90 const SkPMColor* sptr = src; 91 SkColor* dptr = dst; 92 for (int x = 0; x < width; ++x) { 93#ifndef SK_DISABLE_BLUR_DIVISION_OPTIMIZATION 94 *dptr = SkPackARGB32((sumA * scale + half) >> 24, 95 (sumR * scale + half) >> 24, 96 (sumG * scale + half) >> 24, 97 (sumB * scale + half) >> 24); 98#else 99 *dptr = SkPackARGB32(sumA / kernelSize, 100 sumR / kernelSize, 101 sumG / kernelSize, 102 sumB / kernelSize); 103#endif 104 if (x >= leftOffset) { 105 SkColor l = *(sptr - leftOffset * srcStrideX); 106 sumA -= SkGetPackedA32(l); 107 sumR -= SkGetPackedR32(l); 108 sumG -= SkGetPackedG32(l); 109 sumB -= SkGetPackedB32(l); 110 } 111 if (x + rightOffset + 1 < width) { 112 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX); 113 sumA += SkGetPackedA32(r); 114 sumR += SkGetPackedR32(r); 115 sumG += SkGetPackedG32(r); 116 sumB += SkGetPackedB32(r); 117 } 118 sptr += srcStrideX; 119 if (srcDirection == kY) { 120 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX); 121 } 122 dptr += dstStrideX; 123 } 124 src += srcStrideY; 125 dst += dstStrideY; 126 } 127} 128 129static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, 130 int *highOffset) 131{ 132 float pi = SkScalarToFloat(SK_ScalarPI); 133 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f)); 134 *kernelSize = d; 135 if (d % 2 == 1) { 136 *lowOffset = *highOffset = (d - 1) / 2; 137 *kernelSize3 = d; 138 } else { 139 *highOffset = d / 2; 140 *lowOffset = *highOffset - 1; 141 *kernelSize3 = d + 1; 142 } 143} 144 145bool SkBlurImageFilter::onFilterImage(Proxy* proxy, 146 const SkBitmap& source, const SkMatrix& ctm, 147 SkBitmap* dst, SkIPoint* offset) { 148 SkBitmap src = source; 149 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) { 150 return false; 151 } 152 153 if (src.config() != SkBitmap::kARGB_8888_Config) { 154 return false; 155 } 156 157 SkAutoLockPixels alp(src); 158 if (!src.getPixels()) { 159 return false; 160 } 161 162 SkIRect srcBounds, dstBounds; 163 src.getBounds(&srcBounds); 164 if (!this->applyCropRect(&srcBounds, ctm)) { 165 return false; 166 } 167 168 dst->setConfig(src.config(), srcBounds.width(), srcBounds.height()); 169 dst->getBounds(&dstBounds); 170 dst->allocPixels(); 171 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 172 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 173 getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 174 getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 175 176 if (kernelSizeX < 0 || kernelSizeY < 0) { 177 return false; 178 } 179 180 if (kernelSizeX == 0 && kernelSizeY == 0) { 181 src.copyTo(dst, dst->config()); 182 return true; 183 } 184 185 SkBitmap temp; 186 temp.setConfig(dst->config(), dst->width(), dst->height()); 187 if (!temp.allocPixels()) { 188 return false; 189 } 190 191 const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top()); 192 SkPMColor* t = temp.getAddr32(0, 0); 193 SkPMColor* d = dst->getAddr32(0, 0); 194 int w = dstBounds.width(), h = dstBounds.height(); 195 int sw = src.rowBytesAsPixels(); 196 SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY; 197 if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY)) { 198 boxBlurX = boxBlur<kX, kX>; 199 boxBlurY = boxBlur<kY, kY>; 200 boxBlurXY = boxBlur<kX, kY>; 201 } 202 203 if (kernelSizeX > 0 && kernelSizeY > 0) { 204#ifndef SK_DISABLE_BLUR_DIVISION_OPTIMIZATION 205 boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); 206 boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); 207 boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); 208 boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 209 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 210 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 211#else 212 boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); 213 boxBlurY(t, w, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 214 boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); 215 boxBlurY(t, w, d, kernelSizeY, highOffsetY, lowOffsetY, h, w); 216 boxBlurX(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); 217 boxBlurY(t, w, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 218#endif 219 } else if (kernelSizeX > 0) { 220 boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); 221 boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); 222 boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); 223 } else if (kernelSizeY > 0) { 224 boxBlurY(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 225 boxBlurY(d, w, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 226 boxBlurY(t, w, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 227 } 228 offset->fX += srcBounds.fLeft; 229 offset->fY += srcBounds.fTop; 230 return true; 231} 232 233bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, 234 SkBitmap* result, SkIPoint* offset) { 235#if SK_SUPPORT_GPU 236 SkBitmap input; 237 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) { 238 return false; 239 } 240 GrTexture* source = input.getTexture(); 241 SkIRect rect; 242 src.getBounds(&rect); 243 if (!this->applyCropRect(&rect, ctm)) { 244 return false; 245 } 246 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), 247 source, 248 false, 249 SkRect::Make(rect), 250 true, 251 fSigma.width(), 252 fSigma.height())); 253 offset->fX += rect.fLeft; 254 offset->fY += rect.fTop; 255 return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result); 256#else 257 SkDEBUGFAIL("Should not call in GPU-less build"); 258 return false; 259#endif 260} 261