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