Skip to main content

ai_imagesize/container/
atc.rs

1use no_std_io::io::{BufRead, Seek, SeekFrom};
2
3use crate::{
4    util::{read_u16, read_u32, Endian},
5    ImageResult, ImageSize,
6};
7
8/// Compression formats for ATC containers
9///
10/// Adaptive Texture Compression (ATC) is used primarily on Adreno GPUs.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum AtcCompression {
13    /// ATC RGB (no alpha)
14    Rgb,
15    /// ATC RGBA with explicit alpha
16    RgbaExplicit,
17    /// ATC RGBA with interpolated alpha
18    RgbaInterpolated,
19    /// Other/Unknown ATC format
20    Unknown,
21}
22
23pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
24    // ATC files typically use DDS container format
25    // But also can be in PKM format or custom ATC format
26
27    // Try DDS format first
28    let mut header = [0u8; 4];
29    reader.read_exact(&mut header)?;
30
31    if header == *b"DDS " {
32        // DDS format - seek to dimensions
33        reader.seek(SeekFrom::Start(12))?;
34        let height = read_u32(reader, &Endian::Little)? as usize;
35        let width = read_u32(reader, &Endian::Little)? as usize;
36        return Ok(ImageSize { width, height });
37    }
38
39    // Try PKM format (used by some ATC implementations)
40    reader.seek(SeekFrom::Start(0))?;
41    let mut pkm_header = [0u8; 8];
42    reader.read_exact(&mut pkm_header)?;
43
44    if pkm_header.starts_with(b"PKM ")
45        && (pkm_header[4..6] == [b'1', b'0'] || pkm_header[4..6] == [b'2', b'0'])
46    {
47        // Check for ATC-specific data types
48        let data_type = u16::from_be_bytes([pkm_header[6], pkm_header[7]]);
49        if matches!(data_type, 0x8C92 | 0x8C93 | 0x87EE) {
50            // ATC_RGB, ATC_RGBA_EXPLICIT_ALPHA, and ATC_RGBA_INTERPOLATED_ALPHA
51            reader.seek(SeekFrom::Start(8))?; // Skip magic + version + data type
52            let _extended_width = read_u16(reader, &Endian::Big)?;
53            let _extended_height = read_u16(reader, &Endian::Big)?;
54            let width = read_u16(reader, &Endian::Big)? as usize;
55            let height = read_u16(reader, &Endian::Big)? as usize;
56            return Ok(ImageSize { width, height });
57        }
58    }
59
60    // Fallback: assume basic ATC dimensions at a standard location
61    reader.seek(SeekFrom::Start(4))?;
62    let height = read_u32(reader, &Endian::Little)? as usize;
63    let width = read_u32(reader, &Endian::Little)? as usize;
64    Ok(ImageSize { width, height })
65}
66
67pub fn matches(header: &[u8]) -> bool {
68    // Only check for PKM format with ATC data types
69    // DDS files with ATC compression should be handled by the DDS format detector
70    if header.len() >= 8
71        && header.starts_with(b"PKM ")
72        && (header[4..6] == [b'1', b'0'] || header[4..6] == [b'2', b'0'])
73    {
74        let data_type = u16::from_be_bytes([header[6], header[7]]);
75        return matches!(data_type, 0x8C92 | 0x8C93 | 0x87EE);
76    }
77
78    false
79}
80
81pub fn detect_compression<R: BufRead + Seek>(reader: &mut R) -> ImageResult<AtcCompression> {
82    // Check if it's a PKM format first
83    let mut header = [0u8; 8];
84    reader.seek(SeekFrom::Start(0))?;
85    reader.read_exact(&mut header)?;
86
87    if header.starts_with(b"PKM ") && (header[4..6] == [b'1', b'0'] || header[4..6] == [b'2', b'0'])
88    {
89        let data_type = u16::from_be_bytes([header[6], header[7]]);
90        let compression = match data_type {
91            0x8C92 => AtcCompression::Rgb,              // ATC_RGB
92            0x8C93 => AtcCompression::RgbaExplicit,     // ATC_RGBA_EXPLICIT_ALPHA
93            0x87EE => AtcCompression::RgbaInterpolated, // ATC_RGBA_INTERPOLATED_ALPHA
94            _ => AtcCompression::Unknown,
95        };
96        return Ok(compression);
97    }
98
99    // Check if it's DDS format
100    if header[0..4] == *b"DDS " {
101        // For DDS, we'd need to check the pixel format section for ATC FourCC
102        // This is a more complex check that would examine the DDS pixel format
103        return Ok(AtcCompression::Unknown); // Default for DDS-contained ATC
104    }
105
106    Ok(AtcCompression::Unknown)
107}