aboutsummaryrefslogtreecommitdiff
path: root/src/renderer/mod.rs
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2023-03-11 19:38:16 +0100
committerDaniel Schadt <kingdread@gmx.de>2023-03-11 19:38:16 +0100
commit4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992 (patch)
tree5fc46f69c5ddc78d974f3b93e83af0eacf1b59d1 /src/renderer/mod.rs
parent718d4fbf8b6e85ab808b11143b0657e75096ef73 (diff)
downloadhittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.tar.gz
hittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.tar.bz2
hittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.zip
abstract away tile rendering logic
This is in prepration for the tilehunt mode, where we want to render tiles differently.
Diffstat (limited to 'src/renderer/mod.rs')
-rw-r--r--src/renderer/mod.rs90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
new file mode 100644
index 0000000..927c6ed
--- /dev/null
+++ b/src/renderer/mod.rs
@@ -0,0 +1,90 @@
+//! Generic "tile rendering" methods.
+use std::thread;
+
+use color_eyre::Result;
+use crossbeam_channel::Sender;
+
+use super::gpx::Coordinates;
+
+pub mod heatmap;
+
+const CHANNEL_SIZE: usize = 30;
+
+/// Represents a fully rendered tile.
+#[derive(Debug, Clone)]
+pub struct RenderedTile {
+ /// The `x` coordinate of the tile.
+ pub x: u64,
+ /// The `y` coordinate of the tile.
+ pub y: u64,
+ /// The encoded (PNG) image data, ready to be saved to disk.
+ pub data: Vec<u8>,
+}
+
+/// An object that is responsible for turning raw GPX tracks into a representation.
+///
+/// This is done in two steps, preparation and actual rendering. This allows different feedback for
+/// the user.
+pub trait Renderer {
+ type Prepared: Send;
+
+ /// Prepare the rendered data.
+ ///
+ /// The `tick` channel is used to provide user-feedback, for every finished track a tick should
+ /// be sent.
+ fn prepare(zoom: u32, tracks: &[Vec<Coordinates>], tick: Sender<()>) -> Result<Self::Prepared>;
+
+ /// Actually produce the colored tiles, using the previously prepared data.
+ ///
+ /// The `saver` channel is used to send the finished tiles to a thread that is responsible for
+ /// saving them.
+ fn colorize(prepared: Self::Prepared, saver: Sender<RenderedTile>) -> Result<()>;
+
+ /// Returns the tile count of the prepared data.
+ ///
+ /// This is used for the user interface, to scale progress bars appropriately.
+ fn tile_count(prepared: &Self::Prepared) -> Result<u64>;
+}
+
+/// A convenience wrapper to call [`Renderer::prepare`].
+///
+/// This function takes the same arguments, but provides the ability to use a callback closure
+/// instead of having to set up a channel. The callback is always called on the same thread.
+pub fn prepare<R: Renderer, F: FnMut() -> Result<()>>(
+ zoom: u32,
+ tracks: &[Vec<Coordinates>],
+ mut tick: F,
+) -> Result<R::Prepared> {
+ thread::scope(|s| {
+ let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE);
+
+ let preparer = s.spawn(|| R::prepare(zoom, tracks, sender));
+
+ for _ in receiver {
+ tick()?;
+ }
+
+ preparer.join().unwrap()
+ })
+}
+
+/// A convenience wrapper to call [`Renderer::colorize`].
+///
+/// This function takes the same arguments, but provides the ability to use a callback closure
+/// instead of having to set up a channel. The callback is always called on the same thread.
+pub fn colorize<R: Renderer, F: FnMut(RenderedTile) -> Result<()>>(
+ prepared: R::Prepared,
+ mut saver: F,
+) -> Result<()> {
+ thread::scope(|s| {
+ let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE);
+
+ let colorizer = s.spawn(|| R::colorize(prepared, sender));
+
+ for tile in receiver {
+ saver(tile)?;
+ }
+
+ colorizer.join().unwrap()
+ })
+}