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 "SkReadBuffer.h" 12 #include "SkWriteBuffer.h" 13 #include "SkGpuBlurUtils.h" 14 #include "SkBlurImage_opts.h" 15 #if SK_SUPPORT_GPU 16 #include "GrContext.h" 17 #endif 18 19 // This rather arbitrary-looking value results in a maximum box blur kernel size 20 // of 1000 pixels on the raster path, which matches the WebKit and Firefox 21 // implementations. Since the GPU path does not compute a box blur, putting 22 // the limit on sigma ensures consistent behaviour between the GPU and 23 // raster paths. 24 #define MAX_SIGMA SkIntToScalar(532) 25 26 static SkVector mapSigma(const SkSize& localSigma, const SkMatrix& ctm) { 27 SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height()); 28 ctm.mapVectors(&sigma, 1); 29 sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA); 30 sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA); 31 return sigma; 32 } 33 34 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, 35 SkScalar sigmaY, 36 SkImageFilter* input, 37 const CropRect* cropRect) 38 : INHERITED(1, &input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) { 39 } 40 41 SkFlattenable* SkBlurImageFilter::CreateProc(SkReadBuffer& buffer) { 42 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 43 SkScalar sigmaX = buffer.readScalar(); 44 SkScalar sigmaY = buffer.readScalar(); 45 return Create(sigmaX, sigmaY, common.getInput(0), &common.cropRect()); 46 } 47 48 void SkBlurImageFilter::flatten(SkWriteBuffer& buffer) const { 49 this->INHERITED::flatten(buffer); 50 buffer.writeScalar(fSigma.fWidth); 51 buffer.writeScalar(fSigma.fHeight); 52 } 53 54 enum BlurDirection { 55 kX, kY 56 }; 57 58 /** 59 * 60 * In order to make memory accesses cache-friendly, we reorder the passes to 61 * use contiguous memory reads wherever possible. 62 * 63 * For example, the 6 passes of the X-and-Y blur case are rewritten as 64 * follows. Instead of 3 passes in X and 3 passes in Y, we perform 65 * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, 66 * then 1 pass in X transposed to Y on write. 67 * 68 * +----+ +----+ +----+ +---+ +---+ +---+ +----+ 69 * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | 70 * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ 71 * +---+ +---+ +---+ 72 * 73 * In this way, two of the y-blurs become x-blurs applied to transposed 74 * images, and all memory reads are contiguous. 75 */ 76 77 template<BlurDirection srcDirection, BlurDirection dstDirection> 78 static void boxBlur(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize, 79 int leftOffset, int rightOffset, int width, int height) 80 { 81 int rightBorder = SkMin32(rightOffset + 1, width); 82 int srcStrideX = srcDirection == kX ? 1 : srcStride; 83 int dstStrideX = dstDirection == kX ? 1 : height; 84 int srcStrideY = srcDirection == kX ? srcStride : 1; 85 int dstStrideY = dstDirection == kX ? width : 1; 86 uint32_t scale = (1 << 24) / kernelSize; 87 uint32_t half = 1 << 23; 88 for (int y = 0; y < height; ++y) { 89 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 90 const SkPMColor* p = src; 91 for (int i = 0; i < rightBorder; ++i) { 92 sumA += SkGetPackedA32(*p); 93 sumR += SkGetPackedR32(*p); 94 sumG += SkGetPackedG32(*p); 95 sumB += SkGetPackedB32(*p); 96 p += srcStrideX; 97 } 98 99 const SkPMColor* sptr = src; 100 SkColor* dptr = dst; 101 for (int x = 0; x < width; ++x) { 102 *dptr = SkPackARGB32((sumA * scale + half) >> 24, 103 (sumR * scale + half) >> 24, 104 (sumG * scale + half) >> 24, 105 (sumB * scale + half) >> 24); 106 if (x >= leftOffset) { 107 SkColor l = *(sptr - leftOffset * srcStrideX); 108 sumA -= SkGetPackedA32(l); 109 sumR -= SkGetPackedR32(l); 110 sumG -= SkGetPackedG32(l); 111 sumB -= SkGetPackedB32(l); 112 } 113 if (x + rightOffset + 1 < width) { 114 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX); 115 sumA += SkGetPackedA32(r); 116 sumR += SkGetPackedR32(r); 117 sumG += SkGetPackedG32(r); 118 sumB += SkGetPackedB32(r); 119 } 120 sptr += srcStrideX; 121 if (srcDirection == kY) { 122 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX); 123 } 124 dptr += dstStrideX; 125 } 126 src += srcStrideY; 127 dst += dstStrideY; 128 } 129 } 130 131 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, 132 int *highOffset) 133 { 134 float pi = SkScalarToFloat(SK_ScalarPI); 135 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f)); 136 *kernelSize = d; 137 if (d % 2 == 1) { 138 *lowOffset = *highOffset = (d - 1) / 2; 139 *kernelSize3 = d; 140 } else { 141 *highOffset = d / 2; 142 *lowOffset = *highOffset - 1; 143 *kernelSize3 = d + 1; 144 } 145 } 146 147 bool SkBlurImageFilter::onFilterImage(Proxy* proxy, 148 const SkBitmap& source, const Context& ctx, 149 SkBitmap* dst, SkIPoint* offset) const { 150 SkBitmap src = source; 151 SkIPoint srcOffset = SkIPoint::Make(0, 0); 152 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { 153 return false; 154 } 155 156 if (src.colorType() != kN32_SkColorType) { 157 return false; 158 } 159 160 SkIRect srcBounds, dstBounds; 161 if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &srcBounds, &src)) { 162 return false; 163 } 164 165 SkAutoLockPixels alp(src); 166 if (!src.getPixels()) { 167 return false; 168 } 169 170 if (!dst->tryAllocPixels(src.info().makeWH(srcBounds.width(), srcBounds.height()))) { 171 return false; 172 } 173 dst->getBounds(&dstBounds); 174 175 SkVector sigma = mapSigma(fSigma, ctx.ctm()); 176 177 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 178 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 179 getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 180 getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 181 182 if (kernelSizeX < 0 || kernelSizeY < 0) { 183 return false; 184 } 185 186 if (kernelSizeX == 0 && kernelSizeY == 0) { 187 src.copyTo(dst, dst->colorType()); 188 offset->fX = srcBounds.fLeft; 189 offset->fY = srcBounds.fTop; 190 return true; 191 } 192 193 SkBitmap temp; 194 if (!temp.tryAllocPixels(dst->info())) { 195 return false; 196 } 197 198 offset->fX = srcBounds.fLeft; 199 offset->fY = srcBounds.fTop; 200 srcBounds.offset(-srcOffset); 201 const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top()); 202 SkPMColor* t = temp.getAddr32(0, 0); 203 SkPMColor* d = dst->getAddr32(0, 0); 204 int w = dstBounds.width(), h = dstBounds.height(); 205 int sw = src.rowBytesAsPixels(); 206 SkBoxBlurProc boxBlurX, boxBlurXY, boxBlurYX; 207 if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurXY, &boxBlurYX)) { 208 boxBlurX = boxBlur<kX, kX>; 209 boxBlurXY = boxBlur<kX, kY>; 210 boxBlurYX = boxBlur<kY, kX>; 211 } 212 213 if (kernelSizeX > 0 && kernelSizeY > 0) { 214 boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); 215 boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); 216 boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); 217 boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 218 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 219 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 220 } else if (kernelSizeX > 0) { 221 boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); 222 boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); 223 boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); 224 } else if (kernelSizeY > 0) { 225 boxBlurYX(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 226 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 227 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 228 } 229 return true; 230 } 231 232 233 void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { 234 if (getInput(0)) { 235 getInput(0)->computeFastBounds(src, dst); 236 } else { 237 *dst = src; 238 } 239 240 dst->outset(SkScalarMul(fSigma.width(), SkIntToScalar(3)), 241 SkScalarMul(fSigma.height(), SkIntToScalar(3))); 242 } 243 244 bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, 245 SkIRect* dst) const { 246 SkIRect bounds = src; 247 SkVector sigma = mapSigma(fSigma, ctm); 248 bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), 249 SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); 250 if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { 251 return false; 252 } 253 *dst = bounds; 254 return true; 255 } 256 257 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, 258 SkBitmap* result, SkIPoint* offset) const { 259 #if SK_SUPPORT_GPU 260 SkBitmap input = src; 261 SkIPoint srcOffset = SkIPoint::Make(0, 0); 262 if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) { 263 return false; 264 } 265 SkIRect rect; 266 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &rect, &input)) { 267 return false; 268 } 269 GrTexture* source = input.getTexture(); 270 SkVector sigma = mapSigma(fSigma, ctx.ctm()); 271 offset->fX = rect.fLeft; 272 offset->fY = rect.fTop; 273 rect.offset(-srcOffset); 274 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), 275 source, 276 false, 277 SkRect::Make(rect), 278 true, 279 sigma.x(), 280 sigma.y())); 281 if (!tex) { 282 return false; 283 } 284 WrapTexture(tex, rect.width(), rect.height(), result); 285 return true; 286 #else 287 SkDEBUGFAIL("Should not call in GPU-less build"); 288 return false; 289 #endif 290 } 291 292 #ifndef SK_IGNORE_TO_STRING 293 void SkBlurImageFilter::toString(SkString* str) const { 294 str->appendf("SkBlurImageFilter: ("); 295 str->appendf("sigma: (%f, %f) input (", fSigma.fWidth, fSigma.fHeight); 296 297 if (this->getInput(0)) { 298 this->getInput(0)->toString(str); 299 } 300 301 str->append("))"); 302 } 303 #endif 304