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
scale_irect_roundout(SkIRect * rect,float xScale,float yScale)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
scale_irect(SkIRect * rect,int xScale,int yScale)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
is_even(int x)42 static inline int is_even(int x) { return !(x & 1); }
43 #endif
44
shrink_irect_by_2(SkIRect * rect,bool xAxis,bool yAxis)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
adjust_sigma(float sigma,int maxTextureSize,int * scaleFactor,int * radius)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
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])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
get_blur_config(GrTextureProxy * proxy)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
convolve_gaussian_2d(GrContext * context,sk_sp<GrTextureProxy> proxy,const SkIRect & srcBounds,const SkIPoint & srcOffset,int radiusX,int radiusY,SkScalar sigmaX,SkScalar sigmaY,GrTextureDomain::Mode mode,const SkImageInfo & dstII,SkBackingFit dstFit)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
convolve_gaussian(GrContext * context,sk_sp<GrTextureProxy> proxy,const SkIRect & srcRect,const SkIPoint & srcOffset,Direction direction,int radius,float sigma,SkIRect * contentRect,GrTextureDomain::Mode mode,const SkImageInfo & dstII,SkBackingFit fit)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
decimate(GrContext * context,sk_sp<GrTextureProxy> src,SkIPoint * srcOffset,SkIRect * contentRect,int scaleFactorX,int scaleFactorY,bool willBeXFiltering,bool willBeYFiltering,int radiusX,int radiusY,GrTextureDomain::Mode mode,const SkImageInfo & dstII)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'.
reexpand(GrContext * context,sk_sp<GrRenderTargetContext> srcRenderTargetContext,const SkIRect & localSrcBounds,int scaleFactorX,int scaleFactorY,GrTextureDomain::Mode mode,const SkImageInfo & dstII,SkBackingFit fit)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
GaussianBlur(GrContext * context,sk_sp<GrTextureProxy> srcProxy,sk_sp<SkColorSpace> colorSpace,const SkIRect & dstBounds,const SkIRect & srcBounds,float sigmaX,float sigmaY,GrTextureDomain::Mode mode,SkAlphaType at,SkBackingFit fit)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