I'm having some trouble with image rotation in iOS. I'm performing some image manipulation in the background of an app... I would like to rotate and crop the images. Currently, the rotation seems to be working correctly, but no matter what I have tried, the crop is off. I have performed these same operations in GIMP and found that the images crop correctly, so I believe it has something to do with the Quartz coordinate system. Furthermore, the greater the radians in either direction, the further "off" the crop becomes. Here is the code I am using to rotate and crop:
+(UIImage*)imageWithImageFile:(NSString*)imageFile
radians:(float)radians
imageSize:(CGSize)imageSize
croppedToRect:(CGRect)croppedToRect
{
UIImage* returnImg = nil;
#autoreleasepool {
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);
UIGraphicsBeginImageContext(croppedToRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, YES);
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextTranslateCTM(context, imageSize.width * 0.5,
imageSize.height * 0.5);
CGContextRotateCTM(context, radians);
CGContextTranslateCTM(context, -imageSize.width * 0.5, -imageSize.height * 0.5);
//Translate and scale upside-down to compensate for Quartz's inverted coordinate system
CGContextTranslateCTM(context, 0.0, imageSize.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, (CGRect) {croppedToRect.origin, imageSize}, image);
returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGDataProviderRelease(dataProvider);
CGImageRelease(image);
}
return returnImg;
}
I believe this is the solution: Since the context is a different size and has a different origin inside the original image, you must offset the point where you "paint" the image on the context (canvas for those of you more visual people). You first find the origin of the centered context, then compare it to the origin of the desired crop. The difference (or offset) can be subtracted from the center point of the context, which will offset the point at which the image is painted.
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);
UIGraphicsBeginImageContext(croppedToRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint contextCenter = CGPointMake(croppedToRect.size.width * 0.5f,
croppedToRect.size.height * 0.5f);
CGPoint centerOfImage = CGPointMake(imageSize.width * 0.5f,
imageSize.height * 0.5f);
CGPoint contextOriginInImage = CGPointMake(centerOfImage.x - contextCenter.x,
centerOfImage.y - contextCenter.y);
CGPoint desiredOrigin = croppedToRect.origin;
CGPoint offset = CGPointMake(desiredOrigin.x - contextOriginInImage.x,
desiredOrigin.y - contextOriginInImage.y);
CGContextTranslateCTM(context, contextCenter.x - offset.x, contextCenter.y - offset.y);
CGContextRotateCTM(context, radians);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextDrawImage(context, CGRectMake(-imageSize.width * 0.5f,
-imageSize.height * 0.5f,
imageSize.width,
imageSize.height), image);
returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGDataProviderRelease(dataProvider);
CGImageRelease(image);
Related
I want to create a button that rotates an image in a UIImageView several times. After that I have to send it to an external API. I need not loose the proportions (like in the images)
Image after rotatation
Image before rotation
I have try two options
Option 1: I loose the proportions
(UIImage *)imageRotatedByDegrees:(UIImage*)oldImage deg:(CGFloat)degrees
{
// calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,oldImage.size.width, oldImage.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(degrees * M_PI / 180);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
// // Rotate the image context
CGContextRotateCTM(bitmap, (degrees * M_PI / 180));
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-oldImage.size.width / 2, -oldImage.size.height / 2, oldImage.size.width, oldImage.size.height), [oldImage CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Option 2:
(UIImage*)upsideDownBunny:(CGFloat)radians withImage:(UIImage*)testImage {
__block CGImageRef cgImg;
__block CGSize imgSize;
__block UIImageOrientation orientation;
dispatch_block_t createStartImgBlock = ^(void) {
// UIImages should only be accessed from the main thread
UIImage *img =testImage;
imgSize = [img size]; // this size will be pre rotated
orientation = [img imageOrientation];
cgImg = CGImageRetain([img CGImage]); // this data is not rotated
};
if([NSThread isMainThread]) {
createStartImgBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), createStartImgBlock);
}
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
// in iOS4+ you can let the context allocate memory by passing NULL
CGContextRef context = CGBitmapContextCreate( NULL,
imgSize.width,
imgSize.height,
8,
imgSize.width * 4,
colorspace,
kCGImageAlphaPremultipliedLast);
// rotate so the image respects the original UIImage's orientation
switch (orientation) {
case UIImageOrientationDown:
CGContextTranslateCTM(context, imgSize.width, imgSize.height);
CGContextRotateCTM(context, -radians);
break;
case UIImageOrientationLeft:
CGContextTranslateCTM(context, 0.0, imgSize.height);
CGContextRotateCTM(context, 3.0 * -radians / 2.0);
break;
case UIImageOrientationRight:
CGContextTranslateCTM(context,imgSize.width, 0.0);
CGContextRotateCTM(context, -radians / 2.0);
break;
default:
// there are mirrored modes possible
// but they aren't generated by the iPhone's camera
break;
}
// rotate the image upside down
CGContextTranslateCTM(context, +(imgSize.width * 0.5f), +(imgSize.height * 0.5f));
CGContextRotateCTM(context, -radians);
//CGContextDrawImage( context, CGRectMake(0.0, 0.0, imgSize.width, imgSize.height), cgImg );
CGContextDrawImage(context, (CGRect){.origin.x = -imgSize.width* 0.9f , .origin.y = -imgSize.width* 0.5f , .size.width = imgSize.width, .size.height = imgSize.width}, cgImg);
// grab the new rotated image
CGContextFlush(context);
CGImageRef newCgImg = CGBitmapContextCreateImage(context);
__block UIImage *newImage;
dispatch_block_t createRotatedImgBlock = ^(void) {
// UIImages should only be accessed from the main thread
newImage = [UIImage imageWithCGImage:newCgImg];
};
if([NSThread isMainThread]) {
createRotatedImgBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), createRotatedImgBlock);
}
CGColorSpaceRelease(colorspace);
CGImageRelease(newCgImg);
CGContextRelease(context);
return newImage;
}
It works well with the rotation of the image but with every rotation the image and the view is reduced.
Can someone can help me?
If you want to animate the rotation you can use Core Animation. This will not change the image itself, just rotate it on the screen.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = numRotations;
[self.imgviewRotate.layer addAnimation:animation forKey:#"animation"];
Help, Im new to ios programming, I want to rotate may UIImage but I dont want the edges to be cut or loose some part of the image.
this is my code:
double angle = M_PI * 10/ 180; CGSize s = {image.size.width, image.size.height}; UIGraphicsBeginImageContext(s); CGContextRef ctx = UIGraphicsGetCurrentContext();
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, image.size.width/2, image.size.height/2);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx,CGRectMake(-[image size].width/2,-[image size].height/2,image.size.width, image.size.height),image.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil);
The image is rotating but the size of the frame does not change because of that some of the image has been cut.
OUTPUT:https://fbcdn-sphotos-e-a.akamaihd.net/hphotos-ak-xpf1/t1.0-9/1555286_761679470521535_1800180000235265553_n.jpg
EXPECTED OUTPUT: https://fbcdn-photos-g-a.akamaihd.net/hphotos-ak-xfa1/t1.0-0/10314770_761675840521898_6536715783383115855_a.jpg
Please help me thank you.
This might be what you are searching for.
Just copy the following code at the end of the .m file (after the #end) in which you want to rotate an image.
#interface UIImage (RotationMethods)
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees;
#end
#implementation UIImage (RotationMethods)
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees
{
// calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
// // Rotate the image context
CGContextRotateCTM(bitmap, DegreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Then rotate your image like in the example below:
CGFloat degrees = 90;
yourImage = [yourImage imageRotatedByDegrees:degrees];
In the code below I'm adding a small image to a large image. I need to be able to apply a transform to the small image. Right now something weird is going on, for example if I pass in a transform of CGAffineTransformMakeRotation(M_PI_2), the newImage is not rotated in place but rather drawn somewhere off screen. How can I fix this?
+ (UIImage*)addToImage:(UIImage *)baseImage newImage:(UIImage*)newImage atPoint:(CGPoint)point transform:(CGAffineTransform)transform {
UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
[baseImage drawInRect:CGRectMake(0, 0, baseImage.size.width, baseImage.size.height)];
CGRect newRect = CGRectMake(point.x,
point.y,
newImage.size.width,
newImage.size.height);
CGContextConcatCTM(context, transform);
[newImage drawInRect:newRect];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Note that I only want to apply the transform to newImage, not baseImage.
Seems like you just forgot to translate the origin of the context before and after the rotation. Here, try this:
- (UIImage*)addToImage:(UIImage *)baseImage newImage:(UIImage*)newImage atPoint:(CGPoint)point transform:(CGAffineTransform)transform {
UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect baseRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);
[baseImage drawInRect:baseRect];
CGRect newRect = CGRectMake(point.x, point.y, newImage.size.width, newImage.size.height);
CGContextTranslateCTM(context, point.x, point.y);
CGContextConcatCTM(context, transform);
CGContextTranslateCTM(context, -point.x, -point.y);
[newImage drawInRect:newRect];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
EDIT:
If you want to rotate the image around its center, add the halves of the image's sides to the corresponding translation values:
float xTranslation = point.x+newRect.size.width/2;
float yTranslation = point.y+newRect.size.height/2;
CGContextTranslateCTM(context, xTranslation, yTranslation);
CGContextConcatCTM(context, transform);
CGContextTranslateCTM(context, -xTranslation, -yTranslation);
I have an overlay of route to be placed over a map. I have to adjust its rotation so that it overlaps exactly with the underlying map view. How can I achieve this? I have found the angle to rotate. How can I rotate my overlay view? Far till now I have added the overlay but can't rotate using transforms. What I noticed is that when I change the angle, the image is not rotating, instead it slides along x axis(side ways).
Here is the code that I tried
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"routeImage" ofType:#"png"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:imagePath];
MKMapRect theMapRect = self.overlay.boundingMapRect;
CGRect theRect = [self rectForMapRect:theMapRect];
CGAffineTransform transform = CGAffineTransformMakeRotation((-35.88) / 180.0 * M_PI);
[self setTransform:transform];
CGContextSaveGState(context);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -theRect.size.height);
CGContextDrawImage(context, theRect,image.CGImage);
CGContextRestoreGState(context);
}
This is what I get
I have a workaround in which I manually rotate the image using GIMP and place it. But I need it to be customized.
This is the image transformed using GIMP and when placed on map as overlay
and this is when I rotate Context using transform [note: while rotating the context the image is rotated but is not at the exact location See image. Also the image breaks on zooming]
This is what I did
//Hayl-Road-Final.png is the actual image without transformation using GIMP
UIImage *image = [[UIImage imageNamed:#"Hayl-Road-Final.png"] retain];
CGImageRef imageReference = image.CGImage;
MKMapRect theMapRect = [self.overlay boundingMapRect];
CGRect theRect = [self rectForMapRect:theMapRect];
// We need to flip and reposition the image here
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextTranslateCTM(ctx, 0.0, -theRect.size.height);
CGContextRotateCTM(ctx,-35.88);
//drawing the image to the context
CGContextDrawImage(ctx, theRect, imageReference);
Try adding this method:
- (UIImage *) rotate: (UIImage *) image angle:(double)
{
//double angle = 20;
CGSize s = {image.size.width, image.size.height};
UIGraphicsBeginImageContext(s);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, 0,image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextRotateCTM(ctx, 2*M_PI*angle/360);
CGContextDrawImage(ctx,CGRectMake(0,0,image.size.width, image.size.height),image.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
From your code, you change order of two line. It will be ok:
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextRotateCTM(ctx,-35.88);
CGContextTranslateCTM(ctx, 0.0, -theRect.size.height);
CGContextDrawImage(ctx, theRect, imageReference);
It is correct with me, please try
I'm developing an iOS 6 app for iPad. I've developed some code which rotates a UIImage. It works great with square images, but when the images aren't square they get cropped, so you only see a part of the image (a square).
My code:
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM( context, 0.5f * image.size.width, 0.5f * image.size.height ) ;
CGContextRotateCTM( context, -1.5707963267949) ;
[image drawInRect:(CGRect){ { -imatgetemporal.size.width * 0.5f, -image.size.height * 0.5f }, image.size }];
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
I think the problem is in the 0.5f, but I don't know how to solve it.
What can I do?
You need to make your graphics context big enough for the rotated image. You're using image.size, but should be using a the rotated size as the argument to UIGraphicsBeginImageContext. Additionally, your argument to drawInRect is incorrect. You need to offset the (rotated) origin x, which determines the y origin of the final image, in the following way: "Up" by half the original height (which puts the final image entirely off the top of the drawing context) then "down" by the full original width, which is the final image height.
const CGSize imageSize = image.size;
UIGraphicsBeginImageContext(CGSizeMake(imageSize.height, imageSize.width));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.5f * imageSize.width, 0.5f * imageSize.height ) ;
CGContextRotateCTM(context, -1.5707963267949) ;
[image drawInRect:(CGRect){ { imageSize.height * 0.5 - imageSize.width, -imageSize.width * 0.5f }, imageSize }];
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();