wagahai_lut 0.1.0

CUBE LUT parser and image processing library with SIMD
Documentation
/*
 * SPDX-FileCopyrightText: © 2026 Jinwoo Park (pmnxis@gmail.com)
 *
 * SPDX-License-Identifier: MIT
 */

//! Basic usage example demonstrating in-place (mut) LUT application
//!
//! This example shows how to use the zero-allocation in-place processing
//! functions to apply LUTs to images without creating new buffers.

use image::{DynamicImage, ImageBuffer, Rgb, Rgba};
use std::env;
use std::time::Instant;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = env::args().collect();

    if args.len() < 2 {
        println!(
            "Usage: {} <cube-file> [input-image] [output-image]",
            args[0]
        );
        println!("\nExamples:");
        println!(
            "  {} lut.cube                              # Create test image and apply LUT",
            args[0]
        );
        println!(
            "  {} lut.cube input.jpg out.jpg            # Apply LUT to image file",
            args[0]
        );
        println!("\nThis example demonstrates in-place (mut) processing:");
        println!("  - Zero memory allocation during processing");
        println!("  - Direct modification of image buffer");
        println!("  - Preserves alpha channel in RGBA images");
        return Ok(());
    }

    let cube_path = &args[1];

    // Parse CUBE LUT file
    println!("Loading CUBE LUT from: {}", cube_path);
    let lut = wagahai_lut::CubeParser::from_file(cube_path)?;

    // Display LUT information
    println!("\nLUT Information:");
    if let Some(ref title) = lut.title {
        println!("  Title: {}", title);
    }
    println!("  Type: {:?}", lut.get_lut_type());

    if args.len() >= 4 {
        // Process an actual image file
        let input_path = &args[2];
        let output_path = &args[3];

        println!("\nApplying LUT to image: {}", input_path);

        // Load image
        let load_start = Instant::now();
        let image = image::open(input_path)?;
        let load_duration = load_start.elapsed();
        println!("  Image size: {}x{}", image.width(), image.height());
        println!("  Image load time: {:?}", load_duration);

        // Apply LUT based on image type with timing
        let apply_start = Instant::now();
        let processed = match image {
            DynamicImage::ImageRgb8(img) => {
                println!("  Converting RGB with in-place processing...");
                let mut img = img;
                wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
                DynamicImage::ImageRgb8(img)
            }
            DynamicImage::ImageRgba8(img) => {
                println!("  Converting RGBA with in-place processing (alpha preserved)...");
                let mut img = img;
                wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
                DynamicImage::ImageRgba8(img)
            }
            DynamicImage::ImageRgb16(img) => {
                println!("  Converting RGB16 (downsampling to RGB8)...");
                let img = DynamicImage::ImageRgb16(img).to_rgb8();
                let mut img = img;
                wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
                DynamicImage::ImageRgb8(img)
            }
            DynamicImage::ImageRgba16(img) => {
                println!("  Converting RGBA16 (downsampling to RGBA8)...");
                let img = DynamicImage::ImageRgba16(img).to_rgba8();
                let mut img = img;
                wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
                DynamicImage::ImageRgba8(img)
            }
            DynamicImage::ImageLuma8(img) => {
                println!("  Converting grayscale (duplicating to RGB)...");
                let img = DynamicImage::ImageLuma8(img).to_rgb8();
                let mut img = img;
                wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
                DynamicImage::ImageRgb8(img)
            }
            DynamicImage::ImageLumaA8(img) => {
                println!("  Converting grayscale+alpha (converting to RGBA)...");
                let img = DynamicImage::ImageLumaA8(img).to_rgba8();
                let mut img = img;
                wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
                DynamicImage::ImageRgba8(img)
            }
            _ => {
                println!("  Converting to RGB8...");
                let img = image.to_rgb8();
                let mut img = img;
                wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
                DynamicImage::ImageRgb8(img)
            }
        };
        let apply_duration = apply_start.elapsed();
        println!("  LUT apply time (in-place): {:?}", apply_duration);

        let save_start = Instant::now();
        processed.save(output_path)?;
        let save_duration = save_start.elapsed();
        println!("  Image save time: {:?}", save_duration);
        println!(
            "  Total time: {:?}",
            load_duration + apply_duration + save_duration
        );
        println!("  Saved to: {}", output_path);
    } else {
        // Create test images and demonstrate in-place processing
        println!("\nCreating test images...");

        // RGB test with timing
        println!("\n1. RGB Image (800x600):");
        let create_start = Instant::now();
        let mut rgb_image = create_test_rgb_image(800, 600);
        let create_duration = create_start.elapsed();
        println!(
            "   Created test image with color gradients: {:?}",
            create_duration
        );

        // Apply LUT in-place
        let apply_start = Instant::now();
        wagahai_lut::lut::apply_rgb_mut(&lut, &mut rgb_image);
        let apply_duration = apply_start.elapsed();
        println!(
            "   Applied LUT in-place (zero allocation): {:?}",
            apply_duration
        );

        // Save result
        let save_start = Instant::now();
        rgb_image.save("test_output_rgb.png")?;
        let save_duration = save_start.elapsed();
        println!("   Saved to: test_output_rgb.png: {:?}", save_duration);

        // RGBA test with timing
        println!("\n2. RGBA Image (800x600):");
        let create_start = Instant::now();
        let mut rgba_image = create_test_rgba_image(800, 600);
        let create_duration = create_start.elapsed();
        println!(
            "   Created test image with color gradients and alpha: {:?}",
            create_duration
        );

        // Apply LUT in-place
        let apply_start = Instant::now();
        wagahai_lut::lut::apply_rgba_mut(&lut, &mut rgba_image);
        let apply_duration = apply_start.elapsed();
        println!(
            "   Applied LUT in-place (alpha preserved, zero allocation): {:?}",
            apply_duration
        );

        // Save result
        let save_start = Instant::now();
        rgba_image.save("test_output_rgba.png")?;
        let save_duration = save_start.elapsed();
        println!("   Saved to: test_output_rgba.png: {:?}", save_duration);

        // Test with color values
        println!("\n3. Color Value Tests:");
        let colors = [
            ("Red  ", [1.0, 0.0, 0.0]),
            ("Green", [0.0, 1.0, 0.0]),
            ("Blue ", [0.0, 0.0, 1.0]),
            ("White", [1.0, 1.0, 1.0]),
            ("Black", [0.0, 0.0, 0.0]),
        ];

        for (name, color) in colors {
            let result = lut.apply_to_color(color)?;
            println!(
                "   {:>5} [{:.1}, {:.1}, {:.1}] -> [{:.4}, {:.4}, {:.4}]",
                name, color[0], color[1], color[2], result[0], result[1], result[2]
            );
        }

        println!("\nTest images saved successfully!");
        println!("\nKey Features of In-Place Processing:");
        println!("  ✓ Zero memory allocation during processing");
        println!("  ✓ Direct modification of image buffer");
        println!("  ✓ Better memory efficiency for large images");
        println!("  ✓ Alpha channel preserved in RGBA images");
        println!("  ✓ Auto-detects 1D/3D LUT type");
    }

    Ok(())
}

/// Create a test RGB image with color gradients
fn create_test_rgb_image(width: u32, height: u32) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let mut img = ImageBuffer::new(width, height);

    for y in 0..height {
        for x in 0..width {
            let r = (x as f32) / (width as f32 - 1.0);
            let g = (y as f32) / (height as f32 - 1.0);
            let b = 1.0 - r;

            let pixel = Rgb([(r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8]);
            img.put_pixel(x, y, pixel);
        }
    }

    img
}

/// Create a test RGBA image with color gradients and alpha
fn create_test_rgba_image(width: u32, height: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
    let mut img = ImageBuffer::new(width, height);

    for y in 0..height {
        for x in 0..width {
            let r = (x as f32) / (width as f32 - 1.0);
            let g = (y as f32) / (height as f32 - 1.0);
            let b = 1.0 - r;
            let a = ((x + y) as f32) / ((width + height) as f32);

            let pixel = Rgba([
                (r * 255.0) as u8,
                (g * 255.0) as u8,
                (b * 255.0) as u8,
                (a * 255.0) as u8,
            ]);
            img.put_pixel(x, y, pixel);
        }
    }

    img
}