gfx/skia/trunk/src/effects/SkGpuBlurUtils.cpp

changeset 0
6474c204b198
     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 +}

mercurial