beryllium/video/
renderer.rs

1use crate::surface::Surface;
2
3use super::*;
4
5macro_rules! nz_is_err {
6  ($x:expr) => {{
7    let ret = $x;
8    if ret == 0 {
9      Ok(())
10    } else {
11      Err(get_error())
12    }
13  }};
14}
15
16#[derive(Clone, Copy, Default)]
17#[repr(transparent)]
18pub struct RendererFlags(SDL_RendererFlags);
19impl RendererFlags {
20  pub const SOFTWARE: Self = Self(SDL_RENDERER_SOFTWARE);
21  pub const ACCELERATED: Self = Self(SDL_RENDERER_ACCELERATED);
22  pub const VSYNC: Self = Self(SDL_RENDERER_PRESENTVSYNC);
23  pub const TARGETTEXTURE: Self = Self(SDL_RENDERER_TARGETTEXTURE);
24  //
25  pub const ACCELERATED_VSYNC: Self =
26    Self(SDL_RendererFlags(SDL_RENDERER_ACCELERATED.0 | SDL_RENDERER_PRESENTVSYNC.0));
27}
28impl core::fmt::Debug for RendererFlags {
29  #[inline]
30  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31    let mut s = f.debug_set();
32    if (self.0 .0 & Self::SOFTWARE.0 .0) != 0 {
33      s.entry(&"Software");
34    }
35    if (self.0 .0 & Self::ACCELERATED.0 .0) != 0 {
36      s.entry(&"Accelerated");
37    }
38    if (self.0 .0 & Self::VSYNC.0 .0) != 0 {
39      s.entry(&"VSync");
40    }
41    if (self.0 .0 & Self::TARGETTEXTURE.0 .0) != 0 {
42      s.entry(&"TargetTexture");
43    }
44    s.finish()
45  }
46}
47
48#[derive(Clone, Copy, Default)]
49#[repr(transparent)]
50pub struct PixelFormatEnum(SDL_PixelFormatEnum);
51impl core::fmt::Debug for PixelFormatEnum {
52  #[inline]
53  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54    let s = match self.0 {
55      SDL_PIXELFORMAT_INDEX1LSB => "INDEX1LSB",
56      SDL_PIXELFORMAT_INDEX1MSB => "INDEX1MSB",
57      SDL_PIXELFORMAT_INDEX4LSB => "INDEX4LSB",
58      SDL_PIXELFORMAT_INDEX4MSB => "INDEX4MSB",
59      SDL_PIXELFORMAT_INDEX8 => "INDEX8",
60      SDL_PIXELFORMAT_RGB332 => "RGB332",
61      SDL_PIXELFORMAT_RGB444 => "RGB444",
62      SDL_PIXELFORMAT_RGB555 => "RGB555",
63      SDL_PIXELFORMAT_BGR555 => "BGR555",
64      SDL_PIXELFORMAT_ARGB4444 => "ARGB4444",
65      SDL_PIXELFORMAT_RGBA4444 => "RGBA4444",
66      SDL_PIXELFORMAT_ABGR4444 => "ABGR4444",
67      SDL_PIXELFORMAT_BGRA4444 => "BGRA4444",
68      SDL_PIXELFORMAT_ARGB1555 => "ARGB1555",
69      SDL_PIXELFORMAT_RGBA5551 => "RGBA5551",
70      SDL_PIXELFORMAT_ABGR1555 => "ABGR1555",
71      SDL_PIXELFORMAT_BGRA5551 => "BGRA5551",
72      SDL_PIXELFORMAT_RGB565 => "RGB565",
73      SDL_PIXELFORMAT_BGR565 => "BGR565",
74      SDL_PIXELFORMAT_RGB24 => "RGB24",
75      SDL_PIXELFORMAT_BGR24 => "BGR24",
76      SDL_PIXELFORMAT_RGB888 => "RGB888",
77      SDL_PIXELFORMAT_RGBX8888 => "RGBX8888",
78      SDL_PIXELFORMAT_BGR888 => "BGR888",
79      SDL_PIXELFORMAT_BGRX8888 => "BGRX8888",
80      SDL_PIXELFORMAT_ARGB8888 => "ARGB8888",
81      SDL_PIXELFORMAT_RGBA8888 => "RGBA8888",
82      SDL_PIXELFORMAT_ABGR8888 => "ABGR8888",
83      SDL_PIXELFORMAT_BGRA8888 => "BGRA8888",
84      SDL_PIXELFORMAT_ARGB2101010 => "ARGB2101010",
85      SDL_PIXELFORMAT_YV12 => "YV12",
86      SDL_PIXELFORMAT_IYUV => "IYUV",
87      SDL_PIXELFORMAT_YUY2 => "YUY2",
88      SDL_PIXELFORMAT_UYVY => "UYVY",
89      SDL_PIXELFORMAT_YVYU => "YVYU",
90      SDL_PIXELFORMAT_NV12 => "NV12",
91      SDL_PIXELFORMAT_NV21 => "NV21",
92      _ => "?",
93    };
94    write!(f, "{s}")
95  }
96}
97
98#[derive(Debug, Clone, Default)]
99pub struct RendererInfo {
100  pub name: String,
101  pub flags: RendererFlags,
102  pub texture_formats: Vec<PixelFormatEnum>,
103  pub max_texture_width: i32,
104  pub max_texture_height: i32,
105}
106
107impl Sdl {
108  #[inline]
109  pub fn get_renderer_driver_infos(&self) -> Result<Vec<RendererInfo>, SdlError> {
110    let num_drivers = unsafe { SDL_GetNumRenderDrivers() };
111    if num_drivers < 0 {
112      return Err(get_error());
113    }
114    let mut drivers = Vec::new();
115    for driver_index in 0..num_drivers {
116      let mut raw_info = SDL_RendererInfo::default();
117      let get_result = unsafe { SDL_GetRenderDriverInfo(driver_index, &mut raw_info) };
118      if get_result < 0 {
119        return Err(get_error());
120      } else {
121        let mut info = RendererInfo::default();
122        let mut p = raw_info.name.cast::<u8>();
123        while !p.is_null() && unsafe { *p } != 0 {
124          info.name.push(unsafe { *p } as char);
125          p = unsafe { p.add(1) };
126        }
127        for format in
128          raw_info.texture_formats.iter().copied().take(raw_info.num_texture_formats as _)
129        {
130          info.texture_formats.push(PixelFormatEnum(SDL_PixelFormatEnum(format)));
131        }
132        info.flags = RendererFlags(SDL_RendererFlags(raw_info.flags));
133        info.max_texture_width = raw_info.max_texture_width;
134        info.max_texture_height = raw_info.max_texture_height;
135        drivers.push(info);
136      }
137    }
138    Ok(drivers)
139  }
140}
141
142#[derive(Clone)]
143#[repr(C)]
144struct Window {
145  win: NonNull<SDL_Window>,
146  parent: Arc<SdlInit>,
147}
148impl Deref for Window {
149  type Target = CommonWindow;
150  #[inline]
151  fn deref(&self) -> &Self::Target {
152    unsafe { &*(self as *const Self).cast::<CommonWindow>() }
153  }
154}
155impl Drop for Window {
156  #[inline]
157  fn drop(&mut self) {
158    unsafe { SDL_DestroyWindow(self.win.as_ptr()) };
159  }
160}
161
162#[derive(Clone)]
163#[repr(C)]
164struct Renderer {
165  rend: NonNull<SDL_Renderer>,
166  win: Arc<Window>,
167}
168impl Deref for Renderer {
169  type Target = NonNull<SDL_Renderer>;
170  #[inline]
171  fn deref(&self) -> &Self::Target {
172    &self.rend
173  }
174}
175impl Drop for Renderer {
176  #[inline]
177  fn drop(&mut self) {
178    unsafe { SDL_DestroyRenderer(self.rend.as_ptr()) };
179  }
180}
181
182#[repr(C)]
183pub struct RendererWindow {
184  rend: Arc<Renderer>,
185  win: Arc<Window>,
186  init: Arc<SdlInit>,
187}
188impl Deref for RendererWindow {
189  type Target = CommonWindow;
190  #[inline]
191  fn deref(&self) -> &Self::Target {
192    &self.win
193  }
194}
195impl Sdl {
196  /// You can only have one GL window active!
197  #[inline]
198  pub fn create_renderer_window(
199    &self, args: CreateWinArgs<'_>, flags: RendererFlags,
200  ) -> Result<RendererWindow, SdlError> {
201    let title_null: String = alloc::format!("{}\0", args.title);
202    let win_p: *mut SDL_Window = unsafe {
203      SDL_CreateWindow(
204        title_null.as_ptr().cast(),
205        SDL_WINDOWPOS_CENTERED,
206        SDL_WINDOWPOS_CENTERED,
207        args.width,
208        args.height,
209        args.window_flags().0,
210      )
211    };
212    let win = match NonNull::new(win_p) {
213      Some(win) => Arc::new(Window { win, parent: self.init.clone() }),
214      None => return Err(get_error()),
215    };
216    let rend_p: *mut SDL_Renderer = unsafe { SDL_CreateRenderer(win_p, -1, flags.0 .0) };
217    let rend = match NonNull::new(rend_p) {
218      Some(rend) => Arc::new(Renderer { rend, win: win.clone() }),
219      None => return Err(get_error()),
220    };
221    Ok(RendererWindow { rend, win, init: self.init.clone() })
222  }
223  /// See [SDL_ComposeCustomBlendMode](https://wiki.libsdl.org/SDL2/SDL_ComposeCustomBlendMode)
224  #[inline]
225  pub fn compose_custom_blend_mode(
226    &self, src_color: BlendFactor, dst_color: BlendFactor, color_op: BlendOperation,
227    src_alpha: BlendFactor, dst_alpha: BlendFactor, alpha_op: BlendOperation,
228  ) -> BlendMode {
229    BlendMode(unsafe {
230      SDL_ComposeCustomBlendMode(
231        SDL_BlendFactor(src_color as _),
232        SDL_BlendFactor(dst_color as _),
233        SDL_BlendOperation(color_op as _),
234        SDL_BlendFactor(src_alpha as _),
235        SDL_BlendFactor(dst_alpha as _),
236        SDL_BlendOperation(alpha_op as _),
237      )
238    })
239  }
240}
241impl RendererWindow {
242  #[inline]
243  pub fn get_renderer_info(&self) -> Result<RendererInfo, SdlError> {
244    let mut raw_info = SDL_RendererInfo::default();
245    let get_result = unsafe { SDL_GetRendererInfo(self.rend.as_ptr(), &mut raw_info) };
246    if get_result < 0 {
247      Err(get_error())
248    } else {
249      let mut info = RendererInfo::default();
250      let mut p = raw_info.name.cast::<u8>();
251      while !p.is_null() && unsafe { *p } != 0 {
252        info.name.push(unsafe { *p } as char);
253        p = unsafe { p.add(1) };
254      }
255      for format in raw_info.texture_formats.iter().copied().take(raw_info.num_texture_formats as _)
256      {
257        info.texture_formats.push(PixelFormatEnum(SDL_PixelFormatEnum(format)));
258      }
259      info.flags = RendererFlags(SDL_RendererFlags(raw_info.flags));
260      info.max_texture_width = raw_info.max_texture_width;
261      info.max_texture_height = raw_info.max_texture_height;
262      Ok(info)
263    }
264  }
265  /// See [SDL_SetRenderDrawBlendMode](https://wiki.libsdl.org/SDL2/SDL_SetRenderDrawBlendMode)
266  #[inline]
267  pub fn set_draw_blend_mode(&self, mode: BlendMode) -> Result<(), SdlError> {
268    nz_is_err!(unsafe { SDL_SetRenderDrawBlendMode(self.rend.as_ptr(), mode.0) })
269  }
270
271  /// See [SDL_CreateTexture](https://wiki.libsdl.org/SDL2/SDL_CreateTexture)
272  #[inline]
273  pub fn create_texture_from_surface(&self, surface: &Surface) -> Result<Texture, SdlError> {
274    let tex_p = unsafe { SDL_CreateTextureFromSurface(self.rend.as_ptr(), surface.surf.as_ptr()) };
275    match NonNull::new(tex_p) {
276      Some(tex) => Ok(Texture { tex, parent: self.rend.clone() }),
277      None => Err(get_error()),
278    }
279  }
280  /// See [SDL_SetRenderDrawColor](https://wiki.libsdl.org/SDL2/SDL_SetRenderDrawColor)
281  #[inline]
282  pub fn set_draw_color(&self, r: u8, g: u8, b: u8, a: u8) -> Result<(), SdlError> {
283    nz_is_err!(unsafe { SDL_SetRenderDrawColor(self.rend.as_ptr(), r, g, b, a) })
284  }
285  /// `[x, y, w, h]`. See [SDL_RenderSetClipRect](https://wiki.libsdl.org/SDL2/SDL_RenderSetClipRect)
286  #[inline]
287  pub fn set_clip_rect(&self, rect: [c_int; 4]) -> Result<(), SdlError> {
288    nz_is_err!(unsafe {
289      SDL_RenderSetClipRect(self.rend.as_ptr(), rect.as_ptr().cast::<SDL_Rect>())
290    })
291  }
292  /// See [SDL_RenderClear](https://wiki.libsdl.org/SDL2/SDL_RenderClear)
293  #[inline]
294  pub fn clear(&self) -> Result<(), SdlError> {
295    nz_is_err!(unsafe { SDL_RenderClear(self.rend.as_ptr()) })
296  }
297  /// See [SDL_RenderPresent](https://wiki.libsdl.org/SDL2/SDL_RenderPresent)
298  #[inline]
299  pub fn present(&self) {
300    unsafe { SDL_RenderPresent(self.rend.as_ptr()) }
301  }
302  /// Draws line segments using the points given.
303  ///
304  /// See [SDL_RenderDrawLines](https://wiki.libsdl.org/SDL2/SDL_RenderDrawLines)
305  #[inline]
306  pub fn draw_lines(&self, points: &[[c_int; 2]]) -> Result<(), SdlError> {
307    nz_is_err!(unsafe {
308      SDL_RenderDrawLines(
309        self.rend.as_ptr(),
310        points.as_ptr().cast::<SDL_Point>(),
311        points.len().try_into().unwrap(),
312      )
313    })
314  }
315  /// Draws the points given.
316  ///
317  /// See [SDL_RenderDrawPoints](https://wiki.libsdl.org/SDL2/SDL_RenderDrawPoints)
318  #[inline]
319  pub fn draw_points(&self, points: &[[c_int; 2]]) -> Result<(), SdlError> {
320    nz_is_err!(unsafe {
321      SDL_RenderDrawPoints(
322        self.rend.as_ptr(),
323        points.as_ptr().cast::<SDL_Point>(),
324        points.len().try_into().unwrap(),
325      )
326    })
327  }
328  /// Draws each rectangle using the `[x, y, w, h]` values given.
329  ///
330  /// See [SDL_RenderDrawRects](https://wiki.libsdl.org/SDL2/SDL_RenderDrawRects)
331  #[inline]
332  pub fn draw_rects(&self, points: &[[c_int; 4]]) -> Result<(), SdlError> {
333    nz_is_err!(unsafe {
334      SDL_RenderDrawRects(
335        self.rend.as_ptr(),
336        points.as_ptr().cast::<SDL_Rect>(),
337        points.len().try_into().unwrap(),
338      )
339    })
340  }
341  /// Fills in each rectangle using the `[x, y, w, h]` values given.
342  ///
343  /// See [SDL_RenderFillRects](https://wiki.libsdl.org/SDL2/SDL_RenderFillRects)
344  #[inline]
345  pub fn fill_rects(&self, points: &[[c_int; 4]]) -> Result<(), SdlError> {
346    nz_is_err!(unsafe {
347      SDL_RenderFillRects(
348        self.rend.as_ptr(),
349        points.as_ptr().cast::<SDL_Rect>(),
350        points.len().try_into().unwrap(),
351      )
352    })
353  }
354  /// `[x, y, w, h]`. See [SDL_RenderCopy](https://wiki.libsdl.org/SDL2/SDL_RenderCopy)
355  #[inline]
356  pub fn copy(&self, t: &Texture, src: [c_int; 4], dst: [c_int; 4]) -> Result<(), SdlError> {
357    nz_is_err!(unsafe {
358      SDL_RenderCopy(
359        self.rend.as_ptr(),
360        t.tex.as_ptr(),
361        src.as_ptr().cast::<SDL_Rect>(),
362        dst.as_ptr().cast::<SDL_Rect>(),
363      )
364    })
365  }
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369#[repr(u32)]
370pub enum BlendOperation {
371  Add = SDL_BLENDOPERATION_ADD.0,
372  Subtract = SDL_BLENDOPERATION_SUBTRACT.0,
373  RevSubtract = SDL_BLENDOPERATION_REV_SUBTRACT.0,
374  Minimum = SDL_BLENDOPERATION_MINIMUM.0,
375  Maximum = SDL_BLENDOPERATION_MAXIMUM.0,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379#[repr(u32)]
380pub enum BlendFactor {
381  Zero = SDL_BLENDFACTOR_ZERO.0,
382  One = SDL_BLENDFACTOR_ONE.0,
383  SrcColor = SDL_BLENDFACTOR_SRC_COLOR.0,
384  OneMinusSrcColor = SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR.0,
385  SrcAlpha = SDL_BLENDFACTOR_SRC_ALPHA.0,
386  OneMinusSrcAlpha = SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA.0,
387  DstColor = SDL_BLENDFACTOR_DST_COLOR.0,
388  OneMinusDstColor = SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR.0,
389  DstAlpha = SDL_BLENDFACTOR_DST_ALPHA.0,
390  OneMinusDstAlpha = SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA.0,
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
394#[repr(transparent)]
395pub struct BlendMode(SDL_BlendMode);
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq)]
398#[repr(i32)]
399pub enum TextureAccess {
400  Static = SDL_TEXTUREACCESS_STATIC.0,
401  Streaming = SDL_TEXTUREACCESS_STREAMING.0,
402  Target = SDL_TEXTUREACCESS_TARGET.0,
403}
404
405/// A texture within a renderer.
406///
407/// Make these with
408/// [`create_texture_from_surface`](RendererWindow::create_texture_from_surface).
409#[repr(C)]
410pub struct Texture {
411  tex: NonNull<SDL_Texture>,
412  parent: Arc<Renderer>,
413}
414impl Drop for Texture {
415  #[inline]
416  fn drop(&mut self) {
417    unsafe { SDL_DestroyTexture(self.tex.as_ptr()) };
418  }
419}