tx2-iff 0.1.0

PPF-IFF (Involuted Fractal Format) - Image codec using Physics-Prime Factorization, 360-prime quantization, and symplectic warping
Documentation
//! CLI tool to encode images to PPF-IFF format
//!
//! Usage: cargo run --example encode --features encoder -- input.png output.iff

use std::env;
use std::fs::File;
use std::io::Write;
use tx2_iff::{Encoder, EncoderConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    let args: Vec<String> = env::args().collect();

    if args.len() < 3 {
        eprintln!("Usage: {} <input.png|jpg> <output.iff> [options]", args[0]);
        eprintln!();
        eprintln!("Options:");
        eprintln!("  --levels <n>          Wavelet decomposition levels (default: 5)");
        eprintln!("  --quality <n>         Base quantization (default: 10, lower = better)");
        eprintln!("  --texture-size <n>    Minimum texture region size (default: 32)");
        eprintln!("  --no-texture          Disable Layer 2 (texture synthesis)");
        eprintln!("  --no-warp             Disable Layer 3 (warp fields)");
        eprintln!("  --residual-threshold <f> Residual RMSE threshold (default: 5.0)");
        std::process::exit(1);
    }

    let input_path = &args[1];
    let output_path = &args[2];

    // Parse options
    let mut config = EncoderConfig::default();
    let mut i = 3;
    while i < args.len() {
        match args[i].as_str() {
            "--levels" => {
                i += 1;
                if i < args.len() {
                    config.wavelet_levels = args[i].parse()?;
                }
            }
            "--quality" => {
                i += 1;
                if i < args.len() {
                    config.base_quantization = args[i].parse()?;
                }
            }
            "--texture-size" => {
                i += 1;
                if i < args.len() {
                    config.texture_min_size = args[i].parse()?;
                }
            }
            "--no-texture" => {
                config.enable_layer2 = false;
            }
            "--no-warp" => {
                config.enable_layer3 = false;
            }
            "--residual-threshold" => {
                i += 1;
                if i < args.len() {
                    config.residual_threshold = args[i].parse()?;
                }
            }
            _ => {
                eprintln!("Warning: Unknown option: {}", args[i]);
            }
        }
        i += 1;
    }

    println!("Loading image: {}", input_path);
    let image = image::open(input_path)?;
    let width = image.width();
    let height = image.height();

    println!("Image size: {}x{}", width, height);
    println!();
    println!("Encoder configuration:");
    println!("  Wavelet levels: {}", config.wavelet_levels);
    println!("  Base quantization: {}", config.base_quantization);
    println!("  Layer 2 (texture): {}", config.enable_layer2);
    println!("  Layer 3 (warp): {}", config.enable_layer3);
    println!("  Texture min size: {}", config.texture_min_size);
    println!("  Residual threshold: {:.1}", config.residual_threshold);
    println!();

    println!("Encoding...");
    let encoder = Encoder::new(config);
    let iff_image = encoder.encode(&image)?;

    println!("Serializing...");
    let iff_bytes = iff_image.to_bytes()?;

    println!("Writing to: {}", output_path);
    let mut file = File::create(output_path)?;
    file.write_all(&iff_bytes)?;

    // Calculate compression stats
    let original_size = (width * height * 3) as usize; // RGB
    let compressed_size = iff_bytes.len();
    let ratio = original_size as f64 / compressed_size as f64;
    let savings = (1.0 - (compressed_size as f64 / original_size as f64)) * 100.0;

    println!();
    println!("Encoding complete!");
    println!("  Original size:    {} bytes ({:.2} KB)", original_size, original_size as f64 / 1024.0);
    println!("  Compressed size:  {} bytes ({:.2} KB)", compressed_size, compressed_size as f64 / 1024.0);
    println!("  Compression ratio: {:.2}x", ratio);
    println!("  Space savings:     {:.1}%", savings);
    println!();
    println!("Layer statistics:");
    let layer1_size = iff_image.layer1.y.data.len() + iff_image.layer1.co.data.len() + iff_image.layer1.cg.data.len();
    println!("  Layer 1 (wavelet):  {} bytes (compressed)", layer1_size);
    println!("  Layer 2 (texture):  {} regions", iff_image.layer2.regions.len());
    println!("  Layer 3 (warp):     {} vortices", iff_image.layer3.vortices.len());
    println!("  Residual:           {} bytes (compressed)", iff_image.residual.data.len());

    Ok(())
}