Skip to main content

ai_imagesize/container/
pvrtc.rs

1use no_std_io::io::{BufRead, Seek, SeekFrom};
2
3use crate::{
4    util::{read_u32, read_u64, Endian},
5    ImageResult, ImageSize,
6};
7
8/// Compression formats for PVRTC containers
9///
10/// PowerVR containers can contain different compression formats beyond just PVRTC.
11/// This enum identifies the specific compression algorithm used within the PowerVR container.
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum PvrtcCompression {
14    /// PVRTC 2 bits per pixel RGB
15    Pvrtc2BppRgb,
16    /// PVRTC 2 bits per pixel RGBA
17    Pvrtc2BppRgba,
18    /// PVRTC 4 bits per pixel RGB
19    Pvrtc4BppRgb,
20    /// PVRTC 4 bits per pixel RGBA
21    Pvrtc4BppRgba,
22    /// ETC2 RGB compression
23    Etc2Rgb,
24    /// ETC2 RGBA compression  
25    Etc2Rgba,
26    /// ETC2 RGB with 1-bit alpha
27    Etc2RgbA1,
28    /// EAC R11 (single channel)
29    EacR11,
30    /// EAC RG11 (dual channel)
31    EacRg11,
32    /// Other/Unknown format
33    Unknown,
34}
35
36pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
37    // Check if this is PVR v3 format or legacy format
38    reader.seek(SeekFrom::Start(0))?;
39    let mut magic = [0u8; 4];
40    reader.read_exact(&mut magic)?;
41
42    if &magic == b"PVR\x03" {
43        // PVR v3 format structure:
44        // 0-3: Magic "PVR\x03"
45        // 4-7: Flags
46        // 8-15: Pixel format (8 bytes)
47        // 16-19: Colour space
48        // 20-23: Channel type
49        // 24-27: Height
50        // 28-31: Width
51        // 32-35: Depth
52        // ... rest of header
53        reader.seek(SeekFrom::Start(24))?;
54        let height = read_u32(reader, &Endian::Little)? as usize;
55        let width = read_u32(reader, &Endian::Little)? as usize;
56
57        Ok(ImageSize { width, height })
58    } else {
59        // Legacy PVR format structure:
60        // Header size: 4 bytes (little-endian)
61        // Height: 4 bytes (little-endian)
62        // Width: 4 bytes (little-endian)
63        // ... rest of legacy header
64        reader.seek(SeekFrom::Start(4))?;
65        let height = read_u32(reader, &Endian::Little)? as usize;
66        let width = read_u32(reader, &Endian::Little)? as usize;
67
68        Ok(ImageSize { width, height })
69    }
70}
71
72pub fn matches(header: &[u8]) -> bool {
73    // PVRTC files can have different magic numbers:
74    // Legacy format starts with header length (usually 52 bytes = 0x34000000 in little endian)
75    // Modern format has "PVR!" magic at different offsets
76    if header.len() >= 4 {
77        // Check for legacy format (header size = 52)
78        let header_size = u32::from_le_bytes([header[0], header[1], header[2], header[3]]);
79        if header_size == 52 {
80            return true;
81        }
82    }
83
84    // Check for "PVR!" magic at various positions
85    if header.len() >= 48 {
86        let pvr_magic = &header[44..48];
87        if pvr_magic == b"PVR!" {
88            return true;
89        }
90    }
91
92    // Check for "PVR\x03" which is the modern PVRTC format
93    if header.len() >= 4 && header.starts_with(b"PVR\x03") {
94        return true;
95    }
96
97    false
98}
99
100pub fn detect_compression<R: BufRead + Seek>(reader: &mut R) -> ImageResult<PvrtcCompression> {
101    // Check if this is PVR v3 format or legacy format
102    reader.seek(SeekFrom::Start(0))?;
103    let mut magic = [0u8; 4];
104    reader.read_exact(&mut magic)?;
105
106    if &magic == b"PVR\x03" {
107        // PVR v3 format - read pixel format from offset 8-15
108        reader.seek(SeekFrom::Start(8))?;
109        let pixel_format = read_u64(reader, &Endian::Little)?;
110
111        let compression = match pixel_format {
112            0 => PvrtcCompression::Pvrtc2BppRgb,  // PVRTCI_2BPP_RGB
113            1 => PvrtcCompression::Pvrtc2BppRgba, // PVRTCI_2BPP_RGBA
114            2 => PvrtcCompression::Pvrtc4BppRgb,  // PVRTCI_4BPP_RGB
115            3 => PvrtcCompression::Pvrtc4BppRgba, // PVRTCI_4BPP_RGBA
116            22 => PvrtcCompression::Etc2Rgb,      // ETC2_RGB
117            23 => PvrtcCompression::Etc2Rgba,     // ETC2_RGBA
118            24 => PvrtcCompression::Etc2RgbA1,    // ETC2_RGB_A1
119            25 => PvrtcCompression::EacR11,       // EAC_R11
120            26 => PvrtcCompression::EacRg11,      // EAC_RG11
121            _ => PvrtcCompression::Unknown,
122        };
123
124        Ok(compression)
125    } else {
126        // Legacy format - try to determine compression from other header fields
127        // For legacy format, we don't have the same pixel format field,
128        // so we'll need to infer from other data or default to Unknown
129        Ok(PvrtcCompression::Unknown)
130    }
131}