image/codecs/tga/
header.rs

1use crate::error::{UnsupportedError, UnsupportedErrorKind};
2use crate::{ExtendedColorType, ImageError, ImageFormat, ImageResult};
3use byteorder_lite::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use std::io::{Read, Write};
5
6pub(crate) const ALPHA_BIT_MASK: u8 = 0b1111;
7pub(crate) const SCREEN_ORIGIN_BIT_MASK: u8 = 0b10_0000;
8
9pub(crate) enum ImageType {
10    NoImageData = 0,
11    /// Uncompressed images.
12    RawColorMap = 1,
13    RawTrueColor = 2,
14    RawGrayScale = 3,
15    /// Run length encoded images.
16    RunColorMap = 9,
17    RunTrueColor = 10,
18    RunGrayScale = 11,
19    Unknown,
20}
21
22impl ImageType {
23    /// Create a new image type from a u8.
24    pub(crate) fn new(img_type: u8) -> ImageType {
25        match img_type {
26            0 => ImageType::NoImageData,
27
28            1 => ImageType::RawColorMap,
29            2 => ImageType::RawTrueColor,
30            3 => ImageType::RawGrayScale,
31
32            9 => ImageType::RunColorMap,
33            10 => ImageType::RunTrueColor,
34            11 => ImageType::RunGrayScale,
35
36            _ => ImageType::Unknown,
37        }
38    }
39
40    /// Check if the image format uses colors as opposed to gray scale.
41    pub(crate) fn is_color(&self) -> bool {
42        matches! { *self,
43            ImageType::RawColorMap
44            | ImageType::RawTrueColor
45            | ImageType::RunTrueColor
46            | ImageType::RunColorMap
47        }
48    }
49
50    /// Does the image use a color map.
51    pub(crate) fn is_color_mapped(&self) -> bool {
52        matches! { *self, ImageType::RawColorMap | ImageType::RunColorMap }
53    }
54
55    /// Is the image run length encoded.
56    pub(crate) fn is_encoded(&self) -> bool {
57        matches! {*self, ImageType::RunColorMap | ImageType::RunTrueColor | ImageType::RunGrayScale }
58    }
59}
60
61/// Header used by TGA image files.
62#[derive(Debug, Default)]
63pub(crate) struct Header {
64    pub(crate) id_length: u8,      // length of ID string
65    pub(crate) map_type: u8,       // color map type
66    pub(crate) image_type: u8,     // image type code
67    pub(crate) map_origin: u16,    // starting index of map
68    pub(crate) map_length: u16,    // length of map
69    pub(crate) map_entry_size: u8, // size of map entries in bits
70    pub(crate) x_origin: u16,      // x-origin of image
71    pub(crate) y_origin: u16,      // y-origin of image
72    pub(crate) image_width: u16,   // width of image
73    pub(crate) image_height: u16,  // height of image
74    pub(crate) pixel_depth: u8,    // bits per pixel
75    pub(crate) image_desc: u8,     // image descriptor
76}
77
78impl Header {
79    /// Load the header with values from pixel information.
80    pub(crate) fn from_pixel_info(
81        color_type: ExtendedColorType,
82        width: u16,
83        height: u16,
84        use_rle: bool,
85    ) -> ImageResult<Self> {
86        let mut header = Self::default();
87
88        if width > 0 && height > 0 {
89            let (num_alpha_bits, other_channel_bits, image_type) = match (color_type, use_rle) {
90                (ExtendedColorType::Rgba8, true) => (8, 24, ImageType::RunTrueColor),
91                (ExtendedColorType::Rgb8, true) => (0, 24, ImageType::RunTrueColor),
92                (ExtendedColorType::La8, true) => (8, 8, ImageType::RunGrayScale),
93                (ExtendedColorType::L8, true) => (0, 8, ImageType::RunGrayScale),
94                (ExtendedColorType::Rgba8, false) => (8, 24, ImageType::RawTrueColor),
95                (ExtendedColorType::Rgb8, false) => (0, 24, ImageType::RawTrueColor),
96                (ExtendedColorType::La8, false) => (8, 8, ImageType::RawGrayScale),
97                (ExtendedColorType::L8, false) => (0, 8, ImageType::RawGrayScale),
98                _ => {
99                    return Err(ImageError::Unsupported(
100                        UnsupportedError::from_format_and_kind(
101                            ImageFormat::Tga.into(),
102                            UnsupportedErrorKind::Color(color_type),
103                        ),
104                    ))
105                }
106            };
107
108            header.image_type = image_type as u8;
109            header.image_width = width;
110            header.image_height = height;
111            header.pixel_depth = num_alpha_bits + other_channel_bits;
112            header.image_desc = num_alpha_bits & ALPHA_BIT_MASK;
113            header.image_desc |= SCREEN_ORIGIN_BIT_MASK; // Upper left origin.
114        }
115
116        Ok(header)
117    }
118
119    /// Load the header with values from the reader.
120    pub(crate) fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
121        Ok(Self {
122            id_length: r.read_u8()?,
123            map_type: r.read_u8()?,
124            image_type: r.read_u8()?,
125            map_origin: r.read_u16::<LittleEndian>()?,
126            map_length: r.read_u16::<LittleEndian>()?,
127            map_entry_size: r.read_u8()?,
128            x_origin: r.read_u16::<LittleEndian>()?,
129            y_origin: r.read_u16::<LittleEndian>()?,
130            image_width: r.read_u16::<LittleEndian>()?,
131            image_height: r.read_u16::<LittleEndian>()?,
132            pixel_depth: r.read_u8()?,
133            image_desc: r.read_u8()?,
134        })
135    }
136
137    /// Write out the header values.
138    pub(crate) fn write_to(&self, w: &mut dyn Write) -> ImageResult<()> {
139        w.write_u8(self.id_length)?;
140        w.write_u8(self.map_type)?;
141        w.write_u8(self.image_type)?;
142        w.write_u16::<LittleEndian>(self.map_origin)?;
143        w.write_u16::<LittleEndian>(self.map_length)?;
144        w.write_u8(self.map_entry_size)?;
145        w.write_u16::<LittleEndian>(self.x_origin)?;
146        w.write_u16::<LittleEndian>(self.y_origin)?;
147        w.write_u16::<LittleEndian>(self.image_width)?;
148        w.write_u16::<LittleEndian>(self.image_height)?;
149        w.write_u8(self.pixel_depth)?;
150        w.write_u8(self.image_desc)?;
151        Ok(())
152    }
153}