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 #if SK_SUPPORT_GPU 13 #include "GrContext.h" 14 #include "SkImageFilterUtils.h" 15 #endif 16 17 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer) 18 : INHERITED(buffer) { 19 fSigma.fWidth = buffer.readScalar(); 20 fSigma.fHeight = buffer.readScalar(); 21 } 22 23 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input) 24 : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) { 25 SkASSERT(sigmaX >= 0 && sigmaY >= 0); 26 } 27 28 void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { 29 this->INHERITED::flatten(buffer); 30 buffer.writeScalar(fSigma.fWidth); 31 buffer.writeScalar(fSigma.fHeight); 32 } 33 34 static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, 35 int leftOffset, int rightOffset) 36 { 37 int width = src.width(), height = src.height(); 38 int rightBorder = SkMin32(rightOffset + 1, width); 39 for (int y = 0; y < height; ++y) { 40 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 41 SkPMColor* p = src.getAddr32(0, y); 42 for (int i = 0; i < rightBorder; ++i) { 43 sumA += SkGetPackedA32(*p); 44 sumR += SkGetPackedR32(*p); 45 sumG += SkGetPackedG32(*p); 46 sumB += SkGetPackedB32(*p); 47 p++; 48 } 49 50 const SkColor* sptr = src.getAddr32(0, y); 51 SkColor* dptr = dst->getAddr32(0, y); 52 for (int x = 0; x < width; ++x) { 53 *dptr = SkPackARGB32(sumA / kernelSize, 54 sumR / kernelSize, 55 sumG / kernelSize, 56 sumB / kernelSize); 57 if (x >= leftOffset) { 58 SkColor l = *(sptr - leftOffset); 59 sumA -= SkGetPackedA32(l); 60 sumR -= SkGetPackedR32(l); 61 sumG -= SkGetPackedG32(l); 62 sumB -= SkGetPackedB32(l); 63 } 64 if (x + rightOffset + 1 < width) { 65 SkColor r = *(sptr + rightOffset + 1); 66 sumA += SkGetPackedA32(r); 67 sumR += SkGetPackedR32(r); 68 sumG += SkGetPackedG32(r); 69 sumB += SkGetPackedB32(r); 70 } 71 sptr++; 72 dptr++; 73 } 74 } 75 } 76 77 static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize, 78 int topOffset, int bottomOffset) 79 { 80 int width = src.width(), height = src.height(); 81 int bottomBorder = SkMin32(bottomOffset + 1, height); 82 int srcStride = src.rowBytesAsPixels(); 83 int dstStride = dst->rowBytesAsPixels(); 84 for (int x = 0; x < width; ++x) { 85 int sumA = 0, sumR = 0, sumG = 0, sumB = 0; 86 SkColor* p = src.getAddr32(x, 0); 87 for (int i = 0; i < bottomBorder; ++i) { 88 sumA += SkGetPackedA32(*p); 89 sumR += SkGetPackedR32(*p); 90 sumG += SkGetPackedG32(*p); 91 sumB += SkGetPackedB32(*p); 92 p += srcStride; 93 } 94 95 const SkColor* sptr = src.getAddr32(x, 0); 96 SkColor* dptr = dst->getAddr32(x, 0); 97 for (int y = 0; y < height; ++y) { 98 *dptr = SkPackARGB32(sumA / kernelSize, 99 sumR / kernelSize, 100 sumG / kernelSize, 101 sumB / kernelSize); 102 if (y >= topOffset) { 103 SkColor l = *(sptr - topOffset * srcStride); 104 sumA -= SkGetPackedA32(l); 105 sumR -= SkGetPackedR32(l); 106 sumG -= SkGetPackedG32(l); 107 sumB -= SkGetPackedB32(l); 108 } 109 if (y + bottomOffset + 1 < height) { 110 SkColor r = *(sptr + (bottomOffset + 1) * srcStride); 111 sumA += SkGetPackedA32(r); 112 sumR += SkGetPackedR32(r); 113 sumG += SkGetPackedG32(r); 114 sumB += SkGetPackedB32(r); 115 } 116 sptr += srcStride; 117 dptr += dstStride; 118 } 119 } 120 } 121 122 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset) 123 { 124 float pi = SkScalarToFloat(SK_ScalarPI); 125 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f)); 126 *kernelSize = d; 127 if (d % 2 == 1) { 128 *lowOffset = *highOffset = (d - 1) / 2; 129 *kernelSize3 = d; 130 } else { 131 *highOffset = d / 2; 132 *lowOffset = *highOffset - 1; 133 *kernelSize3 = d + 1; 134 } 135 } 136 137 bool SkBlurImageFilter::onFilterImage(Proxy* proxy, 138 const SkBitmap& source, const SkMatrix& ctm, 139 SkBitmap* dst, SkIPoint* offset) { 140 SkBitmap src = this->getInputResult(proxy, source, ctm, offset); 141 if (src.config() != SkBitmap::kARGB_8888_Config) { 142 return false; 143 } 144 145 SkAutoLockPixels alp(src); 146 if (!src.getPixels()) { 147 return false; 148 } 149 150 dst->setConfig(src.config(), src.width(), src.height()); 151 dst->allocPixels(); 152 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; 153 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; 154 getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); 155 getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); 156 157 if (kernelSizeX < 0 || kernelSizeY < 0) { 158 return false; 159 } 160 161 if (kernelSizeX == 0 && kernelSizeY == 0) { 162 src.copyTo(dst, dst->config()); 163 return true; 164 } 165 166 SkBitmap temp; 167 temp.setConfig(dst->config(), dst->width(), dst->height()); 168 if (!temp.allocPixels()) { 169 return false; 170 } 171 172 if (kernelSizeX > 0 && kernelSizeY > 0) { 173 boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX); 174 boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY); 175 boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); 176 boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY); 177 boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX); 178 boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); 179 } else if (kernelSizeX > 0) { 180 boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX); 181 boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); 182 boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX); 183 } else if (kernelSizeY > 0) { 184 boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY); 185 boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY); 186 boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); 187 } 188 return true; 189 } 190 191 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) { 192 #if SK_SUPPORT_GPU 193 SkBitmap input; 194 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input)) { 195 return false; 196 } 197 GrTexture* source = (GrTexture*) input.getTexture(); 198 SkRect rect; 199 src.getBounds(&rect); 200 SkAutoTUnref<GrTexture> tex(source->getContext()->gaussianBlur(source, false, rect, 201 fSigma.width(), fSigma.height())); 202 return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result); 203 #else 204 SkDEBUGFAIL("Should not call in GPU-less build"); 205 return false; 206 #endif 207 } 208