Skip to content

Commit

Permalink
Harmonize y-direction
Browse files Browse the repository at this point in the history
  • Loading branch information
longmathemagician committed Aug 19, 2022
1 parent be1da57 commit 6bbf3a1
Showing 1 changed file with 75 additions and 31 deletions.
106 changes: 75 additions & 31 deletions piet-coregraphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct CoreGraphicsContext<'a> {
// only those transforms applied by us.
transform_stack: Vec<Affine>,
y_down: bool,
height: f64,
}

impl<'a> CoreGraphicsContext<'a> {
Expand Down Expand Up @@ -99,6 +100,7 @@ impl<'a> CoreGraphicsContext<'a> {
text,
transform_stack: Vec::new(),
y_down,
height: height.unwrap_or_default(),
}
}
}
Expand All @@ -121,14 +123,28 @@ pub enum CoreGraphicsImage {
/// Empty images are not supported for core-graphics, so we need a variant here to handle that
/// case.
Empty,
NonEmpty(CGImage),
YUp(CGImage),
YDown(CGImage),
}

impl CoreGraphicsImage {
fn from_cgimage_and_ydir(image: CGImage, y_down: bool) -> Self {
match y_down {
true => CoreGraphicsImage::YDown(image),
false => CoreGraphicsImage::YUp(image),
}
}
fn as_ref(&self) -> Option<&CGImage> {
match self {
CoreGraphicsImage::Empty => None,
CoreGraphicsImage::NonEmpty(img) => Some(img),
CoreGraphicsImage::YUp(image) | CoreGraphicsImage::YDown(image) => Some(image),
}
}
pub fn copy_image(&self) -> Option<CGImage> {
if let CoreGraphicsImage::YUp(image) | CoreGraphicsImage::YDown(image) = self {
Some(image.clone())
} else {
None
}
}
}
Expand Down Expand Up @@ -343,19 +359,29 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
should_interpolate,
rendering_intent,
);
Ok(CoreGraphicsImage::NonEmpty(image))

Ok(CoreGraphicsImage::from_cgimage_and_ydir(image, self.y_down))
}

fn draw_image(
&mut self,
image: &Self::Image,
src_image: &Self::Image,
rect: impl Into<Rect>,
interp: InterpolationMode,
) {
let image = match image.as_ref() {
Some(img) => img,
None => return,
let image_y_down: bool;
let image = match src_image {
CoreGraphicsImage::YDown(img) => {
image_y_down = true;
img
}
CoreGraphicsImage::YUp(img) => {
image_y_down = false;
img
}
CoreGraphicsImage::Empty => return,
};

self.ctx.save();
//https://developer.apple.com/documentation/coregraphics/cginterpolationquality?language=objc
let quality = match interp {
Expand All @@ -366,13 +392,19 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
};
self.ctx.set_interpolation_quality(quality);
let rect = rect.into();
// CGImage is drawn flipped by default; it's easier for us to handle
// this transformation if we're drawing into a rect at the origin, so
// we convert our origin into a translation of the drawing context.
self.ctx.translate(rect.min_x(), rect.max_y());
self.ctx.scale(1.0, -1.0);
self.ctx
.draw_image(to_cgrect(rect.with_origin(Point::ZERO)), image);

if self.y_down && !image_y_down {
// The CGImage does not need to be inverted, draw it directly to the context.
self.ctx.draw_image(to_cgrect(rect), image);
} else {
// The CGImage needs to be flipped, which we do by translating the drawing rect to be
// centered around the origin before inverting the context.
self.ctx.translate(rect.min_x(), rect.max_y());
self.ctx.scale(1.0, -1.0);
self.ctx
.draw_image(to_cgrect(rect.with_origin(Point::ZERO)), image);
}

self.ctx.restore();
}

Expand All @@ -383,9 +415,13 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
dst_rect: impl Into<Rect>,
interp: InterpolationMode,
) {
if let CoreGraphicsImage::NonEmpty(image) = image {
if let CoreGraphicsImage::YDown(image) = image {
if let Some(cropped) = image.cropped(to_cgrect(src_rect)) {
self.draw_image(&CoreGraphicsImage::YDown(cropped), dst_rect, interp);
}
} else if let CoreGraphicsImage::YUp(image) = image {
if let Some(cropped) = image.cropped(to_cgrect(src_rect)) {
self.draw_image(&CoreGraphicsImage::NonEmpty(cropped), dst_rect, interp);
self.draw_image(&CoreGraphicsImage::YUp(cropped), dst_rect, interp);
}
}
}
Expand All @@ -398,19 +434,24 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
// (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 = if self.y_down {
self.ctx.get_ctm()
let src_cgrect = if self.y_down {
// If the active context is y-down (Piet's default) then we can use the context's
// transformation matrix directly.
let matrix = self.ctx.get_ctm();
to_cgrect(src_rect).apply_transform(&matrix)
} else {
self.ctx.save();
self.ctx.translate(src_rect.x0, -src_rect.y0);
// Otherwise the active context is y-up (macOS default in coregraphics), and we need to
// temporarily translate and flip the context to capture the correct area.
let y_dir_adjusted_src_rect = Rect::new(
src_rect.x0,
self.height - src_rect.y0,
src_rect.x1,
self.height - src_rect.y1,
);
let matrix = self.ctx.get_ctm();
self.ctx.restore();
matrix
to_cgrect(y_dir_adjusted_src_rect).apply_transform(&matrix)
};

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);
}
Expand All @@ -426,13 +467,16 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
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));
return Ok(CoreGraphicsImage::from_cgimage_and_ydir(
full_image,
self.y_down,
));
}

full_image
.cropped(src_cgrect)
.map(CoreGraphicsImage::NonEmpty)
.ok_or(Error::InvalidInput)
match full_image.cropped(src_cgrect) {
Some(image) => Ok(CoreGraphicsImage::from_cgimage_and_ydir(image, self.y_down)),
None => Err(Error::InvalidInput),
}
}

fn blurred_rect(&mut self, rect: Rect, blur_radius: f64, brush: &impl IntoBrush<Self>) {
Expand Down Expand Up @@ -470,7 +514,7 @@ impl Image for CoreGraphicsImage {
// the issue would only be accuracy of the size.
match self {
CoreGraphicsImage::Empty => Size::new(0., 0.),
CoreGraphicsImage::NonEmpty(image) => {
CoreGraphicsImage::YDown(image) | CoreGraphicsImage::YUp(image) => {
Size::new(image.width() as f64, image.height() as f64)
}
}
Expand Down

0 comments on commit 6bbf3a1

Please sign in to comment.