| 1 | use crate::error::{UnsupportedError, UnsupportedErrorKind}; |
| 2 | use crate::{ExtendedColorType, ImageError, ImageFormat, ImageResult}; |
| 3 | use byteorder_lite::{LittleEndian, ReadBytesExt, WriteBytesExt}; |
| 4 | use std::io::{Read, Write}; |
| 5 | |
| 6 | pub(crate) const ALPHA_BIT_MASK: u8 = 0b1111; |
| 7 | pub(crate) const SCREEN_ORIGIN_BIT_MASK: u8 = 0b10_0000; |
| 8 | |
| 9 | pub(crate) enum ImageType { |
| 10 | NoImageData = 0, |
| 11 | /// Uncompressed images. |
| 12 | RawColorMap = 1, |
| 13 | RawTrueColor = 2, |
| 14 | RawGrayScale = 3, |
| 15 | /// Run length encoded images. |
| 16 | RunColorMap = 9, |
| 17 | RunTrueColor = 10, |
| 18 | RunGrayScale = 11, |
| 19 | Unknown, |
| 20 | } |
| 21 | |
| 22 | impl ImageType { |
| 23 | /// Create a new image type from a u8. |
| 24 | pub(crate) fn new(img_type: u8) -> ImageType { |
| 25 | match img_type { |
| 26 | 0 => ImageType::NoImageData, |
| 27 | |
| 28 | 1 => ImageType::RawColorMap, |
| 29 | 2 => ImageType::RawTrueColor, |
| 30 | 3 => ImageType::RawGrayScale, |
| 31 | |
| 32 | 9 => ImageType::RunColorMap, |
| 33 | 10 => ImageType::RunTrueColor, |
| 34 | 11 => ImageType::RunGrayScale, |
| 35 | |
| 36 | _ => ImageType::Unknown, |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | /// Check if the image format uses colors as opposed to gray scale. |
| 41 | pub(crate) fn is_color(&self) -> bool { |
| 42 | matches! { *self, |
| 43 | ImageType::RawColorMap |
| 44 | | ImageType::RawTrueColor |
| 45 | | ImageType::RunTrueColor |
| 46 | | ImageType::RunColorMap |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | /// Does the image use a color map. |
| 51 | pub(crate) fn is_color_mapped(&self) -> bool { |
| 52 | matches! { *self, ImageType::RawColorMap | ImageType::RunColorMap } |
| 53 | } |
| 54 | |
| 55 | /// Is the image run length encoded. |
| 56 | pub(crate) fn is_encoded(&self) -> bool { |
| 57 | matches! {*self, ImageType::RunColorMap | ImageType::RunTrueColor | ImageType::RunGrayScale } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | /// Header used by TGA image files. |
| 62 | #[derive (Debug, Default)] |
| 63 | pub(crate) struct Header { |
| 64 | pub(crate) id_length: u8, // length of ID string |
| 65 | pub(crate) map_type: u8, // color map type |
| 66 | pub(crate) image_type: u8, // image type code |
| 67 | pub(crate) map_origin: u16, // starting index of map |
| 68 | pub(crate) map_length: u16, // length of map |
| 69 | pub(crate) map_entry_size: u8, // size of map entries in bits |
| 70 | pub(crate) x_origin: u16, // x-origin of image |
| 71 | pub(crate) y_origin: u16, // y-origin of image |
| 72 | pub(crate) image_width: u16, // width of image |
| 73 | pub(crate) image_height: u16, // height of image |
| 74 | pub(crate) pixel_depth: u8, // bits per pixel |
| 75 | pub(crate) image_desc: u8, // image descriptor |
| 76 | } |
| 77 | |
| 78 | impl Header { |
| 79 | /// Load the header with values from pixel information. |
| 80 | pub(crate) fn from_pixel_info( |
| 81 | color_type: ExtendedColorType, |
| 82 | width: u16, |
| 83 | height: u16, |
| 84 | use_rle: bool, |
| 85 | ) -> ImageResult<Self> { |
| 86 | let mut header = Self::default(); |
| 87 | |
| 88 | if width > 0 && height > 0 { |
| 89 | let (num_alpha_bits, other_channel_bits, image_type) = match (color_type, use_rle) { |
| 90 | (ExtendedColorType::Rgba8, true) => (8, 24, ImageType::RunTrueColor), |
| 91 | (ExtendedColorType::Rgb8, true) => (0, 24, ImageType::RunTrueColor), |
| 92 | (ExtendedColorType::La8, true) => (8, 8, ImageType::RunGrayScale), |
| 93 | (ExtendedColorType::L8, true) => (0, 8, ImageType::RunGrayScale), |
| 94 | (ExtendedColorType::Rgba8, false) => (8, 24, ImageType::RawTrueColor), |
| 95 | (ExtendedColorType::Rgb8, false) => (0, 24, ImageType::RawTrueColor), |
| 96 | (ExtendedColorType::La8, false) => (8, 8, ImageType::RawGrayScale), |
| 97 | (ExtendedColorType::L8, false) => (0, 8, ImageType::RawGrayScale), |
| 98 | _ => { |
| 99 | return Err(ImageError::Unsupported( |
| 100 | UnsupportedError::from_format_and_kind( |
| 101 | ImageFormat::Tga.into(), |
| 102 | UnsupportedErrorKind::Color(color_type), |
| 103 | ), |
| 104 | )) |
| 105 | } |
| 106 | }; |
| 107 | |
| 108 | header.image_type = image_type as u8; |
| 109 | header.image_width = width; |
| 110 | header.image_height = height; |
| 111 | header.pixel_depth = num_alpha_bits + other_channel_bits; |
| 112 | header.image_desc = num_alpha_bits & ALPHA_BIT_MASK; |
| 113 | header.image_desc |= SCREEN_ORIGIN_BIT_MASK; // Upper left origin. |
| 114 | } |
| 115 | |
| 116 | Ok(header) |
| 117 | } |
| 118 | |
| 119 | /// Load the header with values from the reader. |
| 120 | pub(crate) fn from_reader(r: &mut dyn Read) -> ImageResult<Self> { |
| 121 | Ok(Self { |
| 122 | id_length: r.read_u8()?, |
| 123 | map_type: r.read_u8()?, |
| 124 | image_type: r.read_u8()?, |
| 125 | map_origin: r.read_u16::<LittleEndian>()?, |
| 126 | map_length: r.read_u16::<LittleEndian>()?, |
| 127 | map_entry_size: r.read_u8()?, |
| 128 | x_origin: r.read_u16::<LittleEndian>()?, |
| 129 | y_origin: r.read_u16::<LittleEndian>()?, |
| 130 | image_width: r.read_u16::<LittleEndian>()?, |
| 131 | image_height: r.read_u16::<LittleEndian>()?, |
| 132 | pixel_depth: r.read_u8()?, |
| 133 | image_desc: r.read_u8()?, |
| 134 | }) |
| 135 | } |
| 136 | |
| 137 | /// Write out the header values. |
| 138 | pub(crate) fn write_to(&self, w: &mut dyn Write) -> ImageResult<()> { |
| 139 | w.write_u8(self.id_length)?; |
| 140 | w.write_u8(self.map_type)?; |
| 141 | w.write_u8(self.image_type)?; |
| 142 | w.write_u16::<LittleEndian>(self.map_origin)?; |
| 143 | w.write_u16::<LittleEndian>(self.map_length)?; |
| 144 | w.write_u8(self.map_entry_size)?; |
| 145 | w.write_u16::<LittleEndian>(self.x_origin)?; |
| 146 | w.write_u16::<LittleEndian>(self.y_origin)?; |
| 147 | w.write_u16::<LittleEndian>(self.image_width)?; |
| 148 | w.write_u16::<LittleEndian>(self.image_height)?; |
| 149 | w.write_u8(self.pixel_depth)?; |
| 150 | w.write_u8(self.image_desc)?; |
| 151 | Ok(()) |
| 152 | } |
| 153 | } |
| 154 | |