use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::rc::Rc;
use crate::rect::Rect;
use crate::get_error;
use std::ptr;
use libc::c_int;
use std::convert::TryFrom;
use crate::pixels;
use crate::render::{BlendMode, Canvas};
use crate::rwops::RWops;
use std::mem::transmute;
use crate::render::{Texture, TextureCreator, TextureValueError};
use crate::sys;
pub struct SurfaceContext<'a> {
raw: *mut sys::SDL_Surface,
_marker: PhantomData<&'a ()>
}
impl<'a> Drop for SurfaceContext<'a> {
#[inline]
fn drop(&mut self) {
unsafe { sys::SDL_FreeSurface(self.raw); }
}
}
pub struct Surface<'a> {
context: Rc<SurfaceContext<'a>>,
}
pub struct SurfaceRef {
_raw: ()
}
impl AsRef<SurfaceRef> for SurfaceRef {
fn as_ref(&self) -> &SurfaceRef {
self
}
}
#[test]
fn test_surface_ref_size() {
assert_eq!(::std::mem::size_of::<SurfaceRef>(), 0);
}
impl<'a> Deref for Surface<'a> {
type Target = SurfaceRef;
#[inline]
fn deref(&self) -> &SurfaceRef {
unsafe { mem::transmute(self.context.raw) }
}
}
impl<'a> DerefMut for Surface<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut SurfaceRef {
unsafe { mem::transmute(self.context.raw) }
}
}
impl<'a> AsRef<SurfaceRef> for Surface<'a> {
#[inline]
fn as_ref(&self) -> &SurfaceRef {
unsafe { mem::transmute(self.context.raw) }
}
}
impl<'a> AsMut<SurfaceRef> for Surface<'a> {
#[inline]
fn as_mut(&mut self) -> &mut SurfaceRef {
unsafe { mem::transmute(self.context.raw) }
}
}
impl<'a> Surface<'a> {
pub unsafe fn from_ll<'b>(raw: *mut sys::SDL_Surface) -> Surface<'b> {
let context = SurfaceContext {
raw,
_marker: PhantomData,
};
Surface { context: Rc::new(context) }
}
pub fn new(width: u32, height: u32, format: pixels::PixelFormatEnum) -> Result<Surface<'static>, String> {
let masks = format.into_masks()?;
Surface::from_pixelmasks(width, height, masks)
}
pub fn from_pixelmasks(width: u32, height: u32, masks: pixels::PixelMasks) -> Result<Surface<'static>, String> {
unsafe {
if width >= (1<<31) || height >= (1<<31) {
Err("Image is too large.".to_owned())
} else {
let raw = sys::SDL_CreateRGBSurface(0, width as c_int, height as c_int,
masks.bpp as c_int, masks.rmask, masks.gmask, masks.bmask, masks.amask);
if raw.is_null() {
Err(get_error())
} else {
Ok(Surface::from_ll(raw))
}
}
}
}
pub fn from_data(data: &'a mut [u8], width: u32, height: u32, pitch: u32, format: pixels::PixelFormatEnum) -> Result<Surface<'a>, String> {
let masks = format.into_masks()?;
Surface::from_data_pixelmasks(data, width, height, pitch, masks)
}
pub fn from_data_pixelmasks(data: &'a mut [u8], width: u32, height: u32, pitch: u32, masks: pixels::PixelMasks) -> Result<Surface<'a>, String> {
unsafe {
if width >= (1<<31) || height >= (1<<31) {
Err("Image is too large.".to_owned())
} else if pitch >= (1<<31) {
Err("Pitch is too large.".to_owned())
} else {
let raw = sys::SDL_CreateRGBSurfaceFrom(
data.as_mut_ptr() as *mut _, width as c_int, height as c_int,
masks.bpp as c_int, pitch as c_int, masks.rmask, masks.gmask, masks.bmask, masks.amask);
if raw.is_null() {
Err(get_error())
} else {
Ok(Surface::from_ll(raw))
}
}
}
}
#[cfg(not(feature = "unsafe_textures"))]
pub fn as_texture<'b, T>(&self, texture_creator: &'b TextureCreator<T>) -> Result<Texture<'b>, TextureValueError> {
texture_creator.create_texture_from_surface(self)
}
#[cfg(feature = "unsafe_textures")]
pub fn as_texture<T>(&self, texture_creator: &TextureCreator<T>) -> Result<Texture, TextureValueError> {
texture_creator.create_texture_from_surface(self)
}
pub fn load_bmp_rw(rwops: &mut RWops) -> Result<Surface<'static>, String> {
let raw = unsafe {
sys::SDL_LoadBMP_RW(rwops.raw(), 0)
};
if raw.is_null() {
Err(get_error())
} else {
Ok( unsafe{ Surface::from_ll(raw) } )
}
}
pub fn load_bmp<P: AsRef<Path>>(path: P) -> Result<Surface<'static>, String> {
let mut file = RWops::from_file(path, "rb")?;
Surface::load_bmp_rw(&mut file)
}
pub fn into_canvas(self) -> Result<Canvas<Surface<'a>>, String> {
Canvas::from_surface(self)
}
pub fn context(&self) -> Rc<SurfaceContext<'a>> {
self.context.clone()
}
}
impl SurfaceRef {
#[inline]
pub unsafe fn from_ll<'a>(raw: *const sys::SDL_Surface) -> &'a SurfaceRef {
&*(raw as *const () as *const SurfaceRef)
}
#[inline]
pub unsafe fn from_ll_mut<'a>(raw: *mut sys::SDL_Surface) -> &'a mut SurfaceRef {
&mut *(raw as *mut () as *mut SurfaceRef)
}
#[inline]
pub fn raw(&self) -> *mut sys::SDL_Surface {
self as *const SurfaceRef as *mut SurfaceRef as *mut () as *mut sys::SDL_Surface
}
#[inline]
fn raw_ref(&self) -> &sys::SDL_Surface {
unsafe {
&*(self as *const _ as *const () as *const sys::SDL_Surface)
}
}
pub fn width(&self) -> u32 {
self.raw_ref().w as u32
}
pub fn height(&self) -> u32 {
self.raw_ref().h as u32
}
pub fn pitch(&self) -> u32 {
self.raw_ref().pitch as u32
}
pub fn size(&self) -> (u32, u32) {
(self.width(), self.height())
}
pub fn rect(&self) -> Rect {
Rect::new(0, 0, self.width(), self.height())
}
pub fn pixel_format(&self) -> pixels::PixelFormat {
unsafe {
pixels::PixelFormat::from_ll(self.raw_ref().format)
}
}
pub fn pixel_format_enum(&self) -> pixels::PixelFormatEnum {
pixels::PixelFormatEnum::from(self.pixel_format())
}
pub fn with_lock<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
unsafe {
if sys::SDL_LockSurface(self.raw()) != 0 { panic!("could not lock surface"); }
let raw_pixels = self.raw_ref().pixels as *const _;
let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
let pixels = ::std::slice::from_raw_parts(raw_pixels, len);
let rv = f(pixels);
sys::SDL_UnlockSurface(self.raw());
rv
}
}
pub fn with_lock_mut<R, F: FnOnce(&mut [u8]) -> R>(&mut self, f: F) -> R {
unsafe {
if sys::SDL_LockSurface(self.raw()) != 0 { panic!("could not lock surface"); }
let raw_pixels = self.raw_ref().pixels as *mut _;
let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
let pixels = ::std::slice::from_raw_parts_mut(raw_pixels, len);
let rv = f(pixels);
sys::SDL_UnlockSurface(self.raw());
rv
}
}
pub fn without_lock(&self) -> Option<&[u8]> {
if self.must_lock() {
None
} else {
unsafe {
let raw_pixels = self.raw_ref().pixels as *const _;
let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
Some(::std::slice::from_raw_parts(raw_pixels, len))
}
}
}
pub fn without_lock_mut(&mut self) -> Option<&mut [u8]> {
if self.must_lock() {
None
} else {
unsafe {
let raw_pixels = self.raw_ref().pixels as *mut _;
let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
Some(::std::slice::from_raw_parts_mut(raw_pixels, len))
}
}
}
pub fn must_lock(&self) -> bool {
(self.raw_ref().flags & sys::SDL_RLEACCEL) != 0
}
pub fn save_bmp_rw(&self, rwops: &mut RWops) -> Result<(), String> {
let ret = unsafe {
sys::SDL_SaveBMP_RW(self.raw(), rwops.raw(), 0)
};
if ret == 0 { Ok(()) }
else { Err(get_error()) }
}
pub fn save_bmp<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
let mut file = RWops::from_file(path, "wb")?;
self.save_bmp_rw(&mut file)
}
pub fn set_palette(&mut self, palette: &pixels::Palette) -> Result<(), String> {
let result = unsafe { sys::SDL_SetSurfacePalette(self.raw(), palette.raw()) };
match result {
0 => Ok(()),
_ => Err(get_error())
}
}
#[allow(non_snake_case)]
pub fn enable_RLE(&mut self) {
let result = unsafe { sys::SDL_SetSurfaceRLE(self.raw(), 1) };
if result != 0 {
panic!(get_error());
}
}
#[allow(non_snake_case)]
pub fn disable_RLE(&mut self) {
let result = unsafe { sys::SDL_SetSurfaceRLE(self.raw(), 0) };
if result != 0 {
panic!(get_error());
}
}
pub fn set_color_key(&mut self, enable: bool, color: pixels::Color) -> Result<(), String> {
let key = color.to_u32(&self.pixel_format());
let result = unsafe {
sys::SDL_SetColorKey(self.raw(), if enable { 1 } else { 0 }, key)
};
if result == 0 {
Ok(())
} else {
Err(get_error())
}
}
pub fn color_key(&self) -> Result<pixels::Color, String> {
let mut key = 0;
let result = unsafe {
sys::SDL_GetColorKey(self.raw(), &mut key)
};
if result == 0 {
Ok(pixels::Color::from_u32(&self.pixel_format(), key))
} else {
Err(get_error())
}
}
pub fn set_color_mod(&mut self, color: pixels::Color) {
let (r, g, b) = color.rgb();
let result = unsafe { sys::SDL_SetSurfaceColorMod(self.raw(), r, g, b) };
if result != 0 {
panic!(get_error());
}
}
pub fn color_mod(&self) -> pixels::Color {
let mut r = 0;
let mut g = 0;
let mut b = 0;
let result = unsafe {
sys::SDL_GetSurfaceColorMod(self.raw(), &mut r, &mut g, &mut b) == 0
};
if result {
pixels::Color::RGB(r, g, b)
} else {
panic!(get_error())
}
}
pub fn fill_rect<R>(&mut self, rect: R, color: pixels::Color) -> Result<(), String>
where R: Into<Option<Rect>>
{
unsafe {
let rect = rect.into();
let rect_ptr = mem::transmute(rect.as_ref());
let format = self.pixel_format();
let result = sys::SDL_FillRect(self.raw(), rect_ptr, color.to_u32(&format) );
match result {
0 => Ok(()),
_ => Err(get_error())
}
}
}
#[allow(clippy::clone_on_copy)]
pub fn fill_rects(&mut self, rects: &[Rect], color: pixels::Color) -> Result<(), String>
{
for rect in rects.iter() {
if let Err(e) = self.fill_rect(rect.clone(), color)
{
return Err(e);
}
}
Ok(())
}
pub fn set_alpha_mod(&mut self, alpha: u8) {
let result = unsafe {
sys::SDL_SetSurfaceAlphaMod(self.raw(), alpha)
};
if result != 0 {
panic!(get_error());
}
}
pub fn alpha_mod(&self) -> u8 {
let mut alpha = 0;
let result = unsafe {
sys::SDL_GetSurfaceAlphaMod(self.raw(), &mut alpha)
};
match result {
0 => alpha,
_ => panic!(get_error())
}
}
pub fn set_blend_mode(&mut self, mode: BlendMode) -> Result<(), String> {
let result = unsafe {
sys::SDL_SetSurfaceBlendMode(self.raw(), transmute(mode))
};
match result {
0 => Ok(()),
_ => Err(get_error())
}
}
pub fn blend_mode(&self) -> BlendMode {
let mut mode = sys::SDL_BlendMode::SDL_BLENDMODE_NONE;
let result = unsafe {
sys::SDL_GetSurfaceBlendMode(self.raw(), &mut mode)
};
match result {
0 => BlendMode::try_from(mode as u32).unwrap(),
_ => panic!(get_error())
}
}
pub fn set_clip_rect<R>(&mut self, rect: R) -> bool
where R: Into<Option<Rect>>
{
let rect = rect.into();
unsafe {
sys::SDL_SetClipRect(self.raw(), match rect {
Some(rect) => rect.raw(),
None => ptr::null()
}) == sys::SDL_bool::SDL_TRUE
}
}
pub fn clip_rect(&self) -> Option<Rect> {
let mut raw = mem::MaybeUninit::uninit();
unsafe {
sys::SDL_GetClipRect(self.raw(), raw.as_mut_ptr())
};
let raw = unsafe { raw.assume_init() };
if raw.w == 0 || raw.h == 0 {
None
} else {
Some(Rect::from_ll(raw))
}
}
pub fn convert(&self, format: &pixels::PixelFormat) -> Result<Surface<'static>, String> {
let surface_ptr = unsafe { sys::SDL_ConvertSurface(self.raw(), format.raw(), 0u32) };
if surface_ptr.is_null() {
Err(get_error())
} else {
unsafe { Ok(Surface::from_ll(surface_ptr)) }
}
}
pub fn convert_format(&self, format: pixels::PixelFormatEnum) -> Result<Surface<'static>, String> {
let surface_ptr = unsafe { sys::SDL_ConvertSurfaceFormat(self.raw(), format as u32, 0u32) };
if surface_ptr.is_null() {
Err(get_error())
} else {
unsafe { Ok(Surface::from_ll(surface_ptr)) }
}
}
pub fn blit<R1, R2>(&self, src_rect: R1,
dst: &mut SurfaceRef, dst_rect: R2)
-> Result<Option<Rect>, String>
where R1: Into<Option<Rect>>,
R2: Into<Option<Rect>>,
{
let src_rect = src_rect.into();
let dst_rect = dst_rect.into();
unsafe {
let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null());
let mut dst_rect = dst_rect;
let dst_rect_ptr = dst_rect.as_mut().map(|r| r.raw_mut())
.unwrap_or(ptr::null_mut());
let result = sys::SDL_UpperBlit(
self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr
);
if result == 0 {
Ok(dst_rect)
} else {
Err(get_error())
}
}
}
pub unsafe fn lower_blit<R1, R2>(&self, src_rect: R1,
dst: &mut SurfaceRef, dst_rect: R2) -> Result<(), String>
where R1: Into<Option<Rect>>,
R2: Into<Option<Rect>>,
{
let src_rect = src_rect.into();
let dst_rect = dst_rect.into();
match {
let src_rect_ptr = src_rect.as_ref().map(|r| r.raw())
.unwrap_or(ptr::null()) as *mut _;
let dst_rect_ptr = dst_rect.as_ref().map(|r| r.raw())
.unwrap_or(ptr::null()) as *mut _;
sys::SDL_LowerBlit(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
} {
0 => Ok(()),
_ => Err(get_error())
}
}
pub fn blit_scaled<R1, R2>(&self, src_rect: R1,
dst: &mut SurfaceRef, dst_rect: R2) -> Result<Option<Rect>, String>
where R1: Into<Option<Rect>>,
R2: Into<Option<Rect>>,
{
let src_rect = src_rect.into();
let dst_rect = dst_rect.into();
match unsafe {
let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null());
let mut dst_rect = dst_rect;
let dst_rect_ptr = dst_rect.as_mut().map(|r| r.raw_mut())
.unwrap_or(ptr::null_mut());
sys::SDL_UpperBlitScaled(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
} {
0 => Ok(dst_rect),
_ => Err(get_error())
}
}
pub unsafe fn lower_blit_scaled<R1, R2>(&self, src_rect: R1,
dst: &mut SurfaceRef, dst_rect: R2) -> Result<(), String>
where R1: Into<Option<Rect>>,
R2: Into<Option<Rect>>
{
match {
let src_rect_ptr = src_rect.into().as_ref().map(|r| r.raw())
.unwrap_or(ptr::null()) as *mut _;
let dst_rect_ptr = dst_rect.into().as_ref().map(|r| r.raw())
.unwrap_or(ptr::null()) as *mut _;
sys::SDL_LowerBlitScaled(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
} {
0 => Ok(()),
_ => Err(get_error())
}
}
}