| // 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); |
| } |
| } |