UIImage rotation and orientation issue - objective-c

I'm using standart code to rotate image, but when UIImageRotatation is not equal to 2, the image is rotated not correctly.
Any code to fix UIImageRotation and rotate image by X degrees? Try nearly everything.
NSLog(#"Image orientation %d",self.imageOrientation);
CGSize rotatedSize = CGSizeApplyAffineTransform(self.size, CGAffineTransformMakeRotation(rad(degrees)));
if (rotatedSize.width < 0) {
rotatedSize.width *= -1;
}
if (rotatedSize.height < 0) {
rotatedSize.height *= -1;
}
// Create the bitmap context
UIGraphicsBeginImageContextWithOptions(rotatedSize, YES, 0.0);
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, rad(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;

Related

UIImage Resize with aspect ratio -> SAVE JPG -> LOAD -> image loaded twice bigger

im using this code to resize image.
- (UIImage*)resizeToSize:(CGSize)size {
float height = self.size.height;
float width = self.size.width;
if (width > size.width) {
width = size.width;
height = size.width / (self.size.width / self.size.height);
}
if (height > size.height) {
height = size.height;
width = size.height / (self.size.height / self.size.width);
}
NSLog(#"Resize to size %#",NSStringFromCGSize(size));
if (height == self.size.height && width == self.size.width) {
return self;
}
CGSize newSize = CGSizeMake(width, height);
UIGraphicsBeginImageContextWithOptions(newSize, YES, 0.0);
[self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"Resized %#",NSStringFromCGSize(newImage.size));
return newImage;
}
The image is resized and in next step i save it via [UIImageJPEGRepresentation(image, 1.0) writeToFile:pngPath atomically:YES];.
After that i load the file, and the image size is twice bigger, any hint why?
Thank you!
I suspect what is happening here is that you have an image with scale 2.0 (on a Retina device) but you are saving it without #2x in the filename, so when you reopen it, it is a scale 1.0 image with dimensions twice as large as you want.
If that's the case, there are two solutions: you can fix your filename, or you can re-open the file by reading the file into an NSData object and then use UIImage +imageWithData:scale: to get a properly-scaled image.

How to rotate UIImage

I'm developing an iOS app for iPad. Is there any way to rotate a UIImage 90ยบ and then add it to a UIImageView? I've tried a lot of different codes but none worked...
Thanks!
You may rotate UIImageView itself with:
UIImageView *iv = [[UIImageView alloc] initWithImage:image];
iv.transform = CGAffineTransformMakeRotation(M_PI_2);
Or if you really want to change image, you may use code from this answer, it works.
To rotate the pixels you can use the following. This creates an intermediate UIImage with rotated metadata and renders it into a image context with width/height dimensions transposed. The resulting image has the pixels rotated (i.e the underlying CGImage)
- (UIImage*)rotateUIImage:(UIImage*)sourceImage clockwise:(BOOL)clockwise
{
CGSize size = sourceImage.size;
UIGraphicsBeginImageContext(CGSizeMake(size.height, size.width));
[[UIImage imageWithCGImage:[sourceImage CGImage] scale:1.0 orientation:clockwise ? UIImageOrientationRight : UIImageOrientationLeft] drawInRect:CGRectMake(0,0,size.height ,size.width)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
There are other possible values that can be passed for the orientation parameter to achieve 180 degree rotation and flips etc.
This will rotate an image by any given degrees.
Note this works 2x and 3x retina as well
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees {
CGFloat radians = DegreesToRadians(degrees);
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(radians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2);
CGContextRotateCTM(bitmap, radians);
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;
}
There is also imageWithCIImage:scale:orientation if you wanted to rotate the UIImage not the UIImageView
with one of these orientations:
typedef enum {
UIImageOrientationUp,
UIImageOrientationDown, // 180 deg rotation
UIImageOrientationLeft, // 90 deg CW
UIImageOrientationRight, // 90 deg CCW
UIImageOrientationUpMirrored, // vertical flip
UIImageOrientationDownMirrored, // horizontal flip
UIImageOrientationLeftMirrored, // 90 deg CW then perform horizontal flip
UIImageOrientationRightMirrored, // 90 deg CCW then perform vertical flip
} UIImageOrientation;
Here is the swift version of #RyanG's Objective C code as an extension to UIImage:
extension UIImage {
func rotate(byDegrees degree: Double) -> UIImage {
let radians = CGFloat(degree*M_PI)/180.0 as CGFloat
let rotatedViewBox = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
let t = CGAffineTransform(rotationAngle: radians)
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(rotatedSize, false, scale)
let bitmap = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2);
bitmap!.rotate(by: radians);
bitmap!.scaleBy(x: 1.0, y: -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2 , self.size.width, self.size.height), self.CGImage );
let newImage = UIGraphicsGetImageFromCurrentImageContext()
return newImage
}
}
The usage is image.rotate(degree).
With Swift, you can rotate an image by doing:
var image: UIImage = UIImage(named: "headerBack.png")
var imageRotated: UIImage = UIImage(CGImage: image.CGImage, scale:1, orientation: UIImageOrientation.UpMirrored)
UIImage *img = [UIImage imageWithName#"aaa.png"];
UIImage *image = [UIImage imageWithCGImage:img.CGImage scale:1.0 orientation:UIImageOrientationRight];
Another way of doing this would be to render the UIImage again using Core Graphics.
Once you have the context, use CGContextRotateCTM.
More info on this Apple Doc
Thanks Jason Crocker this solved my problem. Only one minor correction, interchange height and width in both locations and no distortion occurs, ie,
UIGraphicsBeginImageContext(CGSizeMake(size.width, size.height));
[[UIImage imageWithCGImage:[sourceImage CGImage] scale:1.0 orientation:clockwise ? UIImageOrientationRight : UIImageOrientationLeft] drawInRect:CGRectMake(0,0,size.width,size.height)];
My problem could not be solved by CGContextRotateCTM, I don't know why. My issue is that I'm transmitting my image to a server and it was alway displayed off by 90 degrees. You can easily test if your images are going to work in the non apple world by copying the image to an MS Office Program that you are running on your mac.
This is what i've done when i wanted to change the orientation of an image (rotate 90 degree clockwise).
//Checking for the orientation ie, image taken from camera is in portrait or not.
if(yourImage.imageOrientation==3)
{
//Image is in portrait mode.
yourImage=[self imageToRotate:yourImage RotatedByDegrees:90.0];
}
- (UIImage *)image:(UIImage *)imageToRotate RotatedByDegrees:(CGFloat)degrees
{
CGFloat radians = degrees * (M_PI / 180.0);
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0, image.size.height, image.size.width)];
CGAffineTransform t = CGAffineTransformMakeRotation(radians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.height / 2, rotatedSize.width / 2);
CGContextRotateCTM(bitmap, radians);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-image.size.width / 2, -image.size.height / 2 , image.size.height, image.size.width), image.CGImage );
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
The rotated image may be of size >= 15MB (from my experience). So you should compress it and use it. Otherwise, you may met with crash causing memory pressure. Code I used for compressing is given below.
NSData *imageData = UIImageJPEGRepresentation(yourImage, 1);
//1 - it represents the quality of the image.
NSLog(#"Size of Image(bytes):%d",[imageData length]);
//Here I used a loop because my requirement was, the image size should be <= 4MB.
//So put an iteration for more than 1 time upto when the image size is gets <= 4MB.
for(int loop=0;loop<100;loop++)
{
if([imageData length]>=4194304) //4194304 = 4MB in bytes.
{
imageData=UIImageJPEGRepresentation(yourImage, 0.3);
yourImage=[[UIImage alloc]initWithData:imageData];
}
else
{
NSLog(#"%d time(s) compressed.",loop);
break;
}
}
Now your yourImage can be used for anywhere..
Happy coding...

Xcode Screenshot EAGLContext [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to get UIImage from EAGLView?
So I was just wondering if anybody knows any way to save what is stored in an EAGLContext as a UIImage.
I am currently using:
UIGraphicsBeginImageContext(CGSizeMake(768, 1024));
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
in other apps I have and this works fine, but obviously, EAGLContext doesn't have a .layer property. I've tried casting to UIView, but that - unsurprisingly - doesn't work:
UIView *newView = [[UIView alloc] init];
newView = (UIView *)context;
I am drawing to an EAGLContext property on a UIView (technically an EAGLContext on a UIView on another UIView on a View Controller, but I figure that shouldn't make any difference) using OpenGLES 1.
If anybody knows anything about this, even if its just that I'm completely barking up an impossible tree, please let me know!
Matt
After a few days I finally got a working solution to this. There is code provided by Apple which produces an UIImage from an EAGLView. Then you simply need to flip the image vertically since UIKit is upside down. The link to the documentation where I found this method doesn't exist anymore.
Method to capture EAGLView:
-(UIImage *)drawableToCGImage
{
GLint backingWidth2, backingHeight2;
//Bind the color renderbuffer used to render the OpenGL ES view
// If your application only creates a single color renderbuffer which is already bound at this point,
// this call is redundant, but it is needed if you're dealing with multiple renderbuffers.
// Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class.
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
// Get the size of the backing CAEAGLLayer
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth2);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight2);
NSInteger x = 0, y = 0, width2 = backingWidth2, height2 = backingHeight2;
NSInteger dataLength = width2 * height2 * 4;
GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
// Read pixel data from the framebuffer
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(x, y, width2, height2, GL_RGBA, GL_UNSIGNED_BYTE, data);
// Create a CGImage with the pixel data
// If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
// otherwise, use kCGImageAlphaPremultipliedLast
CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef iref = CGImageCreate(width2, height2, 8, 32, width2 * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
ref, NULL, true, kCGRenderingIntentDefault);
// OpenGL ES measures data in PIXELS
// Create a graphics context with the target size measured in POINTS
NSInteger widthInPoints, heightInPoints;
if (NULL != UIGraphicsBeginImageContextWithOptions) {
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// Set the scale parameter to your OpenGL ES view's contentScaleFactor
// so that you get a high-resolution snapshot when its value is greater than 1.0
CGFloat scale = self.contentScaleFactor;
widthInPoints = width2 / scale;
heightInPoints = height2 / scale;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale);
}
else {
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
widthInPoints = width2;
heightInPoints = height2;
UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints));
}
CGContextRef cgcontext = UIGraphicsGetCurrentContext();
// UIKit coordinate system is upside down to GL/Quartz coordinate system
// Flip the CGImage by rendering it to the flipped bitmap context
// The size of the destination area is measured in POINTS
CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
// Retrieve the UIImage from the current context
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Clean up
free(data);
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);
return image;
}
Method to flip the image vertically:
- (UIImage *)flipImageVertically:(UIImage *)originalImage
{
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage];
UIGraphicsBeginImageContext(tempImageView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform flipVertical = CGAffineTransformMake(
1, 0, 0, -1, 0, tempImageView.frame.size.height
);
CGContextConcatCTM(context, flipVertical);
[tempImageView.layer renderInContext:context];
UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//[tempImageView release];
return flippedImage;
}

