• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/core/SkGpuBlurUtils.h"
9 
10 #include "include/core/SkRect.h"
11 
12 #if SK_SUPPORT_GPU
13 #include "include/private/GrRecordingContext.h"
14 #include "src/gpu/GrCaps.h"
15 #include "src/gpu/GrFixedClip.h"
16 #include "src/gpu/GrRecordingContextPriv.h"
17 #include "src/gpu/GrRenderTargetContext.h"
18 #include "src/gpu/GrRenderTargetContextPriv.h"
19 #include "src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h"
20 #include "src/gpu/effects/GrMatrixConvolutionEffect.h"
21 
22 #include "src/gpu/SkGr.h"
23 
24 #define MAX_BLUR_SIGMA 4.0f
25 
26 using Direction = GrGaussianConvolutionFragmentProcessor::Direction;
27 
scale_irect_roundout(SkIRect * rect,float xScale,float yScale)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 
scale_irect(SkIRect * rect,int xScale,int yScale)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
is_even(int x)43 static inline int is_even(int x) { return !(x & 1); }
44 #endif
45 
shrink_irect_by_2(SkIRect * rect,bool xAxis,bool yAxis)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 
adjust_sigma(float sigma,int maxTextureSize,int * scaleFactor,int * radius)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 
convolve_gaussian_1d(GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkIRect & dstRect,const SkIPoint & srcOffset,sk_sp<GrTextureProxy> proxy,Direction direction,int radius,float sigma,GrTextureDomain::Mode mode,int bounds[2])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 
get_blur_color_type(GrTextureProxy * proxy)95 static GrColorType get_blur_color_type(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              kRGBA_half_GrPixelConfig == config || kAlpha_8_GrPixelConfig == config ||
102              kAlpha_8_as_Alpha_GrPixelConfig == config || kAlpha_8_as_Red_GrPixelConfig == config ||
103              kRGBA_1010102_GrPixelConfig == config || kRGBA_half_Clamped_GrPixelConfig == config);
104 
105     return GrPixelConfigToColorType(config);
106 }
107 
convolve_gaussian_2d(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,const SkIRect & srcBounds,const SkIPoint & srcOffset,int radiusX,int radiusY,SkScalar sigmaX,SkScalar sigmaY,GrTextureDomain::Mode mode,int finalW,int finalH,sk_sp<SkColorSpace> finalCS,SkBackingFit dstFit)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                                                          int finalW,
118                                                          int finalH,
119                                                          sk_sp<SkColorSpace> finalCS,
120                                                          SkBackingFit dstFit) {
121     // TODO: Once GrPixelConfig is gone, we need will need the source's color type.
122     GrColorType colorType = get_blur_color_type(proxy.get());
123 
124     sk_sp<GrRenderTargetContext> renderTargetContext;
125     renderTargetContext = context->priv().makeDeferredRenderTargetContext(
126             dstFit,
127             finalW,
128             finalH,
129             colorType,
130             std::move(finalCS),
131             1,
132             GrMipMapped::kNo,
133             proxy->origin(),
134             nullptr,
135             SkBudgeted::kYes,
136             proxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
137     if (!renderTargetContext) {
138         return nullptr;
139     }
140 
141     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
142                                                -SkIntToScalar(srcOffset.y()));
143     SkISize size = SkISize::Make(2 * radiusX + 1,  2 * radiusY + 1);
144     SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
145     GrPaint paint;
146     auto conv = GrMatrixConvolutionEffect::MakeGaussian(std::move(proxy), srcBounds, size, 1.0, 0.0,
147                                                         kernelOffset, mode, true, sigmaX, sigmaY);
148     paint.addColorFragmentProcessor(std::move(conv));
149     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
150     GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
151 
152     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
153                                                  SkRect::MakeWH(finalW, finalH), localMatrix);
154 
155     return renderTargetContext;
156 }
157 
158 // NOTE: Both convolve_gaussian or decimate accept a proxyOffset. This is separate from the
159 // srcBounds and srcOffset, which are relative to the content rect of the image, whereas proxyOffset
160 // maps from the content rect to the proxy's coordinate space. Due to how the destination bounds are
161 // calculated, it is more convenient to have the proxy offset kept separate from the logical bounds
162 // (which do impact destination decisions). Both functions incorporate the proxy offset into the
163 // geometry they submit or before calling convolve_gaussian_1d.
164 
convolve_gaussian(GrRecordingContext * context,sk_sp<GrTextureProxy> proxy,const SkIPoint & proxyOffset,const SkIRect & srcRect,const SkIPoint & srcOffset,Direction direction,int radius,float sigma,SkIRect * contentRect,GrTextureDomain::Mode mode,int finalW,int finalH,sk_sp<SkColorSpace> finalCS,SkBackingFit fit)165 static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* context,
166                                                       sk_sp<GrTextureProxy> proxy,
167                                                       const SkIPoint& proxyOffset,
168                                                       const SkIRect& srcRect,
169                                                       const SkIPoint& srcOffset,
170                                                       Direction direction,
171                                                       int radius,
172                                                       float sigma,
173                                                       SkIRect* contentRect,
174                                                       GrTextureDomain::Mode mode,
175                                                       int finalW,
176                                                       int finalH,
177                                                       sk_sp<SkColorSpace> finalCS,
178                                                       SkBackingFit fit) {
179     SkASSERT(srcRect.width() <= finalW && srcRect.height() <= finalH);
180 
181     // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
182     GrColorType colorType = get_blur_color_type(proxy.get());
183 
184     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
185     dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
186             fit,
187             srcRect.width(),
188             srcRect.height(),
189             colorType,
190             std::move(finalCS),
191             1,
192             GrMipMapped::kNo,
193             proxy->origin(),
194             nullptr,
195             SkBudgeted::kYes,
196             proxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
197     if (!dstRenderTargetContext) {
198         return nullptr;
199     }
200 
201     GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
202 
203     int bounds[2] = { 0, 0 };
204     SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
205     SkIPoint netOffset = srcOffset - proxyOffset;
206     if (GrTextureDomain::kIgnore_Mode == mode) {
207         *contentRect = dstRect;
208         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
209                              std::move(proxy), direction, radius, sigma,
210                              GrTextureDomain::kIgnore_Mode, bounds);
211         return dstRenderTargetContext;
212     }
213     // These destination rects need to be adjusted by srcOffset, but should *not* be adjusted by
214     // the proxyOffset, which is why keeping them separate is convenient.
215     SkIRect midRect = *contentRect, leftRect, rightRect;
216     midRect.offset(srcOffset);
217     SkIRect topRect, bottomRect;
218     if (Direction::kX == direction) {
219         bounds[0] = contentRect->left() + proxyOffset.x();
220         bounds[1] = contentRect->right() + proxyOffset.x();
221         topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
222         bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
223         midRect.inset(radius, 0);
224         leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
225         rightRect =
226             SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
227         dstRect.fTop = midRect.top();
228         dstRect.fBottom = midRect.bottom();
229 
230         contentRect->fLeft = dstRect.fLeft;
231         contentRect->fTop = midRect.fTop;
232         contentRect->fRight = dstRect.fRight;
233         contentRect->fBottom = midRect.fBottom;
234     } else {
235         bounds[0] = contentRect->top() + proxyOffset.y();
236         bounds[1] = contentRect->bottom() + proxyOffset.y();
237         topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
238         bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
239         midRect.inset(0, radius);
240         leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
241         rightRect =
242             SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
243         dstRect.fLeft = midRect.left();
244         dstRect.fRight = midRect.right();
245 
246         contentRect->fLeft = midRect.fLeft;
247         contentRect->fTop = dstRect.fTop;
248         contentRect->fRight = midRect.fRight;
249         contentRect->fBottom = dstRect.fBottom;
250     }
251     if (!topRect.isEmpty()) {
252         dstRenderTargetContext->clear(&topRect, SK_PMColor4fTRANSPARENT,
253                                       GrRenderTargetContext::CanClearFullscreen::kNo);
254     }
255 
256     if (!bottomRect.isEmpty()) {
257         dstRenderTargetContext->clear(&bottomRect, SK_PMColor4fTRANSPARENT,
258                                       GrRenderTargetContext::CanClearFullscreen::kNo);
259     }
260 
261     if (midRect.isEmpty()) {
262         // Blur radius covers srcBounds; use bounds over entire draw
263         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
264                              std::move(proxy), direction, radius, sigma, mode, bounds);
265     } else {
266         // Draw right and left margins with bounds; middle without.
267         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset,
268                              proxy, direction, radius, sigma, mode, bounds);
269         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset,
270                              proxy, direction, radius, sigma, mode, bounds);
271         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
272                              std::move(proxy), direction, radius, sigma,
273                              GrTextureDomain::kIgnore_Mode, bounds);
274     }
275 
276     return dstRenderTargetContext;
277 }
278 
279 // Note: despite its name this function does not kill every tenth legionnaire.
decimate(GrRecordingContext * context,sk_sp<GrTextureProxy> src,const SkIPoint & proxyOffset,SkIPoint * srcOffset,SkIRect * contentRect,int scaleFactorX,int scaleFactorY,bool willBeXFiltering,bool willBeYFiltering,int radiusX,int radiusY,GrTextureDomain::Mode mode,int finalW,int finalH,sk_sp<SkColorSpace> finalCS)280 static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
281                                       sk_sp<GrTextureProxy> src,
282                                       const SkIPoint& proxyOffset,
283                                       SkIPoint* srcOffset,
284                                       SkIRect* contentRect,
285                                       int scaleFactorX, int scaleFactorY,
286                                       bool willBeXFiltering, bool willBeYFiltering,
287                                       int radiusX, int radiusY,
288                                       GrTextureDomain::Mode mode,
289                                       int finalW,
290                                       int finalH,
291                                       sk_sp<SkColorSpace> finalCS) {
292     SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
293     SkASSERT(scaleFactorX > 1 || scaleFactorY > 1);
294 
295     // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
296     GrColorType colorType = get_blur_color_type(src.get());
297 
298     SkIRect srcRect;
299     if (GrTextureDomain::kIgnore_Mode == mode) {
300         srcRect = SkIRect::MakeWH(finalW, finalH);
301     } else {
302         srcRect = *contentRect;
303         srcRect.offset(*srcOffset);
304     }
305 
306     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
307     scale_irect(&srcRect, scaleFactorX, scaleFactorY);
308 
309     SkIRect dstRect(srcRect);
310 
311     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
312 
313     for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
314         shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
315 
316         // We know this will not be the final draw so we are free to make it an approx match.
317         dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
318                 SkBackingFit::kApprox,
319                 dstRect.fRight,
320                 dstRect.fBottom,
321                 colorType,
322                 finalCS,
323                 1,
324                 GrMipMapped::kNo,
325                 src->origin(),
326                 nullptr,
327                 SkBudgeted::kYes,
328                 src->isProtected() ? GrProtected::kYes : GrProtected::kNo);
329         if (!dstRenderTargetContext) {
330             return nullptr;
331         }
332 
333         GrPaint paint;
334         if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
335             // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
336             GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
337                                                                 ? GrTextureDomain::kDecal_Mode
338                                                                 : mode;
339 
340             SkRect domain = SkRect::Make(*contentRect);
341             domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
342                          (i < scaleFactorY) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f);
343             // Ensure that the insetting doesn't invert the domain rectangle.
344             if (domain.fRight < domain.fLeft) {
345                 domain.fLeft = domain.fRight = SkScalarAve(domain.fLeft, domain.fRight);
346             }
347             if (domain.fBottom < domain.fTop) {
348                 domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
349             }
350             domain.offset(proxyOffset.x(), proxyOffset.y());
351             auto fp = GrTextureDomainEffect::Make(std::move(src),
352                                                   SkMatrix::I(),
353                                                   domain,
354                                                   modeForScaling,
355                                                   GrSamplerState::Filter::kBilerp);
356             paint.addColorFragmentProcessor(std::move(fp));
357             srcRect.offset(-(*srcOffset));
358             // TODO: consume the srcOffset in both first draws and always set it to zero
359             // back in GaussianBlur
360             srcOffset->set(0, 0);
361         } else {
362             paint.addColorTextureProcessor(std::move(src), SkMatrix::I(),
363                                            GrSamplerState::ClampBilerp());
364         }
365         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
366 
367         dstRenderTargetContext->fillRectToRect(GrFixedClip::Disabled(), std::move(paint), GrAA::kNo,
368                                                SkMatrix::I(), SkRect::Make(dstRect),
369                                                SkRect::Make(srcRect.makeOffset(proxyOffset.x(),
370                                                                                proxyOffset.y())));
371 
372         src = dstRenderTargetContext->asTextureProxyRef();
373         if (!src) {
374             return nullptr;
375         }
376         srcRect = dstRect;
377     }
378 
379     *contentRect = dstRect;
380 
381     SkASSERT(dstRenderTargetContext);
382 
383     if (willBeXFiltering) {
384         if (scaleFactorX > 1) {
385             // Clear out a radius to the right of the contentRect to prevent the
386             // X convolution from reading garbage.
387             SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fRight, contentRect->fTop,
388                                                   radiusX, contentRect->height());
389             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
390         }
391     } else {
392         if (scaleFactorY > 1) {
393             // Clear out a radius below the contentRect to prevent the Y
394             // convolution from reading garbage.
395             SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fLeft, contentRect->fBottom,
396                                                   contentRect->width(), radiusY);
397             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
398         }
399     }
400 
401     return dstRenderTargetContext->asTextureProxyRef();
402 }
403 
404 // Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'. At this point, we are
405 // expanding an intermediate image, so there's no need to account for a proxy offset from the
406 // original input.
reexpand(GrRecordingContext * context,sk_sp<GrRenderTargetContext> srcRenderTargetContext,const SkIRect & localSrcBounds,int scaleFactorX,int scaleFactorY,GrTextureDomain::Mode mode,int finalW,int finalH,sk_sp<SkColorSpace> finalCS,SkBackingFit fit)407 static sk_sp<GrRenderTargetContext> reexpand(GrRecordingContext* context,
408                                              sk_sp<GrRenderTargetContext> srcRenderTargetContext,
409                                              const SkIRect& localSrcBounds,
410                                              int scaleFactorX, int scaleFactorY,
411                                              GrTextureDomain::Mode mode,
412                                              int finalW,
413                                              int finalH,
414                                              sk_sp<SkColorSpace> finalCS,
415                                              SkBackingFit fit) {
416     const SkIRect srcRect = SkIRect::MakeWH(srcRenderTargetContext->width(),
417                                             srcRenderTargetContext->height());
418 
419     // Clear one pixel to the right and below, to accommodate bilinear upsampling.
420     // TODO: it seems like we should actually be clamping here rather than darkening
421     // the bottom right edges.
422     SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1);
423     srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
424     clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height());
425     srcRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
426 
427     sk_sp<GrTextureProxy> srcProxy = srcRenderTargetContext->asTextureProxyRef();
428     if (!srcProxy) {
429         return nullptr;
430     }
431 
432     srcRenderTargetContext = nullptr; // no longer needed
433 
434     // TODO: Once GrPixelConfig is gone we'll need the source's color type here.
435     GrColorType colorType = get_blur_color_type(srcProxy.get());
436 
437     sk_sp<GrRenderTargetContext> dstRenderTargetContext =
438             context->priv().makeDeferredRenderTargetContext(fit, finalW, finalH, colorType,
439                                                             std::move(finalCS), 1, GrMipMapped::kNo,
440                                                             srcProxy->origin());
441     if (!dstRenderTargetContext) {
442         return nullptr;
443     }
444 
445     GrPaint paint;
446     if (GrTextureDomain::kIgnore_Mode != mode) {
447         // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
448         GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
449                                                             ? GrTextureDomain::kDecal_Mode
450                                                             : mode;
451 
452         SkRect domain = SkRect::Make(localSrcBounds);
453         auto fp = GrTextureDomainEffect::Make(std::move(srcProxy),
454                                                 SkMatrix::I(),
455                                                 domain,
456                                                 modeForScaling,
457                                                 GrSamplerState::Filter::kBilerp);
458         paint.addColorFragmentProcessor(std::move(fp));
459     } else {
460         // FIXME:  this should be mitchell, not bilinear.
461         paint.addColorTextureProcessor(std::move(srcProxy), SkMatrix::I(),
462                                        GrSamplerState::ClampBilerp());
463     }
464     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
465     GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
466 
467     // TODO: using dstII as dstRect results in some image diffs - why?
468     SkIRect dstRect(srcRect);
469     scale_irect(&dstRect, scaleFactorX, scaleFactorY);
470 
471     dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
472                                            SkRect::Make(dstRect), SkRect::Make(srcRect));
473 
474     return dstRenderTargetContext;
475 }
476 
477 namespace SkGpuBlurUtils {
478 
GaussianBlur(GrRecordingContext * context,sk_sp<GrTextureProxy> srcProxy,const SkIPoint & proxyOffset,sk_sp<SkColorSpace> colorSpace,const SkIRect & dstBounds,const SkIRect & srcBounds,float sigmaX,float sigmaY,GrTextureDomain::Mode mode,SkAlphaType at,SkBackingFit fit)479 sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
480                                           sk_sp<GrTextureProxy> srcProxy,
481                                           const SkIPoint& proxyOffset,
482                                           sk_sp<SkColorSpace> colorSpace,
483                                           const SkIRect& dstBounds,
484                                           const SkIRect& srcBounds,
485                                           float sigmaX,
486                                           float sigmaY,
487                                           GrTextureDomain::Mode mode,
488                                           SkAlphaType at,
489                                           SkBackingFit fit) {
490     SkASSERT(context);
491 
492     TRACE_EVENT2("skia.gpu", "GaussianBlur", "sigmaX", sigmaX, "sigmaY", sigmaY);
493 
494     int finalW = dstBounds.width();
495     int finalH = dstBounds.height();
496 
497     int scaleFactorX, radiusX;
498     int scaleFactorY, radiusY;
499     int maxTextureSize = context->priv().caps()->maxTextureSize();
500     sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
501     sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
502     SkASSERT(sigmaX || sigmaY);
503 
504     SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
505     SkIRect localSrcBounds = srcBounds;
506     SkIPoint localProxyOffset = proxyOffset;
507 
508     // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
509     // launch a single non separable kernel vs two launches
510     if (sigmaX > 0.0f && sigmaY > 0.0f &&
511             (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
512         // We shouldn't be scaling because this is a small size blur
513         SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
514         // Apply the proxy offset to src bounds and offset directly
515         srcOffset -= proxyOffset;
516         localSrcBounds.offset(proxyOffset);
517         return convolve_gaussian_2d(context, std::move(srcProxy), localSrcBounds, srcOffset,
518                                     radiusX, radiusY, sigmaX, sigmaY, mode, finalW, finalH,
519                                     colorSpace, fit);
520     }
521 
522     // Only the last rendered renderTargetContext needs to match the supplied 'fit'
523     SkBackingFit xFit = fit, yFit = fit;
524     if (scaleFactorX > 1 || scaleFactorY > 1) {
525         xFit = yFit = SkBackingFit::kApprox;  // reexpand will be last
526     } else if (sigmaY > 0.0f) {
527         xFit = SkBackingFit::kApprox;         // the y-pass will be last
528     }
529 
530     if (scaleFactorX > 1 || scaleFactorY > 1) {
531         srcProxy = decimate(context, std::move(srcProxy), localProxyOffset, &srcOffset,
532                             &localSrcBounds, scaleFactorX, scaleFactorY, sigmaX > 0.0f,
533                             sigmaY > 0.0f, radiusX, radiusY, mode, finalW, finalH, colorSpace);
534         localProxyOffset.set(0, 0);
535         if (!srcProxy) {
536             return nullptr;
537         }
538     }
539 
540     sk_sp<GrRenderTargetContext> dstRenderTargetContext;
541 
542     auto srcRect = SkIRect::MakeWH(finalW, finalH);
543     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
544     if (sigmaX > 0.0f) {
545         dstRenderTargetContext = convolve_gaussian(
546                 context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kX,
547                 radiusX, sigmaX, &localSrcBounds, mode, finalW, finalH, colorSpace, xFit);
548         if (!dstRenderTargetContext) {
549             return nullptr;
550         }
551 
552         if (sigmaY > 0.0f) {
553             // Clear out a radius below the srcRect to prevent the Y
554             // convolution from reading garbage.
555             SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom,
556                                                   srcRect.width(), radiusY);
557             dstRenderTargetContext->priv().absClear(&clearRect, SK_PMColor4fTRANSPARENT);
558         }
559 
560         srcProxy = dstRenderTargetContext->asTextureProxyRef();
561         if (!srcProxy) {
562             return nullptr;
563         }
564 
565         srcRect.offsetTo(0, 0);
566         srcOffset.set(0, 0);
567         localProxyOffset.set(0, 0);
568     }
569 
570     if (sigmaY > 0.0f) {
571         dstRenderTargetContext = convolve_gaussian(
572                 context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kY,
573                 radiusY, sigmaY, &localSrcBounds, mode, finalW, finalH, colorSpace, yFit);
574         if (!dstRenderTargetContext) {
575             return nullptr;
576         }
577 
578         srcProxy = dstRenderTargetContext->asTextureProxyRef();
579         if (!srcProxy) {
580             return nullptr;
581         }
582 
583         srcRect.offsetTo(0, 0);
584         srcOffset.set(0, 0);
585         localProxyOffset.set(0, 0);
586     }
587 
588     SkASSERT(dstRenderTargetContext);
589     SkASSERT(srcProxy.get() == dstRenderTargetContext->asTextureProxy());
590     SkASSERT(localProxyOffset.x() == 0 && localProxyOffset.y() == 0);
591 
592     if (scaleFactorX > 1 || scaleFactorY > 1) {
593         dstRenderTargetContext =
594                 reexpand(context, std::move(dstRenderTargetContext), localSrcBounds, scaleFactorX,
595                          scaleFactorY, mode, finalW, finalH, colorSpace, fit);
596     }
597 
598     SkASSERT(!dstRenderTargetContext || dstRenderTargetContext->origin() == srcProxy->origin());
599     return dstRenderTargetContext;
600 }
601 
602 }
603 
604 #endif
605