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