Resize a UIImage the right way

When deadlines loom, even skilled and experienced programmers can get a little sloppy. The pressure to ship may cause them to cut corners and look for a quick and easy solution, even if that solution is sure to cause trouble later on. Eventually, their coding style devolves into copy and paste programming, a lamentable tactic that involves cherry-picking snippets of code from a past project and putting them to use in the current one. Of course, the proper solution is to factor out the code into some kind of reusable library, but due to time constraints, it’s simply duplicated wherever it’s needed. Any bugs in the original code have now spread to a dozen different places in a dozen different projects. It’s an algorithm for chaos.

Yet in the world of iPhone applications, copy and paste programming seems to be disturbingly common. The fact that so many iPhone apps are short-term, one-off projects doesn’t help, but the situation has been aggravated even more by Apple’s security restrictions. In particular, dynamic linking to any shared library that doesn’t ship with the OS is strictly forbidden. One could argue that this rule is a necessary side-effect of the iPhone’s sandboxing security model, but even workarounds such as consolidating code into a static shared library are extraordinarily difficult. Another contributing factor is that the iPhone API is still relatively immature, and developers too often require custom code to fill in its gaps.

This situation has transformed more than a few iPhone programmers into copy and paste programmers. When they inevitably encounter some limitation with the iPhone API, the typical response is:

  1. Search online for a solution
  2. Find a snippet of code somewhere that gets the job done (usually at Stack Overflow or iPhone Dev SDK)
  3. Copy and paste the snippet into their project
  4. Move on to the next problem

Now imagine what happens when a thousand iPhone developers find the same snippet. Suddenly the problems of copy and paste programming have gone global. Offline, a bug in a single snippet of code may infect a dozen projects; online, it can spread to thousands.

As a reluctant copy and paste iPhone programmer myself, I’ve witnessed this scenario first-hand. I recently encountered a limitation with a certain iPhone class—UIImage—and I found in a discussion forum what seemed to be a popular, well-regarded solution. The code snippet was the first hit in a Google search, and many readers had replied with thanks to its author. However, a bit of testing showed that it worked for most images but completely failed for others. By the time I stumbled upon it, the buggy code had probably spread to countless programs already.

In the process of finding the bug and posting the fix, I ended up writing a substantial amount of additional code to address various other annoyances related to UIImage. The complete listing is available for download below. Though it won’t solve the copy and paste problem, it should be a welcome remedy for other iPhone developers who have run into similar obstacles.

Background

Programming for the iPhone, a highly graphical device, necessarily involves a substantial amount of image manipulation. Its SDK therefore provides an abstraction called UIImage that handles much of the effort in importing and drawing images. For example, imagine you want to load a JPEG file, scale it down to icon size, and give it rounded corners. These tasks may require tens or even hundreds of lines of code on other platforms, but on the iPhone, it’s only a matter of instantiating a UIImage, passing it to a UIImageView of the appropriate size, and setting the cornerRadius property.

Despite its ease of use, or perhaps because of it, UIImage suffers from some serious limitations. Key among these is its lack of support for resizing the image, a feature that is normally handled dynamically by its companion, the UIImageView component. However, should an iPhone application need to reduce the size of an image for storage or for exchange with an external entity (such as a web server), the UIImage class is insufficient.

Of course, UIImage is not the only means of image manipulation on the iPhone. It ships with a rather sophisticated graphics API, known as Quartz 2D, that offers low-level control of bitmap data. Clearly, the functionality for resizing an image exists, although taking advantage of it is not straightforward and requires the developer to write non-trivial code. How best to accomplish this task has been the source of much confusion and debate, particularly in forums such as iPhone Dev SDK:

  • Resizing a photo to a new UIImage

    This is crazy. I know there are threads that touch on this already, but none of them have led me to the answer. I can’t believe that it is really this difficult!

  • Resize Image High Quality

    I have done lots of searching for a way to resize images via the iPhone SDK and I have come across a few methods which work but the resulting image does not look nearly as good as if you took the full resolution image and told it to draw inside a rectangle.

These discussions have resulted in countless code snippets that claim to resize a UIImage, but many of them contain bugs, or they simply leave out functionality such as EXIF orientation support, an absolute necessity when dealing with photographs taken by the iPhone’s camera. For instance, a particularly popular code snippet for UIImage resizing incorrectly processes alpha information, resulting in a pink tint for certain image files.

Image resized correctly
Image resized correctly

Image resized with pink tint
Image resized with buggy code

A Better Way to Resize Images

