use super::header::{Header, ImageType, ALPHA_BIT_MASK};
use crate::error::DecodingError;
use crate::io::ReadExt;
use crate::utils::vec_try_with_capacity;
use crate::{
color::{ColorType, ExtendedColorType},
error::{ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind},
ImageDecoder, ImageFormat,
};
use byteorder_lite::ReadBytesExt;
use std::io::{self, Read};
struct ColorMap {
start_offset: usize,
entry_size: usize,
bytes: Vec<u8>,
}
impl ColorMap {
pub(crate) fn get(&self, index: usize) -> Option<&[u8]> {
let entry = self.entry_size * index.checked_sub(self.start_offset)?;
self.bytes.get(entry..entry + self.entry_size)
}
}
pub struct TgaDecoder<R> {
r: R,
width: usize,
height: usize,
raw_bytes_per_pixel: usize,
image_type: ImageType,
color_type: ColorType,
original_color_type: Option<ExtendedColorType>,
header: Header,
color_map: Option<ColorMap>,
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
enum TgaOrientation {
TopLeft,
TopRight,
BottomRight,
BottomLeft,
}
impl TgaOrientation {
fn from_image_desc_byte(value: u8) -> Self {
if value & (1u8 << 4) == 0 {
if value & (1u8 << 5) == 0 {
TgaOrientation::BottomLeft
} else {
TgaOrientation::TopLeft
}
} else {
if value & (1u8 << 5) == 0 {
TgaOrientation::BottomRight
} else {
TgaOrientation::TopRight
}
}
}
}
impl<R: Read> TgaDecoder<R> {
pub fn new(mut r: R) -> ImageResult<TgaDecoder<R>> {
let header = Header::from_reader(&mut r)?;
let image_type = ImageType::new(header.image_type);
let width = header.image_width as usize;
let height = header.image_height as usize;
let raw_bytes_per_pixel = (header.pixel_depth as usize).div_ceil(8);
let num_alpha_bits = header.image_desc & ALPHA_BIT_MASK;
if width == 0 || height == 0 {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"Invalid empty image",
)));
}
if ![8, 16, 24, 32].contains(&header.pixel_depth) || ![0, 8].contains(&num_alpha_bits) {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Tga.into(),
UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)),
),
));
}
if image_type.is_color_mapped() {
if header.map_type != 1 {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"Color map type must be 1 for color mapped images",
)));
} else if ![8, 16].contains(&header.pixel_depth) {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"Color map must use 1 or 2 byte indexes",
)));
} else if header.pixel_depth > header.map_entry_size {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Tga.into(),
UnsupportedErrorKind::GenericFeature(
"Indices larger than pixel values".into(),
),
),
));
}
}
let mut tmp = [0u8; 256];
r.read_exact(&mut tmp[0..header.id_length as usize])?;
let mut color_map = None;
if header.map_type == 1 {
let entry_size = (header.map_entry_size as usize).div_ceil(8);
if ![2, 3, 4].contains(&entry_size) {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Tga.into(),
UnsupportedErrorKind::GenericFeature(
"Unsupported color map entry size".into(),
),
),
));
}
let mut bytes = Vec::new();
r.read_exact_vec(&mut bytes, entry_size * header.map_length as usize)?;
if image_type.is_color_mapped() {
color_map = Some(ColorMap {
entry_size,
start_offset: header.map_origin as usize,
bytes,
});
}
}
let total_pixel_bits = if header.map_type == 1 {
header.map_entry_size
} else {
header.pixel_depth
};
let num_other_bits = total_pixel_bits
.checked_sub(num_alpha_bits)
.ok_or_else(|| {
ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"More alpha bits than pixel bits",
))
})?;
let color_type;
let mut original_color_type = None;
match (num_alpha_bits, num_other_bits, image_type.is_color()) {
(0, 32, true) => color_type = ColorType::Rgba8,
(8, 24, true) => color_type = ColorType::Rgba8,
(0, 24, true) => color_type = ColorType::Rgb8,
(8, 8, false) => color_type = ColorType::La8,
(0, 8, false) => color_type = ColorType::L8,
(8, 0, false) => {
color_type = ColorType::L8;
original_color_type = Some(ExtendedColorType::A8);
}
_ => {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Tga.into(),
UnsupportedErrorKind::Color(ExtendedColorType::Unknown(header.pixel_depth)),
),
))
}
}
Ok(TgaDecoder {
r,
width,
height,
raw_bytes_per_pixel,
image_type,
color_type,
original_color_type,
header,
color_map,
})
}
fn read_encoded_data(&mut self, buf: &mut [u8]) -> io::Result<()> {
assert!(self.raw_bytes_per_pixel <= 4);
let mut repeat_buf = [0; 4];
let repeat_buf = &mut repeat_buf[..self.raw_bytes_per_pixel];
let mut index = 0;
while index < buf.len() {
let run_packet = self.r.read_u8()?;
if (run_packet & 0x80) != 0 {
let repeat_count = ((run_packet & !0x80) + 1) as usize;
self.r.read_exact(repeat_buf)?;
for chunk in buf[index..]
.chunks_exact_mut(self.raw_bytes_per_pixel)
.take(repeat_count)
{
chunk.copy_from_slice(repeat_buf);
}
index += repeat_count * self.raw_bytes_per_pixel;
} else {
let num_raw_bytes =
((run_packet + 1) as usize * self.raw_bytes_per_pixel).min(buf.len() - index);
self.r.read_exact(&mut buf[index..][..num_raw_bytes])?;
index += num_raw_bytes;
}
}
Ok(())
}
fn expand_color_map(
&self,
input: &[u8],
output: &mut [u8],
color_map: &ColorMap,
) -> ImageResult<()> {
if self.raw_bytes_per_pixel == 1 {
for (&index, chunk) in input
.iter()
.zip(output.chunks_exact_mut(color_map.entry_size))
{
if let Some(color) = color_map.get(index as usize) {
chunk.copy_from_slice(color);
} else {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"Invalid color map index",
)));
}
}
} else if self.raw_bytes_per_pixel == 2 {
for (index, chunk) in input
.chunks_exact(2)
.zip(output.chunks_exact_mut(color_map.entry_size))
{
let index = u16::from_le_bytes(index.try_into().unwrap());
if let Some(color) = color_map.get(index as usize) {
chunk.copy_from_slice(color);
} else {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Tga.into(),
"Invalid color map index",
)));
}
}
} else {
unreachable!("Supported bytes_per_pixel values are checked in TgaDecoder::new");
}
Ok(())
}
fn reverse_encoding_in_output(&mut self, pixels: &mut [u8]) {
match self.color_type {
ColorType::Rgb8 | ColorType::Rgba8 => {
for chunk in pixels.chunks_mut(self.color_type.bytes_per_pixel().into()) {
chunk.swap(0, 2);
}
}
_ => {}
}
}
fn fixup_orientation(&mut self, pixels: &mut [u8]) {
let orientation = TgaOrientation::from_image_desc_byte(self.header.image_desc);
if (orientation == TgaOrientation::BottomLeft || orientation == TgaOrientation::BottomRight)
&& self.height > 1
{
let row_stride = self.width * self.raw_bytes_per_pixel;
let (left_part, right_part) = pixels.split_at_mut(self.height / 2 * row_stride);
for (src, dst) in left_part
.chunks_exact_mut(row_stride)
.zip(right_part.chunks_exact_mut(row_stride).rev())
{
for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
std::mem::swap(src, dst);
}
}
}
if (orientation == TgaOrientation::BottomRight || orientation == TgaOrientation::TopRight)
&& self.width > 1
{
for row in pixels.chunks_exact_mut(self.width * self.raw_bytes_per_pixel) {
let (left_part, right_part) =
row.split_at_mut(self.width / 2 * self.raw_bytes_per_pixel);
for (src, dst) in left_part
.chunks_exact_mut(self.raw_bytes_per_pixel)
.zip(right_part.chunks_exact_mut(self.raw_bytes_per_pixel).rev())
{
for (src, dst) in src.iter_mut().zip(dst.iter_mut()) {
std::mem::swap(dst, src);
}
}
}
}
}
}
impl<R: Read> ImageDecoder for TgaDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}
fn color_type(&self) -> ColorType {
self.color_type
}
fn original_color_type(&self) -> ExtendedColorType {
self.original_color_type
.unwrap_or_else(|| self.color_type().into())
}
fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
let num_raw_bytes = self.width * self.height * self.raw_bytes_per_pixel;
if self.image_type.is_encoded() {
self.read_encoded_data(&mut buf[..num_raw_bytes])?;
} else {
self.r.read_exact(&mut buf[..num_raw_bytes])?;
}
self.fixup_orientation(&mut buf[..num_raw_bytes]);
if let Some(ref color_map) = self.color_map {
let mut rawbuf = vec_try_with_capacity(num_raw_bytes)?;
rawbuf.extend_from_slice(&buf[..num_raw_bytes]);
self.expand_color_map(&rawbuf, buf, color_map)?;
}
self.reverse_encoding_in_output(buf);
Ok(())
}
fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
(*self).read_image(buf)
}
}