tim2 0.3.1

An image loader for TIM2 (.tm2) image files
Documentation
use crate::common::*;
use crate::error::Error;
use crate::pixel::{Format, Pixel};

use byteorder::{ByteOrder, LittleEndian};

const SWIZZLE_WIDTH: usize = 16;
const SWIZZLE_HEIGHT: usize = 8;

pub type PixelBuffer = Vec::<Pixel>;

#[derive(Debug)]
pub enum DataKind {
	Indices(Vec::<u8>),
	Pixels(PixelBuffer),
}

impl DataKind {
	pub fn len(&self) -> usize {
		match self {
			DataKind::Indices(v) => v.len(),
			DataKind::Pixels(v) => v.len(),
		}
	}
}

#[derive(Debug)]
struct Header {
	total_size: u32,
    palette_size: u32,
    image_size: u32,
    header_size: u16,
    color_entry_count: u16,
    paletted: u8,
	mipmap_count: u8,
	clut_format: u8,
	bpp: u8,
	width: usize,
	height: usize,
	gs_regs: u32,
	gs_tex_clut: u32,
	gs_tex_0: [u8;8],
	gs_tex_1: [u8;8],
	user_data: Vec::<u8>,
}

impl Header {
	pub fn read(buffer: &[u8], offset: &mut usize) -> Result<Header, Error> {
		let mut load_part = |size| { get_slice(&buffer, offset, size) };
	
		let mut result = Header {
			total_size: LittleEndian::read_u32(load_part(4)),
			palette_size: LittleEndian::read_u32(load_part(4)),
			image_size: LittleEndian::read_u32(load_part(4)),
			header_size: LittleEndian::read_u16(load_part(2)),
			color_entry_count: LittleEndian::read_u16(load_part(2)),
			paletted: load_part(1)[0],
			mipmap_count: load_part(1)[0],
			clut_format: load_part(1)[0],
			bpp: Header::find_bpp(load_part(1)[0])?,
			width: LittleEndian::read_u16(load_part(2)) as usize,
			height: LittleEndian::read_u16(load_part(2)) as usize,
			gs_tex_0: clone_into_array(load_part(8)),
			gs_tex_1: clone_into_array(load_part(8)),
			gs_regs: LittleEndian::read_u32(load_part(4)),
			gs_tex_clut: LittleEndian::read_u32(load_part(4)),
			user_data: Vec::new(),
		};

		let user_data_size = result.header_size as usize - 48;
		if user_data_size > 0 {
			result.user_data = load_part(user_data_size).to_vec();
		}

		if result.palette_size > 0 && result.bpp > 8 {
			Err(Error::TrueColorAndPaletteFound)
		} else {
			Ok(result)
		}
	}

	fn find_bpp(v: u8) -> Result<u8, Error> {
		match v {
			1 => Ok(16),
			2 => Ok(24),
			3 => Ok(32),
			4 => Ok(4),
			5 => Ok(8),
			n => Err(Error::InvalidBppFormat(n)),
		}
	}

	pub fn is_linear_palette(&self) -> bool {
		self.clut_format & 0x80 != 0
	}

	pub fn color_size(&self) -> usize {
		if self.bpp > 8 {
			(self.bpp / 8) as usize
		} else {
			(self.clut_format & 0x07) as usize + 1
		}
	}

	pub fn pixel_format(&self) -> Result<Format, Error> {
		match self.bpp {
			4 => Ok(Format::Indexed),
			8 => Ok(Format::Indexed),
			16 => Ok(Format::Abgr1555),
			24 => Ok(Format::Rgb888),
			32 => Ok(Format::Rgba8888),
			n => Err(Error::InvalidBpp(n)),
		}
	}
}

#[derive(Debug)]
pub struct Frame {
	header: Header,
	data: DataKind,
	palettes: Vec::<PixelBuffer>,
}

impl Frame {
	pub fn read(buffer: &[u8], offset: &mut usize) -> Result<Frame, Error> {
		let header = Header::read(buffer, offset)?;
		let data = Frame::read_data(buffer, offset, &header)?;
		let palettes= Frame::read_palettes(buffer, offset, &header)?;

		Ok(Frame { header, data, palettes })
	}