The following sections describe yet another collection of source code for resizing UIImage objects. Functionally, it is similar to code samples that can be found elsewhere on the Internet in discussion forums and blogs, but it consolidates their features into a self-contained, reusable package and offers several notable improvements:

  • Additional methods for cropping images, generating thumbnails, and more.
  • Implemented as Objective-C categories to facilitate reuse. With categories, you can simply plop the code into your project, import a header file, and all of your UIImage objects will automatically have access to the new methods.
  • Bugs that commonly plague other code of this type have been found and fixed. The categories have been vetted in a large, real-world iPhone app, and they contain no known bugs.
  • The code has been simplified as much as possible and is more thoroughly documented.

The source code to the categories can be downloaded from the links below or as a single archive. If you are an experienced iPhone programmer, you can probably grab the files and start using them right away. Continue reading for more detail on how to apply them, as well as a run-down of the problems that prompted their creation.

UIImage+Resize.h, UIImage+Resize.m
Extends the UIImage class to support resizing (optionally preserving the original aspect ratio), cropping, and generating thumbnails.

UIImage+RoundedCorner.h, UIImage+RoundedCorner.m
Extends the UIImage class to support adding rounded corners to an image.

UIImage+Alpha.h, UIImage+Alpha.m
Extends the UIImage class with helper methods for working with alpha layers (transparencies).

UIImage+Alpha

The Alpha category is perhaps not as directly useful as the others, though it provides some necessary functionality that they build upon. Its methods include:

- (BOOL)hasAlpha;

Tells whether the image has an alpha layer.

- (UIImage *)imageWithAlpha;

Returns a copy of the image, adding an alpha channel if it doesn’t already have one. An alpha is required when adding transparent regions (e.g., rounded corners) to an image. It may also be necessary when loading certain kinds of image files that are not directly supported by Quartz 2D. For example, if you load a JPEG using imageNamed:, the resulting UIImage will have 32 bits per pixel with the first 8 bits unused (kCGImageAlphaNoneSkipFirst). But if you take the same image and save it in BMP format, and load it exactly the same way, the UIImage will have 24 bits per pixel (kCGImageAlphaNone), which is unsupported in Quartz 2D. Trying to render it to a graphics context will cause run-time errors. The obvious way around this problem is to make sure you only load image files that produce a Quartz-compatible pixel format. (A complete list is available in the Supported Pixel Formats section of the Quartz 2D Programming Guide.) If for some reason this is not possible, adding an alpha channel to the UIImage at runtime may also work.

- (UIImage *)transparentBorderImage:(NSUInteger)borderSize;

Returns a copy of the image with a transparent border of the given size added around its edges. This solves a special problem that occurs when rotating a UIImageView using Core Animation: Its borders look incredibly ugly. There’s simply no antialiasing around the view’s edges. Luckily, adding a one-pixel transparent border around the image fixes the problem. The extra border moves the visible edges of the image to the inside, and because Core Animation interpolates all inner pixels during rotation, the image’s borders will magically become antialiased. This trick also works for rotating a UIButton that has a custom image. The following before-and-after video shows the technique in action. (The top square is the original image; the bottom square has a one-pixel transparent border.)

Get the Flash Player to see this content.

UIImage+RoundedCorner

With the release of iPhone OS 3.0, a new Core Animation feature called cornerRadius became available. When applied to a layer, it makes the corners soft and round, just the thing for achieving a Web 2.0 or Mac OS X look-and-feel. For example, if you have a UIButton with a custom image like this:

Square corners

And add a couple lines of code:

button.layer.cornerRadius = 30;
button.layer.masksToBounds = YES;

You get this:

Rounded corners

