1use crate::error::{IffError, Result};
21
22#[derive(Debug, Clone)]
24pub struct Channel {
25 pub width: usize,
26 pub height: usize,
27 pub data: Vec<i32>,
28}
29
30impl Channel {
31 pub fn new(width: usize, height: usize) -> Self {
32 Channel {
33 width,
34 height,
35 data: vec![0; width * height],
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct YCoCgImage {
43 pub width: usize,
44 pub height: usize,
45 pub y: Channel,
46 pub co: Channel,
47 pub cg: Channel,
48}
49
50pub fn rgb_to_ycocg(rgb: &[[u8; 3]], width: usize, height: usize) -> YCoCgImage {
52 let mut y_channel = Channel::new(width, height);
53 let mut co_channel = Channel::new(width, height);
54 let mut cg_channel = Channel::new(width, height);
55
56 for i in 0..rgb.len() {
57 let r = rgb[i][0] as i32;
58 let g = rgb[i][1] as i32;
59 let b = rgb[i][2] as i32;
60
61 let co = r - b;
62 let tmp = b + (co >> 1);
63 let cg = g - tmp;
64 let y = tmp + (cg >> 1);
65
66 y_channel.data[i] = y;
67 co_channel.data[i] = co;
68 cg_channel.data[i] = cg;
69 }
70
71 YCoCgImage {
72 width,
73 height,
74 y: y_channel,
75 co: co_channel,
76 cg: cg_channel,
77 }
78}
79
80pub fn ycocg_to_rgb(image: &YCoCgImage) -> Result<Vec<[u8; 3]>> {
82 let width = image.width;
83 let height = image.height;
84 let len = width * height;
85
86 if image.y.data.len() != len || image.co.data.len() != len || image.cg.data.len() != len {
87 return Err(IffError::Other("Channel dimensions mismatch".to_string()));
88 }
89
90 let mut rgb = Vec::with_capacity(len);
91
92 for i in 0..len {
93 let y = image.y.data[i];
94 let co = image.co.data[i];
95 let cg = image.cg.data[i];
96
97 let tmp = y - (cg >> 1);
98 let g = cg + tmp;
99 let b = tmp - (co >> 1);
100 let r = b + co;
101
102 rgb.push([
103 r.clamp(0, 255) as u8,
104 g.clamp(0, 255) as u8,
105 b.clamp(0, 255) as u8,
106 ]);
107 }
108
109 Ok(rgb)
110}
111
112pub fn subsample_420(channel: &Channel) -> Channel {
114 let new_width = channel.width / 2;
115 let new_height = channel.height / 2;
116 let mut new_data = Vec::with_capacity(new_width * new_height);
117
118 for y in 0..new_height {
119 for x in 0..new_width {
120 let idx = (y * 2) * channel.width + (x * 2);
122 let sum = channel.data[idx]
123 + channel.data[idx + 1]
124 + channel.data[idx + channel.width]
125 + channel.data[idx + channel.width + 1];
126 new_data.push(sum / 4);
127 }
128 }
129
130 Channel {
131 width: new_width,
132 height: new_height,
133 data: new_data,
134 }
135}
136
137pub fn upsample_420(channel: &Channel, target_width: usize, target_height: usize) -> Channel {
140 let mut new_data = vec![0; target_width * target_height];
141
142 for y in 0..target_height {
143 for x in 0..target_width {
144 let src_x = (x / 2).min(channel.width - 1);
145 let src_y = (y / 2).min(channel.height - 1);
146 let val = channel.data[src_y * channel.width + src_x];
147 new_data[y * target_width + x] = val;
148 }
149 }
150
151 Channel {
152 width: target_width,
153 height: target_height,
154 data: new_data,
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_ycocg_reversible() {
164 let rgb = vec![[100, 150, 200]];
165 let ycocg = rgb_to_ycocg(&rgb, 1, 1);
166 let restored = ycocg_to_rgb(&ycocg).unwrap();
167 assert_eq!(rgb, restored);
168 }
169
170 #[test]
171 fn test_subsample() {
172 let data = vec![10, 10, 20, 20];
173 let channel = Channel {
174 width: 2,
175 height: 2,
176 data,
177 };
178 let sub = subsample_420(&channel);
179 assert_eq!(sub.width, 1);
180 assert_eq!(sub.height, 1);
181 assert_eq!(sub.data[0], 15);
182 }
183}