// 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.

// 1D (directional) gaussian blur.
//
// Paths for future optimization:
//   * Remove the uv bounds multiplier in SampleColor by adding optional
//     support for SamplerAddressMode::ClampToBorder in the texture sampler.
//   * Render both blur passes into a smaller texture than the source image
//     (~1/radius size).
//   * If doing the small texture render optimization, cache misses can be
//     reduced in the first pass by sampling the source textures with a mip
//     level of log2(min_radius).

#include <impeller/constants.glsl>
#include <impeller/gaussian.glsl>
#include <impeller/texture.glsl>
#include <impeller/types.glsl>

uniform f16sampler2D texture_sampler;

uniform BlurInfo {
  f16vec2 blur_uv_offset;

  // The blur sigma and radius have a linear relationship which is defined
  // host-side, but both are useful controls here. Sigma (pixels per standard
  // deviation) is used to define the gaussian function itself, whereas the
  // radius is used to limit how much of the function is integrated.
  float blur_sigma;
  float16_t blur_radius;
}
blur_info;

#if ENABLE_ALPHA_MASK
uniform f16sampler2D alpha_mask_sampler;

uniform MaskInfo {
  float16_t src_factor;
  float16_t inner_blur_factor;
  float16_t outer_blur_factor;
}
mask_info;
#endif

f16vec4 Sample(f16sampler2D tex, vec2 coords) {
#if ENABLE_DECAL_SPECIALIZATION
  return IPHalfSampleDecal(tex, coords);
#else
  return texture(tex, coords);
#endif
}

in vec2 v_texture_coords;
in vec2 v_src_texture_coords;

out f16vec4 frag_color;

void main() {
  f16vec4 total_color = f16vec4(0.0hf);
  float16_t gaussian_integral = 0.0hf;

  for (float16_t i = -blur_info.blur_radius; i <= blur_info.blur_radius;
       i += 2.0hf) {
    // Use the 32 bit Gaussian function because the 16 bit variation results in
    // quality loss/visible banding. Also, 16 bit variation internally breaks
    // down at a moderately high (but still reasonable) blur sigma of >255 when
    // computing sigma^2 due to the exponent only having 5 bits.
    float16_t gaussian = float16_t(IPGaussian(float(i), blur_info.blur_sigma));
    gaussian_integral += gaussian;
    total_color +=
        gaussian * Sample(texture_sampler,  // sampler
                          v_texture_coords + blur_info.blur_uv_offset *
                                                 i  // texture coordinates
                   );
  }

  frag_color = total_color / gaussian_integral;

#if ENABLE_ALPHA_MASK
  f16vec4 src_color = Sample(alpha_mask_sampler,   // sampler
                             v_src_texture_coords  // texture coordinates
  );

  float16_t blur_factor;
  if (src_color.a > 0.0hf) {
    blur_factor = mask_info.inner_blur_factor;
  } else if (src_color.a == 0.0hf) {
    blur_factor = mask_info.outer_blur_factor;
  }

  frag_color = frag_color * blur_factor + src_color * mask_info.src_factor;
#endif
}
