diff --git a/examples/masked.rs b/examples/masked.rs new file mode 100644 index 00000000..0332f338 --- /dev/null +++ b/examples/masked.rs @@ -0,0 +1,30 @@ +use image::{ImageBuffer, Rgb}; +use imageproc::{ + drawing::{draw_filled_circle, draw_filled_rect_mut}, + rect::Rect, +}; + +fn main() { + // Load the image + let image = ImageBuffer::, Vec<_>>::new(256, 256); + + // Create a mask + let mask = ImageBuffer::new(image.width(), image.height()); + let shorter_side = mask.width().min(mask.height()) as i32; + let center_x = mask.width() / 2; + let center_y = mask.height() / 2; + let mask = draw_filled_circle( + &mask, + (center_x as i32, center_y as i32), + shorter_side / 4, + image::Luma([255u8]), + ); + + let mut canvas = imageproc::drawing::Masked::new(image, mask); + draw_filled_rect_mut( + &mut canvas, + Rect::at(0, 0).of_size(center_x, center_y), + Rgb([255u8, 0u8, 0u8]), + ); + canvas.inner.save("masked.png").unwrap(); +} diff --git a/src/drawing/canvas.rs b/src/drawing/canvas.rs index 1fe764bd..59e3f658 100644 --- a/src/drawing/canvas.rs +++ b/src/drawing/canvas.rs @@ -1,4 +1,4 @@ -use image::{GenericImage, GenericImageView, Pixel}; +use image::{GenericImage, GenericImageView, Luma, Pixel}; /// A surface for drawing on - many drawing functions in this /// library are generic over a `Canvas` to allow the user to @@ -71,6 +71,9 @@ pub trait Canvas { /// should be within `dimensions` - if not then panicking /// is a valid implementation behaviour. fn draw_pixel(&mut self, x: u32, y: u32, color: Self::Pixel); + + /// Consumes the canvas and returns the underlying image. + fn into_image(self) -> impl GenericImage; } impl Canvas for I @@ -90,6 +93,10 @@ where fn draw_pixel(&mut self, x: u32, y: u32, color: Self::Pixel) { self.put_pixel(x, y, color) } + + fn into_image(self) -> impl GenericImage { + self + } } /// A canvas that blends pixels when drawing. @@ -98,7 +105,7 @@ where /// for an example using this type. pub struct Blend(pub I); -impl Canvas for Blend { +impl Canvas for Blend { type Pixel = I::Pixel; fn dimensions(&self) -> (u32, u32) { @@ -112,6 +119,51 @@ impl Canvas for Blend { fn draw_pixel(&mut self, x: u32, y: u32, color: Self::Pixel) { let mut pix = self.0.get_pixel(x, y); pix.blend(&color); - self.0.put_pixel(x, y, pix); + self.0.draw_pixel(x, y, pix); + } + + fn into_image(self) -> impl GenericImage { + self.0.into_image() + } +} + +/// A canvas that only draws pixels where a mask is non-zero. +pub struct Masked { + /// A canvas to draw on. + pub inner: I, + /// A mask image where non-zero pixels allow drawing. + pub mask: M, +} + +impl Masked { + /// Create a new masked canvas. + /// + /// # Panics + /// If the dimensions of the inner canvas and mask do not match. + pub fn new(inner: I, mask: M) -> Self { + assert_eq!(inner.dimensions(), mask.dimensions()); + Masked { inner, mask } + } +} + +impl>> Canvas for Masked { + type Pixel = I::Pixel; + + fn dimensions(&self) -> (u32, u32) { + self.inner.dimensions() + } + + fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { + self.inner.get_pixel(x, y) + } + + fn draw_pixel(&mut self, x: u32, y: u32, color: Self::Pixel) { + if self.mask.get_pixel(x, y).0[0] != 0 { + self.inner.draw_pixel(x, y, color); + } + } + + fn into_image(self) -> impl GenericImage { + self.inner.into_image() } } diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs index a56aa0fa..620212c2 100644 --- a/src/drawing/mod.rs +++ b/src/drawing/mod.rs @@ -4,7 +4,7 @@ mod bezier; pub use self::bezier::{draw_cubic_bezier_curve, draw_cubic_bezier_curve_mut}; mod canvas; -pub use self::canvas::{Blend, Canvas}; +pub use self::canvas::{Blend, Canvas, Masked}; mod conics; pub use self::conics::{