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