use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::rc::Rc;
use crate::get_error;
use crate::pixels;
use crate::rect::Rect;
use crate::render::{BlendMode, Canvas};
use crate::render::{Texture, TextureCreator, TextureValueError};
use crate::rwops::RWops;
use libc::c_int;
use std::convert::TryFrom;
use std::mem::transmute;
use std::ptr;
use crate::sys;
pub struct SurfaceContext<'a> {
raw: *mut sys::SDL_Surface,
_marker: PhantomData<&'a ()>,
}
impl<'a> Drop for SurfaceContext<'a> {
#[inline]
#[doc(alias = "SDL_FreeSurface")]
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)
}
#[doc(alias = "SDL_CreateRGBSurface")]
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)
}
#[doc(alias = "SDL_CreateRGBSurfaceFrom")]
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)
}
#[doc(alias = "SDL_LoadBMP_RW")]
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]
#[allow(clippy::trivially_copy_pass_by_ref)]
#[doc(alias = "SDL_Surface")]
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())
}
#[doc(alias = "SDL_LockSurface")]
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
}
}
#[doc(alias = "SDL_LockSurface")]
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
}
#[doc(alias = "SDL_SaveBMP_RW")]
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)
}
#[doc(alias = "SDL_SetSurfacePalette")]
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)]
#[doc(alias = "SDL_SetSurfaceRLE")]
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)]
#[doc(alias = "SDL_SetSurfaceRLE")]
pub fn disable_RLE(&mut self) {
let result = unsafe { sys::SDL_SetSurfaceRLE(self.raw(), 0) };
if result != 0 {
panic!("{}", get_error());
}
}
#[doc(alias = "SDL_SetColorKey")]
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())
}
}
#[doc(alias = "SDL_GetColorKey")]
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())
}
}
#[doc(alias = "SDL_SetSurfaceColorMod")]
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());
}
}
#[doc(alias = "SDL_GetSurfaceColorMod")]
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())
}
}
#[doc(alias = "SDL_FillRect")]
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(())
}
#[doc(alias = "SDL_SetSurfaceAlphaMod")]
pub fn set_alpha_mod(&mut self, alpha: u8) {
let result = unsafe { sys::SDL_SetSurfaceAlphaMod(self.raw(), alpha) };
if result != 0 {
panic!("{}", get_error());
}
}
#[doc(alias = "SDL_GetSurfaceAlphaMod")]
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()),
}
}
#[doc(alias = "SDL_SetSurfaceBlendMode")]
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()),
}
}
#[doc(alias = "SDL_GetSurfaceBlendMode")]
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()),
}
}
#[doc(alias = "SDL_SetClipRect")]
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
}
}
#[doc(alias = "SDL_GetClipRect")]
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))
}
}
#[doc(alias = "SDL_ConvertSurface")]
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)) }
}
}
#[doc(alias = "SDL_ConvertSurfaceFormat")]
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)) }
}
}
#[doc(alias = "SDL_UpperBlit")]
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()),
}
}
#[doc(alias = "SDL_UpperBlitScaled")]
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()),
}
}
}