class GaussianBlurGenerator

Constants

Values

Public Class Methods

generate(epsilon) { |values| ... } click to toggle source
# File lib/gaussian_blur_generator/base.rb, line 4
def self.generate(epsilon)
  row_number = 0
  last_size = 0

  loop do
    row_number += 2

    row = pascal(row_number)
    sum = row.sum
    peak = row[row.size / 2].to_d / sum

    # Remove weights that make negligible difference to the output image.
    row = row.reject { |n| n.to_d / sum < peak * epsilon }
    sum = row.sum

    # If we've already seen a row of this size, skip those that are more truncated.
    next if row.size == last_size
    last_size = row.size

    midpoint = row.size / 2
    next if midpoint.odd?

    weights_d = row.map { |n| n.to_d / sum }[midpoint..]
    offsets_d = (0..midpoint).map(&:to_d)

    break if weights_d[0] == 0 # Precision limit reached

    weights_l = weights_d[1..].each_slice(2).map(&:sum)
    offsets_l = offsets_d[1..].each_slice(2).map do |t1, t2|
      numerator = offsets_d[t1] * weights_d[t1] + offsets_d[t2] * weights_d[t2]
      denominator = weights_d[t1] + weights_d[t2]

      numerator / denominator
    end

    weights_l.unshift(weights_d.first)
    offsets_l.unshift(0.0)
    texture_reads = offsets_l.size * 2 - 1

    yield Values.new(offsets_d, weights_d, offsets_l, weights_l, texture_reads, row.size, row_number)
  end
end
glsl(values, axis) click to toggle source
# File lib/gaussian_blur_generator/glsl.rb, line 2
  def self.glsl(values, axis)
    offsets = values.offsets_l[1..].map.with_index do |offset, index|
      "float offset#{index + 1} = one_pixel * #{offset.to_f};"
    end

    if axis == "x"
      template = "coords.x + offset@, coords.y"
    else
      template = "coords.x, coords.y + offset@"
    end

    positives = values.weights_l[1..].map.with_index do |weight, index|
      "#{weight.to_f} * texture2D(u_texture, vec2(#{template.sub("@", (index + 1).to_s)}))"
    end

    negatives = positives.map { |s| s.sub("+", "-") }

    summation = negatives.reverse + [
      "#{values.weights_l[0].to_f} * texture2D(u_texture, coords)"
    ] + positives

    <<~GLSL
      #ifdef GL_FRAGMENT_PRECISION_HIGH
        precision highp float;
      #else
        precision mediump float;
      #endif

      uniform sampler2D u_texture;
      uniform vec4 u_scaling;

      // Generated by https://github.com/tuzz/gaussian_blur_generator

      void main() {
        vec2 coords = gl_FragCoord.xy * u_scaling.xy;

        float one_pixel = u_scaling.#{axis};
        #{offsets.join("\n  ")}

        gl_FragColor = (
          #{summation.join(" +\n    ")}
        );
      }
    GLSL
  end
pascal(n) click to toggle source
# File lib/gaussian_blur_generator/base.rb, line 47
def self.pascal(n)
  @cache ||= {}
  @cache[n] ||= (n == 0 ? [1] : [1, *pascal(n - 1).each_cons(2).map(&:sum), 1])
end