Objective-C iOS Crop Image Using Zoom? - ios

I am trying to use this code to allow users to zoom and crop their images in my app:
https://github.com/iosdeveloper/ImageCropper/tree/master/Classes/ImageCropper
code snippet
float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.origin.x = [scrollView contentOffset].x * zoomScale;
rect.origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;
CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:cr];
CGImageRelease(cr);
[delegate imageCropper:self didFinishCroppingWithImage:cropped];
But it does not take into account images being rotated (I guess it's some sort of metadata on the iPhone images). I do not want to have users draw a rectangle to crop as I much prefer this interface.
Can someone tell me how to make it not rotate my images?
I am building in iOS 5 & 6 if that helps.
[EDIT] Also, this is done nicely in the Game Center application for uploading your profile picture. Anyone know where I can find the code for that?

I found an answer here that helped me do what I wanted
iOS: Cropping a still image grabbed from a UIImagePickerController camera with overlay
Code Snippet for those interested
float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
NSLog(#"contentOffset is :%f,%f",[scrollView contentOffset].x,[scrollView contentOffset].y);
rect.origin.x = fabsf([scrollView contentOffset].x * zoomScale);
rect.origin.y = fabsf([scrollView contentOffset].y * zoomScale);
rect.size.width = fabsf([scrollView bounds].size.width * zoomScale);
rect.size.height = fabsf([scrollView bounds].size.height * zoomScale);
UIGraphicsBeginImageContextWithOptions( CGSizeMake( rect.size.width,
rect.size.height),
NO,
0.);
[[imageView image] drawAtPoint:CGPointMake( -rect.origin.x, -rect.origin.y)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Related

Convert UIScrollViewPoint to UIImage + ZOOM + ROTATE

I need to crop UIImage which is loaded in UIScrollview with some rect of Another UIView which is also in UIScrollView
Following is View Hierarchy
--> View
--> UIScrollView
--> viewBase (UIView)
--> UIImageView -> (Zoomed & rotated )
--> UIView (Target View)(Movable User can move anywhere in scrollview to crop rect)
My Image is Rotated & Zoomed I need to get exact part of image in TargetView
I am drawing UIImage with rotation on context following is code
CGFloat angleCroppedImageRetreacted = atan2f(self.imgVPhoto.transform.b, self.imgVPhoto.transform.a);
angleCroppedImageRetreacted = angleCroppedImageRetreacted * (180 / M_PI);
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.imgVPhoto.image.size.width, self.imgVPhoto.image.size.height)];
rotatedViewBox.transform = CGAffineTransformMakeRotation(-angleCroppedImageRetreacted);
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0f, rotatedSize.height / 2.0f);
CGContextRotateCTM(bitmap, -angleCroppedImageRetreacted);
CGContextScaleCTM(bitmap, 1.0f, -1.0f);
CGContextDrawImage(bitmap, CGRectMake(-self.imgVPhoto.image.size.width / 2.0f,
-self.imgVPhoto.image.size.height / 2.0f,
self.imgVPhoto.image.size.width,
self.imgVPhoto.image.size.height),
self.imgVPhoto.image.CGImage);
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
And it works fine . I am getting Rotated UIImage same as i can see in Simulator
For converting Point of Target View to UIImage I use following code which is NOT WORKING
CGPoint imageViewPoint = [self.viewBase convertPoint:self.targetImageview.center toView:self.imgVPhoto];
float percentX = imageViewPoint.x / self.imgVPhoto.frame.size.width;
float percentY = imageViewPoint.y / self.imgVPhoto.frame.size.height;
CGPoint imagePoint = CGPointMake(resultImage.size.width * percentX, resultImage.size.height * percentY);
rect.origin = imagePoint;
//rect.origin.x *= (self.imgVPhoto.image.size.width / self.imgVPhoto.frame.size.width);
//rect.origin.y *= (self.imgVPhoto.image.size.height / self.imgVPhoto.frame.size.height);
imageRef = CGImageCreateWithImageInRect([resultImage CGImage], rect);
img = [UIImage imageWithCGImage:imageRef scale:viewImage.scale orientation:viewImage.imageOrientation];
I think issue is we can't use Rect after Transform Apply
Please Help me to crop UIImage which is zoomed and rotated from rect on same Hierarchy
If you need more info pls ask
I am answering my own question .
Thanks to Matic for giving a idea
I changed a logic
I have achieved same functionality what i looking for
CGPoint locationInImageView = [self.viewBase convertPoint:self.targetImageview.center toView:self.view]; // received from touch
locationInImageView = [self.view convertPoint:locationInImageView toView:self.imgVPhoto];
// I GOT LOCATION IN UIIMAGEVIEW OF TOUCH POINT
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0);
[self.imgVPhoto.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img1 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// I GOT UIIMAGE FROM CURRENT CONTEXT
CGFloat width = self.targetImageview.frame.size.width * self.zoomScale ;
CGFloat height = self.targetImageview.frame.size.height * self.zoomScale ;
//2 IS SCALE FACTOR
CGFloat xPos = (locationInImageView.x * 2) - width / 2;
CGFloat yPos = (locationInImageView.y * 2) - height / 2;
CGRect rect1 = CGRectMake(xPos, yPos, width, height);
CGImageRef imageRef = CGImageCreateWithImageInRect([img1 CGImage], rect1);
// YAHHH YOU HAVE EXACT IMAGE
UIImage *img = [UIImage imageWithCGImage:imageRef scale:img1.scale orientation:img1.imageOrientation];

Crop zoomed image in UIScrollView issue

After going through this link, issue with my code is that output image is unable to set proper x and y values as cropped image seems to have 0 and 0 in the resultant image irrespective to where I zoom (or where the scroll offset is calculated). Here's what I tried.
- (IBAction)crop:(id)sender
{
float zoomScale = 1.0f / [self.scroll zoomScale];
CGRect rect;
NSLog(#"contentOffset is :%f,%f",[self.scroll contentOffset].x,[self.scroll contentOffset].y);
rect.origin.x = self.scroll.contentOffset.x * zoomScale;
rect.origin.y = self.scroll.contentOffset.y * zoomScale;
rect.size.width = self.scroll.bounds.size.width * zoomScale;
rect.size.height = self.scroll.bounds.size.height * zoomScale;
UIGraphicsBeginImageContextWithOptions( CGSizeMake(rect.size.width, rect.size.height),
NO,
0.);
NSLog(#"rect offset is :%f,%f",rect.origin.x,rect.origin.y);
CGPoint point = CGPointMake(-rect.origin.x, -rect.origin.y); **//even though above NSLog have some values, but output image is unable to set proper x and y values as cropped image seems to have 0 and 0 in the resultant image.**
[[self.imagV image] drawAtPoint:point
blendMode:kCGBlendModeCopy
alpha:1];
self.croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
DTImageViewController *imageViewController = [[DTImageViewController alloc] initWithNibName:#"DTImageViewController" bundle:nil];
imageViewController.image = self.croppedImage;
[self.navigationController pushViewController:imageViewController animated:YES];
}
Similar code as already posted but just taking whole UIScrollView bounds without passing a CGRect
-(void)takeScreenShotOfScrollView
{
UIGraphicsBeginImageContextWithOptions(scrollView.bounds.size, YES, [UIScreen mainScreen].scale);
CGPoint offset = scrollView.contentOffset;
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -offset.x, -offset.y);
[scrollView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
img = [SDImageHelper imageWithImage:img_image scaledToSize:CGSizeMake(769, 495)];
}
BONUS:
The cropping method
+(UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I'm using the following code for this and it does exactly what expected:
- (UIImage *) croppedImageOfView:(UIView *) view withFrame:(CGRect) rect
{
UIGraphicsBeginImageContextWithOptions(rect.size,NO,0.0);
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -rect.origin.x, -rect.origin.y);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *visibleScrollViewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return visibleScrollViewImage;
}
- (void) crop
{
CGRect neededRect = CGRectMake(50,50,100,100);
UIImage *image = [self croppedImageOfView:_scrollView withFrame:neededRect];
}
Works well even if content is zoomed, the only thing that must be calculated wisely is needed area CGRect.

Cropping UIImage from pinch/Zoom state

I have been looking around to find a way to crop an image (imageView) inside ScrollView. What i need to achieve here is user pinch to zoom an image, (which is working great) and then able to crop ONLY the part of the image that is shown on the screen after the zoom. so in another words, if i have a series of number 1 to 10, user zoom it to see only number 4 and 5 and when user taps crop, i ONLY need image of 4 and 5 that was visible at the time of crop. I'm using this code...
- (UIImage *)croppedImageWithImage:(UIImage *)image zoom:(CGFloat)zoom
{
CGFloat zoomReciprocal = 1.0f / zoom;
CGRect croppedRect = CGRectMake(self.scrollView.contentOffset.x*zoom,
self.scrollView.contentOffset.y*zoom,
image.size.width * zoomReciprocal,
image.size.height * zoomReciprocal);
CGImageRef croppedImageRef = CGImageCreateWithImageInRect([image CGImage], croppedRect);
UIImage* croppedImage = [[UIImage alloc] initWithCGImage:croppedImageRef scale:[image scale] orientation:[image imageOrientation]];
CGImageRelease(croppedImageRef);
return croppedImage;
}
This method crops the image however, the x and y values are incorrect... What do i have to do to get the correct x and y of my crop area?
FYI.. the image i'm trying to crop is 2200 * 3200, not sure if that would make any difference?
I solved it!! here is the updated method for others who might come across this issue.
- (UIImage *)croppedImageWithImage:(UIImage *)image zoom:(CGFloat)zoom {
CGFloat zoomReciprocal = 1.0f / zoom;
CGFloat xOffset = image.size.width / self.scrollViewBackground.contentSize.width;
CGFloat yOffset = image.size.height / self.scrollViewBackground.contentSize.height;
CGRect croppedRect = CGRectMake(self.scrollViewBackground.contentOffset.x * xOffset,
self.scrollViewBackground.contentOffset.y * yOffset,
image.size.width * zoomReciprocal,
image.size.height * zoomReciprocal);
CGImageRef croppedImageRef = CGImageCreateWithImageInRect([image CGImage], croppedRect);
UIImage *croppedImage = [[UIImage alloc] initWithCGImage:croppedImageRef scale:[image scale] orientation:[image imageOrientation]];
CGImageRelease(croppedImageRef);
return croppedImage;
}

CGImageCreateWithImageInRect incorrect area?

I am using this github image cropper: https://github.com/iosdeveloper/ImageCropper
In this image cropper, in the init method they have these lines:
CGRect rect;
rect.size.width = image.size.width;
rect.size.height = image.size.height;
[imageView setFrame:rect];
Beforehand, I resize the image to the screens resolution not points. So if I leave the code above the way it is, my UIImage will not fill the UIImageView for some odd reason, and I have tried to adjust the contentModes also.
But if I adjust it to this (the size of the UIPopover that the image cropper is in):
[imageView setFrame:CGRectMake(0, 0, 320, 480)];
The UIImage will fill the UIPopover.
The real issue is here:
If I zoom into a certain part of the image and execute the code below, it will take screenshot of the top left part of the UIImage no where near to where I have cropped the image to. Although it does abide by the scrollView's zoomScale.
Here is the screenshot code:
- (void)finishCropping {
float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.origin.x = [scrollView contentOffset].x * zoomScale;
rect.origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;
CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:cr];
CGImageRelease(cr);
[delegate imageCropper:self didFinishCroppingWithImage:cropped];
}
Does anyone know why this is happening?
Thanks!
I think you have at first to use [UIImage imageWithCGImage:cr scale:self.scale orientation:self.imageOrientation] instead [UIImage imageWithCGImage:cr] to create UIImage, this will take off many problems with orientation and double check the width and height are in the right direction in rect.

Merge Two UIImage that are Rotated & Scaled

I'am doing an app something like this: You load a photo and you put images over it, like balloons, etc..
When I try to merge one of this over images with only resize it works fine. Like 10px more than it should be but no problem.
The problem comes when you rotate the image [UIImageView] it appears much bigger that the image its, I try allot of things and nothing. I leave the code. I hope someone could help.
Note: The image size its inside UIImageView, then multiplied it by the scale of the main image
- (UIImage *)mergeImage:(UIImageView *)mainImage withImageView:(UIImageView *)imageView {
UIImage *temp = imageView.image;
UIImage *tempMain = mainImage.image;
CGFloat mainScale = [self imageViewScaleFactor:mainImage];
CGFloat tempScale = 1/mainScale;
NSLog(#"%f", tempScale);
//Rotate UIIMAGE
UIGraphicsBeginImageContext(temp.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, temp.size.width/2, temp.size.height/2);
CGFloat angle = atan2(imageView.transform.b, imageView.transform.a);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(ctx, transform);
// Draw the image into the context
CGContextDrawImage(ctx, CGRectMake(-temp.size.width/2, -temp.size.height/2, temp.size.width, temp.size.height), temp.CGImage);
// Get an image from the context
temp = [UIImage imageWithCGImage: CGBitmapContextCreateImage(ctx)];
NSLog(#"%f %f %f", mainScale, mainImage.frame.size.width, mainImage.frame.size.height);
UIGraphicsBeginImageContextWithOptions(tempMain.size, NO, 1.0f);
//Get imageView size & position
NSLog(#"%f %f %f %f", imageView.frame.origin.x, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height);
CGFloat offsetX = 0;
CGFloat offsetY = -44;
if (tempMain.size.height > tempMain.size.width) {
offsetX = ((tempMain.size.width * mainScale) - 320)/2;
}else{
offsetY = ((tempMain.size.height * mainScale) - 416)/2;
offsetY -= 44;
}
CGFloat imageViewX = (imageView.frame.origin.x + offsetX) * tempScale;
CGFloat imageViewY = (imageView.frame.origin.y + offsetY) * tempScale;
CGFloat imageViewW = imageView.frame.size.width * tempScale;
CGFloat imageViewH = imageView.frame.size.height * tempScale;
CGRect tempRect = CGRectMake(imageViewX, imageViewY, imageViewW, imageViewH);
[tempMain drawAtPoint:CGPointZero];
[temp drawInRect:tempRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Thanks
This is the solution that works for me
Merging a previosly rotated by gesture UIImageView with another one. WYS is not WYG
I just take a photo to the main screen and then crop it to the size of the photo, its faster, and clean. and the resolution it ok if the apps runs in retina in a normal device isn't too good. And you need to prepare that code to work in retina & non-retina

Resources