Index: gui/Source/NSImageCell.m
===================================================================
--- gui/Source/NSImageCell.m	(revision 30158)
+++ gui/Source/NSImageCell.m	(working copy)
@@ -28,6 +28,7 @@
 
 #include "config.h"
 #include <Foundation/NSDebug.h>
+#include "AppKit/NSAffineTransform.h"
 #include "AppKit/NSCell.h"
 #include "AppKit/NSGraphics.h"
 #include "AppKit/NSImageCell.h"
@@ -197,6 +198,7 @@
   NSPoint	position;
   BOOL		is_flipped = [controlView isFlipped];
   NSSize  imageSize, realImageSize;
+  NSAffineTransform *xform = nil;
 
   NSDebugLLog(@"NSImageCell", @"NSImageCell drawInteriorWithFrame called");
 
@@ -273,10 +275,13 @@
     }
 
   // account for flipped views
-  if (is_flipped)
+  if (is_flipped && controlView != nil)
     {
-      position.y += imageSize.height;
-      imageSize.height = -imageSize.height;
+      xform = [NSAffineTransform transform];
+      [xform translateXBy: 0 yBy: [controlView bounds].size.height];
+      [xform scaleXBy: 1 yBy: -1];
+      [xform concat];
+      position.y = [controlView bounds].size.height - position.y - imageSize.height;
     }
 
   // draw!
@@ -286,6 +291,12 @@
                                     realImageSize.height)
                operation: NSCompositeSourceOver
                fraction: 1.0];
+
+  if (is_flipped && controlView != nil)
+    {
+      [xform invert];
+      [xform concat];
+    }
 }
 
 - (NSSize) cellSize
Index: gui/Source/NSClipView.m
===================================================================
--- gui/Source/NSClipView.m	(revision 30158)
+++ gui/Source/NSClipView.m	(working copy)
@@ -372,11 +372,11 @@
     }
 
   /* ?? TODO: Understand the following code - and add explanatory comment */
-  if ([NSView focusView] == _documentView)
+  /*if ([NSView focusView] == _documentView)
     {
       PStranslate (NSMinX (originalBounds) - aPoint.x, 
 		   NSMinY (originalBounds) - aPoint.y);
-    }
+    }*/
   
   [_super_view reflectScrolledClipView: self];
 }
Index: gui/Source/NSImageRep.m
===================================================================
--- gui/Source/NSImageRep.m	(revision 30158)
+++ gui/Source/NSImageRep.m	(working copy)
@@ -453,8 +453,6 @@
   ctxt = GSCurrentContext();
   if (aPoint.x != 0 || aPoint.y != 0)
     {
-      if ([[ctxt focusView] isFlipped])
-	aPoint.y -= _size.height;
       ctm = GSCurrentCTM(ctxt);
       DPStranslate(ctxt, aPoint.x, aPoint.y);
       reset = 1;
@@ -480,8 +478,6 @@
   ctxt = GSCurrentContext();
   scale = NSMakeSize(NSWidth(aRect) / _size.width, 
 		     NSHeight(aRect) / _size.height);
-  if ([[ctxt focusView] isFlipped])
-    aRect.origin.y -= NSHeight(aRect);
   ctm = GSCurrentCTM(ctxt);
   DPStranslate(ctxt, NSMinX(aRect), NSMinY(aRect));
   DPSscale(ctxt, scale.width, scale.height);
Index: gui/Source/NSImage.m
===================================================================
--- gui/Source/NSImage.m	(revision 30158)
+++ gui/Source/NSImage.m	(working copy)
@@ -795,7 +795,6 @@
   return _color;
 }
 
-
 // Using the Image 
 - (void) compositeToPoint: (NSPoint)aPoint 
                 operation: (NSCompositingOperation)op
