use crate::color_table::ColorTable;
use crate::image_data::ImageData;
use crate::image_descriptor::ImageDescriptor;
#[derive(Debug)]
pub struct TableBasedImage {
image_descriptor: ImageDescriptor,
local_color_table: ColorTable,
pub decompressed: Vec<u8>,
}
impl TableBasedImage {
pub fn parse_from_reader(rdr: &mut std::io::Cursor<&[u8]>) -> std::io::Result<Self> {
let image_descriptor: ImageDescriptor = ImageDescriptor::parse_from_reader(rdr)?;
let color_count = if image_descriptor.local_color_table_flag() == 1 {
image_descriptor.color_count()
} else {
0
};
let local_color_table = ColorTable::parse_from_reader(rdr, color_count)?;
let image_data: ImageData = ImageData::parse_from_reader(rdr)?;
let decompressed = image_data.decompress();
let image = Self {
image_descriptor,
local_color_table,
decompressed,
};
Ok(image)
}
pub fn convert_to_frame(
&self,
screen_width: u16,
screen_height: u16,
global_color_table: &ColorTable,
transparent_index: Option<u8>,
) -> Vec<u8> {
let descriptor = &self.image_descriptor;
let colors = if self.local_color_table.colors.len() > 0 {
&self.local_color_table.colors
} else {
&global_color_table.colors
};
let d = &self.decompressed;
let byte_count = 4 * screen_width as usize * screen_height as usize;
let mut frame_bytes: Vec<u8> = Vec::with_capacity(byte_count);
for _ in 0..byte_count {
frame_bytes.push(0);
}
let slice = frame_bytes.as_mut_slice();
for (n, &i) in d.iter().enumerate() {
let mut index = i as usize;
index = if let Some(trans_index) = transparent_index {
if index == (trans_index as usize) {
colors.len()
} else {
index
}
} else {
index
};
if index >= colors.len() {
} else {
let offset = 4 * cal_screen_offset(n, screen_width, screen_height, descriptor);
if offset >= slice.len() {
} else {
let color = &colors[index];
slice[offset] = color.red;
slice[offset + 1] = color.green;
slice[offset + 2] = color.blue;
slice[offset + 3] = 255;
}
}
}
frame_bytes
}
}
fn cal_screen_offset(n: usize, screen_width: u16, screen_height: u16, descriptor: &ImageDescriptor) -> usize {
let left = descriptor.left_position;
let top = descriptor.top_position;
let w = descriptor.width;
let h = descriptor.height;
let mut c = offset_to_coord(n, w as usize, h as usize);
c.0 += left as usize;
c.1 += top as usize;
coord_to_offset(c, screen_width as usize, screen_height as usize)
}
fn offset_to_coord(offset: usize, w: usize, _h: usize) -> (usize, usize) {
let x = offset % w;
let y = offset / w;
(x, y)
}
fn coord_to_offset(cord: (usize, usize), w: usize, _h: usize) -> usize {
cord.1 * w + cord.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offset_to_coord() {
let (width, height) = (300, 200);
assert_eq!((0, 0), offset_to_coord(0, width, height));
assert_eq!((10, 0), offset_to_coord(10, width, height));
assert_eq!((299, 0), offset_to_coord(299, width, height));
assert_eq!((0, 1), offset_to_coord(300, width, height));
assert_eq!((2, 1), offset_to_coord(302, width, height));
}
#[test]
fn test_coord_to_offset() {
let (width, height) = (500, 10);
assert_eq!(0, coord_to_offset((0, 0), width, height));
assert_eq!(1, coord_to_offset((1, 0), width, height));
assert_eq!(499, coord_to_offset((499, 0), width, height));
assert_eq!(500, coord_to_offset((0, 1), width, height));
assert_eq!(503, coord_to_offset((3, 1), width, height));
}
}