use crate::{ error::{UnsupportedError, UnsupportedErrorKind}, ColorType, ImageError, ImageFormat, ImageResult, }; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::io::{Read, Write}; pub(crate) const ALPHA_BIT_MASK: u8 = 0b1111; pub(crate) const SCREEN_ORIGIN_BIT_MASK: u8 = 0b10_0000; pub(crate) enum ImageType { NoImageData = 0, /// Uncompressed images. RawColorMap = 1, RawTrueColor = 2, RawGrayScale = 3, /// Run length encoded images. RunColorMap = 9, RunTrueColor = 10, RunGrayScale = 11, Unknown, } impl ImageType { /// Create a new image type from a u8. pub(crate) fn new(img_type: u8) -> ImageType { match img_type { 0 => ImageType::NoImageData, 1 => ImageType::RawColorMap, 2 => ImageType::RawTrueColor, 3 => ImageType::RawGrayScale, 9 => ImageType::RunColorMap, 10 => ImageType::RunTrueColor, 11 => ImageType::RunGrayScale, _ => ImageType::Unknown, } } /// Check if the image format uses colors as opposed to gray scale. pub(crate) fn is_color(&self) -> bool { matches! { *self, ImageType::RawColorMap | ImageType::RawTrueColor | ImageType::RunTrueColor | ImageType::RunColorMap } } /// Does the image use a color map. pub(crate) fn is_color_mapped(&self) -> bool { matches! { *self, ImageType::RawColorMap | ImageType::RunColorMap } } /// Is the image run length encoded. pub(crate) fn is_encoded(&self) -> bool { matches! {*self, ImageType::RunColorMap | ImageType::RunTrueColor | ImageType::RunGrayScale } } } /// Header used by TGA image files. #[derive(Debug, Default)] pub(crate) struct Header { pub(crate) id_length: u8, // length of ID string pub(crate) map_type: u8, // color map type pub(crate) image_type: u8, // image type code pub(crate) map_origin: u16, // starting index of map pub(crate) map_length: u16, // length of map pub(crate) map_entry_size: u8, // size of map entries in bits pub(crate) x_origin: u16, // x-origin of image pub(crate) y_origin: u16, // y-origin of image pub(crate) image_width: u16, // width of image pub(crate) image_height: u16, // height of image pub(crate) pixel_depth: u8, // bits per pixel pub(crate) image_desc: u8, // image descriptor } impl Header { /// Load the header with values from pixel information. pub(crate) fn from_pixel_info( color_type: ColorType, width: u16, height: u16, ) -> ImageResult { let mut header = Self::default(); if width > 0 && height > 0 { let (num_alpha_bits, other_channel_bits, image_type) = match color_type { ColorType::Rgba8 => (8, 24, ImageType::RawTrueColor), ColorType::Rgb8 => (0, 24, ImageType::RawTrueColor), ColorType::La8 => (8, 8, ImageType::RawGrayScale), ColorType::L8 => (0, 8, ImageType::RawGrayScale), _ => { return Err(ImageError::Unsupported( UnsupportedError::from_format_and_kind( ImageFormat::Tga.into(), UnsupportedErrorKind::Color(color_type.into()), ), )) } }; header.image_type = image_type as u8; header.image_width = width; header.image_height = height; header.pixel_depth = num_alpha_bits + other_channel_bits; header.image_desc = num_alpha_bits & ALPHA_BIT_MASK; header.image_desc |= SCREEN_ORIGIN_BIT_MASK; // Upper left origin. } Ok(header) } /// Load the header with values from the reader. pub(crate) fn from_reader(r: &mut dyn Read) -> ImageResult { Ok(Self { id_length: r.read_u8()?, map_type: r.read_u8()?, image_type: r.read_u8()?, map_origin: r.read_u16::()?, map_length: r.read_u16::()?, map_entry_size: r.read_u8()?, x_origin: r.read_u16::()?, y_origin: r.read_u16::()?, image_width: r.read_u16::()?, image_height: r.read_u16::()?, pixel_depth: r.read_u8()?, image_desc: r.read_u8()?, }) } /// Write out the header values. pub(crate) fn write_to(&self, w: &mut dyn Write) -> ImageResult<()> { w.write_u8(self.id_length)?; w.write_u8(self.map_type)?; w.write_u8(self.image_type)?; w.write_u16::(self.map_origin)?; w.write_u16::(self.map_length)?; w.write_u8(self.map_entry_size)?; w.write_u16::(self.x_origin)?; w.write_u16::(self.y_origin)?; w.write_u16::(self.image_width)?; w.write_u16::(self.image_height)?; w.write_u8(self.pixel_depth)?; w.write_u8(self.image_desc)?; Ok(()) } }