1use std::io::{BufRead, Seek, SeekFrom};
2
3use crate::{util::*, ImageResult, ImageSize};
4
5pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
6 reader.seek(pos:SeekFrom::Start(12))?;
7
8 let width: usize = read_u16(reader, &Endian::Little)? as usize;
9 let height: usize = read_u16(reader, &Endian::Little)? as usize;
10
11 Ok(ImageSize { width, height })
12}
13
14pub fn matches<R: BufRead + Seek>(header: &[u8], reader: &mut R) -> bool {
15 // Do a naive check first to filter out any obviously non-TGA files
16 let colormap_type = header[1];
17 let image_type = header[2];
18
19 // Check the image type (byte 2) to be one of the uncompressed or RLE compressed types
20 // Note: I've seen mention of types 0, 32, and 33 but have no example files so have omitted them.
21 if image_type != 1
22 && image_type != 2
23 && image_type != 3
24 && image_type != 9
25 && image_type != 10
26 && image_type != 11
27 {
28 return false;
29 }
30
31 // Check that the colormap type (byte 1) is either 0 (no colormap) or 1 (colormap present)
32 // Technically 2-127 is reserved and 128-255 are usable by devs, but for simplicity we ignore them
33 if colormap_type >= 2 {
34 return false;
35 }
36
37 is_tga(reader, image_type, colormap_type).unwrap_or(false)
38}
39
40fn is_tga<R: BufRead + Seek>(
41 reader: &mut R,
42 image_type: u8,
43 colormap_type: u8,
44) -> ImageResult<bool> {
45 // Attempt to go to footer section. This also doubles as a size check since
46 // if there aren't 18 bytes available it will return an error.
47 reader.seek(SeekFrom::End(-18))?;
48
49 // Look for Version 2 TGA footer signature as it's the only concrete data to verify it's a TGA
50 let mut signature = [0; 18];
51 reader.read_exact(&mut signature)?;
52
53 // If signature is found then we should be confident it's a TGA
54 //
55 // We do not reset the seek here because size methods should
56 // be seeking themselves as a first step anyway.
57 if &signature == b"TRUEVISION-XFILE.\0" {
58 return Ok(true);
59 }
60
61 // Now we're into heuristic territory.
62 // With no footer I don't believe there is a 100% way to verify whether given bytes
63 // are a TGA or not. To get around this we add a few corroborating byte checks and
64 // if they make up a valid TGA configuration we assume that it's a TGA.
65
66 // If image type is color mapped, then color map type must be set to 1
67 if (image_type == 1 || image_type == 9) && colormap_type != 1 {
68 return Ok(false);
69 }
70
71 // Start reading the header information
72 reader.seek(SeekFrom::Start(3))?;
73
74 let colormap_offset = read_u32(reader, &Endian::Little)?;
75 let colormap_size = read_u8(reader)?;
76
77 // If there is no color map then assume that origin, length, and entry size must be 0
78 if colormap_type == 0 {
79 if colormap_offset != 0 {
80 return Ok(false);
81 }
82
83 if colormap_size != 0 {
84 return Ok(false);
85 }
86 }
87
88 // Assume color map sizes must be a multiple of 8
89 if colormap_type == 1
90 && (colormap_size != 0
91 && colormap_size != 8
92 && colormap_size != 16
93 && colormap_size != 24
94 && colormap_size != 32)
95 {
96 return Ok(false);
97 }
98
99 reader.seek(SeekFrom::Start(16))?;
100 let pixel_size = read_u8(reader)?;
101 let descriptor = read_u8(reader)?;
102 let alpha_bits = descriptor & 0x0F;
103
104 // Reserved bit, must be set to 0
105 if descriptor & 0x10 != 0 {
106 return Ok(false);
107 }
108
109 // Assume pixel size must be a multiple of 8
110 if pixel_size != 8 && pixel_size != 16 && pixel_size != 24 && pixel_size != 32 {
111 return Ok(false);
112 }
113
114 // Verify that the alpha bits value makes sense given pixel size
115 //
116 // 8 and 24 bits have no alpha
117 if (pixel_size == 8 || pixel_size == 24) && alpha_bits != 0 {
118 return Ok(false);
119 }
120
121 // 16 bits can either have 0 or 1 bits of alpha
122 if pixel_size == 16 && alpha_bits >= 2 {
123 return Ok(false);
124 }
125
126 // 32 bits must have 8 bits of alpha, although I've seen one with 0?
127 if pixel_size == 32 && (alpha_bits != 8 && alpha_bits != 0) {
128 return Ok(false);
129 }
130
131 Ok(true)
132}
133