img-optimize 0.1.0

Image minify library for Rust
Documentation
#[cfg(feature = "png")]
use png::BitDepth;
use crate::structs::*;
use crate::magic;

pub fn read(buf: &[u8]) -> Result<ImageInfo, ()> {
    let file_type = magic::detect_file_type_with_magic_number(&buf).unwrap();

    match file_type {
        "png" => {
            #[cfg(feature="png")]
            return Ok(read_png(&buf));
            #[cfg(not(feature="png"))]
            return Err(());
        }
        "webp" => {
            #[cfg(feature="webp")]
            return Ok(read_webp(&buf));
            #[cfg(not(feature="webp"))]
            return Err(());
        }
        "jxl" => {
            #[cfg(feature="jpegxl")]
            return Ok(read_jpeg_xl(&buf));
            #[cfg(not(feature="jpegxl"))]
            return Err(());
        }
        _ => Err(()),
    }
}

#[cfg(feature="webp")]
pub fn read_webp(buf: &[u8]) -> ImageInfo {
    let decoder = webp::Decoder::new(&buf);

    let webp_info = decoder.decode().unwrap();

    let bit_per_pixel = match webp_info.is_alpha() {
        true => 4, // RGBA
        false => 3, // RGB
    };

    let pixels_num = webp_info.width() as usize * webp_info.height() as usize;
    let dots_num = pixels_num * bit_per_pixel;

    let image = webp_info.to_image();

    let mut pixels: Vec<u8> = vec![0; dots_num];

    if webp_info.is_alpha() {
        let image = image.as_rgba8().unwrap();

        for x in 0..webp_info.width() {
            for y in 0..webp_info.height() {
                let pixel = image.get_pixel(x, y);
                let i: usize = (y * webp_info.width() + x) as usize;
                pixels[i * 4 + 0] = pixel[0];
                pixels[i * 4 + 1] = pixel[1];
                pixels[i * 4 + 2] = pixel[2];
                pixels[i * 4 + 3] = pixel[3];
            }
        }
    } else {
        let image = image.as_rgb8().unwrap();

        for x in 0..webp_info.width() {
            for y in 0..webp_info.height() {
                let pixel = image.get_pixel(x, y);
                let i: usize = (y * webp_info.width() + x) as usize;
                pixels[i * 3 + 0] = pixel[0];
                pixels[i * 3 + 1] = pixel[1];
                pixels[i * 3 + 2] = pixel[2];
            }
        }
    }

    let im_info = ImageInfo {
        width: webp_info.width(),
        height: webp_info.height(),
        bit_depth: 8,
        color_type: match webp_info.is_alpha() {
            true => 6, // RGBA
            false => 2, // RGB
        },
        pixels: ImagePixels::U8(pixels),
    };

    im_info
}

#[cfg(feature="png")]
pub fn read_png(buf: &[u8]) -> ImageInfo {
    let decoder = png::Decoder::new(buf);
    let mut reader = decoder.read_info().unwrap();
    let info = reader.info().to_owned();

    // ToDo: Verify this works in 1-8 bit depth.

    let mut dot_per_pixel = match info.color_type {
        png::ColorType::Grayscale => 1,
        png::ColorType::Rgb => 3,
        png::ColorType::Indexed => 1,
        png::ColorType::GrayscaleAlpha => 2,
        png::ColorType::Rgba => 4,
    };

    if info.bit_depth == BitDepth::Sixteen {
        dot_per_pixel *= 2;
    }

    let mut pxls = vec![0; info.width as usize * info.height as usize * dot_per_pixel];

    reader.next_frame(&mut pxls).unwrap();

    let pixels: ImagePixels;

    if info.bit_depth == BitDepth::Sixteen {
        let mut pixels16 = vec![0; info.width as usize * info.height as usize * dot_per_pixel / 2];

        for i in 0..pixels16.len() {
            pixels16[i] = (pxls[i * 2] as u16) << 8 | pxls[i * 2 + 1] as u16;
        }

        pixels = ImagePixels::U16(pixels16);
    } else {
        let mut pixels8 = vec![0; info.width as usize * info.height as usize * dot_per_pixel];

        for i in 0..pixels8.len() {
            pixels8[i] = pxls[i];
        }

        pixels = ImagePixels::U8(pixels8);
    }

    let im_info = ImageInfo {
        width: info.width,
        height: info.height,
        bit_depth: match info.bit_depth {
            BitDepth::One => 1,
            BitDepth::Two => 2,
            BitDepth::Four => 4,
            BitDepth::Eight => 8,
            BitDepth::Sixteen => 16,
        },
        color_type: match info.color_type {
            png::ColorType::Grayscale => 0,
            png::ColorType::Rgb => 2,
            png::ColorType::Indexed => 3,
            png::ColorType::GrayscaleAlpha => 4,
            png::ColorType::Rgba => 6,
        },
        pixels,
    };

    im_info
}

#[cfg(feature="jpegxl")]
pub fn read_jpeg_xl(buf: &[u8]) -> ImageInfo {
    let mut decoder_builder = jpegxl_rs::decode::decoder_builder();
    let mut decoder = decoder_builder.build().unwrap();
    let data = decoder.decode(buf).unwrap();

    let metadata = data.0;
    let pixels = data.1;

    let bit_depth = match pixels {
        jpegxl_rs::decode::Pixels::Uint8(_) => 8,
        jpegxl_rs::decode::Pixels::Uint16(_) => 16,
        jpegxl_rs::decode::Pixels::Float16(_) => 16,
        jpegxl_rs::decode::Pixels::Float(_) => 32,
    };

    let pixels = match pixels {
        jpegxl_rs::decode::Pixels::Uint8(pixels) => {
            ImagePixels::U8(pixels)
        },
        jpegxl_rs::decode::Pixels::Uint16(pixels) => {
            ImagePixels::U16(pixels)
        },
        jpegxl_rs::decode::Pixels::Float16(pixels) => {
            ImagePixels::F16(pixels)
        },
        jpegxl_rs::decode::Pixels::Float(pixels) => {
            ImagePixels::F32(pixels)
        },
    };

    let mut im_info = ImageInfo {
        width: metadata.width,
        height: metadata.height,
        bit_depth,
        color_type: (metadata.num_color_channels + (if metadata.has_alpha_channel { 1 } else { 0 })) as u8,
        pixels,
    };

    im_info
}