Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
kawase_blur_rt.cpp
Go to the documentation of this file.
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"
10#include "include/core/SkData.h"
12#include "include/core/SkSize.h"
16#include "tools/DecodeUtils.h"
17#include "tools/Resources.h"
18
20public:
21 // Downsample scale factor used to improve performance
22 static constexpr float kInputScale = 0.25f;
23 // Downsample scale factor used to improve performance
24 static constexpr float kInverseInputScale = 1.0f / kInputScale;
25 // Maximum number of render passes
26 static constexpr uint32_t kMaxPasses = 4;
27 // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
28 // image, up to this radius.
29 static constexpr float kMaxCrossFadeRadius = 30.0f;
30
32 SkString blurString(R"(
33 uniform shader src;
34 uniform float in_inverseScale;
35 uniform float2 in_blurOffset;
36
37 half4 main(float2 xy) {
38 float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
39
40 half4 c = src.eval(scaled_xy);
41 c += src.eval(scaled_xy + float2( in_blurOffset.x, in_blurOffset.y));
42 c += src.eval(scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y));
43 c += src.eval(scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y));
44 c += src.eval(scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y));
45
46 return half4(c.rgb * 0.2, 1.0);
47 }
48 )");
49
50 SkString mixString(R"(
51 uniform shader in_blur;
52 uniform shader in_original;
53 uniform float in_inverseScale;
54 uniform float in_mix;
55
56 half4 main(float2 xy) {
57 float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
58
59 half4 blurred = in_blur.eval(scaled_xy);
60 half4 composition = in_original.eval(xy);
61 return mix(composition, blurred, in_mix);
62 }
63 )");
64
65 auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
66 if (!blurEffect) {
67 SkDEBUGFAILF("RuntimeShader error: %s\n", error.c_str());
68 }
69 fBlurEffect = std::move(blurEffect);
70
71 auto [mixEffect, error2] = SkRuntimeEffect::MakeForShader(mixString);
72 if (!mixEffect) {
73 SkDEBUGFAILF("RuntimeShader error: %s\n", error2.c_str());
74 }
75 fMixEffect = std::move(mixEffect);
76 }
77
80 return surface;
81 }
82 // serialize-8888 returns null from makeSurface; fallback to a raster surface.
84 }
85
86 void draw(SkCanvas* canvas, sk_sp<SkImage> input, int blurRadius) {
87 // NOTE: this is only experimental and the current blur params cause points to be sampled
88 // beyond the input blur radius.
89
90 // Kawase is an approximation of Gaussian, but it behaves differently from it.
91 // A radius transformation is required for approximating them, and also to introduce
92 // non-integer steps, necessary to smoothly interpolate large radii.
93 float tmpRadius = (float)blurRadius / 6.0f;
94 float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
95 float radiusByPasses = tmpRadius / (float)numberOfPasses;
96
97 SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
98 (float)input->height() * kInputScale);
99 auto drawSurface = MakeSurface(canvas, scaledInfo);
100
101 const float stepX = radiusByPasses;
102 const float stepY = radiusByPasses;
103
104 // start by drawing and downscaling and doing the first blur pass
105 SkRuntimeShaderBuilder blurBuilder(fBlurEffect);
106 blurBuilder.child("src") = input->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
107 blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
108 blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInverseInputScale,
109 stepY * kInverseInputScale};
111 paint.setShader(blurBuilder.makeShader());
112 drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
113
114 // DEBUG draw each of the stages
115 canvas->save();
116 canvas->drawImage(drawSurface->makeImageSnapshot(), input->width() / 4, 0,
118 canvas->translate(input->width() / 4, input->height() * 0.75);
119
120 // And now we'll ping pong between our surfaces, to accumulate the result of various
121 // offsets.
122 auto lastDrawTarget = drawSurface;
123 if (numberOfPasses > 1) {
124 auto readSurface = drawSurface;
125 drawSurface = MakeSurface(canvas, scaledInfo);
126
127 for (auto i = 1; i < numberOfPasses; i++) {
128 const float stepScale = (float)i * kInputScale;
129
130 blurBuilder.child("src") = readSurface->makeImageSnapshot()->makeShader(
132 blurBuilder.uniform("in_inverseScale") = 1.0f;
133 blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale , stepY * stepScale};
134
135 paint.setShader(blurBuilder.makeShader());
136 drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
137
138 // DEBUG draw each of the stages
139 canvas->drawImage(drawSurface->makeImageSnapshot(), 0, 0, SkSamplingOptions());
140 canvas->translate(0, input->height() * 0.75);
141
142 // Swap buffers for next iteration
143 auto tmp = drawSurface;
144 drawSurface = readSurface;
145 readSurface = tmp;
146 }
147 lastDrawTarget = readSurface;
148 }
149
150 // restore translations done for debug and offset
151 canvas->restore();
152 SkAutoCanvasRestore acr(canvas, true);
153 canvas->translate(input->width(), 0);
154
155 // do the final composition and when we scale our blur up. It will be interpolated
156 // with the larger composited texture to hide downscaling artifacts.
157 SkRuntimeShaderBuilder mixBuilder(fMixEffect);
158 mixBuilder.child("in_blur") = lastDrawTarget->makeImageSnapshot()->makeShader(
160 mixBuilder.child("in_original") =
161 input->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
162 mixBuilder.uniform("in_inverseScale") = kInputScale;
163 mixBuilder.uniform("in_mix") = std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius);
164
165 paint.setShader(mixBuilder.makeShader());
166 canvas->drawIRect(input->bounds(), paint);
167 }
168
169private:
170 sk_sp<SkRuntimeEffect> fBlurEffect;
171 sk_sp<SkRuntimeEffect> fMixEffect;
172};
173
174class KawaseBlurRT : public skiagm::GM {
175public:
177 SkString getName() const override { return SkString("kawase_blur_rt"); }
178 SkISize getISize() override { return {1280, 768}; }
179
180 void onOnceBeforeDraw() override {
181 fMandrill = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
182 }
183
184 void onDraw(SkCanvas* canvas) override {
185 canvas->drawImage(fMandrill, 0, 0);
186 canvas->translate(256, 0);
187 KawaseBlurFilter blurFilter;
188 blurFilter.draw(canvas, fMandrill, 45);
189 canvas->translate(512, 0);
190 blurFilter.draw(canvas, fMandrill, 55);
191 }
192
193private:
194 sk_sp<SkImage> fMandrill;
195};
196DEF_GM(return new KawaseBlurRT;)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define SkDEBUGFAILF(fmt,...)
Definition SkAssert.h:119
static constexpr float kInverseInputScale
static constexpr float kMaxCrossFadeRadius
static constexpr float kInputScale
static constexpr uint32_t kMaxPasses
static sk_sp< SkSurface > MakeSurface(SkCanvas *canvas, const SkImageInfo &info)
void draw(SkCanvas *canvas, sk_sp< SkImage > input, int blurRadius)
SkString getName() const override
void onDraw(SkCanvas *canvas) override
void onOnceBeforeDraw() override
SkISize getISize() override
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
sk_sp< SkSurface > makeSurface(const SkImageInfo &info, const SkSurfaceProps *props=nullptr)
void drawIRect(const SkIRect &rect, const SkPaint &paint)
Definition SkCanvas.h:1358
int save()
Definition SkCanvas.cpp:451
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
BuilderUniform uniform(std::string_view name)
BuilderChild child(std::string_view name)
static Result MakeForShader(SkString sksl, const Options &)
sk_sp< SkShader > makeShader(const SkMatrix *localMatrix=nullptr) const
const Paint & paint
VkSurfaceKHR surface
Definition main.cc:49
const uint8_t uint32_t uint32_t GError ** error
#define DEF_GM(CODE)
Definition gm.h:40
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition DecodeUtils.h:25
static SkImageInfo MakeN32Premul(int width, int height)
SkIRect bounds() const
Definition SkM44.h:19