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 "SkBlurImageFilter.h" 9 #include "SkColorPriv.h" 10 11 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer) 12 : INHERITED(buffer) { 13 fSigma.fWidth = buffer.readScalar(); 14 fSigma.fHeight = buffer.readScalar(); 15 } 16 17 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY) 18 : fSigma(SkSize::Make(sigmaX, sigmaY)) { 19 SkASSERT(sigmaX >= 0 && sigmaY >= 0); 20 } 21 22 bool SkBlurImageFilter::asABlur(SkSize* sigma) const { 23 *sigma = fSigma; 24 return true; 25 } 26 27 void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) { 28 this->INHERITED::flatten(buffer); 29 buffer.writeScalar(fSigma.fWidth); 30 buffer.writeScalar(fSigma.fHeight); 31 } 32 33 static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, 34 int leftOffset, int rightOffset) 35 { 36 int width = src.width(), height = src.height(); 37 int rightBorder = SkMin32(rightOffset + 1, width); 38 for (int y = 0; y < height; ++y) { 39 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 40 SkPMColor* p = src.getAddr32(0, y); 41 for (int i = 0; i < rightBorder; ++i) { 42 sumA += SkGetPackedA32(*p); 43 sumR += SkGetPackedR32(*p); 44 sumG += SkGetPackedG32(*p); 45 sumB += SkGetPackedB32(*p); 46 p++; 47 } 48 49 const SkColor* sptr = src.getAddr32(0, y); 50 SkColor* dptr = dst->getAddr32(0, y); 51 for (int x = 0; x < width; ++x) { 52 *dptr = SkPackARGB32(sumA / kernelSize, 53 sumR / kernelSize, 54 sumG / kernelSize, 55 sumB / kernelSize); 56 if (x >= leftOffset) { 57 SkColor l = *(sptr - leftOffset); 58 sumA -= SkGetPackedA32(l); 59 sumR -= SkGetPackedR32(l); 60 sumG -= SkGetPackedG32(l); 61 sumB -= SkGetPackedB32(l); 62 } 63 if (x + rightOffset + 1 < width) { 64 SkColor r = *(sptr + rightOffset + 1); 65 sumA += SkGetPackedA32(r); 66 sumR += SkGetPackedR32(r); 67 sumG += SkGetPackedG32(r); 68 sumB += SkGetPackedB32(r); 69 } 70 sptr++; 71 dptr++; 72 } 73 } 74 } 75 76 static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize, 77 int topOffset, int bottomOffset) 78 { 79 int width = src.width(), height = src.height(); 80 int bottomBorder = SkMin32(bottomOffset + 1, height); 81 int srcStride = src.rowBytesAsPixels(); 82 int dstStride = dst->rowBytesAsPixels(); 83 for (int x = 0; x < width; ++x) { 84 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 85 SkColor* p = src.getAddr32(x, 0); 86 for (int i = 0; i < bottomBorder; ++i) { 87 sumA += SkGetPackedA32(*p); 88 sumR += SkGetPackedR32(*p); 89 sumG += SkGetPackedG32(*p); 90 sumB += SkGetPackedB32(*p); 91 p += srcStride; 92 } 93 94 const SkColor* sptr = src.getAddr32(x, 0); 95 SkColor* dptr = dst->getAddr32(x, 0); 96 for (int y = 0; y < height; ++y) { 97 *dptr = SkPackARGB32(sumA / kernelSize, 98 sumR / kernelSize, 99 sumG / kernelSize, 100 sumB / kernelSize); 101 if (y >= topOffset) { 102 SkColor l = *(sptr - topOffset * srcStride); 103 sumA -= SkGetPackedA32(l); 104 sumR -= SkGetPackedR32(l); 105 sumG -= SkGetPackedG32(l); 106 sumB -= SkGetPackedB32(l); 107 } 108 if (y + bottomOffset + 1 < height) { 109 SkColor r = *(sptr + (bottomOffset + 1) * srcStride); 110 sumA += SkGetPackedA32(r); 111 sumR += SkGetPackedR32(r); 112 sumG += SkGetPackedG32(r); 113 sumB += SkGetPackedB32(r); 114 } 115 sptr += srcStride; 116 dptr += dstStride; 117 } 118 } 119 } 120 121 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, 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*, 137 const SkBitmap& src, const SkMatrix&, 138 SkBitmap* dst, SkIPoint*) { 139 if (src.config() != SkBitmap::kARGB_8888_Config) { 140 return false; 141 } 142 143 SkAutoLockPixels alp(src); 144 if (!src.getPixels()) { 145 return false; 146 } 147 148 dst->setConfig(src.config(), src.width(), src.height()); 149 dst->allocPixels(); 150 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 151 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 152 getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 153 getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 154 155 if (kernelSizeX < 0 || kernelSizeY < 0) { 156 return false; 157 } 158 159 if (kernelSizeX == 0 && kernelSizeY == 0) { 160 src.copyTo(dst, dst->config()); 161 return true; 162 } 163 164 SkBitmap temp; 165 temp.setConfig(dst->config(), dst->width(), dst->height()); 166 if (!temp.allocPixels()) { 167 return false; 168 } 169 170 if (kernelSizeX > 0 && kernelSizeY > 0) { 171 boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX); 172 boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY); 173 boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); 174 boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY); 175 boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX); 176 boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); 177 } else if (kernelSizeX > 0) { 178 boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX); 179 boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); 180 boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX); 181 } else if (kernelSizeY > 0) { 182 boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY); 183 boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY); 184 boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); 185 } 186 return true; 187 } 188 189 SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurImageFilter) 190