views:

14622

answers:

7

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

UIImage *image = [UIImage imageNamed:@"testImage.png"];

And then simply asking core graphics to draw it to my context:

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

It renders in the right place, and dimensions, but the image is upside down. I must be missing something really obvious here?

+3  A: 

I'm not sure for UIImage, but this kind of behaviour usually occurs when coordinates are flipped. Most of OS X coordinate systems have their origin at the lower left corner, as in Postscript and PDF. But CGImage coordinate system has its origin at the upper left corner.

Possible solutions may involve an isFlipped property or a scaleYBy:-1 affine transform.

mouviciel
You are right, I looked at the 'QuartzDemo' supplied by Apple and it shows how to temporarily apply a transform to fix this behaviour. Thanks for the head start :)
rustyshelf
+27  A: 

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:

Quartz 2d 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 Quartz 2D 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 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.

Raal
this is good but i found that other stuff was being influenced by the translate and scale even when i tried setting back to 0,0 and 1.0,1.0 I went with Kendall's solution in the end but i realise this that is using UIImage rather than the lower level CGImage stuff that we are working with here
PeanutPower
+21  A: 

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 UI image holding onto knowledge of the orientation while the CGContextDrawImage method gets the underlying raw image data with no understanding of orientation.

Note that you'll run into this problem in many areas, but one specific example is dealing with address book user images.

Kendall Helmstetter Gelner
This solution doesn't allow you to use CG functions on your image, such as CGContextSetAlpha(), whereas the 2nd answer does.
Sam V
Good point, though mostly you don't need custom alpha levels for drawing an image (typically those are baked into images ahead of time for things that need alpha). Basically my motto is, use a little code as you can because more code means more chances for bugs.
Kendall Helmstetter Gelner
Hi, What do you mean by in the middle of your begin/end CGContext methods, can you give me more sample code. I am trying with storing the context somewhere and use the context to draw the image in
vodkhang
You shouldn't store the context, use UIGraphicsGetCurrentContext() to get a context you can pass to that method. There's also a UIGraphicsBeginImageContext and UIGraphicsEndImageContext pair of methods that you use when you are drawing into a custom image, instead of the current context.
Kendall Helmstetter Gelner
+2  A: 

http://trandangkhoa.blogspot.com/2009/07/iphone-os-drawing-image-and-stupid.html The code in this article fix problem related to inverse image and you can use that to solve your problem also. It takes me more than 1 day to find the trick.

Why Apple always do the complicated thing?

I did exactly this comment about Apple's documentation on their forum and one of their moderators told me their documentation is great. I particularly found their documentation is a crap, and the main reason is that the documentation is non-linear, written as a web page and and even a newbie knows how bad is to read a web site with zillions of pages. Documentation has to be linear, like a book. If you are reading a text and suddenly there's a link for 10 pages that you should know before proceeding, then you get lost in a sea of open pages going nowhere.
Digital Robot
irrelevant comment: I totally agree, Apple's documentation is horribly inadequate. I'm glad I started developing on the iphone AFTER they opened up the NDA. How did they do it before?
Brenden
+1  A: 

drawInRect is certainly the way to go. Here's another little thing that will come in way useful when doing this. Usually the picture and the rectangle into which it is going to go don't conform. In that case drawInRect will stretch the picture. Here's a quick and cool way to make sure that the picture's aspect ration isn't changed, by reversing the transformation (which will be to fit the whole thing in):

//Picture and irect don't conform, so there'll be stretching, compensate
    float xf = Picture.size.width/irect.size.width;
    float yf = Picture.size.height/irect.size.height;
    float m = MIN(xf, yf);
    xf /= m;
    yf /= m;
    CGContextScaleCTM(ctx, xf, yf);

    [Picture drawInRect: irect];
marc meyer
+1  A: 

UIImage contains a CGImage as its main content member as well as scaling and orientation factors. Since CGImage and its various functions are derived from OSX, it expects a coordinate system that is upside down compared to the iPhone. When you create a UIImage, it defaults to an upside-down orientation to compensate (you can change this!). Use the .CGImage property to access the very powerful CGImage functions, but drawing onto the iPhone screen etc. is best done with the UIImage methods.

James L