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,GrColorType srcColorType,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 GrColorType srcColorType,
80 Direction direction,
81 int radius,
82 float sigma,
83 GrTextureDomain::Mode mode,
84 int bounds[2]) {
85 GrPaint paint;
86 std::unique_ptr<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
87 std::move(proxy), srcColorType, direction, radius, sigma, mode, bounds));
88 paint.addColorFragmentProcessor(std::move(conv));
89 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
90 SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
91 -SkIntToScalar(srcOffset.y()));
92 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
93 SkRect::Make(dstRect), localMatrix);
94 }
95
convolve_gaussian_2d(GrRecordingContext * context,sk_sp<GrTextureProxy> srcProxy,GrColorType srcColorType,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)96 static std::unique_ptr<GrRenderTargetContext> convolve_gaussian_2d(GrRecordingContext* context,
97 sk_sp<GrTextureProxy> srcProxy,
98 GrColorType srcColorType,
99 const SkIRect& srcBounds,
100 const SkIPoint& srcOffset,
101 int radiusX,
102 int radiusY,
103 SkScalar sigmaX,
104 SkScalar sigmaY,
105 GrTextureDomain::Mode mode,
106 int finalW,
107 int finalH,
108 sk_sp<SkColorSpace> finalCS,
109 SkBackingFit dstFit) {
110
111 auto renderTargetContext = context->priv().makeDeferredRenderTargetContext(
112 dstFit,
113 finalW,
114 finalH,
115 srcColorType,
116 std::move(finalCS),
117 1,
118 GrMipMapped::kNo,
119 srcProxy->origin(),
120 nullptr,
121 SkBudgeted::kYes,
122 srcProxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
123 if (!renderTargetContext) {
124 return nullptr;
125 }
126
127 SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
128 -SkIntToScalar(srcOffset.y()));
129 SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1);
130 SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
131 GrPaint paint;
132 auto conv = GrMatrixConvolutionEffect::MakeGaussian(std::move(srcProxy), srcBounds, size,
133 1.0, 0.0, kernelOffset, mode, true,
134 sigmaX, sigmaY);
135 paint.addColorFragmentProcessor(std::move(conv));
136 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
137 GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
138
139 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
140 SkRect::MakeWH(finalW, finalH), localMatrix);
141
142 return renderTargetContext;
143 }
144
145 // NOTE: Both convolve_gaussian or decimate accept a proxyOffset. This is separate from the
146 // srcBounds and srcOffset, which are relative to the content rect of the image, whereas proxyOffset
147 // maps from the content rect to the proxy's coordinate space. Due to how the destination bounds are
148 // calculated, it is more convenient to have the proxy offset kept separate from the logical bounds
149 // (which do impact destination decisions). Both functions incorporate the proxy offset into the
150 // geometry they submit or before calling convolve_gaussian_1d.
151
convolve_gaussian(GrRecordingContext * context,sk_sp<GrTextureProxy> srcProxy,GrColorType srcColorType,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)152 static std::unique_ptr<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* context,
153 sk_sp<GrTextureProxy> srcProxy,
154 GrColorType srcColorType,
155 const SkIPoint& proxyOffset,
156 const SkIRect& srcRect,
157 const SkIPoint& srcOffset,
158 Direction direction,
159 int radius,
160 float sigma,
161 SkIRect* contentRect,
162 GrTextureDomain::Mode mode,
163 int finalW,
164 int finalH,
165 sk_sp<SkColorSpace> finalCS,
166 SkBackingFit fit) {
167 SkASSERT(srcRect.width() <= finalW && srcRect.height() <= finalH);
168
169 auto dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
170 fit,
171 srcRect.width(),
172 srcRect.height(),
173 srcColorType,
174 std::move(finalCS),
175 1,
176 GrMipMapped::kNo,
177 srcProxy->origin(),
178 nullptr,
179 SkBudgeted::kYes,
180 srcProxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
181 if (!dstRenderTargetContext) {
182 return nullptr;
183 }
184
185 GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
186
187 int bounds[2] = { 0, 0 };
188 SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
189 SkIPoint netOffset = srcOffset - proxyOffset;
190 if (GrTextureDomain::kIgnore_Mode == mode) {
191 *contentRect = dstRect;
192 convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
193 std::move(srcProxy), srcColorType, direction, radius, sigma,
194 GrTextureDomain::kIgnore_Mode, bounds);
195 return dstRenderTargetContext;
196 }
197 // These destination rects need to be adjusted by srcOffset, but should *not* be adjusted by
198 // the proxyOffset, which is why keeping them separate is convenient.
199 SkIRect midRect = *contentRect, leftRect, rightRect;
200 midRect.offset(srcOffset);
201 SkIRect topRect, bottomRect;
202 if (Direction::kX == direction) {
203 bounds[0] = contentRect->left() + proxyOffset.x();
204 bounds[1] = contentRect->right() + proxyOffset.x();
205 topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
206 bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
207 midRect.inset(radius, 0);
208 leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
209 rightRect =
210 SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
211 dstRect.fTop = midRect.top();
212 dstRect.fBottom = midRect.bottom();
213
214 contentRect->fLeft = dstRect.fLeft;
215 contentRect->fTop = midRect.fTop;
216 contentRect->fRight = dstRect.fRight;
217 contentRect->fBottom = midRect.fBottom;
218 } else {
219 bounds[0] = contentRect->top() + proxyOffset.y();
220 bounds[1] = contentRect->bottom() + proxyOffset.y();
221 topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
222 bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
223 midRect.inset(0, radius);
224 leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
225 rightRect =
226 SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
227 dstRect.fLeft = midRect.left();
228 dstRect.fRight = midRect.right();
229
230 contentRect->fLeft = midRect.fLeft;
231 contentRect->fTop = dstRect.fTop;
232 contentRect->fRight = midRect.fRight;
233 contentRect->fBottom = dstRect.fBottom;
234 }
235 if (!topRect.isEmpty()) {
236 dstRenderTargetContext->clear(&topRect, SK_PMColor4fTRANSPARENT,
237 GrRenderTargetContext::CanClearFullscreen::kYes);
238 }
239
240 if (!bottomRect.isEmpty()) {
241 dstRenderTargetContext->clear(&bottomRect, SK_PMColor4fTRANSPARENT,
242 GrRenderTargetContext::CanClearFullscreen::kYes);
243 }
244
245 if (midRect.isEmpty()) {
246 // Blur radius covers srcBounds; use bounds over entire draw
247 convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
248 std::move(srcProxy), srcColorType, direction, radius, sigma, mode,
249 bounds);
250 } else {
251 // Draw right and left margins with bounds; middle without.
252 convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset,
253 srcProxy, srcColorType, direction, radius, sigma, mode, bounds);
254 convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset,
255 srcProxy, srcColorType, direction, radius, sigma, mode, bounds);
256 convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
257 std::move(srcProxy), srcColorType, direction, radius, sigma,
258 GrTextureDomain::kIgnore_Mode, bounds);
259 }
260
261 return dstRenderTargetContext;
262 }
263
264 // Returns a high quality scaled-down version of src. This is used to create an intermediate,
265 // shrunken version of the source image in the event that the requested blur sigma exceeds
266 // MAX_BLUR_SIGMA.
decimate(GrRecordingContext * context,sk_sp<GrTextureProxy> srcProxy,GrColorType srcColorType,const SkIPoint & proxyOffset,SkIPoint * srcOffset,SkIRect * contentRect,int scaleFactorX,int scaleFactorY,int radiusX,int radiusY,GrTextureDomain::Mode mode,int finalW,int finalH,sk_sp<SkColorSpace> finalCS)267 static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
268 sk_sp<GrTextureProxy> srcProxy,
269 GrColorType srcColorType,
270 const SkIPoint& proxyOffset,
271 SkIPoint* srcOffset,
272 SkIRect* contentRect,
273 int scaleFactorX, int scaleFactorY,
274 int radiusX, int radiusY,
275 GrTextureDomain::Mode mode,
276 int finalW,
277 int finalH,
278 sk_sp<SkColorSpace> finalCS) {
279 SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
280 SkASSERT(scaleFactorX > 1 || scaleFactorY > 1);
281
282 SkIRect srcRect;
283 if (GrTextureDomain::kIgnore_Mode == mode) {
284 srcRect = SkIRect::MakeWH(finalW, finalH);
285 } else {
286 srcRect = *contentRect;
287 srcRect.offset(*srcOffset);
288 }
289
290 scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
291 scale_irect(&srcRect, scaleFactorX, scaleFactorY);
292
293 SkIRect dstRect(srcRect);
294
295 // Map the src rect into proxy space, this only has to happen once since subsequent loops
296 // to decimate will have created a new proxy that has its origin at (0, 0).
297 srcRect.offset(proxyOffset.x(), proxyOffset.y());
298 std::unique_ptr<GrRenderTargetContext> dstRenderTargetContext;
299
300 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
301 shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY);
302
303 // We know this will not be the final draw so we are free to make it an approx match.
304 dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
305 SkBackingFit::kApprox,
306 dstRect.fRight,
307 dstRect.fBottom,
308 srcColorType,
309 finalCS,
310 1,
311 GrMipMapped::kNo,
312 srcProxy->origin(),
313 nullptr,
314 SkBudgeted::kYes,
315 srcProxy->isProtected() ? GrProtected::kYes : GrProtected::kNo);
316 if (!dstRenderTargetContext) {
317 return nullptr;
318 }
319
320 GrPaint paint;
321 if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
322 // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
323 GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
324 ? GrTextureDomain::kDecal_Mode
325 : mode;
326
327 SkRect domain = SkRect::Make(*contentRect);
328 domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
329 (i < scaleFactorY) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f);
330 // Ensure that the insetting doesn't invert the domain rectangle.
331 if (domain.fRight < domain.fLeft) {
332 domain.fLeft = domain.fRight = SkScalarAve(domain.fLeft, domain.fRight);
333 }
334 if (domain.fBottom < domain.fTop) {
335 domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
336 }
337 domain.offset(proxyOffset.x(), proxyOffset.y());
338 auto fp = GrTextureDomainEffect::Make(std::move(srcProxy),
339 srcColorType,
340 SkMatrix::I(),
341 domain,
342 modeForScaling,
343 GrSamplerState::Filter::kBilerp);
344 paint.addColorFragmentProcessor(std::move(fp));
345 srcRect.offset(-(*srcOffset));
346 // TODO: consume the srcOffset in both first draws and always set it to zero
347 // back in GaussianBlur
348 srcOffset->set(0, 0);
349 } else {
350 paint.addColorTextureProcessor(std::move(srcProxy), srcColorType, SkMatrix::I(),
351 GrSamplerState::ClampBilerp());
352 }
353 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
354
355 dstRenderTargetContext->fillRectToRect(GrFixedClip::Disabled(), std::move(paint), GrAA::kNo,
356 SkMatrix::I(), SkRect::Make(dstRect),
357 SkRect::Make(srcRect));
358
359 srcProxy = dstRenderTargetContext->asTextureProxyRef();
360 if (!srcProxy) {
361 return nullptr;
362 }
363 srcRect = dstRect;
364 }
365
366 *contentRect = dstRect;
367
368 SkASSERT(dstRenderTargetContext);
369
370 return dstRenderTargetContext->asTextureProxyRef();
371 }
372
373 // Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'. At this point, we are
374 // expanding an intermediate image, so there's no need to account for a proxy offset from the
375 // original input.
reexpand(GrRecordingContext * context,std::unique_ptr<GrRenderTargetContext> srcRenderTargetContext,const SkIRect & localSrcBounds,int scaleFactorX,int scaleFactorY,int finalW,int finalH,sk_sp<SkColorSpace> finalCS,SkBackingFit fit)376 static std::unique_ptr<GrRenderTargetContext> reexpand(
377 GrRecordingContext* context,
378 std::unique_ptr<GrRenderTargetContext> srcRenderTargetContext,
379 const SkIRect& localSrcBounds,
380 int scaleFactorX, int scaleFactorY,
381 int finalW,
382 int finalH,
383 sk_sp<SkColorSpace> finalCS,
384 SkBackingFit fit) {
385 const SkIRect srcRect = SkIRect::MakeWH(srcRenderTargetContext->width(),
386 srcRenderTargetContext->height());
387
388 sk_sp<GrTextureProxy> srcProxy = srcRenderTargetContext->asTextureProxyRef();
389 if (!srcProxy) {
390 return nullptr;
391 }
392
393 GrColorType srcColorType = srcRenderTargetContext->colorInfo().colorType();
394
395 srcRenderTargetContext = nullptr; // no longer needed
396
397 auto dstRenderTargetContext = context->priv().makeDeferredRenderTargetContext(
398 fit, finalW, finalH, srcColorType, std::move(finalCS), 1, GrMipMapped::kNo,
399 srcProxy->origin());
400 if (!dstRenderTargetContext) {
401 return nullptr;
402 }
403
404 GrPaint paint;
405 SkRect domain = GrTextureDomain::MakeTexelDomain(localSrcBounds, GrTextureDomain::kClamp_Mode,
406 GrTextureDomain::kClamp_Mode);
407 auto fp = GrTextureDomainEffect::Make(std::move(srcProxy), srcColorType, SkMatrix::I(), domain,
408 GrTextureDomain::kClamp_Mode,
409 GrSamplerState::Filter::kBilerp);
410 paint.addColorFragmentProcessor(std::move(fp));
411 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
412 GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
413
414 // TODO: using dstII as dstRect results in some image diffs - why?
415 SkIRect dstRect(srcRect);
416 scale_irect(&dstRect, scaleFactorX, scaleFactorY);
417
418 dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
419 SkRect::Make(dstRect), SkRect::Make(srcRect));
420
421 return dstRenderTargetContext;
422 }
423
424 namespace SkGpuBlurUtils {
425
GaussianBlur(GrRecordingContext * context,sk_sp<GrTextureProxy> srcProxy,GrColorType srcColorType,SkAlphaType srcAT,const SkIPoint & proxyOffset,sk_sp<SkColorSpace> colorSpace,const SkIRect & dstBounds,const SkIRect & srcBounds,float sigmaX,float sigmaY,GrTextureDomain::Mode mode,SkBackingFit fit)426 std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
427 sk_sp<GrTextureProxy> srcProxy,
428 GrColorType srcColorType,
429 SkAlphaType srcAT,
430 const SkIPoint& proxyOffset,
431 sk_sp<SkColorSpace> colorSpace,
432 const SkIRect& dstBounds,
433 const SkIRect& srcBounds,
434 float sigmaX,
435 float sigmaY,
436 GrTextureDomain::Mode mode,
437 SkBackingFit fit) {
438 SkASSERT(context);
439
440 TRACE_EVENT2("skia.gpu", "GaussianBlur", "sigmaX", sigmaX, "sigmaY", sigmaY);
441
442 int finalW = dstBounds.width();
443 int finalH = dstBounds.height();
444
445 int scaleFactorX, radiusX;
446 int scaleFactorY, radiusY;
447 int maxTextureSize = context->priv().caps()->maxTextureSize();
448 sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
449 sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
450 SkASSERT(sigmaX || sigmaY);
451
452 SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
453 SkIRect localSrcBounds = srcBounds;
454 SkIPoint localProxyOffset = proxyOffset;
455
456 // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
457 // launch a single non separable kernel vs two launches
458 if (sigmaX > 0.0f && sigmaY > 0.0f &&
459 (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
460 // We shouldn't be scaling because this is a small size blur
461 SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
462 // Apply the proxy offset to src bounds and offset directly
463 srcOffset -= proxyOffset;
464 localSrcBounds.offset(proxyOffset);
465 return convolve_gaussian_2d(context, std::move(srcProxy), srcColorType, localSrcBounds,
466 srcOffset, radiusX, radiusY, sigmaX, sigmaY, mode,
467 finalW, finalH, colorSpace, fit);
468 }
469
470 // Only the last rendered renderTargetContext needs to match the supplied 'fit'
471 SkBackingFit xFit = fit, yFit = fit;
472 if (scaleFactorX > 1 || scaleFactorY > 1) {
473 xFit = yFit = SkBackingFit::kApprox; // reexpand will be last
474 } else if (sigmaY > 0.0f) {
475 xFit = SkBackingFit::kApprox; // the y-pass will be last
476 }
477
478 GrTextureDomain::Mode currDomainMode = mode;
479 if (scaleFactorX > 1 || scaleFactorY > 1) {
480 srcProxy = decimate(context, std::move(srcProxy), srcColorType, localProxyOffset,
481 &srcOffset, &localSrcBounds, scaleFactorX, scaleFactorY, radiusX,
482 radiusY, currDomainMode, finalW, finalH, colorSpace);
483 if (!srcProxy) {
484 return nullptr;
485 }
486 localProxyOffset.set(0, 0);
487 if (GrTextureDomain::kIgnore_Mode == currDomainMode) {
488 // decimate() always returns an approx texture, possibly with garbage after the image.
489 // We can't ignore the domain anymore.
490 currDomainMode = GrTextureDomain::kClamp_Mode;
491 }
492 }
493
494 std::unique_ptr<GrRenderTargetContext> dstRenderTargetContext;
495
496 auto srcRect = SkIRect::MakeWH(finalW, finalH);
497 scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
498 if (sigmaX > 0.0f) {
499 dstRenderTargetContext = convolve_gaussian(
500 context, std::move(srcProxy), srcColorType, localProxyOffset, srcRect, srcOffset,
501 Direction::kX, radiusX, sigmaX, &localSrcBounds, currDomainMode, finalW, finalH,
502 colorSpace, xFit);
503 if (!dstRenderTargetContext) {
504 return nullptr;
505 }
506
507 srcProxy = dstRenderTargetContext->asTextureProxyRef();
508 if (!srcProxy) {
509 return nullptr;
510 }
511
512 srcRect.offsetTo(0, 0);
513 srcOffset.set(0, 0);
514 localProxyOffset.set(0, 0);
515 if (SkBackingFit::kApprox == xFit && GrTextureDomain::kIgnore_Mode == currDomainMode) {
516 // srcProxy is now an approx texture, possibly with garbage after the image. We can't
517 // ignore the domain anymore.
518 currDomainMode = GrTextureDomain::kClamp_Mode;
519 }
520 }
521
522 if (sigmaY > 0.0f) {
523 dstRenderTargetContext = convolve_gaussian(
524 context, std::move(srcProxy), srcColorType, localProxyOffset, srcRect, srcOffset,
525 Direction::kY, radiusY, sigmaY, &localSrcBounds, currDomainMode, finalW, finalH,
526 colorSpace, yFit);
527 if (!dstRenderTargetContext) {
528 return nullptr;
529 }
530
531 srcProxy = dstRenderTargetContext->asTextureProxyRef();
532 if (!srcProxy) {
533 return nullptr;
534 }
535
536 srcRect.offsetTo(0, 0);
537 srcOffset.set(0, 0);
538 localProxyOffset.set(0, 0);
539 }
540
541 SkASSERT(dstRenderTargetContext);
542 SkASSERT(srcProxy.get() == dstRenderTargetContext->asTextureProxy());
543 SkASSERT(localProxyOffset.x() == 0 && localProxyOffset.y() == 0);
544
545 if (scaleFactorX > 1 || scaleFactorY > 1) {
546 dstRenderTargetContext =
547 reexpand(context, std::move(dstRenderTargetContext), localSrcBounds, scaleFactorX,
548 scaleFactorY, finalW, finalH, colorSpace, fit);
549 }
550
551 SkASSERT(!dstRenderTargetContext || dstRenderTargetContext->origin() == srcProxy->origin());
552 return dstRenderTargetContext;
553 }
554 }
555
556 #endif
557