1use crate::errors::IndexedImageError::*;
2use crate::palette::FilePalette::*;
3use crate::prelude::*;
4use std::collections::HashSet;
5
6pub(crate) const PAL_NO_DATA: u8 = 0;
7pub(crate) const PAL_ID: u8 = 1;
8pub(crate) const PAL_NAME: u8 = 2;
9pub(crate) const PAL_COLORS: u8 = 3;
10
11#[derive(Debug, Clone, Eq, PartialEq)]
13pub enum FilePalette {
14 NoData,
16 ID(u16),
18 Name(String),
20 Colors,
22}
23
24fn distinct_count(colors: &[Color]) -> usize {
25 colors.iter().collect::<HashSet<_>>().len()
26}
27
28pub fn simplify_palette_to_fit(colors: &[Color], max: usize) -> Vec<Color> {
31 let mut output = colors.to_vec();
32 let mut threshold = 2;
33 while distinct_count(&output) >= max {
34 output = simplify_palette(&output, threshold);
35 threshold += 10;
36 }
37 output
38}
39
40pub fn simplify_palette(colors: &[Color], threshold: usize) -> Vec<Color> {
46 let mut output = colors.to_vec();
47 let mut idx = 0;
48
49 loop {
50 let color = &output[idx];
51 let mut to_merge = None;
52 for (i, cmp_color) in output.iter().enumerate() {
53 let diff = color.diff(cmp_color);
54 if idx != i && diff > 0 && diff < threshold {
55 to_merge = Some(i);
56 break;
57 }
58 }
59 if let Some(i) = to_merge {
60 let c = output[idx].mid(&output[i]);
61 output[idx] = c;
62 output[i] = c;
63 } else {
64 idx += 1;
65 if idx >= output.len() {
66 break;
67 }
68 }
69 }
70
71 output
72}
73
74impl FilePalette {
75 pub(crate) fn to_byte(&self) -> u8 {
76 match self {
77 NoData => PAL_NO_DATA,
78 ID(_) => PAL_ID,
79 Name(_) => PAL_NAME,
80 Colors => PAL_COLORS,
81 }
82 }
83}
84
85pub(crate) fn write(
86 palette: &FilePalette,
87 colors: &[Color],
88 output: &mut Vec<u8>,
89) -> Result<(), IndexedImageError> {
90 output.push(palette.to_byte());
91 match palette {
92 NoData => {}
93 ID(id) => output.extend_from_slice(&id.to_be_bytes()),
94 Name(name) => {
95 let len = name.len();
96 if len < 1 {
97 return Err(PaletteNameTooShort);
98 }
99 if len > 255 {
100 return Err(PaletteNameTooLong);
101 }
102 output.push(len as u8);
103 output.extend_from_slice(name.as_bytes())
104 }
105 Colors => {
106 output.push(colors.len() as u8);
107 for color in colors {
108 output.push(color.r);
109 output.push(color.g);
110 output.push(color.b);
111 output.push(color.a);
112 }
113 }
114 }
115
116 Ok(())
117}
118
119pub(crate) fn read(
120 mut start_idx: usize,
121 bytes: &[u8],
122) -> Result<(usize, FilePalette, Option<Vec<Color>>), IndexedImageError> {
123 if bytes.len() <= start_idx {
124 return Err(InvalidFileFormat(
125 start_idx,
126 "No data after header, expected palette format".to_string(),
127 ));
128 }
129 let pal_type = bytes[start_idx];
130 start_idx += 1;
131 match pal_type {
132 PAL_NO_DATA => Ok((1, NoData, None)),
133 PAL_ID => {
134 if bytes.len() < start_idx + 1 {
135 return Err(InvalidFileFormat(
136 start_idx,
137 "No data after palette format, expected ID".to_string(),
138 ));
139 }
140 let bytes = &bytes[start_idx..=start_idx + 1];
141 let id = u16::from_be_bytes([bytes[0], bytes[1]]);
142 Ok((3, ID(id), None))
143 }
144 PAL_NAME => {
145 if bytes.len() < start_idx {
146 return Err(InvalidFileFormat(
147 start_idx,
148 "No data after palette format, expected palette name length".to_string(),
149 ));
150 }
151 let len = bytes[start_idx];
152 start_idx += 1;
153 let end = len as usize;
154 if bytes.len() < start_idx + end {
155 return Err(InvalidFileFormat(
156 start_idx,
157 "Incomplete data after palette name length, expected palette name".to_string(),
158 ));
159 }
160 let name = String::from_utf8(bytes[start_idx..start_idx + end].to_vec())
161 .map_err(PaletteNameNotUtf8)?;
162 Ok((end + 2, Name(name), None))
163 }
164 PAL_COLORS => {
165 if bytes.len() < start_idx {
166 return Err(InvalidFileFormat(
167 start_idx,
168 "No data after palette format, expected color count".to_string(),
169 ));
170 }
171 let count = bytes[start_idx];
172 start_idx += 1;
173 let end = count as usize * 4;
174 if bytes.len() < start_idx + end {
175 return Err(InvalidFileFormat(
176 start_idx,
177 format!("Incomplete data after palette color count, expected {count} colors"),
178 ));
179 }
180 let mut colors = vec![];
181 let color_bytes: Vec<&u8> = bytes.iter().skip(start_idx).take(end).collect();
182 for color in color_bytes.chunks_exact(4) {
183 colors.push(Color::new(*color[0], *color[1], *color[2], *color[3]));
184 }
185 Ok((end + 2, Colors, Some(colors)))
186 }
187 _ => Err(InvalidFileFormat(
188 start_idx,
189 format!("Unsupport palette type {pal_type}"),
190 )),
191 }
192}
193
194#[cfg(test)]
195mod test {
196 use super::*;
197
198 #[test]
199 fn write_no_data() {
200 let mut output = vec![];
201 write(&NoData, &[], &mut output).unwrap();
202 assert_eq!(output, vec![PAL_NO_DATA]);
203
204 let mut output = vec![];
205 write(&NoData, &[Color::new(255, 45, 231, 2)], &mut output).unwrap();
206 assert_eq!(output, vec![PAL_NO_DATA]);
207 }
208
209 #[test]
210 fn write_id() {
211 let mut output = vec![];
212 write(&ID(5), &[], &mut output).unwrap();
213 assert_eq!(output, vec![PAL_ID, 0, 5]);
214
215 let mut output = vec![];
216 write(&ID(256), &[Color::new(255, 45, 231, 2)], &mut output).unwrap();
217 assert_eq!(output, vec![PAL_ID, 1, 0]);
218 }
219
220 #[test]
221 fn write_name() {
222 let mut output = vec![];
223 write(&Name("test".to_string()), &[], &mut output).unwrap();
224 assert_eq!(output, vec![PAL_NAME, 4, b't', b'e', b's', b't']);
225
226 let mut output = vec![];
227 write(
228 &Name("😺".to_string()),
229 &[Color::new(255, 45, 231, 2)],
230 &mut output,
231 )
232 .unwrap();
233 assert_eq!(output, vec![PAL_NAME, 4, 240, 159, 152, 186]);
234 }
235
236 #[test]
237 fn write_colors() {
238 let mut output = vec![];
239 write(&Colors, &[Color::new(100, 101, 102, 103)], &mut output).unwrap();
240 assert_eq!(output, vec![PAL_COLORS, 1, 100, 101, 102, 103]);
241
242 let mut output = vec![];
243 write(
244 &Colors,
245 &[Color::new(100, 101, 102, 103), Color::new(0, 0, 0, 255)],
246 &mut output,
247 )
248 .unwrap();
249 assert_eq!(
250 output,
251 vec![PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255]
252 );
253 }
254
255 #[test]
256 fn read_no_data() {
257 let (skip, pal_type, colors) = read(0, &[PAL_NO_DATA]).unwrap();
258 assert_eq!(skip, 1);
259 assert_eq!(pal_type, NoData);
260 assert_eq!(colors, None);
261 }
262
263 #[test]
264 fn read_id() {
265 let (skip, pal_type, colors) = read(0, &[PAL_ID, 0, 5]).unwrap();
266 assert_eq!(skip, 3);
267 assert_eq!(pal_type, ID(5));
268 assert_eq!(colors, None);
269 }
270
271 #[test]
272 fn read_name() {
273 let (skip, pal_type, colors) = read(0, &[PAL_NAME, 4, 240, 159, 152, 186]).unwrap();
274 assert_eq!(skip, 6);
275 assert_eq!(pal_type, Name("😺".to_string()));
276 assert_eq!(colors, None);
277 }
278
279 #[test]
280 fn read_colors() {
281 let (skip, pal_type, colors) =
282 read(0, &[PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255]).unwrap();
283 assert_eq!(skip, 10);
284 assert_eq!(pal_type, Colors);
285 assert_eq!(
286 colors,
287 Some(vec![
288 Color::new(100, 101, 102, 103),
289 Color::new(0, 0, 0, 255)
290 ])
291 );
292 }
293
294 #[test]
295 fn write_data_before() {
296 let mut output = vec![1, 1, 1, 1];
297 write(&ID(5), &[], &mut output).unwrap();
298 assert_eq!(output, vec![1, 1, 1, 1, PAL_ID, 0, 5]);
299 }
300
301 #[test]
302 fn read_data_either_side() {
303 let bytes = [
304 1, 1, 1, 1, PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255, 2, 2, 2, 2,
305 ];
306 let start = 4;
307 let (skip, pal_type, colors) = read(start, &bytes).unwrap();
308 assert_eq!(skip, 10);
309 assert_eq!(pal_type, Colors);
310 assert_eq!(
311 colors,
312 Some(vec![
313 Color::new(100, 101, 102, 103),
314 Color::new(0, 0, 0, 255)
315 ])
316 );
317 assert_eq!(bytes[start + skip..], [2, 2, 2, 2]);
318 }
319}