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