1use std::error::Error;
8use std::fmt;
9use std::io::{self, Read, Seek, Write};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12#[repr(u8)]
13pub enum DataType {
14 NoData = 0,
15 Idx = 1,
16 TrueColor = 2,
17 Gray = 3,
18 IdxRle = 9,
19 TruecolorRle = 10,
20 GrayRle = 11,
21}
22
23#[derive(Debug)]
24pub struct InvalidDataType(u8);
25
26impl Error for InvalidDataType {}
27
28impl fmt::Display for InvalidDataType {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(f, "Invalid data type ({})", self.0)
31 }
32}
33
34impl TryFrom<u8> for DataType {
35 type Error = InvalidDataType;
36
37 fn try_from(value: u8) -> Result<Self, Self::Error> {
38 Ok(match value {
39 0 => Self::NoData,
40 1 => Self::Idx,
41 2 => Self::TrueColor,
42 3 => Self::Gray,
43 9 => Self::IdxRle,
44 10 => Self::TruecolorRle,
45 11 => Self::GrayRle,
46 n => return Err(InvalidDataType(n)),
47 })
48 }
49}
50
51#[derive(Clone, Debug)]
52pub struct TgaHeader {
53 pub width: u16,
54 pub height: u16,
55 pub id_len: u8,
56 pub palette_type: u8,
57 pub data_type: DataType,
58 pub bits_pp: u8,
59 pub flags: u8,
60}
61
62#[derive(Debug)]
63pub enum TgaDecodeError {
64 Io(io::Error),
65 InvalidHeader,
66 Unsupported(&'static str),
67 TooBig,
68}
69
70impl Error for TgaDecodeError {
71 fn source(&self) -> Option<&(dyn Error + 'static)> {
72 match self {
73 TgaDecodeError::Io(err) => Some(err),
74 _ => None,
75 }
76 }
77}
78
79impl fmt::Display for TgaDecodeError {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Self::Io(_) => write!(f, "IO error when decoding TGA file"),
83 Self::InvalidHeader => write!(f, "Invalid TGA file header"),
84 Self::Unsupported(reason) => write!(f, "Unsupported TGA file: {}", reason),
85 Self::TooBig => write!(f, "Image is too big"),
86 }
87 }
88}
89
90impl From<io::Error> for TgaDecodeError {
91 fn from(err: io::Error) -> Self {
92 Self::Io(err)
93 }
94}
95
96fn read_tga_header<R: Read + Seek>(reader: &mut R) -> Result<TgaHeader, TgaDecodeError> {
98 let id_len = read_u8(reader)?;
99 let palette_type = read_u8(reader)?;
100 let data_type = read_u8(reader)?.try_into().map_err(|_| TgaDecodeError::InvalidHeader)?;
101 let palette_beg = read_le_u16(reader)?;
102 let palette_len = read_le_u16(reader)?;
103 let palette_bits = read_u8(reader)?;
104
105 reader.seek(io::SeekFrom::Current(2 + 2))?; let width = read_le_u16(reader)?;
108 let height = read_le_u16(reader)?;
109 let bits_pp = read_u8(reader)?;
110 let flags = read_u8(reader)?;
111
112 if width < 1
113 || height < 1
114 || palette_type > 1
115 || (palette_type == 0 && (palette_beg > 0 || palette_len > 0 || palette_bits > 0))
116 {
117 return Err(TgaDecodeError::InvalidHeader);
118 }
119
120 Ok(TgaHeader {
121 id_len,
122 palette_type,
123 data_type,
124 width,
125 height,
126 bits_pp,
127 flags,
128 })
129}
130
131#[derive(Clone, Debug)]
132pub struct TgaImage {
133 pub header: TgaHeader,
134 pub data: Vec<u8>,
135 pub channels: TgaChannels,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139#[repr(u8)]
140pub enum TgaChannels {
141 Y = 1,
142 Ya = 2,
143 Bgr = 3,
144 Bgra = 4,
145}
146
147#[derive(Debug)]
148pub struct InvalidTgaChannelCount(u8);
149
150impl Error for InvalidTgaChannelCount {}
151
152impl fmt::Display for InvalidTgaChannelCount {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 write!(f, "Invalid TGA channel count ({})", self.0)
155 }
156}
157
158impl TryFrom<u8> for TgaChannels {
159 type Error = InvalidTgaChannelCount;
160
161 fn try_from(value: u8) -> Result<Self, Self::Error> {
162 Ok(match value {
163 1 => Self::Y,
164 2 => Self::Ya,
165 3 => Self::Bgr,
166 4 => Self::Bgra,
167 n => return Err(InvalidTgaChannelCount(n)),
168 })
169 }
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173pub enum BitsPerChannel {
174 B8,
175 B16,
176}
177
178const TGA_FLAG_INTERLACED: u8 = 0xc0;
179const TGA_FLAG_RIGHT_TO_LEFT: u8 = 0x10;
180const TGA_FLAG_BITSPP: u8 = 0x0f;
181const TGA_FLAG_ORIGIN_AT_TOP: u8 = 0x20;
182
183const TGA_FLAG_PACKET_IS_RLE: u8 = 0x80;
184const TGA_FLAG_PACKET_LEN: u8 = 0x7f;
185
186const TGA_MAXIMUM_IMAGE_SIZE: u64 = 0x7fff_ffff;
187
188pub fn read_tga<R: Read + Seek>(reader: &mut R) -> Result<TgaImage, TgaDecodeError> {
190 let header = read_tga_header(reader)?;
191
192 if header.flags & TGA_FLAG_INTERLACED > 0 {
193 return Err(TgaDecodeError::Unsupported("interlaced"));
194 }
195 if header.flags & TGA_FLAG_RIGHT_TO_LEFT > 0 {
196 return Err(TgaDecodeError::Unsupported("right-to-left"));
197 }
198
199 let attr_bits_pp = header.flags & TGA_FLAG_BITSPP;
200 if attr_bits_pp != 0 && attr_bits_pp != 8 {
201 return Err(TgaDecodeError::Unsupported("bits per pixel != 8"));
203 }
204 if header.palette_type > 0 {
205 return Err(TgaDecodeError::Unsupported("palette type != 0"));
206 }
207
208 match header.data_type {
209 DataType::TrueColor | DataType::TruecolorRle => {
210 if header.bits_pp != 24 && header.bits_pp != 32 {
211 return Err(TgaDecodeError::Unsupported("bits per pixel != 24 or 32"));
212 }
213 }
214 DataType::Gray | DataType::GrayRle => {
215 if header.bits_pp != 8 && !(header.bits_pp == 16 && attr_bits_pp == 8) {
216 return Err(TgaDecodeError::Unsupported("unsupported bits per pixel"));
217 }
218 }
219 DataType::NoData => {
220 return Err(TgaDecodeError::Unsupported("no data type"));
221 }
222 DataType::Idx => {
223 return Err(TgaDecodeError::Unsupported("idx data type"));
224 }
225 DataType::IdxRle => {
226 return Err(TgaDecodeError::Unsupported("idx rle data type"));
227 }
228 }
229
230 let is_origin_at_top = header.flags & TGA_FLAG_ORIGIN_AT_TOP > 0;
231 let is_rle = matches!(
232 header.data_type,
233 DataType::IdxRle | DataType::GrayRle | DataType::TruecolorRle
234 );
235
236 let channels: TgaChannels = (header.bits_pp / 8).try_into().unwrap(); let tchans = 4;
238 let linebuf_size = header.width * channels as u16;
239 let tline_size = header.width * tchans;
240
241 let flip = !is_origin_at_top;
242 let tstride = if flip { -(tline_size as i32) } else { tline_size as i32 };
243 let mut ti = if flip {
244 (header.height as isize - 1) * tline_size as isize
245 } else {
246 0
247 } as usize;
248
249 if header.width as u64 * header.height as u64 * tchans as u64 > TGA_MAXIMUM_IMAGE_SIZE {
250 return Err(TgaDecodeError::TooBig);
251 }
252
253 let mut data = vec![0_u8; header.width as usize * header.height as usize * tchans as usize];
254 let mut linebuf = vec![0_u8; linebuf_size as usize];
255
256 if header.id_len > 0 {
257 reader.seek(io::SeekFrom::Current(header.id_len as i64))?;
258 }
259
260 if !is_rle {
261 for _ in 0..header.height {
262 reader.read_exact(&mut linebuf)?;
263 to_rgba(channels, &linebuf, &mut data[ti..ti + tline_size as usize])?;
264 ti = ti.saturating_add_signed(tstride as isize);
265 }
266 } else {
267 let mut pixel = [0_u8; 4];
268 let mut packet_len = 0;
269 let mut is_rle = false;
270
271 for _ in 0..header.height {
272 let mut wanted = linebuf_size as usize; while wanted > 0 {
274 if packet_len == 0 {
275 let packet_head = read_u8(reader)?;
276 is_rle = packet_head & TGA_FLAG_PACKET_IS_RLE > 0;
277 packet_len = ((packet_head & TGA_FLAG_PACKET_LEN) + 1) as usize * channels as usize;
278 }
279
280 let gotten = linebuf_size as usize - wanted;
281 let copy_size = wanted.min(packet_len);
282 if is_rle {
283 let channels = channels as usize;
284 reader.read_exact(&mut pixel[..channels])?;
285
286 let mut p = gotten;
287 while p < gotten + copy_size {
288 let mut place = &mut linebuf[p..p + channels];
289 place.write_all(&pixel[..channels])?;
290 p += channels;
291 }
292 } else {
293 reader.read_exact(&mut linebuf[gotten..gotten + copy_size])?;
295 }
296
297 wanted -= copy_size;
298 packet_len -= copy_size;
299 }
300
301 to_rgba(channels, &linebuf, &mut data[ti..ti + tline_size as usize])?;
302 ti = ti.saturating_add_signed(tstride as isize);
303 }
304 }
305
306 Ok(TgaImage { header, data, channels })
307}
308
309fn to_rgba(channels: TgaChannels, src: &[u8], tgt: &mut [u8]) -> io::Result<()> {
310 match channels {
311 TgaChannels::Y => y_to_rgba(src, tgt),
312 TgaChannels::Ya => ya_to_rgba(src, tgt),
313 TgaChannels::Bgr => bgr_to_rgba(src, tgt),
314 TgaChannels::Bgra => bgra_to_rgba(src, tgt),
315 }
316}
317
318fn y_to_rgba(src: &[u8], tgt: &mut [u8]) -> io::Result<()> {
319 for i in 0..src.len() {
320 let (k, t) = (i, i * 4);
321 let mut tgt = &mut tgt[t..t + 3];
322 tgt.write_all(&[src[k]; 3])?;
323 tgt[t + 3] = 255;
324 }
325
326 Ok(())
327}
328
329fn ya_to_rgba(src: &[u8], tgt: &mut [u8]) -> io::Result<()> {
330 for i in 0..src.len() / 2 {
331 let (k, t) = (i * 2, i * 4);
332 let mut tgt = &mut tgt[t..t + 3];
333 tgt.write_all(&[src[k]; 3])?;
334 tgt[t + 3] = src[k + 1];
335 }
336
337 Ok(())
338}
339
340fn bgr_to_rgba(src: &[u8], tgt: &mut [u8]) -> io::Result<()> {
341 for i in 0..src.len() / 3 {
342 let (k, t) = (i * 3, i * 4);
343 tgt[t] = src[k + 2];
344 tgt[t + 1] = src[k + 1];
345 tgt[t + 2] = src[k];
346 tgt[t + 3] = 255;
347 }
348
349 Ok(())
350}
351
352fn bgra_to_rgba(src: &[u8], tgt: &mut [u8]) -> io::Result<()> {
353 for i in 0..src.len() / 4 {
354 let (k, t) = (i * 4, i * 4);
355 tgt[t] = src[k + 2];
356 tgt[t + 1] = src[k + 1];
357 tgt[t + 2] = src[k];
358 tgt[t + 3] = src[k + 3];
359 }
360
361 Ok(())
362}
363
364#[inline]
365fn read_u8<R: Read>(data: &mut R) -> io::Result<u8> {
366 let mut buf: [u8; 1] = [0];
367 data.read_exact(&mut buf)?;
368 Ok(u8::from_ne_bytes(buf))
369}
370
371#[inline]
372fn read_le_u16<R: Read>(data: &mut R) -> io::Result<u16> {
373 let mut buf: [u8; 2] = [0, 0];
374 data.read_exact(&mut buf)?;
375 Ok(u16::from_le_bytes(buf))
376}