use crate::color::Color16;
#[cfg(feature = "image")]
use crate::color::{BLACK_16, Color};
#[cfg(feature = "image")]
use crate::error::ToImageError;
use crate::error::{MulReaderError, MulReaderResult};
use crate::mul::MulReader;
use byteorder::{LittleEndian, ReadBytesExt};
#[cfg(feature = "image")]
use image::error::{DecodingError, ImageFormatHint};
#[cfg(feature = "image")]
use image::{ImageError, Rgba, RgbaImage};
use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::path::Path;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct GumpPair {
pub color: Color16,
pub count: u16,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Gump {
pub width: u16,
pub height: u16,
pub data: Vec<Vec<GumpPair>>,
}
#[cfg(feature = "image")]
impl Gump {
pub fn to_image(&self) -> Result<RgbaImage, ImageError> {
let mut buffer = RgbaImage::new(self.width as u32, self.height as u32);
for (y, row) in self.data.iter().enumerate() {
let mut x = 0;
for run_pair in row {
if x + run_pair.count > self.width {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormatHint::Name("UO Gump".to_string()),
ToImageError::PixelOutOfBounds {
x: (x + run_pair.count) as i64,
y: y as i64,
},
)));
}
if run_pair.color != BLACK_16 {
let (r, g, b, a) = run_pair.color.to_rgba();
for i in 0..run_pair.count {
buffer.put_pixel(x as u32 + i as u32, y as u32, Rgba([r, g, b, a]));
}
}
x += run_pair.count;
}
}
Ok(buffer)
}
}
#[derive(Debug)]
pub struct GumpReader<T: Read + Seek> {
mul_reader: MulReader<T>,
}
impl GumpReader<File> {
pub fn new(index_path: &Path, mul_path: &Path) -> MulReaderResult<GumpReader<File>> {
let mul_reader = MulReader::new(index_path, mul_path)?;
Ok(GumpReader { mul_reader })
}
}
impl<T: Read + Seek> GumpReader<T> {
pub fn from_mul(reader: MulReader<T>) -> GumpReader<T> {
GumpReader { mul_reader: reader }
}
pub fn read(&mut self, index: u32) -> MulReaderResult<Gump> {
let raw = self.mul_reader.read(index)?;
let mut output = vec![];
let len = raw.data.len();
if len % 4 != 0 {
return Err(MulReaderError::UnexpectedSize {
found: len as u32,
expected: (len + (len % 4)) as u32,
});
}
let mut reader = Cursor::new(raw.data);
let mut row_offsets = vec![];
for _i in 0..raw.opt1 {
row_offsets.push(reader.read_u32::<LittleEndian>()?);
}
for (row_idx, offset) in row_offsets.iter().enumerate() {
let row_length = if row_idx == row_offsets.len() - 1 {
(len / 4) as u32 - offset
} else {
let next_row = row_offsets[row_idx + 1];
next_row - offset
};
reader.seek(SeekFrom::Start((*offset as u64) * 4))?;
let mut row = vec![];
for _i in 0..row_length {
let color = reader.read_u16::<LittleEndian>()?;
let count = reader.read_u16::<LittleEndian>()?;
row.push(GumpPair { color, count });
}
output.push(row);
}
Ok(Gump {
height: raw.opt1,
width: raw.opt2,
data: output,
})
}
}