From ecc3b8238595ac5c26355fe196dc74f577edaf23 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Mon, 9 Mar 2020 23:10:33 +0100
Subject: [PATCH] add more configurable image formats

---
 src/ingest/extract.rs     |  3 +-
 src/ingest/spritesheet.rs | 60 ++++++++++++++++++++++++++-------------
 src/main.rs               | 14 +++++++--
 3 files changed, 54 insertions(+), 23 deletions(-)

diff --git a/src/ingest/extract.rs b/src/ingest/extract.rs
index 658cf09..a633087 100644
--- a/src/ingest/extract.rs
+++ b/src/ingest/extract.rs
@@ -1,6 +1,7 @@
 use std::path::Path;
 
 use failure::{format_err, Error};
+use image::ImageOutputFormat;
 
 use crate::ffmpeg_api::api::*;
 use crate::ffmpeg_api::enums::*;
@@ -15,7 +16,7 @@ pub fn extract(
     frame_interval: MediaTime,
     input_file: &Path,
     output_folder: &Path,
-    format: impl AsRef<str>,
+    format: ImageOutputFormat,
     scaler: SwsScaler,
     flags: SwsFlags,
 ) -> Result<(), Error> {
diff --git a/src/ingest/spritesheet.rs b/src/ingest/spritesheet.rs
index 6fa2853..de26266 100644
--- a/src/ingest/spritesheet.rs
+++ b/src/ingest/spritesheet.rs
@@ -1,11 +1,18 @@
+use std::fs::File;
+use std::io::BufWriter;
 use std::path::PathBuf;
 
 use failure::{bail, format_err, Error};
-use image::{ImageBuffer, RgbImage};
+use image::{DynamicImage, ImageOutputFormat, RgbImage};
 
 use crate::util::media_time::MediaTime;
 use crate::util::webvtt::{WebVTTCue, WebVTTFile};
 
+pub enum ImageFormat {
+    Jpeg(i32),
+    Png,
+}
+
 pub struct SpritesheetManager {
     num_horizontal: u32,
     num_vertical: u32,
@@ -19,7 +26,7 @@ pub struct SpritesheetManager {
     metadata: WebVTTFile,
     output_path: PathBuf,
     name: String,
-    format: String,
+    format: ImageOutputFormat,
     initialized: bool,
 }
 
@@ -31,7 +38,7 @@ impl SpritesheetManager {
         frame_interval: MediaTime,
         output_path: impl Into<PathBuf>,
         name: impl AsRef<str>,
-        format: impl AsRef<str>,
+        format: ImageOutputFormat,
     ) -> SpritesheetManager {
         SpritesheetManager {
             num_horizontal,
@@ -39,14 +46,14 @@ impl SpritesheetManager {
             max_side,
             sprite_width: 0,
             sprite_height: 0,
-            spritesheet: ImageBuffer::new(0, 0),
+            spritesheet: RgbImage::new(0, 0),
             current_image: 0,
             last_timestamp: MediaTime::from_millis(0),
             frame_interval,
             metadata: WebVTTFile::new(),
             output_path: output_path.into(),
             name: String::from(name.as_ref()),
-            format: String::from(format.as_ref()),
+            format,
             initialized: false,
         }
     }
@@ -59,15 +66,15 @@ impl SpritesheetManager {
             self.sprite_height = self.max_side;
             self.sprite_width = self.sprite_height * width / height;
         }
-        self.reinit_buffer();
+        self.spritesheet = self.reinit_buffer();
         self.initialized = true;
     }
 
-    fn reinit_buffer(&mut self) {
-        self.spritesheet = ImageBuffer::new(
+    fn reinit_buffer(&self) -> RgbImage {
+        RgbImage::new(
             self.sprite_width * self.num_horizontal,
             self.sprite_height * self.num_vertical,
-        );
+        )
     }
 
     pub fn initialized(&self) -> bool {
@@ -95,6 +102,15 @@ impl SpritesheetManager {
         index * self.sprite_width
     }
 
+    fn ending(&self) -> String {
+        String::from(match self.format {
+            ImageOutputFormat::Png => "png",
+            ImageOutputFormat::Jpeg(_) => "jpeg",
+            ImageOutputFormat::Bmp => "bmp",
+            _ => panic!("Invalid image format: {:?}", self.format),
+        })
+    }
+
     fn y(&self, current: u32) -> u32 {
         let index = (current / self.num_horizontal) % self.num_vertical;
         index * self.sprite_height
@@ -141,7 +157,7 @@ impl SpritesheetManager {
                 "{}_{}.{}#xywh={},{},{},{}",
                 self.name,
                 self.spritesheet_index(self.current_image - 1),
-                self.format,
+                self.ending(),
                 self.x(self.current_image - 1),
                 self.y(self.current_image - 1),
                 self.sprite_width,
@@ -151,15 +167,21 @@ impl SpritesheetManager {
     }
 
     fn save_spritesheet(&mut self) -> Result<(), Error> {
-        self.spritesheet
-            .save(self.output_path.join(format!(
-                "{}_{}.{}",
-                self.name,
-                self.spritesheet_index(self.current_image),
-                self.format
-            )))
-            .map_err(|error| format_err!("Could not write spritesheet: {}", error))?;
-        self.reinit_buffer();
+        let name = format!(
+            "{}_{}.{}",
+            self.name,
+            self.spritesheet_index(self.current_image),
+            self.ending(),
+        );
+
+        let file = File::create(self.output_path.join(&name))
+            .map_err(|err| format_err!("Could not create spritesheet {}: {}", &name, err))?;
+
+        let new_buffer = self.reinit_buffer();
+        DynamicImage::ImageRgb8(std::mem::replace(&mut self.spritesheet, new_buffer))
+            .write_to(&mut BufWriter::new(file), self.format.clone())
+            .map_err(|err| format_err!("Could not write spritesheet {}: {}", &name, err))?;
+
         Ok(())
     }
 
diff --git a/src/main.rs b/src/main.rs
index 39a2214..3c99314 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,6 +11,7 @@ use structopt::StructOpt;
 
 use crate::ffmpeg_api::enums::{SwsFlags, SwsScaler};
 use crate::util::media_time::MediaTime;
+use image::ImageOutputFormat;
 
 fn parse_scaler(src: &str) -> Result<SwsScaler, String> {
     match src {
@@ -40,10 +41,12 @@ struct Options {
     num_horizontal: u32,
     #[structopt(long = "num-vertical", default_value = "5")]
     num_vertical: u32,
-    #[structopt(long = "max-size", default_value = "160")]
+    #[structopt(long = "max-size", default_value = "240")]
     max_size: u32,
-    #[structopt(long = "format", default_value = "png")]
+    #[structopt(long = "format", default_value = "jpg")]
     format: String,
+    #[structopt(long = "quality", default_value = "90")]
+    quality: u8,
     #[structopt(long = "scaler", default_value = "area", parse(try_from_str = parse_scaler))]
     scaler: SwsScaler,
     #[structopt(long = "fast-chroma")]
@@ -75,7 +78,12 @@ fn main() -> Result<(), Error> {
         MediaTime::from_seconds(options.frame_interval),
         Path::new(&options.input),
         Path::new(&options.output),
-        options.format,
+        match options.format.as_str() {
+            "jpeg" | "jpg" => ImageOutputFormat::Jpeg(options.quality),
+            "png" => ImageOutputFormat::Png,
+            "bmp" => ImageOutputFormat::Bmp,
+            _ => panic!("Unsupported image format: {}", options.format),
+        },
         options.scaler,
         flags,
     )?;
-- 
GitLab