blob: 60dcd22cfdcab9f8de9f270a87ca277ed1a93c7a [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"
11#include "src/core/SkGpuBlurUtils.h"
Brian Salomon702c5a32020-04-28 16:21:48 -040012#include "src/gpu/GrContextPriv.h"
13#include "src/gpu/GrStyle.h"
14#include "src/gpu/SkGr.h"
15#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
16#include "src/image/SkImage_Base.h"
17
Brian Salomon11ad4cc2020-05-15 12:07:59 -040018static GrSurfaceProxyView blur(GrContext* ctx,
19 GrSurfaceProxyView src,
20 SkIRect dstB,
21 SkIRect srcB,
22 float sigmaX,
23 float sigmaY,
24 SkTileMode mode) {
25 auto resultRTC =
26 SkGpuBlurUtils::GaussianBlur(ctx, src, GrColorType::kRGBA_8888, kPremul_SkAlphaType,
27 nullptr, dstB, srcB, sigmaX, sigmaY, mode);
28 if (!resultRTC) {
29 return {};
30 }
31 return resultRTC->readSurfaceView();
32};
33
34static void run(GrContext* ctx, GrRenderTargetContext* rtc, bool subsetSrc, bool ref) {
35 auto srcII = SkImageInfo::Make(60, 60, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Brian Salomon702c5a32020-04-28 16:21:48 -040036 auto surf = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, srcII);
37 GrSurfaceProxyView src;
38 if (surf) {
39 SkScalar w = surf->width();
40 SkScalar h = surf->height();
41 surf->getCanvas()->drawColor(SK_ColorDKGRAY);
42 SkPaint paint;
43 paint.setAntiAlias(true);
44 paint.setStyle(SkPaint::kStroke_Style);
45 // Draw four horizontal lines at 1/8, 1/4, 3/4, 7/8.
46 paint.setStrokeWidth(h/12.f);
47 paint.setColor(SK_ColorRED);
48 surf->getCanvas()->drawLine({0.f, 1.f*h/8.f}, {w, 1.f*h/8.f}, paint);
49 paint.setColor(/* sea foam */ 0xFF71EEB8);
50 surf->getCanvas()->drawLine({0.f, 1.f*h/4.f}, {w, 1.f*h/4.f}, paint);
51 paint.setColor(SK_ColorYELLOW);
52 surf->getCanvas()->drawLine({0.f, 3.f*h/4.f}, {w, 3.f*h/4.f}, paint);
53 paint.setColor(SK_ColorCYAN);
54 surf->getCanvas()->drawLine({0.f, 7.f*h/8.f}, {w, 7.f*h/8.f}, paint);
55
56 // Draw four vertical lines at 1/8, 1/4, 3/4, 7/8.
57 paint.setStrokeWidth(w/12.f);
58 paint.setColor(/* orange */ 0xFFFFA500);
59 surf->getCanvas()->drawLine({1.f*w/8.f, 0.f}, {1.f*h/8.f, h}, paint);
60 paint.setColor(SK_ColorBLUE);
61 surf->getCanvas()->drawLine({1.f*w/4.f, 0.f}, {1.f*h/4.f, h}, paint);
62 paint.setColor(SK_ColorMAGENTA);
63 surf->getCanvas()->drawLine({3.f*w/4.f, 0.f}, {3.f*h/4.f, h}, paint);
64 paint.setColor(SK_ColorGREEN);
65 surf->getCanvas()->drawLine({7.f*w/8.f, 0.f}, {7.f*h/8.f, h}, paint);
66
67 auto img = surf->makeImageSnapshot();
68 if (auto v = as_IB(img)->view(ctx)) {
69 src = *v;
70 }
71 }
72 if (!src) {
73 return;
74 }
Brian Salomon702c5a32020-04-28 16:21:48 -040075
Brian Salomon11ad4cc2020-05-15 12:07:59 -040076 SkIRect srcRect = SkIRect::MakeSize(src.dimensions());
77 if (subsetSrc) {
78 srcRect = SkIRect::MakeXYWH(2.f*srcRect.width() /8.f,
79 1.f*srcRect.height()/8.f,
80 5.f*srcRect.width() /8.f,
81 6.f*srcRect.height()/8.f);
82 }
83 int srcW = srcRect.width();
84 int srcH = srcRect.height();
85 // Each set of rects is drawn in one test area so they probably should not abut or overlap
86 // to visualize the blurs separately.
87 const std::vector<SkIRect> dstRectSets[] = {
Brian Salomon287d5982020-05-19 14:24:43 -040088 // encloses source bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040089 {
90 srcRect.makeOutset(srcW/5, srcH/5)
91 },
Brian Salomon702c5a32020-04-28 16:21:48 -040092
Brian Salomon287d5982020-05-19 14:24:43 -040093 // partial overlap from above/below.
Brian Salomon11ad4cc2020-05-15 12:07:59 -040094 {
95 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() + 3*srcH/4, srcW, srcH),
96 SkIRect::MakeXYWH(srcRect.x(), srcRect.y() - 3*srcH/4, srcW, srcH)
97 },
98
Brian Salomon287d5982020-05-19 14:24:43 -040099 // adjacent to each side of src bounds.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400100 {
101 srcRect.makeOffset( 0, srcH),
102 srcRect.makeOffset( srcW, 0),
103 srcRect.makeOffset( 0, -srcH),
104 srcRect.makeOffset(-srcW, 0),
105 },
106
Brian Salomon287d5982020-05-19 14:24:43 -0400107 // fully outside src bounds in one direction.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400108 {
109 SkIRect::MakeXYWH(-6.f*srcW/8.f, -7.f*srcH/8.f, 4.f*srcW/8.f, 20.f*srcH/8.f)
110 .makeOffset(srcRect.topLeft()),
111 SkIRect::MakeXYWH(-1.f*srcW/8.f, -7.f*srcH/8.f, 16.f*srcW/8.f, 2.f*srcH/8.f)
112 .makeOffset(srcRect.topLeft()),
113 SkIRect::MakeXYWH(10.f*srcW/8.f, -3.f*srcH/8.f, 4.f*srcW/8.f, 16.f*srcH/8.f)
114 .makeOffset(srcRect.topLeft()),
115 SkIRect::MakeXYWH(-7.f*srcW/8.f, 14.f*srcH/8.f, 18.f*srcW/8.f, 1.f*srcH/8.f)
116 .makeOffset(srcRect.topLeft()),
117 },
Brian Salomon287d5982020-05-19 14:24:43 -0400118
119 // outside of src bounds in both directions.
120 {
121 SkIRect::MakeXYWH(-5.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
122 .makeOffset(srcRect.topLeft()),
123 SkIRect::MakeXYWH(-5.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
124 .makeOffset(srcRect.topLeft()),
125 SkIRect::MakeXYWH(12.f*srcW/8.f, -5.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
126 .makeOffset(srcRect.topLeft()),
127 SkIRect::MakeXYWH(12.f*srcW/8.f, 12.f*srcH/8.f, 2.f*srcW/8.f, 2.f*srcH/8.f)
128 .makeOffset(srcRect.topLeft()),
129 },
Brian Salomon702c5a32020-04-28 16:21:48 -0400130 };
131
132 const auto& caps = *ctx->priv().caps();
Brian Salomon702c5a32020-04-28 16:21:48 -0400133
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400134 static constexpr SkScalar kPad = 10;
135 SkVector trans = {kPad, kPad};
136
137 rtc->clear(SK_PMColor4fWHITE);
138
139 SkIRect testArea = srcRect;
140 testArea.outset(testArea.width(), testArea.height());
141 for (const auto& dstRectSet : dstRectSets) {
142 for (int t = 0; t < kSkTileModeCount; ++t) {
143 auto mode = static_cast<SkTileMode>(t);
144 GrSamplerState sampler(SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest);
Mike Reed1f607332020-05-21 12:11:27 -0400145 SkMatrix m = SkMatrix::Translate(trans.x() - testArea.x(), trans.y() - testArea.y());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400146 // Draw the src subset in the tile mode faded as a reference before drawing the blur
147 // on top.
148 {
149 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, SkMatrix::I(),
150 sampler, SkRect::Make(srcRect), caps);
151 GrPaint paint;
152 paint.addColorFragmentProcessor(std::move(fp));
153 static constexpr float kAlpha = 0.2f;
154 paint.setColor4f({kAlpha, kAlpha, kAlpha, kAlpha});
Michael Ludwig7c12e282020-05-29 09:54:07 -0400155 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, SkRect::Make(testArea));
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400156 }
157 // If we're in ref mode we will create a temp image that has the original image
158 // tiled into it and then do a clamp blur with adjusted params that should produce
159 // the same result as the original params.
160 std::unique_ptr<GrRenderTargetContext> refSrc;
161 SkIRect refRect;
162 if (ref) {
163 // Blow up testArea into a much larger rect so that our clamp blur will not
164 // reach anywhere near the edge of our temp surface.
165 refRect = testArea.makeOutset(testArea.width(), testArea.height());
166 refSrc = GrRenderTargetContext::Make(ctx, GrColorType::kRGBA_8888, nullptr,
167 SkBackingFit::kApprox, refRect.size());
168 refSrc->clear(SK_PMColor4fWHITE);
169 // Setup an effect to put the original src rect at the correct logical place
170 // in the temp where the temp's origin is at the top left of refRect.
Mike Reed1f607332020-05-21 12:11:27 -0400171 SkMatrix tm = SkMatrix::Translate(refRect.left(), refRect.top());
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400172 auto fp = GrTextureEffect::MakeSubset(src, kPremul_SkAlphaType, tm, sampler,
173 SkRect::Make(srcRect), caps);
174 GrPaint paint;
175 paint.addColorFragmentProcessor(std::move(fp));
Michael Ludwig7c12e282020-05-29 09:54:07 -0400176 refSrc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(),
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400177 SkRect::Make(refRect.size()));
178 }
179 // Do a blur for each dstRect in the set over our testArea-sized background.
180 for (const auto& dstRect : dstRectSet) {
181 // Setup the normal blur args.
182 const SkScalar sigmaX = src.width() / 10.f;
183 const SkScalar sigmaY = src.height() / 10.f;
184 auto blurSrc = src;
185 auto blurMode = mode;
186 auto blurDstRect = dstRect;
187 auto blurSrcRect = srcRect;
188 if (ref) {
189 // Move the dst rect to be relative to our temp surface.
190 blurDstRect = dstRect.makeOffset(-refRect.topLeft());
191 // Our new src is the entire temp containing the tiled original.
192 blurSrcRect = SkIRect::MakeSize(refRect.size());
193 // This shouldn't really matter because we should have made a large enough
194 // temp that the edges don't come into play. But this puts us on a simpler
195 // path through SkGpuBlurUtils.
196 blurMode = SkTileMode::kClamp;
197 blurSrc = refSrc->readSurfaceView();
Brian Salomon702c5a32020-04-28 16:21:48 -0400198 }
199 // Blur using the rect and draw on top.
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400200 if (auto blurView = blur(ctx, blurSrc, blurDstRect, blurSrcRect, sigmaX, sigmaY,
201 blurMode)) {
Brian Salomon702c5a32020-04-28 16:21:48 -0400202 auto fp = GrTextureEffect::Make(blurView, kPremul_SkAlphaType, SkMatrix::I(),
203 sampler, caps);
204 GrPaint paint;
205 // Compose against white (default paint color) and then replace the dst
206 // (SkBlendMode::kSrc).
207 fp = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(fp),
208 SkBlendMode::kSrcOver);
209 paint.addColorFragmentProcessor(std::move(fp));
210 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400211 rtc->fillRectToRect(nullptr, std::move(paint), GrAA::kNo, m,
Brian Salomon702c5a32020-04-28 16:21:48 -0400212 SkRect::Make(dstRect), SkRect::Make(blurView.dimensions()));
213 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400214 // Show the outline of the dst rect. Mostly for kDecal but also allows visual
215 // confirmation that the resulting blur is the right size and in the right place.
216 {
217 GrPaint paint;
218 static constexpr float kAlpha = 0.6f;
219 paint.setColor4f({0, kAlpha, 0, kAlpha});
220 SkPaint stroke;
221 stroke.setStyle(SkPaint::kStroke_Style);
222 stroke.setStrokeWidth(1.f);
223 GrStyle style(stroke);
224 auto dstR = SkRect::Make(dstRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400225 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, dstR, &style);
Brian Salomon702c5a32020-04-28 16:21:48 -0400226 }
Brian Salomon702c5a32020-04-28 16:21:48 -0400227 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400228 // Show the rect that's being blurred.
229 {
230 GrPaint paint;
231 static constexpr float kAlpha = 0.3f;
232 paint.setColor4f({0, 0, 0, kAlpha});
233 SkPaint stroke;
234 stroke.setStyle(SkPaint::kStroke_Style);
235 stroke.setStrokeWidth(1.f);
236 GrStyle style(stroke);
237 auto srcR = SkRect::Make(srcRect).makeOutset(0.5f, 0.5f);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400238 rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, m, srcR, &style);
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400239 }
240 trans.fX += testArea.width() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400241 }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400242 trans.fX = kPad;
243 trans.fY += testArea.height() + kPad;
Brian Salomon702c5a32020-04-28 16:21:48 -0400244 }
245}
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400246
Brian Salomon287d5982020-05-19 14:24:43 -0400247DEF_SIMPLE_GPU_GM(gpu_blur_utils, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, false); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400248
Brian Salomon287d5982020-05-19 14:24:43 -0400249DEF_SIMPLE_GPU_GM(gpu_blur_utils_ref, ctx, rtc, canvas, 765, 955) { run(ctx, rtc, false, true); }
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400250
Brian Salomon287d5982020-05-19 14:24:43 -0400251DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_rect, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400252 run(ctx, rtc, true, false);
253}
254
Brian Salomon287d5982020-05-19 14:24:43 -0400255DEF_SIMPLE_GPU_GM(gpu_blur_utils_subset_ref, ctx, rtc, canvas, 485, 730) {
Brian Salomon11ad4cc2020-05-15 12:07:59 -0400256 run(ctx, rtc, true, true);
257}