ndless_sdl/
video.rs

1use core::mem;
2use core::ptr;
3use core::slice;
4
5use cstr_core::CString;
6use cty::c_int;
7use ndless::prelude::*;
8
9use crate::get_error;
10use crate::Rect;
11
12pub use self::Color::{RGB, RGBA};
13
14#[allow(non_camel_case_types)]
15type c_float = f32;
16
17pub mod ll {
18	#![allow(non_camel_case_types)]
19
20	use cty::{
21		c_char as c_schar, c_int, c_uchar, c_uint, c_void, int32_t, uint16_t, uint32_t, uint8_t,
22	};
23
24	use crate::Rect;
25
26	type c_float = f32;
27	pub type SDL_Rect = Rect;
28
29	#[repr(C)]
30	#[derive(Copy, Clone)]
31	pub struct SDL_RWops {
32		pub seek: *mut uint8_t,
33		pub read: *mut uint8_t,
34		pub write: *mut uint8_t,
35		pub close: *mut uint8_t,
36		pub _type: uint32_t,
37		_hidden: [c_uchar; 24],
38	}
39
40	#[repr(C)]
41	#[derive(Copy, Clone)]
42	pub struct SDL_Surface {
43		pub flags: uint32_t,
44		pub format: *mut SDL_PixelFormat,
45		pub w: c_int,
46		pub h: c_int,
47		pub pitch: uint16_t,
48		pub pixels: *mut c_void,
49		pub offset: c_int,
50		pub hwdata: *mut c_void,
51		pub clip_rect: SDL_Rect,
52		pub unused1: uint32_t,
53		pub locked: uint32_t,
54		pub map: *mut c_void,
55		pub format_version: c_uint,
56		pub refcount: c_int,
57	}
58
59	#[repr(C)]
60	#[derive(Copy, Clone)]
61	pub struct SDL_Color {
62		pub r: uint8_t,
63		pub g: uint8_t,
64		pub b: uint8_t,
65		pub unused: uint8_t,
66	}
67
68	#[repr(C)]
69	#[derive(Copy, Clone)]
70	pub struct SDL_Palette {
71		pub ncolors: c_int,
72		pub colors: *mut SDL_Color,
73	}
74
75	#[allow(non_snake_case)]
76	#[repr(C)]
77	#[derive(Copy, Clone)]
78	pub struct SDL_PixelFormat {
79		pub palette: *mut SDL_Palette,
80		pub BitsPerPixel: uint8_t,
81		pub BytesPerPixel: uint8_t,
82		pub Rloss: uint8_t,
83		pub Gloss: uint8_t,
84		pub Bloss: uint8_t,
85		pub Aloss: uint8_t,
86		pub Rshift: uint8_t,
87		pub Gshift: uint8_t,
88		pub Bshift: uint8_t,
89		pub Ashift: uint8_t,
90		pub Rmask: uint32_t,
91		pub Gmask: uint32_t,
92		pub Bmask: uint32_t,
93		pub Amask: uint32_t,
94		pub colorkey: uint32_t,
95		pub alpha: uint8_t,
96	}
97
98	#[repr(C)]
99	#[derive(Copy, Clone)]
100	pub struct SDL_VideoInfo {
101		pub flags: uint32_t,
102		// actually a set of packed fields
103		pub video_mem: uint32_t,
104		pub vfmt: *mut SDL_PixelFormat,
105		pub current_w: c_int,
106		pub current_h: c_int,
107	}
108
109	extern "C" {
110		pub fn SDL_CreateRGBSurface(
111			flags: uint32_t,
112			width: c_int,
113			height: c_int,
114			depth: c_int,
115			Rmask: uint32_t,
116			Gmask: uint32_t,
117			Bmask: uint32_t,
118			Amask: uint32_t,
119		) -> *mut SDL_Surface;
120		pub fn SDL_CreateRGBSurfaceFrom(
121			pixels: *mut c_void,
122			width: c_int,
123			height: c_int,
124			depth: c_int,
125			pitch: c_int,
126			Rmask: uint32_t,
127			Gmask: uint32_t,
128			Bmask: uint32_t,
129			Amask: uint32_t,
130		) -> *mut SDL_Surface;
131		pub fn SDL_FreeSurface(surface: *mut SDL_Surface);
132		pub fn SDL_MapRGB(
133			format: *const SDL_PixelFormat,
134			r: uint8_t,
135			g: uint8_t,
136			b: uint8_t,
137		) -> uint32_t;
138		pub fn SDL_MapRGBA(
139			format: *const SDL_PixelFormat,
140			r: uint8_t,
141			g: uint8_t,
142			b: uint8_t,
143			a: uint8_t,
144		) -> uint32_t;
145		pub fn SDL_GetRGB(
146			pixel: uint32_t,
147			fmt: *const SDL_PixelFormat,
148			r: *mut uint8_t,
149			g: *mut uint8_t,
150			b: *mut uint8_t,
151		);
152		pub fn SDL_GetRGBA(
153			pixel: uint32_t,
154			fmt: *const SDL_PixelFormat,
155			r: *mut uint8_t,
156			g: *mut uint8_t,
157			b: *mut uint8_t,
158			a: *mut uint8_t,
159		);
160		pub fn SDL_SetVideoMode(
161			width: c_int,
162			height: c_int,
163			bpp: c_int,
164			flags: uint32_t,
165		) -> *mut SDL_Surface;
166		pub fn SDL_VideoModeOK(width: c_int, height: c_int, bpp: c_int, flags: uint32_t) -> c_int;
167		pub fn SDL_GetVideoInfo() -> *const SDL_VideoInfo;
168		pub fn SDL_GetVideoSurface() -> *mut SDL_Surface;
169		pub fn SDL_UpdateRect(
170			screen: *mut SDL_Surface,
171			x: int32_t,
172			y: int32_t,
173			w: uint32_t,
174			h: uint32_t,
175		);
176		pub fn SDL_UpdateRects(screen: *mut SDL_Surface, numrects: c_int, rects: *mut SDL_Rect);
177		pub fn SDL_SetColors(
178			surface: *mut SDL_Surface,
179			colors: *mut SDL_Color,
180			firstcolor: c_int,
181			ncolors: c_int,
182		) -> c_int;
183		pub fn SDL_SetPalette(
184			surface: *mut SDL_Surface,
185			flags: c_int,
186			colors: *mut SDL_Color,
187			firstcolor: c_int,
188			ncolors: c_int,
189		) -> c_int;
190		pub fn SDL_LockSurface(surface: *mut SDL_Surface) -> c_int;
191		pub fn SDL_UnlockSurface(surface: *mut SDL_Surface);
192		pub fn SDL_Flip(screen: *mut SDL_Surface) -> c_int;
193		pub fn SDL_ConvertSurface(
194			src: *mut SDL_Surface,
195			fmt: *mut SDL_PixelFormat,
196			flags: uint32_t,
197		) -> *mut SDL_Surface;
198		pub fn SDL_DisplayFormat(surface: *mut SDL_Surface) -> *mut SDL_Surface;
199		pub fn SDL_DisplayFormatAlpha(surface: *mut SDL_Surface) -> *mut SDL_Surface;
200		pub fn SDL_SetColorKey(surface: *mut SDL_Surface, flag: uint32_t, key: uint32_t) -> c_int;
201		pub fn SDL_SetAlpha(surface: *mut SDL_Surface, flag: uint32_t, alpha: uint8_t) -> c_int;
202		pub fn SDL_SetClipRect(surface: *mut SDL_Surface, rect: *const SDL_Rect);
203		pub fn SDL_UpperBlit(
204			src: *mut SDL_Surface,
205			srcrect: *mut SDL_Rect,
206			dst: *mut SDL_Surface,
207			dstrect: *mut SDL_Rect,
208		) -> c_int;
209		pub fn SDL_FillRect(
210			dst: *mut SDL_Surface,
211			dstrect: *mut SDL_Rect,
212			color: uint32_t,
213		) -> c_int;
214		pub fn SDL_SetGamma(r: c_float, g: c_float, b: c_float) -> c_int;
215		pub fn SDL_SetGammaRamp(
216			r: *const uint16_t,
217			g: *const uint16_t,
218			b: *const uint16_t,
219		) -> c_int;
220		pub fn SDL_GetGammaRamp(r: *mut uint16_t, g: *mut uint16_t, b: *mut uint16_t) -> c_int;
221		pub fn SDL_RWFromFile(file: *const c_schar, mode: *const c_schar) -> *mut SDL_RWops;
222		pub fn SDL_RWFromConstMem(mem: *const c_void, size: c_int) -> *mut SDL_RWops;
223		pub fn SDL_LoadBMP_RW(src: *mut SDL_RWops, freesrc: c_int) -> *mut SDL_Surface;
224		pub fn SDL_SaveBMP_RW(
225			surface: *mut SDL_Surface,
226			dst: *mut SDL_RWops,
227			freedst: c_int,
228		) -> c_int;
229		pub fn SDL_GL_SwapBuffers();
230	}
231}
232
233#[derive(Debug, PartialEq)]
234pub struct Surface {
235	pub raw: *mut ll::SDL_Surface,
236	pub owned: bool,
237}
238
239fn wrap_surface(raw: *mut ll::SDL_Surface, owned: bool) -> Surface {
240	Surface { raw, owned }
241}
242
243impl Drop for Surface {
244	fn drop(&mut self) {
245		unsafe {
246			if self.owned {
247				ll::SDL_FreeSurface(self.raw);
248			}
249		}
250	}
251}
252
253#[derive(PartialEq, Copy, Clone, Debug)]
254pub struct Palette {
255	pub raw: *mut ll::SDL_Palette,
256}
257
258fn wrap_palette(palette: *mut ll::SDL_Palette) -> Option<Palette> {
259	if palette.is_null() {
260		None
261	} else {
262		Some(Palette { raw: palette })
263	}
264}
265
266pub type PaletteColors<'a> = slice::Iter<'a, ll::SDL_Color>;
267
268impl Palette {
269	pub fn colors<'a>(&'a self) -> PaletteColors<'a> {
270		use self::ll::SDL_Color;
271		let colors = unsafe { (*self.raw).colors } as *const SDL_Color;
272		let ncolors = unsafe { (*self.raw).ncolors } as usize;
273		let colors: &'a [SDL_Color] = unsafe {
274			&*(slice::from_raw_parts(&colors, ncolors) as *const [*const SDL_Color]
275				as *const [SDL_Color])
276		};
277		colors.iter()
278	}
279}
280
281#[derive(PartialEq, Debug)]
282pub struct PixelFormat {
283	pub palette: Option<Palette>,
284	pub bpp: u8,
285	pub r_loss: u8,
286	pub g_loss: u8,
287	pub b_loss: u8,
288	pub a_loss: u8,
289	pub r_shift: u8,
290	pub g_shift: u8,
291	pub b_shift: u8,
292	pub a_shift: u8,
293	pub r_mask: u32,
294	pub g_mask: u32,
295	pub b_mask: u32,
296	pub a_mask: u32,
297	pub color_key: u32,
298	pub alpha: u8,
299}
300
301fn wrap_pixel_format(raw: *mut ll::SDL_PixelFormat) -> PixelFormat {
302	let fmt = &unsafe { *raw };
303	PixelFormat {
304		palette: wrap_palette(fmt.palette),
305		bpp: fmt.BitsPerPixel,
306		r_loss: fmt.Rloss,
307		g_loss: fmt.Gloss,
308		b_loss: fmt.Bloss,
309		a_loss: fmt.Aloss,
310		r_shift: fmt.Rshift,
311		g_shift: fmt.Gshift,
312		b_shift: fmt.Bshift,
313		a_shift: fmt.Ashift,
314		r_mask: fmt.Rmask,
315		g_mask: fmt.Gmask,
316		b_mask: fmt.Bmask,
317		a_mask: fmt.Amask,
318		color_key: fmt.colorkey,
319		alpha: fmt.alpha,
320	}
321}
322
323fn unwrap_pixel_format(fmt: &PixelFormat) -> ll::SDL_PixelFormat {
324	ll::SDL_PixelFormat {
325		palette: match fmt.palette {
326			None => ptr::null_mut(),
327			Some(palette) => palette.raw,
328		},
329		BitsPerPixel: fmt.bpp,
330		BytesPerPixel: fmt.bpp / 8,
331		Rloss: fmt.r_loss,
332		Gloss: fmt.g_loss,
333		Bloss: fmt.b_loss,
334		Aloss: fmt.a_loss,
335		Rshift: fmt.r_shift,
336		Gshift: fmt.g_shift,
337		Bshift: fmt.b_shift,
338		Ashift: fmt.a_shift,
339		Rmask: fmt.r_mask,
340		Gmask: fmt.g_mask,
341		Bmask: fmt.b_mask,
342		Amask: fmt.a_mask,
343		colorkey: fmt.color_key,
344		alpha: fmt.alpha,
345	}
346}
347
348#[derive(Debug, PartialEq, Eq, Copy, Clone)]
349pub enum Color {
350	RGB(u8, u8, u8),
351	RGBA(u8, u8, u8, u8),
352}
353
354impl Color {
355	pub fn from_mapped(bit: u32, fmt: *const ll::SDL_PixelFormat) -> Color {
356		let mut r = 0;
357		let mut g = 0;
358		let mut b = 0;
359		let mut a = 0;
360
361		unsafe { ll::SDL_GetRGBA(bit, fmt, &mut r, &mut g, &mut b, &mut a) }
362
363		RGBA(r, g, b, a)
364	}
365
366	pub fn to_mapped(self, fmt: *const ll::SDL_PixelFormat) -> u32 {
367		match self {
368			RGB(r, g, b) => unsafe { ll::SDL_MapRGB(fmt, r, g, b) },
369			RGBA(r, g, b, a) => unsafe { ll::SDL_MapRGBA(fmt, r, g, b, a) },
370		}
371	}
372
373	pub fn from_struct(c: ll::SDL_Color) -> Color {
374		RGB(c.r, c.g, c.b)
375	}
376
377	pub fn to_struct(self) -> ll::SDL_Color {
378		match self {
379			RGB(r, g, b) => ll::SDL_Color { r, g, b, unused: 0 },
380			RGBA(r, g, b, _) => ll::SDL_Color { r, g, b, unused: 0 },
381		}
382	}
383}
384
385#[derive(PartialEq, Eq, Copy, Clone)]
386pub enum SurfaceFlag {
387	SWSurface = 0x0000_0000,
388	HWSurface = 0x0000_0001,
389	AsyncBlit = 0x0000_0004,
390	SrcColorKey = 0x0000_1000,
391	SrcAlpha = 0x0001_0000,
392	RLEAccel = 0x0000_4000,
393}
394
395#[derive(PartialEq, Eq, Copy, Clone)]
396pub enum VideoFlag {
397	AnyFormat = 0x1000_0000,
398	HWPalette = 0x2000_0000,
399	DoubleBuf = 0x4000_0000,
400	Fullscreen = 0x8000_0000usize as isize,
401	// 0x8000_0000 > INT_MAX on i686
402	OpenGL = 0x0000_0002,
403	OpenGLBlit = 0x0000_000A,
404	Resizable = 0x0000_0010,
405	NoFrame = 0x0000_0020,
406}
407
408pub fn set_video_mode(
409	w: isize,
410	h: isize,
411	bpp: isize,
412	surface_flags: &[SurfaceFlag],
413	video_flags: &[VideoFlag],
414) -> Result<Surface, String> {
415	let flags = surface_flags
416		.iter()
417		.fold(0u32, |flags, &flag| flags | flag as u32);
418	let flags = video_flags
419		.iter()
420		.fold(flags, |flags, &flag| flags | flag as u32);
421
422	unsafe {
423		let raw = ll::SDL_SetVideoMode(w as c_int, h as c_int, bpp as c_int, flags);
424
425		if raw.is_null() {
426			Err(get_error())
427		} else {
428			Ok(wrap_surface(raw, false))
429		}
430	}
431}
432
433pub fn is_video_mode_ok(
434	w: isize,
435	h: isize,
436	bpp: isize,
437	surface_flags: &[SurfaceFlag],
438	video_flags: &[VideoFlag],
439) -> Option<isize> {
440	let flags = surface_flags
441		.iter()
442		.fold(0u32, |flags, &flag| flags | flag as u32);
443	let flags = video_flags
444		.iter()
445		.fold(flags, |flags, &flag| flags | flag as u32);
446
447	unsafe {
448		let bpp = ll::SDL_VideoModeOK(w as c_int, h as c_int, bpp as c_int, flags);
449
450		if bpp == 0 {
451			None
452		} else {
453			Some(bpp as isize)
454		}
455	}
456}
457
458#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
459pub enum VideoInfoFlag {
460	HWAvailable = 0x0000_0001,
461	WMAvailable = 0x0000_0002,
462	BlitHW = 0x0000_0200,
463	BlitHWColorkey = 0x0000_0400,
464	BlitHWAlpha = 0x0000_0800,
465	BlitSW = 0x0000_1000,
466	BlitSWColorkey = 0x0000_2000,
467	BlitSWAlpha = 0x0000_4000,
468	BlitFill = 0x0000_8000,
469}
470
471#[derive(Debug, PartialEq)]
472pub struct VideoInfo {
473	pub flags: Vec<VideoInfoFlag>,
474	pub width: isize,
475	pub height: isize,
476	pub format: PixelFormat,
477}
478
479fn wrap_video_info_flags(bitflags: u32) -> Vec<VideoInfoFlag> {
480	let flags = [
481		VideoInfoFlag::HWAvailable,
482		VideoInfoFlag::WMAvailable,
483		VideoInfoFlag::BlitHW,
484		VideoInfoFlag::BlitHWColorkey,
485		VideoInfoFlag::BlitHWAlpha,
486		VideoInfoFlag::BlitSW,
487		VideoInfoFlag::BlitSWColorkey,
488		VideoInfoFlag::BlitSWAlpha,
489		VideoInfoFlag::BlitFill,
490	];
491
492	flags
493		.iter()
494		.filter_map(|&flag| {
495			if bitflags & (flag as u32) != 0 {
496				Some(flag)
497			} else {
498				None
499			}
500		})
501		.collect()
502}
503
504pub fn get_video_info() -> VideoInfo {
505	let raw = unsafe { ll::SDL_GetVideoInfo() };
506	VideoInfo {
507		flags: wrap_video_info_flags(unsafe { (*raw).flags } as u32),
508		width: unsafe { (*raw).current_w } as isize,
509		height: unsafe { (*raw).current_h } as isize,
510		format: wrap_pixel_format(unsafe { (*raw).vfmt }),
511	}
512}
513
514#[derive(Copy, Clone)]
515pub enum PaletteType {
516	Logical = 1,
517	Physical,
518}
519
520pub fn get_video_surface() -> Result<Surface, String> {
521	let raw = unsafe { ll::SDL_GetVideoSurface() };
522
523	if raw.is_null() {
524		Err(get_error())
525	} else {
526		Ok(wrap_surface(raw, false))
527	}
528}
529
530// TODO: get_video_modes, get_video_driver_name
531#[allow(clippy::too_many_arguments)]
532impl Surface {
533	pub fn new(
534		surface_flags: &[SurfaceFlag],
535		width: isize,
536		height: isize,
537		bpp: isize,
538		rmask: u32,
539		gmask: u32,
540		bmask: u32,
541		amask: u32,
542	) -> Result<Surface, String> {
543		let flags = surface_flags
544			.iter()
545			.fold(0u32, |flags, flag| flags | *flag as u32);
546
547		unsafe {
548			let raw = ll::SDL_CreateRGBSurface(
549				flags,
550				width as c_int,
551				height as c_int,
552				bpp as c_int,
553				rmask,
554				gmask,
555				bmask,
556				amask,
557			);
558
559			if raw.is_null() {
560				Err(get_error())
561			} else {
562				Ok(Surface { raw, owned: true })
563			}
564		}
565	}
566
567	pub fn from_bmp(path: impl Into<String>) -> Result<Surface, String> {
568		let path = path.into();
569		let cpath = CString::new(path).unwrap();
570		let mode = CString::new("rb").unwrap();
571		let raw =
572			unsafe { ll::SDL_LoadBMP_RW(ll::SDL_RWFromFile(cpath.as_ptr(), mode.as_ptr()), 1) };
573
574		if raw.is_null() {
575			Err(get_error())
576		} else {
577			Ok(wrap_surface(raw, true))
578		}
579	}
580
581	// TODO: from_data (hard because the pixel data has to stay alive)
582
583	pub fn get_width(&self) -> u16 {
584		unsafe { (*self.raw).w as u16 }
585	}
586
587	pub fn get_height(&self) -> u16 {
588		unsafe { (*self.raw).h as u16 }
589	}
590
591	pub fn get_size(&self) -> (u16, u16) {
592		(self.get_width(), self.get_height())
593	}
594
595	pub fn get_rect(&self) -> Rect {
596		Rect {
597			x: 0,
598			y: 0,
599			w: self.get_width(),
600			h: self.get_height(),
601		}
602	}
603
604	pub fn update_rect(&self, rect: Rect) {
605		unsafe {
606			ll::SDL_UpdateRect(
607				self.raw,
608				i32::from(rect.x),
609				i32::from(rect.y),
610				u32::from(rect.w),
611				u32::from(rect.h),
612			);
613		}
614	}
615
616	pub fn update_rects(&self, rects: &[Rect]) {
617		unsafe {
618			ll::SDL_UpdateRects(self.raw, rects.len() as c_int, rects.as_ptr() as *mut Rect);
619		}
620	}
621
622	pub fn set_colors(&self, colors: &[Color]) -> bool {
623		let mut colors: Vec<_> = colors.iter().map(|color| color.to_struct()).collect();
624
625		unsafe { ll::SDL_SetColors(self.raw, colors.as_mut_ptr(), 0, colors.len() as c_int) == 1 }
626	}
627
628	pub fn set_palette(&self, palettes: &[PaletteType], colors: &[Color]) -> bool {
629		let mut colors: Vec<_> = colors.iter().map(|color| color.to_struct()).collect();
630		let flags = palettes
631			.iter()
632			.fold(0 as c_int, |flags, &flag| flags | flag as c_int);
633
634		unsafe {
635			ll::SDL_SetPalette(
636				self.raw,
637				flags,
638				colors.as_mut_ptr(),
639				0,
640				colors.len() as c_int,
641			) == 1
642		}
643	}
644
645	pub fn lock(&self) -> bool {
646		unsafe { ll::SDL_LockSurface(self.raw) == 0 }
647	}
648
649	/// Locks a surface so that the pixels can be directly accessed safely.
650	pub fn with_lock<F: Fn(&mut [u8]) -> bool>(&self, f: F) -> bool {
651		unsafe {
652			if ll::SDL_LockSurface(self.raw) != 0 {
653				panic!("could not lock surface");
654			}
655			let len = (*self.raw).pitch as usize * ((*self.raw).h as usize);
656			let pixels: &mut [u8] = mem::transmute(((*self.raw).pixels, len));
657			let rv = f(pixels);
658			ll::SDL_UnlockSurface(self.raw);
659			rv
660		}
661	}
662
663	pub fn unlock(&self) {
664		unsafe {
665			ll::SDL_UnlockSurface(self.raw);
666		}
667	}
668
669	pub fn flip(&self) -> bool {
670		unsafe { ll::SDL_Flip(self.raw) == 0 }
671	}
672
673	pub fn convert(&self, fmt: &PixelFormat, flags: &[SurfaceFlag]) -> Result<Surface, String> {
674		let flags = flags.iter().fold(0u32, |flags, &flag| flags | flag as u32);
675
676		let mut rawfmt = unwrap_pixel_format(fmt);
677
678		let new = unsafe { ll::SDL_ConvertSurface(self.raw, &mut rawfmt, flags) };
679		if new.is_null() {
680			Err(get_error())
681		} else {
682			Ok(wrap_surface(new, true))
683		}
684	}
685
686	pub fn try_clone(&self) -> Result<Surface, String> {
687		let new =
688			unsafe { ll::SDL_ConvertSurface(self.raw, (*self.raw).format, (*self.raw).flags) };
689		if new.is_null() {
690			Err(get_error())
691		} else {
692			Ok(wrap_surface(new, true))
693		}
694	}
695
696	pub fn display_format(&self) -> Result<Surface, String> {
697		let raw = unsafe { ll::SDL_DisplayFormat(self.raw) };
698
699		if raw.is_null() {
700			Err(get_error())
701		} else {
702			Ok(wrap_surface(raw, true))
703		}
704	}
705
706	pub fn display_format_alpha(&self) -> Result<Surface, String> {
707		let raw = unsafe { ll::SDL_DisplayFormatAlpha(self.raw) };
708
709		if raw.is_null() {
710			Err(get_error())
711		} else {
712			Ok(wrap_surface(raw, true))
713		}
714	}
715
716	pub fn save_bmp(&self, path: impl Into<String>) -> bool {
717		let path = path.into();
718		let cpath = CString::new(path).unwrap();
719		let mode = CString::new("wb").unwrap();
720		unsafe {
721			ll::SDL_SaveBMP_RW(
722				self.raw,
723				ll::SDL_RWFromFile(cpath.as_ptr(), mode.as_ptr()),
724				1,
725			) == 0
726		}
727	}
728
729	pub fn set_alpha(&self, flags: &[SurfaceFlag], alpha: u8) -> bool {
730		let flags = flags.iter().fold(0u32, |flags, &flag| flags | flag as u32);
731
732		unsafe { ll::SDL_SetAlpha(self.raw, flags, alpha) == 0 }
733	}
734
735	pub fn set_color_key(&self, flags: &[SurfaceFlag], color: Color) -> bool {
736		let flags = flags.iter().fold(0u32, |flags, &flag| flags | flag as u32);
737
738		unsafe {
739			ll::SDL_SetColorKey(
740				self.raw,
741				flags,
742				color.to_mapped((*self.raw).format as *const _),
743			) == 0
744		}
745	}
746
747	pub fn set_clip_rect(&self, rect: Rect) {
748		unsafe {
749			ll::SDL_SetClipRect(self.raw, &rect);
750		}
751	}
752
753	pub fn get_clip_rect(&self) -> Rect {
754		let rect = Rect {
755			x: 0,
756			y: 0,
757			w: 0,
758			h: 0,
759		};
760
761		unsafe {
762			ll::SDL_SetClipRect(self.raw, &rect as *const Rect);
763		}
764
765		rect
766	}
767
768	pub fn blit_rect(
769		&self,
770		src: &Surface,
771		src_rect: Option<Rect>,
772		dest_rect: Option<Rect>,
773	) -> bool {
774		unsafe {
775			ll::SDL_UpperBlit(
776				src.raw,
777				match src_rect {
778					Some(ref rect) => rect as *const Rect as *mut Rect,
779					None => ptr::null_mut(),
780				},
781				self.raw,
782				match dest_rect {
783					Some(ref rect) => rect as *const Rect as *mut Rect,
784					None => ptr::null_mut(),
785				},
786			) == 0
787		}
788	}
789
790	pub fn blit(&self, src: &Surface) -> bool {
791		self.blit_rect(src, None, None)
792	}
793
794	pub fn blit_at(&self, src: &Surface, x: i16, y: i16) -> bool {
795		let (w, h) = src.get_size();
796
797		self.blit_rect(src, None, Some(Rect { x, y, w, h }))
798	}
799
800	pub fn fill_rect(&self, rect: Option<Rect>, color: Color) -> bool {
801		unsafe {
802			ll::SDL_FillRect(
803				self.raw,
804				match rect {
805					Some(ref rect) => rect as *const Rect as *mut Rect,
806					None => ptr::null_mut(),
807				},
808				color.to_mapped((*self.raw).format as *const _),
809			) == 0
810		}
811	}
812
813	pub fn fill(&self, color: Color) -> bool {
814		self.fill_rect(None, color)
815	}
816
817	pub fn clear(&self) -> bool {
818		self.fill(RGB(0, 0, 0))
819	}
820
821	pub fn draw_str(&self, font: &crate::nsdl::Font, msg: &str, x: i32, y: i32) {
822		font.draw(self.raw, msg, x, y)
823	}
824}
825
826impl Clone for Surface {
827	fn clone(&self) -> Self {
828		self.try_clone().unwrap()
829	}
830}
831
832pub fn set_gamma(r: f32, g: f32, b: f32) -> bool {
833	unsafe { ll::SDL_SetGamma(r as c_float, g as c_float, b as c_float) != -1 }
834}
835
836pub fn set_gamma_ramp(r: Option<[u16; 256]>, g: Option<[u16; 256]>, b: Option<[u16; 256]>) -> bool {
837	unsafe {
838		ll::SDL_SetGammaRamp(
839			match r {
840				Some(r) => r.as_ptr(),
841				None => ptr::null(),
842			},
843			match g {
844				Some(g) => g.as_ptr(),
845				None => ptr::null(),
846			},
847			match b {
848				Some(b) => b.as_ptr(),
849				None => ptr::null(),
850			},
851		) != -1
852	}
853}
854
855pub fn get_gamma_ramp() -> ([u16; 256], [u16; 256], [u16; 256]) {
856	let mut r = [0u16; 256];
857	let mut g = [0u16; 256];
858	let mut b = [0u16; 256];
859
860	unsafe {
861		ll::SDL_GetGammaRamp(r.as_mut_ptr(), g.as_mut_ptr(), b.as_mut_ptr());
862	}
863
864	(r, g, b)
865}
866
867pub fn swap_buffers() {
868	unsafe {
869		ll::SDL_GL_SwapBuffers();
870	}
871}
872
873// TODO: YUV