1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/effects/SkGpuBlurUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,265 @@ 1.4 +/* 1.5 + * Copyright 2013 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkGpuBlurUtils.h" 1.12 + 1.13 +#include "SkRect.h" 1.14 + 1.15 +#if SK_SUPPORT_GPU 1.16 +#include "effects/GrConvolutionEffect.h" 1.17 +#include "effects/GrTextureDomain.h" 1.18 +#include "GrContext.h" 1.19 +#endif 1.20 + 1.21 +namespace SkGpuBlurUtils { 1.22 + 1.23 +#if SK_SUPPORT_GPU 1.24 + 1.25 +#define MAX_BLUR_SIGMA 4.0f 1.26 + 1.27 +static void scale_rect(SkRect* rect, float xScale, float yScale) { 1.28 + rect->fLeft = SkScalarMul(rect->fLeft, xScale); 1.29 + rect->fTop = SkScalarMul(rect->fTop, yScale); 1.30 + rect->fRight = SkScalarMul(rect->fRight, xScale); 1.31 + rect->fBottom = SkScalarMul(rect->fBottom, yScale); 1.32 +} 1.33 + 1.34 +static float adjust_sigma(float sigma, int *scaleFactor, int *radius) { 1.35 + *scaleFactor = 1; 1.36 + while (sigma > MAX_BLUR_SIGMA) { 1.37 + *scaleFactor *= 2; 1.38 + sigma *= 0.5f; 1.39 + } 1.40 + *radius = static_cast<int>(ceilf(sigma * 3.0f)); 1.41 + SkASSERT(*radius <= GrConvolutionEffect::kMaxKernelRadius); 1.42 + return sigma; 1.43 +} 1.44 + 1.45 +static void convolve_gaussian_pass(GrContext* context, 1.46 + const SkRect& srcRect, 1.47 + const SkRect& dstRect, 1.48 + GrTexture* texture, 1.49 + Gr1DKernelEffect::Direction direction, 1.50 + int radius, 1.51 + float sigma, 1.52 + bool useBounds, 1.53 + float bounds[2]) { 1.54 + GrPaint paint; 1.55 + paint.reset(); 1.56 + SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian( 1.57 + texture, direction, radius, sigma, useBounds, bounds)); 1.58 + paint.reset(); 1.59 + paint.addColorEffect(conv); 1.60 + context->drawRectToRect(paint, dstRect, srcRect); 1.61 +} 1.62 + 1.63 +static void convolve_gaussian(GrContext* context, 1.64 + const SkRect& srcRect, 1.65 + const SkRect& dstRect, 1.66 + GrTexture* texture, 1.67 + Gr1DKernelEffect::Direction direction, 1.68 + int radius, 1.69 + float sigma, 1.70 + bool cropToSrcRect) { 1.71 + float bounds[2] = { 0.0f, 1.0f }; 1.72 + if (!cropToSrcRect) { 1.73 + convolve_gaussian_pass(context, srcRect, dstRect, texture, 1.74 + direction, radius, sigma, false, bounds); 1.75 + return; 1.76 + } 1.77 + SkRect lowerSrcRect = srcRect, lowerDstRect = dstRect; 1.78 + SkRect middleSrcRect = srcRect, middleDstRect = dstRect; 1.79 + SkRect upperSrcRect = srcRect, upperDstRect = dstRect; 1.80 + SkScalar size; 1.81 + SkScalar rad = SkIntToScalar(radius); 1.82 + if (direction == Gr1DKernelEffect::kX_Direction) { 1.83 + bounds[0] = SkScalarToFloat(srcRect.left()) / texture->width(); 1.84 + bounds[1] = SkScalarToFloat(srcRect.right()) / texture->width(); 1.85 + size = srcRect.width(); 1.86 + lowerSrcRect.fRight = srcRect.left() + rad; 1.87 + lowerDstRect.fRight = dstRect.left() + rad; 1.88 + upperSrcRect.fLeft = srcRect.right() - rad; 1.89 + upperDstRect.fLeft = dstRect.right() - rad; 1.90 + middleSrcRect.inset(rad, 0); 1.91 + middleDstRect.inset(rad, 0); 1.92 + } else { 1.93 + bounds[0] = SkScalarToFloat(srcRect.top()) / texture->height(); 1.94 + bounds[1] = SkScalarToFloat(srcRect.bottom()) / texture->height(); 1.95 + size = srcRect.height(); 1.96 + lowerSrcRect.fBottom = srcRect.top() + rad; 1.97 + lowerDstRect.fBottom = dstRect.top() + rad; 1.98 + upperSrcRect.fTop = srcRect.bottom() - rad; 1.99 + upperDstRect.fTop = dstRect.bottom() - rad; 1.100 + middleSrcRect.inset(0, rad); 1.101 + middleDstRect.inset(0, rad); 1.102 + } 1.103 + if (radius >= size * SK_ScalarHalf) { 1.104 + // Blur radius covers srcRect; use bounds over entire draw 1.105 + convolve_gaussian_pass(context, srcRect, dstRect, texture, 1.106 + direction, radius, sigma, true, bounds); 1.107 + } else { 1.108 + // Draw upper and lower margins with bounds; middle without. 1.109 + convolve_gaussian_pass(context, lowerSrcRect, lowerDstRect, texture, 1.110 + direction, radius, sigma, true, bounds); 1.111 + convolve_gaussian_pass(context, upperSrcRect, upperDstRect, texture, 1.112 + direction, radius, sigma, true, bounds); 1.113 + convolve_gaussian_pass(context, middleSrcRect, middleDstRect, texture, 1.114 + direction, radius, sigma, false, bounds); 1.115 + } 1.116 +} 1.117 + 1.118 +GrTexture* GaussianBlur(GrContext* context, 1.119 + GrTexture* srcTexture, 1.120 + bool canClobberSrc, 1.121 + const SkRect& rect, 1.122 + bool cropToRect, 1.123 + float sigmaX, 1.124 + float sigmaY) { 1.125 + SkASSERT(NULL != context); 1.126 + 1.127 + GrContext::AutoRenderTarget art(context); 1.128 + 1.129 + GrContext::AutoMatrix am; 1.130 + am.setIdentity(context); 1.131 + 1.132 + SkIRect clearRect; 1.133 + int scaleFactorX, radiusX; 1.134 + int scaleFactorY, radiusY; 1.135 + sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); 1.136 + sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); 1.137 + 1.138 + SkRect srcRect(rect); 1.139 + scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); 1.140 + srcRect.roundOut(); 1.141 + scale_rect(&srcRect, static_cast<float>(scaleFactorX), 1.142 + static_cast<float>(scaleFactorY)); 1.143 + 1.144 + GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height())); 1.145 + 1.146 + SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || 1.147 + kRGBA_8888_GrPixelConfig == srcTexture->config() || 1.148 + kAlpha_8_GrPixelConfig == srcTexture->config()); 1.149 + 1.150 + GrTextureDesc desc; 1.151 + desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; 1.152 + desc.fWidth = SkScalarFloorToInt(srcRect.width()); 1.153 + desc.fHeight = SkScalarFloorToInt(srcRect.height()); 1.154 + desc.fConfig = srcTexture->config(); 1.155 + 1.156 + GrAutoScratchTexture temp1, temp2; 1.157 + GrTexture* dstTexture = temp1.set(context, desc); 1.158 + GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc); 1.159 + if (NULL == dstTexture || NULL == tempTexture) { 1.160 + return NULL; 1.161 + } 1.162 + 1.163 + for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { 1.164 + GrPaint paint; 1.165 + SkMatrix matrix; 1.166 + matrix.setIDiv(srcTexture->width(), srcTexture->height()); 1.167 + context->setRenderTarget(dstTexture->asRenderTarget()); 1.168 + SkRect dstRect(srcRect); 1.169 + if (cropToRect && i == 1) { 1.170 + dstRect.offset(-dstRect.fLeft, -dstRect.fTop); 1.171 + SkRect domain; 1.172 + matrix.mapRect(&domain, rect); 1.173 + domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, 1.174 + i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); 1.175 + SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create( 1.176 + srcTexture, 1.177 + matrix, 1.178 + domain, 1.179 + GrTextureDomain::kDecal_Mode, 1.180 + GrTextureParams::kBilerp_FilterMode)); 1.181 + paint.addColorEffect(effect); 1.182 + } else { 1.183 + GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); 1.184 + paint.addColorTextureEffect(srcTexture, matrix, params); 1.185 + } 1.186 + scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, 1.187 + i < scaleFactorY ? 0.5f : 1.0f); 1.188 + context->drawRectToRect(paint, dstRect, srcRect); 1.189 + srcRect = dstRect; 1.190 + srcTexture = dstTexture; 1.191 + SkTSwap(dstTexture, tempTexture); 1.192 + } 1.193 + 1.194 + SkIRect srcIRect; 1.195 + srcRect.roundOut(&srcIRect); 1.196 + 1.197 + if (sigmaX > 0.0f) { 1.198 + if (scaleFactorX > 1) { 1.199 + // Clear out a radius to the right of the srcRect to prevent the 1.200 + // X convolution from reading garbage. 1.201 + clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1.202 + radiusX, srcIRect.height()); 1.203 + context->clear(&clearRect, 0x0, false); 1.204 + } 1.205 + context->setRenderTarget(dstTexture->asRenderTarget()); 1.206 + SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); 1.207 + convolve_gaussian(context, srcRect, dstRect, srcTexture, 1.208 + Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); 1.209 + srcTexture = dstTexture; 1.210 + srcRect = dstRect; 1.211 + SkTSwap(dstTexture, tempTexture); 1.212 + } 1.213 + 1.214 + if (sigmaY > 0.0f) { 1.215 + if (scaleFactorY > 1 || sigmaX > 0.0f) { 1.216 + // Clear out a radius below the srcRect to prevent the Y 1.217 + // convolution from reading garbage. 1.218 + clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, 1.219 + srcIRect.width(), radiusY); 1.220 + context->clear(&clearRect, 0x0, false); 1.221 + } 1.222 + 1.223 + context->setRenderTarget(dstTexture->asRenderTarget()); 1.224 + SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); 1.225 + convolve_gaussian(context, srcRect, dstRect, srcTexture, 1.226 + Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); 1.227 + srcTexture = dstTexture; 1.228 + srcRect = dstRect; 1.229 + SkTSwap(dstTexture, tempTexture); 1.230 + } 1.231 + 1.232 + if (scaleFactorX > 1 || scaleFactorY > 1) { 1.233 + // Clear one pixel to the right and below, to accommodate bilinear 1.234 + // upsampling. 1.235 + clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, 1.236 + srcIRect.width() + 1, 1); 1.237 + context->clear(&clearRect, 0x0, false); 1.238 + clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1.239 + 1, srcIRect.height()); 1.240 + context->clear(&clearRect, 0x0, false); 1.241 + SkMatrix matrix; 1.242 + matrix.setIDiv(srcTexture->width(), srcTexture->height()); 1.243 + context->setRenderTarget(dstTexture->asRenderTarget()); 1.244 + 1.245 + GrPaint paint; 1.246 + // FIXME: this should be mitchell, not bilinear. 1.247 + GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); 1.248 + paint.addColorTextureEffect(srcTexture, matrix, params); 1.249 + 1.250 + SkRect dstRect(srcRect); 1.251 + scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); 1.252 + context->drawRectToRect(paint, dstRect, srcRect); 1.253 + srcRect = dstRect; 1.254 + srcTexture = dstTexture; 1.255 + SkTSwap(dstTexture, tempTexture); 1.256 + } 1.257 + if (srcTexture == temp1.texture()) { 1.258 + return temp1.detach(); 1.259 + } else if (srcTexture == temp2.texture()) { 1.260 + return temp2.detach(); 1.261 + } else { 1.262 + srcTexture->ref(); 1.263 + return srcTexture; 1.264 + } 1.265 +} 1.266 +#endif 1.267 + 1.268 +}