	fn read_data(buffer: &[u8], offset: &mut usize, header: &Header) -> Result<DataKind, Error> {
		let pixel_size = header.bpp as usize / 8;
		let size = header.image_size as usize;
		let slice = get_slice(buffer, offset, size);
		let data = if header.bpp == 4 {
			let mut result = Vec::with_capacity(slice.len() * 2);

			for index_pair in slice {
				result.push(*index_pair & 0xF0 >> 4);
				result.push(*index_pair & 0xF);
			}

			result
		} else {
			slice.to_vec()
		};

		if header.palette_size > 0 {
			let raw = Frame::unswizzle(&data.to_vec(), header);

			Ok(DataKind::Indices(raw))
		} else {
			let colors = Frame::read_colors(&data, pixel_size)?;
			let raw = Frame::unswizzle(&colors, header);

			Ok(DataKind::Pixels(raw))
		}
	}

	fn read_palettes(buffer: &[u8], offset: &mut usize, header: &Header) -> Result<Vec<PixelBuffer>, Error> {
		let total_size = header.palette_size as usize;
		let slice = get_slice(buffer, offset, total_size);
		let size = header.color_entry_count as usize * header.color_size();
		let count = total_size / size;
		let color_size = header.color_size();
		let mut result = Vec::with_capacity(count);

		for i in 0..count {
			let start_index = size * i;
			let end_index = start_index + size;
			let data = &slice[start_index..end_index];
			let mut palette = Frame::read_colors(data, color_size)?;

			if !header.is_linear_palette() && header.bpp == 8 {
				Frame::linearize_palette(&mut palette);
			}

			result.push(palette);
		}

		Ok(result)
	}

	fn read_colors(buffer: &[u8], color_size: usize) -> Result<PixelBuffer, Error> {
		let mut offset = 0usize;
		let mut result = Vec::new();

		for _ in (0..buffer.len()).step_by(color_size) {
			let slice = get_slice(buffer, &mut offset, color_size);
			let pixel = Pixel::from_buf(slice)?;

			result.push(pixel)
		}

		Ok(result)
	}

	fn linearize_palette(palette: &mut PixelBuffer) {
		const COLOR_COUNT: usize = 8;
		const BLOCK_COUNT: usize = 2;
		const STRIPE_COUNT: usize = 2;

		let mut i = 0usize;
		let part_count = palette.len() / 32;
		let original = palette.clone();

		for part in 0..part_count {
			for block in 0..BLOCK_COUNT {
				for stripe in 0..STRIPE_COUNT {
					for color in 0..COLOR_COUNT {
						let i1 = part * COLOR_COUNT * STRIPE_COUNT * BLOCK_COUNT;
						let i2 = block * COLOR_COUNT;
						let i3 = stripe * STRIPE_COUNT * COLOR_COUNT;

						palette[i] = original[i1 + i2 + i3 + color];
						i += 1;
					}
				}
			}
		}
	}

	fn unswizzle<T: Default + Copy>(buffer: &Vec::<T>, header: &Header) -> Vec::<T> {
		let mut i = 0usize;
		let mut result = vec![Default::default(); buffer.len()];
	
		for y in (0..header.height).step_by(SWIZZLE_HEIGHT) {
			for x in (0..header.width).step_by(SWIZZLE_WIDTH) {
				for tile_y in y..(y + SWIZZLE_HEIGHT) {
					for tile_x in x..(x + SWIZZLE_WIDTH) {
						if tile_x < header.width && tile_y < header.height {
							let index = tile_y * header.width + tile_x;

							result[index] = buffer[i];
						}
	
						i += 1;
					}
				}
			}
		}
	
		result
	}

	pub fn has_mipmaps(&self) -> bool {
		self.header.mipmap_count > 1
	}

	pub fn width(&self) -> usize {
		self.header.width as usize
	}

	pub fn height(&self) -> usize {
		self.header.height as usize
	}

	pub fn format(&self) -> Result<Format, Error> {
		self.header.pixel_format()
	}

	pub fn data(&self) -> &DataKind {
		&self.data
	}

	pub fn get_pixels(&self) -> PixelBuffer {
		let palette = &self.palettes[0];

		match &self.data {
			DataKind::Indices(v) => {
				let mut result = Vec::with_capacity(v.len());

				for index in v {
					result.push(palette[*index as usize]);
				}

				result
			},
			DataKind::Pixels(v) => v.to_vec(),
		}
	}

	pub fn to_raw(&self, color_key: Option<Pixel>) -> Vec::<u8> {
		let pixels = self.get_pixels();
		let mut result = Vec::with_capacity(pixels.len() * 4);

		for pixel in pixels {
			let alpha = if let Some(v) = color_key {
				if v != pixel { pixel.a() } else { 0 }
			} else {
				pixel.a()
			};

			result.push(pixel.r());
			result.push(pixel.g());
			result.push(pixel.b());
			result.push(alpha);
		}

		result
	}
}