Skip to main content

image/codecs/tga/
decoder.rs

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