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