UIScrollview changes UIButton's image quality on zoom out

I've got a large display area that can be panned and zoomed to view different objects. The problem that I'm running into is that the quality of the PNG images UIButton becomes somewhat degraded if I'm zoomed out (however it is back to normal when I zoom back in to 100%). It almost looks as if the image becomes oversharpened. Is this something that I'm going to have to live with, or is there a way to get rid of this grainy edge effect? The aspect ratio of the images are always 1:1, by the way.
I was able to solve this by using the answer found here in my scrollViewDidEndZooming method. Here is my code:
Resize function
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = image.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(context, flipVertical);
// Draw into the context; this scales the image
CGContextDrawImage(context, newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}
ScrollView Method
(Widget is a UIViewController subclass which contains a button and a "widgetImage" which stores the full resolution of the image that the button should display)
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
for(Widget *theWidget in widgets){
UIImage *newScaledImage = [self resizeImage:theWidget.widgetImage newSize:CGSizeMake(theWidget.view.frame.size.width * scale, theWidget.view.frame.size.height * scale)];
[theWidget.widgetButton setImage:newScaledImage forState:UIControlStateNormal];
// theWidget.widgetButton.currentImage = newScaledImage;
}
}

Resize and saving an image to disk in Monotouch

I'm trying to resize an image loaded from disk - a JPG or PNG (I don't know the format when I load it) - and then save it back to disk.
I've got the following code which I've tried to port from objective-c, however I've got stuck on the last parts. Original Objective-C.
This may not be the best way of achieving what I want to do - any solution is fine for me.
int width = 100;
int height = 100;
using (UIImage image = UIImage.FromFile(filePath))
{
CGImage cgimage = image.CGImage;
CGImageAlphaInfo alphaInfo = cgimage.AlphaInfo;
if (alphaInfo == CGImageAlphaInfo.None)
alphaInfo = CGImageAlphaInfo.NoneSkipLast;
CGBitmapContext context = new CGBitmapContext(IntPtr.Zero,
width,
height,
cgimage.BitsPerComponent,
4 * width,
cgimage.ColorSpace,
alphaInfo);
context.DrawImage(new RectangleF(0, 0, width, height), cgimage);
/*
Not sure how to convert this part:
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap); // ok if NULL
CGImageRelease(ref);
*/
}
In the upcoming MonoTouch we will have a scale method, this is its implementation in UIImage.cs:
public UIImage Scale (SizeF newSize)
{
UIGraphics.BeginImageContext (newSize);
var context = UIGraphics.GetCurrentContext ();
context.TranslateCTM (0, newSize.Height);
context.ScaleCTM (1f, -1f);
context.DrawImage (new RectangleF (0, 0, newSize.Width, newSize.Height), CGImage);
var scaledImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return scaledImage;
}
Adjusted to be reused outside of MonoTouch:
public static UIImage Scale (UIImage source, SizeF newSize)
{
UIGraphics.BeginImageContext (newSize);
var context = UIGraphics.GetCurrentContext ();
context.TranslateCTM (0, newSize.Height);
context.ScaleCTM (1f, -1f);
context.DrawImage (new RectangleF (0, 0, newSize.Width, newSize.Height), source.CGImage);
var scaledImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return scaledImage;
}
public static UIImage EditPhoto(int iMode, UIImage origImg)
{
SizeF newSize;
if (iMode == 1 || iMode == 2)
newSize = new SizeF(origImg.Size.Height, origImg.Size.Width);
else
newSize = origImg.Size;
UIGraphics.BeginImageContext(newSize);
CGContext ctx = UIGraphics.GetCurrentContext();
switch (iMode)
{
case 1: // Rotate counter-clockwise 90 degrees
ctx.TranslateCTM(origImg.Size.Height, origImg.Size.Width);
ctx.ScaleCTM(1f, -1f);
ctx.RotateCTM(1.57079633f); // angle is in radians
break;
case 2: // Rotate clockwise 90 degrees
ctx.ScaleCTM(1f, -1f);
ctx.RotateCTM(-1.57079633f); // angle is in radians
break;
case 3: // Flip vertical
// Do nothing. The image comes out flipped vertically because Core Graphics / OpenTK uses cartesian coordinates
break;
case 4: // Flip horizontal
ctx.TranslateCTM(newSize.Width, newSize.Height);
ctx.ScaleCTM(-1f, -1f);
break;
default: // Return unchanged image
ctx.TranslateCTM(0, origImg.Size.Height);
ctx.ScaleCTM(1f, -1f);
break;
}
ctx.DrawImage(new RectangleF(0, 0, origImg.Size.Width, origImg.Size.Height), origImg.CGImage);
UIImage resImg = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return resImg;
}
To get an UIImage from context all you need to do is:
UIImage* result = UIImage.FromImage(context.ToImage());