1 /*
2  * Copyright 2020 Google LLC.
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 "gm/gm.h"
9 
10 #include "include/effects/SkGradientShader.h"
11 #include "include/gpu/GrRecordingContext.h"
12 #include "src/core/SkGpuBlurUtils.h"
13 #include "src/gpu/GrRecordingContextPriv.h"
14 #include "src/gpu/GrStyle.h"
15 #include "src/gpu/SkGr.h"
16 #include "src/gpu/effects/GrBlendFragmentProcessor.h"
17 #include "src/image/SkImage_Base.h"
18 
blur(GrRecordingContext * ctx,GrSurfaceProxyView src,SkIRect dstB,SkIRect srcB,float sigmaX,float sigmaY,SkTileMode mode)19 static GrSurfaceProxyView blur(GrRecordingContext* ctx,
20                                GrSurfaceProxyView src,
21                                SkIRect dstB,
22                                SkIRect srcB,
23                                float sigmaX,
24                                float sigmaY,
25                                SkTileMode mode) {
26     auto resultSDC = SkGpuBlurUtils::GaussianBlur(ctx,
27                                                   src,
28                                                   GrColorType::kRGBA_8888,
29                                                   kPremul_SkAlphaType,
30                                                   nullptr,
31                                                   dstB,
32                                                   srcB,
33                                                   sigmaX,
34                                                   sigmaY,
35                                                   mode);
36     if (!resultSDC) {
37         return {};
38     }
39     return resultSDC->readSurfaceView();
40 };
41 
42 // Performs tiling first of the src into dst bounds with a surrounding skirt so the blur can use
43 // clamp. Does repeated blurs rather than invoking downsampling.
slow_blur(GrRecordingContext * ctx,GrSurfaceProxyView src,SkIRect dstB,SkIRect srcB,float sigmaX,float sigmaY,SkTileMode mode)44 static GrSurfaceProxyView slow_blur(GrRecordingContext* ctx,
45                                     GrSurfaceProxyView src,
46                                     SkIRect dstB,
47                                     SkIRect srcB,
48                                     float sigmaX,
49                                     float sigmaY,
50                                     SkTileMode mode) {
51     auto tileInto = [ctx](GrSurfaceProxyView src,
52                           SkIRect srcTileRect,
53                           SkISize resultSize,
54                           SkIPoint offset,
55                           SkTileMode mode) {
56         GrImageInfo info(GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr, resultSize);
57         auto fc = GrSurfaceFillContext::Make(ctx, info);
58         if (!fc) {
59             return GrSurfaceProxyView{};
60         }
61         GrSamplerState sampler(SkTileModeToWrapMode(mode), SkFilterMode::kNearest);
62         auto fp = GrTextureEffect::MakeSubset(src,
63                                               kPremul_SkAlphaType,
64                                               SkMatrix::Translate(-offset.x(), -offset.y()),
65                                               sampler,
66                                               SkRect::Make(srcTileRect),
67                                               *ctx->priv().caps());
68         fc->fillWithFP(std::move(fp));
69         return fc->readSurfaceView();
70     };
71 
72     SkIPoint outset = {SkGpuBlurUtils::SigmaRadius(sigmaX), SkGpuBlurUtils::SigmaRadius(sigmaY)};
73     SkISize size = {dstB.width() + 2*outset.x(), dstB.height() + 2*outset.y()};
74     src = tileInto(std::move(src), srcB, size, outset - dstB.topLeft(), mode);
75     if (!src) {
76         return {};
77     }
78     dstB = SkIRect::MakePtSize(outset, dstB.size());
79 
80     while (sigmaX || sigmaY) {
81         float stepX = sigmaX;
82         if (stepX > SkGpuBlurUtils::kMaxSigma) {
83             stepX = SkGpuBlurUtils::kMaxSigma;
84             // A blur of sigma1 followed by a blur of sigma2 is equiv. to a single blur of
85             // sqrt(sigma1^2 + sigma2^2).
86             sigmaX = sqrt(sigmaX*sigmaX - SkGpuBlurUtils::kMaxSigma*SkGpuBlurUtils::kMaxSigma);
87         } else {
88             sigmaX = 0.f;
89         }
90         float stepY = sigmaY;
91         if (stepY > SkGpuBlurUtils::kMaxSigma) {
92             stepY = SkGpuBlurUtils::kMaxSigma;
93             sigmaY = sqrt(sigmaY*sigmaY- SkGpuBlurUtils::kMaxSigma*SkGpuBlurUtils::kMaxSigma);
94         } else {
95             sigmaY = 0.f;
96         }
97         auto bounds = SkIRect::MakeSize(src.dimensions());
98         auto sdc = SkGpuBlurUtils::GaussianBlur(ctx,
99                                                 std::move(src),
100                                                 GrColorType::kRGBA_8888,
101                                                 kPremul_SkAlphaType,
102                                                 nullptr,
103                                                 bounds,
104                                                 bounds,
105                                                 stepX,
106                                                 stepY,
107                                                 SkTileMode::kClamp);
108         if (!sdc) {
109             return {};
110         }
111         src = sdc->readSurfaceView();
112     }
113     // We have o use the original mode here because we may have only blurred in X or Y and then
114     // the other dimension was not expanded.
115     auto srcRect = SkIRect::MakeSize(src.dimensions());
116     return tileInto(std::move(src), srcRect, dstB.size(), -outset, SkTileMode::kClamp);
117 };
118 
119 // Makes a src texture for as a source for blurs. If 'contentArea' then the content will
120 // be in that rect, the 1-pixel surrounding border will be transparent black, and red outside of
121 // that. Otherwise, the content fills the dimensions.
make_src_image(GrRecordingContext * rContext,SkISize dimensions,const SkIRect * contentArea=nullptr)122 GrSurfaceProxyView make_src_image(GrRecordingContext* rContext,
123                                   SkISize dimensions,
124                                   const SkIRect* contentArea = nullptr) {
125     auto srcII = SkImageInfo::Make(dimensions, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
126     auto surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, srcII);
127     if (!surf) {
128         return {};
129     }
130 
131     float w, h;
132     if (contentArea) {
133         surf->getCanvas()->clear(SK_ColorRED);
134         surf->getCanvas()->clipIRect(contentArea->makeOutset(1, 1));
135         surf->getCanvas()->clear(SK_ColorTRANSPARENT);
136         surf->getCanvas()->clipIRect(*contentArea);
137         surf->getCanvas()->translate(contentArea->top(), contentArea->left());
138         w = contentArea->width();
139         h = contentArea->height();
140     } else {
141         w = dimensions.width();
142         h = dimensions.height();
143     }
144 
145     surf->getCanvas()->drawColor(SK_ColorDKGRAY);
146     SkPaint paint;
147     paint.setAntiAlias(true);
148     paint.setStyle(SkPaint::kStroke_Style);
149     // Draw four horizontal lines at 1/8, 1/4, 3/4, 7/8.
150     paint.setStrokeWidth(h/12.f);
151     paint.setColor(SK_ColorRED);
152     surf->getCanvas()->drawLine({0.f, 1.f*h/8.f}, {w, 1.f*h/8.f}, paint);
153     paint.setColor(/* sea foam */ 0xFF71EEB8);
154     surf->getCanvas()->drawLine({0.f, 1.f*h/4.f}, {w, 1.f*h/4.f}, paint);
155     paint.setColor(SK_ColorYELLOW);
156     surf->getCanvas()->drawLine({0.f, 3.f*h/4.f}, {w, 3.f*h/4.f}, paint);
157     paint.setColor(SK_ColorCYAN);
158     surf->getCanvas()->drawLine({0.f, 7.f*h/8.f}, {w, 7.f*h/8.f}, paint);
159 
160     // Draw four vertical lines at 1/8, 1/4, 3/4, 7/8.
161     paint.setStrokeWidth(w/12.f);
162     paint.setColor(/* orange */ 0xFFFFA500);
163     surf->getCanvas()->drawLine({1.f*w/8.f, 0.f}, {1.f*h/8.f, h}, paint);
164     paint.setColor(SK_ColorBLUE);
165     surf->getCanvas()->drawLine({1.f*w/4.f, 0.f}, {1.f*h/4.f, h}, paint);
166     paint.setColor(SK_ColorMAGENTA);
167     surf->getCanvas()->drawLine({3.f*w/4.f, 0.f}, {3.f*h/4.f, h}, paint);
168     paint.setColor(SK_ColorGREEN);
169     surf->getCanvas()->drawLine({7.f*w/8.f, 0.f}, {7.f*h/8.f, h}, paint);
170 
171     auto img = surf->makeImageSnapshot();
172     auto [src, ct] = as_IB(img)->asView(rContext, GrMipmapped::kNo);
173     return src;
174 }
175 
run(GrRecordingContext * rContext,GrSurfaceDrawContext * sdc,bool subsetSrc,bool ref)176 static void run(GrRecordingContext* rContext, GrSurfaceDrawContext* sdc, bool subsetSrc, bool ref) {
177     GrSurfaceProxyView src = make_src_image(rContext, {60, 60});
178     if (!src) {
179         return;
180     }
181 
182     SkIRect srcRect = SkIRect::MakeSize(src.dimensions());
183     if (subsetSrc) {
184         srcRect = SkIRect::MakeXYWH(2.f*srcRect.width() /8.f,
185                                     1.f*srcRect.height()/8.f,
186                                     5.f*srcRect.width() /8.f,
187                                     6.f*srcRect.height()/8.f);
188     }
189     int srcW = srcRect.width();
190     int srcH = srcRect.height();
191     // Each set of rects is drawn in one test area so they probably should not abut or overlap
192     // to visualize the blurs separately.
193     const std::vector<SkIRect> dstRectSets[] = {
194             // encloses source bounds.
195             {
196                     srcRect.makeOutset(srcW/5, srcH/5)
197             },
198 
199             // partial overlap from above/below.
200             {
201                     SkIRect::MakeXYWH(srcRect.x(), srcRect.y() + 3*srcH/4, srcW, srcH),
202                     SkIRect::MakeXYWH(srcRect.x(), srcRect.y() - 3*srcH/4, srcW, srcH)
203             },
204 
205             // adjacent to each side of src bounds.
206             {
207                     srcRect.makeOffset(    0,  srcH),
208                     srcRect.makeOffset( srcW,     0),
209                     srcRect.makeOffset(    0, -srcH),
210                     srcRect.makeOffset(-srcW,     0),
211             },
212 
213             // fully outside src bounds in one direction.
214             {
215                     SkIRect::MakeXYWH(-6.f*srcW/8.f, -7.f*srcH/8.f,  4.f*srcW/8.f, 20.f*srcH/8.f)
216                             .makeOffset(srcRect.topLeft()),
217                     SkIRect::MakeXYWH(-1.f*srcW/8.f, -7.f*srcH/8.f, 16.f*srcW/8.f,  2.f*srcH/8.f)
218                             .makeOffset(srcRect.topLeft()),
219                     SkIRect::MakeXYWH(10.f*srcW/8.f, -3.f*srcH/8.f,  4.f*srcW/8.f, 16.f*srcH/8.f)
220                             .makeOffset(srcRect.topLeft()),
221                     SkIRect::MakeXYWH(-7.f*srcW/8.f, 14.f*srcH/8.f, 18.f*srcW/8.f,  1.f*srcH/8.f)
222                             .makeOffset(srcRect.topLeft()),
223             },
224 
225             // outside of src bounds in both directions.
226             {
227                     SkIRect::MakeXYWH(-5.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
228                             .makeOffset(srcRect.topLeft()),
229                     SkIRect::MakeXYWH(-5.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
230                             .makeOffset(srcRect.topLeft()),
231                     SkIRect::MakeXYWH(12.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
232                             .makeOffset(srcRect.topLeft()),
233                     SkIRect::MakeXYWH(12.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
234                             .makeOffset(srcRect.topLeft()),
235             },
236     };
237 
238     const auto& caps = *rContext->priv().caps();
239 
240     static constexpr SkScalar kPad = 10;
241     SkVector trans = {kPad, kPad};
242 
243     sdc->clear(SK_PMColor4fWHITE);
244 
245     SkIRect testArea = srcRect;
246     testArea.outset(testArea.width(), testArea.height());
247     for (const auto& dstRectSet : dstRectSets) {
248         for (int t = 0; t < kSkTileModeCount; ++t) {
249             auto mode = static_cast<SkTileMode>(t);
250             GrSamplerState sampler(SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest);
251             SkMatrix m = SkMatrix::Translate(trans.x() - testArea.x(), trans.y() - testArea.y());
252             // Draw the src subset in the tile mode faded as a reference before drawing the blur
253             // on top.
254             {
255                 static constexpr float kAlpha = 0.2f;
256                 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, SkMatrix::I(),
257                                                       sampler, SkRect::Make(srcRect), caps);
258                 fp = GrFragmentProcessor::ModulateRGBA(std::move(fp),
259                                                        {kAlpha, kAlpha, kAlpha, kAlpha});
260                 GrPaint paint;
261                 paint.setColorFragmentProcessor(std::move(fp));
262                 sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, SkRect::Make(testArea));
263             }
264             // Do a blur for each dstRect in the set over our testArea-sized background.
265             for (const auto& dstRect : dstRectSet) {
266                 const SkScalar sigmaX = src.width()  / 10.f;
267                 const SkScalar sigmaY = src.height() / 10.f;
268                 auto blurFn = ref ? slow_blur : blur;
269                 // Blur using the rect and draw on top.
270                 if (auto blurView = blurFn(rContext,
271                                            src,
272                                            dstRect,
273                                            srcRect,
274                                            sigmaX,
275                                            sigmaY,
276                                            mode)) {
277                     auto fp = GrTextureEffect::Make(blurView,
278                                                     kPremul_SkAlphaType,
279                                                     SkMatrix::I(),
280                                                     sampler,
281                                                     caps);
282                     // Compose against white (default paint color)
283                     fp = GrBlendFragmentProcessor::Make(std::move(fp),
284                                                         /*dst=*/nullptr,
285                                                         SkBlendMode::kSrcOver);
286                     GrPaint paint;
287                     // Compose against white (default paint color) and then replace the dst
288                     // (SkBlendMode::kSrc).
289                     fp = GrBlendFragmentProcessor::Make(std::move(fp), /*dst=*/nullptr,
290                                                         SkBlendMode::kSrcOver);
291                     paint.setColorFragmentProcessor(std::move(fp));
292                     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
293                     sdc->fillRectToRect(nullptr,
294                                         std::move(paint),
295                                         GrAA::kNo,
296                                         m,
297                                         SkRect::Make(dstRect),
298                                         SkRect::Make(blurView.dimensions()));
299                 }
300                 // Show the outline of the dst rect. Mostly for kDecal but also allows visual
301                 // confirmation that the resulting blur is the right size and in the right place.
302                 {
303                     GrPaint paint;
304                     static constexpr float kAlpha = 0.6f;
305                     paint.setColor4f({0, kAlpha, 0, kAlpha});
306                     SkPaint stroke;
307                     stroke.setStyle(SkPaint::kStroke_Style);
308                     stroke.setStrokeWidth(1.f);
309                     GrStyle style(stroke);
310                     auto dstR = SkRect::Make(dstRect).makeOutset(0.5f, 0.5f);
311                     sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, dstR, &style);
312                 }
313             }
314             // Show the rect that's being blurred.
315             {
316                 GrPaint paint;
317                 static constexpr float kAlpha = 0.3f;
318                 paint.setColor4f({0, 0, 0, kAlpha});
319                 SkPaint stroke;
320                 stroke.setStyle(SkPaint::kStroke_Style);
321                 stroke.setStrokeWidth(1.f);
322                 GrStyle style(stroke);
323                 auto srcR = SkRect::Make(srcRect).makeOutset(0.5f, 0.5f);
324                 sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, srcR, &style);
325             }
326             trans.fX += testArea.width() + kPad;
327         }
328         trans.fX = kPad;
329         trans.fY += testArea.height() + kPad;
330     }
331 }
332 
333 DEF_SIMPLE_GPU_GM(gpu_blur_utils, ctx, sdc, canvas, 765, 955) { run(ctx, sdc, false, false); }
334 
335 DEF_SIMPLE_GPU_GM(gpu_blur_utils_ref, ctx, sdc, canvas, 765, 955) { run(ctx, sdc, false, true); }
336 
337 DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_rect, ctx, sdc, canvas, 485, 730) {
338     run(ctx, sdc, true, false);
339 }
340 
341 DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_ref, ctx, sdc, canvas, 485, 730) {
342     run(ctx, sdc, true, true);
343 }
344 
345 // Because of the way blur sigmas concat (sigTotal = sqrt(sig1^2 + sig2^2) generating these images
346 // for very large sigmas is incredibly slow. This can be enabled while working on the blur code to
347 // check results.
348 static bool constexpr kShowSlowRefImages = false;
349 
do_very_large_blur_gm(GrSurfaceDrawContext * sdc,GrRecordingContext * rContext,GrSurfaceProxyView src,SkIRect srcB)350 static void do_very_large_blur_gm(GrSurfaceDrawContext* sdc,
351                                   GrRecordingContext* rContext,
352                                   GrSurfaceProxyView src,
353                                   SkIRect srcB) {
354     // Clear to a color other than gray to contrast with test image.
355     sdc->clear(SkColor4f{0.3f, 0.4f, 0.2f, 1});
356 
357     int x = 10;
358     int y = 10;
359     for (auto blurDirs : {0b01, 0b10, 0b11}) {
360         for (int t = 0; t <= static_cast<int>(SkTileMode::kLastTileMode); ++t) {
361             auto tm = static_cast<SkTileMode>(t);
362             auto dstB = srcB.makeOutset(30, 30);
363             for (float sigma : {0.f, 5.f, 25.f, 80.f}) {
364                 std::vector<decltype(blur)*> blurs;
365                 blurs.push_back(blur);
366                 if (kShowSlowRefImages) {
367                     blurs.push_back(slow_blur);
368                 }
369                 for (auto b : blurs) {
370                     float sigX = sigma*((blurDirs & 0b01) >> 0);
371                     float sigY = sigma*((blurDirs & 0b10) >> 1);
372                     GrSurfaceProxyView result = b(rContext, src, dstB, srcB, sigX, sigY, tm);
373                     auto dstRect = SkIRect::MakeSize(dstB.size()).makeOffset(x, y);
374                     // Draw a rect to show where the result should be so it's obvious if it's
375                     // missing.
376                     GrPaint paint;
377                     paint.setColor4f(b == blur ? SkPMColor4f{0, 0, 1, 1} : SkPMColor4f{1, 0, 0, 1});
378                     sdc->drawRect(nullptr,
379                                   std::move(paint),
380                                   GrAA::kNo,
381                                   SkMatrix::I(),
382                                   SkRect::Make(dstRect).makeOutset(0.5, 0.5),
383                                   &GrStyle::SimpleHairline());
384                     if (result) {
385                         std::unique_ptr<GrFragmentProcessor> fp =
386                                 GrTextureEffect::Make(std::move(result), kPremul_SkAlphaType);
387                         fp = GrBlendFragmentProcessor::Make(std::move(fp),
388                                                             /*dst=*/nullptr,
389                                                             SkBlendMode::kSrcOver);
390                         sdc->fillRectToRectWithFP(SkIRect::MakeSize(dstB.size()),
391                                                   dstRect,
392                                                   std::move(fp));
393                     }
394                     x += dstB.width() + 10;
395                 }
396             }
397             x = 10;
398             y += dstB.height() + 10;
399         }
400     }
401 }
402 
403 DEF_SIMPLE_GPU_GM(very_large_sigma_gpu_blur, ctx, sdc, canvas, 350, 1030) {
404     auto src = make_src_image(ctx, {15, 15});
405     auto srcB = SkIRect::MakeSize(src.dimensions());
406     do_very_large_blur_gm(sdc, ctx, std::move(src), srcB);
407 }
408 
409 DEF_SIMPLE_GPU_GM(very_large_sigma_gpu_blur_subset, ctx, sdc, canvas, 350, 1030) {
410     auto srcB = SkIRect::MakeXYWH(2, 2, 15, 15);
411     SkISize imageSize = SkISize{srcB.width() + 4, srcB.height() + 4};
412     auto src = make_src_image(ctx, imageSize, &srcB);
413     do_very_large_blur_gm(sdc, ctx, std::move(src), srcB);
414 }
415 
416 DEF_SIMPLE_GPU_GM(very_large_sigma_gpu_blur_subset_transparent_border,
417                   ctx,
418                   sdc,
419                   canvas,
420                   355, 1055) {
421     auto srcB = SkIRect::MakeXYWH(3, 3, 15, 15);
422     SkISize imageSize = SkISize{srcB.width() + 4, srcB.height() + 4};
423     auto src = make_src_image(ctx, imageSize, &srcB);
424     do_very_large_blur_gm(sdc, ctx, std::move(src), srcB.makeOutset(1, 1));
425 }
426