mirror of
https://github.com/Dangoware/sqp.git
synced 2025-06-22 14:42:54 -05:00
168 lines
4.2 KiB
Rust
168 lines
4.2 KiB
Rust
mod utils;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use clap::{Args, Parser, Subcommand};
|
|
use image::ImageReader;
|
|
use anyhow::{bail, Result};
|
|
use sqp::{ColorFormat, CompressionType};
|
|
use utils::{color_format, color_format_to_type, color_type_to_format, exists_decision, Assume};
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(version, about, long_about = None)]
|
|
struct Cli {
|
|
#[command(subcommand)]
|
|
command: Subcommands,
|
|
|
|
/// Overwrite output files
|
|
#[arg(short = 'y', long = "overwrite", conflicts_with = "assumeno")]
|
|
assumeyes: bool,
|
|
|
|
/// Do not overwrite output files
|
|
#[arg(short = 'n', long = "preserve", conflicts_with = "assumeyes")]
|
|
assumeno: bool,
|
|
}
|
|
|
|
#[derive(Debug, Subcommand)]
|
|
enum Subcommands {
|
|
/// Encode an image to SQP format
|
|
Encode(EncodeArgs),
|
|
|
|
/// Decode an SQP image into another format
|
|
Decode(DecodeArgs),
|
|
}
|
|
|
|
#[derive(Debug, Args)]
|
|
struct EncodeArgs {
|
|
/// Input image file of any type supported by `image`
|
|
input: PathBuf,
|
|
/// Output path to SQP location
|
|
output: PathBuf,
|
|
|
|
/// Quality setting, a higher value = higher quality.
|
|
#[arg(default_value_t = 100, short, long, conflicts_with = "uncompressed", value_parser = clap::value_parser!(u8).range(1..=100))]
|
|
quality: u8,
|
|
|
|
/// Create an uncompressed image.
|
|
///
|
|
/// Incompatible with quality setting.
|
|
#[arg(short, long, conflicts_with = "quality")]
|
|
uncompressed: bool,
|
|
|
|
/// The color format to use for the output image
|
|
///
|
|
/// Valid values:
|
|
/// - RGBA8
|
|
/// - RGB8
|
|
/// - GrayA8
|
|
/// - Gray8
|
|
#[arg(short, long, value_parser = color_format, verbatim_doc_comment)]
|
|
color_format: Option<ColorFormat>,
|
|
}
|
|
|
|
#[derive(Debug, Args)]
|
|
struct DecodeArgs {
|
|
/// Input SQP image file
|
|
input: PathBuf,
|
|
|
|
/// Output image file
|
|
output: PathBuf,
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
let args = Cli::parse();
|
|
|
|
let assume = if args.assumeyes {
|
|
Some(Assume::Yes)
|
|
} else if args.assumeno {
|
|
Some(Assume::No)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
match args.command {
|
|
Subcommands::Encode(a) => encode(a, assume),
|
|
Subcommands::Decode(a) => decode(a, assume),
|
|
}
|
|
}
|
|
|
|
fn encode(args: EncodeArgs, assume: Option<Assume>) -> Result<()> {
|
|
if !args.input.try_exists()? {
|
|
bail!("Input file {:?} does not exist", args.input);
|
|
}
|
|
|
|
if args.output.try_exists()?
|
|
&& !exists_decision("Output", "Overwrite", &args.output, assume)
|
|
{
|
|
return Ok(())
|
|
}
|
|
|
|
let image = ImageReader::open(args.input)?
|
|
.decode()?;
|
|
|
|
let width = image.width();
|
|
let height = image.height();
|
|
|
|
let color_format = args.color_format.or_else(
|
|
|| color_type_to_format(image.color())
|
|
).unwrap_or(ColorFormat::Rgba8);
|
|
|
|
let bitmap = match color_format {
|
|
ColorFormat::Rgba8 => image.into_rgba8().into_vec(),
|
|
ColorFormat::Rgb8 => image.into_rgb8().into_vec(),
|
|
ColorFormat::GrayA8 => image.into_luma_alpha8().into_vec(),
|
|
ColorFormat::Gray8 => image.into_luma8().into_vec(),
|
|
};
|
|
|
|
let (compression_type, quality) = if args.uncompressed {
|
|
(CompressionType::None, None)
|
|
} else if args.quality == 100 {
|
|
(CompressionType::Lossless, None)
|
|
} else {
|
|
(CompressionType::LossyDct, Some(args.quality))
|
|
};
|
|
|
|
let sqp_img = sqp::SquishyPicture::from_raw(
|
|
width,
|
|
height,
|
|
color_format,
|
|
compression_type,
|
|
quality,
|
|
bitmap,
|
|
);
|
|
|
|
sqp_img.save(&args.output)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn decode(args: DecodeArgs, assume: Option<Assume>) -> Result<()> {
|
|
if !args.input.try_exists()? {
|
|
bail!("Input file {:?} does not exist", args.input);
|
|
}
|
|
|
|
if args.output.try_exists()?
|
|
&& !exists_decision("Output", "Overwrite", &args.output, assume)
|
|
{
|
|
return Ok(())
|
|
}
|
|
|
|
let sqp_img = sqp::open(args.input)?;
|
|
|
|
let width = sqp_img.width();
|
|
let height = sqp_img.height();
|
|
let color_format = color_format_to_type(sqp_img.color_format());
|
|
|
|
let mut img = sqp_img.into_raw();
|
|
img.truncate(width as usize * height as usize * color_format.bytes_per_pixel() as usize);
|
|
|
|
image::save_buffer(
|
|
args.output,
|
|
&img,
|
|
width,
|
|
height,
|
|
color_format,
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|