~raph/blurrr

cde0387b1110df44dc0e9b752d4ca6d2e4b84261 — Raph Levien 1 year, 8 months ago 6cc5ee0
First cut of distance field renderer
4 files changed, 54 insertions(+), 13 deletions(-)

A src/distfield.rs
M src/image.rs
M src/integration.rs
M src/main.rs
A src/distfield.rs => src/distfield.rs +29 -0
@@ 0,0 1,29 @@
//! Approximate solution based on distance fields.

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> {
    let s_inv = s.max(1e-6).recip();
    let mut buf = vec![0u8; width * height];
    let min_edge = w.min(h);
    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 = 0.5 * (compute_erf7(s_inv * (min_edge + d)) - compute_erf7(s_inv * d));
            buf[j * width + i] = (z * 255.0).round() as u8;
        }
    }
    buf
}

M src/image.rs => src/image.rs +13 -0
@@ 2,6 2,7 @@ use druid::piet::{Image, ImageFormat};
use druid::{PaintCtx, RenderContext};

// Make a grayscale image from a single intensity map.
#[allow(unused)]
pub fn make_image_one(ctx: &mut PaintCtx, w: usize, h: usize, data: &[u8]) -> Image {
    let mut buf = vec![255u8; w * h * 4];
    for i in 0..(w * h) {


@@ 12,3 13,15 @@ pub fn make_image_one(ctx: &mut PaintCtx, w: usize, h: usize, data: &[u8]) -> Im
    }
    ctx.make_image(w, h, &buf, ImageFormat::RgbaPremul).unwrap()
}

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];
        buf[i * 4] = r;
        buf[i * 4 + 1] = g;
        buf[i * 4 + 2] = g;
    }
    ctx.make_image(w, h, &buf, ImageFormat::RgbaPremul).unwrap()
}

M src/integration.rs => src/integration.rs +0 -1
@@ 26,7 26,6 @@ pub fn gen_integrate(width: usize, height: usize, w: f64, h: f64, r: f64, s: f64
                    line[i] += compute_erf7((xmax - x) * s_inv) + compute_erf7((xmax + x) * s_inv);
                }
            }

        }
        let off0 = (j + height / 2) * width;
        let off1 = (height / 2 - j - 1) * width;

M src/main.rs => src/main.rs +12 -12
@@ 1,10 1,9 @@
use druid::piet::InterpolationMode;
use druid::widget::prelude::*;
use druid::widget::{Flex, Slider};
use druid::{
    AppLauncher, Color, Data, Lens, LocalizedString, Point, Rect, Widget, WidgetExt, WindowDesc,
};
use druid::{AppLauncher, Data, Lens, LocalizedString, Widget, WidgetExt, WindowDesc};

mod distfield;
mod image;
mod integration;
mod math;


@@ 48,15 47,16 @@ impl Widget<AppState> for BlurWidget {
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &AppState, _env: &Env) {
        // Let's draw a picture with Piet!

        // Clear the whole widget with the color of your choice
        // (ctx.size() returns the size of the layout rect we're painting in)
        let size = ctx.size();
        let rect = Rect::from_origin_size(Point::ORIGIN, size);
        //ctx.fill(rect.to_rounded_rect(data.radius), &Color::WHITE);
        let radius = data.radius.min(0.5 * data.width.min(data.height));
        let data = integration::gen_integrate(
        let d0 = distfield::gen_distfield(
            IM_WIDTH,
            IM_HEIGHT,
            data.width,
            data.height,
            radius,
            data.std_dev,
        );
        let d1 = integration::gen_integrate(
            IM_WIDTH,
            IM_HEIGHT,
            data.width,


@@ 64,7 64,7 @@ impl Widget<AppState> for BlurWidget {
            radius,
            data.std_dev,
        );
        let image = image::make_image_one(ctx, IM_WIDTH, IM_HEIGHT, &data);
        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);
    }