/src/image/src/codecs/tga/decoder.rs
Line | Count | Source |
1 | | use super::header::{Header, ImageType, ALPHA_BIT_MASK}; |
2 | | use crate::error::DecodingError; |
3 | | use crate::io::ReadExt; |
4 | | use crate::utils::vec_try_with_capacity; |
5 | | use crate::{ |
6 | | color::{ColorType, ExtendedColorType}, |
7 | | error::{ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind}, |
8 | | ImageDecoder, ImageFormat, |
9 | | }; |
10 | | use byteorder_lite::ReadBytesExt; |
11 | | use std::io::{self, Read}; |
12 | | |
13 | | struct ColorMap { |
14 | | /// sizes in bytes |
15 | | start_offset: usize, |
16 | | entry_size: usize, |
17 | | bytes: Vec<u8>, |
18 | | } |
19 | | |
20 | | impl ColorMap { |
21 | | /// Get one entry from the color map |
22 | 1.88M | pub(crate) fn get(&self, index: usize) -> Option<&[u8]> { |
23 | 1.88M | let entry = self.entry_size * index.checked_sub(self.start_offset)?; |
24 | 1.88M | self.bytes.get(entry..entry + self.entry_size) |
25 | 1.88M | } |
26 | | } |
27 | | |
28 | | /// The representation of a TGA decoder |
29 | | pub struct TgaDecoder<R> { |
30 | | r: R, |
31 | | |
32 | | width: usize, |
33 | | height: usize, |
34 | | |
35 | | // The number of bytes in the raw input data for each pixel. If a color map is used, this is the |
36 | | // number of bytes for each color map index. |
37 | | raw_bytes_per_pixel: usize, |
38 | | |
39 | | image_type: ImageType, |
40 | | color_type: ColorType, |
41 | | original_color_type: Option<ExtendedColorType>, |
42 | | |
43 | | header: Header, |
44 | | color_map: Option<ColorMap>, |
45 | | } |
46 | | |
47 | | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] |
48 | | enum TgaOrientation { |
49 | | TopLeft, |
50 | | TopRight, |
51 | | BottomRight, |
52 | | BottomLeft, |
53 | | } |
54 | | |
55 | | impl TgaOrientation { |
56 | 489 | fn from_image_desc_byte(value: u8) -> Self { |
57 | | // Set bits 4 and 5 indicates direction, if bit 4 is set then pixel order right -> left, |
58 | | // when bit 5 is set it indicates rows top -> bottom direction. |
59 | | // Sources: |
60 | | // https://en.wikipedia.org/wiki/Truevision_TGA ; Image specification (field 5) |
61 | 489 | if value & (1u8 << 4) == 0 { |
62 | | // Left -> Right |
63 | 188 | if value & (1u8 << 5) == 0 { |
64 | 170 | TgaOrientation::BottomLeft |
65 | | } else { |
66 | 18 | TgaOrientation::TopLeft |
67 | | } |
68 | | } else { |
69 | | // Right -> Left |
70 | 301 | if value & (1u8 << 5) == 0 { |
71 | 280 | TgaOrientation::BottomRight |
72 | | } else { |
73 | 21 | TgaOrientation::TopRight |
74 | | } |
75 | | } |
76 | 489 | } |
77 | | } |
78 | | |
79 | | /// This contains the nearest integers to the rational numbers |
80 | | /// `(255 x) / 31`, for each `x` between 0 and 31, inclusive. |
81 | | static LOOKUP_TABLE_5_BIT_TO_8_BIT: [u8; 32] = [ |
82 | | 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, |
83 | | 181, 189, 197, 206, 214, 222, 230, 239, 247, 255, |
84 | | ]; |
85 | | |
86 | | /// Convert TGA's 15/16-bit pixel format to its 24 bit pixel format |
87 | 3.10M | fn expand_rgb15_to_rgb24(data: [u8; 2]) -> [u8; 3] { |
88 | 3.10M | let val = u16::from_le_bytes(data); |
89 | 3.10M | [ |
90 | 3.10M | LOOKUP_TABLE_5_BIT_TO_8_BIT[(val & 0b11111) as usize], |
91 | 3.10M | LOOKUP_TABLE_5_BIT_TO_8_BIT[((val >> 5) & 0b11111) as usize], |
92 | 3.10M | LOOKUP_TABLE_5_BIT_TO_8_BIT[((val >> 10) & 0b11111) as usize], |
93 | 3.10M | ] |
94 | 3.10M | } |
95 | | |
96 | | impl<R: Read> TgaDecoder<R> { |
97 | | /// Create a new decoder that decodes from the stream `r` |
98 | 1.03k | pub fn new(mut r: R) -> ImageResult<TgaDecoder<R>> { |
99 | | // Read header |
100 | 1.03k | let header = Header::from_reader(&mut r)?; |
101 | 1.00k | let image_type = ImageType::new(header.image_type); |
102 | 1.00k | let width = header.image_width as usize; |
103 | 1.00k | let height = header.image_height as usize; |
104 | 1.00k | let raw_bytes_per_pixel = (header.pixel_depth as usize).div_ceil(8); |
105 | 1.00k | let num_attrib_bits = header.image_desc & ALPHA_BIT_MASK; |
106 | | |
107 | 1.00k | if width == 0 || height == 0 { |
108 | 7 | return Err(ImageError::Decoding(DecodingError::new( |
109 | 7 | ImageFormat::Tga.into(), |
110 | 7 | "Invalid empty image", |
111 | 7 | ))); |
112 | 994 | } |
113 | | |
114 | 994 | if image_type.is_color_mapped() { |
115 | 509 | if header.map_type != 1 { |
116 | 8 | return Err(ImageError::Decoding(DecodingError::new( |
117 | 8 | ImageFormat::Tga.into(), |
118 | 8 | "Color map type must be 1 for color mapped images", |
119 | 8 | ))); |
120 | 501 | } else if ![8, 16].contains(&header.pixel_depth) { |
121 | 5 | return Err(ImageError::Decoding(DecodingError::new( |
122 | 5 | ImageFormat::Tga.into(), |
123 | 5 | "Color map must use 1 or 2 byte indexes", |
124 | 5 | ))); |
125 | 496 | } else if header.pixel_depth > header.map_entry_size { |
126 | 5 | return Err(ImageError::Unsupported( |
127 | 5 | UnsupportedError::from_format_and_kind( |
128 | 5 | ImageFormat::Tga.into(), |
129 | 5 | UnsupportedErrorKind::GenericFeature( |
130 | 5 | "Indices larger than pixel values".into(), |
131 | 5 | ), |
132 | 5 | ), |
133 | 5 | )); |
134 | 491 | } |
135 | 485 | } |
136 | | |
137 | | // Compute output pixel depth |
138 | 976 | let total_pixel_bits = if image_type.is_color_mapped() { |
139 | 491 | header.map_entry_size |
140 | | } else { |
141 | 485 | header.pixel_depth |
142 | | }; |
143 | 976 | let num_other_bits = total_pixel_bits |
144 | 976 | .checked_sub(num_attrib_bits) |
145 | 976 | .ok_or_else(|| { |
146 | 2 | ImageError::Decoding(DecodingError::new( |
147 | 2 | ImageFormat::Tga.into(), |
148 | 2 | "More alpha bits than pixel bits", |
149 | 2 | )) |
150 | 2 | })?; |
151 | | |
152 | | // Determine color type |
153 | | let color_type; |
154 | 974 | let mut original_color_type = None; |
155 | 974 | match (num_attrib_bits, num_other_bits, image_type.is_color()) { |
156 | | // really, the encoding is BGR and BGRA, this is fixed up with |
157 | | // `TgaDecoder::reverse_encoding`. |
158 | 165 | (0, 32, true) => color_type = ColorType::Rgba8, |
159 | 34 | (8, 24, true) => color_type = ColorType::Rgba8, |
160 | 178 | (0, 24, true) => color_type = ColorType::Rgb8, |
161 | 307 | (1, 15, true) | (0, 15, true) | (0, 16, true) => { |
162 | 307 | // the 'A' bit for 5-bit-per-primary images is an 'attribute' |
163 | 307 | // bit, and cannot safely be interpreted as an alpha channel. |
164 | 307 | // (It may contain all zero values or a pattern unrelated to the image.) |
165 | 307 | color_type = ColorType::Rgb8; |
166 | 307 | original_color_type = Some(ExtendedColorType::Rgb5x1); |
167 | 307 | } |
168 | 19 | (8, 8, false) => color_type = ColorType::La8, |
169 | 172 | (0, 8, false) => color_type = ColorType::L8, |
170 | 62 | (8, 0, false) => { |
171 | 62 | // alpha-only image is treated as L8 |
172 | 62 | color_type = ColorType::L8; |
173 | 62 | original_color_type = Some(ExtendedColorType::A8); |
174 | 62 | } |
175 | | _ => { |
176 | 37 | return Err(ImageError::Unsupported( |
177 | 37 | UnsupportedError::from_format_and_kind( |
178 | 37 | ImageFormat::Tga.into(), |
179 | 37 | UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)), |
180 | 37 | ), |
181 | 37 | )) |
182 | | } |
183 | | } |
184 | | |
185 | | // TODO: validate the rest of the fields in the header. |
186 | | |
187 | | // Read image ID (and ignore it) |
188 | 937 | let mut tmp = [0u8; 256]; |
189 | 937 | r.read_exact(&mut tmp[0..header.id_length as usize])?; |
190 | | |
191 | | // Read color map |
192 | 929 | let mut color_map = None; |
193 | 929 | if header.map_type == 1 { |
194 | 560 | if ![15, 16, 24, 32].contains(&header.map_entry_size) { |
195 | 7 | return Err(ImageError::Unsupported( |
196 | 7 | UnsupportedError::from_format_and_kind( |
197 | 7 | ImageFormat::Tga.into(), |
198 | 7 | UnsupportedErrorKind::GenericFeature( |
199 | 7 | "Unsupported color map entry size".into(), |
200 | 7 | ), |
201 | 7 | ), |
202 | 7 | )); |
203 | 553 | } |
204 | 553 | let mut entry_size = (header.map_entry_size as usize).div_ceil(8); |
205 | | |
206 | 553 | let mut bytes = Vec::new(); |
207 | 553 | r.read_exact_vec(&mut bytes, entry_size * header.map_length as usize)?; |
208 | | |
209 | | // Color maps are technically allowed in non-color-mapped images, so check that we |
210 | | // actually need the color map before storing it. |
211 | 458 | if image_type.is_color_mapped() { |
212 | | // Pre-expand 5-bit-per-primary values to simplify later decoding |
213 | 414 | if [15, 16].contains(&header.map_entry_size) { |
214 | 188 | let mut expanded = Vec::new(); |
215 | 678k | for &entry in bytes.as_chunks::<2>().0.iter() { |
216 | 678k | expanded.extend_from_slice(&expand_rgb15_to_rgb24(entry)); |
217 | 678k | } |
218 | 188 | bytes = expanded; |
219 | 188 | entry_size = 3; |
220 | 226 | } |
221 | | |
222 | 414 | color_map = Some(ColorMap { |
223 | 414 | entry_size, |
224 | 414 | start_offset: header.map_origin as usize, |
225 | 414 | bytes, |
226 | 414 | }); |
227 | 44 | } |
228 | 369 | } |
229 | | |
230 | 827 | Ok(TgaDecoder { |
231 | 827 | r, |
232 | 827 | |
233 | 827 | width, |
234 | 827 | height, |
235 | 827 | raw_bytes_per_pixel, |
236 | 827 | |
237 | 827 | image_type, |
238 | 827 | color_type, |
239 | 827 | original_color_type, |
240 | 827 | |
241 | 827 | header, |
242 | 827 | color_map, |
243 | 827 | }) |
244 | 1.03k | } |
245 | | |
246 | | /// Reads a run length encoded data for given number of bytes |
247 | 557 | fn read_encoded_data(&mut self, buf: &mut [u8]) -> io::Result<()> { |
248 | 557 | assert!(self.raw_bytes_per_pixel <= 4); |
249 | 557 | let mut repeat_buf = [0; 4]; |
250 | 557 | let repeat_buf = &mut repeat_buf[..self.raw_bytes_per_pixel]; |
251 | | |
252 | 557 | let mut index = 0; |
253 | 582k | while index < buf.len() { |
254 | 582k | let run_packet = self.r.read_u8()?; |
255 | | // If the highest bit in `run_packet` is set, then we repeat pixels |
256 | | // |
257 | | // Note: the TGA format adds 1 to both counts because having a count |
258 | | // of 0 would be pointless. |
259 | 582k | if (run_packet & 0x80) != 0 { |
260 | | // high bit set, so we will repeat the data |
261 | 388k | let repeat_count = ((run_packet & !0x80) + 1) as usize; |
262 | 388k | self.r.read_exact(repeat_buf)?; |
263 | | |
264 | 45.9M | for chunk in buf[index..] |
265 | 388k | .chunks_exact_mut(self.raw_bytes_per_pixel) |
266 | 388k | .take(repeat_count) |
267 | 45.9M | { |
268 | 45.9M | chunk.copy_from_slice(repeat_buf); |
269 | 45.9M | } |
270 | 388k | index += repeat_count * self.raw_bytes_per_pixel; |
271 | | } else { |
272 | | // not set, so `run_packet+1` is the number of non-encoded pixels |
273 | 194k | let num_raw_bytes = |
274 | 194k | ((run_packet + 1) as usize * self.raw_bytes_per_pixel).min(buf.len() - index); |
275 | | |
276 | 194k | self.r.read_exact(&mut buf[index..][..num_raw_bytes])?; |
277 | 194k | index += num_raw_bytes; |
278 | | } |
279 | | } |
280 | | |
281 | 363 | Ok(()) |
282 | 557 | } |
283 | | |
284 | | /// Expands indices into its mapped color |
285 | 252 | fn expand_color_map( |
286 | 252 | &self, |
287 | 252 | input: &[u8], |
288 | 252 | output: &mut [u8], |
289 | 252 | color_map: &ColorMap, |
290 | 252 | ) -> ImageResult<()> { |
291 | 252 | if self.raw_bytes_per_pixel == 1 { |
292 | 1.88M | for (&index, chunk) in input |
293 | 162 | .iter() |
294 | 162 | .zip(output.chunks_exact_mut(color_map.entry_size)) |
295 | | { |
296 | 1.88M | if let Some(color) = color_map.get(index as usize) { |
297 | 1.88M | chunk.copy_from_slice(color); |
298 | 1.88M | } else { |
299 | 130 | return Err(ImageError::Decoding(DecodingError::new( |
300 | 130 | ImageFormat::Tga.into(), |
301 | 130 | "Invalid color map index", |
302 | 130 | ))); |
303 | | } |
304 | | } |
305 | 90 | } else if self.raw_bytes_per_pixel == 2 { |
306 | 90 | let input_chunks = input.as_chunks::<2>().0.iter(); |
307 | 8.70k | for (&index, chunk) in input_chunks.zip(output.chunks_exact_mut(color_map.entry_size)) { |
308 | 8.70k | let index = u16::from_le_bytes(index); |
309 | 8.70k | if let Some(color) = color_map.get(index as usize) { |
310 | 8.64k | chunk.copy_from_slice(color); |
311 | 8.64k | } else { |
312 | 64 | return Err(ImageError::Decoding(DecodingError::new( |
313 | 64 | ImageFormat::Tga.into(), |
314 | 64 | "Invalid color map index", |
315 | 64 | ))); |
316 | | } |
317 | | } |
318 | | } else { |
319 | 0 | unreachable!("Supported bytes_per_pixel values are checked in TgaDecoder::new"); |
320 | | } |
321 | | |
322 | 58 | Ok(()) |
323 | 252 | } |
324 | | |
325 | | /// Reverse from BGR encoding to RGB encoding |
326 | | /// |
327 | | /// TGA files are stored in the BGRA encoding. This function swaps |
328 | | /// the blue and red bytes in the `pixels` array. |
329 | 295 | fn reverse_encoding_in_output(&mut self, pixels: &mut [u8]) { |
330 | | // We only need to reverse the encoding of color images |
331 | 295 | match self.color_type { |
332 | | ColorType::Rgb8 | ColorType::Rgba8 => { |
333 | 7.30M | for chunk in pixels.chunks_exact_mut(self.color_type.bytes_per_pixel().into()) { |
334 | 7.30M | chunk.swap(0, 2); |
335 | 7.30M | } |
336 | | } |
337 | 105 | _ => {} |
338 | | } |
339 | 295 | } |
340 | | |
341 | | /// Change image orientation depending on the flags set |
342 | 489 | fn fixup_orientation(&mut self, pixels: &mut [u8]) { |
343 | | // The below code assumes that the image is non-empty and will crash |
344 | | // otherwise. The constructor *currently* disallows empty images, so |
345 | | // this is method does not panic. |
346 | 489 | debug_assert!(self.width > 0 && self.height > 0); |
347 | | |
348 | 489 | let orientation = TgaOrientation::from_image_desc_byte(self.header.image_desc); |
349 | | |
350 | | // Flip image if bottom->top direction |
351 | 489 | if (orientation == TgaOrientation::BottomLeft || orientation == TgaOrientation::BottomRight) |
352 | 450 | && self.height > 1 |
353 | | { |
354 | 267 | let row_stride = self.width * self.raw_bytes_per_pixel; |
355 | | |
356 | 267 | let (left_part, right_part) = pixels.split_at_mut(self.height / 2 * row_stride); |
357 | | |
358 | 522k | for (src, dst) in left_part |
359 | 267 | .chunks_exact_mut(row_stride) |
360 | 267 | .zip(right_part.chunks_exact_mut(row_stride).rev()) |
361 | | { |
362 | 19.3M | for (src, dst) in src.iter_mut().zip(dst.iter_mut()) { |
363 | 19.3M | std::mem::swap(src, dst); |
364 | 19.3M | } |
365 | | } |
366 | 222 | } |
367 | | |
368 | | // Flop image if right->left direction |
369 | 489 | if (orientation == TgaOrientation::BottomRight || orientation == TgaOrientation::TopRight) |
370 | 301 | && self.width > 1 |
371 | | { |
372 | 398k | for row in pixels.chunks_exact_mut(self.width * self.raw_bytes_per_pixel) { |
373 | 398k | let (left_part, right_part) = |
374 | 398k | row.split_at_mut(self.width / 2 * self.raw_bytes_per_pixel); |
375 | 7.12M | for (src, dst) in left_part |
376 | 398k | .chunks_exact_mut(self.raw_bytes_per_pixel) |
377 | 398k | .zip(right_part.chunks_exact_mut(self.raw_bytes_per_pixel).rev()) |
378 | | { |
379 | 10.6M | for (src, dst) in src.iter_mut().zip(dst.iter_mut()) { |
380 | 10.6M | std::mem::swap(dst, src); |
381 | 10.6M | } |
382 | | } |
383 | | } |
384 | 248 | } |
385 | 489 | } |
386 | | } |
387 | | |
388 | | impl<R: Read> ImageDecoder for TgaDecoder<R> { |
389 | 2.35k | fn dimensions(&self) -> (u32, u32) { |
390 | 2.35k | (self.width as u32, self.height as u32) |
391 | 2.35k | } |
392 | | |
393 | 2.35k | fn color_type(&self) -> ColorType { |
394 | 2.35k | self.color_type |
395 | 2.35k | } |
396 | | |
397 | 0 | fn original_color_type(&self) -> ExtendedColorType { |
398 | 0 | self.original_color_type |
399 | 0 | .unwrap_or_else(|| self.color_type().into()) |
400 | 0 | } |
401 | | |
402 | 764 | fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { |
403 | 764 | assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); |
404 | | |
405 | | // Decode the raw data |
406 | | // |
407 | | // We currently assume that the indices take less space than the |
408 | | // pixels they encode, so it is safe to read the raw data into `buf`. |
409 | 764 | if self.raw_bytes_per_pixel > self.color_type.bytes_per_pixel().into() { |
410 | 0 | return Err(ImageError::Unsupported( |
411 | 0 | UnsupportedError::from_format_and_kind( |
412 | 0 | ImageFormat::Tga.into(), |
413 | 0 | UnsupportedErrorKind::GenericFeature( |
414 | 0 | "Color-mapped images with indices wider than color are not supported" |
415 | 0 | .into(), |
416 | 0 | ), |
417 | 0 | ), |
418 | 0 | )); |
419 | 764 | } |
420 | 764 | let num_raw_bytes = self.width * self.height * self.raw_bytes_per_pixel; |
421 | 764 | debug_assert!(num_raw_bytes <= buf.len()); |
422 | | |
423 | 764 | if self.image_type.is_encoded() { |
424 | 557 | self.read_encoded_data(&mut buf[..num_raw_bytes])?; |
425 | | } else { |
426 | 207 | self.r.read_exact(&mut buf[..num_raw_bytes])?; |
427 | | } |
428 | | |
429 | 489 | self.fixup_orientation(&mut buf[..num_raw_bytes]); |
430 | | |
431 | | // Expand the indices using the color map if necessary |
432 | 489 | if let Some(ref color_map) = self.color_map { |
433 | | // This allocation could be avoided by expanding each row (or block of pixels) as it is |
434 | | // read, or by doing the color map expansion in-place. But those may be more effort than |
435 | | // it is worth. |
436 | 252 | let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?; |
437 | 252 | rawbuf.extend_from_slice(&buf[..num_raw_bytes]); |
438 | | |
439 | 252 | self.expand_color_map(&rawbuf, buf, color_map)?; |
440 | 237 | } else if self.original_color_type == Some(ExtendedColorType::Rgb5x1) { |
441 | | // Expand the 15-bit to 24-bit representation for non-color-mapped images; |
442 | | // the expansion for color-mapped 15-bit images was already done in the color map |
443 | 53 | let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?; |
444 | 53 | rawbuf.extend_from_slice(&buf[..num_raw_bytes]); |
445 | | |
446 | 53 | let rawbuf_chunks = rawbuf.as_chunks::<2>().0.iter(); |
447 | 53 | let buf_chunks = buf.as_chunks_mut::<3>().0.iter_mut(); |
448 | 2.42M | for (&src, dst) in rawbuf_chunks.zip(buf_chunks) { |
449 | 2.42M | *dst = expand_rgb15_to_rgb24(src); |
450 | 2.42M | } |
451 | 184 | } |
452 | | |
453 | 295 | self.reverse_encoding_in_output(buf); |
454 | | |
455 | 295 | Ok(()) |
456 | 764 | } |
457 | | |
458 | 0 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
459 | 0 | (*self).read_image(buf) |
460 | 0 | } |
461 | | } |