@@ -811,8 +810,8 @@
                  fromRect: (NSRect)aRect
                 operation: (NSCompositingOperation)op
 {
-  [self compositeToPoint: aPoint
-        fromRect: aRect
+  [self compositeToPoint: aPoint 
+        fromRect: aRect 
         operation: op
         fraction: 1.0];
 }
@@ -848,27 +847,27 @@
 
       if (cache != nil)
         {
+          NSGraphicsContext *ctxt = GSCurrentContext();
           NSRect rect = [cache rect];
 
           NSDebugLLog(@"NSImage", @"composite rect %@ in %@", 
                       NSStringFromRect(rect), NSStringFromRect(srcRect));
+
           // Move the drawing rectangle to the origin of the image rep
           // and intersect the two rects.
           srcRect.origin.x += rect.origin.x;
           srcRect.origin.y += rect.origin.y;
           rect = NSIntersectionRect(srcRect, rect);
           
-          [GSCurrentContext() GScomposite: [[cache window] gState]
-                           toPoint: aPoint
-                           fromRect: rect
-                           operation: op
-                           fraction: delta];
+          [ctxt GScomposite: [[cache window] gState]
+                    toPoint: aPoint
+                   fromRect: rect
+                  operation: op
+                   fraction: delta];
         }
       else        
         {
-          NSRect rect;
-
-          rect = NSMakeRect(aPoint.x, aPoint.y, _size.width, _size.height);
+          NSRect rect = NSMakeRect(aPoint.x, aPoint.y, _size.width, _size.height);
           [self drawRepresentation: rep inRect: rect];
         }
     }
@@ -920,9 +919,6 @@
     {
       NSRect fillrect = aRect;
 
-      if ([[NSView focusView] isFlipped])
-        fillrect.origin.y -= _size.height;
-
       [_color set];
       NSRectFill(fillrect);
 
@@ -955,12 +951,147 @@
         fraction: delta];
 }
 
