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