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