-- (void) drawInRect: (NSRect)dstRect
-           fromRect: (NSRect)srcRect
-          operation: (NSCompositingOperation)op
-           fraction: (float)delta
+/* New code path that delegates as much as possible to the backend and whose 
+behavior precisely matches Cocoa. */
+- (void) nativeDrawInRect: (NSRect)dstRect
+                 fromRect: (NSRect)srcRect
+                operation: (NSCompositingOperation)op
+                 fraction: (float)delta
 {
   NSGraphicsContext *ctxt = GSCurrentContext();
+  NSSize imgSize = [self size];
+  float widthScaleFactor;
+  float heightScaleFactor;
+
+  if (NSEqualRects(srcRect, NSZeroRect))
+    {
+      srcRect.size = imgSize;
+      /* For -drawAtPoint:fromRect:operation:fraction: used with a zero rect */
+      if (NSEqualSizes(dstRect.size, NSZeroSize))
+        {
+          dstRect.size = imgSize;
+        }
+    }
+
+  if (!dstRect.size.width || !dstRect.size.height
+    || !srcRect.size.width || !srcRect.size.height)
+    return;
+
+  // Clip to image bounds
+  if (srcRect.origin.x < 0)
+    srcRect.origin.x = 0;
+  if (srcRect.origin.y < 0)
+    srcRect.origin.y = 0;
+  if (NSMaxX(srcRect) > imgSize.width)
+    srcRect.size.width = imgSize.width - srcRect.origin.x;
+  if (NSMaxY(srcRect) > imgSize.height)
+    srcRect.size.height = imgSize.height - srcRect.origin.y;
+
+  widthScaleFactor = dstRect.size.width / srcRect.size.width;
+  heightScaleFactor = dstRect.size.height / srcRect.size.height;
+
+  if (![ctxt isDrawingToScreen])
+    {
+      /* We can't composite or dissolve if we aren't drawing to a screen,
+         so we'll just draw the right part of the image in the right
+         place. */
+      NSPoint p;
+  
+      p.x = dstRect.origin.x / widthScaleFactor - srcRect.origin.x;
+      p.y = dstRect.origin.y / heightScaleFactor - srcRect.origin.y;
+
+      DPSgsave(ctxt);
+      DPSrectclip(ctxt, dstRect.origin.x, dstRect.origin.y,
+                  dstRect.size.width, dstRect.size.height);
+      DPSscale(ctxt, widthScaleFactor, heightScaleFactor);
+      [self drawRepresentation: [self bestRepresentationForDevice: nil]
+            inRect: NSMakeRect(p.x, p.y, imgSize.width, imgSize.height)];
+      DPSgrestore(ctxt);
+
+      return;
+    }
+
+  /* We cannot ask the backend to draw the image directly when the source rect 
+     doesn't cover the whole image.
+     Cairo doesn't support to specify a source rect for a surface used as a 
+     source, see cairo_set_source_surface()).
+     CoreGraphics is similarly limited, see CGContextDrawImage().
+     For now, we always use a two step process:
+     - draw the image data in a cache to apply the srcRect to inRect scaling
+     - draw the cache into the destination context
+     It might be worth to move the first step to the backend, so we don't have 
+     to create a cache window but just an intermediate surface.
+     We create a cache every time but otherwise we are more efficient than the 
+     old code path since the cache size is limited to what we actually draw 
+     and doesn't involve drawing the whole image. */
+  {
+    /* An intermediate image used to scale the image to be drawn as needed */
+    NSCachedImageRep *cache;
+    /* The scaled image graphics state we used as the source from which we 
+       draw into the destination (the current graphics context)*/
+    int gState;
+    /* The context of the cache window */
+    NSGraphicsContext *cacheCtxt;
+    /* The size of the cache window that will hold the scaled image */
+    NSSize cacheSize = NSMakeSize(imgSize.width * widthScaleFactor, 
+      imgSize.height * heightScaleFactor);
+    NSRect srcRectInCache = NSMakeRect(srcRect.origin.x * widthScaleFactor, 
+                                      srcRect.origin.y * heightScaleFactor, 
+                                     srcRect.size.width * widthScaleFactor, 
+                                   srcRect.size.height * heightScaleFactor);
+
+    cache = [[NSCachedImageRep alloc]
+                initWithSize: cacheSize
+                       depth: [[NSScreen mainScreen] depth]
+                    separate: YES
+                       alpha: YES];
+
+    [[[cache window] contentView] lockFocus];
+    cacheCtxt = GSCurrentContext();
+
+    /* Clear the cache window surface */
+    DPScompositerect(cacheCtxt, 0, 0, cacheSize.width, cacheSize.height, NSCompositeClear);
+    gState = [cacheCtxt GSDefineGState];
+
+    //NSLog(@"Draw in cache size %@", NSStringFromSize(cacheSize));
+
+    /* We must not use -drawRepresentation:inRect: because the image must drawn 
+       scaled even when -scalesWhenResized is NO */
+    [[self bestRepresentationForDevice: nil] 
+      drawInRect: NSMakeRect(0, 0, cacheSize.width, cacheSize.height)];
+    /* If we're doing a dissolve, use a DestinationIn composite to lower
+       the alpha of the pixels.  */
+    if (delta != 1.0)
+      {
+        DPSsetalpha(cacheCtxt, delta);
+        DPScompositerect(cacheCtxt, 0, 0, cacheSize.width, cacheSize.height,
+                         NSCompositeDestinationIn);
+      }
+
+    [[[cache window] contentView] unlockFocus];
+
+    //NSLog(@"Draw in %@ from %@ from cache rect %@", NSStringFromRect(dstRect), 
+    //  NSStringFromRect(srcRect), NSStringFromRect(srcRectInCache));
+
+    [ctxt GSdraw: gState
+         toPoint: dstRect.origin
+        fromRect: srcRectInCache
+       operation: op
+        fraction: delta];
+
+    [ctxt GSUndefineGState: gState];
+    DESTROY(cache);
+  }
+}
+
+/* Old code path that can probably partially be merged with the new native implementation.
+Fallback for backends other than Cairo. */
+- (void) guiDrawInRect: (NSRect)dstRect
+              fromRect: (NSRect)srcRect
+             operation: (NSCompositingOperation)op
+              fraction: (float)delta
+{
+  NSGraphicsContext *ctxt = GSCurrentContext();
   NSAffineTransform *transform;
   NSSize s;
 
@@ -1141,6 +1272,21 @@
   }
 }
 
