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();