diff --git a/changelog.md b/changelog.md index 04c6b57c0e..6fd043b790 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,8 @@ when upgrading from a version of rust-sdl2 to another. ### v0.39.0 +[PR #1507](https://github.com/Rust-SDL2/rust-sdl2/pull/1507) **BREAKING CHANGE** Add binding for `SDL_ComposeCustomBlendMode`. This should only be a breaking change for users relying on internal details of `BlendMode` and `SDL_BlendMode`. + [PR #1510](https://github.com/Rust-SDL2/rust-sdl2/pull/1510) Fix clippy warnings. [PR #1509](https://github.com/Rust-SDL2/rust-sdl2/pull/1509) **BREAKING CHANGE** Add Send + 'static bounds for EventWatchCallback and 'static bound to AudioCallback to avoid soundness hole. diff --git a/sdl2-sys/build.rs b/sdl2-sys/build.rs index 7091b22dd4..b92d3373b8 100644 --- a/sdl2-sys/build.rs +++ b/sdl2-sys/build.rs @@ -660,6 +660,7 @@ fn generate_bindings(target: &str, host: &str, headers_paths: &[String]) { .allowlist_item("(SDL|AUDIO|RW)_.*") .bitfield_enum("SDL_RendererFlip") .newtype_enum("SDL_Keymod") + .newtype_enum("SDL_BlendMode") .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: false, }) diff --git a/sdl2-sys/sdl_bindings.rs b/sdl2-sys/sdl_bindings.rs index a19097b022..c7cba76505 100644 --- a/sdl2-sys/sdl_bindings.rs +++ b/sdl2-sys/sdl_bindings.rs @@ -2495,22 +2495,23 @@ unsafe extern "C" { Y2: *mut f32, ) -> SDL_bool; } -#[repr(u32)] -#[doc = " The blend mode used in SDL_RenderCopy() and drawing operations."] -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub enum SDL_BlendMode { +impl SDL_BlendMode { #[doc = "< no blending\ndstRGBA = srcRGBA"] - SDL_BLENDMODE_NONE = 0, + pub const SDL_BLENDMODE_NONE: SDL_BlendMode = SDL_BlendMode(0); #[doc = "< alpha blending\ndstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))\ndstA = srcA + (dstA * (1-srcA))"] - SDL_BLENDMODE_BLEND = 1, + pub const SDL_BLENDMODE_BLEND: SDL_BlendMode = SDL_BlendMode(1); #[doc = "< additive blending\ndstRGB = (srcRGB * srcA) + dstRGB\ndstA = dstA"] - SDL_BLENDMODE_ADD = 2, + pub const SDL_BLENDMODE_ADD: SDL_BlendMode = SDL_BlendMode(2); #[doc = "< color modulate\ndstRGB = srcRGB * dstRGB\ndstA = dstA"] - SDL_BLENDMODE_MOD = 4, + pub const SDL_BLENDMODE_MOD: SDL_BlendMode = SDL_BlendMode(4); #[doc = "< color multiply\ndstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA))\ndstA = dstA"] - SDL_BLENDMODE_MUL = 8, - SDL_BLENDMODE_INVALID = 2147483647, + pub const SDL_BLENDMODE_MUL: SDL_BlendMode = SDL_BlendMode(8); + pub const SDL_BLENDMODE_INVALID: SDL_BlendMode = SDL_BlendMode(2147483647); } +#[repr(transparent)] +#[doc = " The blend mode used in SDL_RenderCopy() and drawing operations."] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct SDL_BlendMode(pub libc::c_uint); #[repr(u32)] #[doc = " The blend operation used when combining source and destination pixel\n components"] #[derive(Copy, Clone, Hash, PartialEq, Eq)] diff --git a/src/sdl2/render.rs b/src/sdl2/render.rs index 7077789f93..b0ead433f3 100644 --- a/src/sdl2/render.rs +++ b/src/sdl2/render.rs @@ -1,6 +1,6 @@ //! 2D accelerated rendering //! -//! Official C documentation: https://wiki.libsdl.org/CategoryRender +//! Official C documentation: //! # Introduction //! //! This module contains functions for 2D accelerated rendering. @@ -55,9 +55,9 @@ use std::rc::Rc; use std::slice; use crate::sys; -use crate::sys::SDL_BlendMode; -use crate::sys::SDL_ScaleMode; -use crate::sys::SDL_TextureAccess; +use crate::sys::{ + SDL_BlendFactor, SDL_BlendMode, SDL_BlendOperation, SDL_ScaleMode, SDL_TextureAccess, +}; /// Contains the description of an error returned by SDL #[derive(Debug, Clone)] @@ -133,52 +133,234 @@ pub struct RendererInfo { } /// Blend mode for `Canvas`, `Texture` or `Surface`. -#[repr(i32)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub enum BlendMode { - /// no blending (replace destination with source). - None = SDL_BlendMode::SDL_BLENDMODE_NONE as i32, +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct BlendMode(pub SDL_BlendMode); + +impl fmt::Debug for BlendMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("BlendMode").field(&self.0 .0).finish() + } +} + +#[expect(non_upper_case_globals, reason = "deprecated constants")] +impl BlendMode { + /// No blending (replace destination with source). + pub const NONE: Self = Self(SDL_BlendMode::SDL_BLENDMODE_NONE); + #[deprecated( + since = "0.39.0", + note = "use NONE instead, this used to be an enum member" + )] + pub const None: Self = Self::NONE; + /// Alpha blending /// /// dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)) /// /// dstA = srcA + (dstA * (1-srcA)) - Blend = SDL_BlendMode::SDL_BLENDMODE_BLEND as i32, + pub const BLEND: Self = Self(SDL_BlendMode::SDL_BLENDMODE_BLEND); + #[deprecated( + since = "0.39.0", + note = "use BLEND instead, this used to be an enum member" + )] + pub const Blend: Self = Self::BLEND; + /// Additive blending /// /// dstRGB = (srcRGB * srcA) + dstRGB /// /// dstA = dstA (keep original alpha) - Add = SDL_BlendMode::SDL_BLENDMODE_ADD as i32, + pub const ADD: Self = Self(SDL_BlendMode::SDL_BLENDMODE_ADD); + #[deprecated( + since = "0.39.0", + note = "use ADD instead, this used to be an enum member" + )] + pub const Add: Self = Self::ADD; + /// Color modulate /// /// dstRGB = srcRGB * dstRGB - Mod = SDL_BlendMode::SDL_BLENDMODE_MOD as i32, + pub const MOD: Self = Self(SDL_BlendMode::SDL_BLENDMODE_MOD); + #[deprecated( + since = "0.39.0", + note = "use MOD instead, this used to be an enum member" + )] + pub const Mod: Self = Self::MOD; + /// Color multiply - Mul = SDL_BlendMode::SDL_BLENDMODE_MUL as i32, + pub const MUL: Self = Self(SDL_BlendMode::SDL_BLENDMODE_MUL); + #[deprecated( + since = "0.39.0", + note = "use MUL instead, this used to be an enum member" + )] + pub const Mul: Self = Self::MUL; + /// Invalid blending mode (indicates error) - Invalid = SDL_BlendMode::SDL_BLENDMODE_INVALID as i32, -} + pub const INVALID: Self = Self(SDL_BlendMode::SDL_BLENDMODE_INVALID); + #[deprecated( + since = "0.39.0", + note = "use INVALID instead, this used to be an enum member" + )] + pub const Invalid: Self = Self::INVALID; +} + +macro_rules! enum_binding { + ( + $(#[$meta:meta])* + pub enum $enum:ident = $sdl_enum:ident {$( + #[$($member_meta:meta)*] + $member:ident = $sdl_constant:ident, + )*} + ) => { + $(#[$meta])* + /// + #[doc = concat!("SDL C API type: [`", stringify!($sdl_enum), "`].")] + /// [`From`] implementations are provided to convert between this type and the C API type. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum $enum {$( + $member = $sdl_enum::$sdl_constant as isize, + )*} + + impl From<$sdl_enum> for $enum { + fn from(value: $sdl_enum) -> Self { + match value {$( + $sdl_enum::$sdl_constant => Self::$member, + )*} + } + } -impl TryFrom for BlendMode { - type Error = (); + impl From<$enum> for $sdl_enum { + fn from(value: $enum) -> Self { + match value {$( + $enum::$member => Self::$sdl_constant, + )*} + } + } + }; +} - fn try_from(n: u32) -> Result { - use self::BlendMode::*; - use crate::sys::SDL_BlendMode::*; +enum_binding! { + /// The blend operation used when combining source and destination pixel components. + pub enum BlendOperation = SDL_BlendOperation { + /// dst + src: supported by all renderers + Add = SDL_BLENDOPERATION_ADD, + /// dst - src: supported by D3D9, D3D11, OpenGL, OpenGLES + Subtract = SDL_BLENDOPERATION_SUBTRACT, + /// src - dst: supported by D3D9, D3D11, OpenGL, OpenGLES + RevSubtract = SDL_BLENDOPERATION_REV_SUBTRACT, + /// min(dst, src): supported by D3D9, D3D11 + Minimum = SDL_BLENDOPERATION_MINIMUM, + /// max(dst, src): supported by D3D9, D3D11 + Maximum = SDL_BLENDOPERATION_MAXIMUM, + } +} + +enum_binding! { + /// The normalized factor used to multiply pixel components. + pub enum BlendFactor = SDL_BlendFactor { + /// 0, 0, 0, 0 + Zero = SDL_BLENDFACTOR_ZERO, + /// 1, 1, 1, 1 + One = SDL_BLENDFACTOR_ONE, + /// srcR, srcG, srcB, srcA + SrcColor = SDL_BLENDFACTOR_SRC_COLOR, + /// 1-srcR, 1-srcG, 1-srcB, 1-srcA + OneMinusSrcColor = SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + /// srcA, srcA, srcA, srcA + SrcAlpha = SDL_BLENDFACTOR_SRC_ALPHA, + /// 1-srcA, 1-srcA, 1-srcA, 1-srcA + OneMinusSrcAlpha = SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + /// dstR, dstG, dstB, dstA + DstColor = SDL_BLENDFACTOR_DST_COLOR, + /// 1-dstR, 1-dstG, 1-dstB, 1-dstA + OneMinusDstColor = SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR, + /// dstA, dstA, dstA, dstA + DstAlpha = SDL_BLENDFACTOR_DST_ALPHA, + /// 1-dstA, 1-dstA, 1-dstA, 1-dstA + OneMinusDstAlpha = SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + } +} + +/// Arguments to [`SDL_ComposeCustomBlendMode`][sys::SDL_ComposeCustomBlendMode]. +/// +/// This struct implements [`Default`] with a blend mode equivalent to [`BlendMode::NONE`]. +/// Struct update syntax ([Rust Book][book-struct-update], [Rust Reference][reference-struct-update]) can be used to only change a few arguments. +/// +/// See [`Self::compose`] for examples. +/// +/// [book-struct-update]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax +/// [reference-struct-update]: https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax +#[derive(Debug, Clone, Copy)] +pub struct CustomBlendMode { + pub src_color_factor: BlendFactor, + pub dst_color_factor: BlendFactor, + pub color_operation: BlendOperation, + pub src_alpha_factor: BlendFactor, + pub dst_alpha_factor: BlendFactor, + pub alpha_operation: BlendOperation, +} - match n { - x if x == SDL_BLENDMODE_NONE as u32 => Ok(None), - x if x == SDL_BLENDMODE_BLEND as u32 => Ok(Blend), - x if x == SDL_BLENDMODE_ADD as u32 => Ok(Add), - x if x == SDL_BLENDMODE_MOD as u32 => Ok(Mod), - x if x == SDL_BLENDMODE_MUL as u32 => Ok(Mul), - x if x == SDL_BLENDMODE_INVALID as u32 => Ok(Invalid), - _ => Err(()), +impl Default for CustomBlendMode { + fn default() -> Self { + Self { + src_color_factor: BlendFactor::One, + dst_color_factor: BlendFactor::Zero, + color_operation: BlendOperation::Add, + src_alpha_factor: BlendFactor::One, + dst_alpha_factor: BlendFactor::Zero, + alpha_operation: BlendOperation::Add, } } } +impl CustomBlendMode { + /// Compose a custom blend mode for renderers. + /// + /// # Examples + /// + /// ``` + /// use sdl2::render::{BlendFactor, BlendOperation, CustomBlendMode}; + /// + /// let no_blending = CustomBlendMode { + /// src_color_factor: BlendFactor::One, + /// dst_color_factor: BlendFactor::Zero, + /// color_operation: BlendOperation::Add, + /// src_alpha_factor: BlendFactor::One, + /// dst_alpha_factor: BlendFactor::Zero, + /// alpha_operation: BlendOperation::Add, + /// }.compose(); + /// + /// let ignore_alpha = CustomBlendMode { + /// src_alpha_factor: BlendFactor::Zero, + /// dst_alpha_factor: BlendFactor::One, + /// ..Default::default() + /// }.compose(); + /// ``` + /// + /// See [`SDL_ComposeCustomBlendMode`][sys::SDL_ComposeCustomBlendMode]. + #[doc(alias = "SDL_ComposeCustomBlendMode")] + pub fn compose(self) -> BlendMode { + let Self { + src_color_factor, + dst_color_factor, + color_operation, + src_alpha_factor, + dst_alpha_factor, + alpha_operation, + } = self; + + BlendMode(unsafe { + sys::SDL_ComposeCustomBlendMode( + src_color_factor.into(), + dst_color_factor.into(), + color_operation.into(), + src_alpha_factor.into(), + dst_alpha_factor.into(), + alpha_operation.into(), + ) + }) + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum ScaleMode { /// nearest pixel sampling. default @@ -1098,8 +1280,7 @@ impl Canvas { /// Sets the blend mode used for drawing operations (Fill and Line). #[doc(alias = "SDL_SetRenderDrawBlendMode")] pub fn set_blend_mode(&mut self, blend: BlendMode) { - let ret = - unsafe { sys::SDL_SetRenderDrawBlendMode(self.context.raw, transmute(blend as u32)) }; + let ret = unsafe { sys::SDL_SetRenderDrawBlendMode(self.context.raw, blend.0) }; // Should only fail on an invalid renderer if ret != 0 { panic!("{}", get_error()) @@ -1115,8 +1296,7 @@ impl Canvas { if ret != 0 { panic!("{}", get_error()) } else { - let blend = unsafe { blend.assume_init() }; - BlendMode::try_from(blend as u32).unwrap() + BlendMode(unsafe { blend.assume_init() }) } } @@ -2541,7 +2721,7 @@ impl InternalTexture { #[doc(alias = "SDL_SetTextureBlendMode")] pub fn set_blend_mode(&mut self, blend: BlendMode) { - let ret = unsafe { sys::SDL_SetTextureBlendMode(self.raw, transmute(blend as u32)) }; + let ret = unsafe { sys::SDL_SetTextureBlendMode(self.raw, blend.0) }; if ret != 0 { panic!("Error setting blend: {}", get_error()) @@ -2557,8 +2737,7 @@ impl InternalTexture { if ret != 0 { panic!("{}", get_error()) } else { - let blend = unsafe { blend.assume_init() }; - BlendMode::try_from(blend as u32).unwrap() + BlendMode(unsafe { blend.assume_init() }) } } diff --git a/src/sdl2/surface.rs b/src/sdl2/surface.rs index eb633413f2..c49d62c421 100644 --- a/src/sdl2/surface.rs +++ b/src/sdl2/surface.rs @@ -11,8 +11,6 @@ use crate::render::{BlendMode, Canvas}; use crate::render::{Texture, TextureCreator, TextureValueError}; use crate::rwops::RWops; use libc::c_int; -use std::convert::TryFrom; -use std::mem::transmute; use std::ptr; use crate::sys; @@ -595,8 +593,8 @@ impl SurfaceRef { /// The function will fail if the blend mode is not supported by SDL. #[doc(alias = "SDL_SetSurfaceBlendMode")] - pub fn set_blend_mode(&mut self, mode: BlendMode) -> Result<(), String> { - let result = unsafe { sys::SDL_SetSurfaceBlendMode(self.raw(), transmute(mode)) }; + pub fn set_blend_mode(&mut self, blend: BlendMode) -> Result<(), String> { + let result = unsafe { sys::SDL_SetSurfaceBlendMode(self.raw(), blend.0) }; match result { 0 => Ok(()), @@ -606,11 +604,11 @@ impl SurfaceRef { #[doc(alias = "SDL_GetSurfaceBlendMode")] pub fn blend_mode(&self) -> BlendMode { - let mut mode = sys::SDL_BlendMode::SDL_BLENDMODE_NONE; - let result = unsafe { sys::SDL_GetSurfaceBlendMode(self.raw(), &mut mode) }; + let mut blend: mem::MaybeUninit = mem::MaybeUninit::uninit(); + let result = unsafe { sys::SDL_GetSurfaceBlendMode(self.raw(), blend.as_mut_ptr()) }; match result { - 0 => BlendMode::try_from(mode as u32).unwrap(), + 0 => BlendMode(unsafe { blend.assume_init() }), // Should only fail on a null Surface _ => panic!("{}", get_error()), }