Skip to content

Commit 0545cad

Browse files
authored
Compose blend mode (#1507)
* add binding for SDL_ComposeCustomBlendMode * update changelog * change is technically breaking
1 parent 5dbf6e5 commit 0545cad

File tree

5 files changed

+233
-52
lines changed

5 files changed

+233
-52
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ when upgrading from a version of rust-sdl2 to another.
33

44
### v0.39.0
55

6+
[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`.
7+
68
[PR #1510](https://github.com/Rust-SDL2/rust-sdl2/pull/1510) Fix clippy warnings.
79

810
[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.

sdl2-sys/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ fn generate_bindings(target: &str, host: &str, headers_paths: &[String]) {
660660
.allowlist_item("(SDL|AUDIO|RW)_.*")
661661
.bitfield_enum("SDL_RendererFlip")
662662
.newtype_enum("SDL_Keymod")
663+
.newtype_enum("SDL_BlendMode")
663664
.default_enum_style(bindgen::EnumVariation::Rust {
664665
non_exhaustive: false,
665666
})

sdl2-sys/sdl_bindings.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,22 +2495,23 @@ unsafe extern "C" {
24952495
Y2: *mut f32,
24962496
) -> SDL_bool;
24972497
}
2498-
#[repr(u32)]
2499-
#[doc = " The blend mode used in SDL_RenderCopy() and drawing operations."]
2500-
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
2501-
pub enum SDL_BlendMode {
2498+
impl SDL_BlendMode {
25022499
#[doc = "< no blending\ndstRGBA = srcRGBA"]
2503-
SDL_BLENDMODE_NONE = 0,
2500+
pub const SDL_BLENDMODE_NONE: SDL_BlendMode = SDL_BlendMode(0);
25042501
#[doc = "< alpha blending\ndstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))\ndstA = srcA + (dstA * (1-srcA))"]
2505-
SDL_BLENDMODE_BLEND = 1,
2502+
pub const SDL_BLENDMODE_BLEND: SDL_BlendMode = SDL_BlendMode(1);
25062503
#[doc = "< additive blending\ndstRGB = (srcRGB * srcA) + dstRGB\ndstA = dstA"]
2507-
SDL_BLENDMODE_ADD = 2,
2504+
pub const SDL_BLENDMODE_ADD: SDL_BlendMode = SDL_BlendMode(2);
25082505
#[doc = "< color modulate\ndstRGB = srcRGB * dstRGB\ndstA = dstA"]
2509-
SDL_BLENDMODE_MOD = 4,
2506+
pub const SDL_BLENDMODE_MOD: SDL_BlendMode = SDL_BlendMode(4);
25102507
#[doc = "< color multiply\ndstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA))\ndstA = dstA"]
2511-
SDL_BLENDMODE_MUL = 8,
2512-
SDL_BLENDMODE_INVALID = 2147483647,
2508+
pub const SDL_BLENDMODE_MUL: SDL_BlendMode = SDL_BlendMode(8);
2509+
pub const SDL_BLENDMODE_INVALID: SDL_BlendMode = SDL_BlendMode(2147483647);
25132510
}
2511+
#[repr(transparent)]
2512+
#[doc = " The blend mode used in SDL_RenderCopy() and drawing operations."]
2513+
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
2514+
pub struct SDL_BlendMode(pub libc::c_uint);
25142515
#[repr(u32)]
25152516
#[doc = " The blend operation used when combining source and destination pixel\n components"]
25162517
#[derive(Copy, Clone, Hash, PartialEq, Eq)]

src/sdl2/render.rs

Lines changed: 214 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! 2D accelerated rendering
22
//!
3-
//! Official C documentation: https://wiki.libsdl.org/CategoryRender
3+
//! Official C documentation: <https://wiki.libsdl.org/SDL2/CategoryRender>
44
//! # Introduction
55
//!
66
//! This module contains functions for 2D accelerated rendering.
@@ -55,9 +55,9 @@ use std::rc::Rc;
5555
use std::slice;
5656

5757
use crate::sys;
58-
use crate::sys::SDL_BlendMode;
59-
use crate::sys::SDL_ScaleMode;
60-
use crate::sys::SDL_TextureAccess;
58+
use crate::sys::{
59+
SDL_BlendFactor, SDL_BlendMode, SDL_BlendOperation, SDL_ScaleMode, SDL_TextureAccess,
60+
};
6161

6262
/// Contains the description of an error returned by SDL
6363
#[derive(Debug, Clone)]
@@ -133,52 +133,234 @@ pub struct RendererInfo {
133133
}
134134

135135
/// Blend mode for `Canvas`, `Texture` or `Surface`.
136-
#[repr(i32)]
137-
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
138-
pub enum BlendMode {
139-
/// no blending (replace destination with source).
140-
None = SDL_BlendMode::SDL_BLENDMODE_NONE as i32,
136+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
137+
pub struct BlendMode(pub SDL_BlendMode);
138+
139+
impl fmt::Debug for BlendMode {
140+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141+
f.debug_tuple("BlendMode").field(&self.0 .0).finish()
142+
}
143+
}
144+
145+
#[expect(non_upper_case_globals, reason = "deprecated constants")]
146+
impl BlendMode {
147+
/// No blending (replace destination with source).
148+
pub const NONE: Self = Self(SDL_BlendMode::SDL_BLENDMODE_NONE);
149+
#[deprecated(
150+
since = "0.39.0",
151+
note = "use NONE instead, this used to be an enum member"
152+
)]
153+
pub const None: Self = Self::NONE;
154+
141155
/// Alpha blending
142156
///
143157
/// dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
144158
///
145159
/// dstA = srcA + (dstA * (1-srcA))
146-
Blend = SDL_BlendMode::SDL_BLENDMODE_BLEND as i32,
160+
pub const BLEND: Self = Self(SDL_BlendMode::SDL_BLENDMODE_BLEND);
161+
#[deprecated(
162+
since = "0.39.0",
163+
note = "use BLEND instead, this used to be an enum member"
164+
)]
165+
pub const Blend: Self = Self::BLEND;
166+
147167
/// Additive blending
148168
///
149169
/// dstRGB = (srcRGB * srcA) + dstRGB
150170
///
151171
/// dstA = dstA (keep original alpha)
152-
Add = SDL_BlendMode::SDL_BLENDMODE_ADD as i32,
172+
pub const ADD: Self = Self(SDL_BlendMode::SDL_BLENDMODE_ADD);
173+
#[deprecated(
174+
since = "0.39.0",
175+
note = "use ADD instead, this used to be an enum member"
176+
)]
177+
pub const Add: Self = Self::ADD;
178+
153179
/// Color modulate
154180
///
155181
/// dstRGB = srcRGB * dstRGB
156-
Mod = SDL_BlendMode::SDL_BLENDMODE_MOD as i32,
182+
pub const MOD: Self = Self(SDL_BlendMode::SDL_BLENDMODE_MOD);
183+
#[deprecated(
184+
since = "0.39.0",
185+
note = "use MOD instead, this used to be an enum member"
186+
)]
187+
pub const Mod: Self = Self::MOD;
188+
157189
/// Color multiply
158-
Mul = SDL_BlendMode::SDL_BLENDMODE_MUL as i32,
190+
pub const MUL: Self = Self(SDL_BlendMode::SDL_BLENDMODE_MUL);
191+
#[deprecated(
192+
since = "0.39.0",
193+
note = "use MUL instead, this used to be an enum member"
194+
)]
195+
pub const Mul: Self = Self::MUL;
196+
159197
/// Invalid blending mode (indicates error)
160-
Invalid = SDL_BlendMode::SDL_BLENDMODE_INVALID as i32,
161-
}
198+
pub const INVALID: Self = Self(SDL_BlendMode::SDL_BLENDMODE_INVALID);
199+
#[deprecated(
200+
since = "0.39.0",
201+
note = "use INVALID instead, this used to be an enum member"
202+
)]
203+
pub const Invalid: Self = Self::INVALID;
204+
}
205+
206+
macro_rules! enum_binding {
207+
(
208+
$(#[$meta:meta])*
209+
pub enum $enum:ident = $sdl_enum:ident {$(
210+
#[$($member_meta:meta)*]
211+
$member:ident = $sdl_constant:ident,
212+
)*}
213+
) => {
214+
$(#[$meta])*
215+
///
216+
#[doc = concat!("SDL C API type: [`", stringify!($sdl_enum), "`].")]
217+
/// [`From`] implementations are provided to convert between this type and the C API type.
218+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
219+
pub enum $enum {$(
220+
$member = $sdl_enum::$sdl_constant as isize,
221+
)*}
222+
223+
impl From<$sdl_enum> for $enum {
224+
fn from(value: $sdl_enum) -> Self {
225+
match value {$(
226+
$sdl_enum::$sdl_constant => Self::$member,
227+
)*}
228+
}
229+
}
162230

163-
impl TryFrom<u32> for BlendMode {
164-
type Error = ();
231+
impl From<$enum> for $sdl_enum {
232+
fn from(value: $enum) -> Self {
233+
match value {$(
234+
$enum::$member => Self::$sdl_constant,
235+
)*}
236+
}
237+
}
238+
};
239+
}
165240

166-
fn try_from(n: u32) -> Result<Self, Self::Error> {
167-
use self::BlendMode::*;
168-
use crate::sys::SDL_BlendMode::*;
241+
enum_binding! {
242+
/// The blend operation used when combining source and destination pixel components.
243+
pub enum BlendOperation = SDL_BlendOperation {
244+
/// dst + src: supported by all renderers
245+
Add = SDL_BLENDOPERATION_ADD,
246+
/// dst - src: supported by D3D9, D3D11, OpenGL, OpenGLES
247+
Subtract = SDL_BLENDOPERATION_SUBTRACT,
248+
/// src - dst: supported by D3D9, D3D11, OpenGL, OpenGLES
249+
RevSubtract = SDL_BLENDOPERATION_REV_SUBTRACT,
250+
/// min(dst, src): supported by D3D9, D3D11
251+
Minimum = SDL_BLENDOPERATION_MINIMUM,
252+
/// max(dst, src): supported by D3D9, D3D11
253+
Maximum = SDL_BLENDOPERATION_MAXIMUM,
254+
}
255+
}
256+
257+
enum_binding! {
258+
/// The normalized factor used to multiply pixel components.
259+
pub enum BlendFactor = SDL_BlendFactor {
260+
/// 0, 0, 0, 0
261+
Zero = SDL_BLENDFACTOR_ZERO,
262+
/// 1, 1, 1, 1
263+
One = SDL_BLENDFACTOR_ONE,
264+
/// srcR, srcG, srcB, srcA
265+
SrcColor = SDL_BLENDFACTOR_SRC_COLOR,
266+
/// 1-srcR, 1-srcG, 1-srcB, 1-srcA
267+
OneMinusSrcColor = SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
268+
/// srcA, srcA, srcA, srcA
269+
SrcAlpha = SDL_BLENDFACTOR_SRC_ALPHA,
270+
/// 1-srcA, 1-srcA, 1-srcA, 1-srcA
271+
OneMinusSrcAlpha = SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
272+
/// dstR, dstG, dstB, dstA
273+
DstColor = SDL_BLENDFACTOR_DST_COLOR,
274+
/// 1-dstR, 1-dstG, 1-dstB, 1-dstA
275+
OneMinusDstColor = SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
276+
/// dstA, dstA, dstA, dstA
277+
DstAlpha = SDL_BLENDFACTOR_DST_ALPHA,
278+
/// 1-dstA, 1-dstA, 1-dstA, 1-dstA
279+
OneMinusDstAlpha = SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
280+
}
281+
}
282+
283+
/// Arguments to [`SDL_ComposeCustomBlendMode`][sys::SDL_ComposeCustomBlendMode].
284+
///
285+
/// This struct implements [`Default`] with a blend mode equivalent to [`BlendMode::NONE`].
286+
/// Struct update syntax ([Rust Book][book-struct-update], [Rust Reference][reference-struct-update]) can be used to only change a few arguments.
287+
///
288+
/// See [`Self::compose`] for examples.
289+
///
290+
/// [book-struct-update]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax
291+
/// [reference-struct-update]: https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax
292+
#[derive(Debug, Clone, Copy)]
293+
pub struct CustomBlendMode {
294+
pub src_color_factor: BlendFactor,
295+
pub dst_color_factor: BlendFactor,
296+
pub color_operation: BlendOperation,
297+
pub src_alpha_factor: BlendFactor,
298+
pub dst_alpha_factor: BlendFactor,
299+
pub alpha_operation: BlendOperation,
300+
}
169301

170-
match n {
171-
x if x == SDL_BLENDMODE_NONE as u32 => Ok(None),
172-
x if x == SDL_BLENDMODE_BLEND as u32 => Ok(Blend),
173-
x if x == SDL_BLENDMODE_ADD as u32 => Ok(Add),
174-
x if x == SDL_BLENDMODE_MOD as u32 => Ok(Mod),
175-
x if x == SDL_BLENDMODE_MUL as u32 => Ok(Mul),
176-
x if x == SDL_BLENDMODE_INVALID as u32 => Ok(Invalid),
177-
_ => Err(()),
302+
impl Default for CustomBlendMode {
303+
fn default() -> Self {
304+
Self {
305+
src_color_factor: BlendFactor::One,
306+
dst_color_factor: BlendFactor::Zero,
307+
color_operation: BlendOperation::Add,
308+
src_alpha_factor: BlendFactor::One,
309+
dst_alpha_factor: BlendFactor::Zero,
310+
alpha_operation: BlendOperation::Add,
178311
}
179312
}
180313
}
181314

315+
impl CustomBlendMode {
316+
/// Compose a custom blend mode for renderers.
317+
///
318+
/// # Examples
319+
///
320+
/// ```
321+
/// use sdl2::render::{BlendFactor, BlendOperation, CustomBlendMode};
322+
///
323+
/// let no_blending = CustomBlendMode {
324+
/// src_color_factor: BlendFactor::One,
325+
/// dst_color_factor: BlendFactor::Zero,
326+
/// color_operation: BlendOperation::Add,
327+
/// src_alpha_factor: BlendFactor::One,
328+
/// dst_alpha_factor: BlendFactor::Zero,
329+
/// alpha_operation: BlendOperation::Add,
330+
/// }.compose();
331+
///
332+
/// let ignore_alpha = CustomBlendMode {
333+
/// src_alpha_factor: BlendFactor::Zero,
334+
/// dst_alpha_factor: BlendFactor::One,
335+
/// ..Default::default()
336+
/// }.compose();
337+
/// ```
338+
///
339+
/// See [`SDL_ComposeCustomBlendMode`][sys::SDL_ComposeCustomBlendMode].
340+
#[doc(alias = "SDL_ComposeCustomBlendMode")]
341+
pub fn compose(self) -> BlendMode {
342+
let Self {
343+
src_color_factor,
344+
dst_color_factor,
345+
color_operation,
346+
src_alpha_factor,
347+
dst_alpha_factor,
348+
alpha_operation,
349+
} = self;
350+
351+
BlendMode(unsafe {
352+
sys::SDL_ComposeCustomBlendMode(
353+
src_color_factor.into(),
354+
dst_color_factor.into(),
355+
color_operation.into(),
356+
src_alpha_factor.into(),
357+
dst_alpha_factor.into(),
358+
alpha_operation.into(),
359+
)
360+
})
361+
}
362+
}
363+
182364
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
183365
pub enum ScaleMode {
184366
/// nearest pixel sampling. default
@@ -1098,8 +1280,7 @@ impl<T: RenderTarget> Canvas<T> {
10981280
/// Sets the blend mode used for drawing operations (Fill and Line).
10991281
#[doc(alias = "SDL_SetRenderDrawBlendMode")]
11001282
pub fn set_blend_mode(&mut self, blend: BlendMode) {
1101-
let ret =
1102-
unsafe { sys::SDL_SetRenderDrawBlendMode(self.context.raw, transmute(blend as u32)) };
1283+
let ret = unsafe { sys::SDL_SetRenderDrawBlendMode(self.context.raw, blend.0) };
11031284
// Should only fail on an invalid renderer
11041285
if ret != 0 {
11051286
panic!("{}", get_error())
@@ -1115,8 +1296,7 @@ impl<T: RenderTarget> Canvas<T> {
11151296
if ret != 0 {
11161297
panic!("{}", get_error())
11171298
} else {
1118-
let blend = unsafe { blend.assume_init() };
1119-
BlendMode::try_from(blend as u32).unwrap()
1299+
BlendMode(unsafe { blend.assume_init() })
11201300
}
11211301
}
11221302

@@ -2541,7 +2721,7 @@ impl InternalTexture {
25412721

25422722
#[doc(alias = "SDL_SetTextureBlendMode")]
25432723
pub fn set_blend_mode(&mut self, blend: BlendMode) {
2544-
let ret = unsafe { sys::SDL_SetTextureBlendMode(self.raw, transmute(blend as u32)) };
2724+
let ret = unsafe { sys::SDL_SetTextureBlendMode(self.raw, blend.0) };
25452725

25462726
if ret != 0 {
25472727
panic!("Error setting blend: {}", get_error())
@@ -2557,8 +2737,7 @@ impl InternalTexture {
25572737
if ret != 0 {
25582738
panic!("{}", get_error())
25592739
} else {
2560-
let blend = unsafe { blend.assume_init() };
2561-
BlendMode::try_from(blend as u32).unwrap()
2740+
BlendMode(unsafe { blend.assume_init() })
25622741
}
25632742
}
25642743

0 commit comments

Comments
 (0)