From fa3bce5fbcf5640b1414c10ef93c57d4db41793e Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 2 Apr 2020 18:07:40 -0700 Subject: [PATCH] All parameters automatically tuned --- src/distfield.rs | 25 +++++++++++++------------ src/image.rs | 2 +- src/main.rs | 8 ++++++-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/distfield.rs b/src/distfield.rs index eed8bab..dfa395a 100644 --- a/src/distfield.rs +++ b/src/distfield.rs @@ -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 { - // 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 { + // 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; } diff --git a/src/image.rs b/src/image.rs index 28d83a7..325287b 100644 --- a/src/image.rs +++ b/src/image.rs @@ -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 diff --git a/src/main.rs b/src/main.rs index 2569580..9bfc796 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 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 { .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(); -- 2.38.5