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>(
|
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 | |