## ~raph/blurrr

fa3bce5fbcf5640b1414c10ef93c57d4db41793e — Raph Levien 4 years ago
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 {
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,
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_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() {