From 63d4a5cb8c6cca9cb76c74cc1f607c4c5bc8acda Mon Sep 17 00:00:00 2001 From: Constantin Berhard Date: Wed, 10 Aug 2016 20:44:36 +0200 Subject: [PATCH] u16 output format --- Cargo.lock | 2 +- Cargo.toml | 2 +- Readme.md | 15 +++++++++++++-- src/main.rs | 28 ++++++++++++++++++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2e759f..695f4f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "sdfgen" -version = "0.2.1" +version = "0.2.2" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index d938981..ce091c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sdfgen" -version = "0.2.2" +version = "0.3.0" authors = [ "Constantin Berhard " ] [[bin]] diff --git a/Readme.md b/Readme.md index 68df42b..7375ef2 100644 --- a/Readme.md +++ b/Readme.md @@ -38,8 +38,9 @@ You need Rust. The dependencies (image and getopts) are on crates.io so they wil --save-mipmaps BASENAME save the mipmaps used for accelerated calculation to BASENAMEi.png, where 'i' is the mipmap level - -t --type TYPE One of 'png', 'f32', 'f64'. f32 and f64 are raw - floating point formats. Default: png + -t, --type TYPE One of 'png', 'u16', 'f32', 'f64'. f32 and f64 are raw + floating point formats, u16 is raw unsigned 16 bit + integers. Default: png --threads THREADCOUNT How many CPU computing threads to use. @@ -70,3 +71,13 @@ With normal (bilinear) filtering it would have looked like this: But this is not all you can do with an SDF texture. Your pixel shader can also map the different distances to different colors. You can simulate this outside OpenGL with [Gimp](http://en.wikipedia.org/wiki/GIMP)s [Gradient Map](http://docs.gimp.org/en/plug-in-gradmap.html) tool (Colors -> Map -> To Gradient). ![artistic shader using SDF](http://cberhard.de/github/sdfgen/catsdfarts.jpg) + +### 16 Bit PNG + +Unfortunately the image library in use does not (yet?) support exporting PNG images with 16 bit color depth. +You can however use the output mode `u16` and convert it to a PNG16 using ImageMagick like so: + + convert -depth 16 -size 512x512 gray:sdf_file -depth 16 out.png + +For the `-size` parameter you must use the actual size of the output image. +This is e.g. useful to open the signed distance field in GIMP (version 2.9 or newer) for further manipulation. diff --git a/src/main.rs b/src/main.rs index 93d30b4..9f11f54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ fn main() { opts.optopt ("s","size","size of the output signed distance field image, must be a power of 2. Defaults to input size / 4","OUTPUT_SIZE"); opts.optopt ( "","maxdst","saturation distance (i.e. 'most far away meaningful distance') in half pixels of the input image. Defaults to input size / 4","SATURATION_DISTANCE"); opts.optopt ( "","save-mipmaps","save the mipmaps used for accelerated calculation to BASENAMEi.png, where 'i' is the mipmap level","BASENAME"); - opts.optopt ("t","type","One of 'png', 'f32', 'f64'. f32 and f64 are raw floating point formats. Default: png","TYPE"); + opts.optopt ("t","type","One of 'png', 'u16', 'f32', 'f64'. f32 and f64 are raw floating point formats, u16 is raw unsigned 16 bit integers. Default: png","TYPE"); opts.optopt ( "","threads","How many CPU computing threads to use.","THREADCOUNT"); if args.len() == 1 { print_usage(&program_name, &opts); @@ -95,7 +95,7 @@ fn main() { }; let n_threads : usize = match parsed_opts.opt_str("threads") { Some(s) => s.parse::().unwrap(), - None => 32, + None => 32, }; if verbose { println!("Calculating signed distance field of size {} with saturation distance {} using {} thread(s)", sdf_size, sat_dst, n_threads); @@ -107,7 +107,7 @@ fn main() { } let output_type = match parsed_opts.opt_str("type") { Some(s) => s, - None => format!("png") // FIXME we don't really need "format!" here + None => format!("png") // FIXME we don't really need "format!" here }; match output_type.as_ref() { "png" => { @@ -119,9 +119,29 @@ fn main() { let pngenc = image::png::PNGEncoder::::new(outf); let (w,h) = sdf_u8.dimensions(); pngenc.encode(sdf_u8.into_raw().as_ref(), w, h, image::ColorType::Gray(8)).unwrap(); - //sdf_u8.save(output_image_name).unwrap(); } // TODO: remove code duplication here + "u16" => { + let mut buf = vec![]; + for px in sdf.into_raw() { + let mut dst = px; + dst = dst / sat_dst * 32767_f64; + if dst < -32767_f64 { + dst = -32767_f64; + } else if dst > 32767_f64 { + dst = 32767_f64; + } + debug_assert!(dst <= 32767_f64); + debug_assert!(dst >= -32767_f64); + let v:u16 = (dst as i32 + 32767) as u16; + buf.write_u16::(v).unwrap(); + } + if verbose { + println!("Saving signed distance field image in u16 raw format as '{}'", output_image_name); + } + let mut outf = File::create(output_image_name).unwrap(); + outf.write_all(buf.as_ref()).unwrap(); + } "f64" => { let mut buf = vec![]; for px in sdf.into_raw() {