The fun stops there. The cornerRadius setting only affects the run-time appearance of the layer. As soon as you save the image or send it over the network, the rounded corners go away. Also, if you animate the layer, perhaps by making it rotate, the cornerRadius property mysteriously reverts to zero, giving the image sharp corners again. This is a confirmed bug (#7235852) in iPhone OS 3.0 and 3.1.

To fix this problem, the RoundedCorner category can apply rounded corners to a UIImage permanently. It modifies the image data itself, adding an alpha layer if necessary. Not only does this work around the Core Animation bug, it also preserves the rounded corner effect when exporting the UIImage to a file or network stream, assuming that the output format supports transparency.

The category exposes a single method:

- (UIImage *)roundedCornerImage:(NSInteger)cornerSize
                     borderSize:(NSInteger)borderSize;

Creates a copy of the image, adding rounded corners of the specified radius. If borderSize is non-zero, a transparent border of the given size will also be added. (The primary purpose of this parameter is to work around the aforementioned aliasing problem that occurs when rotating an image view.) The implementation is based on code by Björn Sållarp.

UIImage+Resize

Resizing a UIImage is more complicated than it may seem. First, there’s simply the matter of learning Quartz 2D—a somewhat complex, low-level API. A mistake in a single parameter can suddenly affect thousands of pixels, yielding unexpected results like the pink tint problem shown previously.

Another issue to consider is the quality of the resulting image. By default, Quartz 2D applies a fast but not-so-high-quality interpolation algorithm when scaling images up or down. The effect is especially noticeable when reducing an image to a very small size, perhaps for an icon or thumbnail representation. The aliasing caused by the algorithm transforms smooth lines into jagged edges. Faces become a pixelated mess.

To illustrate, the following image is the result of squeezing a 1024×516-pixel JPEG (courtesy of PD Photo) into a 320×200-pixel UIImageView with automatic resizing enabled:

Resize without interpolation

Note the serrated edges along the wings. To counteract the unsightliness, Quartz 2D can be configured for a different scaling algorithm by calling CGContextSetInterpolationQuality. Here is the same image, pre-scaled using the kCGInterpolationHigh option, and displayed in the same UIImageView:

Resize with interpolation

The jaggies are now gone, replaced with smoother, cleaner lines.

Yet another obstacle, one of particular importance to iPhone developers, is image orientation. When a user takes a snapshot with the iPhone’s camera, the image is not upright but is in fact rotated 90 degrees counterclockwise. The reason is because the iPhone’s camera is positioned in a way that makes up (from the lens’s perspective) point to the left-hand side of the camera. The iPhone’s camera software knows this and therefore adds a special flag to the image data that indicates how the pixels should be rotated to produce the correct orientation. The software employs the same tactic when the user takes a picture in landscape mode (i.e., holding the phone sideways). It can rotate the image without having to apply a transformation across millions of pixels. Instead, it simply changes the orientation flag. Components such as UIImageView automatically read this flag—stored in the imageOrientation property of UIImage—and apply the proper rotation at run-time when displaying the image.

Unfortunately, as soon as you dip into the low-level Quartz 2D API, which has no knowledge of the high-level UIImage class, the orientation information is lost. An image resize algorithm written using this API will need to be provided with the orientation and perform the rotation explicitly.

The Resize category solves each of these problems while incorporating additional handy features. Its methods include:

- (UIImage *)croppedImage:(CGRect)bounds;

Returns a copy of the image that is cropped to the given bounds. The bounds will be adjusted using CGRectIntegral, meaning that any fractional values will be converted to integers.

- (UIImage *)thumbnailImage:(NSInteger)thumbnailSize
          transparentBorder:(NSUInteger)borderSize
               cornerRadius:(NSUInteger)cornerRadius
       interpolationQuality:(CGInterpolationQuality)quality;

Returns a copy of the image reduced to the given thumbnail dimensions. If the image has a non-square aspect ratio, the longer portion will be cropped. If borderSize is non-zero, a transparent border of the given size will also be added. (The primary purpose of this parameter is to work around the aforementioned aliasing problem that occurs when rotating an image view.) Finally, the quality parameter determines the amount of antialiasing to perform when scaling the image.

- (UIImage *)resizedImage:(CGSize)newSize
     interpolationQuality:(CGInterpolationQuality)quality;

Returns a resized copy of the image. The quality parameter determines the amount of antialiasing to perform when scaling the image. Note that the image will be scaled disproportionately if necessary to fit the specified bounds. In other words, the aspect ratio is not preserved.

This method, as well as all other methods described here that perform resizing, takes into account the orientation of the UIImage and transforms the pixels accordingly. The resulting image’s orientation will be up (UIImageOrientationUp), regardless of the current orientation value. The code to perform this transformation is based in part on the following sources:

- (UIImage *)
  resizedImageWithContentMode:(UIViewContentMode)contentMode
                       bounds:(CGSize)bounds
         interpolationQuality:(CGInterpolationQuality)quality;

UIImageView offers a remarkably helpful ability: It can resize displayed images while preserving their aspect ratio. The manner of preservation depends on a setting known as the content mode. For example, if a large JPEG (courtesy of PD Photo) is displayed in a small view with the content mode set to Center (UIViewContentModeCenter), only a portion of the image is visible:

Content mode - unscaled

To include the entire image, the view’s content can be scaled to fit within the bounds (UIViewContentModeScaleToFill). Note that Scale To Fill does not preserve the aspect ratio, resulting in a squashed image:

Content mode - scaled

To scale the image without changing the aspect ratio, one option is to shrink the content until it fits entirely within the bounds (UIViewContentModeScaleAspectFit). Although this option shows the full image, it has the side-effect of not filling the entire view:

Content mode - aspect fit

(Note that any area not covered by the image in Aspect Fill mode is actually transparent. It’s colored gray here to show the view boundary.)

Another aspect-preserving option is to shrink the content just enough to fit the smaller dimension within the view. The larger dimension (in this case, the length) will be cropped:

Content mode - aspect fill

The correct choice of content mode depends, of course, on the desired appearance and the nature of the source image.

Because these modes are so useful, equivalent functionality has been rolled into the Resize category. Scale To Fill is the default behavior of resizedImage:interpolationQuality:, while resizedImageWithContentMode: supports both Aspect Fit and Aspect Fill. (Other content modes, such as Left and Bottom Right, were left unimplemented because they are rarely used.)

License

All code presented here is free for both personal and commercial use, with or without modification. No warranty is expressed or implied.


 

222 Responses to “Resize a UIImage the right way”

  1. tadej5553 says:

    Hey, I’m having a problem with this, and maybe you can help. I’m trying to resize a normal PNG image, but it gives me this error

    : CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 3-component color space; kCGImageAlphaNone; 576 bytes/row.

    • CJ says:

      I’m having the same issue when trying to resize loaded PNG files, any help would be greatly appreciated, only difference being that I’m loading PNG files with an alpha channel so the error I get is:

      CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 512 bytes/row.

      looks to me like there is a problem with it being a 32 bits/pixel image with a 3 component color space but I cant seem to rectify it.

      • smallduck says:

        Got bit by this today too. Apple Q&A 1037 ttp://developer.apple.com/qa/qa2001/qa1037.html says CGBitmapContextCreate doesn’t support kCGImageAlphaLast, only ..None or ..PremultipliedLast.

        I going to add a check for this and switch the bitmap info bits to be kCGImageAlphaNoneSkipLast, essentially losing the alpha.

        • Sorig says:

          Hey, im getting the error too. What exactly have you done to avoid this?

          • cramhead says:

            I change the changed the – (UIImage *)resizedImage:(CGSize)newSize
            transform:(CGAffineTransform)transform
            drawTransposed:(BOOL)transpose
            interpolationQuality:(CGInterpolationQuality)quality ; //private method to fix the problem.

            The CGBitmapContextCreate called is changed to
            // Build a context that’s the same dimensions as the new size
            CGContextRef bitmap = CGBitmapContextCreate(NULL,
            newRect.size.width,
            newRect.size.height,
            CGImageGetBitsPerComponent(imageRef),
            0,
            CGImageGetColorSpace(imageRef),
            kCGImageAlphaNoneSkipLast);

  2. [...] Trevor’s Bike Shed This entry was posted in hints and tagged cocoa, helper, objc. Bookmark the permalink. ← permalink structure in wordpress [...]

  3. Karib Chiang says:

    Thanks a lot Trevor for this insightful article on image resizing. The code snippet works perfectly in my implementation.

    Regards,
    Karib

  4. Rob says:

    Hi,
    Thank you for posting this very interesting guide and code.
    I’m using it to resize some images I get from an RSS feed to build an app around some content for a friend.
    They are a mix of PNG and JPEG images.
    One on of them I get no image loaded and an error spit to the console. ANy ideas as to why?
    Heres the output:

    Sat Sep 11 14:58:55 mercury.config myAPP[1334] : CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component colorspace; kCGImageAlphaLast; 6176 bytes/row.
    Sat Sep 11 14:58:55 mercury.config myAPP[1334] : CGContextConcatCTM: invalid context
    Sat Sep 11 14:58:55 mercury.config myAPP[1334] : CGContextSetInterpolationQuality: invalid context
    Sat Sep 11 14:58:55 mercury.config myAPP[1334] : CGContextDrawImage: invalid context
    Sat Sep 11 14:58:55 mercury.config myAPP[1334] : CGBitmapContextCreateImage: invalid context

    • G Allan says:

      I used the following to completely ignore the alpha information from CGImageGetBitmapInfo. Which seems to work for me!

      // no alpha channel
      CGBitmapInfo info=CGImageGetBitmapInfo(imageRef);
      info = info & ~kCGBitmapAlphaInfoMask;
      info= info | kCGImageAlphaNoneSkipLast;
      // Build a context that’s the same dimensions as the new size
      CGContextRef bitmap = CGBitmapContextCreate(NULL,
      newRect.size.width,
      newRect.size.height,
      CGImageGetBitsPerComponent(imageRef),
      0,
      CGImageGetColorSpace(imageRef),
      info);

  5. felix says:

    Im trying to make a photo editor so i grab photos from the library or local app folder and i can scale, rotate and pan them but how do apply all these transformations to the larger image and save it to a file? currently i can save whats on the screen by copying the image and then saving the raw image data to a file. however the max size would only be 480X320. I’m looking to apply my transformations to the original 2400X1900 picture and saving that data to file. Any suggestions?

  6. [...] thing to note is I’m using some great UIImage Categories from here for resizing the image and cropping out the tiles. You just need to include those in your project [...]

  7. vodkhang says:

    Hi, Using the below block of code, I can fix the PNG problem:

    CGBitmapInfo info=CGImageGetBitmapInfo(imageRef);
    if (info == kCGImageAlphaLast) {
    info = kCGImageAlphaNoneSkipLast;
    }
    info = info & ~kCGBitmapAlphaInfoMask;
    info= info | kCGImageAlphaNoneSkipLast;

    However, I still have the problem with GIF format, is it supported? The error message says something about unsupported colorspace

    Error: CGBitmapContextCreate: unsupported colorspace.
    Error: CGContextConcatCTM: invalid context
    Error: CGContextSetInterpolationQuality: invalid context
    Error: CGContextDrawImage: invalid context
    Error: CGBitmapContextCreateImage: invalid contextt

    • Did you find a fix for this? I am getting similar errors in 4.2 update.

      • Ishaq Malik says:

        in my case it was because the color space model was kCGColorSpaceModelIndexed, So I fixed with something like this:

        CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);

        if(CGColorSpaceGetModel(colorspaceRef) == kCGColorSpaceModelIndexed)
        {
        colorspaceRef = CGColorSpaceCreateDeviceRGB();
        }

        CGContextRef bitmap = CGBitmapContextCreate(NULL,
        newRect.size.width,
        newRect.size.height,
        CGImageGetBitsPerComponent(imageRef),
        0,
        colorspaceRef,
        info);

        • Nice work! And thanks for the comments. Unfortunately it still didn’t work for me on iOS 4.x with 16 bit images, such as PNG created by screen captures. You’ll get white rectangle and this error in your console:

          <Error>: Unsupported pixel description – 3 components, 16 bits-per-component, 64 bits-per-pixel

          So my additional workaround is to set bits per component to 8:

          CGContextRef bitmap =
           CGBitmapContextCreate
            (NULL,
            newRect.size.width,
            newRect.size.height,
            8, //CGImageGetBitsPerComponent(imageRef),
            0,
            CGImageGetColorSpace(imageRef),
            kCGImageAlphaNoneSkipLast);

          Ugly, yeah. Anyway, I’ve forked coreyalder’s github project. That was a good idea.

          • philtre says:

            Shouldn’t:
            kCGImageAlphaNoneSkipLast
            actually be:
            CGImageGetAlphaInfo(imageRef)
            ?

            Otherwise the resulting image will always be opaque.

          • philtre says:

            Actually, in case the alpha info of the original image is kCGImageAlphaLast, you have to perform an additional step:

            CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);

            if(alphaInfo == kCGImageAlphaLast || alphaInfo == kCGImageAlphaFirst) {
            alphaInfo = kCGImageAlphaPremultipliedLast;
            }
            CGContextRef bitmap = CGBitmapContextCreate( NULL, newRect.size.width, newRect.size.height, 8, 0, CGImageGetColorSpace( imageRef ), alphaInfo);

            Does this look about right?

  8. Mathias says:

    Hey Trevor!

    Thanks a bundle for your code, it spared me from having to write the same!
    Again, thanks a lot!!!

    cheers,
    Mathias

  9. [...] Another thing that user were asking for was the ability to play offline – with the photos from there own. An interesting technical part in this feature was that the photos made by iPhone4 are pretty big and I had a performance problem with them. Thanks to this post: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/ [...]

  10. Yam Hiu Chun Draco says:

    I am a newbie to objective C but I really want to use the library. After doing some research to it. I think I have found a solution to the png format problem although I am not sure if i am correct.

    I am using the following function:

    – (UIImage *)resizedImage:(CGSize)newSize
    transform:(CGAffineTransform)transform
    drawTransposed:(BOOL)transpose
    interpolationQuality:(CGInterpolationQuality)quality

    and I have changed the

    CGImageGetBitmapInfo(imageRef) to kCGImageAlphaPremultipliedFirst and it works with a transparent background. Please help me out if i am wrong. I am not quite sure with the concept.

    the code inside becomes
    // Build a context that’s the same dimensions as the new size
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
    newRect.size.width,
    newRect.size.height,
    CGImageGetBitsPerComponent(imageRef),
    0,
    CGImageGetColorSpace(imageRef),
    kCGImageAlphaPremultipliedFirst);

  11. Byron says:

    Trevor,

    Gotta say…this was exactly what I needed! Hours and hours of Googling…and hours of trying to figure out why things were oriented wrong and too small and you name it.

    Plopping this in fixed the whole shebang!

    I understand your diatribe against copy/paste programming, but really appreciate your contribution to the Art!

    Cheers,
    Byron

  12. Gerd says:

    I tested your library and run into an error in console:

    CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 128 bytes/row

    Exchanging CGImageGetBitmapInfo(imageRef) with kCGImageAlphaPremultipliedLast (value proposed in the Apple doc) solved the problem.

    This leads to
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
    newRect.size.width,
    newRect.size.height,
    CGImageGetBitsPerComponent(imageRef),
    0,
    CGImageGetColorSpace(imageRef),
    kCGImageAlphaPremultipliedLast);

    I have no clue, what happens here. I’m working on iOS4.2, perhaps any change here?

  13. Cory says:

    I put up a github repo for UIImage+Resize. Lots of people are using this in projects on there, but I figured a central repo would be useful so people can clone or submodule it, and any bug fixes or updates can be tracked. Check it out: https://github.com/coryalder/UIImage_Resize

    Thanks for this article! Everyone should heed the warning here and copy and paste with care. :)

  14. simple mind says:

    These classes seem to be of utmost quality. it saddens me to find that they are secured against usage by the simple folks like me and bob.

    could some enlightened souls put a version of this online that uses a simple class-name instead of this UIImage (Resize) trickery?

    i cant even google that, because i dont know how its called…and i have never seen the likes, despite having read like 4 objective-c books!

    its witchcraft, i say!

  15. Mapa says:

    Thank you for the nice image resizing code. This seems to work perfectly in my need.

  16. great mind says:

    dear simple mind,

    please google “objective-c categories” to find the answers to your questions.

  17. [...] Resizing a UIImage the right way How to resize and scale an image without all the gotchas. [...]

  18. foobar says:

    thanks, saved me some time. i copied & pasted !

  19. foobar says:

    i was getting an error creating the bitmap context to resize an image. said unsupported parameter combination. i changed the last parameter to kCGImageAlphaPremultipliedLast instead of your “CGImageGetBitmapInfo(imageRef)” and that seems to have fixed it.

  20. Henrik Erlandsson says:

    Nice work, but I would like to see more suggestions from you for preventing copy-paste coding than criticism of it. Of all the languages and frameworks I’ve programmed, nothing has required copy-paste of working code as much as the lethal combination of Objective-C and Cocoa Touch. Don’t you think that every decent programmer in the world would code reusable components in an instant if it this combo didn’t throw up at every attempt to be a good programmer?

  21. Todd Rimes says:

    Great article and I cannot wait to use your code. I have been searching for something just like it and had almost decided I was going to have to write it myself. Thank you!

  22. [...] Resize a UIImage the right way — Trevor’s Bike Shed [...]

  23. reecon says:

    Outstanding work. Saves a lot of time. Thanks!

  24. Jason says:

    I imported your library however I am not seeing any improvement on my rotated images! Here is how I am using it:

    imgView.image = [[image transparentBorderImage: 10] croppedImage: CGRectMake(10,10,image.size.width,image.size.height)];

    imgView is a UIImageView and image is my raw UIImage, imgView lives inside a UIScrollView.

    The images are still jagged!

    • Trevor says:

      You are not seeing any improvement in resized images because you are not resizing. The transparentBorderImage and croppedImage methods do not resize the content of the image.

  25. Felix says:

    Hey,

    initial retrieve the code works not for me! The fault lay with me and now I’m happy!
    Thank you, have long sought nor this solution!

    Regards,
    Felix

    Sorry for my bad english!

  26. Pål de Vibe says:

    Amazing article and code. You are the dude! Thanks for contributing with strong knowledge in a community that needs it!

  27. asish says:

    please help me ..can i crop any part of the image using this code.
    i mean if a user select dynamically a part of the picture which is selected from his gallery to an UIImageview.????????

    can anyone give me an answer????

    • Trevor says:

      Yes, you can crop any part of the image using this code.

      No, the code does not provide a graphical interface that would allow the user to select the crop area. You’ll have to write that yourself.

  28. Joe Swindler says:

    This is great! I’ve had a nasty image orientation bug in my Faceover app for awhile that this fixed. Donation on the way!

  29. SkipK says:

    Hey Trevor,

    First off.. great stuff, thanks for sharing!

    I’m getting some warning messages from the compiler in transformForOrientation in UIImage+Resize.m. It says all the enumerated types are not being handled by the switch statement. I see you handle the other enumerated types in a second switch statement. Is there any reason you didn’t combine them into one switch statement?

    Inquiring minds wanna know! :)

    -Skip

  30. Ron Miller says:

    Thank you very much for the code! It is very complete, and very useful!

    I have run into a crash while resizing, and I was wondering if anyone else has seen it. I think that it might be an out of memory error and the OS may be terminating my app. It happens during CGContextDrawImage() while working on a 5616×3744 image. I was trying to resize an image imported from my camera to my iPad, and then resize it to a more manageable level.

    My iPhone4 never seems to have a problem … just my iPad (original).

    I never see an out of memory notification in my App, but I’m guessing that I never receive one because the main run loop is allocating a huge chunk of memory in one shot, and the OS terminates the App before it is able to send it any notifications. When running in the debugger, I stop right before CGContextDrawImage(), and after stepping over that routine, my program exits with signal “0” and the debugger exits as well.

    Does my theory sound correct?

  31. Reb Jemberts says:

    I hate people that copy and paste code!! We’re like, so totally above them, you know??

  32. B says:

    a little trail of internet searches while suffering from the dreaded copy and paste programming bug finally left me here. Thank you.

  33. Joe Swindler says:

    Ron Miller, I’m seeing the exact same problem. The original image is 4800 x 6000. No matter how small I try to resize it on the iPad I get two memory warnings and a crash. On the iPhone, it works fine.

    Any ideas? I’ve heard virtual memory may be an option but thought I’d check if anyone had tried anything else first.

  34. Jim says:

    Thank you! The code looks good, and the post opened my eyes to the possibility that some services might be ignoring the orientation in the EXIF and TIFF data, which turned out to be the cause of my “Why is Flickr sometimes showing my images incorrectly rotated?” problem.

  35. Ivica says:

    Thank you very much for this useful article and the code snippets. I am using it in “The Greedy Sponge” for making rounded rect images, so I donated few $$ for it. Thanks again.

    Moreover, I am trying to “misuse” your lib for rotating a screenshot taken in landscape mode. For this purpose I made the two helper methods public. Doing the following:

    CGAffineTransform transform = CGAffineTransformRotate(CGAffineTransformIdentity, -M_PI_2);
    UIImage* transformedImg =
    [image
    resizedImage:image.size
    transform:transform
    drawTransposed:NO
    interpolationQuality:kCGInterpolationHigh];

    However, what I get is only an empty image.
    If I change the transform to identity, as expected everything remains unchanged, but as soon as I try to rotate, it gets gray.

    I hope you can help me on this.

  36. Martin says:

    Looks great!

    On thing regarding memory usage: Lets say you want the same image displayed twice with different sizes. Doesn’t your resize code create copies of the same image twice? Ie. allocates more memory.

    Compare that with using two UIImageView’s (with different sizes) backed by *the same* UIImage. That only uses one copy and saves lots of memory. My app uses lots of images, so that is how I do it now for that reason. (Of course, I get the jagged edges etc when rotating which).

  37. Luck says:

    Just wanted to say a big thanks for posting this great article and source code. It saved me hours of time.

  38. Malvid says:

    This code has been very useful to me. But the resize function didn’t handle retina displays correctly (you know, the “@2x” / [UIImage scale] stuff), so I modified the code for my needs, and here it is (to be substituted at UIImage+Resize.m). All changes involve the text “self.scale”.


    #pragma mark Private helper methods

    // Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size
    // The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation
    // If the new size is not integral, it will be rounded up
    - (UIImage *)resizedImage:(CGSize)newSize
    transform:(CGAffineTransform)transform
    drawTransposed:(BOOL)transpose
    interpolationQuality:(CGInterpolationQuality)quality {
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width*self.scale, newSize.height*self.scale));
    CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width);
    CGImageRef imageRef = self.CGImage;

    // Build a context that's the same dimensions as the new size
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
    newRect.size.width,
    newRect.size.height,
    CGImageGetBitsPerComponent(imageRef),
    0,
    CGImageGetColorSpace(imageRef),
    CGImageGetBitmapInfo(imageRef));

    // Rotate and/or flip the image if required by its orientation
    CGContextConcatCTM(bitmap, transform);

    // Set the quality level to use when rescaling
    CGContextSetInterpolationQuality(bitmap, quality);

    // Draw into the context; this scales the image
    CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef);

    // Get the resized image from the context and a UIImage
    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef scale:self.scale orientation:self.imageOrientation];

    // Clean up
    CGContextRelease(bitmap);
    CGImageRelease(newImageRef);

    return newImage;
    }

  39. Nathan says:

    Hey

    I have tried using these methods. Whilst I have received no errors, the UIImage being returned to me has no size or image to it (a height and width check after the fact returns a zero for the new UIImage, but returns the dimensions of the original UIIMage).

    I am relatively new to this, but I can’t seem to explain away the result.

    Below is where I am calling the required method:

    UIImage *rowImage = [UIImage imageNamed:thisImage];
    CGSize newSize = CGSizeMake(98.0, 68.0);

    UIImage *newImage = [rowImage resizedImage:newSize interpolationQuality:kCGInterpolationHigh];

  40. Adrian says:

    Hi!
    If someone has problems with UIImage+Alpha’s transparentBorderImage generating a black border instead of a transparent one, you need to replace self.CGImage with image.CGImage in the parameters of the call to CGBitmapContextCreate. That solved it for me. Thanks for the code Trevor!

  41. Rob says:

    Thanks Adrian – same change fixed a problem with UIImage+RoundedCorner that emerged for me after upgrade to iOS 5.

    And thanks for the code Trevor!

  42. JonG says:

    Hi Trevor, thanks for posting these high quality categories!

    I’m currently using them in an iPhone app where I capture photos from the camera and then resize and crop them before uploading them to a server.

    The categories work well, but I’ve found them to be rather slow. When resizing images captured at 1280×720 on an iPhone 4 (using AVFoundation not UIImagePickerController), resizing and cropping them to 960×640 and 100×100 (for upload and preview thumbnails respectively), it takes about 3.5 seconds total for each photo to be processed (two resize and two crop operations to produce an image for upload and a thumbnail for display).

    In my application, since a user can take up to 4 images as part of each upload operation, it’s possible the app will spend up to 16 seconds resizing images in the background! I manage to keep the interface responsive by using an NSOperationQueue for each resize operation, and also only process thumbnails if needed, but it’s easily the slowest part of my application.

    Even using low quality interpolation, there’s only a small improvement in speed. Interestingly enough, there isn’t a big difference in the time to complete each resize (perhaps 1s) between an iPhone 3G and an iPhone 4, but an iPad 2 with its two cores completes the work in under a second. When looking at what’s going on in Time Profiler, it shows that all the time is being spent in the CGContextDrawImage function – which is what I would expect if everything was working right, so I dont think I’m using the categories incorrectly.

    I wanted to ask if these timings seem typical to you. I’m hoping that with iOS 5, using the Core Image framework and the Accelerate framework will speed things up considerably (will require a rewrite of these categories to take advantage of those frameworks).

    Any suggestions on how to resize images faster would be greatly appreciated. Thanks again for releasing this code.

  43. [...] Resize a UIImage the right way Posted on August 9, 2011 by joris When deadlines loom, even skilled and experienced programmers can get a little sloppy. The pressure to ship may cause them to cut corners and look for a quick and easy solution, even if that solution is sure to cause trouble later on. Eventually, their coding style devolves into copy and paste programming, a lamentable tactic that involves cherry-picking snippets of code from a past project and putting them to use in the current one. Trevor’s Bike Shed [...]

  44. [...] them to go on a 44-pixel-tall table view cell. I’m using the popular image-resizing routines from Trevor’s Bike Shed to do the resizing with a nice interpolation quailty.Those resized images are now cached. I use an [...]

  45. Hey,

    First of all thank you for the useful categories.

    In the following function:

    singleRoundedCornerImage:(UIRectCorner)corner cornerSize(NSInteger)cornerSize

    It seems that scale is not preserved.


    UIGraphicsBeginImageContext(aRect.size);

    Should be


    UIGraphicsBeginImageContextWithOptions(aRect.size, false, self.scale);

  46. [...] job extending UIImage to do some of the things you need it to do with some nice categories. Check it out here and make good use of it. window.fbAsyncInit = function() { FB.init({ appId: [...]

  47. [...] – Used code found here [...]

  48. Chris Chapman says:

    Hi

    I’m using your UIImage+Alpha with great success on an iPad app but it’s in a project where my part of the work gets supplied as a .a library file to be included by others.

    On adding your UIImage+Alpha code it all works fine while I am compiling all the source including yours, but when I build a .a library file and try and use it I get this error:

    -[UIImage imageWithAlpha]: unrecognized selector sent to instance 0x35a1d0
    2011-09-22 18:49:37.260 VisualizerLibOnlyTester[173:707] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UIImage imageWithAlpha]: unrecognized selector sent to instance 0x35a1d0′

    Any ideas?

    Thanks

    Chris

  49. Jay Wright says:

    Great classes. I’ve been using them without a hitch for a while now.

    Recently I’m taken on a high memory consuming app which is spitting out an interesting error that I can’t find much about on-line:

    ImageIO: <ERROR> PNGNot enough image data

    Has anyone seen this before? It seems to happen when memory usage his high (no warning issued) and I resize a seemingly in tact UIImage. I don’t believe it’s tied to these caegories and it seems to come from deep within the CG framework.

    Thoughts? Is there a way to check if a UIImage is uncorrupted and in tact?

    • Jay Wright says:

      It seems to come from the

      CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef);

      method in UIImage Resize.

Leave a Reply