30 Scalar scalar = 1.0 - 3.4e-3 * clamped + 3.4e-06 * clamped * clamped;
31 return Sigma(clamped * scalar);
45 secondary_blur_sigma_ = sigma;
50 if (blur_direction_.
IsZero()) {
51 blur_direction_ =
Vector2(0, 1);
56 blur_style_ = blur_style;
61 tile_mode_ = tile_mode;
65 bool is_second_pass) {
66 is_second_pass_ = is_second_pass;
69 std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
73 const Matrix& effect_transform,
75 const std::optional<Rect>& coverage_hint)
const {
87 auto radius = Radius{
ScaleSigma(blur_sigma_)}.radius;
90 auto transformed_blur_radius =
93 auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
97 std::optional<Rect> expanded_coverage_hint;
98 if (coverage_hint.has_value()) {
100 Point(transformed_blur_radius_length, transformed_blur_radius_length)
102 expanded_coverage_hint =
103 is_second_pass_ ? coverage_hint : coverage_hint->Expand(r);
105 auto input_snapshot = inputs[0]->GetSnapshot(
"GaussianBlur", renderer, entity,
106 expanded_coverage_hint);
107 if (!input_snapshot.has_value()) {
118 if (transformed_blur_radius_length < .5) {
126 transformed_blur_radius.Normalize().AngleTo({1, 0}));
130 auto pass_transform = texture_rotate * input_snapshot->transform;
136 auto pass_texture_rect =
Rect::MakeSize(input_snapshot->texture->GetSize())
138 .
Expand(transformed_blur_radius_length, 0);
142 auto pass_uv_project = [&texture_rotate,
143 &pass_texture_rect](Snapshot& input) {
145 (texture_rotate * input.transform).Invert();
146 return pass_texture_rect.GetTransformedPoints(uv_matrix);
149 auto input_uvs = pass_uv_project(input_snapshot.value());
158 auto& host_buffer = pass.GetTransientsBuffer();
160 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
161 vtx_builder.AddVertices({
162 {
Point(0, 0), input_uvs[0]},
163 {
Point(1, 0), input_uvs[1]},
164 {
Point(0, 1), input_uvs[2]},
165 {
Point(1, 1), input_uvs[3]},
168 VS::FrameInfo frame_info;
170 frame_info.texture_sampler_y_coord_scale =
171 input_snapshot->texture->GetYCoordScale();
173 FS::BlurInfo frag_info;
174 auto r = Radius{transformed_blur_radius_length};
175 frag_info.blur_sigma = Sigma{r}.sigma;
176 frag_info.blur_radius = std::round(r.radius);
177 frag_info.step_size = 2.0;
180 frag_info.blur_uv_offset =
181 pass_transform.Invert().TransformDirection(
Vector2(1, 0)).Normalize() /
182 Point(input_snapshot->GetCoverage().value().GetSize());
186 transformed_blur_radius_length));
187 cmd.BindVertices(vtx_builder.CreateVertexBuffer(host_buffer));
192 auto input_descriptor = input_snapshot->sampler_descriptor;
193 switch (tile_mode_) {
195 if (renderer.GetDeviceCapabilities()
196 .SupportsDecalSamplerAddressMode()) {
217 bool has_decal_specialization =
219 !renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
221 if (has_decal_specialization) {
222 cmd.pipeline = renderer.GetGaussianBlurDecalPipeline(options);
224 cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
227 FS::BindTextureSampler(
228 cmd, input_snapshot->texture,
229 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
231 VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
232 FS::BindBlurInfo(cmd, host_buffer.EmplaceUniform(frag_info));
234 return pass.AddCommand(std::move(cmd));
238 auto scale_curve = [](
Scalar radius) {
239 constexpr
Scalar decay = 4.0;
240 constexpr
Scalar limit = 0.95;
242 std::min(1.0, decay / (std::max(1.0f, radius) + decay - 1.0));
243 return (curve - 1) * limit + 1;
246 scale.x = scale_curve(transformed_blur_radius_length);
248 Scalar y_radius = std::abs(pass_transform.GetDirectionScale(
Vector2(
249 0, !is_second_pass_ ? 1 : Radius{secondary_blur_sigma_}.radius)));
250 scale.y = scale_curve(y_radius);
253 Vector2 scaled_size = pass_texture_rect.GetSize() * scale;
254 ISize floored_size =
ISize(scaled_size.x, scaled_size.y);
256 fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
257 "Directional Gaussian Blur Filter", floored_size, subpass_callback);
259 if (!render_target.ok()) {
263 SamplerDescriptor sampler_desc;
271 .texture = render_target.value().GetRenderTargetTexture(),
273 texture_rotate.Invert() *
276 .sampler_descriptor = sampler_desc,
277 .opacity = input_snapshot->opacity},
283 const Matrix& effect_transform,
284 const Rect& output_limit)
const {
285 auto transform = effect_transform.
Basis();
286 auto transformed_blur_vector =
289 return output_limit.
Expand(transformed_blur_vector);
295 const Matrix& effect_transform)
const {
296 if (inputs.empty()) {
300 auto coverage = inputs[0]->GetCoverage(entity);
301 if (!coverage.has_value()) {
305 auto transform = inputs[0]->GetTransform(entity) * effect_transform.
Basis();
306 auto transformed_blur_vector =
311 return coverage->
Expand(transformed_blur_vector);