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 19 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer) 20 : INHERITED(1, 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 29 SkBlurImageFilter::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 37 void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { 38 this->INHERITED::flatten(buffer); 39 buffer.writeScalar(fSigma.fWidth); 40 buffer.writeScalar(fSigma.fHeight); 41 } 42 43 enum 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 66 template<BlurDirection srcDirection, BlurDirection dstDirection> 67 static 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 uint32_t scale = (1 << 24) / kernelSize; 76 uint32_t half = 1 << 23; 77 for (int y = 0; y < height; ++y) { 78 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 79 const SkPMColor* p = src; 80 for (int i = 0; i < rightBorder; ++i) { 81 sumA += SkGetPackedA32(*p); 82 sumR += SkGetPackedR32(*p); 83 sumG += SkGetPackedG32(*p); 84 sumB += SkGetPackedB32(*p); 85 p += srcStrideX; 86 } 87 88 const SkPMColor* sptr = src; 89 SkColor* dptr = dst; 90 for (int x = 0; x < width; ++x) { 91 *dptr = SkPackARGB32((sumA * scale + half) >> 24, 92 (sumR * scale + half) >> 24, 93 (sumG * scale + half) >> 24, 94 (sumB * scale + half) >> 24); 95 if (x >= leftOffset) { 96 SkColor l = *(sptr - leftOffset * srcStrideX); 97 sumA -= SkGetPackedA32(l); 98 sumR -= SkGetPackedR32(l); 99 sumG -= SkGetPackedG32(l); 100 sumB -= SkGetPackedB32(l); 101 } 102 if (x + rightOffset + 1 < width) { 103 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX); 104 sumA += SkGetPackedA32(r); 105 sumR += SkGetPackedR32(r); 106 sumG += SkGetPackedG32(r); 107 sumB += SkGetPackedB32(r); 108 } 109 sptr += srcStrideX; 110 if (srcDirection == kY) { 111 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX); 112 } 113 dptr += dstStrideX; 114 } 115 src += srcStrideY; 116 dst += dstStrideY; 117 } 118 } 119 120 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, 121 int *highOffset) 122 { 123 float pi = SkScalarToFloat(SK_ScalarPI); 124 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f)); 125 *kernelSize = d; 126 if (d % 2 == 1) { 127 *lowOffset = *highOffset = (d - 1) / 2; 128 *kernelSize3 = d; 129 } else { 130 *highOffset = d / 2; 131 *lowOffset = *highOffset - 1; 132 *kernelSize3 = d + 1; 133 } 134 } 135 136 bool SkBlurImageFilter::onFilterImage(Proxy* proxy, 137 const SkBitmap& source, const SkMatrix& ctm, 138 SkBitmap* dst, SkIPoint* offset) { 139 SkBitmap src = source; 140 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) { 141 return false; 142 } 143 144 if (src.config() != SkBitmap::kARGB_8888_Config) { 145 return false; 146 } 147 148 SkAutoLockPixels alp(src); 149 if (!src.getPixels()) { 150 return false; 151 } 152 153 SkIRect srcBounds, dstBounds; 154 src.getBounds(&srcBounds); 155 if (!this->applyCropRect(&srcBounds, ctm)) { 156 return false; 157 } 158 159 dst->setConfig(src.config(), srcBounds.width(), srcBounds.height()); 160 dst->getBounds(&dstBounds); 161 dst->allocPixels(); 162 if (!dst->getPixels()) { 163 return false; 164 } 165 166 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 167 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 168 getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 169 getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 170 171 if (kernelSizeX < 0 || kernelSizeY < 0) { 172 return false; 173 } 174 175 if (kernelSizeX == 0 && kernelSizeY == 0) { 176 src.copyTo(dst, dst->config()); 177 return true; 178 } 179 180 SkBitmap temp; 181 temp.setConfig(dst->config(), dst->width(), dst->height()); 182 if (!temp.allocPixels()) { 183 return false; 184 } 185 186 const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top()); 187 SkPMColor* t = temp.getAddr32(0, 0); 188 SkPMColor* d = dst->getAddr32(0, 0); 189 int w = dstBounds.width(), h = dstBounds.height(); 190 int sw = src.rowBytesAsPixels(); 191 SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX; 192 if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) { 193 boxBlurX = boxBlur<kX, kX>; 194 boxBlurY = boxBlur<kY, kY>; 195 boxBlurXY = boxBlur<kX, kY>; 196 boxBlurYX = boxBlur<kY, kX>; 197 } 198 199 if (kernelSizeX > 0 && kernelSizeY > 0) { 200 boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); 201 boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); 202 boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); 203 boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 204 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 205 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 206 } else if (kernelSizeX > 0) { 207 boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); 208 boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); 209 boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); 210 } else if (kernelSizeY > 0) { 211 boxBlurYX(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); 212 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); 213 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); 214 } 215 offset->fX += srcBounds.fLeft; 216 offset->fY += srcBounds.fTop; 217 return true; 218 } 219 220 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, 221 SkBitmap* result, SkIPoint* offset) { 222 #if SK_SUPPORT_GPU 223 SkBitmap input; 224 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) { 225 return false; 226 } 227 GrTexture* source = input.getTexture(); 228 SkIRect rect; 229 src.getBounds(&rect); 230 if (!this->applyCropRect(&rect, ctm)) { 231 return false; 232 } 233 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), 234 source, 235 false, 236 SkRect::Make(rect), 237 true, 238 fSigma.width(), 239 fSigma.height())); 240 offset->fX += rect.fLeft; 241 offset->fY += rect.fTop; 242 return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result); 243 #else 244 SkDEBUGFAIL("Should not call in GPU-less build"); 245 return false; 246 #endif 247 } 248