diff --git a/Cargo.lock b/Cargo.lock index f84b2001dfd3f5b2448e6eb781b42fd87b4552fe..be444f847c8058f489ac93a00620ecbdec19fbc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -787,10 +787,12 @@ dependencies = [ name = "rapidriter-cat" version = "0.1.0" dependencies = [ + "anyhow", "bitvec", "clap", "colored", "console", + "gif", "image", ] diff --git a/Cargo.toml b/Cargo.toml index 81bc4a9d0e940ac6c2719edeeec4655e2891a352..f8b97156e38461206c42a3d947c8c2a26cf03980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,12 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] +anyhow = "1.0.96" bitvec = "1.0.1" clap = { version = "4.5.30", features = ["derive"] } colored = "3.0.0" console = "0.15.10" +gif = "0.13.1" [build-dependencies] image = "0.25.5" diff --git a/src/main.rs b/src/main.rs index b9174f572b324199d2b1f11d2ed031d99a108c3a..af1e0794d08f1bfafead489d4736ff835c83b6b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ use std::{ - io::{self, Read}, + borrow::Cow, + fs::File, + path::PathBuf, thread, time::{Duration, Instant}, }; @@ -9,6 +11,7 @@ use bitvec::{order::Msb0, view::AsBits}; use clap::Parser; use colored::Colorize; use console::Term; +use gif::{Encoder, Frame, Repeat}; use rapidriter_cat::next_frame; const WIDTH: usize = 96; @@ -16,17 +19,59 @@ const HEIGHT: usize = 38; mod arg; -fn main() { +fn main() -> anyhow::Result<()> { let args = Args::parse(); - // clear screen - print!("\x1B[2J"); - - if args.interactive { - interactive_mode(); + if let Some(path) = args.out { + generate_gif(path)?; } else { - non_interactive_mode(); + // clear screen + print!("\x1B[2J"); + + if args.interactive { + interactive_mode(); + } else { + non_interactive_mode(); + } } + + Ok(()) +} + +fn generate_gif(path: PathBuf) -> anyhow::Result<()> { + let mut gif = File::create(path)?; + let mut encoder = Encoder::new( + &mut gif, + WIDTH.try_into().unwrap(), + HEIGHT.try_into().unwrap(), + &[0, 0, 0, 0xFF, 0, 0], + )?; + encoder.set_repeat(Repeat::Infinite)?; + + let mut frame = [0; 456]; + for i in 0..100 { + let done = unsafe { next_frame(i, frame.as_mut_ptr()) } == 1; + let mut exploded_frame = vec![]; + for b in frame.as_bits::<Msb0>() { + exploded_frame.push(if *b { 1 } else { 0 }); + } + + let f = Frame { + delay: 10, // 100 ms, or 10 FPS + width: WIDTH.try_into().unwrap(), + height: HEIGHT.try_into().unwrap(), + buffer: Cow::Borrowed(&*exploded_frame), + + ..Default::default() + }; + encoder.write_frame(&f)?; + + if done { + break; + } + } + + Ok(()) } fn interactive_mode() { @@ -74,12 +119,14 @@ fn non_interactive_mode() { thread::sleep(target - now); } - if unsafe { next_frame(i, frame.as_mut_ptr()) } == 1 { - break; - } + let done = unsafe { next_frame(i, frame.as_mut_ptr()) } == 1; draw_frame(&frame); println!("Frame: {}", i); + + if done { + break; + } } }