use crate::core::image::ImageBlp;
use crate::error::error::BlpError;
use image::imageops::{FilterType, crop_imm, resize};
use image::{self};
use psd::Psd;
fn is_psd_file(buf: &[u8]) -> bool {
buf.len() >= 4 && &buf[0..4] == b"8BPS"
}
fn is_blp_file(buf: &[u8]) -> bool {
buf.len() >= 4 && (&buf[0..4] == b"BLP1" || &buf[0..4] == b"BLP2")
}
fn load_psd_as_image(buf: &[u8]) -> Result<image::DynamicImage, BlpError> {
let psd = Psd::from_bytes(buf).map_err(|e| BlpError::new("error-psd-parse").with_arg("error", e.to_string()))?;
let rgba_data = psd.rgba();
let width = psd.width();
let height = psd.height();
#[cfg(debug_assertions)]
{
eprintln!("PSD Info: {}x{}, {} bytes of RGBA data", width, height, rgba_data.len());
}
let img_buf = image::ImageBuffer::from_raw(width, height, rgba_data).ok_or_else(|| {
BlpError::new("error-psd-invalid-dimensions")
.with_arg("width", width)
.with_arg("height", height)
})?;
Ok(image::DynamicImage::ImageRgba8(img_buf))
}
fn decode_image_common(buf: &[u8]) -> Result<image::DynamicImage, BlpError> {
if is_psd_file(buf) {
load_psd_as_image(buf)
} else {
image::load_from_memory(buf).map_err(|_| BlpError::new("error-image-load"))
}
}
impl ImageBlp {
pub fn decode_image(&mut self, buf: &[u8], mip_visible: &[bool]) -> Result<(), BlpError> {
let src = decode_image_common(buf)?;
let src = src.to_rgba8();
let (tw, th) = (self.width.max(1), self.height.max(1));
let (sw, sh) = src.dimensions();
if sw == 0 || sh == 0 {
return Err(BlpError::new("error-image-empty")
.with_arg("width", sw)
.with_arg("height", sh));
}
let sx = tw as f32 / sw as f32;
let sy = th as f32 / sh as f32;
let s = sx.max(sy);
let rw = (sw as f32 * s).ceil() as u32;
let rh = (sh as f32 * s).ceil() as u32;
let resized = resize(&src, rw, rh, FilterType::Lanczos3);
let cx = ((rw.saturating_sub(tw)) / 2).min(rw.saturating_sub(tw));
let cy = ((rh.saturating_sub(th)) / 2).min(rh.saturating_sub(th));
let base = crop_imm(&resized, cx, cy, tw, th).to_image();
let mut prev = base;
let (mut w, mut h) = (tw, th);
for i in 0..self.mipmaps.len() {
self.mipmaps[i].width = w;
self.mipmaps[i].height = h;
let visible = mip_visible
.get(i)
.copied()
.unwrap_or(true);
if visible {
self.mipmaps[i].image = Some(prev.clone());
} else {
self.mipmaps[i].image = None;
}
if w == 1 && h == 1 {
for j in (i + 1)..self.mipmaps.len() {
self.mipmaps[j].width = 1;
self.mipmaps[j].height = 1;
self.mipmaps[j].image = None;
}
break;
}
let next_w = (w / 2).max(1);
let next_h = (h / 2).max(1);
let next_img = resize(&prev, next_w, next_h, FilterType::Lanczos3);
prev = next_img;
w = next_w;
h = next_h;
}
Ok(())
}
}
pub fn decode_to_rgba(buf: &[u8]) -> Result<image::DynamicImage, BlpError> {
if is_blp_file(buf) {
let mut blp = ImageBlp::from_buf_blp(buf)?;
blp.decode(buf, &[true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false])?;
if let Some(img) = blp.mipmaps[0].image.take() {
Ok(image::DynamicImage::ImageRgba8(img))
} else {
Err(BlpError::new("error-blp-no-mipmap"))
}
} else {
decode_image_common(buf)
}
}