Skip to content

Commit

Permalink
clipping rect disambiguate (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
jagprog5 authored Feb 8, 2025
1 parent 009a303 commit 5795e2f
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 13 deletions.
115 changes: 102 additions & 13 deletions src/sdl3/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,75 @@ impl TryFrom<u32> for BlendMode {
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClippingRect {
/// a non-zero area clipping rect
Some(Rect),
/// a clipping rect with zero area
Zero,
/// the absence of a clipping rect
None,
}

impl Into<ClippingRect> for Rect {
fn into(self) -> ClippingRect {
ClippingRect::Some(self)
}
}

impl Into<ClippingRect> for Option<Rect> {
fn into(self) -> ClippingRect {
match self {
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,
},
},
}
}

/// shrink the clipping rect to the part which contains the position
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 => {
// clipping rect has infinite area, so it's just whatever position is
ClippingRect::Some(position)
}
}
}
None => {
// position is zero area so intersection result is zero
ClippingRect::Zero
}
}
}
}

/// Manages what keeps a `SDL_Renderer` alive
///
/// When the `RendererContext` is dropped, it destroys the `SDL_Renderer`
Expand Down Expand Up @@ -1129,31 +1198,51 @@ impl<T: RenderTarget> Canvas<T> {
}

/// Sets the clip rectangle for rendering on the specified target.
///
/// If the rectangle is `None`, clipping will be disabled.
#[doc(alias = "SDL_SetRenderClipRect")]
pub fn set_clip_rect<R: Into<Option<Rect>>>(&mut self, rect: R) {
let rect = rect.into();
// as_ref is important because we need rect to live until the end of the FFI call, but map_or consumes an Option<T>
let ptr = rect.as_ref().map_or(ptr::null(), |rect| rect.raw());
let ret = unsafe { sys::render::SDL_SetRenderClipRect(self.context.raw, ptr) };
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())
}
}

/// Gets the clip rectangle for the current target.
///
/// Returns `None` if clipping is disabled.
#[doc(alias = "SDL_GetRenderClipRect")]
pub fn clip_rect(&self) -> Option<Rect> {
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 { sys::render::SDL_GetRenderClipRect(self.context.raw, raw.as_mut_ptr()) };
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 {
None
ClippingRect::Zero
} else {
Some(Rect::from_ll(raw))
ClippingRect::Some(Rect::from_ll(raw))
}
}

Expand Down
82 changes: 82 additions & 0 deletions tests/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use sdl3::{rect::Rect, render::ClippingRect};

extern crate sdl3;

#[test]
fn clipping_rect_intersection() {
// a zero area clipping rect intersecting with anything else gives zero.
assert_eq!(
ClippingRect::Zero.intersection(ClippingRect::Zero),
ClippingRect::Zero
);
assert_eq!(
ClippingRect::Zero.intersection(ClippingRect::None),
ClippingRect::Zero
);
assert_eq!(
ClippingRect::Zero.intersection(ClippingRect::Some(Rect::new(0, 0, 1, 1))),
ClippingRect::Zero
);

// none gives whatever the arg was
assert_eq!(
ClippingRect::None.intersection(ClippingRect::Zero),
ClippingRect::Zero
);
assert_eq!(
ClippingRect::None.intersection(ClippingRect::None),
ClippingRect::None
);
assert_eq!(
ClippingRect::None.intersection(ClippingRect::Some(Rect::new(0, 0, 1, 1))),
ClippingRect::Some(Rect::new(0, 0, 1, 1))
);

assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 1, 1)).intersection(ClippingRect::Zero),
ClippingRect::Zero
);
assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 1, 1)).intersection(ClippingRect::None),
ClippingRect::Some(Rect::new(0, 0, 1, 1))
);
assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 10, 10))
.intersection(ClippingRect::Some(Rect::new(20, 20, 1, 1))),
ClippingRect::Zero
);

assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 10, 10))
.intersection(ClippingRect::Some(Rect::new(5, 5, 10, 10))),
ClippingRect::Some(Rect::new(5, 5, 5, 5))
);
}

#[test]
fn clipping_rect_intersect_rect() {
assert_eq!(ClippingRect::Zero.intersect_rect(None), ClippingRect::Zero);
assert_eq!(
ClippingRect::Zero.intersect_rect(Rect::new(0, 0, 1, 1)),
ClippingRect::Zero
);

assert_eq!(ClippingRect::None.intersect_rect(None), ClippingRect::Zero);
assert_eq!(
ClippingRect::None.intersect_rect(Rect::new(0, 0, 1, 1)),
ClippingRect::Some(Rect::new(0, 0, 1, 1))
);

assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 1, 1)).intersect_rect(None),
ClippingRect::Zero
);
assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 10, 10)).intersect_rect(Rect::new(5, 5, 10, 10)),
ClippingRect::Some(Rect::new(5, 5, 5, 5))
);
assert_eq!(
ClippingRect::Some(Rect::new(0, 0, 10, 10)).intersect_rect(Rect::new(20, 20, 1, 1)),
ClippingRect::Zero
);
}

0 comments on commit 5795e2f

Please sign in to comment.