+- (void) drawInRect: (NSRect)dstRect
+           fromRect: (NSRect)srcRect
+          operation: (NSCompositingOperation)op
+           fraction: (float)delta
+{
+  if ([GSCurrentContext() supportsDrawGState])
+  {
+    [self nativeDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
+  }
+  else
+  {
+    [self guiDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
+  }
+}
+
 - (void) addRepresentation: (NSImageRep *)imageRep
 {
   GSRepData *repd;
Index: gui/Source/NSView.m
===================================================================
--- gui/Source/NSView.m	(revision 30158)
+++ gui/Source/NSView.m	(working copy)
@@ -3002,9 +3002,19 @@
   destPoint = aRect.origin;
   destPoint.x += delta.width;
   destPoint.y += delta.height;
+  if ([self isFlipped])
+    {
+      // FIXME: Probably won't play well with backends other than Cairo.
+      // Other backends might need to be updated to take in account the scaling 
+      // when computing the destination point (in theory we should also take in 
+      // account the rotation).
+      destPoint.y += aRect.size.height;
+    }
 
+  //NSLog(@"destPoint %@ in %@", NSStringFromPoint(destPoint), NSStringFromRect(_bounds));
+
   [self lockFocus];
-  NSCopyBits(0, aRect, destPoint);
+  NSCopyBits([[self window] gState], [self convertRect: aRect toView: nil], destPoint);
   [self unlockFocus];
 }
 
Index: gui/Headers/AppKit/NSGraphicsContext.h
===================================================================
--- gui/Headers/AppKit/NSGraphicsContext.h	(revision 30158)
+++ gui/Headers/AppKit/NSGraphicsContext.h	(working copy)
@@ -410,6 +410,12 @@
 	    fromRect: (NSRect)srcRect
 	   operation: (NSCompositingOperation)op
 	    fraction: (float)delta;
+- (BOOL) supportsDrawGState;
+- (void) GSdraw: (int)gstateNum
+        toPoint: (NSPoint)aPoint
+       fromRect: (NSRect)srcRect
+      operation: (NSCompositingOperation)op
+       fraction: (float)delta;
 - (void) GSDrawImage: (NSRect)rect : (void *)imageref;
 
 /* ----------------------------------------------------------------------- */
Index: back/Source/art/composite.m
===================================================================
--- back/Source/art/composite.m	(revision 30158)
+++ back/Source/art/composite.m	(working copy)
@@ -415,7 +415,6 @@
   return YES;
 }
 
-
 - (void) compositeGState: (GSGState *)source
                 fromRect: (NSRect)aRect
                  toPoint: (NSPoint)aPoint
Index: back/Source/cairo/CairoContext.m
===================================================================
--- back/Source/cairo/CairoContext.m	(revision 30158)
+++ back/Source/cairo/CairoContext.m	(working copy)
@@ -96,6 +96,11 @@
   return YES;
 }
 
+- (BOOL) supportsDrawGState
+{
+	return YES;
+}
+
 - (void) flushGraphics
 {
 #if BUILD_SERVER == SERVER_x11
Index: back/Source/cairo/CairoGState.m
===================================================================
--- back/Source/cairo/CairoGState.m	(revision 30158)
+++ back/Source/cairo/CairoGState.m	(working copy)
@@ -1038,6 +1038,17 @@
     }
 }
 
