From 44c817af39cffc34b84b73a0598b5016b6b52549 Mon Sep 17 00:00:00 2001 From: holly sparkles Date: Wed, 20 Dec 2023 21:32:51 +0100 Subject: [PATCH] refactor: break up main into `imaging` and `graphics` modules --- src/graphics.rs | 16 ++++++++++++++++ src/imaging.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 52 +++++++++++----------------------------------------- 3 files changed, 77 insertions(+), 41 deletions(-) create mode 100644 src/graphics.rs create mode 100644 src/imaging.rs diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..66b5f48 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,16 @@ +use screenshots::Screen; + +pub struct Display; + +impl Display { + /// A wrapper around `screenshots::Screen::all()` to get displays in order from left to right + pub fn get_displays() -> Vec { + let mut screens = Screen::all().unwrap_or_else(|err| { + eprintln!("there was an error retrieving screens: {:?}", err); + vec![] + }); + + screens.sort_by(|first, second| first.display_info.x.cmp(&second.display_info.x)); + screens + } +} diff --git a/src/imaging.rs b/src/imaging.rs new file mode 100644 index 0000000..497534f --- /dev/null +++ b/src/imaging.rs @@ -0,0 +1,50 @@ +use screenshots::image::{ImageBuffer, Rgba}; + +/// Represents the size of an image in pixels. +/// +/// Printed as: `(width, height)` +#[derive(Default)] +pub struct ImageDimensions { + pub width: u32, + pub height: u32, +} + +impl std::fmt::Display for ImageDimensions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({}, {})", self.width, self.height)) + } +} + +pub struct Renderer; + +impl Renderer { + /// Horizontally stitches together a given collection of images. + /// + /// The height of the output image is dependent on the height of the tallest image. + pub fn stitch_horizontally( + images: Vec, Vec>>, + ) -> ImageBuffer, Vec> { + let dimensions = images + .iter() + .fold(ImageDimensions::default(), |t, buf| ImageDimensions { + width: t.width + buf.width(), + height: t.height.max(buf.height()), + }); + + let mut img_buf = ImageBuffer::new(dimensions.width, dimensions.height); + + images + .iter() + .fold(ImageDimensions::default(), |offset, buf| { + for (x, y, pixel) in buf.enumerate_pixels() { + img_buf.put_pixel(offset.width + x, y, *pixel); + } + ImageDimensions { + width: offset.width + buf.width(), + height: offset.height, + } + }); + + img_buf + } +} diff --git a/src/main.rs b/src/main.rs index fedf979..61020e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ use std::path::PathBuf; use chrono::{DateTime, Local, Utc}; use clap::Parser; -use screenshots::{image::ImageBuffer, Screen}; +use imaging::Renderer; mod cli; +mod graphics; +mod imaging; fn main() { let args = cli::Args::parse(); @@ -12,39 +14,14 @@ fn main() { capture_all(args.out, args.filename_format.unwrap_or_default()); } +/// Captures a screenshot of all active displays and writes the resulting image to disk. fn capture_all(out_dir: String, filename_format: String) { - let mut screens = Screen::all().unwrap_or_else(|err| { - eprintln!("there was an error retrieving screens: {:?}", err); - vec![] - }); - - screens.sort_by(|first, second| first.display_info.x.cmp(&second.display_info.x)); - - let captures: Vec<_> = screens + let captures: Vec<_> = graphics::Display::get_displays() .iter() .filter_map(|screen| screen.capture().ok()) .collect(); - let dimensions = captures - .iter() - .fold(ImageDimensions::default(), |t, buf| ImageDimensions { - x: t.x + buf.width(), - y: t.y.max(buf.height()), - }); - - let mut img = ImageBuffer::new(dimensions.x, dimensions.y); - - captures - .iter() - .fold(ImageDimensions::default(), |offset, buf| { - for (x, y, pixel) in buf.enumerate_pixels() { - img.put_pixel(offset.x + x, y, *pixel); - } - ImageDimensions { - x: offset.x + buf.width(), - y: offset.y, - } - }); + let img = Renderer::stitch_horizontally(captures); if !img.is_empty() { let timestamp: DateTime = Utc::now().with_timezone(&Local); @@ -61,18 +38,11 @@ fn capture_all(out_dir: String, filename_format: String) { } } -#[derive(Default)] -struct ImageDimensions { - pub x: u32, - pub y: u32, -} - -impl std::fmt::Display for ImageDimensions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("({}, {})", self.x, self.y)) - } -} - +/// Generate a new filename if a file already exists at the target. +/// +/// If the file exists, the format of `_.` will be generated. +/// +/// eg. `my_screenshot-2023-12-20.png` becomes `my_screenshot-2023-12-20_1.png` fn generate_filename(filename: &PathBuf) -> PathBuf { let mut index = 0; let mut new_filename = filename.clone(); -- libgit2 1.7.2