@@ -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
4247impl < ' 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-
450459fn 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 {
456465extern "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