Home | History | Annotate | Download | only in core
      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 #include "SkGr.h"
     22 
     23 #define MAX_BLUR_SIGMA 4.0f
     24 
     25 using Direction = GrGaussianConvolutionFragmentProcessor::Direction;
     26 
     27 static void scale_irect_roundout(SkIRect* rect, float xScale, float yScale) {
     28     rect->fLeft   = SkScalarFloorToInt(rect->fLeft  * xScale);
     29     rect->fTop    = SkScalarFloorToInt(rect->fTop   * yScale);
     30     rect->fRight  = SkScalarCeilToInt(rect->fRight  * xScale);
     31     rect->fBottom = SkScalarCeilToInt(rect->fBottom * yScale);
     32 }
     33 
     34 static void scale_irect(SkIRect* rect, int xScale, int yScale) {
     35     rect->fLeft   *= xScale;
     36     rect->fTop    *= yScale;
     37     rect->fRight  *= xScale;
     38     rect->fBottom *= yScale;
     39 }
     40 
     41 #ifdef SK_DEBUG
     42 static inline int is_even(int x) { return !(x & 1); }
     43 #endif
     44 
     45 static void shrink_irect_by_2(SkIRect* rect, bool xAxis, bool yAxis) {
     46     if (xAxis) {
     47         SkASSERT(is_even(rect->fLeft) && is_even(rect->fRight));
     48         rect->fLeft /= 2;
     49         rect->fRight /= 2;
     50     }
     51     if (yAxis) {
     52         SkASSERT(is_even(rect->fTop) && is_even(rect->fBottom));
     53         rect->fTop /= 2;
     54         rect->fBottom /= 2;
     55     }
     56 }
     57 
     58 static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int *radius) {
     59     *scaleFactor = 1;
     60     while (sigma > MAX_BLUR_SIGMA) {
     61         *scaleFactor *= 2;
     62         sigma *= 0.5f;
     63         if (*scaleFactor > maxTextureSize) {
     64             *scaleFactor = maxTextureSize;
     65             sigma = MAX_BLUR_SIGMA;
     66         }
     67     }
     68     *radius = static_cast<int>(ceilf(sigma * 3.0f));
     69     SkASSERT(*radius <= GrGaussianConvolutionFragmentProcessor::kMaxKernelRadius);
     70     return sigma;
     71 }
     72 
     73 static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext,
     74                                  const GrClip& clip,
     75                                  const SkIRect& dstRect,
     76                                  const SkIPoint& srcOffset,
     77                                  sk_sp<GrTextureProxy> proxy,
     78                                  Direction direction,
     79                                  int radius,
     80                                  float sigma,
     81                                  GrTextureDomain::Mode mode,
     82                                  int bounds[2]) {
     83     GrPaint paint;
     84     std::unique_ptr<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
     85             std::move(proxy), direction, radius, sigma, mode, bounds));
     86     paint.addColorFragmentProcessor(std::move(conv));
     87     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     88     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
     89                                                -SkIntToScalar(srcOffset.y()));
     90     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
     91                                                  SkRect::Make(dstRect), localMatrix);
     92 }
     93 
     94 static GrPixelConfig get_blur_config(GrTextureProxy* proxy) {
     95     GrPixelConfig config = proxy->config();
     96 
     97     SkASSERT(kBGRA_8888_GrPixelConfig == config || kRGBA_8888_GrPixelConfig == config ||
     98              kRGB_888_GrPixelConfig == config || kRGBA_4444_GrPixelConfig == config ||
     99              kRGB_565_GrPixelConfig == config || kSRGBA_8888_GrPixelConfig == config ||
    100              kSBGRA_8888_GrPixelConfig == config || kRGBA_half_GrPixelConfig == config ||
    101              kAlpha_8_GrPixelConfig == config || kRGBA_1010102_GrPixelConfig == config);
    102 
    103     return config;
    104 }
    105 
    106 static sk_sp<GrRenderTargetContext> convolve_gaussian_2d(GrContext* context,
    107                                                          sk_sp<GrTextureProxy> proxy,
    108                                                          const SkIRect& srcBounds,
    109                                                          const SkIPoint& srcOffset,
    110                                                          int radiusX,
    111                                                          int radiusY,
    112                                                          SkScalar sigmaX,
    113                                                          SkScalar sigmaY,
    114                                                          GrTextureDomain::Mode mode,
    115                                                          const SkImageInfo& dstII,
    116                                                          SkBackingFit dstFit) {
    117 
    118     GrPixelConfig config = get_blur_config(proxy.get());
    119 
    120     GrBackendFormat format = proxy->backendFormat().makeTexture2D();
    121     if (!format.isValid()) {
    122         return nullptr;
    123     }
    124 
    125     sk_sp<GrRenderTargetContext> renderTargetContext;
    126     renderTargetContext = context->contextPriv().makeDeferredRenderTargetContext(
    127                                                          format,
    128                                                          dstFit, dstII.width(), dstII.height(),
    129                                                          config, dstII.refColorSpace(),
    130                                                          1, GrMipMapped::kNo,
    131                                                          proxy->origin());
    132     if (!renderTargetContext) {
    133         return nullptr;
    134     }
    135 
    136     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
    137                                                -SkIntToScalar(srcOffset.y()));
    138     SkISize size = SkISize::Make(2 * radiusX + 1,  2 * radiusY + 1);
    139     SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
    140     GrPaint paint;
    141     auto conv = GrMatrixConvolutionEffect::MakeGaussian(std::move(proxy), srcBounds, size, 1.0, 0.0,
    142                                                         kernelOffset, mode, true, sigmaX, sigmaY);
    143     paint.addColorFragmentProcessor(std::move(conv));
    144     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    145     GrFixedClip clip(dstII.bounds());
    146 
    147     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
    148                                                  SkRect::Make(dstII.bounds()), localMatrix);
    149 
    150     return renderTargetContext;
    151 }
    152 
    153 static sk_sp<GrRenderTargetContext> convolve_gaussian(GrContext* context,
    154                                                       sk_sp<GrTextureProxy> proxy,
    155                                                       const SkIRect& srcRect,
    156                                                       const SkIPoint& srcOffset,
    157                                                       Direction direction,
    158                                                       int radius,
    159                                                       float sigma,
    160                                                       SkIRect* contentRect,
    161                                                       GrTextureDomain::Mode mode,
    162                                                       const SkImageInfo& dstII,
    163                                                       SkBackingFit fit) {
    164     SkASSERT(srcRect.width() <= dstII.width() && srcRect.height() <= dstII.height());
    165 
    166     GrPixelConfig config = get_blur_config(proxy.get());
    167 
    168     GrBackendFormat format = proxy->backendFormat().makeTexture2D();
    169     if (!format.isValid()) {
    170         return nullptr;
    171     }
    172 
    173     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
    174     dstRenderTargetContext = context->contextPriv().makeDeferredRenderTargetContext(
    175                                                                 format,
    176                                                                 fit, srcRect.width(),
    177                                                                 srcRect.height(),
    178                                                                 config,
    179                                                                 dstII.refColorSpace(),
    180                                                                 1, GrMipMapped::kNo,
    181                                                                 proxy->origin());
    182     if (!dstRenderTargetContext) {
    183         return nullptr;
    184     }
    185 
    186     GrFixedClip clip(dstII.bounds());
    187 
    188     int bounds[2] = { 0, 0 };
    189     SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
    190     if (GrTextureDomain::kIgnore_Mode == mode) {
    191         *contentRect = dstRect;
    192         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, srcOffset,
    193                              std::move(proxy), direction, radius, sigma,
    194                              GrTextureDomain::kIgnore_Mode, bounds);
    195         return dstRenderTargetContext;
    196     }
    197 
    198     SkIRect midRect = *contentRect, leftRect, rightRect;
    199     midRect.offset(srcOffset);
    200     SkIRect topRect, bottomRect;
    201     if (Direction::kX == direction) {
    202         bounds[0] = contentRect->left();
    203         bounds[1] = contentRect->right();
    204         topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
    205         bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
    206         midRect.inset(radius, 0);
    207         leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
    208         rightRect =
    209             SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
    210         dstRect.fTop = midRect.top();
    211         dstRect.fBottom = midRect.bottom();
    212 
    213         contentRect->fLeft = dstRect.fLeft;
    214         contentRect->fTop = midRect.fTop;
    215         contentRect->fRight = dstRect.fRight;
    216         contentRect->fBottom = midRect.fBottom;
    217     } else {
    218         bounds[0] = contentRect->top();
    219         bounds[1] = contentRect->bottom();
    220         topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
    221         bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
    222         midRect.inset(0, radius);
    223         leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
    224         rightRect =
    225             SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
    226         dstRect.fLeft = midRect.left();
    227         dstRect.fRight = midRect.right();
    228 
    229         contentRect->fLeft = midRect.fLeft;
    230         contentRect->fTop = dstRect.fTop;
    231         contentRect->fRight = midRect.fRight;
    232         contentRect->fBottom = dstRect.fBottom;
    233     }
    234     if (!topRect.isEmpty()) {
    235         dstRenderTargetContext->clear(&topRect, SK_PMColor4fTRANSPARENT,
    236                                       GrRenderTargetContext::CanClearFullscreen::kNo);
    237     }
    238 
    239     if (!bottomRect.isEmpty()) {
    240         dstRenderTargetContext->clear(&bottomRect, SK_PMColor4fTRANSPARENT,
    241                                       GrRenderTargetContext::CanClearFullscreen::kNo);
    242     }
    243 
    244     if (midRect.isEmpty()) {
    245         // Blur radius covers srcBounds; use bounds over entire draw
    246         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, srcOffset,
    247                              std::move(proxy), direction, radius, sigma, mode, bounds);
    248     } else {
    249         // Draw right and left margins with bounds; middle without.
    250         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, srcOffset,
    251                              proxy, direction, radius, sigma, mode, bounds);
    252         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, srcOffset,
    253                              proxy, direction, radius, sigma, mode, bounds);
    254         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, srcOffset,
    255                              std::move(proxy), direction, radius, sigma,
    256                              GrTextureDomain::kIgnore_Mode, bounds);
    257     }
    258 
    259     return dstRenderTargetContext;
    260 }
    261 
    262 static sk_sp<GrTextureProxy> decimate(GrContext* context,
    263                                       sk_sp<GrTextureProxy> src,
    264                                       SkIPoint* srcOffset,
    265                                       SkIRect* contentRect,
    266                                       int scaleFactorX, int scaleFactorY,
    267                                       bool willBeXFiltering, bool willBeYFiltering,
    268                                       int radiusX, int radiusY,
    269                                       GrTextureDomain::Mode mode,
    270                                       const SkImageInfo& dstII) {
    271     SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
    272     SkASSERT(scaleFactorX > 1 || scaleFactorY > 1);
    273 
    274     GrPixelConfig config = get_blur_config(src.get());
    275 
    276     SkIRect srcRect;
    277     if (GrTextureDomain::kIgnore_Mode == mode) {
    278         srcRect = dstII.bounds();
    279     } else {
    280         srcRect = *contentRect;
    281         srcRect.offset(*srcOffset);
    282     }
    283 
    284     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    285     scale_irect(&srcRect, scaleFactorX, scaleFactorY);
    286 
    287     SkIRect dstRect(srcRect);
    288 
    289     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
    290 
    291     GrBackendFormat format = src->backendFormat().makeTexture2D();
    292     if (!format.isValid()) {
    293         return nullptr;
    294     }
    295 
    296     for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
    297         shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
    298 
    299         // We know this will not be the final draw so we are free to make it an approx match.
    300         dstRenderTargetContext = context->contextPriv().makeDeferredRenderTargetContext(
    301                                                     format,
    302                                                     SkBackingFit::kApprox,
    303                                                     dstRect.fRight,
    304                                                     dstRect.fBottom,
    305                                                     config, dstII.refColorSpace(),
    306                                                     1, GrMipMapped::kNo,
    307                                                     src->origin());
    308         if (!dstRenderTargetContext) {
    309             return nullptr;
    310         }
    311 
    312         GrPaint paint;
    313         if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
    314             // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
    315             GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
    316                                                                 ? GrTextureDomain::kDecal_Mode
    317                                                                 : mode;
    318 
    319             SkRect domain = SkRect::Make(*contentRect);
    320             domain.inset((i < scaleFactorX) ? SK_ScalarHalf : 0.0f,
    321                          (i < scaleFactorY) ? SK_ScalarHalf : 0.0f);
    322             auto fp = GrTextureDomainEffect::Make(std::move(src),
    323                                                   SkMatrix::I(),
    324                                                   domain,
    325                                                   modeForScaling,
    326                                                   GrSamplerState::Filter::kBilerp);
    327             paint.addColorFragmentProcessor(std::move(fp));
    328             srcRect.offset(-(*srcOffset));
    329             // TODO: consume the srcOffset in both first draws and always set it to zero
    330             // back in GaussianBlur
    331             srcOffset->set(0, 0);
    332         } else {
    333             paint.addColorTextureProcessor(std::move(src), SkMatrix::I(),
    334                                            GrSamplerState::ClampBilerp());
    335         }
    336         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    337 
    338         GrFixedClip clip(dstRect);
    339         dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo,
    340                                                SkMatrix::I(), SkRect::Make(dstRect),
    341                                                SkRect::Make(srcRect));
    342 
    343         src = dstRenderTargetContext->asTextureProxyRef();
    344         if (!src) {
    345             return nullptr;
    346         }
    347         srcRect = dstRect;
    348     }
    349 
    350     *contentRect = dstRect;
    351 
    352     SkASSERT(dstRenderTargetContext);
    353 
    354     if (willBeXFiltering) {
    355         if (scaleFactorX > 1) {
    356             // Clear out a radius to the right of the contentRect to prevent the
    357             // X convolution from reading garbage.
    358             SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fRight, contentRect->fTop,
    359                                                   radiusX, contentRect->height());
    360             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
    361         }
    362     } else {
    363         if (scaleFactorY > 1) {
    364             // Clear out a radius below the contentRect to prevent the Y
    365             // convolution from reading garbage.
    366             SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fLeft, contentRect->fBottom,
    367                                                   contentRect->width(), radiusY);
    368             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
    369         }
    370     }
    371 
    372     return dstRenderTargetContext->asTextureProxyRef();
    373 }
    374 
    375 // Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'.
    376 static sk_sp<GrRenderTargetContext> reexpand(GrContext* context,
    377                                              sk_sp<GrRenderTargetContext> srcRenderTargetContext,
    378                                              const SkIRect& localSrcBounds,
    379                                              int scaleFactorX, int scaleFactorY,
    380                                              GrTextureDomain::Mode mode,
    381                                              const SkImageInfo& dstII,
    382                                              SkBackingFit fit) {
    383     const SkIRect srcRect = SkIRect::MakeWH(srcRenderTargetContext->width(),
    384                                             srcRenderTargetContext->height());
    385 
    386     // Clear one pixel to the right and below, to accommodate bilinear upsampling.
    387     // TODO: it seems like we should actually be clamping here rather than darkening
    388     // the bottom right edges.
    389     SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1);
    390     srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
    391     clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height());
    392     srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
    393 
    394     sk_sp<GrTextureProxy> srcProxy = srcRenderTargetContext->asTextureProxyRef();
    395     if (!srcProxy) {
    396         return nullptr;
    397     }
    398 
    399     srcRenderTargetContext = nullptr; // no longer needed
    400 
    401     GrPixelConfig config = get_blur_config(srcProxy.get());
    402 
    403     GrBackendFormat format = srcProxy->backendFormat().makeTexture2D();
    404     if (!format.isValid()) {
    405         return nullptr;
    406     }
    407 
    408     sk_sp<GrRenderTargetContext> dstRenderTargetContext =
    409         context->contextPriv().makeDeferredRenderTargetContext(format,
    410                                                                fit, dstII.width(), dstII.height(),
    411                                                                config, dstII.refColorSpace(),
    412                                                                1, GrMipMapped::kNo,
    413                                                                srcProxy->origin());
    414     if (!dstRenderTargetContext) {
    415         return nullptr;
    416     }
    417 
    418     GrPaint paint;
    419     if (GrTextureDomain::kIgnore_Mode != mode) {
    420         // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
    421         GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
    422                                                             ? GrTextureDomain::kDecal_Mode
    423                                                             : mode;
    424 
    425         SkRect domain = SkRect::Make(localSrcBounds);
    426         auto fp = GrTextureDomainEffect::Make(std::move(srcProxy),
    427                                                 SkMatrix::I(),
    428                                                 domain,
    429                                                 modeForScaling,
    430                                                 GrSamplerState::Filter::kBilerp);
    431         paint.addColorFragmentProcessor(std::move(fp));
    432     } else {
    433         // FIXME:  this should be mitchell, not bilinear.
    434         paint.addColorTextureProcessor(std::move(srcProxy), SkMatrix::I(),
    435                                        GrSamplerState::ClampBilerp());
    436     }
    437     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    438     GrFixedClip clip(dstII.bounds());
    439 
    440     // TODO: using dstII as dstRect results in some image diffs - why?
    441     SkIRect dstRect(srcRect);
    442     scale_irect(&dstRect, scaleFactorX, scaleFactorY);
    443 
    444     dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
    445                                            SkRect::Make(dstRect), SkRect::Make(srcRect));
    446 
    447     return dstRenderTargetContext;
    448 }
    449 
    450 namespace SkGpuBlurUtils {
    451 
    452 sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context,
    453                                           sk_sp<GrTextureProxy> srcProxy,
    454                                           sk_sp<SkColorSpace> colorSpace,
    455                                           const SkIRect& dstBounds,
    456                                           const SkIRect& srcBounds,
    457                                           float sigmaX,
    458                                           float sigmaY,
    459                                           GrTextureDomain::Mode mode,
    460                                           SkAlphaType at,
    461                                           SkBackingFit fit) {
    462     SkASSERT(context);
    463 
    464     const GrPixelConfig config = get_blur_config(srcProxy.get());
    465     SkColorType ct;
    466     if (!GrPixelConfigToColorType(config, &ct)) {
    467         return nullptr;
    468     }
    469 
    470     const SkImageInfo finalDestII = SkImageInfo::Make(dstBounds.width(), dstBounds.height(),
    471                                                       ct, at, std::move(colorSpace));
    472 
    473     int scaleFactorX, radiusX;
    474     int scaleFactorY, radiusY;
    475     int maxTextureSize = context->contextPriv().caps()->maxTextureSize();
    476     sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
    477     sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
    478     SkASSERT(sigmaX || sigmaY);
    479 
    480     SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
    481 
    482     // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
    483     // launch a single non separable kernel vs two launches
    484     if (sigmaX > 0.0f && sigmaY > 0.0f &&
    485             (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
    486         // We shouldn't be scaling because this is a small size blur
    487         SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
    488 
    489         return convolve_gaussian_2d(context, std::move(srcProxy), srcBounds, srcOffset,
    490                                     radiusX, radiusY, sigmaX, sigmaY,
    491                                     mode, finalDestII, fit);
    492     }
    493 
    494     // Only the last rendered renderTargetContext needs to match the supplied 'fit'
    495     SkBackingFit xFit = fit, yFit = fit;
    496     if (scaleFactorX > 1 || scaleFactorY > 1) {
    497         xFit = yFit = SkBackingFit::kApprox;  // reexpand will be last
    498     } else if (sigmaY > 0.0f) {
    499         xFit = SkBackingFit::kApprox;         // the y-pass will be last
    500     }
    501 
    502     SkIRect localSrcBounds = srcBounds;
    503 
    504     if (scaleFactorX > 1 || scaleFactorY > 1) {
    505         srcProxy = decimate(context, std::move(srcProxy), &srcOffset, &localSrcBounds,
    506                             scaleFactorX, scaleFactorY, sigmaX > 0.0f, sigmaY > 0.0f,
    507                             radiusX, radiusY, mode, finalDestII);
    508         if (!srcProxy) {
    509             return nullptr;
    510         }
    511     }
    512 
    513     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
    514 
    515     SkIRect srcRect = finalDestII.bounds();
    516     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    517     if (sigmaX > 0.0f) {
    518         dstRenderTargetContext = convolve_gaussian(context, std::move(srcProxy), srcRect, srcOffset,
    519                                                    Direction::kX, radiusX, sigmaX, &localSrcBounds,
    520                                                    mode, finalDestII, xFit);
    521         if (!dstRenderTargetContext) {
    522             return nullptr;
    523         }
    524 
    525         if (sigmaY > 0.0f) {
    526             // Clear out a radius below the srcRect to prevent the Y
    527             // convolution from reading garbage.
    528             SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom,
    529                                                   srcRect.width(), radiusY);
    530             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
    531         }
    532 
    533         srcProxy = dstRenderTargetContext->asTextureProxyRef();
    534         if (!srcProxy) {
    535             return nullptr;
    536         }
    537 
    538         srcRect.offsetTo(0, 0);
    539         srcOffset.set(0, 0);
    540     }
    541 
    542     if (sigmaY > 0.0f) {
    543         dstRenderTargetContext = convolve_gaussian(context, std::move(srcProxy), srcRect, srcOffset,
    544                                                    Direction::kY, radiusY, sigmaY, &localSrcBounds,
    545                                                    mode, finalDestII, yFit);
    546         if (!dstRenderTargetContext) {
    547             return nullptr;
    548         }
    549 
    550         srcProxy = dstRenderTargetContext->asTextureProxyRef();
    551         if (!srcProxy) {
    552             return nullptr;
    553         }
    554 
    555         srcRect.offsetTo(0, 0);
    556         srcOffset.set(0, 0);
    557     }
    558 
    559     SkASSERT(dstRenderTargetContext);
    560     SkASSERT(srcProxy.get() == dstRenderTargetContext->asTextureProxy());
    561 
    562     if (scaleFactorX > 1 || scaleFactorY > 1) {
    563         dstRenderTargetContext = reexpand(context, std::move(dstRenderTargetContext),
    564                                           localSrcBounds, scaleFactorX, scaleFactorY,
    565                                           mode, finalDestII, fit);
    566     }
    567 
    568     SkASSERT(!dstRenderTargetContext || dstRenderTargetContext->origin() == srcProxy->origin());
    569     return dstRenderTargetContext;
    570 }
    571 
    572 }
    573 
    574 #endif
    575