+/* For debugging */
+- (void) drawOrientationMarkersIn: (cairo_t *)ct
+{
+  cairo_rectangle(_ct, 0, 0, 20, 10);
+  cairo_set_source_rgba(_ct, 0, 1, 0, 1);
+  cairo_fill(_ct);
+  cairo_rectangle(_ct, 0, 30, 20, 10);
+  cairo_set_source_rgba(_ct, 0, 0, 1, 1);
+  cairo_fill(_ct);
+}
+
 - (void) DPSimage: (NSAffineTransform *)matrix : (int)pixelsWide
 		 : (int)pixelsHigh : (int)bitsPerSample 
 		 : (int)samplesPerPixel : (int)bitsPerPixel
@@ -1197,45 +1208,26 @@
 		    tstruct.tX, tstruct.tY);
   cairo_transform(_ct, &local_matrix);
 
-  // Make up for flip done in GUI
-  if (viewIsFlipped)
-    {
-      cairo_pattern_t *cpattern;
-      cairo_matrix_t local_matrix;
-      
-      cpattern = cairo_pattern_create_for_surface(surface);
-      cairo_matrix_init_scale(&local_matrix, 1, -1);
-      cairo_matrix_translate(&local_matrix, 0, -2*pixelsHigh);
-      cairo_pattern_set_matrix(cpattern, &local_matrix);
-      if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
-        {
-          cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
-        }
-      cairo_set_source(_ct, cpattern);
-      cairo_pattern_destroy(cpattern);
-
-      cairo_rectangle(_ct, 0, pixelsHigh, pixelsWide, pixelsHigh);
-    }
-  else 
-    {
-      cairo_pattern_t *cpattern;
-      cairo_matrix_t local_matrix;
-      
-      cpattern = cairo_pattern_create_for_surface(surface);
-      cairo_matrix_init_scale(&local_matrix, 1, -1);
-      cairo_matrix_translate(&local_matrix, 0, -pixelsHigh);
-      cairo_pattern_set_matrix(cpattern, &local_matrix);
-      if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
-        {
-          cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
-        }
-      cairo_set_source(_ct, cpattern);
-      cairo_pattern_destroy(cpattern);
-
-      cairo_rectangle(_ct, 0, 0, pixelsWide, pixelsHigh);
-    }
+  {
+    cairo_pattern_t *cpattern;
+    cairo_matrix_t source_matrix;
+ 
+    cpattern = cairo_pattern_create_for_surface(surface);
+    cairo_matrix_init_scale(&source_matrix, 1, -1);
+    cairo_matrix_translate(&source_matrix, 0, -pixelsHigh);
+    cairo_pattern_set_matrix(cpattern, &source_matrix);
+    if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
+      {
+        cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
+      }
+    cairo_set_source(_ct, cpattern);
+    cairo_pattern_destroy(cpattern);
+    cairo_rectangle(_ct, 0, 0, pixelsWide, pixelsHigh);
+  }
+ 
   cairo_clip(_ct);
   cairo_paint(_ct);
+  //[self drawOrientationMarkersIn: _ct];
   cairo_surface_destroy(surface);
   cairo_restore(_ct);
 
@@ -1271,94 +1263,114 @@
 }
 
 - (void) compositeGState: (CairoGState *)source 
