Add option to save image to PNG file
This commit is contained in:
parent
b8dddd7498
commit
4451d1d71f
|
|
@ -15,6 +15,7 @@ sdl2 = "0.32"
|
||||||
simba = "0.1.2"
|
simba = "0.1.2"
|
||||||
csv = "1.1.3"
|
csv = "1.1.3"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
|
png = "0.16"
|
||||||
|
|
||||||
[dependencies.nalgebra]
|
[dependencies.nalgebra]
|
||||||
version = "0.21"
|
version = "0.21"
|
||||||
|
|
|
||||||
29
src/image.rs
29
src/image.rs
|
|
@ -1,4 +1,7 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufWriter;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use nalgebra::{clamp, convert, Vector3};
|
use nalgebra::{clamp, convert, Vector3};
|
||||||
|
|
||||||
|
|
@ -60,6 +63,32 @@ impl ImageRgbU8 {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, start_row: usize, start_column: usize, image: &ImageRgbU8) {
|
||||||
|
assert!(start_column + image.width <= self.width);
|
||||||
|
assert!(start_row + image.height <= self.height);
|
||||||
|
for row in 0..image.height {
|
||||||
|
let source_start = image.calculate_index(row, 0);
|
||||||
|
let source_end = image.calculate_index(row, image.width - 1) + 3;
|
||||||
|
let destination_start = self.calculate_index(start_row + row, start_column);
|
||||||
|
let destination_end =
|
||||||
|
self.calculate_index(start_row + row, start_column + image.width - 1) + 3;
|
||||||
|
self.pixel_data[destination_start..destination_end]
|
||||||
|
.copy_from_slice(&image.pixel_data[source_start..source_end]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_png(&self, filename: &Path) -> Result<(), std::io::Error> {
|
||||||
|
let file = File::create(filename)?;
|
||||||
|
let ref mut file_buffer = BufWriter::new(file);
|
||||||
|
|
||||||
|
let mut encoder = png::Encoder::new(file_buffer, self.width as u32, self.height as u32);
|
||||||
|
encoder.set_color(png::ColorType::RGB);
|
||||||
|
encoder.set_depth(png::BitDepth::Eight);
|
||||||
|
let mut writer = encoder.write_header()?;
|
||||||
|
writer.write_image_data(self.pixel_data.as_slice())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn calculate_index(&self, row: usize, column: usize) -> usize {
|
fn calculate_index(&self, row: usize, column: usize) -> usize {
|
||||||
assert!(row < self.height && column < self.width);
|
assert!(row < self.height && column < self.width);
|
||||||
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
|
||||||
|
|
|
||||||
36
src/main.rs
36
src/main.rs
|
|
@ -11,7 +11,7 @@ use nalgebra::{Point3, Vector3};
|
||||||
|
|
||||||
use clap::Arg;
|
use clap::Arg;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::{mpsc, Arc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -31,6 +31,7 @@ use vanrijn::util::{Tile, TileIterator};
|
||||||
struct CommandLineParameters {
|
struct CommandLineParameters {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
output_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_args() -> CommandLineParameters {
|
fn parse_args() -> CommandLineParameters {
|
||||||
|
|
@ -46,11 +47,24 @@ fn parse_args() -> CommandLineParameters {
|
||||||
.number_of_values(2)
|
.number_of_values(2)
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("output_png")
|
||||||
|
.long("out")
|
||||||
|
.value_name("FILENAME")
|
||||||
|
.help("Filename for output PNG.")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
let mut size_iter = matches.values_of("size").unwrap();
|
let mut size_iter = matches.values_of("size").unwrap();
|
||||||
let width = size_iter.next().unwrap().parse().unwrap();
|
let width = size_iter.next().unwrap().parse().unwrap();
|
||||||
let height = size_iter.next().unwrap().parse().unwrap();
|
let height = size_iter.next().unwrap().parse().unwrap();
|
||||||
CommandLineParameters { width, height }
|
let output_file = matches.value_of_os("output_png").map(|f| PathBuf::from(f));
|
||||||
|
CommandLineParameters {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
output_file,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_texture(tile: &Tile, image: &ImageRgbU8, texture: &mut Texture) {
|
fn update_texture(tile: &Tile, image: &ImageRgbU8, texture: &mut Texture) {
|
||||||
|
|
@ -68,6 +82,10 @@ fn update_texture(tile: &Tile, image: &ImageRgbU8, texture: &mut Texture) {
|
||||||
.expect("Couldn't update texture.");
|
.expect("Couldn't update texture.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_image(tile: &Tile, tile_image: &ImageRgbU8, image: &mut ImageRgbU8) {
|
||||||
|
image.update(tile.start_row, tile.start_column, tile_image);
|
||||||
|
}
|
||||||
|
|
||||||
fn init_canvas(
|
fn init_canvas(
|
||||||
image_width: usize,
|
image_width: usize,
|
||||||
image_height: usize,
|
image_height: usize,
|
||||||
|
|
@ -90,6 +108,8 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let image_width = parameters.width;
|
let image_width = parameters.width;
|
||||||
let image_height = parameters.height;
|
let image_height = parameters.height;
|
||||||
|
|
||||||
|
let mut rendered_image = ImageRgbU8::new(image_width, image_height);
|
||||||
|
|
||||||
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
||||||
|
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
|
|
@ -165,6 +185,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut tile_rx = Some(tile_rx);
|
let mut tile_rx = Some(tile_rx);
|
||||||
|
|
||||||
let worker_boss = std::thread::spawn(move || {
|
let worker_boss = std::thread::spawn(move || {
|
||||||
|
let end_tx = tile_tx.clone();
|
||||||
TileIterator::new(image_width as usize, image_height as usize, 32)
|
TileIterator::new(image_width as usize, image_height as usize, 32)
|
||||||
.map(move |tile| (tile, tile_tx.clone()))
|
.map(move |tile| (tile, tile_tx.clone()))
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
|
|
@ -173,18 +194,25 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
// There's nothing we can do if this fails, and we're already
|
// There's nothing we can do if this fails, and we're already
|
||||||
// at the end of the function anyway, so just ignore result.
|
// at the end of the function anyway, so just ignore result.
|
||||||
tx.send((tile, rendered_tile)).ok()
|
tx.send(Some((tile, rendered_tile))).ok()
|
||||||
});
|
});
|
||||||
|
end_tx.send(None).ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
'running: loop {
|
'running: loop {
|
||||||
if let Some(ref tile_rx) = tile_rx {
|
if let Some(ref tile_rx) = tile_rx {
|
||||||
for (tile, tile_image) in tile_rx.try_iter() {
|
for message in tile_rx.try_iter() {
|
||||||
|
if let Some((tile, tile_image)) = message {
|
||||||
let mut tile_image_rgbu8 = ImageRgbU8::new(tile.width(), tile.height());
|
let mut tile_image_rgbu8 = ImageRgbU8::new(tile.width(), tile.height());
|
||||||
ClampingToneMapper {}.apply_tone_mapping(&tile_image, &mut tile_image_rgbu8);
|
ClampingToneMapper {}.apply_tone_mapping(&tile_image, &mut tile_image_rgbu8);
|
||||||
update_texture(&tile, &tile_image_rgbu8, &mut rendered_image_texture);
|
update_texture(&tile, &tile_image_rgbu8, &mut rendered_image_texture);
|
||||||
|
update_image(&tile, &tile_image_rgbu8, &mut rendered_image);
|
||||||
canvas.copy(&rendered_image_texture, None, None).unwrap();
|
canvas.copy(&rendered_image_texture, None, None).unwrap();
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
} else if let Some(image_filename) = parameters.output_file {
|
||||||
|
rendered_image.write_png(&image_filename)?;
|
||||||
|
break 'running;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue