extern crate alloc;
use crate::c;
use crate::color;
use crate::color::Color8;
use crate::color::Color16;
use core::mem::MaybeUninit;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BmpHeader {
Info = 0x28,
InfoV3 = 0x38,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
CantOpen,
CantRead,
CantWrite,
PartialWrite,
InvalidFileType,
InvalidFileName,
InvalidFileData,
Unsupported,
OutOfMemory,
Unknown,
OutOfBounds,
InvalidImage,
InvalidValue,
NullStr,
}
#[repr(C)]
pub struct Image {
data: *mut u8,
width: u32,
height: u32,
channels: u8,
bpc: u8,
bpp: u8,
profile: color::Profile,
flags: u8,
}
impl Image {
pub fn new(width: u32, height: u32, profile: color::Profile, bits_per_color: u8) -> Result<Self, Error> {
let mut image = MaybeUninit::uninit();
match unsafe{c::lexlibImageNew(image.as_mut_ptr(), width, height, profile as u8, bits_per_color)} {
c::LEXLIB_OK => Ok(unsafe{image.assume_init()}),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
c::LEXLIB_INVALID_VALUE |
c::LEXLIB_INVALID_ARG => Err(Error::InvalidValue),
_ => panic!("lexlib::image::Image::new unknown error")
}
}
pub fn validate(&self) -> Result<(), Error> {
if unsafe{c::lexlibImageValidate(self)} == c::LEXLIB_OK {
return Ok(());
}
Err(Error::InvalidImage)
}
pub fn flip_x(&mut self) -> Result<(), Error> {
match unsafe{c::lexlibImageFlip(self, c::LEXLIB_FLIP_X)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidValue),
_ => panic!("lexlib::image::Image::flip_x unknown error")
}
}
pub fn flip_y(&mut self) -> Result<(), Error> {
match unsafe{c::lexlibImageFlip(self, c::LEXLIB_FLIP_Y)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidValue),
_ => panic!("lexlib::image::Image::flip_x unknown error")
}
}
pub fn change_profile(&mut self, profile: color::Profile) -> Result<(), Error> {
match unsafe{c::lexlibImageProfileChange(self, profile as u8)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidImage),
c::LEXLIB_INVALID_ARG => Err(Error::InvalidValue),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
_ => panic!("lexlib::image::Image::change_profile unknown error")
}
}
pub fn fill_area(&mut self, x: u32, y: u32, w: u32, h: u32, color: color::Color8, blend_mode: color::BlendMode) -> Result<(), Error> {
match unsafe{c::lexlibImageFillArea(self, x, y, w, h, color, blend_mode as u8)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_OPERATION => Err(Error::OutOfBounds),
_ => panic!("lexlib::image::Image::fill_area unknown error")
}
}
pub fn pixel(&self, x: u32, y: u32 ) -> Option<Color8> {
let mut color = MaybeUninit::uninit();
if unsafe{c::lexlibImagePixel(self, x, y, color.as_mut_ptr())} != c::LEXLIB_OK {
return None;
}
Some(unsafe{color.assume_init()})
}
pub fn pixel16(&self, x: u32, y: u32 ) -> Option<Color16> {
let mut color = MaybeUninit::uninit();
if unsafe{c::lexlibImagePixel16(self, x, y, color.as_mut_ptr())} != c::LEXLIB_OK {
return None;
}
Some(unsafe{color.assume_init()})
}
pub fn set_pixel(&mut self, x: u32, y: u32, color: Color8, blend_mode: color::BlendMode) -> Result<(), Error> {
match unsafe{c::lexlibImagePixelSet(self, x, y, color, blend_mode as u8)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_OPERATION => Err(Error::OutOfBounds),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidImage),
_ => panic!("lexlib::image::Image::set_pixel unknown error")
}
}
pub fn set_pixel16(&mut self, x: u32, y: u32, color: Color16, blend_mode: color::BlendMode) -> Result<(), Error> {
match unsafe{c::lexlibImagePixel16Set(self, x, y, color, blend_mode as u8)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_OPERATION => Err(Error::OutOfBounds),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidImage),
_ => panic!("lexlib::image::Image::set_pixel16 unknown error")
}
}
pub fn load(filename: &str) -> Result<Image, Error> {
let cstr = match alloc::ffi::CString::new(filename) {
Ok(v) => { v }
Err(_) => return Err(Error::NullStr)
};
let mut image = MaybeUninit::<Image>::uninit();
return match unsafe{c::lexlibImageLoad(image.as_mut_ptr(), cstr.as_ptr())} {
c::LEXLIB_OK => Ok(unsafe{image.assume_init()}),
c::LEXLIB_CANT_OPEN => Err(Error::CantOpen),
c::LEXLIB_CANT_READ => Err(Error::CantRead),
c::LEXLIB_INVALID_TYPE => Err(Error::InvalidFileType),
c::LEXLIB_INVALID_DATA => Err(Error::InvalidFileData),
c::LEXLIB_UNSUPORTED => Err(Error::Unsupported),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
c::LEXLIB_ERROR => Err(Error::Unknown),
_ => panic!("lexlib::image::Image::load unknown error")
}
}
pub fn save(&self, filename: &str) -> Result<(), Error> {
let cstr = match alloc::ffi::CString::new(filename) {
Ok(v) => {v}
Err(_) => return Err(Error::NullStr)
};
match unsafe{c::lexlibImageSave(self, cstr.as_ptr())}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_NAME => Err(Error::InvalidFileName),
c::LEXLIB_CANT_WRITE => Err(Error::CantWrite),
c::LEXLIB_PARTIAL_WRITE => Err(Error::PartialWrite),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
c::LEXLIB_UNSUPORTED => Err(Error::Unsupported),
_ => panic!("lexlib::image::Image::save unknown error")
}
}
pub fn save_bmp(&self, filename: &str) -> Result<(), Error> {
let cstr = match alloc::ffi::CString::new(filename) {
Ok(v) => {v}
Err(_) => return Err(Error::NullStr)
};
match unsafe{c::lexlibImageSaveBmp(self, cstr.as_ptr())}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_VALUE => Err(Error::InvalidImage),
c::LEXLIB_CANT_WRITE => Err(Error::CantWrite),
c::LEXLIB_PARTIAL_WRITE => Err(Error::PartialWrite),
_ => panic!("lexlib::image::Image::save_bmp unknown error")
}
}
pub fn save_bmp_ex(&self, filename: &str, profile: color::Profile, header: BmpHeader) -> Result<(), Error> {
let cstr = match alloc::ffi::CString::new(filename) {
Ok(v) => {v}
Err(_) => return Err(Error::NullStr)
};
match unsafe{c::lexlibImageSaveBmpEx(self, cstr.as_ptr(), profile as u8, header as u8)}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_INVALID_ARG => Err(Error::InvalidValue),
c::LEXLIB_UNSUPORTED => Err(Error::Unsupported),
c::LEXLIB_CANT_WRITE => Err(Error::CantWrite),
c::LEXLIB_PARTIAL_WRITE => Err(Error::PartialWrite),
_ => panic!("lexlib::image::Image::save_bmp_ex unknown error")
}
}
pub fn save_png(&self, filename: &str) -> Result<(), Error> {
let cstr = match alloc::ffi::CString::new(filename) {
Ok(v) => {v}
Err(_) => return Err(Error::NullStr)
};
match unsafe{c::lexlibImageSavePng(self, cstr.as_ptr())}{
c::LEXLIB_OK => Ok(()),
c::LEXLIB_CANT_WRITE => Err(Error::CantWrite),
c::LEXLIB_UNSUPORTED => Err(Error::Unsupported),
c::LEXLIB_OUT_OF_MEMORY => Err(Error::OutOfMemory),
_ => panic!("lexlib::image::Image::save_png, returned unknown error")
}
}
}
impl Image {
#[inline(always)]
pub fn raw_data(&self) -> *const u8 {
self.data
}
#[inline(always)]
pub unsafe fn raw_data_mut(&mut self) -> *mut u8 {
self.data
}
#[inline(always)]
pub fn width(&self) -> u32 {
self.width
}
#[inline(always)]
pub fn height(&self) -> u32 {
self.height
}
#[inline(always)]
pub fn channels(&self) -> u8 {
self.channels
}
#[inline(always)]
pub fn bpc(&self) -> u8 {
self.bpc
}
#[inline(always)]
pub fn bpp(&self) -> u8 {
self.bpp
}
#[inline(always)]
pub fn profile(&self) -> color::Profile {
self.profile
}
}
impl Drop for Image {
#[inline(always)]
fn drop(&mut self){
unsafe{c::lexlibImageDelete(self)}
}
}
impl Clone for Image {
#[inline(always)]
fn clone(&self) -> Self {
let mut copy = MaybeUninit::uninit();
match unsafe{c::lexlibImageCopy(copy.as_mut_ptr(), self)} {
c::LEXLIB_OK => unsafe{copy.assume_init()},
c::LEXLIB_OUT_OF_MEMORY => panic!("out of memory"),
_ => panic!("lexlib::image::Image::clone() unknown error")
}
}
}
#[cfg(test)]
use crate::color::Color;
#[test]
fn image_compat(){
let imageog = Image::new(240, 160, color::Profile::RGBA, 8).expect("failed to create image");
let mut image = imageog.clone();
drop(imageog);
assert!(!image.data.is_null());
assert_eq!(image.width, 240);
assert_eq!(image.height, 160);
assert_eq!(image.channels, 4);
assert_eq!(image.bpc, 8);
assert_eq!(image.profile, color::Profile::RGBA);
assert_eq!(image.flags, 0x80);
image.fill_area(0,0,240,160, color::Color8::RED, color::BlendMode::Mod).unwrap();
image.validate().expect("validation failed");
image.flip_x().expect("failed to flip x");
image.flip_y().expect("failed to flip y");
image.set_pixel(0, 0, color::Color8::YELLOW, color::BlendMode::None).unwrap();
image.set_pixel(0, 1, color::Color8::new_gray(0x00), color::BlendMode::None).unwrap();
image.change_profile(color::Profile::RGB).unwrap();
image.save("resources/out/img.bmp").unwrap();
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "lexlib::image::Error::{:?}", self)
}
}
impl std::error::Error for Error {}
impl core::fmt::Display for BmpHeader {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "BmpHeader::{:?}", self)
}
}