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