Skip to content

Commit

Permalink
Fix capture_image_area not respecting CG transformation matrix
Browse files Browse the repository at this point in the history
The `capture_image_area` method of the CoreGraphicsContext was not
taking into account that a transformation matrix is applied to the
context. As a result, it only worked correctly if one applied this
scaling manually to the `src_rect` prior to passing it to the method.

With this addition, we now respect the CG transformation matrix and
apply it to the `src_rect` prior to cropping the captured `CGImage`.
  • Loading branch information
x3ro committed May 15, 2022
1 parent 436c5d9 commit 27388f4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
21 changes: 15 additions & 6 deletions piet-coregraphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,26 +391,35 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {

fn capture_image_area(&mut self, src_rect: impl Into<Rect>) -> Result<Self::Image, Error> {
let src_rect = src_rect.into();
if src_rect.width() < 1.0 || src_rect.height() < 1.0 {

// When creating a CoreGraphicsContext, we a transformation matrix is applied to map
// between piet's coordinate system and CoreGraphic's coordinate system
// (see [`CoreGraphicsContext::new_impl`] for details). Since the `src_rect` we receive
// as parameter is in piet's coordinate system, we need to first convert it to the CG one,
// as otherwise our captured image area would be wrong.
let transformation_matrix = self.ctx.get_ctm();
let src_cgrect = to_cgrect(src_rect).apply_transform(&transformation_matrix);

if src_cgrect.size.width < 1.0 || src_cgrect.size.height < 1.0 {
return Err(Error::InvalidInput);
}

if src_rect.width() > self.ctx.width() as f64
|| src_rect.height() > self.ctx.height() as f64
if src_cgrect.size.width > self.ctx.width() as f64
|| src_cgrect.size.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().round() as usize == self.ctx.width()
&& src_rect.height().round() as usize == self.ctx.height()
if src_cgrect.size.width.round() as usize == self.ctx.width()
&& src_cgrect.size.height.round() as usize == self.ctx.height()
{
return Ok(CoreGraphicsImage::NonEmpty(full_image));
}

full_image
.cropped(to_cgrect(src_rect))
.cropped(src_cgrect)
.map(CoreGraphicsImage::NonEmpty)
.ok_or(Error::InvalidInput)
}
Expand Down
2 changes: 2 additions & 0 deletions piet/src/samples/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod picture_12;
mod picture_13;
mod picture_14;
mod picture_15;
mod picture_16;

type BoxErr = Box<dyn std::error::Error>;

Expand Down Expand Up @@ -55,6 +56,7 @@ pub fn get<R: RenderContext>(number: usize) -> Result<SamplePicture<R>, BoxErr>
13 => SamplePicture::new(picture_13::SIZE, picture_13::draw),
14 => SamplePicture::new(picture_14::SIZE, picture_14::draw),
15 => SamplePicture::new(picture_15::SIZE, picture_15::draw),
16 => SamplePicture::new(picture_16::SIZE, picture_16::draw),
_ => return Err(format!("No sample #{} exists", number).into()),
})
}
Expand Down
45 changes: 45 additions & 0 deletions piet/src/samples/picture_16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Clipping and clearing.
//!
//! This tests interactions between clipping, transforms, and the clear method.
//!
//! 1. clear ignores clipping and transforms
use crate::kurbo::{Affine, Circle, Rect, Size};
use crate::{Color, Error, InterpolationMode, RenderContext};

pub const SIZE: Size = Size::new(400., 400.);

const RED: Color = Color::rgb8(255, 0, 0);
const BLUE: Color = Color::rgb8(0, 0, 255);
const SEMI_TRANSPARENT_GREEN: Color = Color::rgba8(0, 255, 0, 125);
const SEMI_TRANSPARENT_WHITE: Color = Color::rgba8(255, 255, 255, 125);

pub fn draw<R: RenderContext>(rc: &mut R) -> Result<(), Error> {
rc.clear(None, Color::BLACK);

let outer_rect = Rect::new(20., 20., 180., 180.);
let inner_rect = Rect::new(21., 21., 179., 179.);

let top_left_corner = Rect::new(5., 5., 50., 50.);
let top_right_corner = Rect::new(150., 5., 195., 50.);
let bottom_left_corner = Rect::new(5., 150., 50., 195.);
let bottom_right_corner = Rect::new(150., 150., 195., 195.);


// Draw a box with a red border
rc.fill(outer_rect, &RED);
rc.fill(inner_rect, &BLUE);

// Cache the box, clear the image and re-draw the box from the cache
let cache = rc.capture_image_area(outer_rect).unwrap();
rc.clear(None, Color::WHITE);
rc.draw_image(&cache, outer_rect, InterpolationMode::NearestNeighbor);

// Draw the cached image, scaled, in all four corners of the image
rc.draw_image(&cache, top_left_corner, InterpolationMode::Bilinear);
rc.draw_image(&cache, top_right_corner, InterpolationMode::Bilinear);
rc.draw_image(&cache, bottom_left_corner, InterpolationMode::Bilinear);
rc.draw_image(&cache, bottom_right_corner, InterpolationMode::Bilinear);

Ok(())
}

0 comments on commit 27388f4

Please sign in to comment.