15

I have a UIImageView with an image in it. I have rotated the image prior to display by setting the transform property of the UIImageView to CGAffineTransformMakeRotation(angle) where angle is the angle in radians.

I want to be able to create another UIImage that corresponds to the rotated version that I can see in my view.

I am almost there, by rotating the image context I get a rotated image:

- (UIImage *) rotatedImageFromImageView: (UIImageView *) imageView
{
    UIImage *rotatedImage;

    // Get image width, height of the bounding rectangle
    CGRect boundingRect = [self getBoundingRectAfterRotation: imageView.bounds byAngle:angle];

    // Create a graphics context the size of the bounding rectangle
    UIGraphicsBeginImageContext(boundingRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Rotate and translate the context
    CGAffineTransform ourTransform = CGAffineTransformIdentity;
    ourTransform = CGAffineTransformConcat(ourTransform, CGAffineTransformMakeRotation(angle));

    CGContextConcatCTM(context, ourTransform);

    // Draw the image into the context
    CGContextDrawImage(context, CGRectMake(0, 0, imageView.image.size.width, imageView.image.size.height), imageView.image.CGImage);

    // Get an image from the context
    rotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];

    // Clean up
    UIGraphicsEndImageContext();
    return rotatedImage;
 }

However the image is not rotated about its centre. I have tried all kinds of transforms concatenated with my rotate to get it to rotate around the centre but to no avail. Am I missing a trick? Is this even possible since I am rotating the context not the image?

Getting desperate to make this work now, so any help would be appreciated.

Dave

EDIT: I've been asked several times for my boundingRect code, so here it is:

- (CGRect) getBoundingRectAfterRotation: (CGRect) rectangle byAngle: (CGFloat) angleOfRotation {
    // Calculate the width and height of the bounding rectangle using basic trig
    CGFloat newWidth = rectangle.size.width * fabs(cosf(angleOfRotation)) + rectangle.size.height * fabs(sinf(angleOfRotation));
    CGFloat newHeight = rectangle.size.height * fabs(cosf(angleOfRotation)) + rectangle.size.width * fabs(sinf(angleOfRotation));

    // Calculate the position of the origin
    CGFloat newX = rectangle.origin.x + ((rectangle.size.width - newWidth) / 2);
    CGFloat newY = rectangle.origin.y + ((rectangle.size.height - newHeight) / 2);

    // Return the rectangle
    return CGRectMake(newX, newY, newWidth, newHeight);
}
Magic Bullet Dave
  • 9,006
  • 10
  • 51
  • 81
  • What is this `getBoundingRectAfterRotation` function? – bmdhacks Nov 12 '11 at 04:37
  • Hi @bmdhacks the getBoundingRectAfterRotation is a method I have created, so you wont find it in any of the Cocoa-Touch classes. It takes a rect and an angle as arguments and returns a new CGRect that would encompass the original rect if it were rotated by the given angle. – Magic Bullet Dave Nov 13 '11 at 22:30
  • Hi @bmdhacks due to popular demand have added the method in an edit to my original question. – Magic Bullet Dave Nov 22 '11 at 12:19
  • this should do it
    http://stackoverflow.com/questions/1315251/how-to-rotate-a-uiimage-90-degrees
    – zapping May 24 '10 at 09:39

1 Answers1

22

OK - at last I seem to have done it. Any comments on the correctness would be useful... needed a translate, a rotate, a scale and an offset from the drawing rect position to make it work. Code is here:

CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, boundingRect.size.width/2, boundingRect.size.height/2);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0);

CGContextConcatCTM(context, transform);

// Draw the image into the context
CGContextDrawImage(context, CGRectMake(-imageView.image.size.width/2, -imageView.image.size.height/2, imageView.image.size.width, imageView.image.size.height), imageView.image.CGImage);

// Get an image from the context
rotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];
Magic Bullet Dave
  • 9,006
  • 10
  • 51
  • 81
  • 1
    why do you need `transform = CGAffineTransformScale(transform, 1.0, -1.0);` ? – Hlung Mar 03 '13 at 07:53
  • 3
    The CoreGraphics uses a co-ordinate system where the origin is the bottom left. UIKit objects use an origin at the top left with the y-axis increasing as you move down the screen. This transform flips the y-axis to ensure the image is drawn the right way up. – Magic Bullet Dave Mar 03 '13 at 08:19
  • 1
    Thanks! I was struggling on drawing image on context with translate, rotation, and scaling. I tried UIImage's `drawInRect:` and translate the context but it always rotate around top left (0,0). Your solution is the only one that works! :) – Hlung Mar 03 '13 at 09:43
  • Hi, this code does not work with photos that has different orientation than UIImageOrientationUp, do you have any solution? – Pion Apr 12 '13 at 16:18