diff --git a/piet-cairo/src/lib.rs b/piet-cairo/src/lib.rs index 81d232a84..1018a69b6 100644 --- a/piet-cairo/src/lib.rs +++ b/piet-cairo/src/lib.rs @@ -301,6 +301,10 @@ impl<'a> RenderContext for CairoRenderContext<'a> { self.draw_image_inner(&image.0, Some(src_rect.into()), dst_rect.into(), interp); } + fn capture_image_area(&mut self, _src_rect: impl Into) -> Result { + Err(Error::MissingFeature) + } + fn blurred_rect(&mut self, rect: Rect, blur_radius: f64, brush: &impl IntoBrush) { let brush = brush.make_brush(self, || rect); let (image, origin) = compute_blurred_rect(rect, blur_radius); diff --git a/piet-common/examples/png.rs b/piet-common/examples/png.rs index f8be2a490..6791b2ff7 100644 --- a/piet-common/examples/png.rs +++ b/piet-common/examples/png.rs @@ -24,16 +24,13 @@ fn main() { rc.stroke(Line::new((10.0, 10.0), (100.0, 50.0)), &brush, 1.0); rc.finish().unwrap(); - let copy = rc.save_image(Rect::new(10.0, 10.0, 640.0, 480.0)).unwrap(); - let copy1 = rc.save_image(Rect::new(10.0, 10.0, 100.0, 50.0)).unwrap(); - - - rc.draw_image(©, Rect::new(100.0, 50.0, 740.0, 530.0), InterpolationMode::Bilinear); - rc.draw_image(©1, Rect::new(200.0, 50.0, 740.0, 530.0), InterpolationMode::Bilinear); - rc.draw_image(©1, Rect::new(10.0, 50.0, 100.0, 90.0), InterpolationMode::Bilinear); + if let Ok(copy) = rc.capture_image_area(Rect::new(10.0, 10.0, 100.0, 50.0)) { + rc.draw_image(©, Rect::new(100.0, 50.0, 190.0, 90.0), InterpolationMode::Bilinear); + rc.draw_image(©, Rect::new(200.0, 50.0, 740.0, 530.0), InterpolationMode::Bilinear); + rc.draw_image(©, Rect::new(10.0, 50.0, 100.0, 90.0), InterpolationMode::Bilinear); + } rc.finish().unwrap(); - std::mem::drop(rc); bitmap diff --git a/piet-coregraphics/src/lib.rs b/piet-coregraphics/src/lib.rs index 084aaf49c..8d02e524f 100644 --- a/piet-coregraphics/src/lib.rs +++ b/piet-coregraphics/src/lib.rs @@ -281,22 +281,6 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> { self.ctx.concat_ctm(to_cgaffine(transform)); } - fn save_image(&mut self, rect: impl Into) -> Result { - // Creates a CGImage from the current context. - // In my testing so far, this image was the same size as - // the entire drawable area of the window, so I'm guessing - // there's only one graphics context per window and not e.g. per - // widget. - let raw_image = self.ctx.create_image().ok_or(Error::InvalidInput)?; - - // Since the raw image above contains everything, and we're likely only - // interested in e.g. a particular widget, we need to crop it now. - raw_image - .cropped(to_cgrect(rect)) - .map(|img| CoreGraphicsImage::NonEmpty(img)) - .ok_or(Error::InvalidInput) - } - fn make_image( &mut self, width: usize, @@ -385,6 +369,28 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> { } } + fn capture_image_area(&mut self, src_rect: impl Into) -> Result { + let src_rect = src_rect.into(); + if src_rect.width() < 1.0 || src_rect.height() < 1.0 { + return Err(Error::InvalidInput); + } + + if src_rect.width() > self.ctx.width() as f64 || src_rect.height() > self.ctx.height() as f64 { + return Err(Error::InvalidInput); + } + + let full_image = self.ctx.create_image().ok_or(Error::InvalidInput)?; + + if src_rect.width() == self.ctx.width() as f64 && src_rect.height() == self.ctx.height() as f64 { + return Ok(CoreGraphicsImage::NonEmpty(full_image)); + } + + full_image + .cropped(to_cgrect(src_rect)) + .map(|img| CoreGraphicsImage::NonEmpty(img)) + .ok_or(Error::InvalidInput) + } + fn blurred_rect(&mut self, rect: Rect, blur_radius: f64, brush: &impl IntoBrush) { let (image, rect) = compute_blurred_rect(rect, blur_radius); let cg_rect = to_cgrect(rect); @@ -638,4 +644,17 @@ mod tests { piet.restore().unwrap(); assert_affine_eq!(piet.current_transform(), Affine::default()); } + + #[test] + fn capture_image_area() { + let mut ctx = make_context((400.0, 400.0)); + let mut piet = CoreGraphicsContext::new_y_down(&mut ctx, None); + + assert!(piet.capture_image_area(Rect::new(0.0, 0.0, 0.0, 0.0)).is_err()); + assert!(piet.capture_image_area(Rect::new(0.0, 0.0, 500.0, 400.0)).is_err()); + assert!(piet.capture_image_area(Rect::new(100.0, 100.0, 200.0, 200.0)).is_ok()); + + let copy = piet.capture_image_area(Rect::new(100.0, 100.0, 200.0, 200.0)).unwrap(); + piet.draw_image(©, Rect::new(0.0, 0.0, 400.0, 400.0), InterpolationMode::Bilinear); + } } diff --git a/piet-svg/src/lib.rs b/piet-svg/src/lib.rs index c6e169bbd..c9f24a818 100644 --- a/piet-svg/src/lib.rs +++ b/piet-svg/src/lib.rs @@ -272,6 +272,10 @@ impl piet::RenderContext for RenderContext { draw_image(self, image, Some(src_rect.into()), dst_rect.into(), interp); } + fn capture_image_area(&mut self, _src_rect: impl Into) -> Result { + Err(Error::MissingFeature) + } + fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush) { unimplemented!() } diff --git a/piet/src/null_renderer.rs b/piet/src/null_renderer.rs index e8ccc847f..fa0fc35c4 100644 --- a/piet/src/null_renderer.rs +++ b/piet/src/null_renderer.rs @@ -95,7 +95,7 @@ impl RenderContext for NullRenderContext { } fn transform(&mut self, _transform: Affine) {} - fn save_image(&mut self, _rect: impl Into) -> Result { + fn capture_image_area(&mut self, _src_rect: impl Into) -> Result { Ok(NullImage) } diff --git a/piet/src/render_context.rs b/piet/src/render_context.rs index e8286da2b..662099d23 100644 --- a/piet/src/render_context.rs +++ b/piet/src/render_context.rs @@ -175,8 +175,6 @@ where format: ImageFormat, ) -> Result; - fn save_image(&mut self, rect: impl Into) -> Result; - /// Draw an image. /// /// The `image` is scaled to the provided `dst_rect`. @@ -200,6 +198,12 @@ where interp: InterpolationMode, ); + /// Capture a specified area of an image. + /// + /// The `src_rect` area of the current render context will be captured + /// as a copy and returned. + fn capture_image_area(&mut self, src_rect: impl Into) -> Result; + /// Draw a rectangle with Gaussian blur. /// /// The blur radius is sometimes referred to as the "standard deviation" of