blob: 28c44b909d18122d84568464385004f55628d98b [file] [log] [blame]
Brian Salomon702c5a32020-04-28 16:21:48 -04001/*
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"
Robert Phillips2f9dad42020-11-12 14:48:28 -050011#include "include/gpu/GrRecordingContext.h"
Brian Salomon702c5a32020-04-28 16:21:48 -040012#include "src/core/SkGpuBlurUtils.h"
Robert Phillips95c250c2020-06-29 15:36:12 -040013#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon702c5a32020-04-28 16:21:48 -040014#include "src/gpu/GrStyle.h"
15#include "src/gpu/SkGr.h"
John Stilesf743d4e2020-07-23 11:35:08 -040016#include "src/gpu/effects/GrBlendFragmentProcessor.h"
Brian Salomon702c5a32020-04-28 16:21:48 -040017#include "src/image/SkImage_Base.h"
18
Robert Phillips95c250c2020-06-29 15:36:12 -040019static GrSurfaceProxyView blur(GrRecordingContext* ctx,
Brian Salomon11ad4cc2020-05-15 12:07:59 -040020 GrSurfaceProxyView src,
21 SkIRect dstB,
22 SkIRect srcB,
23 float sigmaX,
24 float sigmaY,
25 SkTileMode mode) {
26 auto resultRTC =
27 SkGpuBlurUtils::GaussianBlur(ctx, src, GrColorType::kRGBA_8888, kPremul_SkAlphaType,
28 nullptr, dstB, srcB, sigmaX, sigmaY, mode);
29 if (!resultRTC) {
30 return {};
31 }
32 return resultRTC->readSurfaceView();
33};
34
Robert Phillips2f9dad42020-11-12 14:48:28 -050035static void run(GrRecordingContext* rContext, GrRenderTargetContext* rtc,
36 bool subsetSrc, bool ref) {
Robert Phillips95c250c2020-06-29 15:36:12 -040037
Brian Salomon11ad4cc2020-05-15 12:07:59 -040038 auto srcII = SkImageInfo::Make(60, 60, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Robert Phillips2f9dad42020-11-12 14:48:28 -050039 auto surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, srcII);
Brian Salomon702c5a32020-04-28 16:21:48 -040040 GrSurfaceProxyView src;
41 if (surf) {
42 SkScalar w = surf->width();
43 SkScalar h = surf->height();
44 surf->getCanvas()->drawColor(SK_ColorDKGRAY);
45 SkPaint paint;
46 paint.setAntiAlias(true);
47 paint.setStyle(SkPaint::kStroke_Style);
48 // Draw four horizontal lines at 1/8, 1/4, 3/4, 7/8.
49 paint.setStrokeWidth(h/12.f);
50 paint.setColor(SK_ColorRED);
51 surf->getCanvas()->drawLine({0.f, 1.f*h/8.f}, {w, 1.f*h/8.f}, paint);
52 paint.setColor(/* sea foam */ 0xFF71EEB8);
53 surf->getCanvas()->drawLine({0.f, 1.f*h/4.f}, {w, 1.f*h/4.f}, paint);
54 paint.setColor(SK_ColorYELLOW);
55 surf->getCanvas()->drawLine({0.f, 3.f*h/4.f}, {w, 3.f*h/4.f}, paint);
56 paint.setColor(SK_ColorCYAN);
57 surf->getCanvas()->drawLine({0.f, 7.f*h/8.f}, {w, 7.f*h/8.f}, paint);
58
59 // Draw four vertical lines at 1/8, 1/4, 3/4, 7/8.
60 paint.setStrokeWidth(w/12.f);
61 paint.setColor(/* orange */ 0xFFFFA500);
62 surf->getCanvas()->drawLine({1.f*w/8.f, 0.f}, {1.f*h/8.f, h}, paint);
63 paint.setColor(SK_ColorBLUE);
64 surf->getCanvas()->drawLine({1.f*w/4.f, 0.f}, {1.f*h/4.f, h}, paint);
65 paint.setColor(SK_ColorMAGENTA);
66 surf->getCanvas()->drawLine({3.f*w/4.f, 0.f}, {3.f*h/4.f, h}, paint);
67 paint.setColor(SK_ColorGREEN);
68 surf->getCanvas()->drawLine({7.f*w/8.f, 0.f}, {7.f*h/8.f, h}, paint);
69
70 auto img = surf->makeImageSnapshot();
Robert Phillips2f9dad42020-11-12 14:48:28 -050071 if (auto v = as_IB(img)->view(rContext)) {
Brian Salomon702c5a32020-04-28 16:21:48 -040072 src = *v;
73 }
74 }
75 if (!src) {
76 return;
77 }
Brian Salomon702c5a32020-04-28 16:21:48 -040078
Brian Salomon11ad4cc2020-05-15 12:07:59 -040079 SkIRect srcRect = SkIRect::MakeSize(src.dimensions());
80 if (subsetSrc) {
81 srcRect = SkIRect::MakeXYWH(2.f*srcRect.width() /8.f,
82 1.f*srcRect.height()/8.f,
83 5.f*srcRect.width() /8.f,
84 6.f*srcRect.height()/8.f);
85 }
86 int srcW = srcRect.width();
87 int srcH = srcRect.height();
88 // Each set of rects is drawn in one test area so they probably should not abut or overlap
89 // to visualize the blurs separately.
90 const std::vector<SkIRect> dstRectSets[] = {
Brian Salomon287d5982020-05-19 14:24:43 -040091 // encloses source bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040092 {
93 srcRect.makeOutset(srcW/5, srcH/5)
94 },
Brian Salomon702c5a32020-04-28 16:21:48 -040095
Brian Salomon287d5982020-05-19 14:24:43 -040096 // partial overlap from above/below.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040097 {
98 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() + 3*srcH/4, srcW, srcH),
99 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() - 3*srcH/4, srcW, srcH)
100 },
101
Brian Salomon287d5982020-05-19 14:24:43 -0400102 // adjacent to each side of src bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400103 {
104 srcRect.makeOffset( 0, srcH),
105 srcRect.makeOffset( srcW, 0),
106 srcRect.makeOffset( 0, -srcH),
107 srcRect.makeOffset(-srcW, 0),
108 },
109
Brian Salomon287d5982020-05-19 14:24:43 -0400110 // fully outside src bounds in one direction.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400111 {
112 SkIRect::MakeXYWH(-6.f*srcW/8.f, -7.f*srcH/8.f, 4.f*srcW/8.f, 20.f*srcH/8.f)
113 .makeOffset(srcRect.topLeft()),
114 SkIRect::MakeXYWH(-1.f*srcW/8.f, -7.f*srcH/8.f, 16.f*srcW/8.f, 2.f*srcH/8.f)
115 .makeOffset(srcRect.topLeft()),
116 SkIRect::MakeXYWH(10.f*srcW/8.f, -3.f*srcH/8.f, 4.f*srcW/8.f, 16.f*srcH/8.f)
117 .makeOffset(srcRect.topLeft()),
118 SkIRect::MakeXYWH(-7.f*srcW/8.f, 14.f*srcH/8.f, 18.f*srcW/8.f, 1.f*srcH/8.f)
119 .makeOffset(srcRect.topLeft()),
120 },
Brian Salomon287d5982020-05-19 14:24:43 -0400121
122 // outside of src bounds in both directions.
123 {
124 SkIRect::MakeXYWH(-5.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
125 .makeOffset(srcRect.topLeft()),
126 SkIRect::MakeXYWH(-5.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
127 .makeOffset(srcRect.topLeft()),
128 SkIRect::MakeXYWH(12.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
129 .makeOffset(srcRect.topLeft()),
130 SkIRect::MakeXYWH(12.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
131 .makeOffset(srcRect.topLeft()),
132 },
Brian Salomon702c5a32020-04-28 16:21:48 -0400133 };
134
Robert Phillips2f9dad42020-11-12 14:48:28 -0500135 const auto& caps = *rContext->priv().caps();
Brian Salomon702c5a32020-04-28 16:21:48 -0400136
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400137 static constexpr SkScalar kPad = 10;
138 SkVector trans = {kPad, kPad};
139
140 rtc->clear(SK_PMColor4fWHITE);
141
142 SkIRect testArea = srcRect;
143 testArea.outset(testArea.width(), testArea.height());
144 for (const auto& dstRectSet : dstRectSets) {
145 for (int t = 0; t < kSkTileModeCount; ++t) {
146 auto mode = static_cast<SkTileMode>(t);
147 GrSamplerState sampler(SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest);
Mike Reed1f607332020-05-21 12:11:27 -0400148 SkMatrix m = SkMatrix::Translate(trans.x() - testArea.x(), trans.y() - testArea.y());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400149 // Draw the src subset in the tile mode faded as a reference before drawing the blur
150 // on top.
151 {
Brian Osmanf48f76e2020-07-15 16:04:17 -0400152 static constexpr float kAlpha = 0.2f;
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400153 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, SkMatrix::I(),
154 sampler, SkRect::Make(srcRect), caps);
Brian Osmanf48f76e2020-07-15 16:04:17 -0400155 fp = GrFragmentProcessor::ModulateRGBA(std::move(fp),
156 {kAlpha, kAlpha, kAlpha, kAlpha});
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400157 GrPaint paint;
John Stiles5933d7d2020-07-21 12:28:35 -0400158 paint.setColorFragmentProcessor(std::move(fp));
Michael Ludwig7c12e282020-05-29 09:54:07 -0400159 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, SkRect::Make(testArea));
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400160 }
161 // If we're in ref mode we will create a temp image that has the original image
162 // tiled into it and then do a clamp blur with adjusted params that should produce
163 // the same result as the original params.
164 std::unique_ptr<GrRenderTargetContext> refSrc;
165 SkIRect refRect;
166 if (ref) {
167 // Blow up testArea into a much larger rect so that our clamp blur will not
168 // reach anywhere near the edge of our temp surface.
169 refRect = testArea.makeOutset(testArea.width(), testArea.height());
Robert Phillips2f9dad42020-11-12 14:48:28 -0500170 refSrc = GrRenderTargetContext::Make(rContext, GrColorType::kRGBA_8888, nullptr,
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400171 SkBackingFit::kApprox, refRect.size());
172 refSrc->clear(SK_PMColor4fWHITE);
173 // Setup an effect to put the original src rect at the correct logical place
174 // in the temp where the temp's origin is at the top left of refRect.
Mike Reed1f607332020-05-21 12:11:27 -0400175 SkMatrix tm = SkMatrix::Translate(refRect.left(), refRect.top());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400176 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, tm, sampler,
177 SkRect::Make(srcRect), caps);
178 GrPaint paint;
John Stiles5933d7d2020-07-21 12:28:35 -0400179 paint.setColorFragmentProcessor(std::move(fp));
Michael Ludwig7c12e282020-05-29 09:54:07 -0400180 refSrc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(),
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400181 SkRect::Make(refRect.size()));
182 }
183 // Do a blur for each dstRect in the set over our testArea-sized background.
184 for (const auto& dstRect : dstRectSet) {
185 // Setup the normal blur args.
186 const SkScalar sigmaX = src.width() / 10.f;
187 const SkScalar sigmaY = src.height() / 10.f;
188 auto blurSrc = src;
189 auto blurMode = mode;
190 auto blurDstRect = dstRect;
191 auto blurSrcRect = srcRect;
192 if (ref) {
193 // Move the dst rect to be relative to our temp surface.
194 blurDstRect = dstRect.makeOffset(-refRect.topLeft());
195 // Our new src is the entire temp containing the tiled original.
196 blurSrcRect = SkIRect::MakeSize(refRect.size());
197 // This shouldn't really matter because we should have made a large enough
198 // temp that the edges don't come into play. But this puts us on a simpler
199 // path through SkGpuBlurUtils.
200 blurMode = SkTileMode::kClamp;
201 blurSrc = refSrc->readSurfaceView();
Brian Salomon702c5a32020-04-28 16:21:48 -0400202 }
203 // Blur using the rect and draw on top.
Robert Phillips2f9dad42020-11-12 14:48:28 -0500204 if (auto blurView = blur(rContext, blurSrc, blurDstRect, blurSrcRect,
205 sigmaX, sigmaY, blurMode)) {
Brian Salomon702c5a32020-04-28 16:21:48 -0400206 auto fp = GrTextureEffect::Make(blurView, kPremul_SkAlphaType, SkMatrix::I(),
207 sampler, caps);
208 GrPaint paint;
209 // Compose against white (default paint color) and then replace the dst
210 // (SkBlendMode::kSrc).
John Stilesf743d4e2020-07-23 11:35:08 -0400211 fp = GrBlendFragmentProcessor::Make(std::move(fp), /*dst=*/nullptr,
212 SkBlendMode::kSrcOver);
John Stiles5933d7d2020-07-21 12:28:35 -0400213 paint.setColorFragmentProcessor(std::move(fp));
Brian Salomon702c5a32020-04-28 16:21:48 -0400214 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400215 rtc->fillRectToRect(nullptr, std::move(paint), GrAA::kNo, m,
Brian Salomon702c5a32020-04-28 16:21:48 -0400216 SkRect::Make(dstRect), SkRect::Make(blurView.dimensions()));
217 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400218 // Show the outline of the dst rect. Mostly for kDecal but also allows visual
219 // confirmation that the resulting blur is the right size and in the right place.
220 {
221 GrPaint paint;
222 static constexpr float kAlpha = 0.6f;
223 paint.setColor4f({0, kAlpha, 0, kAlpha});
224 SkPaint stroke;
225 stroke.setStyle(SkPaint::kStroke_Style);
226 stroke.setStrokeWidth(1.f);
227 GrStyle style(stroke);
228 auto dstR = SkRect::Make(dstRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400229 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, dstR, &style);
Brian Salomon702c5a32020-04-28 16:21:48 -0400230 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400231 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400232 // Show the rect that's being blurred.
233 {
234 GrPaint paint;
235 static constexpr float kAlpha = 0.3f;
236 paint.setColor4f({0, 0, 0, kAlpha});
237 SkPaint stroke;
238 stroke.setStyle(SkPaint::kStroke_Style);
239 stroke.setStrokeWidth(1.f);
240 GrStyle style(stroke);
241 auto srcR = SkRect::Make(srcRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400242 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, srcR, &style);
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400243 }
244 trans.fX += testArea.width() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400245 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400246 trans.fX = kPad;
247 trans.fY += testArea.height() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400248 }
249}
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400250
Brian Salomon287d5982020-05-19 14:24:43 -0400251DEF_SIMPLE_GPU_GM(gpu_blur_utils, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, false); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400252
Brian Salomon287d5982020-05-19 14:24:43 -0400253DEF_SIMPLE_GPU_GM(gpu_blur_utils_ref, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, true); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400254
Brian Salomon287d5982020-05-19 14:24:43 -0400255DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_rect, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400256 run(ctx, rtc, true, false);
257}
258
Brian Salomon287d5982020-05-19 14:24:43 -0400259DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_ref, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400260 run(ctx, rtc, true, true);
261}