blob: 4c018bf3ad3679cba523b5c743c502fc67cabf11 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Constant time mask blur for image borders.
//
// This mask blur extends the geometry of the source image (with clamp border
// sampling) and applies a Gaussian blur to the alpha mask at the edges.
//
// The blur itself works by mapping the Gaussian distribution's indefinite
// integral (using an erf approximation) to the 4 edges of the UV rectangle and
// multiplying them.
uniform sampler2D texture_sampler;
in vec2 v_texture_coords;
in vec2 v_sigma_uv;
in float v_src_factor;
in float v_inner_blur_factor;
in float v_outer_blur_factor;
out vec4 frag_color;
// Abramowitz and Stegun erf approximation.
float erf(float x) {
float a = abs(x);
// 0.278393*x + 0.230389*x^2 + 0.078108*x^4 + 1
float b = (0.278393 + (0.230389 + 0.078108 * a * a) * a) * a + 1.0;
return sign(x) * (1 - 1 / (b * b * b * b));
}
const float kHalfSqrtTwo = 0.70710678118;
// Indefinite integral of the Gaussian function (with constant range 0->1).
float GaussianIntegral(float x, float sigma) {
// ( 1 + erf( x * (sqrt(2) / (2 * sigma) ) ) / 2
// Because this sigmoid is always > 1, we remap it (n * 1.07 - 0.07)
// so that it always fades to zero before it reaches the blur radius.
return 0.535 * erf(x * (kHalfSqrtTwo / sigma)) + 0.465;
}
float BoxBlurMask(vec2 uv) {
// LTRB
return GaussianIntegral(uv.x, v_sigma_uv.x) * //
GaussianIntegral(uv.y, v_sigma_uv.y) * //
GaussianIntegral(1 - uv.x, v_sigma_uv.x) * //
GaussianIntegral(1 - uv.y, v_sigma_uv.y);
}
void main() {
vec4 image_color = texture(texture_sampler, v_texture_coords);
float blur_factor = BoxBlurMask(v_texture_coords);
float within_bounds =
float(v_texture_coords.x >= 0 && v_texture_coords.y >= 0 &&
v_texture_coords.x < 1 && v_texture_coords.y < 1);
float inner_factor =
(v_inner_blur_factor * blur_factor + v_src_factor) * within_bounds;
float outer_factor = v_outer_blur_factor * blur_factor * (1 - within_bounds);
float mask_factor = inner_factor + outer_factor;
frag_color = image_color * mask_factor;
}