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}
154