You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

187 lines
3.5 KiB

use crate::{
data::{color::Color, read, vfs},
iter::MaybeRev,
};
use std::io::{self, Read};
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ErrImageRead {
#[error("Bad color encoding in texture")]
Encoding,
#[error("No first mip level in texture")]
InsufficientLevels,
#[error(transparent)]
Io(#[from] io::Error),
}
pub struct Image {
data: Vec<Color>,
width: usize,
heigh: usize,
}
pub struct MipImage {
datum: Vec<Image>,
width: usize,
heigh: usize,
}
impl Image {
pub fn get(&self, x: usize, y: usize) -> Option<&Color> {
if x < self.width && y < self.heigh {
self.data.get(y * self.width + x)
} else {
None
}
}
pub fn data(&self) -> &[Color] {
&self.data
}
pub const fn w(&self) -> usize {
self.width
}
pub const fn h(&self) -> usize {
self.heigh
}
pub fn xor_texture(width: usize, heigh: usize) -> Self {
let mut data = Vec::with_capacity(width * heigh);
for x in 0..width {
for y in 0..heigh {
let c = x as u8 ^ y as u8;
data.push(Color { r: c, g: c, b: c, a: u8::MAX });
}
}
Self { data, width, heigh }
}
pub fn read<R>(rd: &mut R) -> Result<Self, ErrImageRead>
where
R: Read,
{
let mut head = [0; 18];
rd.read_exact(&mut head)?;
// make sure the encoding is OK
let alpha = match head[16] {
| 24 => false,
| 32 => true,
| _ => return Err(ErrImageRead::Encoding),
};
let descr = head[17];
if head[1] != 0
|| head[2] != 2
|| (alpha && descr & 0xF != 8)
|| (!alpha && descr & 0xF != 0)
|| descr & 0xC0 != 0
{
return Err(ErrImageRead::Encoding);
}
// compile size info
let comps = if alpha { 4 } else { 3 };
let width = read::u16le(&head, 12).into();
let heigh = read::u16le(&head, 14).into();
let wsize = width * heigh;
let wscan = width * comps;
// skip ID field
let id_len = usize::from(head[0]);
let mut junk = [0; 255];
rd.read_exact(&mut junk[..id_len])?;
// read in the data
let cdat = read::hunk(rd, wsize * comps)?;
let mut data = Vec::with_capacity(wsize);
// iterate each scanline and then color
let from_lft = descr & 0b010000 == 0;
let from_bot = descr & 0b100000 == 0;
for line in MaybeRev::rev_if(cdat.chunks(wscan), from_bot) {
for color in MaybeRev::rev_if(line.chunks(comps), !from_lft) {
data.push(if alpha {
Color { r: color[2], g: color[1], b: color[0], a: color[3] }
} else {
Color { r: color[2], g: color[1], b: color[0], a: u8::MAX }
});
}
}
Ok(Self { data, width, heigh })
}
}
impl MipImage {
pub fn get(&self, level: usize) -> Option<&Image> {
self.datum.get(level)
}
pub fn data(&self) -> &[Image] {
&self.datum
}
pub const fn w(&self) -> usize {
self.width
}
pub const fn h(&self) -> usize {
self.heigh
}
pub fn levels(&self) -> usize {
self.datum.len()
}
pub fn is_empty(&self) -> bool {
self.datum.is_empty()
}
pub fn read(vfs: &vfs::Vfs) -> Result<Self, ErrImageRead> {
let mut datum = Vec::new();
let mut width = 0;
let mut heigh = 0;
let mut i = 0;
while let Some(file) = vfs.get(&i.to_string()) {
let img = Image::read(&mut file.data())?;
if i == 0 {
width = img.w();
heigh = img.h();
}
datum.push(img);
i += 1;
}
if !datum.is_empty() {
Ok(Self { datum, width, heigh })
} else {
Err(ErrImageRead::InsufficientLevels)
}
}
}
impl std::ops::Index<(usize, usize)> for Image {
type Output = Color;
fn index(&self, index: (usize, usize)) -> &Self::Output {
self.get(index.0, index.1).unwrap()
}
}
// EOF