jp2a ripoff
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | src/main.rs | 10 | ||||
| -rw-r--r-- | src/picture.rs | 57 |
3 files changed, 44 insertions, 27 deletions
@@ -15,6 +15,6 @@ image = { version = "0.24.6", features = [ rgb2ansi256 = "0.1.1" [profile.release] -lto = true +debug = 2 +lto = "thin" opt-level = 3 -strip = true diff --git a/src/main.rs b/src/main.rs index e4185f9..e8fb1be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,15 +6,23 @@ mod picture; #[derive(Parser)] #[command(name = "pascii")] #[command(bin_name = "pascii")] +/// turn image into ascii with ansi struct Args { + /// File to take from #[arg()] from: PathBuf, + /// Palette of chars to use + #[arg(long, default_value = " ...,:clodxkO0KXM")] + pal: String, + /// 3 bit rgb? + #[arg(long)] + three: bool, } fn main() -> anyhow::Result<()> { let args = Args::parse(); let p = image::open(args.from)?; let mut s = BufWriter::new(std::io::stdout().lock()); - p.text(b" ...,:clodxkO0KXM", &mut s)?; + p.text(args.pal.as_bytes(), args.three, &mut s)?; Ok(()) } diff --git a/src/picture.rs b/src/picture.rs index 04de046..6a99c20 100644 --- a/src/picture.rs +++ b/src/picture.rs @@ -2,44 +2,57 @@ use image::{DynamicImage, GenericImageView, Pixel, Rgb}; use rgb2ansi256::rgb_to_ansi256; use std::io::{self, Write}; -macro_rules! fg { - ($color:expr,$c:expr) => { - format!("\x1b[38;5;{}m{}", $color, $c) - }; -} - pub trait ToText { - fn text(&self, palette: &[u8], w: &mut impl Write) -> io::Result<()>; + fn text(&self, palette: &[u8], three: bool, w: &mut impl Write) -> io::Result<()>; } impl ToText for DynamicImage { - fn text(&self, palette: &[u8], w: &mut impl Write) -> io::Result<()> { + fn text(&self, palette: &[u8], three: bool, w: &mut impl Write) -> io::Result<()> { + macro_rules! fg { + ($color:expr,$c:expr) => { + write!(w, "\x1b[38;5;{}m{}", $color, $c)? + }; + (3 $color:expr,$c:expr) => { + write!(w, "\x1b[0;34;3{}m{}", $color, $c)? + }; + } + let p_len = (palette.len() - 1) as f32; let height = (self.height() / 2) as usize; for mut y in 0..height { y <<= 1; - let mut last_p: Option<u8> = None; + let mut last_p = if three { 37 } else { 255 }; for x in 0..self.width() { let p = unsafe { self.unsafe_get_pixel(x, y as u32) }; let y = p.to_luma()[0] as f32 / 255_f32; - let a = i2range(p[3]); + let a = p[3] as f32 / u8::MAX as f32; let pos = (p_len * y).round(); let i: usize = (pos * a).round() as usize; - let ch = palette[i] as char; + let ch = unsafe { *palette.get_unchecked(i) as char }; if y <= 0.01 { write!(w, "{ch}")?; continue; } - // must round it into ansi256 or we will get duplicates - let color = p.to_rgb().ansi_256(); - if let Some(last) = last_p { - if last == color { - write!(w, "{ch}")?; - continue; - } + let color = if three { + p.to_rgb() + .0 + .into_iter() + .enumerate() + .map(|(i, v)| (v / 127) << i) + .sum() + } else { + p.to_rgb().ansi_256() + }; + if last_p == color { + write!(w, "{ch}")?; + continue; } - last_p = Some(color); - write!(w, "{}", fg!(color, ch))?; + last_p = color; + if three { + fg!(3 color, ch); + } else { + fg!(color, ch); + } } writeln!(w)?; } @@ -56,7 +69,3 @@ impl Ansi256 for Rgb<u8> { rgb_to_ansi256(self[0], self[1], self[2]) } } - -fn i2range(i: u8) -> f32 { - (i as f32 / u8::MAX as f32).clamp(0.0, 1.0) -} |