OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2013 Google Inc. |
| 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 "SkGpuBlurUtils.h" |
| 9 |
| 10 #include "SkRect.h" |
| 11 |
| 12 #if SK_SUPPORT_GPU |
| 13 #include "effects/GrConvolutionEffect.h" |
| 14 #include "GrContext.h" |
| 15 #endif |
| 16 |
| 17 namespace SkGpuBlurUtils { |
| 18 |
| 19 #if SK_SUPPORT_GPU |
| 20 |
| 21 #define MAX_BLUR_SIGMA 4.0f |
| 22 |
| 23 static void scale_rect(SkRect* rect, float xScale, float yScale) { |
| 24 rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale)); |
| 25 rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale)); |
| 26 rect->fRight = SkScalarMul(rect->fRight, SkFloatToScalar(xScale)); |
| 27 rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale)); |
| 28 } |
| 29 |
| 30 static float adjust_sigma(float sigma, int *scaleFactor, int *radius) { |
| 31 *scaleFactor = 1; |
| 32 while (sigma > MAX_BLUR_SIGMA) { |
| 33 *scaleFactor *= 2; |
| 34 sigma *= 0.5f; |
| 35 } |
| 36 *radius = static_cast<int>(ceilf(sigma * 3.0f)); |
| 37 GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius); |
| 38 return sigma; |
| 39 } |
| 40 |
| 41 static void convolve_gaussian(GrContext* context, |
| 42 GrTexture* texture, |
| 43 const SkRect& rect, |
| 44 float sigma, |
| 45 int radius, |
| 46 Gr1DKernelEffect::Direction direction) { |
| 47 GrPaint paint; |
| 48 |
| 49 SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture, |
| 50 direction
, |
| 51 radius, |
| 52 sigma)); |
| 53 paint.addColorEffect(conv); |
| 54 context->drawRect(paint, rect); |
| 55 } |
| 56 |
| 57 GrTexture* GaussianBlur(GrContext* context, |
| 58 GrTexture* srcTexture, |
| 59 bool canClobberSrc, |
| 60 const SkRect& rect, |
| 61 float sigmaX, |
| 62 float sigmaY) { |
| 63 GrAssert(NULL != context); |
| 64 |
| 65 GrContext::AutoRenderTarget art(context); |
| 66 |
| 67 GrContext::AutoMatrix am; |
| 68 am.setIdentity(context); |
| 69 |
| 70 SkIRect clearRect; |
| 71 int scaleFactorX, radiusX; |
| 72 int scaleFactorY, radiusY; |
| 73 sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); |
| 74 sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); |
| 75 |
| 76 SkRect srcRect(rect); |
| 77 scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); |
| 78 srcRect.roundOut(); |
| 79 scale_rect(&srcRect, static_cast<float>(scaleFactorX), |
| 80 static_cast<float>(scaleFactorY)); |
| 81 |
| 82 GrContext::AutoClip acs(context, srcRect); |
| 83 |
| 84 GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() || |
| 85 kRGBA_8888_GrPixelConfig == srcTexture->config() || |
| 86 kAlpha_8_GrPixelConfig == srcTexture->config()); |
| 87 |
| 88 GrTextureDesc desc; |
| 89 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; |
| 90 desc.fWidth = SkScalarFloorToInt(srcRect.width()); |
| 91 desc.fHeight = SkScalarFloorToInt(srcRect.height()); |
| 92 desc.fConfig = srcTexture->config(); |
| 93 |
| 94 GrAutoScratchTexture temp1, temp2; |
| 95 GrTexture* dstTexture = temp1.set(context, desc); |
| 96 GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, des
c); |
| 97 if (NULL == dstTexture || NULL == tempTexture) { |
| 98 return NULL; |
| 99 } |
| 100 |
| 101 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { |
| 102 GrPaint paint; |
| 103 SkMatrix matrix; |
| 104 matrix.setIDiv(srcTexture->width(), srcTexture->height()); |
| 105 context->setRenderTarget(dstTexture->asRenderTarget()); |
| 106 SkRect dstRect(srcRect); |
| 107 scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, |
| 108 i < scaleFactorY ? 0.5f : 1.0f); |
| 109 GrTextureParams params(SkShader::kClamp_TileMode, true); |
| 110 paint.addColorTextureEffect(srcTexture, matrix, params); |
| 111 context->drawRectToRect(paint, dstRect, srcRect); |
| 112 srcRect = dstRect; |
| 113 srcTexture = dstTexture; |
| 114 SkTSwap(dstTexture, tempTexture); |
| 115 } |
| 116 |
| 117 SkIRect srcIRect; |
| 118 srcRect.roundOut(&srcIRect); |
| 119 |
| 120 if (sigmaX > 0.0f) { |
| 121 if (scaleFactorX > 1) { |
| 122 // Clear out a radius to the right of the srcRect to prevent the |
| 123 // X convolution from reading garbage. |
| 124 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, |
| 125 radiusX, srcIRect.height()); |
| 126 context->clear(&clearRect, 0x0); |
| 127 } |
| 128 context->setRenderTarget(dstTexture->asRenderTarget()); |
| 129 convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX, |
| 130 Gr1DKernelEffect::kX_Direction); |
| 131 srcTexture = dstTexture; |
| 132 SkTSwap(dstTexture, tempTexture); |
| 133 } |
| 134 |
| 135 if (sigmaY > 0.0f) { |
| 136 if (scaleFactorY > 1 || sigmaX > 0.0f) { |
| 137 // Clear out a radius below the srcRect to prevent the Y |
| 138 // convolution from reading garbage. |
| 139 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, |
| 140 srcIRect.width(), radiusY); |
| 141 context->clear(&clearRect, 0x0); |
| 142 } |
| 143 |
| 144 context->setRenderTarget(dstTexture->asRenderTarget()); |
| 145 convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY, |
| 146 Gr1DKernelEffect::kY_Direction); |
| 147 srcTexture = dstTexture; |
| 148 SkTSwap(dstTexture, tempTexture); |
| 149 } |
| 150 |
| 151 if (scaleFactorX > 1 || scaleFactorY > 1) { |
| 152 // Clear one pixel to the right and below, to accommodate bilinear |
| 153 // upsampling. |
| 154 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, |
| 155 srcIRect.width() + 1, 1); |
| 156 context->clear(&clearRect, 0x0); |
| 157 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, |
| 158 1, srcIRect.height()); |
| 159 context->clear(&clearRect, 0x0); |
| 160 SkMatrix matrix; |
| 161 matrix.setIDiv(srcTexture->width(), srcTexture->height()); |
| 162 context->setRenderTarget(dstTexture->asRenderTarget()); |
| 163 |
| 164 GrPaint paint; |
| 165 // FIXME: this should be mitchell, not bilinear. |
| 166 GrTextureParams params(SkShader::kClamp_TileMode, true); |
| 167 paint.addColorTextureEffect(srcTexture, matrix, params); |
| 168 |
| 169 SkRect dstRect(srcRect); |
| 170 scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); |
| 171 context->drawRectToRect(paint, dstRect, srcRect); |
| 172 srcRect = dstRect; |
| 173 srcTexture = dstTexture; |
| 174 SkTSwap(dstTexture, tempTexture); |
| 175 } |
| 176 if (srcTexture == temp1.texture()) { |
| 177 return temp1.detach(); |
| 178 } else if (srcTexture == temp2.texture()) { |
| 179 return temp2.detach(); |
| 180 } else { |
| 181 srcTexture->ref(); |
| 182 return srcTexture; |
| 183 } |
| 184 } |
| 185 #endif |
| 186 |
| 187 } |
OLD | NEW |