use std::fs::read;
use std::io::Cursor;
use std::io::IsTerminal;
use std::io::Read;
use std::io::stdin;
use a_sixel::PaletteBuilder;
use a_sixel::SixelEncoder;
use a_sixel::dither::Dither;
use clap::CommandFactory;
use clap::Parser;
use image::ImageReader;
use image::RgbaImage;
#[derive(Debug, Parser)]
#[command(name = "a-sixel", about = "Encode images as sixel graphics")]
struct Args {
positional_image_paths: Vec<String>,
#[clap(long = "image_path", short = 'i')]
specified_image_paths: Vec<String>,
#[clap(long, short, default_value_t = 256)]
palette_size: usize,
#[clap(long, short = 'a', default_value_t = PaletteBuilder::Bit)]
algorithm: PaletteBuilder,
#[clap(long, short, default_value_t = Dither::None)]
dither: Dither,
}
fn split_images(data: &[u8]) -> anyhow::Result<Vec<RgbaImage>> {
let mut images = vec![];
let mut cursor = Cursor::new(data);
while cursor.position() < data.len() as u64 {
let Ok(image) = ImageReader::new(&mut cursor)
.with_guessed_format()?
.decode()
else {
let pos = cursor.position();
cursor.set_position(pos.saturating_add(1));
continue;
};
images.push(image.to_rgba8());
}
Ok(images)
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let image_data;
if !stdin().is_terminal() {
let mut buf = vec![];
stdin().read_to_end(&mut buf)?;
image_data = buf;
} else if args.specified_image_paths.is_empty() && args.positional_image_paths.is_empty() {
Args::command().print_help()?;
return Ok(());
} else {
let mut buf = vec![];
for path in args.specified_image_paths {
buf.extend(read(path)?);
}
for path in args.positional_image_paths {
buf.extend(read(path)?);
}
image_data = buf;
}
let encoder = SixelEncoder::new(args.algorithm, args.dither);
for image in split_images(&image_data)? {
let six = encoder.encode_with_palette_size(image, args.palette_size);
println!("{six}");
}
Ok(())
}