use super::super::drawable::{Drawable, Pixel};
use super::super::transform::Transform;
use super::ImageFile;
use crate::geometry::{Dimensions, Point, Size};
use crate::pixelcolor::raw::RawData;
use crate::pixelcolor::PixelColor;
use core::marker::PhantomData;
use tinytga::{Tga, TgaIterator};
#[derive(Debug, Clone)]
pub struct ImageTga<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
tga: Tga<'a>,
pub offset: Point,
pixel_type: PhantomData<C>,
}
impl<'a, C> ImageFile<'a> for ImageTga<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
fn new(image_data: &'a [u8]) -> Result<Self, ()> {
let im = Self {
tga: Tga::from_slice(image_data).map_err(|_| ())?,
offset: Point::zero(),
pixel_type: PhantomData,
};
Ok(im)
}
fn width(&self) -> u32 {
self.tga.width() as u32
}
fn height(&self) -> u32 {
self.tga.height() as u32
}
}
impl<'a, C> Dimensions for ImageTga<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
fn top_left(&self) -> Point {
self.offset
}
fn bottom_right(&self) -> Point {
self.top_left() + self.size()
}
fn size(&self) -> Size {
Size::new(self.tga.width() as u32, self.tga.height() as u32)
}
}
impl<'a, C> IntoIterator for &'a ImageTga<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
type Item = Pixel<C>;
type IntoIter = ImageTgaIterator<'a, C>;
fn into_iter(self) -> Self::IntoIter {
ImageTgaIterator {
im: self,
image_data: self.tga.into_iter(),
x: 0,
y: 0,
}
}
}
#[derive(Debug)]
pub struct ImageTgaIterator<'a, C: 'a>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
x: u32,
y: u32,
im: &'a ImageTga<'a, C>,
image_data: TgaIterator<'a>,
}
impl<'a, C> Iterator for ImageTgaIterator<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
type Item = Pixel<C>;
fn next(&mut self) -> Option<Self::Item> {
self.image_data.next().map(|color| {
let pos = self.im.offset + Point::new(self.x as i32, self.y as i32);
let raw = C::Raw::from_u32(color);
let out = Pixel(pos, raw.into());
self.x += 1;
if self.x >= self.im.width() {
self.y += 1;
self.x = 0;
}
out
})
}
}
impl<'a, C> Drawable for ImageTga<'a, C> where C: PixelColor + From<<C as PixelColor>::Raw> {}
impl<'a, C> Transform for ImageTga<'a, C>
where
C: PixelColor + From<<C as PixelColor>::Raw>,
{
fn translate(&self, by: Point) -> Self {
Self {
offset: self.offset + by,
..self.clone()
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.offset += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pixelcolor::{Rgb888, RgbColor};
const PIXEL_COLORS: [(i32, i32, Rgb888); 16] = [
(0, 0, Rgb888::WHITE),
(1, 0, Rgb888::BLACK),
(2, 0, Rgb888::WHITE),
(3, 0, Rgb888::BLACK),
(0, 1, Rgb888::BLACK),
(1, 1, Rgb888::RED),
(2, 1, Rgb888::BLACK),
(3, 1, Rgb888::GREEN),
(0, 2, Rgb888::WHITE),
(1, 2, Rgb888::BLACK),
(2, 2, Rgb888::BLUE),
(3, 2, Rgb888::BLACK),
(0, 3, Rgb888::BLACK),
(1, 3, Rgb888::WHITE),
(2, 3, Rgb888::BLACK),
(3, 3, Rgb888::WHITE),
];
#[test]
fn chessboard_compressed() -> Result<(), ()> {
let im: ImageTga<Rgb888> = ImageTga::new(include_bytes!("../../tests/chessboard_rle.tga"))?;
let mut pixels = im.into_iter();
for (i, (x, y, color)) in PIXEL_COLORS.iter().enumerate() {
assert_eq!(
pixels.next(),
Some(Pixel(Point::new(*x, *y), *color)),
"Pixel color at index {} does not match",
i
);
}
assert_eq!(pixels.next(), None);
Ok(())
}
#[test]
fn chessboard_uncompressed() -> Result<(), ()> {
let im: ImageTga<Rgb888> = ImageTga::new(include_bytes!("../../tests/chessboard_raw.tga"))?;
let mut pixels = im.into_iter();
for (i, (x, y, color)) in PIXEL_COLORS.iter().enumerate() {
assert_eq!(
pixels.next(),
Some(Pixel(Point::new(*x, *y), *color)),
"Pixel color at index {} does not match",
i
);
}
assert_eq!(pixels.next(), None);
Ok(())
}
}