tx2_iff/
decoder.rs

1//! Image decoder (Layer reconstruction)
2//!
3//! The decoder reconstructs an image from the three layers:
4//! 1. Inverse wavelet transform (skeleton)
5//! 2. Texture synthesis (deterministic noise)
6//! 3. Warp field application (geometric deformation)
7//! 4. Residual addition (correction)
8
9use crate::error::Result;
10use crate::format::IffImage;
11use crate::texture::TextureSynthesizer;
12use crate::warp::BicubicSampler;
13use crate::wavelet::Cdf53Transform;
14use crate::color::{ycocg_to_rgb, upsample_420, YCoCgImage, Channel};
15use serde::{Deserialize, Serialize};
16
17/// Decoder configuration
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct DecoderConfig {
20    /// Enable Layer 2 (texture synthesis)
21    pub enable_layer2: bool,
22    /// Enable Layer 3 (warp fields)
23    pub enable_layer3: bool,
24    /// Enable residual correction
25    pub enable_residual: bool,
26}
27
28impl Default for DecoderConfig {
29    fn default() -> Self {
30        DecoderConfig {
31            enable_layer2: true,
32            enable_layer3: true,
33            enable_residual: true,
34        }
35    }
36}
37
38/// Image decoder
39pub struct Decoder {
40    config: DecoderConfig,
41}
42
43impl Decoder {
44    /// Create a new decoder with given configuration
45    pub fn new(config: DecoderConfig) -> Self {
46        Decoder { config }
47    }
48
49    /// Decode an IFF image to RGB buffer
50    pub fn decode(&self, iff_image: &IffImage) -> Result<Vec<[u8; 3]>> {
51        let width = iff_image.header.width as usize;
52        let height = iff_image.header.height as usize;
53
54        // Layer 1: Inverse wavelet transform
55        let transform = Cdf53Transform::new(iff_image.header.wavelet_levels as usize);
56        
57        let mut image = if iff_image.header.flags.ycocg_420 {
58            // Decode Y (Luma)
59            let y_coeffs = iff_image.layer1.y.to_dense()?;
60            let y_data = transform.inverse(&y_coeffs[0], width, height)?;
61            
62            // Decode Co (Chroma Orange) - Subsampled
63            let co_width = iff_image.layer1.co.width as usize;
64            let co_height = iff_image.layer1.co.height as usize;
65            let co_coeffs = iff_image.layer1.co.to_dense()?;
66            let co_data = transform.inverse(&co_coeffs[0], co_width, co_height)?;
67            
68            // Decode Cg (Chroma Green) - Subsampled
69            let cg_width = iff_image.layer1.cg.width as usize;
70            let cg_height = iff_image.layer1.cg.height as usize;
71            let cg_coeffs = iff_image.layer1.cg.to_dense()?;
72            let cg_data = transform.inverse(&cg_coeffs[0], cg_width, cg_height)?;
73            
74            // Upsample Chroma
75            let co_channel = Channel { width: co_width, height: co_height, data: co_data };
76            let cg_channel = Channel { width: cg_width, height: cg_height, data: cg_data };
77            
78            let co_up = upsample_420(&co_channel, width, height);
79            let cg_up = upsample_420(&cg_channel, width, height);
80            
81            // Convert YCoCg to RGB
82            let ycocg = YCoCgImage {
83                width,
84                height,
85                y: Channel { width, height, data: y_data },
86                co: co_up,
87                cg: cg_up,
88            };
89            
90            ycocg_to_rgb(&ycocg)?
91        } else {
92            // Legacy/RGB Mode
93            // In this mode, we stored R in Y, G in Co, B in Cg
94            let r_coeffs = iff_image.layer1.y.to_dense()?;
95            let g_coeffs = iff_image.layer1.co.to_dense()?;
96            let b_coeffs = iff_image.layer1.cg.to_dense()?;
97            
98            let r_data = transform.inverse(&r_coeffs[0], width, height)?;
99            let g_data = transform.inverse(&g_coeffs[0], width, height)?;
100            let b_data = transform.inverse(&b_coeffs[0], width, height)?;
101            
102            let mut pixels = Vec::with_capacity(width * height);
103            for i in 0..(width * height) {
104                pixels.push([
105                    (r_data[i] + 128).clamp(0, 255) as u8,
106                    (g_data[i] + 128).clamp(0, 255) as u8,
107                    (b_data[i] + 128).clamp(0, 255) as u8,
108                ]);
109            }
110            pixels
111        };
112
113        // Layer 2: Texture synthesis (if enabled)
114        if self.config.enable_layer2 && !iff_image.layer2.regions.is_empty() {
115            let synthesizer = TextureSynthesizer::new();
116            for region in &iff_image.layer2.regions {
117                // Synthesize texture for each region
118                for y in 0..region.h {
119                    for x in 0..region.w {
120                        let global_x = region.x + x;
121                        let global_y = region.y + y;
122                        let idx = (global_y as usize) * width + (global_x as usize);
123
124                        if idx < image.len() {
125                            let color = synthesizer.synthesize_region_pixel(region, x, y);
126                            image[idx] = color;
127                        }
128                    }
129                }
130            }
131        }
132
133        // Layer 3: Warp field (if enabled)
134        if self.config.enable_layer3 && !iff_image.layer3.vortices.is_empty() {
135            // Create warped buffer
136            let mut warped = image.clone();
137
138            for y in 0..height {
139                for x in 0..width {
140                    let (src_x, src_y) = iff_image.layer3.warp_backwards(x as u16, y as u16);
141
142                    // Sample with bicubic interpolation
143                    let color = BicubicSampler::sample(&image, width, height, src_x, src_y);
144
145                    let idx = y * width + x;
146                    warped[idx] = color;
147                }
148            }
149
150            image = warped;
151        }
152
153        // Layer 4: Residual correction (if enabled)
154        if self.config.enable_residual && !iff_image.residual.data.is_empty() {
155            let residual_pixels = iff_image.residual.to_dense()?;
156            
157            if residual_pixels.len() != image.len() {
158                 log::warn!("Residual size mismatch: expected {}, got {}", image.len(), residual_pixels.len());
159            }
160
161            for (i, rgb) in residual_pixels.iter().enumerate() {
162                if i < image.len() {
163                    for c in 0..3 {
164                        let corrected = (image[i][c] as i32 + rgb[c] as i32).clamp(0, 255);
165                        image[i][c] = corrected as u8;
166                    }
167                }
168            }
169        }
170
171        Ok(image)
172    }
173
174    /// Decode to a dynamic image (convenience method)
175    #[cfg(feature = "decoder")]
176    pub fn decode_to_image(&self, iff_image: &IffImage) -> Result<image::DynamicImage> {
177        let buffer = self.decode(iff_image)?;
178        let width = iff_image.header.width;
179        let height = iff_image.header.height;
180
181        // Convert to ImageBuffer
182        let mut img_buffer = image::RgbImage::new(width, height);
183
184        for (i, pixel) in buffer.iter().enumerate() {
185            let x = (i % width as usize) as u32;
186            let y = (i / width as usize) as u32;
187            img_buffer.put_pixel(x, y, image::Rgb(*pixel));
188        }
189
190        Ok(image::DynamicImage::ImageRgb8(img_buffer))
191    }
192}
193
194/// Create a decoder with default configuration
195impl Default for Decoder {
196    fn default() -> Self {
197        Decoder::new(DecoderConfig::default())
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_decoder_config_default() {
207        let config = DecoderConfig::default();
208        assert!(config.enable_layer2);
209        assert!(config.enable_layer3);
210        assert!(config.enable_residual);
211    }
212
213    #[test]
214    fn test_decoder_creation() {
215        let config = DecoderConfig::default();
216        let decoder = Decoder::new(config);
217        assert!(decoder.config.enable_layer2);
218    }
219
220    #[test]
221    fn test_decode_empty_image() {
222        let decoder = Decoder::default();
223        let iff_image = IffImage::new(64, 64, 3);
224
225        let result = decoder.decode(&iff_image);
226        assert!(result.is_ok());
227
228        let buffer = result.unwrap();
229        assert_eq!(buffer.len(), 64 * 64);
230    }
231}