Search code examples
iphoneobjective-cquartz-graphicsquartz-2d

CATiledLayer, CGContextDrawImage and CGContextTranslateCTM


I have a large UIScrollView which contains a CATiledLayer which I'm using to draw tiles of a much larger in drawRect: like this:

- (void)drawRect:(CGRect)rect {
    int firstCol = floorf(CGRectGetMinX(rect) / tileSize);
    int lastCol = floorf((CGRectGetMaxX(rect)-1) / tileSize);
    int firstRow = floorf(CGRectGetMinY(rect) / tileSize);
    int lastRow = floorf((CGRectGetMaxY(rect)-1) / tileSize);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0, tileSize);
CGContextScaleCTM(context, 1.0, -1.0);

    for( int row = firstRow; row <= lastRow; row++ ) {
    for( int col = firstCol; col <= lastCol; col++ ) {
            UIImage = [self getTileWithRow:row column:col];

            CGRect tileRect = CGRectMake((col * tileSize), 
                                         row * tileSize),
                                         tileSize, tileSize);

            CGContextTranslateCTM(context, 0, tileRect.size.height);
            CGContextScaleCTM(context, 1.0, -1.0);
            CGContextDrawImage(context, tileRect, tile.CGImage);
        }
    }

    CGContextRestoreGState(context);
}

This works when I comment out the CGContextSaveGState, CGContextSaveGState, CGContextScaleCTM and CGContextRestoreGState calls but the images are upside down. With the calls in place the image is not drawn at all. I could use [tile drawInRect:] but that draws the rows in reverse which screws up the larger image.

What am I doing wrong with the translation?

EDIT: Moved save/restore and transform out of the loop as suggested but it's still not drawing anything.


Solution

  • Setting the right transformations to flip the content vertically is notoriously hard. The likely cause of not seeing anything, is because the transformations move the image outside of the rect. I have made it work before, but don't remember how I did it. Now I set "geometryFlipped = YES" on the CATiledLayer instead, which does the flipping for me.

    By the way, why don't you set the "tileSize" of the CATiledLayer to the size of your tiles, then you don't need this for-loop tile mapping stuff. The drawRect is called once for each of your tiles, so you can then do simply:

    - (void)drawRect:(CGRect)rect 
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        int col = floorf(CGRectGetMinX(rect) / tileSize);
        int row = floorf(CGRectGetMinY(rect) / tileSize);
    
        UIImage tile = [self getTileWithRow:row column:col];
    
        CGContextDrawImage(context, rect, tile.CGImage);
    }