rosetta-squint-decode 1.1.2

Byte-exact PIL-compatible image decoder library (Rust). Part of rosetta-squint.
Documentation
use crate::bmp::decode_bmp;
use crate::gif::decode_gif;
use crate::heic::decode_heic;
use crate::png::decode_png;
use crate::tiff::decode_tiff;
use crate::error::{DecodeError, DecodeErrorKind};
use crate::types::{DecodedImage, Format};
use crate::webp::decode_webp;

/// Decode auto-detects the format from magic bytes and decodes.
pub fn decode(bytes: &[u8]) -> Result<DecodedImage, DecodeError> {
    let fmt = detect_format(bytes).ok_or_else(|| {
        DecodeError::new(DecodeErrorKind::UnsupportedFormat, None, "")
    })?;
    match fmt {
        Format::Bmp => decode_bmp(bytes),
        Format::Gif => decode_gif(bytes),
        Format::Heic => decode_heic(bytes),
        Format::Jpeg => crate::jpeg::decode_jpeg(bytes),
        Format::Png => decode_png(bytes),
        Format::Tiff => decode_tiff(bytes),
        Format::Webp => decode_webp(bytes),
        other => Err(DecodeError::new(DecodeErrorKind::UnsupportedFormat, Some(other), "")),
    }
}

/// Returns the Format if the magic bytes match, else None.
pub fn detect_format(bytes: &[u8]) -> Option<Format> {
    if bytes.len() < 2 {
        return None;
    }
    if bytes[0] == 0x42 && bytes[1] == 0x4D {
        return Some(Format::Bmp);
    }
    if bytes.len() >= 8
        && bytes[0] == 0x89
        && bytes[1] == 0x50
        && bytes[2] == 0x4E
        && bytes[3] == 0x47
        && bytes[4] == 0x0D
        && bytes[5] == 0x0A
        && bytes[6] == 0x1A
        && bytes[7] == 0x0A
    {
        return Some(Format::Png);
    }
    // GIF87a or GIF89a
    if bytes.len() >= 6
        && bytes[0] == b'G'
        && bytes[1] == b'I'
        && bytes[2] == b'F'
        && bytes[3] == b'8'
        && (bytes[4] == b'7' || bytes[4] == b'9')
        && bytes[5] == b'a'
    {
        return Some(Format::Gif);
    }
    // JPEG: starts with FF D8 (SOI marker); third byte FF is typical but not required for detection
    if bytes.len() >= 2
        && bytes[0] == 0xFF
        && bytes[1] == 0xD8
    {
        return Some(Format::Jpeg);
    }
    // WebP: RIFF????WEBP (bytes 0-3 = "RIFF", bytes 8-11 = "WEBP")
    if bytes.len() >= 12
        && bytes[0..4] == *b"RIFF"
        && bytes[8..12] == *b"WEBP"
    {
        return Some(Format::Webp);
    }
    // TIFF: little-endian (II + 0x002A) or big-endian (MM + 0x2A00)
    if bytes.len() >= 4
        && ((bytes[0] == 0x49 && bytes[1] == 0x49 && bytes[2] == 0x2A && bytes[3] == 0x00)
            || (bytes[0] == 0x4D && bytes[1] == 0x4D && bytes[2] == 0x00 && bytes[3] == 0x2A))
    {
        return Some(Format::Tiff);
    }
    // HEIC: ISO Base Media File (bytes 4-8 == "ftyp"), brand at bytes 8-12 must be
    // a HEVC/HEIF brand; AVIF brand is explicitly excluded (v1 scope).
    if bytes.len() >= 12 && &bytes[4..8] == b"ftyp" {
        let brand = &bytes[8..12];
        match brand {
            b"avif" | b"avis" => return None, // AVIF — not supported in v1
            b"heic" | b"heix" | b"mif1" | b"msf1" | b"hevc" | b"hevx" => {
                return Some(Format::Heic)
            }
            _ => {} // unknown brand → fall through (not detected)
        }
    }
    None
}

/// Returns the list of formats this port can decode.
pub fn supported_formats() -> Vec<Format> {
    vec![Format::Bmp, Format::Png, Format::Gif, Format::Jpeg, Format::Webp, Format::Tiff, Format::Heic]
}