-                fromRect: (NSRect)aRect 
-                 toPoint: (NSPoint)aPoint 
+                fromRect: (NSRect)srcRect 
+                 toPoint: (NSPoint)destPoint 
                       op: (NSCompositingOperation)op
                 fraction: (float)delta
 {
-  cairo_surface_t *src;
+  cairo_surface_t *src = cairo_get_target(source->_ct);
+  NSSize ssize = NSZeroSize;
+  BOOL copyOnSelf = (src == cairo_get_target(_ct));
+  /* The source rect in the source base coordinate space.
+     This rect is the minimum bounding rect of srcRect. */
+  NSRect srcRectInBase = NSZeroRect;
+  /* The destination point in the target base coordinate space */
+  NSPoint destPointInBase = NSZeroPoint;
+  /* The origin of srcRectInBase */
   double minx, miny;
+  /* The composited content size */
   double width, height;
+  /* The adjusted destination point in the target base coordinate space */
   double x, y;
-  NSSize ssize;
+  /* Alternative source rect origin in the source current CTM */
+  NSPoint srcRectAltOrigin = NSMakePoint(srcRect.origin.x, srcRect.origin.y + srcRect.size.height);
+  /* Alternative source rect origin in the source base coordinate space */
+  NSPoint srcRectAltOriginInBase =  [source->ctm transformPoint: srcRectAltOrigin];
+  /* The source rect origin in the source base coordinate space */
+  NSPoint srcRectOriginInBase = [source->ctm transformPoint: srcRect.origin];
+  BOOL originFlippedBetweenBaseAndSource = NO;
+  /* The delta between the origins of srcRect and srcRectInBase */
+  double dx, dy;
   cairo_pattern_t *cpattern;
-  cairo_matrix_t local_matrix;
-  BOOL copyOnSelf = NO;
+  cairo_matrix_t source_matrix;
 
   if (!_ct || !source->_ct)
     {
       return;
     }
 
-  /* 
-   * we check if we copy on ourself, if that's the case
-   * we'll use the groups trick...
-   */
- 
-  src = cairo_get_target(source->_ct);
-  if (src == cairo_get_target(_ct))
-    {
-      NSRect targetRect;
+  //NSLog(@"Composite surface %p source size %@ target size %@", self->_surface, NSStringFromSize([self->_surface size]), NSStringFromSize([source->_surface size]));
 
-      targetRect.origin = aPoint;
-      targetRect.size = aRect.size;
-
-      if (!NSIsEmptyRect(NSIntersectionRect(aRect, targetRect)))
-        {
-          copyOnSelf = YES;
-        }
-    }
-
   cairo_save(_ct);
 
+  /* When the target and source are the same surface, we use the group tricks */
   if (copyOnSelf) cairo_push_group(_ct);
 
   cairo_new_path(_ct);
-  _set_op(_ct, op);
-  
-  if (viewIsFlipped && !copyOnSelf)
-    {
-      aPoint.y -= aRect.size.height;
-    }
+  _set_op(_ct, op); 
 
-  {
-      NSRect newRect;
-      
-      newRect.origin = aPoint;
-      newRect.size = aRect.size;
-      [ctm boundingRectFor: newRect result: &newRect];
-      aPoint = newRect.origin;
-  }
+  //NSLog(@"Point %@", NSStringFromPoint(destPoint));
 
-  [source->ctm boundingRectFor: aRect result: &aRect];
-  if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
+  /* Scales and/or rotates the local destination point with the current AppKit CTM */
+  destPointInBase = [ctm transformPoint: destPoint];
+  //NSLog(@"Point in base %@", NSStringFromPoint(destPointInBase));
+
+  /* Scales and/or rotates the source rect and retrieves the minimum bounding
+     rectangle that encloses it and makes it our source area */
+  [source->ctm boundingRectFor: srcRect result: &srcRectInBase];
+  //NSLog(@"Bounding rect %@ from %@", NSStringFromRect(srcRectInBase), NSStringFromRect(srcRect)); 
+
+  /* Find whether the source rect origin in the base is the same than in the 
+     source current CTM.
+     We need to know the origin in the base to compute how much the source 
+     bounding rect origin is shifted relatively to the closest source rect corner.
+     We use this delta (dx, dy) to correctly composite from a rotated source. */
+  originFlippedBetweenBaseAndSource = 
+    ((srcRect.origin.y < srcRectAltOrigin.y && srcRectOriginInBase.y > srcRectAltOriginInBase.y)
+    || (srcRect.origin.y > srcRectAltOrigin.y && srcRectOriginInBase.y < srcRectAltOriginInBase.y));
+  if (originFlippedBetweenBaseAndSource)
     {
-      NSSize size = [source->_surface size];
-      
-      // For cairo > 1.8 we seem to need this adjustment
-      aRect.origin.y -= 2*(source->offset.y - size.height);
+      srcRectOriginInBase = srcRectAltOriginInBase;
     }
+  dx = srcRectOriginInBase.x - srcRectInBase.origin.x;
+  dy = srcRectOriginInBase.y - srcRectInBase.origin.y;
 
-  x = floorf(aPoint.x);
-  y = floorf(aPoint.y + 0.5);
-  minx = NSMinX(aRect);
-  miny = NSMinY(aRect);
-  width = NSWidth(aRect);
-  height = NSHeight(aRect);
+  //NSLog(@"Point in base adjusted %@", NSStringFromPoint(NSMakePoint(destPointInBase.x - dx, destPointInBase.y - dy)));
 
   if (source->_surface != nil)
     {
       ssize = [source->_surface size];
     }
-  else 
-    {
-      ssize = NSMakeSize(0, 0);
+
+  if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
+    {      
+      // For cairo > 1.8 we seem to need this adjustment
+      srcRectInBase.origin.y -= 2 * (source->offset.y - ssize.height);
     }
 
+  x = floorf(destPointInBase.x);
+  y = floorf(destPointInBase.y + 0.5);
+  minx = NSMinX(srcRectInBase);
+  miny = NSMinY(srcRectInBase);
+  width = NSWidth(srcRectInBase);
+  height = NSHeight(srcRectInBase);
+
+  /* We respect the AppKit CTM effect on the origin 'aPoint' (see 
+     -[ctm transformPoint:]), but we ignore the scaling and rotation effect on 
+     the composited content and size. Which means we never rotate or scale the 
+     content we composite.
+     We use a pattern as a trick to simulate a target CTM change, this way we 
+     don't touch the source CTM even when both source and target are identical 
+     (e.g. scrolling case).
+     We must use a pattern matrix that matches the AppKit base CTM set up in 
+     -DPSinitgraphics to ensure no transform is applied to the source content, 
+     translation adjustements related to destination point and source rect put 
+     aside. */
   cpattern = cairo_pattern_create_for_surface(src);
-  cairo_matrix_init_scale(&local_matrix, 1, -1);
-  cairo_matrix_translate(&local_matrix, -x + minx, - ssize.height - y + miny);
-  cairo_pattern_set_matrix(cpattern, &local_matrix);
+  cairo_matrix_init_scale(&source_matrix, 1, -1);
+  //cairo_matrix_translate(&source_matrix, 0,  -[_surface size].height);
+  cairo_matrix_translate(&source_matrix, minx - x + dx, miny - y + dy - ssize.height);
+  cairo_pattern_set_matrix(cpattern, &source_matrix);
   cairo_set_source(_ct, cpattern);
   cairo_pattern_destroy(cpattern);
   cairo_rectangle(_ct, x, y, width, height);
@@ -1382,6 +1394,82 @@
   cairo_restore(_ct);
 }
 
+/** Unlike -compositeGState, -drawGSstate fully respects the AppKit CTM but 
+doesn't support to use the receiver cairo target as the source. */
+- (void) drawGState: (CairoGState *)source 
+           fromRect: (NSRect)aRect 
+            toPoint: (NSPoint)aPoint 
+                 op: (NSCompositingOperation)op
+           fraction: (float)delta
+{
+  NSAffineTransformStruct tstruct =  [ctm transformStruct];
+  cairo_surface_t *src = cairo_get_target(source->_ct);
+  double width, height;
+  double x, y;
+  cairo_pattern_t *cpattern;
+  cairo_matrix_t local_matrix;
+  cairo_matrix_t source_matrix;
+
+  if (!_ct || !source->_ct)
+    {
+      return;
+    }
+
+  cairo_save(_ct);
+
+  cairo_new_path(_ct);
+  _set_op(_ct, op);
+  
+  if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
+    {
+      NSSize size = [source->_surface size];
+      
+      // For cairo > 1.8 we seem to need this adjustment
+      aRect.origin.y -= 2*(source->offset.y - size.height);
+    }
+
+  x = floorf(aPoint.x);
+  y = floorf(aPoint.y + 0.5);
+  width = NSWidth(aRect);
+  height = NSHeight(aRect);
+
+  // NOTE: We don't keep the Cairo matrix in sync with the AppKit matrix (aka 
+  // -[NSGraphicsContext GSCurrentCTM])
+
+  /* Prepare a Cairo matrix with the current AppKit CTM */
+  cairo_matrix_init(&local_matrix,
+		    tstruct.m11, tstruct.m12,
+		    tstruct.m21, tstruct.m22, 
+		    tstruct.tX, tstruct.tY);
+  /* Append the local point transformation */
+  cairo_matrix_translate(&local_matrix, x - aRect.origin.x, y - aRect.origin.y);
+  /* Concat to the Cairo matrix created in -DPSinitgraphics, which adjusts the 
+     mismatch between the Cairo top left vs AppKit bottom left origin. */
+  cairo_transform(_ct, &local_matrix);
+
+  //[self drawOrientationMarkersIn: _ct];
+
+  cpattern = cairo_pattern_create_for_surface(src);
+  cairo_matrix_init_scale(&source_matrix, 1, -1);
+  cairo_matrix_translate(&source_matrix, 0, -[source->_surface size].height);
+  cairo_pattern_set_matrix(cpattern, &source_matrix);
+  cairo_set_source(_ct, cpattern);
+  cairo_pattern_destroy(cpattern);
+  cairo_rectangle(_ct, aRect.origin.x, aRect.origin.y, width, height);
+  cairo_clip(_ct);
+
+  if (delta < 1.0)
+    {
+      cairo_paint_with_alpha(_ct, delta);
+    }
+  else
+    {
+      cairo_paint(_ct);
+    }
+
+  cairo_restore(_ct);
+}
+
 @end
 
 @implementation CairoGState (PatternColor)
Index: back/Source/gsc/GSContext.m
===================================================================
--- back/Source/gsc/GSContext.m	(revision 30158)
+++ back/Source/gsc/GSContext.m	(working copy)
@@ -821,6 +821,32 @@
                  fraction: delta];
 }
 
