blob: 1cb5840b01319fd0e3b38007eabf1771ad3e2ee3 [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
Brian Salomoneebe7352020-12-09 16:37:04 -050035static void run(GrRecordingContext* rContext, GrSurfaceDrawContext* rtc, bool subsetSrc, bool ref) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -040036 auto srcII = SkImageInfo::Make(60, 60, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Robert Phillips2f9dad42020-11-12 14:48:28 -050037 auto surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, srcII);
Brian Salomon702c5a32020-04-28 16:21:48 -040038 GrSurfaceProxyView src;
39 if (surf) {
40 SkScalar w = surf->width();
41 SkScalar h = surf->height();
42 surf->getCanvas()->drawColor(SK_ColorDKGRAY);
43 SkPaint paint;
44 paint.setAntiAlias(true);
45 paint.setStyle(SkPaint::kStroke_Style);
46 // Draw four horizontal lines at 1/8, 1/4, 3/4, 7/8.
47 paint.setStrokeWidth(h/12.f);
48 paint.setColor(SK_ColorRED);
49 surf->getCanvas()->drawLine({0.f, 1.f*h/8.f}, {w, 1.f*h/8.f}, paint);
50 paint.setColor(/* sea foam */ 0xFF71EEB8);
51 surf->getCanvas()->drawLine({0.f, 1.f*h/4.f}, {w, 1.f*h/4.f}, paint);
52 paint.setColor(SK_ColorYELLOW);
53 surf->getCanvas()->drawLine({0.f, 3.f*h/4.f}, {w, 3.f*h/4.f}, paint);
54 paint.setColor(SK_ColorCYAN);
55 surf->getCanvas()->drawLine({0.f, 7.f*h/8.f}, {w, 7.f*h/8.f}, paint);
56
57 // Draw four vertical lines at 1/8, 1/4, 3/4, 7/8.
58 paint.setStrokeWidth(w/12.f);
59 paint.setColor(/* orange */ 0xFFFFA500);
60 surf->getCanvas()->drawLine({1.f*w/8.f, 0.f}, {1.f*h/8.f, h}, paint);
61 paint.setColor(SK_ColorBLUE);
62 surf->getCanvas()->drawLine({1.f*w/4.f, 0.f}, {1.f*h/4.f, h}, paint);
63 paint.setColor(SK_ColorMAGENTA);
64 surf->getCanvas()->drawLine({3.f*w/4.f, 0.f}, {3.f*h/4.f, h}, paint);
65 paint.setColor(SK_ColorGREEN);
66 surf->getCanvas()->drawLine({7.f*w/8.f, 0.f}, {7.f*h/8.f, h}, paint);
67
68 auto img = surf->makeImageSnapshot();
Robert Phillips2f9dad42020-11-12 14:48:28 -050069 if (auto v = as_IB(img)->view(rContext)) {
Brian Salomon702c5a32020-04-28 16:21:48 -040070 src = *v;
71 }
72 }
73 if (!src) {
74 return;
75 }
Brian Salomon702c5a32020-04-28 16:21:48 -040076
Brian Salomon11ad4cc2020-05-15 12:07:59 -040077 SkIRect srcRect = SkIRect::MakeSize(src.dimensions());
78 if (subsetSrc) {
79 srcRect = SkIRect::MakeXYWH(2.f*srcRect.width() /8.f,
80 1.f*srcRect.height()/8.f,
81 5.f*srcRect.width() /8.f,
82 6.f*srcRect.height()/8.f);
83 }
84 int srcW = srcRect.width();
85 int srcH = srcRect.height();
86 // Each set of rects is drawn in one test area so they probably should not abut or overlap
87 // to visualize the blurs separately.
88 const std::vector<SkIRect> dstRectSets[] = {
Brian Salomon287d5982020-05-19 14:24:43 -040089 // encloses source bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040090 {
91 srcRect.makeOutset(srcW/5, srcH/5)
92 },
Brian Salomon702c5a32020-04-28 16:21:48 -040093
Brian Salomon287d5982020-05-19 14:24:43 -040094 // partial overlap from above/below.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040095 {
96 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() + 3*srcH/4, srcW, srcH),
97 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() - 3*srcH/4, srcW, srcH)
98 },
99
Brian Salomon287d5982020-05-19 14:24:43 -0400100 // adjacent to each side of src bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400101 {
102 srcRect.makeOffset( 0, srcH),
103 srcRect.makeOffset( srcW, 0),
104 srcRect.makeOffset( 0, -srcH),
105 srcRect.makeOffset(-srcW, 0),
106 },
107
Brian Salomon287d5982020-05-19 14:24:43 -0400108 // fully outside src bounds in one direction.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400109 {
110 SkIRect::MakeXYWH(-6.f*srcW/8.f, -7.f*srcH/8.f, 4.f*srcW/8.f, 20.f*srcH/8.f)
111 .makeOffset(srcRect.topLeft()),
112 SkIRect::MakeXYWH(-1.f*srcW/8.f, -7.f*srcH/8.f, 16.f*srcW/8.f, 2.f*srcH/8.f)
113 .makeOffset(srcRect.topLeft()),
114 SkIRect::MakeXYWH(10.f*srcW/8.f, -3.f*srcH/8.f, 4.f*srcW/8.f, 16.f*srcH/8.f)
115 .makeOffset(srcRect.topLeft()),
116 SkIRect::MakeXYWH(-7.f*srcW/8.f, 14.f*srcH/8.f, 18.f*srcW/8.f, 1.f*srcH/8.f)
117 .makeOffset(srcRect.topLeft()),
118 },
Brian Salomon287d5982020-05-19 14:24:43 -0400119
120 // outside of src bounds in both directions.
121 {
122 SkIRect::MakeXYWH(-5.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
123 .makeOffset(srcRect.topLeft()),
124 SkIRect::MakeXYWH(-5.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
125 .makeOffset(srcRect.topLeft()),
126 SkIRect::MakeXYWH(12.f*srcW/8.f, -5.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, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
129 .makeOffset(srcRect.topLeft()),
130 },
Brian Salomon702c5a32020-04-28 16:21:48 -0400131 };
132
Robert Phillips2f9dad42020-11-12 14:48:28 -0500133 const auto& caps = *rContext->priv().caps();
Brian Salomon702c5a32020-04-28 16:21:48 -0400134
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400135 static constexpr SkScalar kPad = 10;
136 SkVector trans = {kPad, kPad};
137
138 rtc->clear(SK_PMColor4fWHITE);
139
140 SkIRect testArea = srcRect;
141 testArea.outset(testArea.width(), testArea.height());
142 for (const auto& dstRectSet : dstRectSets) {
143 for (int t = 0; t < kSkTileModeCount; ++t) {
144 auto mode = static_cast<SkTileMode>(t);
145 GrSamplerState sampler(SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest);
Mike Reed1f607332020-05-21 12:11:27 -0400146 SkMatrix m = SkMatrix::Translate(trans.x() - testArea.x(), trans.y() - testArea.y());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400147 // Draw the src subset in the tile mode faded as a reference before drawing the blur
148 // on top.
149 {
Brian Osmanf48f76e2020-07-15 16:04:17 -0400150 static constexpr float kAlpha = 0.2f;
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400151 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, SkMatrix::I(),
152 sampler, SkRect::Make(srcRect), caps);
Brian Osmanf48f76e2020-07-15 16:04:17 -0400153 fp = GrFragmentProcessor::ModulateRGBA(std::move(fp),
154 {kAlpha, kAlpha, kAlpha, kAlpha});
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400155 GrPaint paint;
John Stiles5933d7d2020-07-21 12:28:35 -0400156 paint.setColorFragmentProcessor(std::move(fp));
Michael Ludwig7c12e282020-05-29 09:54:07 -0400157 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, SkRect::Make(testArea));
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400158 }
159 // If we're in ref mode we will create a temp image that has the original image
160 // tiled into it and then do a clamp blur with adjusted params that should produce
161 // the same result as the original params.
Brian Salomoneebe7352020-12-09 16:37:04 -0500162 std::unique_ptr<GrSurfaceDrawContext> refSrc;
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400163 SkIRect refRect;
164 if (ref) {
165 // Blow up testArea into a much larger rect so that our clamp blur will not
166 // reach anywhere near the edge of our temp surface.
167 refRect = testArea.makeOutset(testArea.width(), testArea.height());
Brian Salomoneebe7352020-12-09 16:37:04 -0500168 refSrc = GrSurfaceDrawContext::Make(rContext, GrColorType::kRGBA_8888, nullptr,
169 SkBackingFit::kApprox, refRect.size());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400170 refSrc->clear(SK_PMColor4fWHITE);
171 // Setup an effect to put the original src rect at the correct logical place
172 // in the temp where the temp's origin is at the top left of refRect.
Mike Reed1f607332020-05-21 12:11:27 -0400173 SkMatrix tm = SkMatrix::Translate(refRect.left(), refRect.top());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400174 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, tm, sampler,
175 SkRect::Make(srcRect), caps);
176 GrPaint paint;
John Stiles5933d7d2020-07-21 12:28:35 -0400177 paint.setColorFragmentProcessor(std::move(fp));
Michael Ludwig7c12e282020-05-29 09:54:07 -0400178 refSrc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(),
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400179 SkRect::Make(refRect.size()));
180 }
181 // Do a blur for each dstRect in the set over our testArea-sized background.
182 for (const auto& dstRect : dstRectSet) {
183 // Setup the normal blur args.
184 const SkScalar sigmaX = src.width() / 10.f;
185 const SkScalar sigmaY = src.height() / 10.f;
186 auto blurSrc = src;
187 auto blurMode = mode;
188 auto blurDstRect = dstRect;
189 auto blurSrcRect = srcRect;
190 if (ref) {
191 // Move the dst rect to be relative to our temp surface.
192 blurDstRect = dstRect.makeOffset(-refRect.topLeft());
193 // Our new src is the entire temp containing the tiled original.
194 blurSrcRect = SkIRect::MakeSize(refRect.size());
195 // This shouldn't really matter because we should have made a large enough
196 // temp that the edges don't come into play. But this puts us on a simpler
197 // path through SkGpuBlurUtils.
198 blurMode = SkTileMode::kClamp;
199 blurSrc = refSrc->readSurfaceView();
Brian Salomon702c5a32020-04-28 16:21:48 -0400200 }
201 // Blur using the rect and draw on top.
Robert Phillips2f9dad42020-11-12 14:48:28 -0500202 if (auto blurView = blur(rContext, blurSrc, blurDstRect, blurSrcRect,
203 sigmaX, sigmaY, blurMode)) {
Brian Salomon702c5a32020-04-28 16:21:48 -0400204 auto fp = GrTextureEffect::Make(blurView, kPremul_SkAlphaType, SkMatrix::I(),
205 sampler, caps);
206 GrPaint paint;
207 // Compose against white (default paint color) and then replace the dst
208 // (SkBlendMode::kSrc).
John Stilesf743d4e2020-07-23 11:35:08 -0400209 fp = GrBlendFragmentProcessor::Make(std::move(fp), /*dst=*/nullptr,
210 SkBlendMode::kSrcOver);
John Stiles5933d7d2020-07-21 12:28:35 -0400211 paint.setColorFragmentProcessor(std::move(fp));
Brian Salomon702c5a32020-04-28 16:21:48 -0400212 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400213 rtc->fillRectToRect(nullptr, std::move(paint), GrAA::kNo, m,
Brian Salomon702c5a32020-04-28 16:21:48 -0400214 SkRect::Make(dstRect), SkRect::Make(blurView.dimensions()));
215 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400216 // Show the outline of the dst rect. Mostly for kDecal but also allows visual
217 // confirmation that the resulting blur is the right size and in the right place.
218 {
219 GrPaint paint;
220 static constexpr float kAlpha = 0.6f;
221 paint.setColor4f({0, kAlpha, 0, kAlpha});
222 SkPaint stroke;
223 stroke.setStyle(SkPaint::kStroke_Style);
224 stroke.setStrokeWidth(1.f);
225 GrStyle style(stroke);
226 auto dstR = SkRect::Make(dstRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400227 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, dstR, &style);
Brian Salomon702c5a32020-04-28 16:21:48 -0400228 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400229 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400230 // Show the rect that's being blurred.
231 {
232 GrPaint paint;
233 static constexpr float kAlpha = 0.3f;
234 paint.setColor4f({0, 0, 0, kAlpha});
235 SkPaint stroke;
236 stroke.setStyle(SkPaint::kStroke_Style);
237 stroke.setStrokeWidth(1.f);
238 GrStyle style(stroke);
239 auto srcR = SkRect::Make(srcRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400240 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, srcR, &style);
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400241 }
242 trans.fX += testArea.width() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400243 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400244 trans.fX = kPad;
245 trans.fY += testArea.height() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400246 }
247}
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400248
Brian Salomon287d5982020-05-19 14:24:43 -0400249DEF_SIMPLE_GPU_GM(gpu_blur_utils, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, false); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400250
Brian Salomon287d5982020-05-19 14:24:43 -0400251DEF_SIMPLE_GPU_GM(gpu_blur_utils_ref, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, true); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400252
Brian Salomon287d5982020-05-19 14:24:43 -0400253DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_rect, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400254 run(ctx, rtc, true, false);
255}
256
Brian Salomon287d5982020-05-19 14:24:43 -0400257DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_ref, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400258 run(ctx, rtc, true, true);
259}