1use std::marker::PhantomData;
2use std::mem;
3use std::ops::{Deref, DerefMut};
4use std::path::Path;
5use std::rc::Rc;
6
7use crate::get_error;
8use crate::pixels;
9use crate::rect::Rect;
10use crate::render::{BlendMode, Canvas};
11use crate::render::{Texture, TextureCreator, TextureValueError};
12use crate::rwops::RWops;
13use libc::c_int;
14use std::convert::TryFrom;
15use std::mem::transmute;
16use std::ptr;
17
18use crate::sys;
19
20pub struct SurfaceContext<'a> {
26    raw: *mut sys::SDL_Surface,
27    _marker: PhantomData<&'a ()>,
28}
29
30impl<'a> Drop for SurfaceContext<'a> {
31    #[inline]
32    #[doc(alias = "SDL_FreeSurface")]
33    fn drop(&mut self) {
34        unsafe {
35            sys::SDL_FreeSurface(self.raw);
36        }
37    }
38}
39
40pub struct Surface<'a> {
45    context: Rc<SurfaceContext<'a>>,
46}
47
48pub struct SurfaceRef {
53    _raw: (),
58}
59
60impl AsRef<SurfaceRef> for SurfaceRef {
61    fn as_ref(&self) -> &SurfaceRef {
62        self
63    }
64}
65
66#[test]
67fn test_surface_ref_size() {
68    assert_eq!(::std::mem::size_of::<SurfaceRef>(), 0);
70}
71
72impl<'a> Deref for Surface<'a> {
73    type Target = SurfaceRef;
74
75    #[inline]
76    fn deref(&self) -> &SurfaceRef {
77        self.as_ref()
78    }
79}
80
81impl<'a> DerefMut for Surface<'a> {
82    #[inline]
83    fn deref_mut(&mut self) -> &mut SurfaceRef {
84        self.as_mut()
85    }
86}
87
88impl<'a> AsRef<SurfaceRef> for Surface<'a> {
89    #[inline]
90    fn as_ref(&self) -> &SurfaceRef {
91        unsafe { &*(self.context.raw as *const SurfaceRef) }
92    }
93}
94
95impl<'a> AsMut<SurfaceRef> for Surface<'a> {
96    #[inline]
97    fn as_mut(&mut self) -> &mut SurfaceRef {
98        unsafe { &mut *(self.context.raw as *mut SurfaceRef) }
99    }
100}
101
102impl<'a> Surface<'a> {
103    pub unsafe fn from_ll<'b>(raw: *mut sys::SDL_Surface) -> Surface<'b> {
104        let context = SurfaceContext {
105            raw,
106            _marker: PhantomData,
107        };
108        Surface {
109            context: Rc::new(context),
110        }
111    }
112
113    pub fn new(
123        width: u32,
124        height: u32,
125        format: pixels::PixelFormatEnum,
126    ) -> Result<Surface<'static>, String> {
127        let masks = format.into_masks()?;
128        Surface::from_pixelmasks(width, height, &masks)
129    }
130
131    #[doc(alias = "SDL_CreateRGBSurface")]
142    pub fn from_pixelmasks(
143        width: u32,
144        height: u32,
145        masks: &pixels::PixelMasks,
146    ) -> Result<Surface<'static>, String> {
147        unsafe {
148            if width >= (1 << 31) || height >= (1 << 31) {
149                Err("Image is too large.".to_owned())
150            } else {
151                let raw = sys::SDL_CreateRGBSurface(
152                    0,
153                    width as c_int,
154                    height as c_int,
155                    masks.bpp as c_int,
156                    masks.rmask,
157                    masks.gmask,
158                    masks.bmask,
159                    masks.amask,
160                );
161
162                if raw.is_null() {
163                    Err(get_error())
164                } else {
165                    Ok(Surface::from_ll(raw))
166                }
167            }
168        }
169    }
170
171    pub fn from_data(
173        data: &'a mut [u8],
174        width: u32,
175        height: u32,
176        pitch: u32,
177        format: pixels::PixelFormatEnum,
178    ) -> Result<Surface<'a>, String> {
179        let masks = format.into_masks()?;
180        Surface::from_data_pixelmasks(data, width, height, pitch, &masks)
181    }
182
183    #[doc(alias = "SDL_CreateRGBSurfaceFrom")]
185    pub fn from_data_pixelmasks(
186        data: &'a mut [u8],
187        width: u32,
188        height: u32,
189        pitch: u32,
190        masks: &pixels::PixelMasks,
191    ) -> Result<Surface<'a>, String> {
192        unsafe {
193            if width >= (1 << 31) || height >= (1 << 31) {
194                Err("Image is too large.".to_owned())
195            } else if pitch >= (1 << 31) {
196                Err("Pitch is too large.".to_owned())
197            } else {
198                let raw = sys::SDL_CreateRGBSurfaceFrom(
199                    data.as_mut_ptr() as *mut libc::c_void,
200                    width as c_int,
201                    height as c_int,
202                    masks.bpp as c_int,
203                    pitch as c_int,
204                    masks.rmask,
205                    masks.gmask,
206                    masks.bmask,
207                    masks.amask,
208                );
209
210                if raw.is_null() {
211                    Err(get_error())
212                } else {
213                    Ok(Surface::from_ll(raw))
214                }
215            }
216        }
217    }
218
219    #[cfg(not(feature = "unsafe_textures"))]
246    pub fn as_texture<'b, T>(
247        &self,
248        texture_creator: &'b TextureCreator<T>,
249    ) -> Result<Texture<'b>, TextureValueError> {
250        texture_creator.create_texture_from_surface(self)
251    }
252
253    #[cfg(feature = "unsafe_textures")]
280    pub fn as_texture<T>(
281        &self,
282        texture_creator: &TextureCreator<T>,
283    ) -> Result<Texture, TextureValueError> {
284        texture_creator.create_texture_from_surface(self)
285    }
286
287    #[doc(alias = "SDL_LoadBMP_RW")]
288    pub fn load_bmp_rw(rwops: &mut RWops) -> Result<Surface<'static>, String> {
289        let raw = unsafe { sys::SDL_LoadBMP_RW(rwops.raw(), 0) };
290
291        if raw.is_null() {
292            Err(get_error())
293        } else {
294            Ok(unsafe { Surface::from_ll(raw) })
295        }
296    }
297
298    pub fn load_bmp<P: AsRef<Path>>(path: P) -> Result<Surface<'static>, String> {
299        let mut file = RWops::from_file(path, "rb")?;
300        Surface::load_bmp_rw(&mut file)
301    }
302
303    pub fn into_canvas(self) -> Result<Canvas<Surface<'a>>, String> {
311        Canvas::from_surface(self)
312    }
313
314    pub fn context(&self) -> Rc<SurfaceContext<'a>> {
315        self.context.clone()
316    }
317}
318
319impl SurfaceRef {
320    #[inline]
321    pub unsafe fn from_ll<'a>(raw: *const sys::SDL_Surface) -> &'a SurfaceRef {
322        &*(raw as *const () as *const SurfaceRef)
323    }
324
325    #[inline]
326    pub unsafe fn from_ll_mut<'a>(raw: *mut sys::SDL_Surface) -> &'a mut SurfaceRef {
327        &mut *(raw as *mut () as *mut SurfaceRef)
328    }
329
330    #[inline]
331    #[allow(clippy::trivially_copy_pass_by_ref)]
334    #[doc(alias = "SDL_Surface")]
335    pub fn raw(&self) -> *mut sys::SDL_Surface {
336        self as *const SurfaceRef as *mut SurfaceRef as *mut () as *mut sys::SDL_Surface
337    }
338
339    #[inline]
340    fn raw_ref(&self) -> &sys::SDL_Surface {
341        unsafe { &*(self as *const _ as *const () as *const sys::SDL_Surface) }
342    }
343
344    pub fn width(&self) -> u32 {
345        self.raw_ref().w as u32
346    }
347
348    pub fn height(&self) -> u32 {
349        self.raw_ref().h as u32
350    }
351
352    pub fn pitch(&self) -> u32 {
353        self.raw_ref().pitch as u32
354    }
355
356    pub fn size(&self) -> (u32, u32) {
357        (self.width(), self.height())
358    }
359
360    pub fn rect(&self) -> Rect {
362        Rect::new(0, 0, self.width(), self.height())
363    }
364
365    pub fn pixel_format(&self) -> pixels::PixelFormat {
366        unsafe { pixels::PixelFormat::from_ll(self.raw_ref().format) }
367    }
368
369    pub fn pixel_format_enum(&self) -> pixels::PixelFormatEnum {
370        pixels::PixelFormatEnum::from(self.pixel_format())
371    }
372
373    #[doc(alias = "SDL_LockSurface")]
375    pub fn with_lock<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
376        unsafe {
377            if sys::SDL_LockSurface(self.raw()) != 0 {
378                panic!("could not lock surface");
379            }
380
381            let raw_pixels = self.raw_ref().pixels as *const u8;
382            let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
383            let pixels = ::std::slice::from_raw_parts(raw_pixels, len);
384            let rv = f(pixels);
385            sys::SDL_UnlockSurface(self.raw());
386            rv
387        }
388    }
389
390    #[doc(alias = "SDL_LockSurface")]
392    pub fn with_lock_mut<R, F: FnOnce(&mut [u8]) -> R>(&mut self, f: F) -> R {
393        unsafe {
394            if sys::SDL_LockSurface(self.raw()) != 0 {
395                panic!("could not lock surface");
396            }
397
398            let raw_pixels = self.raw_ref().pixels as *mut u8;
399            let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
400            let pixels = ::std::slice::from_raw_parts_mut(raw_pixels, len);
401            let rv = f(pixels);
402            sys::SDL_UnlockSurface(self.raw());
403            rv
404        }
405    }
406
407    pub fn without_lock(&self) -> Option<&[u8]> {
410        if self.must_lock() {
411            None
412        } else {
413            unsafe {
414                let raw_pixels = self.raw_ref().pixels as *const u8;
415                let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
416
417                Some(::std::slice::from_raw_parts(raw_pixels, len))
418            }
419        }
420    }
421
422    pub fn without_lock_mut(&mut self) -> Option<&mut [u8]> {
425        if self.must_lock() {
426            None
427        } else {
428            unsafe {
429                let raw_pixels = self.raw_ref().pixels as *mut u8;
430                let len = self.raw_ref().pitch as usize * (self.raw_ref().h as usize);
431
432                Some(::std::slice::from_raw_parts_mut(raw_pixels, len))
433            }
434        }
435    }
436
437    pub fn must_lock(&self) -> bool {
439        (self.raw_ref().flags & sys::SDL_RLEACCEL) != 0
441    }
442
443    #[doc(alias = "SDL_SaveBMP_RW")]
444    pub fn save_bmp_rw(&self, rwops: &mut RWops) -> Result<(), String> {
445        let ret = unsafe { sys::SDL_SaveBMP_RW(self.raw(), rwops.raw(), 0) };
446        if ret == 0 {
447            Ok(())
448        } else {
449            Err(get_error())
450        }
451    }
452
453    pub fn save_bmp<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
454        let mut file = RWops::from_file(path, "wb")?;
455        self.save_bmp_rw(&mut file)
456    }
457
458    #[doc(alias = "SDL_SetSurfacePalette")]
459    pub fn set_palette(&mut self, palette: &pixels::Palette) -> Result<(), String> {
460        let result = unsafe { sys::SDL_SetSurfacePalette(self.raw(), palette.raw()) };
461
462        match result {
463            0 => Ok(()),
464            _ => Err(get_error()),
465        }
466    }
467
468    #[allow(non_snake_case)]
469    #[doc(alias = "SDL_SetSurfaceRLE")]
470    pub fn enable_RLE(&mut self) {
471        let result = unsafe { sys::SDL_SetSurfaceRLE(self.raw(), 1) };
472
473        if result != 0 {
474            panic!("{}", get_error());
476        }
477    }
478
479    #[allow(non_snake_case)]
480    #[doc(alias = "SDL_SetSurfaceRLE")]
481    pub fn disable_RLE(&mut self) {
482        let result = unsafe { sys::SDL_SetSurfaceRLE(self.raw(), 0) };
483
484        if result != 0 {
485            panic!("{}", get_error());
487        }
488    }
489
490    #[doc(alias = "SDL_SetColorKey")]
491    pub fn set_color_key(&mut self, enable: bool, color: pixels::Color) -> Result<(), String> {
492        let key = color.to_u32(&self.pixel_format());
493        let result = unsafe { sys::SDL_SetColorKey(self.raw(), if enable { 1 } else { 0 }, key) };
494        if result == 0 {
495            Ok(())
496        } else {
497            Err(get_error())
498        }
499    }
500
501    #[doc(alias = "SDL_GetColorKey")]
503    pub fn color_key(&self) -> Result<pixels::Color, String> {
504        let mut key = 0;
505
506        let result = unsafe { sys::SDL_GetColorKey(self.raw(), &mut key) };
509
510        if result == 0 {
511            Ok(pixels::Color::from_u32(&self.pixel_format(), key))
512        } else {
513            Err(get_error())
514        }
515    }
516
517    #[doc(alias = "SDL_SetSurfaceColorMod")]
518    pub fn set_color_mod(&mut self, color: pixels::Color) {
519        let (r, g, b) = color.rgb();
520        let result = unsafe { sys::SDL_SetSurfaceColorMod(self.raw(), r, g, b) };
521
522        if result != 0 {
523            panic!("{}", get_error());
525        }
526    }
527
528    #[doc(alias = "SDL_GetSurfaceColorMod")]
529    pub fn color_mod(&self) -> pixels::Color {
530        let mut r = 0;
531        let mut g = 0;
532        let mut b = 0;
533
534        let result =
537            unsafe { sys::SDL_GetSurfaceColorMod(self.raw(), &mut r, &mut g, &mut b) == 0 };
538
539        if result {
540            pixels::Color::RGB(r, g, b)
541        } else {
542            panic!("{}", get_error())
544        }
545    }
546
547    #[doc(alias = "SDL_FillRect")]
548    pub fn fill_rect<R>(&mut self, rect: R, color: pixels::Color) -> Result<(), String>
549    where
550        R: Into<Option<Rect>>,
551    {
552        unsafe {
553            let rect = rect.into();
554            let rect_ptr = mem::transmute(rect.as_ref()); let format = self.pixel_format();
557            let result = sys::SDL_FillRect(self.raw(), rect_ptr, color.to_u32(&format));
558            match result {
559                0 => Ok(()),
560                _ => Err(get_error()),
561            }
562        }
563    }
564
565    #[allow(clippy::clone_on_copy)]
566    pub fn fill_rects(&mut self, rects: &[Rect], color: pixels::Color) -> Result<(), String> {
567        for rect in rects.iter() {
568            self.fill_rect(rect.clone(), color)?
569        }
570
571        Ok(())
572    }
573
574    #[doc(alias = "SDL_SetSurfaceAlphaMod")]
575    pub fn set_alpha_mod(&mut self, alpha: u8) {
576        let result = unsafe { sys::SDL_SetSurfaceAlphaMod(self.raw(), alpha) };
577
578        if result != 0 {
579            panic!("{}", get_error());
581        }
582    }
583
584    #[doc(alias = "SDL_GetSurfaceAlphaMod")]
585    pub fn alpha_mod(&self) -> u8 {
586        let mut alpha = 0;
587        let result = unsafe { sys::SDL_GetSurfaceAlphaMod(self.raw(), &mut alpha) };
588
589        match result {
590            0 => alpha,
591            _ => panic!("{}", get_error()),
593        }
594    }
595
596    #[doc(alias = "SDL_SetSurfaceBlendMode")]
598    pub fn set_blend_mode(&mut self, mode: BlendMode) -> Result<(), String> {
599        let result = unsafe { sys::SDL_SetSurfaceBlendMode(self.raw(), transmute(mode)) };
600
601        match result {
602            0 => Ok(()),
603            _ => Err(get_error()),
604        }
605    }
606
607    #[doc(alias = "SDL_GetSurfaceBlendMode")]
608    pub fn blend_mode(&self) -> BlendMode {
609        let mut mode = sys::SDL_BlendMode::SDL_BLENDMODE_NONE;
610        let result = unsafe { sys::SDL_GetSurfaceBlendMode(self.raw(), &mut mode) };
611
612        match result {
613            0 => BlendMode::try_from(mode as u32).unwrap(),
614            _ => panic!("{}", get_error()),
616        }
617    }
618
619    #[doc(alias = "SDL_SetClipRect")]
623    pub fn set_clip_rect<R>(&mut self, rect: R) -> bool
624    where
625        R: Into<Option<Rect>>,
626    {
627        let rect = rect.into();
628        unsafe {
629            sys::SDL_SetClipRect(
630                self.raw(),
631                match rect {
632                    Some(rect) => rect.raw(),
633                    None => ptr::null(),
634                },
635            ) == sys::SDL_bool::SDL_TRUE
636        }
637    }
638
639    #[doc(alias = "SDL_GetClipRect")]
643    pub fn clip_rect(&self) -> Option<Rect> {
644        let mut raw = mem::MaybeUninit::uninit();
645        unsafe { sys::SDL_GetClipRect(self.raw(), raw.as_mut_ptr()) };
646        let raw = unsafe { raw.assume_init() };
647
648        if raw.w == 0 || raw.h == 0 {
649            None
650        } else {
651            Some(Rect::from_ll(raw))
652        }
653    }
654
655    #[doc(alias = "SDL_ConvertSurface")]
657    pub fn convert(&self, format: &pixels::PixelFormat) -> Result<Surface<'static>, String> {
658        let surface_ptr = unsafe { sys::SDL_ConvertSurface(self.raw(), format.raw(), 0u32) };
660
661        if surface_ptr.is_null() {
662            Err(get_error())
663        } else {
664            unsafe { Ok(Surface::from_ll(surface_ptr)) }
665        }
666    }
667
668    #[doc(alias = "SDL_ConvertSurfaceFormat")]
670    pub fn convert_format(
671        &self,
672        format: pixels::PixelFormatEnum,
673    ) -> Result<Surface<'static>, String> {
674        let surface_ptr = unsafe { sys::SDL_ConvertSurfaceFormat(self.raw(), format as u32, 0u32) };
676
677        if surface_ptr.is_null() {
678            Err(get_error())
679        } else {
680            unsafe { Ok(Surface::from_ll(surface_ptr)) }
681        }
682    }
683
684    #[doc(alias = "SDL_UpperBlit")]
688    pub fn blit<R1, R2>(
689        &self,
690        src_rect: R1,
691        dst: &mut SurfaceRef,
692        dst_rect: R2,
693    ) -> Result<Option<Rect>, String>
694    where
695        R1: Into<Option<Rect>>,
696        R2: Into<Option<Rect>>,
697    {
698        let src_rect = src_rect.into();
699        let dst_rect = dst_rect.into();
700
701        unsafe {
702            let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null());
703
704            let mut dst_rect = dst_rect;
707            let dst_rect_ptr = dst_rect
708                .as_mut()
709                .map(|r| r.raw_mut())
710                .unwrap_or(ptr::null_mut());
711            let result = sys::SDL_UpperBlit(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr);
712
713            if result == 0 {
714                Ok(dst_rect)
715            } else {
716                Err(get_error())
717            }
718        }
719    }
720
721    #[doc(alias = "SDL_LowerBlit")]
726    pub unsafe fn lower_blit<R1, R2>(
727        &self,
728        src_rect: R1,
729        dst: &mut SurfaceRef,
730        dst_rect: R2,
731    ) -> Result<(), String>
732    where
733        R1: Into<Option<Rect>>,
734        R2: Into<Option<Rect>>,
735    {
736        let src_rect = src_rect.into();
737        let dst_rect = dst_rect.into();
738
739        match {
740            let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null()) as *mut _;
742            let dst_rect_ptr = dst_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null()) as *mut _;
743            sys::SDL_LowerBlit(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
744        } {
745            0 => Ok(()),
746            _ => Err(get_error()),
747        }
748    }
749
750    #[doc(alias = "SDL_SoftStretchLinear")]
754    pub unsafe fn soft_stretch_linear<R1, R2>(
755        &self,
756        src_rect: R1,
757        dst: &mut SurfaceRef,
758        dst_rect: R2,
759    ) -> Result<Option<Rect>, String>
760    where
761        R1: Into<Option<Rect>>,
762        R2: Into<Option<Rect>>,
763    {
764        let src_rect = src_rect.into();
765        let dst_rect = dst_rect.into();
766
767        match {
768            let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null());
769
770            let mut dst_rect = dst_rect;
773            let dst_rect_ptr = dst_rect
774                .as_mut()
775                .map(|r| r.raw_mut())
776                .unwrap_or(ptr::null_mut());
777            sys::SDL_SoftStretchLinear(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
778        } {
779            0 => Ok(dst_rect),
780            _ => Err(get_error()),
781        }
782    }
783
784    #[doc(alias = "SDL_UpperBlitScaled")]
788    pub fn blit_scaled<R1, R2>(
789        &self,
790        src_rect: R1,
791        dst: &mut SurfaceRef,
792        dst_rect: R2,
793    ) -> Result<Option<Rect>, String>
794    where
795        R1: Into<Option<Rect>>,
796        R2: Into<Option<Rect>>,
797    {
798        let src_rect = src_rect.into();
799        let dst_rect = dst_rect.into();
800
801        match unsafe {
802            let src_rect_ptr = src_rect.as_ref().map(|r| r.raw()).unwrap_or(ptr::null());
803
804            let mut dst_rect = dst_rect;
807            let dst_rect_ptr = dst_rect
808                .as_mut()
809                .map(|r| r.raw_mut())
810                .unwrap_or(ptr::null_mut());
811            sys::SDL_UpperBlitScaled(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
812        } {
813            0 => Ok(dst_rect),
814            _ => Err(get_error()),
815        }
816    }
817
818    #[doc(alias = "SDL_LowerBlitScaled")]
823    pub unsafe fn lower_blit_scaled<R1, R2>(
824        &self,
825        src_rect: R1,
826        dst: &mut SurfaceRef,
827        dst_rect: R2,
828    ) -> Result<(), String>
829    where
830        R1: Into<Option<Rect>>,
831        R2: Into<Option<Rect>>,
832    {
833        match {
834            let src_rect_ptr = src_rect
836                .into()
837                .as_ref()
838                .map(|r| r.raw())
839                .unwrap_or(ptr::null()) as *mut _;
840            let dst_rect_ptr = dst_rect
841                .into()
842                .as_ref()
843                .map(|r| r.raw())
844                .unwrap_or(ptr::null()) as *mut _;
845            sys::SDL_LowerBlitScaled(self.raw(), src_rect_ptr, dst.raw(), dst_rect_ptr)
846        } {
847            0 => Ok(()),
848            _ => Err(get_error()),
849        }
850    }
851
852    }