use crate::common::{validate_int, IntegerOrSdlError};
use crate::get_error;
use crate::pixels;
use crate::rect::Point;
use crate::rect::Rect;
use crate::surface::{Surface, SurfaceContext, SurfaceRef};
use crate::sys;
use crate::video::{Window, WindowContext};
use crate::Error;
use libc::{c_double, c_int, c_void};
use pixels::PixelFormat;
use std::convert::{Into, TryFrom, TryInto};
use std::error;
use std::ffi::{c_char, CStr, CString};
use std::fmt;
#[cfg(not(feature = "unsafe_textures"))]
use std::marker::PhantomData;
use std::mem;
use std::mem::{transmute, MaybeUninit};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use std::slice;
use std::sync::Arc;
use sys::blendmode::SDL_BlendMode;
use sys::everything::SDL_PropertiesID;
use sys::render::{SDL_GetTextureProperties, SDL_TextureAccess};
use sys::surface::{SDL_FLIP_HORIZONTAL, SDL_FLIP_NONE, SDL_FLIP_VERTICAL};
#[derive(Debug, Clone)]
pub enum TargetRenderError {
SdlError(Error),
}
impl fmt::Display for TargetRenderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TargetRenderError::*;
match *self {
SdlError(ref e) => write!(f, "SDL error: {e}"),
}
}
}
impl error::Error for TargetRenderError {
fn description(&self) -> &str {
use self::TargetRenderError::*;
match self {
SdlError(e) => &e.0,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum TextureAccess {
Static = sys::render::SDL_TEXTUREACCESS_STATIC.0,
Streaming = sys::render::SDL_TEXTUREACCESS_STREAMING.0,
Target = sys::render::SDL_TEXTUREACCESS_TARGET.0,
}
impl From<TextureAccess> for sys::render::SDL_TextureAccess {
fn from(access: TextureAccess) -> sys::render::SDL_TextureAccess {
sys::render::SDL_TextureAccess(access as i32)
}
}
impl From<SDL_TextureAccess> for TextureAccess {
fn from(access: SDL_TextureAccess) -> TextureAccess {
match access {
sys::render::SDL_TEXTUREACCESS_STATIC => TextureAccess::Static,
sys::render::SDL_TEXTUREACCESS_STREAMING => TextureAccess::Streaming,
sys::render::SDL_TEXTUREACCESS_TARGET => TextureAccess::Target,
_ => panic!("Unknown texture access value: {}", access.0),
}
}
}
impl From<i64> for TextureAccess {
fn from(n: i64) -> TextureAccess {
let texture_access_c_int: std::ffi::c_int = n
.try_into()
.expect("Pixel format value out of range for c_int");
let texture_access = SDL_TextureAccess(texture_access_c_int);
texture_access.into()
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct FPoint {
pub x: f32,
pub y: f32,
}
impl FPoint {
pub fn new(x: f32, y: f32) -> FPoint {
FPoint { x, y }
}
pub fn to_ll(&self) -> sys::rect::SDL_FPoint {
sys::rect::SDL_FPoint {
x: self.x,
y: self.y,
}
}
}
impl From<Point> for FPoint {
fn from(point: Point) -> Self {
FPoint::new(point.x as f32, point.y as f32)
}
}
impl From<(f32, f32)> for FPoint {
fn from(point: (f32, f32)) -> Self {
FPoint::new(point.0, point.1)
}
}
impl From<(i32, i32)> for FPoint {
fn from(point: (i32, i32)) -> Self {
FPoint::new(point.0 as f32, point.1 as f32)
}
}
impl From<(u32, u32)> for FPoint {
fn from(point: (u32, u32)) -> Self {
FPoint::new(point.0 as f32, point.1 as f32)
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct FRect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
impl FRect {
pub fn new(x: f32, y: f32, w: f32, h: f32) -> FRect {
FRect { x, y, w, h }
}
pub fn to_ll(&self) -> sys::rect::SDL_FRect {
sys::rect::SDL_FRect {
x: self.x,
y: self.y,
w: self.w,
h: self.h,
}
}
pub fn set_x(&mut self, update: f32) {
self.x = update;
}
pub fn set_y(&mut self, update: f32) {
self.y = update;
}
pub fn set_w(&mut self, update: f32) {
self.w = update;
}
pub fn set_h(&mut self, update: f32) {
self.h = update;
}
pub fn set_xy(&mut self, update: FPoint) {
self.x = update.x;
self.y = update.y;
}
}
impl From<Rect> for FRect {
fn from(rect: Rect) -> Self {
FRect::new(rect.x as f32, rect.y as f32, rect.w as f32, rect.h as f32)
}
}
impl From<(i32, i32, u32, u32)> for FRect {
fn from(rect: (i32, i32, u32, u32)) -> Self {
FRect::new(rect.0 as f32, rect.1 as f32, rect.2 as f32, rect.3 as f32)
}
}
#[derive(Debug)]
pub struct InvalidTextureAccess(u32);
impl std::fmt::Display for InvalidTextureAccess {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Invalid texture access value: {}", self.0)
}
}
impl std::error::Error for InvalidTextureAccess {}
impl TryFrom<u32> for TextureAccess {
type Error = InvalidTextureAccess;
fn try_from(n: u32) -> Result<Self, Self::Error> {
let sdl_access = SDL_TextureAccess(n as i32);
if sdl_access == SDL_TextureAccess::STATIC {
Ok(TextureAccess::Static)
} else if sdl_access == SDL_TextureAccess::STREAMING {
Ok(TextureAccess::Streaming)
} else if sdl_access == SDL_TextureAccess::TARGET {
Ok(TextureAccess::Target)
} else {
Err(InvalidTextureAccess(n))
}
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct RendererInfo {
pub name: &'static str,
pub flags: u32,
pub texture_formats: Vec<PixelFormat>,
pub max_texture_width: u32,
pub max_texture_height: u32,
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum BlendMode {
None = sys::blendmode::SDL_BLENDMODE_NONE.0 as i32,
Blend = sys::blendmode::SDL_BLENDMODE_BLEND.0 as i32,
Add = sys::blendmode::SDL_BLENDMODE_ADD.0 as i32,
Mod = sys::blendmode::SDL_BLENDMODE_MOD.0 as i32,
Mul = sys::blendmode::SDL_BLENDMODE_MUL.0 as i32,
Invalid = sys::blendmode::SDL_BLENDMODE_INVALID.0 as i32,
}
impl From<BlendMode> for SDL_BlendMode {
fn from(value: BlendMode) -> Self {
SDL_BlendMode(value as u32)
}
}
impl TryFrom<SDL_BlendMode> for BlendMode {
type Error = ();
fn try_from(n: SDL_BlendMode) -> Result<Self, Self::Error> {
use self::BlendMode::*;
Ok(match n {
x if x == sys::blendmode::SDL_BLENDMODE_NONE => None,
x if x == sys::blendmode::SDL_BLENDMODE_BLEND => Blend,
x if x == sys::blendmode::SDL_BLENDMODE_ADD => Add,
x if x == sys::blendmode::SDL_BLENDMODE_MOD => Mod,
x if x == sys::blendmode::SDL_BLENDMODE_MUL => Mul,
x if x == sys::blendmode::SDL_BLENDMODE_INVALID => Invalid,
_ => return Err(()),
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClippingRect {
Some(Rect),
Zero,
None,
}
impl From<Rect> for ClippingRect {
fn from(val: Rect) -> Self {
ClippingRect::Some(val)
}
}
impl From<Option<Rect>> for ClippingRect {
fn from(val: Option<Rect>) -> Self {
match val {
Some(v) => v.into(),
None => ClippingRect::None,
}
}
}
impl ClippingRect {
pub fn intersection(&self, other: ClippingRect) -> ClippingRect {
match self {
ClippingRect::Zero => ClippingRect::Zero,
ClippingRect::None => other,
ClippingRect::Some(self_rect) => match other {
ClippingRect::Zero => ClippingRect::Zero,
ClippingRect::None => *self,
ClippingRect::Some(rect) => match self_rect.intersection(rect) {
Some(v) => ClippingRect::Some(v),
None => ClippingRect::Zero,
},
},
}
}
pub fn intersect_rect<R>(&self, position: R) -> ClippingRect
where
R: Into<Option<Rect>>,
{
let position: Option<Rect> = position.into();
match position {
Some(position) => {
match self {
ClippingRect::Some(rect) => match rect.intersection(position) {
Some(v) => ClippingRect::Some(v),
None => ClippingRect::Zero,
},
ClippingRect::Zero => ClippingRect::Zero,
ClippingRect::None => {
ClippingRect::Some(position)
}
}
}
None => {
ClippingRect::Zero
}
}
}
}
pub struct RendererContext<T> {
raw: *mut sys::render::SDL_Renderer,
_target: Arc<T>,
}
impl<T> Drop for RendererContext<T> {
#[doc(alias = "SDL_DestroyRenderer")]
fn drop(&mut self) {
unsafe {
sys::render::SDL_DestroyRenderer(self.raw);
};
}
}
impl<T> RendererContext<T> {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::render::SDL_Renderer {
self.raw
}
pub unsafe fn from_ll(raw: *mut sys::render::SDL_Renderer, target: Arc<T>) -> Self {
RendererContext {
raw,
_target: target,
}
}
unsafe fn set_raw_target(
&self,
raw_texture: *mut sys::render::SDL_Texture,
) -> Result<(), Error> {
if sys::render::SDL_SetRenderTarget(self.raw, raw_texture) {
Ok(())
} else {
Err(get_error())
}
}
unsafe fn get_raw_target(&self) -> *mut sys::render::SDL_Texture {
sys::render::SDL_GetRenderTarget(self.raw)
}
}
impl<T: RenderTarget> Deref for Canvas<T> {
type Target = RendererContext<T::Context>;
fn deref(&self) -> &RendererContext<T::Context> {
self.context.as_ref()
}
}
pub trait RenderTarget {
type Context;
}
impl<'s> RenderTarget for Surface<'s> {
type Context = SurfaceContext<'s>;
}
pub struct Canvas<T: RenderTarget> {
target: T,
context: Rc<RendererContext<T::Context>>,
default_pixel_format: PixelFormat,
pub renderer_name: String,
}
pub type SurfaceCanvas<'s> = Canvas<Surface<'s>>;
impl<'s> Canvas<Surface<'s>> {
#[doc(alias = "SDL_CreateSoftwareRenderer")]
pub fn from_surface(surface: Surface<'s>) -> Result<Self, Error> {
let raw_renderer = unsafe { sys::render::SDL_CreateSoftwareRenderer(surface.raw()) };
if !raw_renderer.is_null() {
let context =
Rc::new(unsafe { RendererContext::from_ll(raw_renderer, surface.context()) });
let default_pixel_format = surface.pixel_format_enum();
Ok(Canvas {
target: surface,
context,
default_pixel_format,
renderer_name: unsafe {
CStr::from_ptr(sys::render::SDL_GetRendererName(raw_renderer))
.to_string_lossy()
.into_owned()
},
})
} else {
Err(get_error())
}
}
#[inline]
pub fn surface(&self) -> &SurfaceRef {
&self.target
}
#[inline]
pub fn surface_mut(&mut self) -> &mut SurfaceRef {
&mut self.target
}
#[inline]
pub fn into_surface(self) -> Surface<'s> {
self.target
}
pub fn texture_creator(&self) -> TextureCreator<SurfaceContext<'s>> {
TextureCreator {
context: self.context.clone(),
default_pixel_format: self.default_pixel_format,
}
}
}
pub type WindowCanvas = Canvas<Window>;
impl RenderTarget for Window {
type Context = WindowContext;
}
impl Canvas<Window> {
#[inline]
pub fn window(&self) -> &Window {
&self.target
}
#[inline]
pub fn window_mut(&mut self) -> &mut Window {
&mut self.target
}
#[inline]
pub fn into_window(self) -> Window {
self.target
}
#[inline]
pub fn default_pixel_format(&self) -> PixelFormat {
self.window().window_pixel_format()
}
pub unsafe fn from_window_and_renderer(
window: Window,
renderer: *mut sys::render::SDL_Renderer,
) -> Self {
let context = Rc::new(RendererContext::from_ll(renderer, window.context()));
let default_pixel_format = window.window_pixel_format();
Canvas::<Window> {
context,
target: window,
default_pixel_format,
renderer_name: CStr::from_ptr(sys::render::SDL_GetRendererName(renderer))
.to_string_lossy()
.into_owned(),
}
}
pub fn texture_creator(&self) -> TextureCreator<WindowContext> {
TextureCreator {
context: self.context.clone(),
default_pixel_format: self.default_pixel_format(),
}
}
}
impl<T: RenderTarget> Canvas<T> {
pub fn with_texture_canvas<F>(
&mut self,
texture: &mut Texture,
f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnOnce(&'r mut Canvas<T>),
{
let target = unsafe { self.get_raw_target() };
unsafe { self.set_raw_target(texture.raw) }.map_err(TargetRenderError::SdlError)?;
f(self);
unsafe { self.set_raw_target(target) }.map_err(TargetRenderError::SdlError)?;
Ok(())
}
#[cfg(not(feature = "unsafe_textures"))]
pub fn with_multiple_texture_canvas<'t: 'a, 'a: 's, 's, I, F, U: 's>(
&mut self,
textures: I,
mut f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnMut(&'r mut Canvas<T>, &U),
I: Iterator<Item = &'s (&'a mut Texture<'t>, U)>,
{
let target = unsafe { self.get_raw_target() };
for (texture, user_context) in textures {
unsafe { self.set_raw_target(texture.raw) }.map_err(TargetRenderError::SdlError)?;
f(self, user_context);
}
unsafe { self.set_raw_target(target) }.map_err(TargetRenderError::SdlError)?;
Ok(())
}
#[cfg(feature = "unsafe_textures")]
pub fn with_multiple_texture_canvas<'a: 's, 's, I, F, U: 's>(
&mut self,
textures: I,
mut f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnMut(&'r mut Canvas<T>, &U),
I: Iterator<Item = &'s (&'a mut Texture, U)>,
{
for &(ref texture, ref user_context) in textures {
unsafe { self.set_raw_target(texture.raw) }
.map_err(|e| TargetRenderError::SdlError(e))?;
f(self, &user_context);
}
unsafe { self.set_raw_target(ptr::null_mut()) }
.map_err(|e| TargetRenderError::SdlError(e))?;
Ok(())
}
}
pub struct TextureCreator<T> {
context: Rc<RendererContext<T>>,
default_pixel_format: PixelFormat,
}
#[doc(alias = "SDL_CreateRenderer")]
pub fn create_renderer(
window: Window,
renderer_name: Option<&CStr>,
) -> Result<WindowCanvas, IntegerOrSdlError> {
use crate::common::IntegerOrSdlError::*;
let raw = unsafe {
sys::render::SDL_CreateRenderer(
window.raw(),
if let Some(renderer_name) = renderer_name {
renderer_name.as_ptr()
} else {
std::ptr::null()
},
)
};
if raw.is_null() {
Err(SdlError(get_error()))
} else {
Ok(unsafe { Canvas::from_window_and_renderer(window, raw) })
}
}
#[derive(Debug, Clone)]
pub enum TextureValueError {
WidthOverflows(u32),
HeightOverflows(u32),
WidthMustBeMultipleOfTwoForFormat(u32, PixelFormat),
SdlError(Error),
}
impl fmt::Display for TextureValueError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TextureValueError::*;
match *self {
WidthOverflows(value) => write!(f, "Integer width overflows ({value})"),
HeightOverflows(value) => write!(f, "Integer height overflows ({value})"),
WidthMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Texture width must be multiple of two for pixel format '{format:?}' ({value})"
)
}
SdlError(ref e) => write!(f, "SDL error: {e}"),
}
}
}
impl error::Error for TextureValueError {
fn description(&self) -> &str {
use self::TextureValueError::*;
match *self {
WidthOverflows(_) => "texture width overflow",
HeightOverflows(_) => "texture height overflow",
WidthMustBeMultipleOfTwoForFormat(..) => "texture width must be multiple of two",
SdlError(ref e) => &e.0,
}
}
}
#[doc(alias = "SDL_CreateTexture")]
fn ll_create_texture(
context: *mut sys::render::SDL_Renderer,
pixel_format: PixelFormat,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<*mut sys::render::SDL_Texture, TextureValueError> {
use self::TextureValueError::*;
let w = match validate_int(width, "width") {
Ok(w) => w,
Err(_) => return Err(WidthOverflows(width)),
};
let h = match validate_int(height, "height") {
Ok(h) => h,
Err(_) => return Err(HeightOverflows(height)),
};
unsafe {
match pixel_format.raw() {
sys::pixels::SDL_PIXELFORMAT_YV12 | sys::pixels::SDL_PIXELFORMAT_IYUV => {
if w % 2 != 0 || h % 2 != 0 {
return Err(WidthMustBeMultipleOfTwoForFormat(width, pixel_format));
}
}
_ => (),
};
}
Ok(
unsafe {
sys::render::SDL_CreateTexture(context, pixel_format.into(), access.into(), w, h)
},
)
}
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum ScaleMode {
Nearest = sdl3_sys::everything::SDL_ScaleMode::NEAREST.0,
Linear = sdl3_sys::everything::SDL_ScaleMode::LINEAR.0,
}
impl From<ScaleMode> for sdl3_sys::everything::SDL_ScaleMode {
fn from(val: ScaleMode) -> Self {
match val {
ScaleMode::Nearest => sdl3_sys::everything::SDL_ScaleMode::NEAREST,
ScaleMode::Linear => sdl3_sys::everything::SDL_ScaleMode::LINEAR,
}
}
}
impl TryFrom<sdl3_sys::everything::SDL_ScaleMode> for ScaleMode {
type Error = ();
fn try_from(n: sdl3_sys::everything::SDL_ScaleMode) -> Result<Self, Self::Error> {
Ok(match n {
sdl3_sys::everything::SDL_ScaleMode::NEAREST => Self::Nearest,
sdl3_sys::everything::SDL_ScaleMode::LINEAR => Self::Linear,
_ => return Err(()),
})
}
}
#[cfg_attr(not(feature = "unsafe_textures"), allow(elided_lifetimes_in_paths))]
impl<T> TextureCreator<T> {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::render::SDL_Renderer {
self.context.raw()
}
pub fn default_pixel_format(&self) -> PixelFormat {
self.default_pixel_format
}
#[cfg(not(feature = "unsafe_textures"))]
pub fn create_texture<F>(
&self,
format: F,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<Texture<'_>, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
use self::TextureValueError::*;
let format: PixelFormat = format.into().unwrap_or(self.default_pixel_format);
let result = ll_create_texture(self.context.raw(), format, access, width, height)?;
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[cfg(feature = "unsafe_textures")]
pub fn create_texture<F>(
&self,
format: F,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
use self::TextureValueError::*;
let format: PixelFormat = format.into().unwrap_or(self.default_pixel_format);
let result = ll_create_texture(self.context.raw(), format, access, width, height)?;
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[inline]
#[cfg(not(feature = "unsafe_textures"))]
pub fn create_texture_static<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture<'_>, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Static, width, height)
}
#[inline]
#[cfg(feature = "unsafe_textures")]
pub fn create_texture_static<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Static, width, height)
}
#[inline]
#[cfg(not(feature = "unsafe_textures"))]
pub fn create_texture_streaming<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture<'_>, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Streaming, width, height)
}
#[inline]
#[cfg(feature = "unsafe_textures")]
pub fn create_texture_streaming<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Streaming, width, height)
}
#[inline]
#[cfg(not(feature = "unsafe_textures"))]
pub fn create_texture_target<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture<'_>, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Target, width, height)
}
#[inline]
#[cfg(feature = "unsafe_textures")]
pub fn create_texture_target<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Target, width, height)
}
#[doc(alias = "SDL_CreateTextureFromSurface")]
#[cfg(not(feature = "unsafe_textures"))]
pub fn create_texture_from_surface<S: AsRef<SurfaceRef>>(
&self,
surface: S,
) -> Result<Texture<'_>, TextureValueError> {
use self::TextureValueError::*;
let result = unsafe {
sys::render::SDL_CreateTextureFromSurface(self.context.raw, surface.as_ref().raw())
};
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[doc(alias = "SDL_CreateTextureFromSurface")]
#[cfg(feature = "unsafe_textures")]
pub fn create_texture_from_surface<S: AsRef<SurfaceRef>>(
&self,
surface: S,
) -> Result<Texture, TextureValueError> {
use self::TextureValueError::*;
let result = unsafe {
sys::render::SDL_CreateTextureFromSurface(self.context.raw, surface.as_ref().raw())
};
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[cfg(not(feature = "unsafe_textures"))]
#[inline]
pub const unsafe fn raw_create_texture(
&self,
raw: *mut sys::render::SDL_Texture,
) -> Texture<'_> {
Texture {
raw,
_marker: PhantomData,
}
}
#[cfg(feature = "unsafe_textures")]
pub const unsafe fn raw_create_texture(&self, raw: *mut sys::render::SDL_Texture) -> Texture {
Texture { raw }
}
}
impl<T: RenderTarget> Canvas<T> {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::render::SDL_Renderer {
self.context.raw()
}
#[doc(alias = "SDL_SetRenderDrawColor")]
pub fn set_draw_color<C: Into<pixels::Color>>(&mut self, color: C) {
let (r, g, b, a) = color.into().rgba();
let ret = unsafe { sys::render::SDL_SetRenderDrawColor(self.raw, r, g, b, a) };
if !ret {
panic!("{}", get_error())
}
}
#[doc(alias = "SDL_GetRenderDrawColor")]
pub fn draw_color(&self) -> pixels::Color {
let (mut r, mut g, mut b, mut a) = (0, 0, 0, 0);
let ret = unsafe {
sys::render::SDL_GetRenderDrawColor(self.context.raw, &mut r, &mut g, &mut b, &mut a)
};
if !ret {
panic!("{}", get_error())
} else {
pixels::Color::RGBA(r, g, b, a)
}
}
#[doc(alias = "SDL_SetRenderDrawBlendMode")]
pub fn set_blend_mode(&mut self, blend: BlendMode) {
let ret =
unsafe { sys::render::SDL_SetRenderDrawBlendMode(self.context.raw, blend.into()) };
if !ret {
panic!("{}", get_error())
}
}
#[doc(alias = "SDL_GetRenderDrawBlendMode")]
pub fn blend_mode(&self) -> BlendMode {
let mut blend: MaybeUninit<SDL_BlendMode> = mem::MaybeUninit::uninit();
let ret = unsafe {
sys::render::SDL_GetRenderDrawBlendMode(self.context.raw, blend.as_mut_ptr())
};
if !ret {
panic!("{}", get_error())
} else {
let blend = unsafe { blend.assume_init() };
BlendMode::try_from(blend).unwrap()
}
}
#[doc(alias = "SDL_RenderClear")]
pub fn clear(&mut self) {
let ret = unsafe { sys::render::SDL_RenderClear(self.context.raw) };
if !ret {
panic!("Could not clear: {}", get_error())
}
}
#[doc(alias = "SDL_RenderPresent")]
pub fn present(&mut self) -> bool {
unsafe { sys::render::SDL_RenderPresent(self.context.raw) }
}
#[doc(alias = "SDL_GetCurrentRenderOutputSize")]
pub fn output_size(&self) -> Result<(u32, u32), Error> {
let mut width = 0;
let mut height = 0;
let result = unsafe {
sys::render::SDL_GetCurrentRenderOutputSize(self.context.raw, &mut width, &mut height)
};
if result {
Ok((width as u32, height as u32))
} else {
Err(get_error())
}
}
#[doc(alias = "SDL_SetRenderLogicalPresentation")]
pub fn set_logical_size(
&mut self,
width: u32,
height: u32,
mode: sys::render::SDL_RendererLogicalPresentation,
) -> Result<(), IntegerOrSdlError> {
use crate::common::IntegerOrSdlError::*;
let width = validate_int(width, "width")?;
let height = validate_int(height, "height")?;
let result = unsafe {
sys::render::SDL_SetRenderLogicalPresentation(self.context.raw, width, height, mode)
};
match result {
true => Ok(()),
false => Err(SdlError(get_error())),
}
}
#[doc(alias = "SDL_GetRenderLogicalPresentation")]
pub fn logical_size(&self) -> (u32, u32, sys::render::SDL_RendererLogicalPresentation) {
let mut width = 0;
let mut height = 0;
let mut mode: sys::render::SDL_RendererLogicalPresentation =
sys::render::SDL_LOGICAL_PRESENTATION_DISABLED;
unsafe {
sys::render::SDL_GetRenderLogicalPresentation(
self.context.raw,
&mut width,
&mut height,
&mut mode,
)
};
(width as u32, height as u32, mode)
}
#[doc(alias = "SDL_SetRenderViewport")]
pub fn set_viewport<R: Into<Option<Rect>>>(&mut self, rect: R) {
let rect = rect.into();
let ptr = rect.as_ref().map_or(ptr::null(), |rect| rect.raw());
let ret = unsafe { sys::render::SDL_SetRenderViewport(self.context.raw, ptr) };
if !ret {
panic!("Could not set viewport: {}", get_error())
}
}
#[doc(alias = "SDL_GetRenderViewport")]
pub fn viewport(&self) -> Rect {
let mut rect = mem::MaybeUninit::uninit();
unsafe { sys::render::SDL_GetRenderViewport(self.context.raw, rect.as_mut_ptr()) };
let rect = unsafe { rect.assume_init() };
Rect::from_ll(rect)
}
#[doc(alias = "SDL_SetRenderClipRect")]
pub fn set_clip_rect<R>(&mut self, arg: R)
where
R: Into<ClippingRect>,
{
let arg: ClippingRect = arg.into();
let ret = match arg {
ClippingRect::Some(r) => unsafe {
sdl3_sys::everything::SDL_SetRenderClipRect(self.context.raw, r.raw())
},
ClippingRect::Zero => {
let r = sdl3_sys::everything::SDL_Rect {
x: 0,
y: 0,
w: 0,
h: 0,
};
let r: *const sdl3_sys::everything::SDL_Rect = &r;
unsafe { sdl3_sys::everything::SDL_SetRenderClipRect(self.context.raw, r) }
}
ClippingRect::None => unsafe {
sdl3_sys::everything::SDL_SetRenderClipRect(self.context.raw, ptr::null())
},
};
if !ret {
panic!("Could not set clip rect: {}", get_error())
}
}
#[doc(alias = "SDL_GetRenderClipRect")]
pub fn clip_rect(&self) -> ClippingRect {
let clip_enabled = unsafe { sdl3_sys::everything::SDL_RenderClipEnabled(self.context.raw) };
if !clip_enabled {
return ClippingRect::None;
}
let mut raw = mem::MaybeUninit::uninit();
unsafe { sdl3_sys::everything::SDL_GetRenderClipRect(self.context.raw, raw.as_mut_ptr()) };
let raw = unsafe { raw.assume_init() };
if raw.w == 0 || raw.h == 0 {
ClippingRect::Zero
} else {
ClippingRect::Some(Rect::from_ll(raw))
}
}
#[doc(alias = "SDL_SetRenderScale")]
pub fn set_scale(&mut self, scale_x: f32, scale_y: f32) -> Result<(), Error> {
let ret = unsafe { sys::render::SDL_SetRenderScale(self.context.raw, scale_x, scale_y) };
if !ret {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_GetRenderScale")]
pub fn scale(&self) -> (f32, f32) {
let mut scale_x = 0.0;
let mut scale_y = 0.0;
unsafe { sys::render::SDL_GetRenderScale(self.context.raw, &mut scale_x, &mut scale_y) };
(scale_x, scale_y)
}
#[doc(alias = "SDL_RenderPoint")]
pub fn draw_point<P: Into<FPoint>>(&mut self, point: P) -> Result<(), Error> {
let point = point.into();
let result = unsafe { sys::render::SDL_RenderPoint(self.context.raw, point.x, point.y) };
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderPoints")]
pub fn draw_points<'a, P: Into<&'a [FPoint]>>(&mut self, points: P) -> Result<(), Error> {
let points = points.into();
let result = unsafe {
sys::render::SDL_RenderPoints(
self.context.raw,
points.as_ptr() as *const sys::rect::SDL_FPoint,
points.len() as c_int,
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderLine")]
pub fn draw_line<P1: Into<FPoint>, P2: Into<FPoint>>(
&mut self,
start: P1,
end: P2,
) -> Result<(), Error> {
let start = start.into();
let end = end.into();
let result = unsafe {
sys::render::SDL_RenderLine(self.context.raw, start.x, start.y, end.x, end.y)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderDebugText")]
pub fn draw_debug_text<P: Into<FPoint>>(
&mut self,
string: &str,
point: P,
) -> Result<(), Error> {
let string = CString::new(string).unwrap();
let point = point.into();
let result = unsafe {
sys::render::SDL_RenderDebugText(
self.context.raw,
point.x,
point.y,
string.as_ptr() as *const c_char,
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderLines")]
pub fn draw_lines<'a, P: Into<&'a [FPoint]>>(&mut self, points: P) -> Result<(), Error> {
let points = points.into();
let result = unsafe {
sys::render::SDL_RenderLines(
self.context.raw,
points
.iter()
.map(|p| p.to_ll())
.collect::<Vec<_>>()
.as_ptr(),
points.len() as c_int,
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderRect")]
pub fn draw_rect<R: Into<FRect>>(&mut self, rect: R) -> Result<(), Error> {
let rect = rect.into().to_ll();
let result = unsafe { sys::render::SDL_RenderRect(self.context.raw, &rect) };
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderRects")]
pub fn draw_rects(&mut self, rects: &[FRect]) -> Result<(), Error> {
let result = unsafe {
sys::render::SDL_RenderRects(
self.context.raw,
rects.iter().map(|r| r.to_ll()).collect::<Vec<_>>().as_ptr(),
rects.len() as c_int,
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderFillRect")]
pub fn fill_rect<R: Into<Option<FRect>>>(&mut self, rect: R) -> Result<(), Error> {
let rect_ll = rect.into().map(|r| r.to_ll());
let result = unsafe {
sys::render::SDL_RenderFillRect(
self.context.raw,
rect_ll.as_ref().map_or(ptr::null(), |r| r),
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderFillRects")]
pub fn fill_rects(&mut self, rects: &[FRect]) -> Result<(), Error> {
let result = unsafe {
sys::render::SDL_RenderFillRects(
self.context.raw,
rects.iter().map(|r| r.to_ll()).collect::<Vec<_>>().as_ptr(),
rects.len() as c_int,
)
};
if !result {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderTexture")]
pub fn copy<R1, R2>(&mut self, texture: &Texture, src: R1, dst: R2) -> Result<(), Error>
where
R1: Into<Option<FRect>>,
R2: Into<Option<FRect>>,
{
let src = src.into().map(|rect| rect.to_ll());
let dst = dst.into().map(|rect| rect.to_ll());
let ret = unsafe {
sys::render::SDL_RenderTexture(
self.context.raw,
texture.raw,
match src {
Some(ref rect) => rect,
None => ptr::null(),
},
match dst {
Some(ref rect) => rect,
None => ptr::null(),
},
)
};
if !ret {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderTextureRotated")]
pub fn copy_ex<R1, R2, P>(
&mut self,
texture: &Texture,
src: R1,
dst: R2,
angle: f64,
center: P,
flip_horizontal: bool,
flip_vertical: bool,
) -> Result<(), Error>
where
R1: Into<Option<FRect>>,
R2: Into<Option<FRect>>,
P: Into<Option<FPoint>>,
{
let flip = unsafe {
match (flip_horizontal, flip_vertical) {
(false, false) => SDL_FLIP_NONE,
(true, false) => SDL_FLIP_HORIZONTAL,
(false, true) => SDL_FLIP_VERTICAL,
(true, true) => transmute::<u32, sys::surface::SDL_FlipMode>(
transmute::<sys::surface::SDL_FlipMode, u32>(SDL_FLIP_HORIZONTAL)
| transmute::<sys::surface::SDL_FlipMode, u32>(SDL_FLIP_VERTICAL),
),
}
};
let src = src.into().map(|rect| rect.to_ll());
let dst = dst.into().map(|rect| rect.to_ll());
let center = center.into().map(|point| point.to_ll());
let ret = unsafe {
sys::render::SDL_RenderTextureRotated(
self.context.raw,
texture.raw,
match src {
Some(ref rect) => rect,
None => ptr::null(),
},
match dst {
Some(ref rect) => rect,
None => ptr::null(),
},
angle as c_double,
match center {
Some(ref point) => point,
None => ptr::null(),
},
flip,
)
};
if !ret {
Err(get_error())
} else {
Ok(())
}
}
#[doc(alias = "SDL_RenderReadPixels")]
pub fn read_pixels<R: Into<Option<Rect>>>(
&self,
rect: R,
) -> Result<Surface<'static>, Error> {
unsafe {
let rect = rect.into();
let (actual_rect, _w, _h) = match rect {
Some(ref rect) => (rect.raw(), rect.width() as usize, rect.height() as usize),
None => {
let (w, h) = self.output_size()?;
(ptr::null(), w as usize, h as usize)
}
};
let surface_ptr = sys::render::SDL_RenderReadPixels(self.context.raw, actual_rect);
if surface_ptr.is_null() {
return Err(get_error());
}
let surface = Surface::from_ll(surface_ptr);
Ok(surface)
}
}
#[cfg(feature = "unsafe_textures")]
pub fn create_texture<F>(
&self,
format: F,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
use self::TextureValueError::*;
let format: PixelFormat = format.into().unwrap_or(self.default_pixel_format);
let result = ll_create_texture(self.context.raw(), format, access, width, height)?;
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_static<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Static, width, height)
}
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_streaming<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Streaming, width, height)
}
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_target<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormat>>,
{
self.create_texture(format, TextureAccess::Target, width, height)
}
#[cfg(feature = "unsafe_textures")]
#[doc(alias = "SDL_CreateTextureFromSurface")]
pub fn create_texture_from_surface<S: AsRef<SurfaceRef>>(
&self,
surface: S,
) -> Result<Texture, TextureValueError> {
use self::TextureValueError::*;
let result = unsafe {
sys::render::SDL_CreateTextureFromSurface(self.context.raw, surface.as_ref().raw())
};
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[cfg(feature = "unsafe_textures")]
pub unsafe fn raw_create_texture(&self, raw: *mut sys::render::SDL_Texture) -> Texture {
Texture { raw }
}
#[doc(alias = "SDL_FlushRenderer")]
pub unsafe fn flush_renderer(&self) {
let ret = sys::render::SDL_FlushRenderer(self.context.raw);
if !ret {
panic!("Error flushing renderer: {}", get_error())
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Vertex {
pub position: FPoint,
pub color: pixels::FColor,
pub tex_coord: FPoint,
}
#[derive(Debug, Clone, Copy)]
pub enum VertexIndices<'a> {
Sequential,
U8(&'a [u8]),
U16(&'a [u16]),
U32(&'a [u32]),
}
impl VertexIndices<'_> {
fn into_raw(self) -> (*const c_void, c_int, c_int) {
match self {
Self::Sequential => (ptr::null(), 0, 0),
Self::U8(indices) => (indices.as_ptr().cast::<c_void>(), indices.len() as c_int, 1),
Self::U16(indices) => (indices.as_ptr().cast::<c_void>(), indices.len() as c_int, 2),
Self::U32(indices) => (indices.as_ptr().cast::<c_void>(), indices.len() as c_int, 4),
}
}
}
impl<'a> From<&'a [u8]> for VertexIndices<'a> {
fn from(value: &'a [u8]) -> Self {
Self::U8(value)
}
}
impl<'a> From<&'a [u16]> for VertexIndices<'a> {
fn from(value: &'a [u16]) -> Self {
Self::U16(value)
}
}
impl<'a> From<&'a [u32]> for VertexIndices<'a> {
fn from(value: &'a [u32]) -> Self {
Self::U32(value)
}
}
impl<'a> From<&'a [i32]> for VertexIndices<'a> {
fn from(value: &'a [i32]) -> Self {
Self::U32(unsafe { slice::from_raw_parts(value.as_ptr().cast::<u32>(), value.len()) })
}
}
impl<'a> From<&'a [[u8; 3]]> for VertexIndices<'a> {
fn from(value: &'a [[u8; 3]]) -> Self {
Self::U8(unsafe { slice::from_raw_parts(value.as_ptr().cast::<u8>(), value.len() * 3) })
}
}
impl<'a> From<&'a [[u16; 3]]> for VertexIndices<'a> {
fn from(value: &'a [[u16; 3]]) -> Self {
Self::U16(unsafe { slice::from_raw_parts(value.as_ptr().cast::<u16>(), value.len() * 3) })
}
}
impl<'a> From<&'a [[u32; 3]]> for VertexIndices<'a> {
fn from(value: &'a [[u32; 3]]) -> Self {
Self::U32(unsafe { slice::from_raw_parts(value.as_ptr().cast::<u32>(), value.len() * 3) })
}
}
impl<'a> From<&'a [[i32; 3]]> for VertexIndices<'a> {
fn from(value: &'a [[i32; 3]]) -> Self {
Self::U32(unsafe { slice::from_raw_parts(value.as_ptr().cast::<u32>(), value.len() * 3) })
}
}
macro_rules! impl_into_vertex_indices_forward {
($($ty:ty)*) => {
$(
impl<'a> From<&'a Vec<$ty>> for VertexIndices<'a> {
fn from(value: &'a Vec<$ty>) -> Self {
Self::from(value.as_slice())
}
}
impl<'a, const N: usize> From<&'a [$ty; N]> for VertexIndices<'a> {
fn from(value: &'a [$ty; N]) -> Self {
Self::from(value.as_slice())
}
}
)*
};
}
impl_into_vertex_indices_forward!(u8 u16 u32 i32 [u8; 3] [u16; 3] [u32; 3] [i32; 3]);
#[derive(Clone, Copy)]
pub struct RenderGeometryTextureParams<'a, TexCoordVertex> {
#[cfg(not(feature = "unsafe_textures"))]
pub texture: &'a Texture<'a>,
#[cfg(feature = "unsafe_textures")]
pub texture: &'a Texture,
pub tex_coords: &'a [TexCoordVertex],
pub tex_coord_offset: usize,
}
impl<T: RenderTarget> Canvas<T> {
#[doc(alias = "SDL_RenderGeometry")]
pub fn render_geometry<'a>(
&mut self,
vertices: &[Vertex],
texture: Option<&Texture>,
indices: impl Into<VertexIndices<'a>>,
) -> Result<(), Error> {
unsafe {
self.render_geometry_raw(
vertices,
mem::offset_of!(Vertex, position),
vertices,
mem::offset_of!(Vertex, color),
texture.map(|texture| RenderGeometryTextureParams {
texture,
tex_coords: vertices,
tex_coord_offset: mem::offset_of!(Vertex, tex_coord),
}),
indices,
)
}
}
#[inline]
#[doc(alias = "SDL_RenderGeometryRaw")]
pub unsafe fn render_geometry_raw<'a, PosVertex, ColorVertex, TexCoordVertex>(
&mut self,
positions: &[PosVertex],
position_offset: usize,
colors: &[ColorVertex],
color_offset: usize,
texture_params: Option<RenderGeometryTextureParams<TexCoordVertex>>,
indices: impl Into<VertexIndices<'a>>,
) -> Result<(), Error> {
let num_vertices = positions.len();
assert_eq!(num_vertices, colors.len());
assert!(position_offset + size_of::<FPoint>() <= size_of::<PosVertex>());
assert!(color_offset + size_of::<pixels::Color>() <= size_of::<ColorVertex>());
let (texture, uv, uv_stride) = if let Some(texture_params) = texture_params {
assert_eq!(num_vertices, texture_params.tex_coords.len());
assert!(
texture_params.tex_coord_offset + size_of::<FPoint>()
<= size_of::<TexCoordVertex>(),
);
(
texture_params.texture.raw,
texture_params
.tex_coords
.as_ptr()
.cast::<f32>()
.byte_offset(texture_params.tex_coord_offset.try_into().unwrap()),
size_of::<TexCoordVertex>().try_into().unwrap(),
)
} else {
(ptr::null_mut(), ptr::null(), 0)
};
let xy = positions
.as_ptr()
.cast::<f32>()
.byte_offset(position_offset.try_into().unwrap());
let xy_stride = size_of::<PosVertex>().try_into().unwrap();
let color = colors
.as_ptr()
.cast::<sys::pixels::SDL_FColor>()
.byte_offset(color_offset.try_into().unwrap());
let color_stride = size_of::<ColorVertex>().try_into().unwrap();
let (indices, num_indices, size_indices) = indices.into().into_raw();
let ret = unsafe {
sys::render::SDL_RenderGeometryRaw(
self.context.raw,
texture,
xy,
xy_stride,
color,
color_stride,
uv,
uv_stride,
num_vertices.try_into().unwrap(),
indices,
num_indices,
size_indices,
)
};
if ret {
Ok(())
} else {
Err(get_error())
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct TextureQuery {
pub format: pixels::PixelFormat,
pub access: TextureAccess,
pub width: u32,
pub height: u32,
}
#[cfg(feature = "unsafe_textures")]
pub struct Texture {
raw: *mut sys::render::SDL_Texture,
}
#[cfg(not(feature = "unsafe_textures"))]
pub struct Texture<'r> {
raw: *mut sys::render::SDL_Texture,
_marker: PhantomData<&'r ()>,
}
#[cfg(not(feature = "unsafe_textures"))]
impl Drop for Texture<'_> {
#[doc(alias = "SDL_DestroyTexture")]
fn drop(&mut self) {
unsafe {
sys::render::SDL_DestroyTexture(self.raw);
}
}
}
#[cfg(feature = "unsafe_textures")]
impl Texture {
pub unsafe fn destroy(self) {
sys::render::SDL_DestroyTexture(self.raw)
}
}
#[derive(Debug, Clone)]
pub enum UpdateTextureError {
PitchOverflows(usize),
PitchMustBeMultipleOfTwoForFormat(usize, PixelFormat),
XMustBeMultipleOfTwoForFormat(i32, PixelFormat),
YMustBeMultipleOfTwoForFormat(i32, PixelFormat),
WidthMustBeMultipleOfTwoForFormat(u32, PixelFormat),
HeightMustBeMultipleOfTwoForFormat(u32, PixelFormat),
SdlError(Error),
}
impl fmt::Display for UpdateTextureError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::UpdateTextureError::*;
match *self {
PitchOverflows(value) => write!(f, "Pitch overflows ({value})"),
PitchMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Pitch must be multiple of two for pixel format '{format:?}' ({value})"
)
}
XMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"X must be multiple of two for pixel format '{format:?}' ({value})"
)
}
YMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Y must be multiple of two for pixel format '{format:?}' ({value})"
)
}
WidthMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Width must be multiple of two for pixel format '{format:?}' ({value})"
)
}
HeightMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Height must be multiple of two for pixel format '{format:?}' ({value})"
)
}
SdlError(ref e) => write!(f, "SDL error: {e}"),
}
}
}
impl error::Error for UpdateTextureError {
fn description(&self) -> &str {
use self::UpdateTextureError::*;
match *self {
PitchOverflows(_) => "pitch overflow",
PitchMustBeMultipleOfTwoForFormat(..) => "pitch must be multiple of two",
XMustBeMultipleOfTwoForFormat(..) => "x must be multiple of two",
YMustBeMultipleOfTwoForFormat(..) => "y must be multiple of two",
WidthMustBeMultipleOfTwoForFormat(..) => "width must be multiple of two",
HeightMustBeMultipleOfTwoForFormat(..) => "height must be multiple of two",
SdlError(ref e) => &e.0,
}
}
}
#[derive(Debug, Clone)]
pub enum UpdateTextureYUVError {
PitchOverflows {
plane: &'static str,
value: usize,
},
InvalidPlaneLength {
plane: &'static str,
length: usize,
pitch: usize,
height: usize,
},
XMustBeMultipleOfTwoForFormat(i32),
YMustBeMultipleOfTwoForFormat(i32),
WidthMustBeMultipleOfTwoForFormat(u32),
HeightMustBeMultipleOfTwoForFormat(u32),
RectNotInsideTexture(Rect),
SdlError(Error),
}
impl fmt::Display for UpdateTextureYUVError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::UpdateTextureYUVError::*;
match *self {
PitchOverflows { plane, value } => {
write!(f, "Pitch overflows on {plane} plane ({value})")
}
InvalidPlaneLength {
plane,
length,
pitch,
height,
} => {
write!(
f,
"The {plane} plane is wrong length ({length}, should be {pitch} * {height})"
)
}
XMustBeMultipleOfTwoForFormat(value) => {
write!(f, "X must be multiple of two ({value})")
}
YMustBeMultipleOfTwoForFormat(value) => {
write!(f, "Y must be multiple of two ({value})")
}
WidthMustBeMultipleOfTwoForFormat(value) => {
write!(f, "Width must be multiple of two ({value})")
}
HeightMustBeMultipleOfTwoForFormat(value) => {
write!(f, "Height must be multiple of two ({value})")
}
RectNotInsideTexture(_) => write!(f, "Rect must be inside texture"),
SdlError(ref e) => write!(f, "SDL error: {e}"),
}
}
}
impl error::Error for UpdateTextureYUVError {
fn description(&self) -> &str {
use self::UpdateTextureYUVError::*;
match *self {
PitchOverflows { .. } => "pitch overflow",
InvalidPlaneLength { .. } => "invalid plane length",
XMustBeMultipleOfTwoForFormat(_) => "x must be multiple of two",
YMustBeMultipleOfTwoForFormat(_) => "y must be multiple of two",
WidthMustBeMultipleOfTwoForFormat(_) => "width must be multiple of two",
HeightMustBeMultipleOfTwoForFormat(_) => "height must be multiple of two",
RectNotInsideTexture(_) => "rect must be inside texture",
SdlError(ref e) => &e.0,
}
}
}
struct InternalTexture {
raw: *mut sys::render::SDL_Texture,
}
impl InternalTexture {
#[doc(alias = "SDL_GetTextureProperties")]
pub fn get_properties(&self) -> SDL_PropertiesID {
unsafe { SDL_GetTextureProperties(self.raw) }
}
pub fn get_format(&self) -> PixelFormat {
let format = unsafe {
sys::properties::SDL_GetNumberProperty(
self.get_properties(),
sys::render::SDL_PROP_TEXTURE_FORMAT_NUMBER,
0,
)
};
PixelFormat::from(format)
}
pub fn get_access(&self) -> TextureAccess {
let access = unsafe {
sys::properties::SDL_GetNumberProperty(
self.get_properties(),
sys::render::SDL_PROP_TEXTURE_ACCESS_NUMBER,
0,
)
};
TextureAccess::from(access)
}
pub fn get_width(&self) -> u32 {
unsafe {
sys::properties::SDL_GetNumberProperty(
self.get_properties(),
sys::render::SDL_PROP_TEXTURE_WIDTH_NUMBER,
0,
) as u32
}
}
pub fn get_height(&self) -> u32 {
unsafe {
sys::properties::SDL_GetNumberProperty(
self.get_properties(),
sys::render::SDL_PROP_TEXTURE_HEIGHT_NUMBER,
0,
) as u32
}
}
#[doc(alias = "SDL_SetTextureColorMod")]
pub fn set_color_mod(&mut self, red: u8, green: u8, blue: u8) {
let ret = unsafe { sys::render::SDL_SetTextureColorMod(self.raw, red, green, blue) };
if !ret {
panic!("Error setting color mod: {}", get_error())
}
}
#[doc(alias = "SDL_GetTextureColorMod")]
pub fn color_mod(&self) -> (u8, u8, u8) {
let (mut r, mut g, mut b) = (0, 0, 0);
let ret = unsafe { sys::render::SDL_GetTextureColorMod(self.raw, &mut r, &mut g, &mut b) };
if !ret {
panic!("{}", get_error())
} else {
(r, g, b)
}
}
#[doc(alias = "SDL_SetTextureAlphaMod")]
pub fn set_alpha_mod(&mut self, alpha: u8) {
let ret = unsafe { sys::render::SDL_SetTextureAlphaMod(self.raw, alpha) };
if !ret {
panic!("Error setting alpha mod: {}", get_error())
}
}
#[doc(alias = "SDL_GetTextureAlphaMod")]
pub fn alpha_mod(&self) -> u8 {
let mut alpha = 0;
let ret = unsafe { sys::render::SDL_GetTextureAlphaMod(self.raw, &mut alpha) };
if !ret {
panic!("{}", get_error())
} else {
alpha
}
}
#[doc(alias = "SDL_SetTextureBlendMode")]
pub fn set_blend_mode(&mut self, blend: BlendMode) {
let ret = unsafe { sys::render::SDL_SetTextureBlendMode(self.raw, blend.into()) };
if !ret {
panic!("Error setting blend: {}", get_error())
}
}
#[doc(alias = "SDL_GetTextureBlendMode")]
pub fn blend_mode(&self) -> BlendMode {
let mut blend: MaybeUninit<SDL_BlendMode> = mem::MaybeUninit::uninit();
let ret = unsafe { sys::render::SDL_GetTextureBlendMode(self.raw, blend.as_mut_ptr()) };
if !ret {
panic!("{}", get_error())
} else {
let blend = unsafe { blend.assume_init() };
BlendMode::try_from(blend).unwrap()
}
}
#[doc(alias = "SDL_SetTextureScaleMode")]
pub fn set_scale_mode(&mut self, scale: ScaleMode) {
let ret = unsafe { sdl3_sys::everything::SDL_SetTextureScaleMode(self.raw, scale.into()) };
if !ret {
panic!("Error setting scale mode: {}", get_error())
}
}
#[doc(alias = "SDL_GetTextureScaleMode")]
pub fn scale_mode(&self) -> ScaleMode {
let mut scale: MaybeUninit<sdl3_sys::everything::SDL_ScaleMode> =
mem::MaybeUninit::uninit();
let ret =
unsafe { sdl3_sys::everything::SDL_GetTextureScaleMode(self.raw, scale.as_mut_ptr()) };
if !ret {
panic!("{}", get_error())
} else {
let scale = unsafe { scale.assume_init() };
ScaleMode::try_from(scale).unwrap()
}
}
#[doc(alias = "SDL_UpdateTexture")]
pub fn update<R>(
&mut self,
rect: R,
pixel_data: &[u8],
pitch: usize,
) -> Result<(), UpdateTextureError>
where
R: Into<Option<Rect>>,
{
use self::UpdateTextureError::*;
let rect = rect.into();
let rect_raw_ptr = match rect {
Some(ref rect) => rect.raw(),
None => ptr::null(),
};
let format = self.get_format();
unsafe {
match format.raw() {
sys::pixels::SDL_PIXELFORMAT_YV12 | sys::pixels::SDL_PIXELFORMAT_IYUV => {
if let Some(r) = rect {
if r.x() % 2 != 0 {
return Err(XMustBeMultipleOfTwoForFormat(r.x(), format));
} else if r.y() % 2 != 0 {
return Err(YMustBeMultipleOfTwoForFormat(r.y(), format));
} else if !r.width().is_multiple_of(2) {
return Err(WidthMustBeMultipleOfTwoForFormat(r.width(), format));
} else if !r.height().is_multiple_of(2) {
return Err(HeightMustBeMultipleOfTwoForFormat(r.height(), format));
}
};
if !pitch.is_multiple_of(2) {
return Err(PitchMustBeMultipleOfTwoForFormat(pitch, format));
}
}
_ => {}
}
}
let pitch = match validate_int(pitch as u32, "pitch") {
Ok(p) => p,
Err(_) => return Err(PitchOverflows(pitch)),
};
let result = unsafe {
sys::render::SDL_UpdateTexture(
self.raw,
rect_raw_ptr,
pixel_data.as_ptr() as *const _,
pitch,
)
};
if !result {
Err(SdlError(get_error()))
} else {
Ok(())
}
}
#[doc(alias = "SDL_UpdateYUVTexture")]
pub fn update_yuv<R>(
&mut self,
rect: R,
y_plane: &[u8],
y_pitch: usize,
u_plane: &[u8],
u_pitch: usize,
v_plane: &[u8],
v_pitch: usize,
) -> Result<(), UpdateTextureYUVError>
where
R: Into<Option<Rect>>,
{
use self::UpdateTextureYUVError::*;
let rect = rect.into();
let rect_raw_ptr = match rect {
Some(ref rect) => rect.raw(),
None => ptr::null(),
};
if let Some(ref r) = rect {
if r.x() % 2 != 0 {
return Err(XMustBeMultipleOfTwoForFormat(r.x()));
} else if r.y() % 2 != 0 {
return Err(YMustBeMultipleOfTwoForFormat(r.y()));
} else if !r.width().is_multiple_of(2) {
return Err(WidthMustBeMultipleOfTwoForFormat(r.width()));
} else if !r.height().is_multiple_of(2) {
return Err(HeightMustBeMultipleOfTwoForFormat(r.height()));
}
};
let width_ = self.get_width();
let height_ = self.get_height();
if let Some(ref r) = rect {
let tex_rect = Rect::new(0, 0, width_, height_);
let inside = match r.intersection(tex_rect) {
Some(intersection) => intersection == *r,
None => false,
};
if !inside {
return Err(RectNotInsideTexture(*r));
}
}
let height = match rect {
Some(ref r) => r.height(),
None => height_,
} as usize;
if y_plane.len() != (y_pitch * height) {
return Err(InvalidPlaneLength {
plane: "y",
length: y_plane.len(),
pitch: y_pitch,
height,
});
}
if u_plane.len() != (u_pitch * height / 2) {
return Err(InvalidPlaneLength {
plane: "u",
length: u_plane.len(),
pitch: u_pitch,
height: height / 2,
});
}
if v_plane.len() != (v_pitch * height / 2) {
return Err(InvalidPlaneLength {
plane: "v",
length: v_plane.len(),
pitch: v_pitch,
height: height / 2,
});
}
let y_pitch = match validate_int(y_pitch as u32, "y_pitch") {
Ok(p) => p,
Err(_) => {
return Err(PitchOverflows {
plane: "y",
value: y_pitch,
})
}
};
let u_pitch = match validate_int(u_pitch as u32, "u_pitch") {
Ok(p) => p,
Err(_) => {
return Err(PitchOverflows {
plane: "u",
value: u_pitch,
})
}
};
let v_pitch = match validate_int(v_pitch as u32, "v_pitch") {
Ok(p) => p,
Err(_) => {
return Err(PitchOverflows {
plane: "v",
value: v_pitch,
})
}
};
let result = unsafe {
sys::render::SDL_UpdateYUVTexture(
self.raw,
rect_raw_ptr,
y_plane.as_ptr(),
y_pitch,
u_plane.as_ptr(),
u_pitch,
v_plane.as_ptr(),
v_pitch,
)
};
if !result {
Err(SdlError(get_error()))
} else {
Ok(())
}
}
#[doc(alias = "SDL_LockTexture")]
pub fn with_lock<F, R, R2>(&mut self, rect: R2, func: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8], usize) -> R,
R2: Into<Option<Rect>>,
{
let rect = rect.into();
let loaded = unsafe {
let mut pixels = ptr::null_mut();
let mut pitch = 0;
let height = self.get_height();
let format = self.get_format();
let (rect_raw_ptr, height) = match rect {
Some(ref rect) => (rect.raw(), rect.height() as usize),
None => (ptr::null(), height as usize),
};
let ret = sys::render::SDL_LockTexture(self.raw, rect_raw_ptr, &mut pixels, &mut pitch);
if ret {
let size = format.byte_size_from_pitch_and_height(pitch as usize, height);
Ok((
::std::slice::from_raw_parts_mut(pixels as *mut u8, size),
pitch,
))
} else {
Err(get_error())
}
};
match loaded {
Ok((interior, pitch)) => {
let result;
unsafe {
result = func(interior, pitch as usize);
sys::render::SDL_UnlockTexture(self.raw);
}
Ok(result)
}
Err(e) => Err(e),
}
}
}
#[cfg(not(feature = "unsafe_textures"))]
impl Texture<'_> {
#[inline]
pub fn query(&self) -> TextureQuery {
let internal = InternalTexture { raw: self.raw };
TextureQuery {
format: internal.get_format(),
access: internal.get_access(),
width: internal.get_width(),
height: internal.get_height(),
}
}
#[inline]
pub fn format(&self) -> PixelFormat {
InternalTexture { raw: self.raw }.get_format()
}
#[inline]
pub fn access(&self) -> TextureAccess {
InternalTexture { raw: self.raw }.get_access()
}
#[inline]
pub fn width(&self) -> u32 {
InternalTexture { raw: self.raw }.get_width()
}
#[inline]
pub fn height(&self) -> u32 {
InternalTexture { raw: self.raw }.get_height()
}
#[inline]
pub fn set_color_mod(&mut self, red: u8, green: u8, blue: u8) {
InternalTexture { raw: self.raw }.set_color_mod(red, green, blue)
}
#[inline]
pub fn color_mod(&self) -> (u8, u8, u8) {
InternalTexture { raw: self.raw }.color_mod()
}
#[inline]
pub fn set_alpha_mod(&mut self, alpha: u8) {
InternalTexture { raw: self.raw }.set_alpha_mod(alpha)
}
#[inline]
pub fn alpha_mod(&self) -> u8 {
InternalTexture { raw: self.raw }.alpha_mod()
}
#[inline]
pub fn set_blend_mode(&mut self, blend: BlendMode) {
InternalTexture { raw: self.raw }.set_blend_mode(blend)
}
#[inline]
pub fn blend_mode(&self) -> BlendMode {
InternalTexture { raw: self.raw }.blend_mode()
}
#[inline]
pub fn set_scale_mode(&mut self, scale: ScaleMode) {
InternalTexture { raw: self.raw }.set_scale_mode(scale)
}
#[inline]
pub fn scale_mode(&self) -> ScaleMode {
InternalTexture { raw: self.raw }.scale_mode()
}
#[inline]
pub fn update<R>(
&mut self,
rect: R,
pixel_data: &[u8],
pitch: usize,
) -> Result<(), UpdateTextureError>
where
R: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }.update(rect, pixel_data, pitch)
}
#[inline]
pub fn update_yuv<R>(
&mut self,
rect: R,
y_plane: &[u8],
y_pitch: usize,
u_plane: &[u8],
u_pitch: usize,
v_plane: &[u8],
v_pitch: usize,
) -> Result<(), UpdateTextureYUVError>
where
R: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }
.update_yuv(rect, y_plane, y_pitch, u_plane, u_pitch, v_plane, v_pitch)
}
#[inline]
pub fn with_lock<F, R, R2>(&mut self, rect: R2, func: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8], usize) -> R,
R2: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }.with_lock(rect, func)
}
#[inline]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub const fn raw(&self) -> *mut sys::render::SDL_Texture {
self.raw
}
#[cfg(not(feature = "unsafe_textures"))]
pub fn from_surface<'a, T>(
surface: &Surface,
texture_creator: &'a TextureCreator<T>,
) -> Result<Texture<'a>, TextureValueError> {
texture_creator.create_texture_from_surface(surface)
}
#[cfg(feature = "unsafe_textures")]
pub fn from_surface<T>(
surface: &Surface,
texture_creator: &TextureCreator<T>,
) -> Result<Texture, TextureValueError> {
texture_creator.create_texture_from_surface(surface)
}
}
#[cfg(feature = "unsafe_textures")]
impl Texture {
#[inline]
pub fn query(&self) -> TextureQuery {
let internal = InternalTexture { raw: self.raw };
TextureQuery {
format: internal.get_format(),
access: internal.get_access(),
width: internal.get_width(),
height: internal.get_height(),
}
}
#[inline]
pub fn format(&self) -> PixelFormat {
InternalTexture { raw: self.raw }.get_format()
}
#[inline]
pub fn access(&self) -> TextureAccess {
InternalTexture { raw: self.raw }.get_access()
}
#[inline]
pub fn width(&self) -> u32 {
InternalTexture { raw: self.raw }.get_width()
}
#[inline]
pub fn height(&self) -> u32 {
InternalTexture { raw: self.raw }.get_height()
}
#[inline]
pub fn set_color_mod(&mut self, red: u8, green: u8, blue: u8) {
InternalTexture { raw: self.raw }.set_color_mod(red, green, blue)
}
#[inline]
pub fn color_mod(&self) -> (u8, u8, u8) {
InternalTexture { raw: self.raw }.color_mod()
}
#[inline]
pub fn set_alpha_mod(&mut self, alpha: u8) {
InternalTexture { raw: self.raw }.set_alpha_mod(alpha)
}
#[inline]
pub fn alpha_mod(&self) -> u8 {
InternalTexture { raw: self.raw }.alpha_mod()
}
#[inline]
pub fn set_blend_mode(&mut self, blend: BlendMode) {
InternalTexture { raw: self.raw }.set_blend_mode(blend)
}
#[inline]
pub fn blend_mode(&self) -> BlendMode {
InternalTexture { raw: self.raw }.blend_mode()
}
#[inline]
pub fn set_scale_mode(&mut self, scale: ScaleMode) {
InternalTexture { raw: self.raw }.set_scale_mode(scale)
}
#[inline]
pub fn scale_mode(&self) -> ScaleMode {
InternalTexture { raw: self.raw }.scale_mode()
}
#[inline]
pub fn update<R>(
&mut self,
rect: R,
pixel_data: &[u8],
pitch: usize,
) -> Result<(), UpdateTextureError>
where
R: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }.update(rect, pixel_data, pitch)
}
#[inline]
pub fn update_yuv<R>(
&mut self,
rect: R,
y_plane: &[u8],
y_pitch: usize,
u_plane: &[u8],
u_pitch: usize,
v_plane: &[u8],
v_pitch: usize,
) -> Result<(), UpdateTextureYUVError>
where
R: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }
.update_yuv(rect, y_plane, y_pitch, u_plane, u_pitch, v_plane, v_pitch)
}
#[inline]
pub fn with_lock<F, R, R2>(&mut self, rect: R2, func: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8], usize) -> R,
R2: Into<Option<Rect>>,
{
InternalTexture { raw: self.raw }.with_lock(rect, func)
}
#[inline]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub const fn raw(&self) -> *mut sys::render::SDL_Texture {
self.raw
}
}
#[derive(Copy, Clone)]
pub struct DriverIterator {
length: i32,
index: i32,
}
impl Iterator for DriverIterator {
type Item = String;
#[inline]
#[doc(alias = "SDL_GetRenderDriver")]
fn next(&mut self) -> Option<String> {
if self.index >= self.length {
None
} else {
let result = unsafe { sys::render::SDL_GetRenderDriver(self.index) };
self.index += 1;
Some(
unsafe { CStr::from_ptr(result) }
.to_string_lossy()
.into_owned(),
)
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.length as usize;
(l, Some(l))
}
}
impl ExactSizeIterator for DriverIterator {}
#[inline]
#[doc(alias = "SDL_GetNumRenderDrivers")]
pub fn drivers() -> DriverIterator {
DriverIterator {
length: unsafe { sys::render::SDL_GetNumRenderDrivers() },
index: 0,
}
}