diff --git a/pixman/Cargo.toml b/pixman/Cargo.toml index 96b1f78..e36bd43 100644 --- a/pixman/Cargo.toml +++ b/pixman/Cargo.toml @@ -28,3 +28,4 @@ image = "0.24.7" [features] default = [] drm-fourcc = ["dep:drm-fourcc"] +sync = [] diff --git a/pixman/examples/alpha.rs b/pixman/examples/alpha.rs index 8459a9c..6d516ee 100644 --- a/pixman/examples/alpha.rs +++ b/pixman/examples/alpha.rs @@ -21,7 +21,7 @@ pub fn main() { ]); let mut alpha = [0x4f00004fu32; WIDTH * HEIGHT]; /* pale blue */ - let alpha_img = Image::from_slice_mut( + let mut alpha_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -32,7 +32,7 @@ pub fn main() { .unwrap(); let mut dest = [0xffffff00u32; WIDTH * HEIGHT]; /* yellow */ - let dest_img = Image::from_slice_mut( + let mut dest_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -53,7 +53,7 @@ pub fn main() { ) .unwrap(); - let grad_img = LinearGradient::new(p1, p2, &stops).unwrap(); + let mut grad_img = LinearGradient::new(p1, p2, &stops).unwrap(); grad_img.set_transform(transform).unwrap(); grad_img.set_repeat(Repeat::Pad); @@ -79,7 +79,7 @@ pub fn main() { ((10 * WIDTH) as u16, HEIGHT as u16), ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dest_img.width(), dest_img.height(), diff --git a/pixman/examples/checkerboard.rs b/pixman/examples/checkerboard.rs index 5cc1543..5597049 100644 --- a/pixman/examples/checkerboard.rs +++ b/pixman/examples/checkerboard.rs @@ -5,8 +5,8 @@ const HEIGHT: usize = 400; const TILE_SIZE: usize = 25; pub fn main() { - let checkerboard = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); - let destination = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); + let mut checkerboard = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); + let mut destination = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); // let transform = Transform::new([ // [-1.96830, -1.82250, 512.12250], @@ -57,7 +57,7 @@ pub fn main() { (WIDTH as u16, HEIGHT as u16), ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, destination.width(), destination.height(), diff --git a/pixman/examples/clip.rs b/pixman/examples/clip.rs index a8b7f20..9f2f686 100644 --- a/pixman/examples/clip.rs +++ b/pixman/examples/clip.rs @@ -20,7 +20,7 @@ pub fn main() { let r_outer = Fixed::from(100.0); let mut src = [0xff0000ffu32; WIDTH * HEIGHT]; - let src_img = Image::from_slice_mut( + let mut src_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -50,7 +50,7 @@ pub fn main() { src_img.set_repeat(Repeat::Normal); let mut dst = [0xffff0000u32; WIDTH * HEIGHT]; - let dst_img = Image::from_slice_mut( + let mut dst_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -70,7 +70,7 @@ pub fn main() { (WIDTH as u16, HEIGHT as u16), ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dst_img.width(), dst_img.height(), diff --git a/pixman/examples/conical.rs b/pixman/examples/conical.rs index 3e1b74a..96a6633 100644 --- a/pixman/examples/conical.rs +++ b/pixman/examples/conical.rs @@ -10,9 +10,9 @@ const HEIGHT: usize = SIZE * NUM_ROWS; const NUM_GRADIENTS: usize = 35; pub fn main() { - let dest_img = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); + let mut dest_img = Image::new(FormatCode::A8R8G8B8, WIDTH, HEIGHT, false).unwrap(); - draw_checkerboard(&dest_img, 25, 0xffaaaaaa, 0xff888888); + draw_checkerboard(&mut dest_img, 25, 0xffaaaaaa, 0xff888888); let transform = Transform::identity() .translate(0.5, 0.5, true) @@ -26,7 +26,7 @@ pub fn main() { let column = i % GRADIENTS_PER_ROW; let row = i / GRADIENTS_PER_ROW; - let src_img = create_conical(i); + let mut src_img = create_conical(i); src_img.set_repeat(Repeat::Normal); src_img.set_transform(transform).unwrap(); @@ -41,7 +41,7 @@ pub fn main() { ); } - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dest_img.width(), dest_img.height(), @@ -89,7 +89,7 @@ fn create_conical(index: usize) -> ConicalGradient<'static> { .unwrap() } -fn draw_checkerboard(image: &Image<'_, '_>, check_size: usize, color1: u32, color2: u32) { +fn draw_checkerboard(image: &mut Image<'_, '_>, check_size: usize, color1: u32, color2: u32) { let c1 = Solid::new(color1).unwrap(); let c2 = Solid::new(color2).unwrap(); diff --git a/pixman/examples/gradient.rs b/pixman/examples/gradient.rs index baf28de..86e10ca 100644 --- a/pixman/examples/gradient.rs +++ b/pixman/examples/gradient.rs @@ -21,7 +21,7 @@ pub fn main() { ]); let mut dest = [0xff00ff00u32; WIDTH * HEIGHT]; - let dest_img = Image::from_slice_mut( + let mut dest_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -31,7 +31,7 @@ pub fn main() { ) .unwrap(); - let src_img = LinearGradient::new(p1, p2, &stops).unwrap(); + let mut src_img = LinearGradient::new(p1, p2, &stops).unwrap(); src_img.set_transform(transform).unwrap(); src_img.set_repeat(Repeat::None); @@ -46,7 +46,7 @@ pub fn main() { ((10 * WIDTH) as u16, HEIGHT as u16), ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dest_img.width(), dest_img.height(), diff --git a/pixman/examples/solid_fill.rs b/pixman/examples/solid_fill.rs index b03da4c..a0fa353 100644 --- a/pixman/examples/solid_fill.rs +++ b/pixman/examples/solid_fill.rs @@ -1,8 +1,8 @@ use pixman::{FormatCode, Image, Operation, Repeat, Solid}; pub fn main() { - let dst = Image::new(FormatCode::A8R8G8B8, 800, 600, true).unwrap(); - let solid = Solid::new([0xffff, 0xffff, 0xffff, 0xffff]).unwrap(); + let mut dst = Image::new(FormatCode::A8R8G8B8, 800, 600, true).unwrap(); + let mut solid = Solid::new([0xffff, 0xffff, 0xffff, 0xffff]).unwrap(); solid.set_repeat(Repeat::Normal); dst.composite( Operation::Over, @@ -14,7 +14,7 @@ pub fn main() { (50, 50), ); - let out_img = Image::new(FormatCode::A8B8G8R8, dst.width(), dst.height(), false).unwrap(); + let mut out_img = Image::new(FormatCode::A8B8G8R8, dst.width(), dst.height(), false).unwrap(); out_img.composite( Operation::Src, &dst, diff --git a/pixman/examples/trap.rs b/pixman/examples/trap.rs index d9c8705..0587ff5 100644 --- a/pixman/examples/trap.rs +++ b/pixman/examples/trap.rs @@ -24,10 +24,10 @@ pub fn main() { ), ); - let mask_img = + let mut mask_img = Image::from_slice_mut(FormatCode::A8, WIDTH, HEIGHT, &mut mbits, WIDTH, false).unwrap(); let src_img = Solid::new(white).unwrap(); - let dest_img = + let mut dest_img = Image::from_slice_mut(FormatCode::A8R8G8B8, WIDTH, HEIGHT, bits, WIDTH * 4, false).unwrap(); mask_img.add_traps((0, 0), &[trap]); @@ -42,7 +42,7 @@ pub fn main() { (WIDTH as u16, HEIGHT as u16), ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dest_img.width(), dest_img.height(), diff --git a/pixman/examples/tri.rs b/pixman/examples/tri.rs index e819709..c4abbd0 100644 --- a/pixman/examples/tri.rs +++ b/pixman/examples/tri.rs @@ -19,7 +19,7 @@ pub fn main() { } let src_img = Solid::new(color).unwrap(); - let dest_img = Image::from_slice_mut( + let mut dest_img = Image::from_slice_mut( FormatCode::A8R8G8B8, WIDTH, HEIGHT, @@ -38,7 +38,7 @@ pub fn main() { &tris, ); - let out_img = Image::new( + let mut out_img = Image::new( FormatCode::A8B8G8R8, dest_img.width(), dest_img.height(), diff --git a/pixman/src/image/bits.rs b/pixman/src/image/bits.rs index 2140c82..614b1d1 100644 --- a/pixman/src/image/bits.rs +++ b/pixman/src/image/bits.rs @@ -13,7 +13,19 @@ pub struct Image<'bits, 'alpha> { _alpha: PhantomData<&'alpha ()>, } -impl<'bits, 'alpha> std::ops::Deref for Image<'bits, 'alpha> { +// SAFETY: A reference to the image is only created by `set_alpha_map`. +// Which returns a type with a lifetime bound, so `&mut self` methods cannot +// be called while this additional reference is in use. +// +// Thus the only mutability allowed is reference counting, which is made +// thread-safe with the `REF_COUNT_LOCK` mutex, used when calling +// `pixman_image_unref`, or `pixman_image_set_alpha_map`. +#[cfg(feature = "sync")] +unsafe impl Send for Image<'_, '_> {} +#[cfg(feature = "sync")] +unsafe impl Sync for Image<'_, '_> {} + +impl std::ops::Deref for Image<'_, '_> { type Target = ImageRef; fn deref(&self) -> &Self::Target { @@ -21,6 +33,12 @@ impl<'bits, 'alpha> std::ops::Deref for Image<'bits, 'alpha> { } } +impl std::ops::DerefMut for Image<'_, '_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.image + } +} + impl Image<'static, 'static> { /// Create a new image with the specified format and size pub fn new( @@ -134,6 +152,8 @@ impl<'bits, 'a> Image<'bits, 'a> { x: i16, y: i16, ) -> Image<'bits, 'alpha> { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_set_alpha_map(self.as_ptr(), alpha_map.as_ptr(), x, y); } @@ -146,6 +166,8 @@ impl<'bits, 'a> Image<'bits, 'a> { /// Clear a previously set alpha map pub fn clear_alpha_map(self) -> Image<'bits, 'static> { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_set_alpha_map(self.as_ptr(), std::ptr::null_mut(), 0, 0); } @@ -157,7 +179,7 @@ impl<'bits, 'a> Image<'bits, 'a> { } } -impl<'bits, 'alpha> Image<'bits, 'alpha> { +impl Image<'_, '_> { /// Get the width of the image pub fn width(&self) -> usize { unsafe { ffi::pixman_image_get_width(self.as_ptr()) as usize } @@ -195,7 +217,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Fill this image with the specified boxes and color pub fn fill_boxes( - &self, + &mut self, op: Operation, color: impl Into, boxes: &[Box32], @@ -219,7 +241,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Fill this image with the specified rectangles and color pub fn fill_rectangles( - &self, + &mut self, op: Operation, color: impl Into, rects: &[Rectangle16], @@ -244,7 +266,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Composite the specified src image into this image #[allow(clippy::too_many_arguments)] pub fn composite( - &self, + &mut self, operation: Operation, src: &ImageRef, mask: Option<&ImageRef>, @@ -280,7 +302,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Composite the specified src image into this image #[allow(clippy::too_many_arguments)] pub fn composite32( - &self, + &mut self, operation: Operation, src: &ImageRef, mask: Option<&ImageRef>, @@ -314,7 +336,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Composite the specified triangles into this image pub fn composite_triangles( - &self, + &mut self, operation: Operation, src: &ImageRef, mask_format: FormatCode, @@ -340,7 +362,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { /// Composite the specified trapezoids into this image pub fn composite_trapezoids( - &self, + &mut self, operation: Operation, src: &ImageRef, mask_format: FormatCode, @@ -365,7 +387,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { } /// Add the specified traps to this image - pub fn add_traps(&self, offset: (i16, i16), traps: &[Trap]) { + pub fn add_traps(&mut self, offset: (i16, i16), traps: &[Trap]) { unsafe { ffi::pixman_add_traps( self.as_ptr(), @@ -378,7 +400,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { } /// Add the specified trapezoids to this image - pub fn add_trapezoids(&self, offset: (i16, i32), traps: &[Trapezoid]) { + pub fn add_trapezoids(&mut self, offset: (i16, i32), traps: &[Trapezoid]) { unsafe { ffi::pixman_add_trapezoids( self.as_ptr(), @@ -391,7 +413,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { } /// Add the specified triangles to this image - pub fn add_triangles(&self, offset: (i32, i32), tris: &[Triangle]) { + pub fn add_triangles(&mut self, offset: (i32, i32), tris: &[Triangle]) { unsafe { ffi::pixman_add_triangles( self.as_ptr(), @@ -444,7 +466,7 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { } /// Rasterize the specified edges - pub fn rasterize_edges(&self, l: Edge, r: Edge, t: impl Into, b: impl Into) { + pub fn rasterize_edges(&mut self, l: Edge, r: Edge, t: impl Into, b: impl Into) { unsafe { ffi::pixman_rasterize_edges( self.as_ptr(), @@ -457,18 +479,21 @@ impl<'bits, 'alpha> Image<'bits, 'alpha> { } /// Rasterize the specified trapezoids - pub fn rasterize_trapezoid(&self, trap: Trapezoid, offset: (i32, i32)) { + pub fn rasterize_trapezoid(&mut self, trap: Trapezoid, offset: (i32, i32)) { unsafe { ffi::pixman_rasterize_trapezoid(self.as_ptr(), trap.as_ptr(), offset.0, offset.1) } } } -impl<'bits, 'alpha> Image<'bits, 'alpha> { +impl Image<'_, '_> { /// Initialize the image from a raw pointer /// /// # Safety /// /// The pointer is expected to be valid and have a ref-count of at least one. /// Ownership of the pointer is transferred and unref will be called on drop. + /// + /// Any other references to the `pixman_image_t` must not be mutated while this + /// `Image` exists, including changes to the reference count. pub unsafe fn from_ptr(ptr: *mut ffi::pixman_image_t) -> Self { Self { image: unsafe { ImageRef::from_ptr(ptr) }, diff --git a/pixman/src/image/mod.rs b/pixman/src/image/mod.rs index 3b64b80..219a1e8 100644 --- a/pixman/src/image/mod.rs +++ b/pixman/src/image/mod.rs @@ -30,14 +30,17 @@ pub struct ImageRef(*mut ffi::pixman_image_t); impl ImageRef { /// Set the repeat operation for this image - pub fn set_repeat(&self, repeat: Repeat) { + pub fn set_repeat(&mut self, repeat: Repeat) { unsafe { ffi::pixman_image_set_repeat(self.0, repeat.into()); } } /// Apply the specified transform during sampling from this image - pub fn set_transform(&self, transform: impl Into) -> Result<(), OperationFailed> { + pub fn set_transform( + &mut self, + transform: impl Into, + ) -> Result<(), OperationFailed> { let transform = transform.into(); let res = unsafe { ffi::pixman_image_set_transform(self.0, transform.as_ptr()) }; if res == 1 { @@ -58,7 +61,7 @@ impl ImageRef { } /// Apply a clip region used during composition - pub fn set_clip_region(&self, region: Option<&Region16>) -> Result<(), OperationFailed> { + pub fn set_clip_region(&mut self, region: Option<&Region16>) -> Result<(), OperationFailed> { let region = if let Some(region) = region { region.as_ptr() } else { @@ -73,7 +76,7 @@ impl ImageRef { } /// Apply a clip region used during composition - pub fn set_clip_region32(&self, region: Option<&Region32>) -> Result<(), OperationFailed> { + pub fn set_clip_region32(&mut self, region: Option<&Region32>) -> Result<(), OperationFailed> { let region = if let Some(region) = region { region.as_ptr() } else { @@ -88,20 +91,20 @@ impl ImageRef { } /// Set the dither operation used during composition - pub fn set_dither(&self, dither: Dither) { + pub fn set_dither(&mut self, dither: Dither) { unsafe { ffi::pixman_image_set_dither(self.0, dither.into()); } } /// Set the dither offset - pub fn set_dither_offset(&self, offset_x: c_int, offset_y: c_int) { + pub fn set_dither_offset(&mut self, offset_x: c_int, offset_y: c_int) { unsafe { ffi::pixman_image_set_dither_offset(self.0, offset_x, offset_y) } } /// Set the filter operation used during composition pub fn set_filter( - &self, + &mut self, filter: Filter, filter_params: &[Fixed], ) -> Result<(), OperationFailed> { @@ -121,7 +124,7 @@ impl ImageRef { } /// Set whether the source clip was set by a client - pub fn set_has_client_clip(&self, client_clip: bool) { + pub fn set_has_client_clip(&mut self, client_clip: bool) { let client_clip = if client_clip { 1 } else { 0 }; unsafe { ffi::pixman_image_set_has_client_clip(self.0, client_clip); @@ -132,7 +135,7 @@ impl ImageRef { //pub fn set_indexed(&mut self) /// Set whether the clip applies when the image is used as a source - pub fn set_source_clipping(&self, source_clipping: bool) { + pub fn set_source_clipping(&mut self, source_clipping: bool) { let source_clipping = if source_clipping { 1 } else { 0 }; unsafe { ffi::pixman_image_set_source_clipping(self.0, source_clipping); @@ -145,7 +148,7 @@ impl ImageRef { } /// Set whether the image has component alpha or unified alpha - pub fn set_component_alpha(&self, component_alpha: bool) { + pub fn set_component_alpha(&mut self, component_alpha: bool) { let component_alpha = if component_alpha { 1 } else { 0 }; unsafe { ffi::pixman_image_set_component_alpha(self.0, component_alpha) } } @@ -171,6 +174,8 @@ impl ImageRef { impl Drop for ImageRef { fn drop(&mut self) { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_unref(self.0); } @@ -194,6 +199,8 @@ macro_rules! image_type { x: i16, y: i16, ) -> $name<'alpha> { + #[cfg(feature = "sync")] + let _lock = $crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { $crate::ffi::pixman_image_set_alpha_map( self.as_ptr(), @@ -210,6 +217,8 @@ macro_rules! image_type { /// Clear a previously set alpha map pub fn clear_alpha_map(self) -> $name<'static> { + #[cfg(feature = "sync")] + let _lock = $crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { $crate::ffi::pixman_image_set_alpha_map( self.as_ptr(), @@ -247,6 +256,12 @@ macro_rules! image_type { &self.image } } + + impl<'alpha> std::ops::DerefMut for $name<'alpha> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.image + } + } }; } diff --git a/pixman/src/lib.rs b/pixman/src/lib.rs index 78bfd26..0fa31d0 100644 --- a/pixman/src/lib.rs +++ b/pixman/src/lib.rs @@ -66,6 +66,9 @@ pub use vector::*; #[error("The requested operation failed")] pub struct OperationFailed; +#[cfg(feature = "sync")] +static REF_COUNT_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + /// Blit the src into the dst with the specified values #[allow(clippy::too_many_arguments)] pub fn blit(