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
79impl<R: Read> TgaDecoder<R> {
80    /// Create a new decoder that decodes from the stream `r`
81    pub fn new(mut r: R) -> ImageResult<TgaDecoder<R>> {
82        // Read header
83        let header = Header::from_reader(&mut r)?;
84        let image_type = ImageType::new(header.image_type);
85        let width = header.image_width as usize;
86        let height = header.image_height as usize;
87        let raw_bytes_per_pixel = (header.pixel_depth as usize).div_ceil(8);
88        let num_alpha_bits = header.image_desc & ALPHA_BIT_MASK;
89
90        if width == 0 || height == 0 {
91            return Err(ImageError::Decoding(DecodingError::new(
92                ImageFormat::Tga.into(),
93                "Invalid empty image",
94            )));
95        }
96
97        // Validate header
98        if ![8, 16, 24, 32].contains(&header.pixel_depth) || ![0, 8].contains(&num_alpha_bits) {
99            return Err(ImageError::Unsupported(
100                UnsupportedError::from_format_and_kind(
101                    ImageFormat::Tga.into(),
102                    UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)),
103                ),
104            ));
105        }
106        if image_type.is_color_mapped() {
107            if header.map_type != 1 {
108                return Err(ImageError::Decoding(DecodingError::new(
109                    ImageFormat::Tga.into(),
110                    "Color map type must be 1 for color mapped images",
111                )));
112            } else if ![8, 16].contains(&header.pixel_depth) {
113                return Err(ImageError::Decoding(DecodingError::new(
114                    ImageFormat::Tga.into(),
115                    "Color map must use 1 or 2 byte indexes",
116                )));
117            } else if header.pixel_depth > header.map_entry_size {
118                return Err(ImageError::Unsupported(
119                    UnsupportedError::from_format_and_kind(
120                        ImageFormat::Tga.into(),
121                        UnsupportedErrorKind::GenericFeature(
122                            "Indices larger than pixel values".into(),
123                        ),
124                    ),
125                ));
126            }
127        }
128
129        // TODO: validate the rest of the fields in the header.
130
131        // Read image ID (and ignore it)
132        let mut tmp = [0u8; 256];
133        r.read_exact(&mut tmp[0..header.id_length as usize])?;
134
135        // Read color map
136        let mut color_map = None;
137        if header.map_type == 1 {
138            let entry_size = (header.map_entry_size as usize).div_ceil(8);
139            if ![2, 3, 4].contains(&entry_size) {
140                return Err(ImageError::Unsupported(
141                    UnsupportedError::from_format_and_kind(
142                        ImageFormat::Tga.into(),
143                        UnsupportedErrorKind::GenericFeature(
144                            "Unsupported color map entry size".into(),
145                        ),
146                    ),
147                ));
148            }
149
150            let mut bytes = Vec::new();
151            r.read_exact_vec(&mut bytes, entry_size * header.map_length as usize)?;
152
153            // Color maps are technically allowed in non-color-mapped images, so check that we
154            // actually need the color map before storing it.
155            if image_type.is_color_mapped() {
156                color_map = Some(ColorMap {
157                    entry_size,
158                    start_offset: header.map_origin as usize,
159                    bytes,
160                });
161            }
162        }
163
164        // Compute output pixel depth
165        let total_pixel_bits = if header.map_type == 1 {
166            header.map_entry_size
167        } else {
168            header.pixel_depth
169        };
170        let num_other_bits = total_pixel_bits
171            .checked_sub(num_alpha_bits)
172            .ok_or_else(|| {
173                ImageError::Decoding(DecodingError::new(
174                    ImageFormat::Tga.into(),
175                    "More alpha bits than pixel bits",
176                ))
177            })?;
178
179        // Determine color type
180        let color_type;
181        let mut original_color_type = None;
182        match (num_alpha_bits, num_other_bits, image_type.is_color()) {
183            // really, the encoding is BGR and BGRA, this is fixed up with
184            // `TgaDecoder::reverse_encoding`.
185            (0, 32, true) => color_type = ColorType::Rgba8,
186            (8, 24, true) => color_type = ColorType::Rgba8,
187            (0, 24, true) => color_type = ColorType::Rgb8,
188            (8, 8, false) => color_type = ColorType::La8,
189            (0, 8, false) => color_type = ColorType::L8,
190            (8, 0, false) => {
191                // alpha-only image is treated as L8
192                color_type = ColorType::L8;
193                original_color_type = Some(ExtendedColorType::A8);
194            }
195            _ => {
196                return Err(ImageError::Unsupported(
197                    UnsupportedError::from_format_and_kind(
198                        ImageFormat::Tga.into(),
199                        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)),
200                    ),
201                ))
202            }
203        }
204
205        Ok(TgaDecoder {
206            r,
207
208            width,
209            height,
210            raw_bytes_per_pixel,
211
212            image_type,
213            color_type,
214            original_color_type,
215
216            header,
217            color_map,
218        })
219    }
220
221    /// Reads a run length encoded data for given number of bytes
222    fn read_encoded_data(&mut self, buf: &mut [u8]) -> io::Result<()> {
223        assert!(self.raw_bytes_per_pixel <= 4);
224        let mut repeat_buf = [0; 4];
225        let repeat_buf = &mut repeat_buf[..self.raw_bytes_per_pixel];
226
227        let mut index = 0;
228        while index < buf.len() {
229            let run_packet = self.r.read_u8()?;
230            // If the highest bit in `run_packet` is set, then we repeat pixels
231            //
232            // Note: the TGA format adds 1 to both counts because having a count
233            // of 0 would be pointless.
234            if (run_packet & 0x80) != 0 {
235                // high bit set, so we will repeat the data
236                let repeat_count = ((run_packet & !0x80) + 1) as usize;
237                self.r.read_exact(repeat_buf)?;
238
239                for chunk in buf[index..]
240                    .chunks_exact_mut(self.raw_bytes_per_pixel)
241                    .take(repeat_count)
242                {
243                    chunk.copy_from_slice(repeat_buf);
244                }
245                index += repeat_count * self.raw_bytes_per_pixel;
246            } else {
247                // not set, so `run_packet+1` is the number of non-encoded pixels
248                let num_raw_bytes =
249                    ((run_packet + 1) as usize * self.raw_bytes_per_pixel).min(buf.len() - index);
250
251                self.r.read_exact(&mut buf[index..][..num_raw_bytes])?;
252                index += num_raw_bytes;
253            }
254        }
255
256        Ok(())
257    }
258
259    /// Expands indices into its mapped color
260    fn expand_color_map(
261        &self,
262        input: &[u8],
263        output: &mut [u8],
264        color_map: &ColorMap,
265    ) -> ImageResult<()> {
266        if self.raw_bytes_per_pixel == 1 {
267            for (&index, chunk) in input
268                .iter()
269                .zip(output.chunks_exact_mut(color_map.entry_size))
270            {
271                if let Some(color) = color_map.get(index as usize) {
272                    chunk.copy_from_slice(color);
273                } else {
274                    return Err(ImageError::Decoding(DecodingError::new(
275                        ImageFormat::Tga.into(),
276                        "Invalid color map index",
277                    )));
278                }
279            }
280        } else if self.raw_bytes_per_pixel == 2 {
281            for (index, chunk) in input
282                .chunks_exact(2)
283                .zip(output.chunks_exact_mut(color_map.entry_size))
284            {
285                let index = u16::from_le_bytes(index.try_into().unwrap());
286                if let Some(color) = color_map.get(index as usize) {
287                    chunk.copy_from_slice(color);
288                } else {
289                    return Err(ImageError::Decoding(DecodingError::new(
290                        ImageFormat::Tga.into(),
291                        "Invalid color map index",
292                    )));
293                }
294            }
295        } else {
296            unreachable!("Supported bytes_per_pixel values are checked in TgaDecoder::new");
297        }
298
299        Ok(())
300    }
301
302    /// Reverse from BGR encoding to RGB encoding
303    ///
304    /// TGA files are stored in the BGRA encoding. This function swaps
305    /// the blue and red bytes in the `pixels` array.
306    fn reverse_encoding_in_output(&mut self, pixels: &mut [u8]) {
307        // We only need to reverse the encoding of color images
308        match self.color_type {
309            ColorType::Rgb8 | ColorType::Rgba8 => {
310                for chunk in pixels.chunks_mut(self.color_type.bytes_per_pixel().into()) {
311                    chunk.swap(0, 2);
312                }
313            }
314            _ => {}
315        }
316    }
317
318    /// Change image orientation depending on the flags set
319    fn fixup_orientation(&mut self, pixels: &mut [u8]) {
320        let orientation = TgaOrientation::from_image_desc_byte(self.header.image_desc);
321
322        // Flip image if bottom->top direction
323        if (orientation == TgaOrientation::BottomLeft || orientation == TgaOrientation::BottomRight)
324            && self.height > 1
325        {
326            let row_stride = self.width * self.raw_bytes_per_pixel;
327
328            let (left_part, right_part) = pixels.split_at_mut(self.height / 2 * row_stride);
329
330            for (src, dst) in left_part
331                .chunks_exact_mut(row_stride)
332                .zip(right_part.chunks_exact_mut(row_stride).rev())
333            {
334                for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
335                    std::mem::swap(src, dst);
336                }
337            }
338        }
339
340        // Flop image if right->left direction
341        if (orientation == TgaOrientation::BottomRight || orientation == TgaOrientation::TopRight)
342            && self.width > 1
343        {
344            for row in pixels.chunks_exact_mut(self.width * self.raw_bytes_per_pixel) {
345                let (left_part, right_part) =
346                    row.split_at_mut(self.width / 2 * self.raw_bytes_per_pixel);
347                for (src, dst) in left_part
348                    .chunks_exact_mut(self.raw_bytes_per_pixel)
349                    .zip(right_part.chunks_exact_mut(self.raw_bytes_per_pixel).rev())
350                {
351                    for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
352                        std::mem::swap(dst, src);
353                    }
354                }
355            }
356        }
357    }
358}
359
360impl<R: Read> ImageDecoder for TgaDecoder<R> {
361    fn dimensions(&self) -> (u32, u32) {
362        (self.width as u32, self.height as u32)
363    }
364
365    fn color_type(&self) -> ColorType {
366        self.color_type
367    }
368
369    fn original_color_type(&self) -> ExtendedColorType {
370        self.original_color_type
371            .unwrap_or_else(|| self.color_type().into())
372    }
373
374    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
375        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
376
377        // Decode the raw data
378        //
379        // We have already checked in `TgaDecoder::new` that the indices take less space than the
380        // pixels they encode, so it is safe to read the raw data into `buf`.
381        let num_raw_bytes = self.width * self.height * self.raw_bytes_per_pixel;
382        if self.image_type.is_encoded() {
383            self.read_encoded_data(&mut buf[..num_raw_bytes])?;
384        } else {
385            self.r.read_exact(&mut buf[..num_raw_bytes])?;
386        }
387
388        self.fixup_orientation(&mut buf[..num_raw_bytes]);
389
390        // Expand the indices using the color map if necessary
391        if let Some(ref color_map) = self.color_map {
392            // This allocation could be avoided by expanding each row (or block of pixels) as it is
393            // read, or by doing the color map expansion in-place. But those may be more effort than
394            // it is worth.
395            let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?;
396            rawbuf.extend_from_slice(&buf[..num_raw_bytes]);
397
398            self.expand_color_map(&rawbuf, buf, color_map)?;
399        }
400
401        self.reverse_encoding_in_output(buf);
402
403        Ok(())
404    }
405
406    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
407        (*self).read_image(buf)
408    }
409}