Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CGContextDrawImage draws image upside down when passed UIImage.CGImage

Instead of

CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);

Use

[image drawInRect:CGRectMake(0, 0, 145, 15)];

In the middle of your begin/end CGcontext methods.

This will draw the image with the correct orientation into your current image context - I'm pretty sure this has something to do with the UIImage holding onto knowledge of the orientation while the CGContextDrawImage method gets the underlying raw image data with no understanding of orientation.


Even after applying everything I have mentioned, I've still had dramas with the images. In the end, i've just used Gimp to create a 'flipped vertical' version of all my images. Now I don't need to use any Transforms. Hopefully this won't cause further problems down the track.

Does anyone know why CGContextDrawImage would be drawing my image upside down? I am loading an image in from my application:

Quartz2d uses a different co-ordinate system, where the origin is in the lower left corner. So when Quartz draws pixel x[5], y[10] of a 100 * 100 image, that pixel is being drawn in the lower left corner instead of the upper left. Thus causing the 'flipped' image.

The x co-ordinate system matches, so you will need to flip the y co-ordinates.

CGContextTranslateCTM(context, 0, image.size.height);

This means we have translated the image by 0 units on the x axis and by the images height on the y axis. However, this alone will mean our image is still upside down, just being drawn "image.size.height" below where we wish it to be drawn.

The Quartz2D programming guide recommends using ScaleCTM and passing negative values to flip the image. You can use the following code to do this -

CGContextScaleCTM(context, 1.0, -1.0);

Combine the two just before your CGContextDrawImage call and you should have the image drawn correctly.

UIImage *image = [UIImage imageNamed:@"testImage.png"];    
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);       

CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage(context, imageRect, image.CGImage);

Just be careful if your imageRect co-ordinates do not match those of your image, as you can get unintended results.

To convert back the coordinates:

CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);

Best of both worlds, use UIImage's drawAtPoint: or drawInRect: while still specifying your custom context:

UIGraphicsPushContext(context);
[image drawAtPoint:CGPointZero]; // UIImage will handle all especial cases!
UIGraphicsPopContext();

Also you avoid modifying your context with CGContextTranslateCTM or CGContextScaleCTM which the second answer does.


Relevant Quartz2D docs: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4

Flipping the Default Coordinate System

Flipping in UIKit drawing modifies the backing CALayer to align a drawing environment having a LLO coordinate system with the default coordinate system of UIKit. If you only use UIKit methods and function for drawing, you shouldn’t need to flip the CTM. However, if you mix Core Graphics or Image I/O function calls with UIKit calls, flipping the CTM might be necessary.

Specifically, if you draw an image or PDF document by calling Core Graphics functions directly, the object is rendered upside-down in the view’s context. You must flip the CTM to display the image and pages correctly.

To flip a object drawn to a Core Graphics context so that it appears correctly when displayed in a UIKit view, you must modify the CTM in two steps. You translate the origin to the upper-left corner of the drawing area, and then you apply a scale translation, modifying the y-coordinate by -1. The code for doing this looks similar to the following:

CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);