1use crate::wz::error::{WzError, WzResult};
7use crate::wz::types::WzPngFormat;
8
9pub fn encode_pixels(
12 rgba: &[u8],
13 width: u32,
14 height: u32,
15 format: WzPngFormat,
16) -> WzResult<Vec<u8>> {
17 let pixel_count = (width * height) as usize;
18 if rgba.len() < pixel_count * 4 {
19 return Err(WzError::Custom(format!(
20 "RGBA data too short: need {} bytes, got {}",
21 pixel_count * 4,
22 rgba.len()
23 )));
24 }
25
26 match format {
27 WzPngFormat::Bgra4444 => Ok(rgba_to_bgra4444(rgba, pixel_count)),
28 WzPngFormat::Bgra8888 => Ok(rgba_to_bgra8888(rgba, pixel_count)),
29 WzPngFormat::Argb1555 => Ok(rgba_to_argb1555(rgba, pixel_count)),
30 WzPngFormat::Rgb565 => Ok(rgba_to_rgb565(rgba, pixel_count)),
31 WzPngFormat::R16 => Ok(rgba_to_r16(rgba, pixel_count)),
32 WzPngFormat::A8 => Ok(rgba_to_a8(rgba, pixel_count)),
33 WzPngFormat::Rgba1010102 => Ok(rgba_to_rgba1010102(rgba, pixel_count)),
34 WzPngFormat::Rgba32Float => Ok(rgba_to_rgba32float(rgba, pixel_count)),
35 _ => Err(WzError::Custom(format!(
36 "Encoding not supported for format {:?} — use Bgra8888 instead",
37 format
38 ))),
39 }
40}
41
42pub fn compress_png_data(raw: &[u8]) -> WzResult<Vec<u8>> {
43 use flate2::write::ZlibEncoder;
44 use flate2::Compression;
45 use std::io::Write;
46
47 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
48 encoder
49 .write_all(raw)
50 .map_err(|e| WzError::Custom(format!("Zlib compression failed: {}", e)))?;
51 encoder
52 .finish()
53 .map_err(|e| WzError::Custom(format!("Zlib compression finish failed: {}", e)))
54}
55
56fn rgba_to_bgra4444(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
59 let mut out = vec![0u8; pixel_count * 2];
60 for i in 0..pixel_count {
61 let (r, g, b, a) = (
62 rgba[i * 4],
63 rgba[i * 4 + 1],
64 rgba[i * 4 + 2],
65 rgba[i * 4 + 3],
66 );
67 let r4 = r >> 4;
68 let g4 = g >> 4;
69 let b4 = b >> 4;
70 let a4 = a >> 4;
71 out[i * 2] = b4 | (g4 << 4);
73 out[i * 2 + 1] = r4 | (a4 << 4);
74 }
75 out
76}
77
78fn rgba_to_bgra8888(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
79 let mut out = vec![0u8; pixel_count * 4];
80 for i in 0..pixel_count {
81 out[i * 4] = rgba[i * 4 + 2]; out[i * 4 + 1] = rgba[i * 4 + 1]; out[i * 4 + 2] = rgba[i * 4]; out[i * 4 + 3] = rgba[i * 4 + 3]; }
86 out
87}
88
89fn rgba_to_argb1555(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
90 let mut out = vec![0u8; pixel_count * 2];
91 for i in 0..pixel_count {
92 let (r, g, b, a) = (
93 rgba[i * 4],
94 rgba[i * 4 + 1],
95 rgba[i * 4 + 2],
96 rgba[i * 4 + 3],
97 );
98 let r5 = (r as u16 >> 3) & 0x1F;
99 let g5 = (g as u16 >> 3) & 0x1F;
100 let b5 = (b as u16 >> 3) & 0x1F;
101 let a1: u16 = if a >= 128 { 1 } else { 0 };
102 let val = (a1 << 15) | (r5 << 10) | (g5 << 5) | b5;
103 out[i * 2..i * 2 + 2].copy_from_slice(&val.to_le_bytes());
104 }
105 out
106}
107
108fn rgba_to_rgb565(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
109 let mut out = vec![0u8; pixel_count * 2];
110 for i in 0..pixel_count {
111 let (r, g, b) = (rgba[i * 4], rgba[i * 4 + 1], rgba[i * 4 + 2]);
112 let r5 = (r as u16 >> 3) & 0x1F;
113 let g6 = (g as u16 >> 2) & 0x3F;
114 let b5 = (b as u16 >> 3) & 0x1F;
115 let val = (r5 << 11) | (g6 << 5) | b5;
116 out[i * 2..i * 2 + 2].copy_from_slice(&val.to_le_bytes());
117 }
118 out
119}
120
121fn rgba_to_r16(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
122 let mut out = vec![0u8; pixel_count * 2];
123 for i in 0..pixel_count {
124 let r = rgba[i * 4];
125 out[i * 2] = 0;
127 out[i * 2 + 1] = r;
128 }
129 out
130}
131
132fn rgba_to_a8(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
133 let mut out = vec![0u8; pixel_count];
134 for i in 0..pixel_count {
135 out[i] = rgba[i * 4 + 3]; }
137 out
138}
139
140fn rgba_to_rgba1010102(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
141 let mut out = vec![0u8; pixel_count * 4];
142 for i in 0..pixel_count {
143 let (r, g, b, a) = (
144 rgba[i * 4],
145 rgba[i * 4 + 1],
146 rgba[i * 4 + 2],
147 rgba[i * 4 + 3],
148 );
149 let r10 = ((r as u32) << 2) | ((r as u32) >> 6);
151 let g10 = ((g as u32) << 2) | ((g as u32) >> 6);
152 let b10 = ((b as u32) << 2) | ((b as u32) >> 6);
153 let a2 = (a as u32) / 85;
155 let val = r10 | (g10 << 10) | (b10 << 20) | (a2 << 30);
156 out[i * 4..i * 4 + 4].copy_from_slice(&val.to_le_bytes());
157 }
158 out
159}
160
161fn rgba_to_rgba32float(rgba: &[u8], pixel_count: usize) -> Vec<u8> {
162 let mut out = vec![0u8; pixel_count * 16];
163 for i in 0..pixel_count {
164 let r = rgba[i * 4] as f32 / 255.0;
165 let g = rgba[i * 4 + 1] as f32 / 255.0;
166 let b = rgba[i * 4 + 2] as f32 / 255.0;
167 let a = rgba[i * 4 + 3] as f32 / 255.0;
168 out[i * 16..i * 16 + 4].copy_from_slice(&r.to_le_bytes());
169 out[i * 16 + 4..i * 16 + 8].copy_from_slice(&g.to_le_bytes());
170 out[i * 16 + 8..i * 16 + 12].copy_from_slice(&b.to_le_bytes());
171 out[i * 16 + 12..i * 16 + 16].copy_from_slice(&a.to_le_bytes());
172 }
173 out
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::image::{decode_pixels, decompress_png_data};
180
181 #[test]
184 fn test_bgra8888_roundtrip() {
185 let rgba = vec![0xFF, 0x80, 0x00, 0xC0, 0x10, 0x20, 0x30, 0x40];
186 let encoded = encode_pixels(&rgba, 2, 1, WzPngFormat::Bgra8888).unwrap();
187 let decoded = decode_pixels(&encoded, 2, 1, WzPngFormat::Bgra8888).unwrap();
188 assert_eq!(decoded, rgba);
189 }
190
191 #[test]
192 fn test_bgra4444_roundtrip_lossy() {
193 let rgba = vec![0xF0, 0x80, 0x30, 0xA0];
195 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::Bgra4444).unwrap();
196 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::Bgra4444).unwrap();
197 assert_eq!(decoded[0], 0xFF); assert_eq!(decoded[1], 0x88); assert_eq!(decoded[2], 0x33); assert_eq!(decoded[3], 0xAA); }
203
204 #[test]
205 fn test_argb1555_roundtrip_lossy() {
206 let rgba = vec![0xFF, 0xFF, 0xFF, 0xFF];
208 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::Argb1555).unwrap();
209 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::Argb1555).unwrap();
210 assert_eq!(decoded, vec![0xFF, 0xFF, 0xFF, 0xFF]);
211 }
212
213 #[test]
214 fn test_rgb565_roundtrip_lossy() {
215 let rgba = vec![0xFF, 0xFF, 0xFF, 0xFF];
216 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::Rgb565).unwrap();
217 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::Rgb565).unwrap();
218 assert_eq!(decoded, vec![0xFF, 0xFF, 0xFF, 0xFF]);
219 }
220
221 #[test]
222 fn test_a8_roundtrip() {
223 let rgba = vec![0xFF, 0xFF, 0xFF, 0x80];
224 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::A8).unwrap();
225 assert_eq!(encoded, vec![0x80]);
226 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::A8).unwrap();
227 assert_eq!(decoded[3], 0x80);
228 }
229
230 #[test]
231 fn test_r16_roundtrip() {
232 let rgba = vec![0xAB, 0x00, 0x00, 0xFF];
233 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::R16).unwrap();
234 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::R16).unwrap();
235 assert_eq!(decoded[0], 0xAB);
236 }
237
238 #[test]
239 fn test_rgba1010102_roundtrip_lossy() {
240 let rgba = vec![0xFF, 0xFF, 0xFF, 0xFF];
241 let encoded = encode_pixels(&rgba, 1, 1, WzPngFormat::Rgba1010102).unwrap();
242 let decoded = decode_pixels(&encoded, 1, 1, WzPngFormat::Rgba1010102).unwrap();
243 assert_eq!(decoded, vec![0xFF, 0xFF, 0xFF, 0xFF]);
244 }
245
246 #[test]
247 fn test_dxt_encoding_unsupported() {
248 let rgba = vec![0; 64]; assert!(encode_pixels(&rgba, 4, 4, WzPngFormat::Dxt3).is_err());
250 assert!(encode_pixels(&rgba, 4, 4, WzPngFormat::Dxt5).is_err());
251 assert!(encode_pixels(&rgba, 4, 4, WzPngFormat::Bc7).is_err());
252 }
253
254 #[test]
255 fn test_short_input_rejected() {
256 assert!(encode_pixels(&[0; 3], 1, 1, WzPngFormat::Bgra8888).is_err());
257 }
258
259 #[test]
262 fn test_compress_decompress_roundtrip() {
263 let raw = vec![0xAA; 1024];
264 let compressed = compress_png_data(&raw).unwrap();
265 let decompressed = decompress_png_data(&compressed, None).unwrap();
266 assert_eq!(decompressed, raw);
267 }
268
269 #[test]
270 fn test_compress_actually_compresses() {
271 let raw = vec![0x42; 4096];
273 let compressed = compress_png_data(&raw).unwrap();
274 assert!(compressed.len() < raw.len() / 2);
275 }
276}