diff options
-rw-r--r-- | src/layer.rs | 16 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/renderer.rs | 41 |
3 files changed, 46 insertions, 13 deletions
diff --git a/src/layer.rs b/src/layer.rs index ff69f7d..2726c81 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -3,7 +3,11 @@ //! This supports OSM-style "tiled" images, but not all of the tiles have to be present. If a tile //! is not present, a default pixel is returned. The tile is allocated with the first call to a //! mutating operation. -use std::{fs::File, io::BufWriter, path::Path}; +use std::{ + fs::File, + io::{BufWriter, Write}, + path::Path, +}; use color_eyre::eyre::Result; use fnv::FnvHashMap; @@ -119,6 +123,10 @@ where pub fn compress_png<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> { let outstream = BufWriter::new(File::create(path)?); + compress_png_stream(image, outstream) +} + +pub fn compress_png_stream<W: Write>(image: &RgbaImage, outstream: W) -> Result<()> { let encoder = PngEncoder::new_with_quality(outstream, CompressionType::Best, FilterType::Adaptive); @@ -127,6 +135,12 @@ pub fn compress_png<P: AsRef<Path>>(image: &RgbaImage, path: P) -> Result<()> { Ok(()) } +pub fn compress_png_as_bytes(image: &RgbaImage) -> Result<Vec<u8>> { + let mut buffer = Vec::new(); + compress_png_stream(image, &mut buffer)?; + Ok(buffer) +} + fn zero_pixel<P: Pixel>() -> P { let zeroes = vec![Zero::zero(); P::CHANNEL_COUNT as usize]; *P::from_slice(&zeroes) diff --git a/src/main.rs b/src/main.rs index e2dacb7..a508fdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,7 +123,7 @@ fn ensure_output_directory<P: AsRef<Path>>(path: P) -> Result<()> { let metadata = fs::metadata(path); match metadata { Err(e) if e.kind() == ErrorKind::NotFound => { - let parent = path.parent().unwrap_or(Path::new("/")); + let parent = path.parent().unwrap_or_else(|| Path::new("/")); fs::create_dir(path) .context(format!("Could not create output directory at {parent:?}"))? } diff --git a/src/renderer.rs b/src/renderer.rs index 2f29828..de65da6 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,9 @@ -use std::{fs, path::Path}; +use std::{fs, mem, path::Path, sync::mpsc, thread}; -use color_eyre::eyre::{bail, Result}; +use color_eyre::{ + eyre::{bail, Result}, + Report, +}; use image::{ImageBuffer, Luma, Pixel, RgbaImage}; use nalgebra::{vector, Vector2}; use rayon::iter::ParallelIterator; @@ -143,22 +146,38 @@ pub fn lazy_colorization<P: AsRef<Path>, F: Fn(usize) + Send + Sync>( return Ok(()); } - layer - .into_parallel_tiles() - .try_for_each(|(tile_x, tile_y, tile)| { - let colorized = colorize_tile(&tile, max.into()); + type Job = (u64, u64, Vec<u8>); + let (tx, rx) = mpsc::sync_channel::<Job>(30); + + thread::scope(|s| { + let saver = s.spawn(move || loop { + let Ok((tile_x, tile_y, data)) = rx.recv() else { return Ok(()) }; let folder = base_dir.join(tile_x.to_string()); let metadata = folder.metadata(); match metadata { - Err(_) => fs::create_dir_all(&folder)?, + Err(_) => fs::create_dir(&folder)?, Ok(m) if !m.is_dir() => bail!("Output path is not a directory"), _ => {} } let file = folder.join(format!("{tile_y}.png")); - layer::compress_png(&colorized, file)?; - progress_callback(1); - Ok(()) - })?; + fs::write(file, data)?; + }); + + layer + .into_parallel_tiles() + .try_for_each(|(tile_x, tile_y, tile)| { + let colorized = colorize_tile(&tile, max.into()); + let data = layer::compress_png_as_bytes(&colorized)?; + tx.send((tile_x, tile_y, data))?; + progress_callback(1); + Ok::<(), Report>(()) + })?; + + mem::drop(tx); + saver.join().unwrap()?; + Ok::<_, Report>(()) + })?; + Ok(()) } |