~raph/blurrr

fa3bce5fbcf5640b1414c10ef93c57d4db41793e — Raph Levien 1 year, 6 months ago 7bedad9 master
All parameters automatically tuned
3 files changed, 20 insertions(+), 15 deletions(-)

M src/distfield.rs
M src/image.rs
M src/main.rs
M src/distfield.rs => src/distfield.rs +13 -12
@@ 1,39 1,40 @@
//! Approximate solution based on distance fields.

use std::f64::consts::{FRAC_1_SQRT_2, LN_2};
use crate::math::compute_erf7;

/// Generate a blurred rounded rectangle using distance field approximation.
pub fn gen_distfield(width: usize, height: usize, w: f64, h: f64, r: f64, s: f64, r_mul: f64, exponent: f64) -> Vec<u8> {
    // Try to calculate parameters
    let r_orig = r;
pub fn gen_distfield(width: usize, height: usize, w: f64, h: f64, r: f64, s: f64) -> Vec<u8> {
    // To avoid divide by 0; potentially should be a bigger number for antialiasing.
    let s = s.max(1e-6);

    let min_edge = w.min(h);
    let rmax = 0.5 * min_edge;
    let r0 = r.hypot(s * 1.15).min(rmax);
    let r1 = r.hypot(s * 2.0).min(rmax);

    //let exponent = (-LN_2) / (1.0 - (1.0 - FRAC_1_SQRT_2) * r0 / r1).ln();
    //let exponent = 6.0 * r1 / r0 - 4.0;
    let exponent = 2.0 * r1 / r0;
    println!("r1/r {}, exponent {}", r1 / r, exponent);
    let r = r1;

    let s_inv = s.max(1e-6).recip();

    // Pull in long end (make less eccentric).
    let delta = 1.25 * s * ((-(0.5 * s_inv * w).powi(2)).exp() - (-(0.5 * s_inv * h).powi(2)).exp());
    let w = w + delta.min(0.0);
    let h = h - delta.max(0.0);

    let recip_exponent = exponent.recip();
    let mut buf = vec![0u8; width * height];
    let scale = 0.5 * compute_erf7(s_inv * 0.5 * (w.max(h) - 0.5 * r_orig));
    let scale = 0.5 * compute_erf7(s_inv * 0.5 * (w.max(h) - 0.5 * r));
    for j in 0..height {
        let y = (j as f64) + 0.5 - 0.5 * (height as f64);
        let y0 = y.abs() - (h * 0.5 - r);
        let y0 = y.abs() - (h * 0.5 - r1);
        let y1 = y0.max(0.0);
        for i in 0..width {
            let x = (i as f64) + 0.5 - 0.5 * (width as f64);
            let x0 = x.abs() - (w * 0.5 - r);
            let x0 = x.abs() - (w * 0.5 - r1);
            let x1 = x0.max(0.0);
            let d_pos = (x1.powf(exponent) + y1.powf(exponent)).powf(recip_exponent);
            let d_neg = x0.max(y0).min(0.0);
            let d = d_pos + d_neg - r;
            let d = d_pos + d_neg - r1;
            let z = scale * (compute_erf7(s_inv * (min_edge + d)) - compute_erf7(s_inv * d));
            buf[j * width + i] = (z * 255.0).round() as u8;
        }

M src/image.rs => src/image.rs +1 -1
@@ 14,7 14,7 @@ pub fn make_image_one(ctx: &mut PaintCtx, w: usize, h: usize, data: &[u8]) -> Im
    ctx.make_image(w, h, &buf, ImageFormat::RgbaPremul).unwrap()
}

const N_STEPS: f64 = 255.0;
const N_STEPS: f64 = 6.0;

fn quantize(b: u8) -> u8 {
    ((b as f64 * (N_STEPS / 255.0)).round() * (255.0 / N_STEPS)).round() as u8

M src/main.rs => src/main.rs +6 -2
@@ 15,9 15,11 @@ struct AppState {
    radius: f64,
    std_dev: f64,

    /*
    // extra parameters for distance field renderer
    r_mul: f64,
    exponent: f64,
    */
}

struct BlurWidget;


@@ 59,8 61,6 @@ impl Widget<AppState> for BlurWidget {
            data.height,
            radius,
            data.std_dev,
            data.r_mul,
            data.exponent,
        );
        let d1 = integration::gen_integrate(
            IM_WIDTH,


@@ 88,10 88,12 @@ fn ui_builder() -> impl Widget<AppState> {
        .with_child(Slider::new().with_range(0.0, 50.0).lens(AppState::radius))
        .with_spacer(5.0)
        .with_child(Slider::new().with_range(0.0, 50.0).lens(AppState::std_dev))
        /*
        .with_spacer(5.0)
        .with_child(Slider::new().with_range(1.0, 5.0).lens(AppState::r_mul))
        .with_spacer(5.0)
        .with_child(Slider::new().with_range(2.0, 10.0).lens(AppState::exponent))
        */
        .with_spacer(5.0)
        .with_flex_child(BlurWidget, 1.0)
}


@@ 103,8 105,10 @@ fn main() {
        radius: 5.0,
        std_dev: 5.0,

        /*
        r_mul: 1.0,
        exponent: 2.0,
        */
    };
    let main_window = WindowDesc::new(ui_builder).title(LocalizedString::new("blur toy"));
    AppLauncher::with_window(main_window).launch(data).unwrap();