index : gmss.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2023-12-20 20:32:51.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2023-12-20 20:32:51.0 +00:00:00
commit
44c817af39cffc34b84b73a0598b5016b6b52549 [patch]
tree
625f09ceee7c7c44322cbaf2a8df2789280303bb
parent
716aeb9196d460205129cc43c73bf8583e4ba074
download
44c817af39cffc34b84b73a0598b5016b6b52549.tar.gz

refactor: break up main into `imaging` and `graphics` modules



Diff

 src/graphics.rs | 16 ++++++++++++++++
 src/imaging.rs  | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/main.rs     | 52 +++++++++++-----------------------------------------
 3 files changed, 77 insertions(+), 41 deletions(-)

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<Screen> {
        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<ImageBuffer<Rgba<u8>, Vec<u8>>>,
    ) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
        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<Local> = 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 `<FILENAME>_<NUM>.<EXT>` 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();