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 "SkDevice.h" 12 #include "SkGpuBlurUtils.h" 13 #include "SkOpts.h" 14 #include "SkReadBuffer.h" 15 #include "SkWriteBuffer.h" 16 #if SK_SUPPORT_GPU 17 #include "GrContext.h" 18 #include "SkGr.h" 19 #endif 20 21 // This rather arbitrary-looking value results in a maximum box blur kernel size 22 // of 1000 pixels on the raster path, which matches the WebKit and Firefox 23 // implementations. Since the GPU path does not compute a box blur, putting 24 // the limit on sigma ensures consistent behaviour between the GPU and 25 // raster paths. 26 #define MAX_SIGMA SkIntToScalar(532) 27 28 static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) { 29 SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height()); 30 ctm.mapVectors(&sigma, 1); 31 sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA); 32 sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA); 33 return sigma; 34 } 35 36 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, 37 SkScalar sigmaY, 38 SkImageFilter* input, 39 const CropRect* cropRect) 40 : INHERITED(1, &input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) { 41 } 42 43 SkFlattenable* SkBlurImageFilter::CreateProc(SkReadBuffer& buffer) { 44 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 45 SkScalar sigmaX = buffer.readScalar(); 46 SkScalar sigmaY = buffer.readScalar(); 47 return Create(sigmaX, sigmaY, common.getInput(0), &common.cropRect()); 48 } 49 50 void SkBlurImageFilter::flatten(SkWriteBuffer& buffer) const { 51 this->INHERITED::flatten(buffer); 52 buffer.writeScalar(fSigma.fWidth); 53 buffer.writeScalar(fSigma.fHeight); 54 } 55 56 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, 57 int *highOffset) 58 { 59 float pi = SkScalarToFloat(SK_ScalarPI); 60 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f)); 61 *kernelSize = d; 62 if (d % 2 == 1) { 63 *lowOffset = *highOffset = (d - 1) / 2; 64 *kernelSize3 = d; 65 } else { 66 *highOffset = d / 2; 67 *lowOffset = *highOffset - 1; 68 *kernelSize3 = d + 1; 69 } 70 } 71 72 bool SkBlurImageFilter::onFilterImageDeprecated(Proxy* proxy, 73 const SkBitmap& source, const Context& ctx, 74 SkBitmap* dst, SkIPoint* offset) const { 75 SkBitmap src = source; 76 SkIPoint srcOffset = SkIPoint::Make(0, 0); 77 if (!this->filterInputDeprecated(0, proxy, source, ctx, &src, &srcOffset)) { 78 return false; 79 } 80 81 if (src.colorType() != kN32_SkColorType) { 82 return false; 83 } 84 85 SkIRect srcBounds = src.bounds(); 86 srcBounds.offset(srcOffset); 87 SkIRect dstBounds; 88 if (!this->applyCropRect(this->mapContext(ctx), srcBounds, &dstBounds)) { 89 return false; 90 } 91 if (!srcBounds.intersect(dstBounds)) { 92 return false; 93 } 94 95 SkVector sigma = map_sigma(fSigma, ctx.ctm()); 96 97 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 98 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 99 getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 100 getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 101 102 if (kernelSizeX < 0 || kernelSizeY < 0) { 103 return false; 104 } 105 106 if (kernelSizeX == 0 && kernelSizeY == 0) { 107 src.extractSubset(dst, srcBounds); 108 offset->fX = srcBounds.x(); 109 offset->fY = srcBounds.y(); 110 return true; 111 } 112 113 SkAutoLockPixels alp(src); 114 if (!src.getPixels()) { 115 return false; 116 } 117 118 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); 119 if (!device) { 120 return false; 121 } 122 *dst = device->accessBitmap(false); 123 SkAutoLockPixels alp_dst(*dst); 124 125 SkAutoTUnref<SkBaseDevice> tempDevice(proxy->createDevice(dst->width(), dst->height())); 126 if (!tempDevice) { 127 return false; 128 } 129 SkBitmap temp = tempDevice->accessBitmap(false); 130 SkAutoLockPixels alpTemp(temp); 131 132 offset->fX = dstBounds.fLeft; 133 offset->fY = dstBounds.fTop; 134 SkPMColor* t = temp.getAddr32(0, 0); 135 SkPMColor* d = dst->getAddr32(0, 0); 136 int w = dstBounds.width(), h = dstBounds.height(); 137 const SkPMColor* s = src.getAddr32(srcBounds.x() - srcOffset.x(), srcBounds.y() - srcOffset.y()); 138 srcBounds.offset(-dstBounds.x(), -dstBounds.y()); 139 dstBounds.offset(-dstBounds.x(), -dstBounds.y()); 140 SkIRect srcBoundsT = SkIRect::MakeLTRB(srcBounds.top(), srcBounds.left(), srcBounds.bottom(), srcBounds.right()); 141 SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); 142 int sw = src.rowBytesAsPixels(); 143 144 /** 145 * 146 * In order to make memory accesses cache-friendly, we reorder the passes to 147 * use contiguous memory reads wherever possible. 148 * 149 * For example, the 6 passes of the X-and-Y blur case are rewritten as 150 * follows. Instead of 3 passes in X and 3 passes in Y, we perform 151 * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, 152 * then 1 pass in X transposed to Y on write. 153 * 154 * +----+ +----+ +----+ +---+ +---+ +---+ +----+ 155 * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | 156 * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ 157 * +---+ +---+ +---+ 158 * 159 * In this way, two of the y-blurs become x-blurs applied to transposed 160 * images, and all memory reads are contiguous. 161 */ 162 if (kernelSizeX > 0 && kernelSizeY > 0) { 163 SkOpts::box_blur_xx(s, sw, srcBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); 164 SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); 165 SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); 166 SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 167 SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 168 SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 169 } else if (kernelSizeX > 0) { 170 SkOpts::box_blur_xx(s, sw, srcBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); 171 SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); 172 SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); 173 } else if (kernelSizeY > 0) { 174 SkOpts::box_blur_yx(s, sw, srcBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 175 SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 176 SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 177 } 178 return true; 179 } 180 181 182 void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { 183 if (this->getInput(0)) { 184 this->getInput(0)->computeFastBounds(src, dst); 185 } else { 186 *dst = src; 187 } 188 189 dst->outset(SkScalarMul(fSigma.width(), SkIntToScalar(3)), 190 SkScalarMul(fSigma.height(), SkIntToScalar(3))); 191 } 192 193 void SkBlurImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, 194 SkIRect* dst, MapDirection) const { 195 *dst = src; 196 SkVector sigma = map_sigma(fSigma, ctm); 197 dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), 198 SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); 199 } 200 201 bool SkBlurImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, 202 const Context& ctx, 203 SkBitmap* result, SkIPoint* offset) const { 204 #if SK_SUPPORT_GPU 205 SkBitmap input = src; 206 SkIPoint srcOffset = SkIPoint::Make(0, 0); 207 if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) { 208 return false; 209 } 210 SkIRect srcBounds = input.bounds(); 211 srcBounds.offset(srcOffset); 212 SkIRect dstBounds; 213 if (!this->applyCropRect(this->mapContext(ctx), srcBounds, &dstBounds)) { 214 return false; 215 } 216 if (!srcBounds.intersect(dstBounds)) { 217 return false; 218 } 219 SkVector sigma = map_sigma(fSigma, ctx.ctm()); 220 if (sigma.x() == 0 && sigma.y() == 0) { 221 input.extractSubset(result, srcBounds); 222 offset->fX = srcBounds.x(); 223 offset->fY = srcBounds.y(); 224 return true; 225 } 226 offset->fX = dstBounds.fLeft; 227 offset->fY = dstBounds.fTop; 228 srcBounds.offset(-srcOffset); 229 dstBounds.offset(-srcOffset); 230 SkRect srcBoundsF(SkRect::Make(srcBounds)); 231 GrTexture* inputTexture = input.getTexture(); 232 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(), 233 inputTexture, 234 false, 235 SkRect::Make(dstBounds), 236 &srcBoundsF, 237 sigma.x(), 238 sigma.y())); 239 if (!tex) { 240 return false; 241 } 242 GrWrapTextureInBitmap(tex, dstBounds.width(), dstBounds.height(), false, result); 243 return true; 244 #else 245 SkDEBUGFAIL("Should not call in GPU-less build"); 246 return false; 247 #endif 248 } 249 250 #ifndef SK_IGNORE_TO_STRING 251 void SkBlurImageFilter::toString(SkString* str) const { 252 str->appendf("SkBlurImageFilter: ("); 253 str->appendf("sigma: (%f, %f) input (", fSigma.fWidth, fSigma.fHeight); 254 255 if (this->getInput(0)) { 256 this->getInput(0)->toString(str); 257 } 258 259 str->append("))"); 260 } 261 #endif 262