aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2023-01-16 20:58:53 +0100
committerDaniel Schadt <kingdread@gmx.de>2023-01-16 21:01:41 +0100
commitf81eb4882a015ff19cb7209980ca3c7dd33bbae1 (patch)
treeacc2e2bbff10dbd65a316ae6954bf1882aeb1d47 /src
parent18f99254e8a044cb1ebd8ea2653e13b67257b67b (diff)
downloadhittekaart-f81eb4882a015ff19cb7209980ca3c7dd33bbae1.tar.gz
hittekaart-f81eb4882a015ff19cb7209980ca3c7dd33bbae1.tar.bz2
hittekaart-f81eb4882a015ff19cb7209980ca3c7dd33bbae1.zip
use a single thread to write out files
It seems like this does not make the encoding slower, and the main point is that we might want to support SQLite storage for the tiles, in which case it might be good to have only one writer. Even with the FS-based approach, maybe it's good to have a single thread responsible for writing everything, and not hammer the OS with 16 write requests at once.
Diffstat (limited to 'src')
-rw-r--r--src/layer.rs16
-rw-r--r--src/main.rs2
-rw-r--r--src/renderer.rs41
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(())
}