Skip to content

Commit 681332f

Browse files
committed
[cg] Manually track our current transform
Relying on CoreGraphics here causes problems from druid; because druid-shell for mac flips the coordinate system, there is a transform applied to the context before we see it that we want to ignore.
1 parent 56ef896 commit 681332f

File tree

1 file changed

+93
-9
lines changed

1 file changed

+93
-9
lines changed

piet-coregraphics/src/lib.rs

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ pub struct CoreGraphicsContext<'a> {
3737
ctx: &'a mut CGContextRef,
3838
// the height of the context; we need this in order to correctly flip the coordinate space
3939
text: CoreGraphicsText<'a>,
40+
// because of the relationship between cocoa and coregraphics (where cocoa
41+
// may be asked to flip the y-axis) we cannot trust the transform returned
42+
// by CTContextGetCTM. Instead we maintain our own stack, which will contain
43+
// only those transforms applied by us.
44+
transform_stack: Vec<Affine>,
4045
}
4146

4247
impl<'a> CoreGraphicsContext<'a> {
@@ -66,6 +71,7 @@ impl<'a> CoreGraphicsContext<'a> {
6671
CoreGraphicsContext {
6772
ctx,
6873
text: CoreGraphicsText::new(),
74+
transform_stack: vec![Affine::default()],
6975
}
7076
}
7177
}
@@ -231,11 +237,16 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
231237

232238
fn save(&mut self) -> Result<(), Error> {
233239
self.ctx.save();
240+
let state = self.transform_stack.last().copied().unwrap_or_default();
241+
self.transform_stack.push(state);
234242
Ok(())
235243
}
236244

245+
//TODO: this panics in CoreGraphics if unbalanced. We could try and track stack depth
246+
//and return an error, maybe?
237247
fn restore(&mut self) -> Result<(), Error> {
238248
self.ctx.restore();
249+
self.transform_stack.pop();
239250
Ok(())
240251
}
241252

@@ -244,8 +255,12 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
244255
}
245256

246257
fn transform(&mut self, transform: Affine) {
247-
let transform = to_cgaffine(transform);
248-
self.ctx.concat_ctm(transform);
258+
if let Some(last) = self.transform_stack.last_mut() {
259+
*last = *last * transform;
260+
} else {
261+
self.transform_stack.push(transform);
262+
}
263+
self.ctx.concat_ctm(to_cgaffine(transform));
249264
}
250265

251266
fn make_image(
@@ -323,8 +338,7 @@ impl<'a> RenderContext for CoreGraphicsContext<'a> {
323338
}
324339

325340
fn current_transform(&self) -> Affine {
326-
let ctm = self.ctx.get_ctm();
327-
from_cgaffine(ctm)
341+
self.transform_stack.last().copied().unwrap_or_default()
328342
}
329343

330344
fn status(&mut self) -> Result<(), Error> {
@@ -442,11 +456,6 @@ fn to_cgrect(rect: impl Into<Rect>) -> CGRect {
442456
CGRect::new(&to_cgpoint(rect.origin()), &to_cgsize(rect.size()))
443457
}
444458

445-
fn from_cgaffine(affine: CGAffineTransform) -> Affine {
446-
let CGAffineTransform { a, b, c, d, tx, ty } = affine;
447-
Affine::new([a, b, c, d, tx, ty])
448-
}
449-
450459
fn to_cgaffine(affine: Affine) -> CGAffineTransform {
451460
let [a, b, c, d, tx, ty] = affine.as_coeffs();
452461
CGAffineTransform::new(a, b, c, d, tx, ty)
@@ -456,3 +465,78 @@ fn to_cgaffine(affine: Affine) -> CGAffineTransform {
456465
extern "C" {
457466
fn CGContextClipToMask(ctx: *mut u8, rect: CGRect, mask: *const u8);
458467
}
468+
469+
#[cfg(test)]
470+
mod tests {
471+
use super::*;
472+
use core_graphics::color_space::CGColorSpace;
473+
use core_graphics::context::CGContext;
474+
475+
fn make_context(size: impl Into<Size>) -> CGContext {
476+
let size = size.into();
477+
CGContext::create_bitmap_context(
478+
None,
479+
size.width as usize,
480+
size.height as usize,
481+
8,
482+
0,
483+
&CGColorSpace::create_device_rgb(),
484+
core_graphics::base::kCGImageAlphaPremultipliedLast,
485+
)
486+
}
487+
488+
fn equalish_affine(one: Affine, two: Affine) -> bool {
489+
one.as_coeffs()
490+
.iter()
491+
.zip(two.as_coeffs().iter())
492+
.all(|(a, b)| (a - b).abs() < f64::EPSILON)
493+
}
494+
495+
macro_rules! assert_affine_eq {
496+
($left:expr, $right:expr) => {{
497+
if !equalish_affine($left, $right) {
498+
panic!(
499+
"assertion failed: `(one == two)`\n\
500+
one: {:?}\n\
501+
two: {:?}",
502+
$left.as_coeffs(),
503+
$right.as_coeffs()
504+
)
505+
}
506+
}};
507+
}
508+
509+
#[test]
510+
fn get_affine_y_up() {
511+
let mut ctx = make_context((400.0, 400.0));
512+
let mut piet = CoreGraphicsContext::new_y_up(&mut ctx, 400.0);
513+
let affine = piet.current_transform();
514+
assert_affine_eq!(affine, Affine::default());
515+
516+
let one = Affine::translate((50.0, 20.0));
517+
let two = Affine::rotate(2.2);
518+
let three = Affine::FLIP_Y;
519+
let four = Affine::scale_non_uniform(2.0, -1.5);
520+
521+
piet.save().unwrap();
522+
piet.transform(one);
523+
piet.transform(one);
524+
piet.save().unwrap();
525+
piet.transform(two);
526+
piet.save().unwrap();
527+
piet.transform(three);
528+
assert_affine_eq!(piet.current_transform(), one * one * two * three);
529+
piet.transform(four);
530+
piet.save().unwrap();
531+
532+
assert_affine_eq!(piet.current_transform(), one * one * two * three * four);
533+
piet.restore().unwrap();
534+
assert_affine_eq!(piet.current_transform(), one * one * two * three * four);
535+
piet.restore().unwrap();
536+
assert_affine_eq!(piet.current_transform(), one * one * two);
537+
piet.restore().unwrap();
538+
assert_affine_eq!(piet.current_transform(), one * one);
539+
piet.restore().unwrap();
540+
assert_affine_eq!(piet.current_transform(), Affine::default());
541+
}
542+
}

0 commit comments

Comments
 (0)