M src/distfield.rs => src/distfield.rs +13 -6
@@ 2,14 2,21 @@
use crate::math::compute_erf7;
-const EXPONENT: f64 = 2.0;
-const RECIP_EXPONENT: f64 = 1.0 / EXPONENT;
-
/// 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) -> Vec<u8> {
+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 r = r.hypot(s * 1.25);
+
+ let r = r * r_mul;
+
+ let r = r.min(0.5 * w.min(h));
+
let s_inv = s.max(1e-6).recip();
+ let recip_exponent = exponent.recip();
let mut buf = vec![0u8; width * height];
let min_edge = w.min(h);
+ 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);
@@ 18,10 25,10 @@ pub fn gen_distfield(width: usize, height: usize, w: f64, h: f64, r: f64, s: f64
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_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 = 0.5 * (compute_erf7(s_inv * (min_edge + d)) - compute_erf7(s_inv * d));
+ 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 +2 -2
@@ 17,8 17,8 @@ pub fn make_image_one(ctx: &mut PaintCtx, w: usize, h: usize, data: &[u8]) -> Im
pub fn make_image_two(ctx: &mut PaintCtx, w: usize, h: usize, d0: &[u8], d1: &[u8]) -> Image {
let mut buf = vec![255u8; w * h * 4];
for i in 0..(w * h) {
- let r = d0[i];
- let g = d1[i];
+ let r = d0[i] ^ 0;
+ let g = d1[i] ^ 0;
buf[i * 4] = r;
buf[i * 4 + 1] = g;
buf[i * 4 + 2] = g;
M src/main.rs => src/main.rs +16 -2
@@ 14,6 14,10 @@ struct AppState {
height: f64,
radius: f64,
std_dev: f64,
+
+ // extra parameters for distance field renderer
+ r_mul: f64,
+ exponent: f64,
}
struct BlurWidget;
@@ 55,6 59,8 @@ impl Widget<AppState> for BlurWidget {
data.height,
radius,
data.std_dev,
+ data.r_mul,
+ data.exponent,
);
let d1 = integration::gen_integrate(
IM_WIDTH,
@@ 65,8 71,9 @@ impl Widget<AppState> for BlurWidget {
data.std_dev,
);
let image = image::make_image_two(ctx, IM_WIDTH, IM_HEIGHT, &d0, &d1);
- let rect = Size::new(IM_WIDTH as f64, IM_HEIGHT as f64).to_rect();
- ctx.draw_image(&image, rect, InterpolationMode::Bilinear);
+ let scale = (ctx.size().width / IM_WIDTH as f64).min(ctx.size().height / IM_HEIGHT as f64);
+ let rect = (Size::new(IM_WIDTH as f64, IM_HEIGHT as f64) * scale).to_rect();
+ ctx.draw_image(&image, rect, InterpolationMode::NearestNeighbor);
}
}
@@ 81,6 88,10 @@ 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)
}
@@ 90,6 101,9 @@ fn main() {
height: 80.0,
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();