Move gaussianBlur functionality to src\effects
https://codereview.chromium.org/18771004/
git-svn-id: http://skia.googlecode.com/svn/trunk@10080 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
new file mode 100644
index 0000000..63b5e0f
--- /dev/null
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGpuBlurUtils.h"
+
+#include "SkRect.h"
+
+#if SK_SUPPORT_GPU
+#include "effects/GrConvolutionEffect.h"
+#include "GrContext.h"
+#endif
+
+namespace SkGpuBlurUtils {
+
+#if SK_SUPPORT_GPU
+
+#define MAX_BLUR_SIGMA 4.0f
+
+static void scale_rect(SkRect* rect, float xScale, float yScale) {
+ rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale));
+ rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale));
+ rect->fRight = SkScalarMul(rect->fRight, SkFloatToScalar(xScale));
+ rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale));
+}
+
+static float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
+ *scaleFactor = 1;
+ while (sigma > MAX_BLUR_SIGMA) {
+ *scaleFactor *= 2;
+ sigma *= 0.5f;
+ }
+ *radius = static_cast<int>(ceilf(sigma * 3.0f));
+ GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius);
+ return sigma;
+}
+
+static void convolve_gaussian(GrContext* context,
+ GrTexture* texture,
+ const SkRect& rect,
+ float sigma,
+ int radius,
+ Gr1DKernelEffect::Direction direction) {
+ GrPaint paint;
+
+ SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
+ direction,
+ radius,
+ sigma));
+ paint.addColorEffect(conv);
+ context->drawRect(paint, rect);
+}
+
+GrTexture* GaussianBlur(GrContext* context,
+ GrTexture* srcTexture,
+ bool canClobberSrc,
+ const SkRect& rect,
+ float sigmaX,
+ float sigmaY) {
+ GrAssert(NULL != context);
+
+ GrContext::AutoRenderTarget art(context);
+
+ GrContext::AutoMatrix am;
+ am.setIdentity(context);
+
+ SkIRect clearRect;
+ int scaleFactorX, radiusX;
+ int scaleFactorY, radiusY;
+ sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
+ sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
+
+ SkRect srcRect(rect);
+ scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
+ srcRect.roundOut();
+ scale_rect(&srcRect, static_cast<float>(scaleFactorX),
+ static_cast<float>(scaleFactorY));
+
+ GrContext::AutoClip acs(context, srcRect);
+
+ GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
+ kRGBA_8888_GrPixelConfig == srcTexture->config() ||
+ kAlpha_8_GrPixelConfig == srcTexture->config());
+
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fWidth = SkScalarFloorToInt(srcRect.width());
+ desc.fHeight = SkScalarFloorToInt(srcRect.height());
+ desc.fConfig = srcTexture->config();
+
+ GrAutoScratchTexture temp1, temp2;
+ GrTexture* dstTexture = temp1.set(context, desc);
+ GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc);
+ if (NULL == dstTexture || NULL == tempTexture) {
+ return NULL;
+ }
+
+ for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
+ GrPaint paint;
+ SkMatrix matrix;
+ matrix.setIDiv(srcTexture->width(), srcTexture->height());
+ context->setRenderTarget(dstTexture->asRenderTarget());
+ SkRect dstRect(srcRect);
+ scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
+ i < scaleFactorY ? 0.5f : 1.0f);
+ GrTextureParams params(SkShader::kClamp_TileMode, true);
+ paint.addColorTextureEffect(srcTexture, matrix, params);
+ context->drawRectToRect(paint, dstRect, srcRect);
+ srcRect = dstRect;
+ srcTexture = dstTexture;
+ SkTSwap(dstTexture, tempTexture);
+ }
+
+ SkIRect srcIRect;
+ srcRect.roundOut(&srcIRect);
+
+ if (sigmaX > 0.0f) {
+ if (scaleFactorX > 1) {
+ // Clear out a radius to the right of the srcRect to prevent the
+ // X convolution from reading garbage.
+ clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
+ radiusX, srcIRect.height());
+ context->clear(&clearRect, 0x0);
+ }
+ context->setRenderTarget(dstTexture->asRenderTarget());
+ convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX,
+ Gr1DKernelEffect::kX_Direction);
+ srcTexture = dstTexture;
+ SkTSwap(dstTexture, tempTexture);
+ }
+
+ if (sigmaY > 0.0f) {
+ if (scaleFactorY > 1 || sigmaX > 0.0f) {
+ // Clear out a radius below the srcRect to prevent the Y
+ // convolution from reading garbage.
+ clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
+ srcIRect.width(), radiusY);
+ context->clear(&clearRect, 0x0);
+ }
+
+ context->setRenderTarget(dstTexture->asRenderTarget());
+ convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY,
+ Gr1DKernelEffect::kY_Direction);
+ srcTexture = dstTexture;
+ SkTSwap(dstTexture, tempTexture);
+ }
+
+ if (scaleFactorX > 1 || scaleFactorY > 1) {
+ // Clear one pixel to the right and below, to accommodate bilinear
+ // upsampling.
+ clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
+ srcIRect.width() + 1, 1);
+ context->clear(&clearRect, 0x0);
+ clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
+ 1, srcIRect.height());
+ context->clear(&clearRect, 0x0);
+ SkMatrix matrix;
+ matrix.setIDiv(srcTexture->width(), srcTexture->height());
+ context->setRenderTarget(dstTexture->asRenderTarget());
+
+ GrPaint paint;
+ // FIXME: this should be mitchell, not bilinear.
+ GrTextureParams params(SkShader::kClamp_TileMode, true);
+ paint.addColorTextureEffect(srcTexture, matrix, params);
+
+ SkRect dstRect(srcRect);
+ scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
+ context->drawRectToRect(paint, dstRect, srcRect);
+ srcRect = dstRect;
+ srcTexture = dstTexture;
+ SkTSwap(dstTexture, tempTexture);
+ }
+ if (srcTexture == temp1.texture()) {
+ return temp1.detach();
+ } else if (srcTexture == temp2.texture()) {
+ return temp2.detach();
+ } else {
+ srcTexture->ref();
+ return srcTexture;
+ }
+}
+#endif
+
+}
\ No newline at end of file