1 | use super::lossless::subsample_size; |
2 | use super::lossless::DecoderError; |
3 | |
4 | #[derive (Debug, Clone)] |
5 | pub(crate) enum TransformType { |
6 | PredictorTransform { |
7 | size_bits: u8, |
8 | predictor_data: Vec<u32>, |
9 | }, |
10 | ColorTransform { |
11 | size_bits: u8, |
12 | transform_data: Vec<u32>, |
13 | }, |
14 | SubtractGreen, |
15 | ColorIndexingTransform { |
16 | table_size: u16, |
17 | table_data: Vec<u32>, |
18 | }, |
19 | } |
20 | |
21 | impl TransformType { |
22 | /// Applies a transform to the image data |
23 | pub(crate) fn apply_transform( |
24 | &self, |
25 | image_data: &mut Vec<u32>, |
26 | width: u16, |
27 | height: u16, |
28 | ) -> Result<(), DecoderError> { |
29 | match self { |
30 | TransformType::PredictorTransform { |
31 | size_bits, |
32 | predictor_data, |
33 | } => { |
34 | let block_xsize = usize::from(subsample_size(width, *size_bits)); |
35 | let width = usize::from(width); |
36 | let height = usize::from(height); |
37 | |
38 | if image_data.len() < width * height { |
39 | return Err(DecoderError::TransformError); |
40 | } |
41 | |
42 | //handle top and left borders specially |
43 | //this involves ignoring mode and just setting prediction values like this |
44 | image_data[0] = add_pixels(image_data[0], 0xff000000); |
45 | |
46 | for x in 1..width { |
47 | image_data[x] = add_pixels(image_data[x], get_left(image_data, x, 0, width)); |
48 | } |
49 | |
50 | for y in 1..height { |
51 | image_data[y * width] = |
52 | add_pixels(image_data[y * width], get_top(image_data, 0, y, width)); |
53 | } |
54 | |
55 | for y in 1..height { |
56 | for x in 1..width { |
57 | let block_index = (y >> size_bits) * block_xsize + (x >> size_bits); |
58 | |
59 | let index = y * width + x; |
60 | |
61 | let green = (predictor_data[block_index] >> 8) & 0xff; |
62 | |
63 | match green { |
64 | 0 => image_data[index] = add_pixels(image_data[index], 0xff000000), |
65 | 1 => { |
66 | image_data[index] = |
67 | add_pixels(image_data[index], get_left(image_data, x, y, width)) |
68 | } |
69 | 2 => { |
70 | image_data[index] = |
71 | add_pixels(image_data[index], get_top(image_data, x, y, width)) |
72 | } |
73 | 3 => { |
74 | image_data[index] = add_pixels( |
75 | image_data[index], |
76 | get_top_right(image_data, x, y, width), |
77 | ) |
78 | } |
79 | 4 => { |
80 | image_data[index] = add_pixels( |
81 | image_data[index], |
82 | get_top_left(image_data, x, y, width), |
83 | ) |
84 | } |
85 | 5 => { |
86 | image_data[index] = add_pixels(image_data[index], { |
87 | let first = average2( |
88 | get_left(image_data, x, y, width), |
89 | get_top_right(image_data, x, y, width), |
90 | ); |
91 | average2(first, get_top(image_data, x, y, width)) |
92 | }) |
93 | } |
94 | 6 => { |
95 | image_data[index] = add_pixels( |
96 | image_data[index], |
97 | average2( |
98 | get_left(image_data, x, y, width), |
99 | get_top_left(image_data, x, y, width), |
100 | ), |
101 | ) |
102 | } |
103 | 7 => { |
104 | image_data[index] = add_pixels( |
105 | image_data[index], |
106 | average2( |
107 | get_left(image_data, x, y, width), |
108 | get_top(image_data, x, y, width), |
109 | ), |
110 | ) |
111 | } |
112 | 8 => { |
113 | image_data[index] = add_pixels( |
114 | image_data[index], |
115 | average2( |
116 | get_top_left(image_data, x, y, width), |
117 | get_top(image_data, x, y, width), |
118 | ), |
119 | ) |
120 | } |
121 | 9 => { |
122 | image_data[index] = add_pixels( |
123 | image_data[index], |
124 | average2( |
125 | get_top(image_data, x, y, width), |
126 | get_top_right(image_data, x, y, width), |
127 | ), |
128 | ) |
129 | } |
130 | 10 => { |
131 | image_data[index] = add_pixels(image_data[index], { |
132 | let first = average2( |
133 | get_left(image_data, x, y, width), |
134 | get_top_left(image_data, x, y, width), |
135 | ); |
136 | let second = average2( |
137 | get_top(image_data, x, y, width), |
138 | get_top_right(image_data, x, y, width), |
139 | ); |
140 | average2(first, second) |
141 | }) |
142 | } |
143 | 11 => { |
144 | image_data[index] = add_pixels( |
145 | image_data[index], |
146 | select( |
147 | get_left(image_data, x, y, width), |
148 | get_top(image_data, x, y, width), |
149 | get_top_left(image_data, x, y, width), |
150 | ), |
151 | ) |
152 | } |
153 | 12 => { |
154 | image_data[index] = add_pixels( |
155 | image_data[index], |
156 | clamp_add_subtract_full( |
157 | get_left(image_data, x, y, width), |
158 | get_top(image_data, x, y, width), |
159 | get_top_left(image_data, x, y, width), |
160 | ), |
161 | ) |
162 | } |
163 | 13 => { |
164 | image_data[index] = add_pixels(image_data[index], { |
165 | let first = average2( |
166 | get_left(image_data, x, y, width), |
167 | get_top(image_data, x, y, width), |
168 | ); |
169 | clamp_add_subtract_half( |
170 | first, |
171 | get_top_left(image_data, x, y, width), |
172 | ) |
173 | }) |
174 | } |
175 | _ => {} |
176 | } |
177 | } |
178 | } |
179 | } |
180 | TransformType::ColorTransform { |
181 | size_bits, |
182 | transform_data, |
183 | } => { |
184 | let block_xsize = usize::from(subsample_size(width, *size_bits)); |
185 | let width = usize::from(width); |
186 | let height = usize::from(height); |
187 | |
188 | for y in 0..height { |
189 | for x in 0..width { |
190 | let block_index = (y >> size_bits) * block_xsize + (x >> size_bits); |
191 | |
192 | let index = y * width + x; |
193 | |
194 | let multiplier = |
195 | ColorTransformElement::from_color_code(transform_data[block_index]); |
196 | |
197 | image_data[index] = transform_color(&multiplier, image_data[index]); |
198 | } |
199 | } |
200 | } |
201 | TransformType::SubtractGreen => { |
202 | let width = usize::from(width); |
203 | for y in 0..usize::from(height) { |
204 | for x in 0..width { |
205 | image_data[y * width + x] = add_green(image_data[y * width + x]); |
206 | } |
207 | } |
208 | } |
209 | TransformType::ColorIndexingTransform { |
210 | table_size, |
211 | table_data, |
212 | } => { |
213 | let mut new_image_data = |
214 | Vec::with_capacity(usize::from(width) * usize::from(height)); |
215 | |
216 | let table_size = *table_size; |
217 | let width_bits: u8 = if table_size <= 2 { |
218 | 3 |
219 | } else if table_size <= 4 { |
220 | 2 |
221 | } else if table_size <= 16 { |
222 | 1 |
223 | } else { |
224 | 0 |
225 | }; |
226 | |
227 | let bits_per_pixel = 8 >> width_bits; |
228 | let mask = (1 << bits_per_pixel) - 1; |
229 | |
230 | let mut src = 0; |
231 | let width = usize::from(width); |
232 | |
233 | let pixels_per_byte = 1 << width_bits; |
234 | let count_mask = pixels_per_byte - 1; |
235 | let mut packed_pixels = 0; |
236 | |
237 | for _y in 0..usize::from(height) { |
238 | for x in 0..width { |
239 | if (x & count_mask) == 0 { |
240 | packed_pixels = (image_data[src] >> 8) & 0xff; |
241 | src += 1; |
242 | } |
243 | |
244 | let pixels: usize = (packed_pixels & mask).try_into().unwrap(); |
245 | let new_val = if pixels >= table_size.into() { |
246 | 0x00000000 |
247 | } else { |
248 | table_data[pixels] |
249 | }; |
250 | |
251 | new_image_data.push(new_val); |
252 | |
253 | packed_pixels >>= bits_per_pixel; |
254 | } |
255 | } |
256 | |
257 | *image_data = new_image_data; |
258 | } |
259 | } |
260 | |
261 | Ok(()) |
262 | } |
263 | } |
264 | |
265 | //predictor functions |
266 | |
267 | /// Adds 2 pixels mod 256 for each pixel |
268 | pub(crate) fn add_pixels(a: u32, b: u32) -> u32 { |
269 | let new_alpha: u32 = ((a >> 24) + (b >> 24)) & 0xff; |
270 | let new_red: u32 = (((a >> 16) & 0xff) + ((b >> 16) & 0xff)) & 0xff; |
271 | let new_green: u32 = (((a >> 8) & 0xff) + ((b >> 8) & 0xff)) & 0xff; |
272 | let new_blue: u32 = ((a & 0xff) + (b & 0xff)) & 0xff; |
273 | |
274 | (new_alpha << 24) + (new_red << 16) + (new_green << 8) + new_blue |
275 | } |
276 | |
277 | /// Get left pixel |
278 | fn get_left(data: &[u32], x: usize, y: usize, width: usize) -> u32 { |
279 | data[y * width + x - 1] |
280 | } |
281 | |
282 | /// Get top pixel |
283 | fn get_top(data: &[u32], x: usize, y: usize, width: usize) -> u32 { |
284 | data[(y - 1) * width + x] |
285 | } |
286 | |
287 | /// Get pixel to top right |
288 | fn get_top_right(data: &[u32], x: usize, y: usize, width: usize) -> u32 { |
289 | // if x == width - 1 this gets the left most pixel of the current row |
290 | // as described in the specification |
291 | data[(y - 1) * width + x + 1] |
292 | } |
293 | |
294 | /// Get pixel to top left |
295 | fn get_top_left(data: &[u32], x: usize, y: usize, width: usize) -> u32 { |
296 | data[(y - 1) * width + x - 1] |
297 | } |
298 | |
299 | /// Get average of 2 pixels |
300 | fn average2(a: u32, b: u32) -> u32 { |
301 | let mut avg: u32 = 0u32; |
302 | for i: i32 in 0..4 { |
303 | let sub_a: u8 = ((a >> (i * 8)) & 0xff).try_into().unwrap(); |
304 | let sub_b: u8 = ((b >> (i * 8)) & 0xff).try_into().unwrap(); |
305 | avg |= u32::from(sub_average2(sub_a, sub_b)) << (i * 8); |
306 | } |
307 | avg |
308 | } |
309 | |
310 | /// Get average of 2 bytes |
311 | fn sub_average2(a: u8, b: u8) -> u8 { |
312 | ((u16::from(a) + u16::from(b)) / 2).try_into().unwrap() |
313 | } |
314 | |
315 | /// Get a specific byte from argb pixel |
316 | fn get_byte(val: u32, byte: u8) -> u8 { |
317 | ((val >> (byte * 8)) & 0xff).try_into().unwrap() |
318 | } |
319 | |
320 | /// Get byte as i32 for convenience |
321 | fn get_byte_i32(val: u32, byte: u8) -> i32 { |
322 | i32::from(get_byte(val, byte)) |
323 | } |
324 | |
325 | /// Select left or top byte |
326 | fn select(left: u32, top: u32, top_left: u32) -> u32 { |
327 | let predict_alpha: i32 = get_byte_i32(val:left, byte:3) + get_byte_i32(val:top, byte:3) - get_byte_i32(val:top_left, byte:3); |
328 | let predict_red: i32 = get_byte_i32(val:left, byte:2) + get_byte_i32(val:top, byte:2) - get_byte_i32(val:top_left, byte:2); |
329 | let predict_green: i32 = get_byte_i32(val:left, byte:1) + get_byte_i32(val:top, byte:1) - get_byte_i32(val:top_left, byte:1); |
330 | let predict_blue: i32 = get_byte_i32(val:left, byte:0) + get_byte_i32(val:top, byte:0) - get_byte_i32(val:top_left, byte:0); |
331 | |
332 | let predict_left: i32 = i32::abs(self:predict_alpha - get_byte_i32(val:left, byte:3)) |
333 | + i32::abs(self:predict_red - get_byte_i32(val:left, byte:2)) |
334 | + i32::abs(self:predict_green - get_byte_i32(val:left, byte:1)) |
335 | + i32::abs(self:predict_blue - get_byte_i32(val:left, byte:0)); |
336 | let predict_top: i32 = i32::abs(self:predict_alpha - get_byte_i32(val:top, byte:3)) |
337 | + i32::abs(self:predict_red - get_byte_i32(val:top, byte:2)) |
338 | + i32::abs(self:predict_green - get_byte_i32(val:top, byte:1)) |
339 | + i32::abs(self:predict_blue - get_byte_i32(val:top, byte:0)); |
340 | |
341 | if predict_left < predict_top { |
342 | left |
343 | } else { |
344 | top |
345 | } |
346 | } |
347 | |
348 | /// Clamp a to [0, 255] |
349 | fn clamp(a: i32) -> i32 { |
350 | if a < 0 { |
351 | 0 |
352 | } else if a > 255 { |
353 | 255 |
354 | } else { |
355 | a |
356 | } |
357 | } |
358 | |
359 | /// Clamp add subtract full on one part |
360 | fn clamp_add_subtract_full_sub(a: i32, b: i32, c: i32) -> i32 { |
361 | clamp(a + b - c) |
362 | } |
363 | |
364 | /// Clamp add subtract half on one part |
365 | fn clamp_add_subtract_half_sub(a: i32, b: i32) -> i32 { |
366 | clamp(a + (a - b) / 2) |
367 | } |
368 | |
369 | /// Clamp add subtract full on 3 pixels |
370 | fn clamp_add_subtract_full(a: u32, b: u32, c: u32) -> u32 { |
371 | let mut value: u32 = 0; |
372 | for i: u8 in 0..4u8 { |
373 | let sub_a: i32 = ((a >> (i * 8)) & 0xff).try_into().unwrap(); |
374 | let sub_b: i32 = ((b >> (i * 8)) & 0xff).try_into().unwrap(); |
375 | let sub_c: i32 = ((c >> (i * 8)) & 0xff).try_into().unwrap(); |
376 | value |= |
377 | u32::try_from(clamp_add_subtract_full_sub(sub_a, sub_b, sub_c)).unwrap() << (i * 8); |
378 | } |
379 | value |
380 | } |
381 | |
382 | /// Clamp add subtract half on 2 pixels |
383 | fn clamp_add_subtract_half(a: u32, b: u32) -> u32 { |
384 | let mut value: u32 = 0; |
385 | for i: u8 in 0..4u8 { |
386 | let sub_a: i32 = ((a >> (i * 8)) & 0xff).try_into().unwrap(); |
387 | let sub_b: i32 = ((b >> (i * 8)) & 0xff).try_into().unwrap(); |
388 | value |= u32::try_from(clamp_add_subtract_half_sub(sub_a, sub_b)).unwrap() << (i * 8); |
389 | } |
390 | |
391 | value |
392 | } |
393 | |
394 | //color transform |
395 | |
396 | #[derive (Debug, Clone, Copy)] |
397 | struct ColorTransformElement { |
398 | green_to_red: u8, |
399 | green_to_blue: u8, |
400 | red_to_blue: u8, |
401 | } |
402 | |
403 | impl ColorTransformElement { |
404 | fn from_color_code(color_code: u32) -> ColorTransformElement { |
405 | ColorTransformElement { |
406 | green_to_red: (color_code & 0xff).try_into().unwrap(), |
407 | green_to_blue: ((color_code >> 8) & 0xff).try_into().unwrap(), |
408 | red_to_blue: ((color_code >> 16) & 0xff).try_into().unwrap(), |
409 | } |
410 | } |
411 | } |
412 | |
413 | /// Does color transform on red and blue transformed by green |
414 | fn color_transform(red: u8, blue: u8, green: u8, trans: &ColorTransformElement) -> (u8, u8) { |
415 | let mut temp_red: u32 = u32::from(red); |
416 | let mut temp_blue: u32 = u32::from(blue); |
417 | |
418 | //as does the conversion from u8 to signed two's complement i8 required |
419 | temp_red += color_transform_delta(t:trans.green_to_red as i8, c:green as i8); |
420 | temp_blue += color_transform_delta(t:trans.green_to_blue as i8, c:green as i8); |
421 | temp_blue += color_transform_delta(t:trans.red_to_blue as i8, c:temp_red as i8); |
422 | |
423 | ( |
424 | (temp_red & 0xff).try_into().unwrap(), |
425 | (temp_blue & 0xff).try_into().unwrap(), |
426 | ) |
427 | } |
428 | |
429 | /// Does color transform on 2 numbers |
430 | fn color_transform_delta(t: i8, c: i8) -> u32 { |
431 | ((i16::from(t) * i16::from(c)) as u32) >> 5 |
432 | } |
433 | |
434 | // Does color transform on a pixel with a color transform element |
435 | fn transform_color(multiplier: &ColorTransformElement, color_value: u32) -> u32 { |
436 | let alpha: u8 = get_byte(val:color_value, byte:3); |
437 | let red: u8 = get_byte(val:color_value, byte:2); |
438 | let green: u8 = get_byte(val:color_value, byte:1); |
439 | let blue: u8 = get_byte(val:color_value, byte:0); |
440 | |
441 | let (new_red: u8, new_blue: u8) = color_transform(red, blue, green, trans:multiplier); |
442 | |
443 | (u32::from(alpha) << 24) |
444 | + (u32::from(new_red) << 16) |
445 | + (u32::from(green) << 8) |
446 | + u32::from(new_blue) |
447 | } |
448 | |
449 | //subtract green function |
450 | |
451 | /// Adds green to red and blue of a pixel |
452 | fn add_green(argb: u32) -> u32 { |
453 | let red: u32 = (argb >> 16) & 0xff; |
454 | let green: u32 = (argb >> 8) & 0xff; |
455 | let blue: u32 = argb & 0xff; |
456 | |
457 | let new_red: u32 = (red + green) & 0xff; |
458 | let new_blue: u32 = (blue + green) & 0xff; |
459 | |
460 | (argb & 0xff00ff00) | (new_red << 16) | (new_blue) |
461 | } |
462 | |