use alloc::format;
use core::ptr::NonNull;
use super::bitmap_data::BitmapData;
use super::color::{Color, PixelColor};
use super::unowned_bitmap::UnownedBitmapMut;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::null_terminated::ToNullTerminatedString;
use crate::Error;
#[derive(Debug)]
pub struct BitmapRef {
ptr: NonNull<CBitmap>,
}
impl BitmapRef {
pub(crate) fn from_ptr(ptr: NonNull<CBitmap>) -> Self {
BitmapRef { ptr }
}
fn data_and_pixels_ptr(&self) -> (BitmapData, *mut u8) {
let mut width = 0;
let mut height = 0;
let mut rowbytes = 0;
let mut hasmask = 0;
let mut pixels = core::ptr::null_mut();
unsafe {
Bitmap::fns().getBitmapData.unwrap()(
self.cptr() as *mut _,
&mut width,
&mut height,
&mut rowbytes,
&mut hasmask,
&mut pixels,
)
};
let data = BitmapData::new(width, height, rowbytes, hasmask);
(data, pixels)
}
pub fn load_file(&mut self, path: &str) -> Result<(), Error> {
let mut out_err: *const u8 = core::ptr::null_mut();
unsafe {
Bitmap::fns().loadIntoBitmap.unwrap()(
path.to_null_terminated_utf8().as_ptr(),
self.cptr_mut(),
&mut out_err,
)
};
if !out_err.is_null() {
let result = unsafe { crate::null_terminated::parse_null_terminated_utf8(out_err) };
match result {
Ok(err) => Err(format!("load_into_bitmap: {}", err).into()),
Err(err) => Err(format!("load_into_bitmap: unknown error ({})", err).into()),
}
} else {
Ok(())
}
}
pub fn data(&self) -> BitmapData {
let (data, _) = self.data_and_pixels_ptr();
data
}
pub fn as_bytes(&self) -> &[u8] {
let (data, pixels) = self.data_and_pixels_ptr();
unsafe { core::slice::from_raw_parts(pixels, (data.row_bytes() * data.height()) as usize) }
}
pub fn as_mut_bytes(&mut self) -> &mut [u8] {
let (data, pixels) = self.data_and_pixels_ptr();
unsafe { core::slice::from_raw_parts_mut(pixels, (data.row_bytes() * data.height()) as usize) }
}
pub fn as_pixels(&self) -> BitmapPixels {
let (data, pixels) = self.data_and_pixels_ptr();
let slice =
unsafe { core::slice::from_raw_parts(pixels, (data.row_bytes() * data.height()) as usize) };
BitmapPixels {
data,
pixels: slice,
}
}
pub fn as_pixels_mut(&mut self) -> BitmapPixelsMut {
let (data, pixels) = self.data_and_pixels_ptr();
let slice = unsafe {
core::slice::from_raw_parts_mut(pixels, (data.row_bytes() * data.height()) as usize)
};
BitmapPixelsMut {
data,
pixels: slice,
}
}
pub fn clear<'a, C: Into<Color<'a>>>(&mut self, bg_color: C) {
unsafe {
Bitmap::fns().clearBitmap.unwrap()(self.cptr_mut(), bg_color.into().to_c_color());
}
}
pub fn set_mask_bitmap(&mut self, mask: &BitmapRef) -> Result<(), Error> {
let result =
unsafe { Bitmap::fns().setBitmapMask.unwrap()(self.cptr_mut(), mask.cptr() as *mut _) };
match result {
1 => Ok(()),
0 => Err(Error::DimensionsDoNotMatch),
_ => panic!("unknown error result from setBitmapMask"),
}
}
pub fn mask_bitmap(&self) -> Option<UnownedBitmapMut> {
let mask = unsafe {
Bitmap::fns().getBitmapMask.unwrap()(self.cptr() as *mut _)
};
Some(UnownedBitmapMut::from_ptr(NonNull::new(mask)?))
}
pub(crate) fn cptr(&self) -> *const CBitmap {
self.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CBitmap {
self.ptr.as_ptr()
}
pub(crate) fn copy_non_null(&self) -> NonNull<CBitmap> {
self.ptr
}
}
impl alloc::borrow::ToOwned for BitmapRef {
type Owned = Bitmap;
fn to_owned(&self) -> Self::Owned {
let ptr = unsafe { Bitmap::fns().copyBitmap.unwrap()(self.cptr() as *mut _) };
Bitmap::from_owned_ptr(NonNull::new(ptr).unwrap())
}
}
#[derive(Debug)]
pub struct Bitmap {
owned: BitmapRef,
}
impl Bitmap {
pub(crate) fn from_owned_ptr(bitmap_ptr: NonNull<CBitmap>) -> Self {
Bitmap {
owned: BitmapRef::from_ptr(bitmap_ptr),
}
}
pub fn new<'a, C: Into<Color<'a>>>(width: i32, height: i32, bg_color: C) -> Bitmap {
let bitmap_ptr =
unsafe { Self::fns().newBitmap.unwrap()(width, height, bg_color.into().to_c_color()) };
Bitmap::from_owned_ptr(NonNull::new(bitmap_ptr).unwrap())
}
pub fn from_bitmap_with_rotation(
bitmap: &BitmapRef,
rotation: f32,
xscale: f32,
yscale: f32,
) -> Bitmap {
let mut _alloced_size: i32 = 0;
let bitmap_ptr = unsafe {
Self::fns().rotatedBitmap.unwrap()(
bitmap.cptr() as *mut _,
rotation,
xscale,
yscale,
&mut _alloced_size,
)
};
Bitmap::from_owned_ptr(NonNull::new(bitmap_ptr).unwrap())
}
pub fn from_file(path: &str) -> Result<Bitmap, Error> {
let mut out_err: *const u8 = core::ptr::null_mut();
let bitmap_ptr = unsafe {
Self::fns().loadBitmap.unwrap()(path.to_null_terminated_utf8().as_ptr(), &mut out_err)
};
if !out_err.is_null() {
let result = unsafe { crate::null_terminated::parse_null_terminated_utf8(out_err) };
match result {
Ok(err) => Err(format!("load_bitmap: {}", err).into()),
Err(err) => Err(format!("load_bitmap: unknown error ({})", err).into()),
}
} else {
assert!(!bitmap_ptr.is_null());
Ok(Bitmap::from_owned_ptr(NonNull::new(bitmap_ptr).unwrap()))
}
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_graphics {
CApiState::get().cgraphics
}
}
impl Clone for Bitmap {
fn clone(&self) -> Self {
use alloc::borrow::ToOwned;
self.as_ref().to_owned()
}
}
impl Drop for Bitmap {
fn drop(&mut self) {
unsafe {
Self::fns().freeBitmap.unwrap()(self.cptr_mut());
}
}
}
impl core::ops::Deref for Bitmap {
type Target = BitmapRef;
fn deref(&self) -> &Self::Target {
&self.owned
}
}
impl core::ops::DerefMut for Bitmap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.owned
}
}
impl core::borrow::Borrow<BitmapRef> for Bitmap {
fn borrow(&self) -> &BitmapRef {
self }
}
impl core::borrow::BorrowMut<BitmapRef> for Bitmap {
fn borrow_mut(&mut self) -> &mut BitmapRef {
self }
}
impl AsRef<BitmapRef> for Bitmap {
fn as_ref(&self) -> &BitmapRef {
self }
}
impl AsMut<BitmapRef> for Bitmap {
fn as_mut(&mut self) -> &mut BitmapRef {
self }
}
pub struct BitmapPixels<'bitmap> {
data: BitmapData,
pixels: &'bitmap [u8],
}
impl BitmapPixels<'_> {
pub fn get(&self, x: usize, y: usize) -> PixelColor {
let byte_index = self.data.row_bytes() as usize * y + x / 8;
let bit_index = x % 8;
let bit = (self.pixels[byte_index] >> (7 - bit_index)) & 0x1 == 0x1;
bit.into()
}
}
pub struct BitmapPixelsMut<'bitmap> {
data: BitmapData,
pixels: &'bitmap mut [u8],
}
impl BitmapPixelsMut<'_> {
pub fn get(&self, x: usize, y: usize) -> PixelColor {
let byte_index = self.data.row_bytes() as usize * y + x / 8;
let bit_index = x % 8;
let bit = (self.pixels[byte_index] >> (7 - bit_index)) & 0x1 == 0x1;
bit.into()
}
pub fn set(&mut self, x: usize, y: usize, new_value: PixelColor) {
let byte_index = self.data.row_bytes() as usize * y + x / 8;
let bit_index = x % 8;
if new_value.to_bit() {
self.pixels[byte_index] |= 1u8 << (7 - bit_index);
} else {
self.pixels[byte_index] &= !(1u8 << (7 - bit_index));
}
}
}