#![no_std]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(missing_copy_implementations)]
#![deny(trivial_casts)]
#![deny(trivial_numeric_casts)]
#![deny(unsafe_code)]
#![deny(unstable_features)]
#![deny(unused_import_braces)]
#![deny(unused_qualifications)]
mod color_map;
mod footer;
mod header;
mod parse_error;
mod pixels;
mod raw_iter;
mod raw_tga;
use core::marker::PhantomData;
use embedded_graphics::{
pixelcolor::{
raw::{RawU16, RawU24, RawU8},
Gray8, Rgb555, Rgb888,
},
prelude::*,
primitives::Rectangle,
};
use raw_iter::{RawColors, Rle, Uncompressed};
pub use crate::{
color_map::ColorMap,
header::{Bpp, Compression, DataType, ImageOrigin, TgaHeader},
parse_error::ParseError,
pixels::Pixels,
raw_iter::{RawPixel, RawPixels},
raw_tga::RawTga,
};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Tga<'a, C> {
raw: RawTga<'a>,
image_color_type: ColorType,
target_color_type: PhantomData<C>,
}
impl<'a, C> Tga<'a, C>
where
C: PixelColor + From<Gray8> + From<Rgb555> + From<Rgb888>,
{
pub fn from_slice(data: &'a [u8]) -> Result<Self, ParseError> {
let raw = RawTga::from_slice(data)?;
let image_color_type = match (raw.color_bpp(), raw.data_type()) {
(Bpp::Bits8, DataType::BlackAndWhite) => ColorType::Gray8,
(Bpp::Bits16, DataType::ColorMapped) => ColorType::Rgb555,
(Bpp::Bits16, DataType::TrueColor) => ColorType::Rgb555,
(Bpp::Bits24, DataType::ColorMapped) => ColorType::Rgb888,
(Bpp::Bits24, DataType::TrueColor) => ColorType::Rgb888,
_ => {
return Err(ParseError::UnsupportedTgaType(
raw.data_type(),
raw.color_bpp(),
));
}
};
Ok(Tga {
raw,
image_color_type,
target_color_type: PhantomData,
})
}
pub fn as_raw(&self) -> &RawTga<'a> {
&self.raw
}
pub fn pixels(&self) -> Pixels<'_, C> {
Pixels::new(self)
}
fn draw_colors<D>(
&self,
target: &mut D,
mut colors: impl Iterator<Item = C>,
) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
{
let bounding_box = self.bounding_box();
if bounding_box.is_zero_sized() {
return Ok(());
}
let origin = self.raw.image_origin();
match origin {
ImageOrigin::TopLeft => target.fill_contiguous(&bounding_box, colors),
ImageOrigin::BottomLeft => {
let mut row_rect =
Rectangle::new(Point::zero(), Size::new(bounding_box.size.width, 1));
for y in bounding_box.rows().rev() {
row_rect.top_left.y = y;
let row_colors = (&mut colors).take(bounding_box.size.width as usize);
target.fill_contiguous(&row_rect, row_colors)?;
}
Ok(())
}
ImageOrigin::TopRight => {
let max_x = bounding_box.bottom_right().map(|p| p.x).unwrap_or_default();
bounding_box
.points()
.zip(colors)
.map(|(p, c)| Pixel(Point::new(max_x - p.x, p.y), c))
.draw(target)
}
ImageOrigin::BottomRight => {
let bottom_right = bounding_box.bottom_right().unwrap_or_default();
bounding_box
.points()
.zip(colors)
.map(|(p, c)| Pixel(bottom_right - p, c))
.draw(target)
}
}
}
fn draw_regular<D, CI, F>(
&self,
target: &mut D,
colors: RawColors<'a, CI::Raw, F>,
) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
CI: PixelColor + From<CI::Raw> + Into<C>,
RawColors<'a, CI::Raw, F>: Iterator<Item = CI::Raw>,
{
self.draw_colors(target, colors.map(|c| CI::from(c).into()))
}
fn draw_color_mapped<D, R, F>(
&self,
target: &mut D,
indices: RawColors<'a, R, F>,
) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
R: RawData,
R::Storage: Into<u32>,
RawColors<'a, R, F>: Iterator<Item = R>,
{
let color_map = if let Some(color_map) = self.raw.color_map() {
color_map
} else {
return Ok(());
};
match self.image_color_type {
ColorType::Rgb555 => {
let colors = indices.map(|index| {
let index = index.into_inner().into() as usize;
color_map.get::<Rgb555>(index).unwrap().into()
});
self.draw_colors(target, colors)
}
ColorType::Rgb888 => {
let colors = indices.map(|index| {
let index = index.into_inner().into() as usize;
color_map.get::<Rgb888>(index).unwrap().into()
});
self.draw_colors(target, colors)
}
ColorType::Gray8 => Ok(()),
}
}
}
impl<C> OriginDimensions for Tga<'_, C> {
fn size(&self) -> Size {
self.raw.size()
}
}
impl<C> ImageDrawable for Tga<'_, C>
where
C: PixelColor + From<Gray8> + From<Rgb555> + From<Rgb888>,
{
type Color = C;
fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
{
match self.raw.image_data_bpp() {
Bpp::Bits8 => match self.raw.compression() {
Compression::Uncompressed => {
let colors = RawColors::<RawU8, Uncompressed>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Gray8, _>(target, colors)
}
}
Compression::Rle => {
let colors = RawColors::<RawU8, Rle>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Gray8, _>(target, colors)
}
}
},
Bpp::Bits16 => match self.raw.compression() {
Compression::Uncompressed => {
let colors = RawColors::<RawU16, Uncompressed>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Rgb555, _>(target, colors)
}
}
Compression::Rle => {
let colors = RawColors::<RawU16, Rle>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Rgb555, _>(target, colors)
}
}
},
Bpp::Bits24 => match self.raw.compression() {
Compression::Uncompressed => {
let colors = RawColors::<RawU24, Uncompressed>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Rgb888, _>(target, colors)
}
}
Compression::Rle => {
let colors = RawColors::<RawU24, Rle>::new(&self.raw);
if self.raw.color_map().is_some() {
self.draw_color_mapped(target, colors)
} else {
self.draw_regular::<_, Rgb888, _>(target, colors)
}
}
},
Bpp::Bits32 => Ok(()),
}
}
fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
self.draw(&mut target.translated(-area.top_left).clipped(area))
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub(crate) enum ColorType {
Gray8,
Rgb555,
Rgb888,
}