tx2_iff/
format.rs

1//! IFF file format structures and serialization
2//!
3//! The PPF-IFF format consists of:
4//!
5//! ```text
6//! [Header]
7//! [Layer 1: Wavelet Skeleton]
8//! [Layer 2: Texture Synthesis Regions]
9//! [Layer 3: Warp Field]
10//! [Residual Data (sparse)]
11//! ```
12//!
13//! ## File Layout
14//!
15//! ```text
16//! Offset  Size  Description
17//! 0       4     Magic ("PPFI")
18//! 4       2     Version (major.minor)
19//! 6       4     Width
20//! 10      4     Height
21//! 14      1     Wavelet levels
22//! 15      1     Flags
23//! 16      4     Layer 1 size (bytes)
24//! 20      4     Layer 2 size (bytes)
25//! 24      4     Layer 3 size (bytes)
26//! 28      4     Residual size (bytes)
27//! 32      ...   Layer data
28//! ```
29
30use crate::error::{IffError, Result};
31use crate::texture::Region;
32use crate::warp::WarpField;
33use crate::wavelet::WaveletDecomposition;
34use crate::compression::{compress_rle, decompress_rle};
35use crate::MAGIC;
36use serde::{Deserialize, Serialize};
37
38/// IFF file version
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40pub struct Version {
41    pub major: u8,
42    pub minor: u8,
43}
44
45impl Version {
46    /// Current version
47    pub const CURRENT: Version = Version { major: 0, minor: 2 };
48
49    /// Check if version is compatible
50    pub fn is_compatible(&self, other: &Version) -> bool {
51        self.major == other.major
52    }
53}
54
55/// IFF file flags
56#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
57pub struct Flags {
58    /// Has alpha channel
59    pub has_alpha: bool,
60    /// Is lossless
61    pub lossless: bool,
62    /// Uses GPU-optimized format
63    pub gpu_optimized: bool,
64    /// Uses YCoCg color space with 4:2:0 subsampling
65    pub ycocg_420: bool,
66}
67
68impl Default for Flags {
69    fn default() -> Self {
70        Flags {
71            has_alpha: false,
72            lossless: false,
73            gpu_optimized: true,
74            ycocg_420: true,
75        }
76    }
77}
78
79impl Flags {
80    /// Convert to u8
81    pub fn to_byte(&self) -> u8 {
82        let mut byte = 0u8;
83        if self.has_alpha {
84            byte |= 0x01;
85        }
86        if self.lossless {
87            byte |= 0x02;
88        }
89        if self.gpu_optimized {
90            byte |= 0x04;
91        }
92        if self.ycocg_420 {
93            byte |= 0x08;
94        }
95        byte
96    }
97
98    /// Create from u8
99    pub fn from_byte(byte: u8) -> Self {
100        Flags {
101            has_alpha: (byte & 0x01) != 0,
102            lossless: (byte & 0x02) != 0,
103            gpu_optimized: (byte & 0x04) != 0,
104            ycocg_420: (byte & 0x08) != 0,
105        }
106    }
107}
108
109/// IFF file header
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct Header {
112    /// Magic number (0x50504649 = "PPFI")
113    pub magic: u32,
114    /// File version
115    pub version: Version,
116    /// Image width
117    pub width: u32,
118    /// Image height
119    pub height: u32,
120    /// Number of wavelet decomposition levels
121    pub wavelet_levels: u8,
122    /// Flags
123    pub flags: Flags,
124    /// Layer 1 size in bytes
125    pub layer1_size: u32,
126    /// Layer 2 size in bytes
127    pub layer2_size: u32,
128    /// Layer 3 size in bytes
129    pub layer3_size: u32,
130    /// Residual size in bytes
131    pub residual_size: u32,
132}
133
134impl Header {
135    /// Header size in bytes (fixed)
136    pub const SIZE: usize = 32;
137
138    /// Create a new header
139    pub fn new(width: u32, height: u32, wavelet_levels: u8) -> Self {
140        Header {
141            magic: MAGIC,
142            version: Version::CURRENT,
143            width,
144            height,
145            wavelet_levels,
146            flags: Flags::default(),
147            layer1_size: 0,
148            layer2_size: 0,
149            layer3_size: 0,
150            residual_size: 0,
151        }
152    }
153
154    /// Serialize header to bytes
155    pub fn to_bytes(&self) -> Vec<u8> {
156        let mut bytes = Vec::with_capacity(Self::SIZE);
157
158        // Magic (4 bytes)
159        bytes.extend_from_slice(&self.magic.to_be_bytes());
160
161        // Version (2 bytes)
162        bytes.push(self.version.major);
163        bytes.push(self.version.minor);
164
165        // Dimensions (8 bytes)
166        bytes.extend_from_slice(&self.width.to_be_bytes());
167        bytes.extend_from_slice(&self.height.to_be_bytes());
168
169        // Wavelet levels (1 byte)
170        bytes.push(self.wavelet_levels);
171
172        // Flags (1 byte)
173        bytes.push(self.flags.to_byte());
174
175        // Layer sizes (16 bytes)
176        bytes.extend_from_slice(&self.layer1_size.to_be_bytes());
177        bytes.extend_from_slice(&self.layer2_size.to_be_bytes());
178        bytes.extend_from_slice(&self.layer3_size.to_be_bytes());
179        bytes.extend_from_slice(&self.residual_size.to_be_bytes());
180
181        bytes
182    }
183
184    /// Deserialize header from bytes
185    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
186        if bytes.len() < Self::SIZE {
187            return Err(IffError::InsufficientData {
188                expected: Self::SIZE,
189                got: bytes.len(),
190            });
191        }
192
193        // Parse magic
194        let magic = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
195        if magic != MAGIC {
196            return Err(IffError::InvalidMagic {
197                expected: MAGIC,
198                got: magic,
199            });
200        }
201
202        // Parse version
203        let version = Version {
204            major: bytes[4],
205            minor: bytes[5],
206        };
207
208        // Parse dimensions
209        let width = u32::from_be_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]);
210        let height = u32::from_be_bytes([bytes[10], bytes[11], bytes[12], bytes[13]]);
211
212        // Validate dimensions
213        if width == 0 || height == 0 {
214            return Err(IffError::Other("Invalid dimensions".to_string()));
215        }
216
217        // Parse wavelet levels
218        let wavelet_levels = bytes[14];
219
220        // Parse flags
221        let flags = Flags::from_byte(bytes[15]);
222
223        // Parse layer sizes
224        let layer1_size = u32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]);
225        let layer2_size = u32::from_be_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]);
226        let layer3_size = u32::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]);
227        let residual_size = u32::from_be_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]);
228
229        Ok(Header {
230            magic,
231            version,
232            width,
233            height,
234            wavelet_levels,
235            flags,
236            layer1_size,
237            layer2_size,
238            layer3_size,
239            residual_size,
240        })
241    }
242}
243
244/// Layer 1: Wavelet skeleton
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct Layer1 {
247    pub y: WaveletDecomposition,
248    pub co: WaveletDecomposition,
249    pub cg: WaveletDecomposition,
250}
251
252impl Layer1 {
253    pub fn new(width: u32, height: u32, levels: usize) -> Self {
254        Layer1 {
255            y: WaveletDecomposition::new(width, height, levels, 1),
256            co: WaveletDecomposition::new(width / 2, height / 2, levels, 1),
257            cg: WaveletDecomposition::new(width / 2, height / 2, levels, 1),
258        }
259    }
260}
261
262/// Layer 2: Texture synthesis regions
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct Layer2 {
265    /// Texture regions
266    pub regions: Vec<Region>,
267}
268
269impl Layer2 {
270    /// Create empty layer
271    pub fn new() -> Self {
272        Layer2 {
273            regions: Vec::new(),
274        }
275    }
276
277    /// Add a region
278    pub fn add_region(&mut self, region: Region) {
279        self.regions.push(region);
280    }
281}
282
283impl Default for Layer2 {
284    fn default() -> Self {
285        Self::new()
286    }
287}
288
289/// Layer 3: Symplectic warp field
290pub type Layer3 = WarpField;
291
292/// Residual data (Compressed)
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct Residual {
295    pub width: u32,
296    pub height: u32,
297    pub channels: u8,
298    /// Compressed residual data (RLE encoded)
299    /// Layout: [Channel 0] [Channel 1] [Channel 2]
300    pub data: Vec<u8>,
301}
302
303impl Residual {
304    /// Create empty residual
305    pub fn new(width: u32, height: u32) -> Self {
306        Residual {
307            width,
308            height,
309            channels: 3,
310            data: Vec::new(),
311        }
312    }
313
314    /// Create from dense residual image
315    pub fn from_dense(width: u32, height: u32, pixels: &[[i16; 3]]) -> Result<Self> {
316        let mut channels = vec![Vec::with_capacity(pixels.len()); 3];
317        
318        for p in pixels {
319            channels[0].push(p[0] as i32);
320            channels[1].push(p[1] as i32);
321            channels[2].push(p[2] as i32);
322        }
323
324        let mut all_data = Vec::new();
325        all_data.extend_from_slice(&channels[0]);
326        all_data.extend_from_slice(&channels[1]);
327        all_data.extend_from_slice(&channels[2]);
328
329        // First pass: RLE
330        let rle_compressed = compress_rle(&all_data)?;
331        
332        // Second pass: Zstd
333        // Level 3 is default, good balance. Level 0 uses default.
334        let final_compressed = zstd::stream::encode_all(std::io::Cursor::new(rle_compressed), 3)
335            .map_err(|e| IffError::Other(format!("Zstd compression failed: {}", e)))?;
336
337        Ok(Residual {
338            width,
339            height,
340            channels: 3,
341            data: final_compressed,
342        })
343    }
344
345    /// Decompress to dense residual image
346    pub fn to_dense(&self) -> Result<Vec<[i16; 3]>> {
347        let total_pixels = (self.width * self.height) as usize;
348        let expected_len = total_pixels * 3;
349
350        // First pass: Zstd decompression
351        let rle_compressed = zstd::stream::decode_all(std::io::Cursor::new(&self.data))
352            .map_err(|e| IffError::Other(format!("Zstd decompression failed: {}", e)))?;
353
354        // Second pass: RLE decompression
355        let all_data = decompress_rle(&rle_compressed, Some(expected_len))?;
356        
357        if all_data.len() < expected_len {
358             return Err(IffError::Other("Insufficient residual data".to_string()));
359        }
360
361        let mut pixels = Vec::with_capacity(total_pixels);
362        for i in 0..total_pixels {
363            pixels.push([
364                all_data[i] as i16,
365                all_data[total_pixels + i] as i16,
366                all_data[2 * total_pixels + i] as i16,
367            ]);
368        }
369
370        Ok(pixels)
371    }
372}
373
374/// Complete IFF image
375#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct IffImage {
377    /// File header
378    pub header: Header,
379    /// Layer 1: Wavelet skeleton
380    pub layer1: Layer1,
381    /// Layer 2: Texture synthesis
382    pub layer2: Layer2,
383    /// Layer 3: Warp field
384    pub layer3: Layer3,
385    /// Residual correction
386    pub residual: Residual,
387}
388
389impl IffImage {
390    /// Create a new IFF image
391    pub fn new(width: u32, height: u32, wavelet_levels: u8) -> Self {
392        IffImage {
393            header: Header::new(width, height, wavelet_levels),
394            layer1: Layer1::new(width, height, wavelet_levels as usize),
395            layer2: Layer2::new(),
396            layer3: WarpField::new(width, height),
397            residual: Residual::new(width, height),
398        }
399    }
400
401    /// Serialize to bytes
402    pub fn to_bytes(&self) -> Result<Vec<u8>> {
403        // Serialize layers
404        let layer1_bytes = bincode::serialize(&self.layer1)?;
405        let layer2_bytes = bincode::serialize(&self.layer2)?;
406        let layer3_bytes = bincode::serialize(&self.layer3)?;
407        let residual_bytes = bincode::serialize(&self.residual)?;
408
409        // Update header with sizes
410        let mut header = self.header.clone();
411        header.layer1_size = layer1_bytes.len() as u32;
412        header.layer2_size = layer2_bytes.len() as u32;
413        header.layer3_size = layer3_bytes.len() as u32;
414        header.residual_size = residual_bytes.len() as u32;
415
416        // Combine all parts
417        let mut bytes = header.to_bytes();
418        bytes.extend_from_slice(&layer1_bytes);
419        bytes.extend_from_slice(&layer2_bytes);
420        bytes.extend_from_slice(&layer3_bytes);
421        bytes.extend_from_slice(&residual_bytes);
422
423        Ok(bytes)
424    }
425
426    /// Deserialize from bytes
427    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
428        // Parse header
429        let header = Header::from_bytes(bytes)?;
430
431        // Check version compatibility
432        if !header.version.is_compatible(&Version::CURRENT) {
433            return Err(IffError::UnsupportedVersion(
434                (header.version.major as u32) << 8 | header.version.minor as u32,
435            ));
436        }
437
438        // Calculate offsets
439        let offset1 = Header::SIZE;
440        let offset2 = offset1 + header.layer1_size as usize;
441        let offset3 = offset2 + header.layer2_size as usize;
442        let offset_residual = offset3 + header.layer3_size as usize;
443        let total_size = offset_residual + header.residual_size as usize;
444
445        // Validate total size
446        if bytes.len() < total_size {
447            return Err(IffError::InsufficientData {
448                expected: total_size,
449                got: bytes.len(),
450            });
451        }
452
453        // Deserialize layers
454        let layer1: Layer1 = bincode::deserialize(&bytes[offset1..offset2])?;
455        let layer2: Layer2 = bincode::deserialize(&bytes[offset2..offset3])?;
456        let layer3: Layer3 = bincode::deserialize(&bytes[offset3..offset_residual])?;
457        let residual: Residual = bincode::deserialize(&bytes[offset_residual..total_size])?;
458
459        Ok(IffImage {
460            header,
461            layer1,
462            layer2,
463            layer3,
464            residual,
465        })
466    }
467
468    /// Get file size estimate
469    pub fn estimated_size(&self) -> usize {
470        Header::SIZE
471            + self.layer1.y.data.len() + self.layer1.co.data.len() + self.layer1.cg.data.len()
472            + self.layer2.regions.len() * 32
473            + self.layer3.vortices.len() * 16
474            + self.residual.data.len()
475    }
476}
477
478/// Layer type identifier
479#[derive(Debug, Clone, Copy, PartialEq, Eq)]
480pub enum Layer {
481    /// Wavelet skeleton
482    Skeleton,
483    /// Texture synthesis
484    Texture,
485    /// Warp field
486    Warp,
487    /// Residual correction
488    Residual,
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494
495    #[test]
496    fn test_version() {
497        let v1 = Version { major: 0, minor: 2 };
498        let v2 = Version { major: 0, minor: 2 };
499        let v3 = Version { major: 1, minor: 0 };
500
501        assert!(v1.is_compatible(&v2)); // Same major version
502        assert!(!v1.is_compatible(&v3)); // Different major version
503    }
504
505    #[test]
506    fn test_flags() {
507        let flags = Flags {
508            has_alpha: true,
509            lossless: false,
510            gpu_optimized: true,
511            ycocg_420: true,
512        };
513
514        let byte = flags.to_byte();
515        let restored = Flags::from_byte(byte);
516
517        assert_eq!(flags.has_alpha, restored.has_alpha);
518        assert_eq!(flags.lossless, restored.lossless);
519        assert_eq!(flags.gpu_optimized, restored.gpu_optimized);
520        assert_eq!(flags.ycocg_420, restored.ycocg_420);
521    }
522}