blob: 7a82a57805e63597b9e33ddc31af65e410f24834 [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.
#include <impeller/gaussian.glsl>
#include <impeller/types.glsl>
uniform FragInfo {
vec4 color;
float blur_sigma;
vec2 rect_size;
float corner_radius;
}
frag_info;
in vec2 v_position;
out vec4 frag_color;
const int kSampleCount = 5;
float RRectDistance(vec2 sample_position, vec2 half_size) {
vec2 space = abs(sample_position) - half_size + frag_info.corner_radius;
return length(max(space, 0.0)) + min(max(space.x, space.y), 0.0) -
frag_info.corner_radius;
}
/// Closed form unidirectional rounded rect blur mask solution using the
/// analytical Gaussian integral (with approximated erf).
float RRectShadowX(vec2 sample_position, vec2 half_size) {
// Compute the X direction distance field (not incorporating the Y distance)
// for the rounded rect.
float space =
min(0, half_size.y - frag_info.corner_radius - abs(sample_position.y));
float rrect_distance =
half_size.x - frag_info.corner_radius +
sqrt(max(0, frag_info.corner_radius * frag_info.corner_radius -
space * space));
// Map the linear distance field to the analytical Gaussian integral.
vec2 integral = IPVec2GaussianIntegral(
sample_position.x + vec2(-rrect_distance, rrect_distance),
frag_info.blur_sigma);
return integral.y - integral.x;
}
float RRectShadow(vec2 sample_position, vec2 half_size) {
// Limit the sampling range to 3 standard deviations in the Y direction from
// the kernel center to incorporate 99.7% of the color contribution.
float half_sampling_range = frag_info.blur_sigma * 3;
float begin_y = max(-half_sampling_range, sample_position.y - half_size.y);
float end_y = min(half_sampling_range, sample_position.y + half_size.y);
float interval = (end_y - begin_y) / kSampleCount;
// Sample the X blur kSampleCount times, weighted by the Gaussian function.
float result = 0;
for (int sample_i = 0; sample_i < kSampleCount; sample_i++) {
float y = begin_y + interval * (sample_i + 0.5);
result += RRectShadowX(vec2(sample_position.x, sample_position.y - y),
half_size) *
IPGaussian(y, frag_info.blur_sigma) * interval;
}
return result;
}
void main() {
frag_color = frag_info.color;
vec2 half_size = frag_info.rect_size * 0.5;
vec2 sample_position = v_position - half_size;
if (frag_info.blur_sigma > 0) {
frag_color *= RRectShadow(sample_position, half_size);
} else {
frag_color *= -RRectDistance(sample_position, half_size);
}
}