1use super::header::{Header, ImageType, ALPHA_BIT_MASK};
2use crate::error::DecodingError;
3use crate::io::ReadExt;
4use crate::utils::vec_try_with_capacity;
5use crate::{
6 color::{ColorType, ExtendedColorType},
7 error::{ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind},
8 ImageDecoder, ImageFormat,
9};
10use alloc::{boxed::Box, vec::Vec};
11use byteorder_lite::ReadBytesExt;
12use no_std_io::io::{self, Read};
13
14struct ColorMap {
15 start_offset: usize,
17 entry_size: usize,
18 bytes: Vec<u8>,
19}
20
21impl ColorMap {
22 pub(crate) fn get(&self, index: usize) -> Option<&[u8]> {
24 let entry = self.entry_size * index.checked_sub(self.start_offset)?;
25 self.bytes.get(entry..entry + self.entry_size)
26 }
27}
28
29pub struct TgaDecoder<R> {
31 r: R,
32
33 width: usize,
34 height: usize,
35
36 raw_bytes_per_pixel: usize,
39
40 image_type: ImageType,
41 color_type: ColorType,
42 original_color_type: Option<ExtendedColorType>,
43
44 header: Header,
45 color_map: Option<ColorMap>,
46}
47
48#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
49enum TgaOrientation {
50 TopLeft,
51 TopRight,
52 BottomRight,
53 BottomLeft,
54}
55
56impl TgaOrientation {
57 fn from_image_desc_byte(value: u8) -> Self {
58 if value & (1u8 << 4) == 0 {
63 if value & (1u8 << 5) == 0 {
65 TgaOrientation::BottomLeft
66 } else {
67 TgaOrientation::TopLeft
68 }
69 } else {
70 if value & (1u8 << 5) == 0 {
72 TgaOrientation::BottomRight
73 } else {
74 TgaOrientation::TopRight
75 }
76 }
77 }
78}
79
80static LOOKUP_TABLE_5_BIT_TO_8_BIT: [u8; 32] = [
83 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173,
84 181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
85];
86
87fn expand_rgb15_to_rgb24(data: [u8; 2]) -> [u8; 3] {
89 let val = u16::from_le_bytes(data);
90 [
91 LOOKUP_TABLE_5_BIT_TO_8_BIT[(val & 0b11111) as usize],
92 LOOKUP_TABLE_5_BIT_TO_8_BIT[((val >> 5) & 0b11111) as usize],
93 LOOKUP_TABLE_5_BIT_TO_8_BIT[((val >> 10) & 0b11111) as usize],
94 ]
95}
96
97impl<R: Read> TgaDecoder<R> {
98 pub fn new(mut r: R) -> ImageResult<TgaDecoder<R>> {
100 let header = Header::from_reader(&mut r)?;
102 let image_type = ImageType::new(header.image_type);
103 let width = header.image_width as usize;
104 let height = header.image_height as usize;
105 let raw_bytes_per_pixel = (header.pixel_depth as usize).div_ceil(8);
106 let num_attrib_bits = header.image_desc & ALPHA_BIT_MASK;
107
108 if width == 0 || height == 0 {
109 return Err(ImageError::Decoding(DecodingError::new(
110 ImageFormat::Tga.into(),
111 "Invalid empty image",
112 )));
113 }
114
115 if image_type.is_color_mapped() {
116 if header.map_type != 1 {
117 return Err(ImageError::Decoding(DecodingError::new(
118 ImageFormat::Tga.into(),
119 "Color map type must be 1 for color mapped images",
120 )));
121 } else if ![8, 16].contains(&header.pixel_depth) {
122 return Err(ImageError::Decoding(DecodingError::new(
123 ImageFormat::Tga.into(),
124 "Color map must use 1 or 2 byte indexes",
125 )));
126 } else if header.pixel_depth > header.map_entry_size {
127 return Err(ImageError::Unsupported(
128 UnsupportedError::from_format_and_kind(
129 ImageFormat::Tga.into(),
130 UnsupportedErrorKind::GenericFeature(
131 "Indices larger than pixel values".into(),
132 ),
133 ),
134 ));
135 }
136 }
137
138 let total_pixel_bits = if image_type.is_color_mapped() {
140 header.map_entry_size
141 } else {
142 header.pixel_depth
143 };
144 let num_other_bits = total_pixel_bits
145 .checked_sub(num_attrib_bits)
146 .ok_or_else(|| {
147 ImageError::Decoding(DecodingError::new(
148 ImageFormat::Tga.into(),
149 "More alpha bits than pixel bits",
150 ))
151 })?;
152
153 let color_type;
155 let mut original_color_type = None;
156 match (num_attrib_bits, num_other_bits, image_type.is_color()) {
157 (0, 32, true) => color_type = ColorType::Rgba8,
160 (8, 24, true) => color_type = ColorType::Rgba8,
161 (0, 24, true) => color_type = ColorType::Rgb8,
162 (1, 15, true) | (0, 15, true) | (0, 16, true) => {
163 color_type = ColorType::Rgb8;
167 original_color_type = Some(ExtendedColorType::Rgb5x1);
168 }
169 (8, 8, false) => color_type = ColorType::La8,
170 (0, 8, false) => color_type = ColorType::L8,
171 (8, 0, false) => {
172 color_type = ColorType::L8;
174 original_color_type = Some(ExtendedColorType::A8);
175 }
176 _ => {
177 return Err(ImageError::Unsupported(
178 UnsupportedError::from_format_and_kind(
179 ImageFormat::Tga.into(),
180 UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)),
181 ),
182 ))
183 }
184 }
185
186 let mut tmp = [0u8; 256];
190 r.read_exact(&mut tmp[0..header.id_length as usize])?;
191
192 let mut color_map = None;
194 if header.map_type == 1 {
195 if ![15, 16, 24, 32].contains(&header.map_entry_size) {
196 return Err(ImageError::Unsupported(
197 UnsupportedError::from_format_and_kind(
198 ImageFormat::Tga.into(),
199 UnsupportedErrorKind::GenericFeature(
200 "Unsupported color map entry size".into(),
201 ),
202 ),
203 ));
204 }
205 let mut entry_size = (header.map_entry_size as usize).div_ceil(8);
206
207 let mut bytes = Vec::new();
208 r.read_exact_vec(&mut bytes, entry_size * header.map_length as usize)?;
209
210 if image_type.is_color_mapped() {
213 if [15, 16].contains(&header.map_entry_size) {
215 let mut expanded = Vec::new();
216 for &entry in bytes.as_chunks::<2>().0.iter() {
217 expanded.extend_from_slice(&expand_rgb15_to_rgb24(entry));
218 }
219 bytes = expanded;
220 entry_size = 3;
221 }
222
223 color_map = Some(ColorMap {
224 entry_size,
225 start_offset: header.map_origin as usize,
226 bytes,
227 });
228 }
229 }
230
231 Ok(TgaDecoder {
232 r,
233
234 width,
235 height,
236 raw_bytes_per_pixel,
237
238 image_type,
239 color_type,
240 original_color_type,
241
242 header,
243 color_map,
244 })
245 }
246
247 fn read_encoded_data(&mut self, buf: &mut [u8]) -> io::Result<()> {
249 assert!(self.raw_bytes_per_pixel <= 4);
250 let mut repeat_buf = [0; 4];
251 let repeat_buf = &mut repeat_buf[..self.raw_bytes_per_pixel];
252
253 let mut index = 0;
254 while index < buf.len() {
255 let run_packet = self.r.read_u8()?;
256 if (run_packet & 0x80) != 0 {
261 let repeat_count = ((run_packet & !0x80) + 1) as usize;
263 self.r.read_exact(repeat_buf)?;
264
265 for chunk in buf[index..]
266 .chunks_exact_mut(self.raw_bytes_per_pixel)
267 .take(repeat_count)
268 {
269 chunk.copy_from_slice(repeat_buf);
270 }
271 index += repeat_count * self.raw_bytes_per_pixel;
272 } else {
273 let num_raw_bytes =
275 ((run_packet + 1) as usize * self.raw_bytes_per_pixel).min(buf.len() - index);
276
277 self.r.read_exact(&mut buf[index..][..num_raw_bytes])?;
278 index += num_raw_bytes;
279 }
280 }
281
282 Ok(())
283 }
284
285 fn expand_color_map(
287 &self,
288 input: &[u8],
289 output: &mut [u8],
290 color_map: &ColorMap,
291 ) -> ImageResult<()> {
292 if self.raw_bytes_per_pixel == 1 {
293 for (&index, chunk) in input
294 .iter()
295 .zip(output.chunks_exact_mut(color_map.entry_size))
296 {
297 if let Some(color) = color_map.get(index as usize) {
298 chunk.copy_from_slice(color);
299 } else {
300 return Err(ImageError::Decoding(DecodingError::new(
301 ImageFormat::Tga.into(),
302 "Invalid color map index",
303 )));
304 }
305 }
306 } else if self.raw_bytes_per_pixel == 2 {
307 let input_chunks = input.as_chunks::<2>().0.iter();
308 for (&index, chunk) in input_chunks.zip(output.chunks_exact_mut(color_map.entry_size)) {
309 let index = u16::from_le_bytes(index);
310 if let Some(color) = color_map.get(index as usize) {
311 chunk.copy_from_slice(color);
312 } else {
313 return Err(ImageError::Decoding(DecodingError::new(
314 ImageFormat::Tga.into(),
315 "Invalid color map index",
316 )));
317 }
318 }
319 } else {
320 unreachable!("Supported bytes_per_pixel values are checked in TgaDecoder::new");
321 }
322
323 Ok(())
324 }
325
326 fn reverse_encoding_in_output(&mut self, pixels: &mut [u8]) {
331 match self.color_type {
333 ColorType::Rgb8 | ColorType::Rgba8 => {
334 for chunk in pixels.chunks_mut(self.color_type.bytes_per_pixel().into()) {
335 chunk.swap(0, 2);
336 }
337 }
338 _ => {}
339 }
340 }
341
342 fn fixup_orientation(&mut self, pixels: &mut [u8]) {
344 let orientation = TgaOrientation::from_image_desc_byte(self.header.image_desc);
345
346 if (orientation == TgaOrientation::BottomLeft || orientation == TgaOrientation::BottomRight)
348 && self.height > 1
349 {
350 let row_stride = self.width * self.raw_bytes_per_pixel;
351
352 let (left_part, right_part) = pixels.split_at_mut(self.height / 2 * row_stride);
353
354 for (src, dst) in left_part
355 .chunks_exact_mut(row_stride)
356 .zip(right_part.chunks_exact_mut(row_stride).rev())
357 {
358 for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
359 core::mem::swap(src, dst);
360 }
361 }
362 }
363
364 if (orientation == TgaOrientation::BottomRight || orientation == TgaOrientation::TopRight)
366 && self.width > 1
367 {
368 for row in pixels.chunks_exact_mut(self.width * self.raw_bytes_per_pixel) {
369 let (left_part, right_part) =
370 row.split_at_mut(self.width / 2 * self.raw_bytes_per_pixel);
371 for (src, dst) in left_part
372 .chunks_exact_mut(self.raw_bytes_per_pixel)
373 .zip(right_part.chunks_exact_mut(self.raw_bytes_per_pixel).rev())
374 {
375 for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
376 core::mem::swap(dst, src);
377 }
378 }
379 }
380 }
381 }
382}
383
384impl<R: Read> ImageDecoder for TgaDecoder<R> {
385 fn dimensions(&self) -> (u32, u32) {
386 (self.width as u32, self.height as u32)
387 }
388
389 fn color_type(&self) -> ColorType {
390 self.color_type
391 }
392
393 fn original_color_type(&self) -> ExtendedColorType {
394 self.original_color_type
395 .unwrap_or_else(|| self.color_type().into())
396 }
397
398 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
399 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
400
401 if self.raw_bytes_per_pixel > self.color_type.bytes_per_pixel().into() {
406 return Err(ImageError::Unsupported(
407 UnsupportedError::from_format_and_kind(
408 ImageFormat::Tga.into(),
409 UnsupportedErrorKind::GenericFeature(
410 "Color-mapped images with indices wider than color are not supported"
411 .into(),
412 ),
413 ),
414 ));
415 }
416 let num_raw_bytes = self.width * self.height * self.raw_bytes_per_pixel;
417 debug_assert!(num_raw_bytes <= buf.len());
418
419 if self.image_type.is_encoded() {
420 self.read_encoded_data(&mut buf[..num_raw_bytes])?;
421 } else {
422 self.r.read_exact(&mut buf[..num_raw_bytes])?;
423 }
424
425 self.fixup_orientation(&mut buf[..num_raw_bytes]);
426
427 if let Some(ref color_map) = self.color_map {
429 let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?;
433 rawbuf.extend_from_slice(&buf[..num_raw_bytes]);
434
435 self.expand_color_map(&rawbuf, buf, color_map)?;
436 } else if self.original_color_type == Some(ExtendedColorType::Rgb5x1) {
437 let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?;
440 rawbuf.extend_from_slice(&buf[..num_raw_bytes]);
441
442 let rawbuf_chunks = rawbuf.as_chunks::<2>().0.iter();
443 let buf_chunks = buf.as_chunks_mut::<3>().0.iter_mut();
444 for (&src, dst) in rawbuf_chunks.zip(buf_chunks) {
445 *dst = expand_rgb15_to_rgb24(src);
446 }
447 }
448
449 self.reverse_encoding_in_output(buf);
450
451 Ok(())
452 }
453
454 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
455 (*self).read_image(buf)
456 }
457}