1use std::io::{BufRead, Seek, SeekFrom};
23use crate::{util::*, ImageResult, ImageSize};
45pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> {
6 reader.seek(SeekFrom::Start(12))?;
78let width = read_u16(reader, &Endian::Little)? as usize;
9let height = read_u16(reader, &Endian::Little)? as usize;
1011Ok(ImageSize { width, height })
12}
1314pub 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
16let colormap_type = header[1];
17let image_type = header[2];
1819// 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.
21if image_type != 1
22&& image_type != 2
23&& image_type != 3
24&& image_type != 9
25&& image_type != 10
26&& image_type != 11
27{
28return false;
29 }
3031// 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
33if colormap_type >= 2 {
34return false;
35 }
3637 is_tga(reader, image_type, colormap_type).unwrap_or(false)
38}
3940fn 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.
43reader.seek(SeekFrom::End(-18))?;
4445// Look for Version 2 TGA footer signature as it's the only concrete data to verify it's a TGA
46let mut signature = [0; 18];
47 reader.read_exact(&mut signature)?;
4849// 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.
53if &signature == b"TRUEVISION-XFILE.\0" {
54return Ok(true);
55 }
5657// 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.
6162 // If image type is color mapped, then color map type must be set to 1
63if (image_type == 1 || image_type == 9) && colormap_type != 1 {
64return Ok(false);
65 }
6667// Start reading the header information
68reader.seek(SeekFrom::Start(3))?;
6970let colormap_offset = read_u32(reader, &Endian::Little)?;
71let colormap_size = read_u8(reader)?;
7273// If there is no color map then assume that origin, length, and entry size must be 0
74if colormap_type == 0 {
75if colormap_offset != 0 {
76return Ok(false);
77 }
7879if colormap_size != 0 {
80return Ok(false);
81 }
82 }
8384// Assume color map sizes must be a multiple of 8
85if colormap_type == 1 &&
86 (colormap_size != 0 &&
87 colormap_size != 8 &&
88 colormap_size != 16 &&
89 colormap_size != 24 &&
90 colormap_size != 32)
91 {
92return Ok(false);
93 }
9495 reader.seek(SeekFrom::Start(16))?;
96let pixel_size = read_u8(reader)?;
97let descriptor = read_u8(reader)?;
98let alpha_bits = descriptor & 0x0F;
99100// Reserved bit, must be set to 0
101if descriptor & 0x10 != 0 {
102return Ok(false);
103 }
104105// Assume pixel size must be a multiple of 8
106if pixel_size != 8 && pixel_size != 16 && pixel_size != 24 && pixel_size != 32 {
107return Ok(false);
108 }
109110// Verify that the alpha bits value makes sense given pixel size
111 //
112 // 8 and 24 bits have no alpha
113if (pixel_size == 8 || pixel_size == 24) && alpha_bits != 0 {
114return Ok(false);
115 }
116117// 16 bits can either have 0 or 1 bits of alpha
118if pixel_size == 16 && alpha_bits >= 2 {
119return Ok(false);
120 }
121122// 32 bits must have 8 bits of alpha, although I've seen one with 0?
123if pixel_size == 32 && (alpha_bits != 8 && alpha_bits != 0) {
124return Ok(false);
125 }
126127Ok(true)
128}