LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkGpuBlurUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 221 0.5 %
Date: 2017-07-14 16:53:18 Functions: 2 11 18.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "GrCaps.h"
      14             : #include "GrContext.h"
      15             : #include "GrFixedClip.h"
      16             : #include "GrRenderTargetContext.h"
      17             : #include "GrRenderTargetContextPriv.h"
      18             : #include "effects/GrGaussianConvolutionFragmentProcessor.h"
      19             : #include "effects/GrMatrixConvolutionEffect.h"
      20             : 
      21             : #define MAX_BLUR_SIGMA 4.0f
      22             : 
      23           0 : static void scale_irect_roundout(SkIRect* rect, float xScale, float yScale) {
      24           0 :     rect->fLeft   = SkScalarFloorToInt(rect->fLeft  * xScale);
      25           0 :     rect->fTop    = SkScalarFloorToInt(rect->fTop   * yScale);
      26           0 :     rect->fRight  = SkScalarCeilToInt(rect->fRight  * xScale);
      27           0 :     rect->fBottom = SkScalarCeilToInt(rect->fBottom * yScale);
      28           0 : }
      29             : 
      30           0 : static void scale_irect(SkIRect* rect, int xScale, int yScale) {
      31           0 :     rect->fLeft   *= xScale;
      32           0 :     rect->fTop    *= yScale;
      33           0 :     rect->fRight  *= xScale;
      34           0 :     rect->fBottom *= yScale;
      35           0 : }
      36             : 
      37             : #ifdef SK_DEBUG
      38           0 : static inline int is_even(int x) { return !(x & 1); }
      39             : #endif
      40             : 
      41           0 : static void shrink_irect_by_2(SkIRect* rect, bool xAxis, bool yAxis) {
      42           0 :     if (xAxis) {
      43           0 :         SkASSERT(is_even(rect->fLeft) && is_even(rect->fRight));
      44           0 :         rect->fLeft /= 2;
      45           0 :         rect->fRight /= 2;
      46             :     }
      47           0 :     if (yAxis) {
      48           0 :         SkASSERT(is_even(rect->fTop) && is_even(rect->fBottom));
      49           0 :         rect->fTop /= 2;
      50           0 :         rect->fBottom /= 2;
      51             :     }
      52           0 : }
      53             : 
      54           0 : static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int *radius) {
      55           0 :     *scaleFactor = 1;
      56           0 :     while (sigma > MAX_BLUR_SIGMA) {
      57           0 :         *scaleFactor *= 2;
      58           0 :         sigma *= 0.5f;
      59           0 :         if (*scaleFactor > maxTextureSize) {
      60           0 :             *scaleFactor = maxTextureSize;
      61           0 :             sigma = MAX_BLUR_SIGMA;
      62             :         }
      63             :     }
      64           0 :     *radius = static_cast<int>(ceilf(sigma * 3.0f));
      65           0 :     SkASSERT(*radius <= GrGaussianConvolutionFragmentProcessor::kMaxKernelRadius);
      66           0 :     return sigma;
      67             : }
      68             : 
      69           0 : static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext,
      70             :                                  const GrClip& clip,
      71             :                                  const SkIRect& dstRect,
      72             :                                  const SkIPoint& srcOffset,
      73             :                                  sk_sp<GrTextureProxy> proxy,
      74             :                                  Gr1DKernelEffect::Direction direction,
      75             :                                  int radius,
      76             :                                  float sigma,
      77             :                                  bool useBounds,
      78             :                                  int bounds[2]) {
      79           0 :     GrPaint paint;
      80           0 :     paint.setGammaCorrect(renderTargetContext->isGammaCorrect());
      81             : 
      82           0 :     GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider();
      83             : 
      84             :     sk_sp<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
      85           0 :             resourceProvider, std::move(proxy), direction, radius, sigma, useBounds, bounds));
      86           0 :     paint.addColorFragmentProcessor(std::move(conv));
      87           0 :     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
      88           0 :     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
      89           0 :                                                -SkIntToScalar(srcOffset.y()));
      90           0 :     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
      91           0 :                                                  SkRect::Make(dstRect), localMatrix);
      92           0 : }
      93             : 
      94           0 : static void convolve_gaussian_2d(GrRenderTargetContext* renderTargetContext,
      95             :                                  const GrClip& clip,
      96             :                                  const SkIRect& dstRect,
      97             :                                  const SkIPoint& srcOffset,
      98             :                                  sk_sp<GrTextureProxy> proxy,
      99             :                                  int radiusX,
     100             :                                  int radiusY,
     101             :                                  SkScalar sigmaX,
     102             :                                  SkScalar sigmaY,
     103             :                                  const SkIRect* srcBounds) {
     104           0 :     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
     105           0 :                                                -SkIntToScalar(srcOffset.y()));
     106           0 :     SkISize size = SkISize::Make(2 * radiusX + 1,  2 * radiusY + 1);
     107           0 :     SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
     108           0 :     GrPaint paint;
     109           0 :     paint.setGammaCorrect(renderTargetContext->isGammaCorrect());
     110           0 :     SkIRect bounds = srcBounds ? *srcBounds : SkIRect::EmptyIRect();
     111             : 
     112           0 :     GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider();
     113             : 
     114             :     sk_sp<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::MakeGaussian(
     115           0 :             resourceProvider, std::move(proxy), bounds, size, 1.0, 0.0, kernelOffset,
     116             :             srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode,
     117           0 :             true, sigmaX, sigmaY));
     118           0 :     paint.addColorFragmentProcessor(std::move(conv));
     119           0 :     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     120           0 :     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
     121           0 :                                                  SkRect::Make(dstRect), localMatrix);
     122           0 : }
     123             : 
     124           0 : static void convolve_gaussian(GrRenderTargetContext* renderTargetContext,
     125             :                               const GrClip& clip,
     126             :                               const SkIRect& srcRect,
     127             :                               sk_sp<GrTextureProxy> proxy,
     128             :                               Gr1DKernelEffect::Direction direction,
     129             :                               int radius,
     130             :                               float sigma,
     131             :                               const SkIRect* srcBounds,
     132             :                               const SkIPoint& srcOffset) {
     133           0 :     int bounds[2] = { 0, 0 };
     134           0 :     SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
     135           0 :     if (!srcBounds) {
     136           0 :         convolve_gaussian_1d(renderTargetContext, clip, dstRect, srcOffset,
     137           0 :                              std::move(proxy), direction, radius, sigma, false, bounds);
     138           0 :         return;
     139             :     }
     140           0 :     SkIRect midRect = *srcBounds, leftRect, rightRect;
     141           0 :     midRect.offset(srcOffset);
     142             :     SkIRect topRect, bottomRect;
     143           0 :     if (direction == Gr1DKernelEffect::kX_Direction) {
     144           0 :         bounds[0] = srcBounds->left();
     145           0 :         bounds[1] = srcBounds->right();
     146           0 :         topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
     147           0 :         bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
     148           0 :         midRect.inset(radius, 0);
     149           0 :         leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
     150             :         rightRect =
     151           0 :             SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
     152           0 :         dstRect.fTop = midRect.top();
     153           0 :         dstRect.fBottom = midRect.bottom();
     154             :     } else {
     155           0 :         bounds[0] = srcBounds->top();
     156           0 :         bounds[1] = srcBounds->bottom();
     157           0 :         topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
     158           0 :         bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
     159           0 :         midRect.inset(0, radius);
     160           0 :         leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
     161             :         rightRect =
     162           0 :             SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
     163           0 :         dstRect.fLeft = midRect.left();
     164           0 :         dstRect.fRight = midRect.right();
     165             :     }
     166           0 :     if (!topRect.isEmpty()) {
     167           0 :         renderTargetContext->clear(&topRect, 0, false);
     168             :     }
     169             : 
     170           0 :     if (!bottomRect.isEmpty()) {
     171           0 :         renderTargetContext->clear(&bottomRect, 0, false);
     172             :     }
     173           0 :     if (midRect.isEmpty()) {
     174             :         // Blur radius covers srcBounds; use bounds over entire draw
     175           0 :         convolve_gaussian_1d(renderTargetContext, clip, dstRect, srcOffset,
     176           0 :                              std::move(proxy), direction, radius, sigma, true, bounds);
     177             :     } else {
     178             :         // Draw right and left margins with bounds; middle without.
     179           0 :         convolve_gaussian_1d(renderTargetContext, clip, leftRect, srcOffset,
     180           0 :                              proxy, direction, radius, sigma, true, bounds);
     181           0 :         convolve_gaussian_1d(renderTargetContext, clip, rightRect, srcOffset,
     182           0 :                              proxy, direction, radius, sigma, true, bounds);
     183           0 :         convolve_gaussian_1d(renderTargetContext, clip, midRect, srcOffset,
     184           0 :                              std::move(proxy), direction, radius, sigma, false, bounds);
     185             :     }
     186             : }
     187             : 
     188             : namespace SkGpuBlurUtils {
     189             : 
     190           0 : sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context,
     191             :                                           sk_sp<GrTextureProxy> srcProxy,
     192             :                                           sk_sp<SkColorSpace> colorSpace,
     193             :                                           const SkIRect& dstBounds,
     194             :                                           const SkIRect* srcBounds,
     195             :                                           float sigmaX,
     196             :                                           float sigmaY,
     197             :                                           SkBackingFit fit) {
     198           0 :     SkASSERT(context);
     199             : 
     200             :     {
     201             :         // Chrome is crashing with proxies when they need to be instantiated.
     202             :         // Force an instantiation here (where, in olden days, we used to require a GrTexture)
     203             :         // to see if the input is already un-instantiable.
     204           0 :         GrTexture* temp = srcProxy->instantiate(context->resourceProvider());
     205           0 :         if (!temp) {
     206           0 :             return nullptr;
     207             :         }
     208             :     }
     209             : 
     210             :     SkIRect clearRect;
     211             :     int scaleFactorX, radiusX;
     212             :     int scaleFactorY, radiusY;
     213           0 :     int maxTextureSize = context->caps()->maxTextureSize();
     214           0 :     sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
     215           0 :     sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
     216           0 :     SkASSERT(sigmaX || sigmaY);
     217             : 
     218           0 :     SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
     219           0 :     SkIRect localDstBounds = SkIRect::MakeWH(dstBounds.width(), dstBounds.height());
     220             :     SkIRect localSrcBounds;
     221             :     SkIRect srcRect;
     222           0 :     if (srcBounds) {
     223           0 :         srcRect = localSrcBounds = *srcBounds;
     224           0 :         srcRect.offset(srcOffset);
     225           0 :         srcBounds = &localSrcBounds;
     226             :     } else {
     227           0 :         srcRect = localDstBounds;
     228             :     }
     229             : 
     230           0 :     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
     231           0 :     scale_irect(&srcRect, scaleFactorX, scaleFactorY);
     232             : 
     233             :     // setup new clip
     234           0 :     GrFixedClip clip(localDstBounds);
     235             : 
     236           0 :     const GrPixelConfig config = srcProxy->config();
     237             : 
     238           0 :     SkASSERT(kBGRA_8888_GrPixelConfig  == config || kRGBA_8888_GrPixelConfig  == config ||
     239             :              kSRGBA_8888_GrPixelConfig == config || kSBGRA_8888_GrPixelConfig == config ||
     240             :              kRGBA_half_GrPixelConfig  == config || kAlpha_8_GrPixelConfig    == config);
     241             : 
     242           0 :     const int width = dstBounds.width();
     243           0 :     const int height = dstBounds.height();
     244             : 
     245             :     sk_sp<GrRenderTargetContext> dstRenderTargetContext(context->makeDeferredRenderTargetContext(
     246           0 :         fit, width, height, config, colorSpace, 0, kBottomLeft_GrSurfaceOrigin));
     247           0 :     if (!dstRenderTargetContext) {
     248           0 :         return nullptr;
     249             :     }
     250             : 
     251             :     // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
     252             :     // launch a single non separable kernel vs two launches
     253           0 :     if (sigmaX > 0.0f && sigmaY > 0.0f &&
     254           0 :             (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
     255             :         // We shouldn't be scaling because this is a small size blur
     256           0 :         SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
     257             : 
     258           0 :         convolve_gaussian_2d(dstRenderTargetContext.get(), clip, localDstBounds, srcOffset,
     259           0 :                              std::move(srcProxy), radiusX, radiusY, sigmaX, sigmaY, srcBounds);
     260             : 
     261           0 :         return dstRenderTargetContext;
     262             :     } 
     263             : 
     264             :     sk_sp<GrRenderTargetContext> tmpRenderTargetContext(context->makeDeferredRenderTargetContext(
     265           0 :         fit, width, height, config, colorSpace, 0, kBottomLeft_GrSurfaceOrigin));
     266           0 :     if (!tmpRenderTargetContext) {
     267           0 :         return nullptr;
     268             :     }
     269             : 
     270           0 :     sk_sp<GrRenderTargetContext> srcRenderTargetContext;
     271             : 
     272           0 :     SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
     273             : 
     274           0 :     for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
     275           0 :         GrPaint paint;
     276           0 :         paint.setGammaCorrect(dstRenderTargetContext->isGammaCorrect());
     277           0 :         SkIRect dstRect(srcRect);
     278           0 :         if (srcBounds && i == 1) {
     279           0 :             SkRect domain = SkRect::Make(*srcBounds);
     280           0 :             domain.inset((i < scaleFactorX) ? SK_ScalarHalf : 0.0f,
     281           0 :                          (i < scaleFactorY) ? SK_ScalarHalf : 0.0f);
     282             :             sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make(
     283             :                                                         context->resourceProvider(),
     284           0 :                                                         std::move(srcProxy),
     285             :                                                         nullptr,
     286             :                                                         SkMatrix::I(),
     287             :                                                         domain,
     288             :                                                         GrTextureDomain::kDecal_Mode,
     289           0 :                                                         GrSamplerParams::kBilerp_FilterMode));
     290           0 :             paint.addColorFragmentProcessor(std::move(fp));
     291           0 :             srcRect.offset(-srcOffset);
     292           0 :             srcOffset.set(0, 0);
     293             :         } else {
     294           0 :             GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
     295           0 :             paint.addColorTextureProcessor(context->resourceProvider(), std::move(srcProxy),
     296           0 :                                            nullptr, SkMatrix::I(), params);
     297             :         }
     298           0 :         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     299           0 :         shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
     300             : 
     301           0 :         dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
     302           0 :                                                SkRect::Make(dstRect), SkRect::Make(srcRect));
     303             : 
     304           0 :         srcRenderTargetContext = dstRenderTargetContext;
     305           0 :         srcRect = dstRect;
     306           0 :         srcProxy = srcRenderTargetContext->asTextureProxyRef();
     307           0 :         if (!srcProxy) {
     308           0 :             return nullptr;
     309             :         }
     310           0 :         dstRenderTargetContext.swap(tmpRenderTargetContext);
     311           0 :         localSrcBounds = srcRect;
     312             :     }
     313             : 
     314           0 :     srcRect = localDstBounds;
     315           0 :     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
     316           0 :     if (sigmaX > 0.0f) {
     317           0 :         if (scaleFactorX > 1) {
     318           0 :             SkASSERT(srcRenderTargetContext);
     319             : 
     320             :             // Clear out a radius to the right of the srcRect to prevent the
     321             :             // X convolution from reading garbage.
     322             :             clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop,
     323           0 :                                           radiusX, srcRect.height());
     324           0 :             srcRenderTargetContext->priv().absClear(&clearRect, 0x0);
     325             :         }
     326             : 
     327           0 :         convolve_gaussian(dstRenderTargetContext.get(), clip, srcRect,
     328           0 :                           std::move(srcProxy), Gr1DKernelEffect::kX_Direction, radiusX, sigmaX,
     329           0 :                           srcBounds, srcOffset);
     330           0 :         srcRenderTargetContext = dstRenderTargetContext;
     331           0 :         srcProxy = srcRenderTargetContext->asTextureProxyRef();
     332           0 :         if (!srcProxy) {
     333           0 :             return nullptr;
     334             :         }
     335           0 :         srcRect.offsetTo(0, 0);
     336           0 :         dstRenderTargetContext.swap(tmpRenderTargetContext);
     337           0 :         localSrcBounds = srcRect;
     338           0 :         srcOffset.set(0, 0);
     339             :     }
     340             : 
     341           0 :     if (sigmaY > 0.0f) {
     342           0 :         if (scaleFactorY > 1 || sigmaX > 0.0f) {
     343           0 :             SkASSERT(srcRenderTargetContext);
     344             : 
     345             :             // Clear out a radius below the srcRect to prevent the Y
     346             :             // convolution from reading garbage.
     347             :             clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom,
     348           0 :                                           srcRect.width(), radiusY);
     349           0 :             srcRenderTargetContext->priv().absClear(&clearRect, 0x0);
     350             :         }
     351             : 
     352           0 :         convolve_gaussian(dstRenderTargetContext.get(), clip, srcRect,
     353           0 :                           std::move(srcProxy), Gr1DKernelEffect::kY_Direction, radiusY, sigmaY,
     354           0 :                           srcBounds, srcOffset);
     355             : 
     356           0 :         srcRenderTargetContext = dstRenderTargetContext;
     357           0 :         srcRect.offsetTo(0, 0);
     358           0 :         dstRenderTargetContext.swap(tmpRenderTargetContext);
     359             :     }
     360             : 
     361           0 :     SkASSERT(srcRenderTargetContext);
     362           0 :     srcProxy.reset(nullptr);  // we don't use this from here on out
     363             : 
     364           0 :     if (scaleFactorX > 1 || scaleFactorY > 1) {
     365             :         // Clear one pixel to the right and below, to accommodate bilinear upsampling.
     366             :         // TODO: it seems like we should actually be clamping here rather than darkening
     367             :         // the bottom right edges.
     368           0 :         clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1);
     369           0 :         srcRenderTargetContext->priv().absClear(&clearRect, 0x0);
     370           0 :         clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height());
     371           0 :         srcRenderTargetContext->priv().absClear(&clearRect, 0x0);
     372             : 
     373           0 :         GrPaint paint;
     374           0 :         paint.setGammaCorrect(dstRenderTargetContext->isGammaCorrect());
     375             :         // FIXME:  this should be mitchell, not bilinear.
     376           0 :         GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
     377           0 :         sk_sp<GrTextureProxy> proxy(srcRenderTargetContext->asTextureProxyRef());
     378           0 :         if (!proxy) {
     379           0 :             return nullptr;
     380             :         }
     381             : 
     382           0 :         paint.addColorTextureProcessor(context->resourceProvider(), std::move(proxy),
     383           0 :                                        nullptr, SkMatrix::I(), params);
     384           0 :         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     385             : 
     386           0 :         SkIRect dstRect(srcRect);
     387           0 :         scale_irect(&dstRect, scaleFactorX, scaleFactorY);
     388             : 
     389           0 :         dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
     390           0 :                                                SkRect::Make(dstRect), SkRect::Make(srcRect));
     391             : 
     392           0 :         srcRenderTargetContext = dstRenderTargetContext;
     393           0 :         srcRect = dstRect;
     394           0 :         dstRenderTargetContext.swap(tmpRenderTargetContext);
     395             :     }
     396             : 
     397           0 :     return srcRenderTargetContext;
     398             : }
     399             : 
     400           9 : }
     401             : 
     402             : #endif
     403             : 

Generated by: LCOV version 1.13