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