RSS

Image Cropping from a UIScrollView

22 Apr

In a recent project I was required to crop an image that was presented to the user in an UIScrollView container. Since the image in question could be larger than the UIScrollView and could also be potentially be zoomed in or out, this presented somewhat of a head scratcher till I eventually figured it out. The solution is simplicity in itself but only once you know all the variables involved. ;) IAC, I thought it might be of use to others as well so here goes nothing:

This is the original image:

And this is part of the image as seen in the UIScrollView (panned horizontally to the right).

The image crop function then should give us the above cropped image from the UIScrollView. To do this we need a couple of things:

1) The image offset into UIScrollView. Since the image can be bigger than the UIScrollView (as above), the user could have potentially panned to any part of the image. The offset is conveniently provided by the contentOffset property of UIScrollView.

2) The zoom scale of UIScrollView. If the image has been been zoomed in/out by the user, then we need to make further adjustments based on the zoom scale.

NOTE: To keep the article simple, I assume that any image orientation issues are already taken into account before displaying it in the UIScrollView. There are a number of articles on that subject already so I won’t go into that here.

The code then to calculate the area in question that is being displayed in UIScrollView is:


float scale = 1.0f/scrollView.zoomScale;

CGRect visibleRect;
visibleRect.origin.x = scrollView.contentOffset.x * scale;
visibleRect.origin.y = scrollView.contentOffset.y * scale;
visibleRect.size.width = scrollView.bounds.size.width * scale;
visibleRect.size.height = scrollView.bounds.size.height * scale;

Now that we have the CGRect structure, it’s easy enough to get the image:

UIImage* cropImage(UIImage* srcImage, CGRect* rect)
{
CGImageRef cr = CGImageCreateWithImageInRect([srcImage CGImage], *rect);
UIImage* cropped = [[[UIImage alloc] initWithCGImage:cr] autorelease];
CGImageRelease(cr);
return cropped;
}

That’s all there is to it, really.

Update: I have created a sample project to demonstrate the above:

https://github.com/ArjunNair/ImageCroppingExample

About these ads
 
13 Comments

Posted by on April 22, 2011 in IPhone

 

13 responses to “Image Cropping from a UIScrollView

  1. Rog

    May 20, 2011 at 11:52 AM

    You’re a legend! I was overcomplicating this so much and yours is a nice, simple and elegant solution. Thanks for sharing it!

     
  2. wcrane

    July 19, 2011 at 12:13 PM

    Thanks for sharing! It’s realy simple, but direction of the image it gets is wrong and it not take the zoom scale between original image and the UIScrollView into consider. So the code that you give us that effective.

     
    • Arjun

      July 20, 2011 at 3:03 PM

      Can you please elaborate on what you mean by “direction of image” and the code not taking the zoom scale into account? If you’re referring to the orientation, then the code isn’t concerned with that. It simply uses the orientation as it’s being used in the UIScrollView. As for the zoom scale, it is being taken into account in the code, so I’m not sure why you seem to think it otherwise?

       
      • Rog

        July 20, 2011 at 3:07 PM

        Images taken with the iPhone camera are always horizontal and the code above doesn’t take the orientation parameter into consideration.

        @wcrane my recommendation is to first rotate the image so it’s oriented UP and then do the cropping as above.

        More details here http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

         
      • Arjun

        July 20, 2011 at 3:36 PM

        Right said Rog. To make the article concise and focused, I assume that the image orientation is already taken into account when it’s displayed in the UIScrollView. I have edited the article to state as much in order to avoid any confusion to the reader.

         
  3. wcrane

    July 20, 2011 at 3:35 PM

    It’s my fault not to describe my question very clearly. In my recent project, i try to crop part of the picture which i pick from my own photo gallery. All of them display normal in a UIImageView which is placed in a scrollview. But when i use the method you gave, some of them have the wrong direction. Finally i found that them all have the imageOrientation property setted to 3, so after i rotated them for the right direction, it works. And the zoom scale i mean is the ratio between the orignal image and the scrollview which you used to display it. For example, the size of original picture is {4000, 2000}, but the size of scrollview is just {320, 372}.

     
    • Arjun

      July 20, 2011 at 3:57 PM

      The UIImageView automatically takes image orientation into account when you display images using it. However, if you need to use that image in some other way you will need to adjust the image orientation yourself either prior or after displaying it in the UIScrollView. In my case, I do it before displaying it in UIScrollView.

      As for the image ratio, note that while the scrollview is smaller than size than the displayed image, it’s content size can (and usually is) larger than the viewable area (limited as it is by the device screen) and is equal to the dimension of the original image. That is, scrollview frame is 320×372 (or whatever) but content size is 4000×2000 (in your example). The code does take this into account when calculating the required dimensions. Note that we’re cropping to the viewable area of the scrollview – nothing more, nothing less.

       
  4. Tim

    August 30, 2011 at 1:53 AM

    Any chance you can post an example project? In my project it appears like the rect is being calculated wrong. Wondering if I set some thing up wrong in my xib.

     
    • Arjun

      August 30, 2011 at 12:46 PM

      I have updated the article with a link to an example project on github. HTH!

       
  5. Nimit Pattanasri

    June 7, 2012 at 5:50 AM

    To get the correct image orientation, try to replace the code in “cropImage” with this:

    CGRect transformedRect=rect;
    if(originalImage.imageOrientation==UIImageOrientationRight) {
    transformed.origin.x = rect.origin.y;
    transformed.origin.y = originalImage.size.width-(rect.origin.x+rect.size.width);
    transformed.size.width = rect.size.height;
    transformed.size.height = rect.size.width;
    }

    CGImageRef cr = CGImageCreateWithImageInRect(originalImage.CGImage, transformed);
    UIImage* cropped = [UIImage imageWithCGImage:cr scale:originalImage.scale orientation:originalImage.imageOrientation];

    CGImageRelease(cr);
    return cropped;

    Since CGImage is not aware of image orientation, the cropped CGRect instead needs to be transformed.

     
  6. Bogdan

    September 24, 2014 at 1:40 AM

    Thanks a lot! That’s way better then rendering UIScrollView or UIView since seem to preserve original image quality.

     
  7. App Development Sydney

    September 30, 2014 at 4:48 PM

    You are amazing…. Thanks for sharing it!

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: