~raph/blurrr

ref: 7bedad9838e0cf186f70f7351575e7560544ee78 blurrr/src/distfield.rs -rw-r--r-- 1.6 KiB
7bedad98Raph Levien Automatic computation of radius and exponent 1 year, 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//! 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;

    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();
    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));
    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 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 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 z = scale * (compute_erf7(s_inv * (min_edge + d)) - compute_erf7(s_inv * d));
            buf[j * width + i] = (z * 255.0).round() as u8;
        }
    }
    buf
}