+- (BOOL) supportsDrawGState
+{
+	return NO;
+}
+
+- (void) GSdraw: (int)gstateNum
+	toPoint: (NSPoint)aPoint
+       fromRect: (NSRect)srcRect
+      operation: (NSCompositingOperation)op
+       fraction: (float)delta
+{
+  GSGState *g = gstate;
+
+  if (gstateNum)
+    {
+      [self DPSexecuserobject: gstateNum];
+      ctxt_pop(g, opstack, GSGState);
+    }
+
+  [gstate drawGState: g
+	    fromRect: srcRect
+             toPoint: aPoint
+                  op: op
+            fraction: delta];
+}
+
 - (void) GSDrawImage: (NSRect) rect: (void *) imageref
 {
   NSBitmapImageRep *bitmap;
Index: back/Headers/gsc/GSGState.h
===================================================================
--- back/Headers/gsc/GSGState.h	(revision 30158)
+++ back/Headers/gsc/GSGState.h	(working copy)
@@ -105,6 +105,16 @@
 
 @end
 
+/** Informal protocol to which backends can conform to when they support drawing a 
+graphics state with arbitrary transforms on the current graphics context. */
+@interface NSObject (GSDrawGState)
+- (void) drawGState: (GSGState *)source 
+           fromRect: (NSRect)aRect 
+            toPoint: (NSPoint)aPoint 
+                 op: (NSCompositingOperation)op
+           fraction: (float)delta;
+@end
+
 #include "GSGStateOps.h"
 
 #endif /* _GSGState_h_INCLUDE */
