Today I wrote a useful Objective-C method which uses Quartz 2D from the iPhone SDK to dynamically generate square thumbnails from any portfolio or landscape UIImage. I failed to find helpful results on Google for the various CGContextClipToRect/CGContextTranslateCTM/CGContextScaleCTM Quartz functions so after a lot of trial and error I managed to get the results I was after with a fairly efficient script – shown below.

So, if for example you want to get a 32×32 pixel square thumbnail, you can pass 32 as the ‘length’ parameter in the function below. For portfolio images the code will take a full square from the original image from the centre using the full width of your original portfolio image. Likewise, for a landscape image the script will use all the height available, clipping the right and left sides.

I think the most import thing I learned when writing this script is that when you call UIGraphicsBeginImageContext(CGSize) make sure that the size you pass in is the size you want your final image to be. Otherwise you end up having to call UIGraphicsEndImageContext() and UIGraphicsBeginImageContext() multiple times with different CGSize instances which I’m sure isn’t an optimised way to achieve the same results.

– (UIImage *)thumbWithSideOfLength:(float)length {

NSString *subdir = @”my/images/directory”;
NSString *filename = @”myOriginalImage.png”;
NSString *fullPathToThumbImage = [subdir stringByAppendingPathComponent:[NSString stringWithFormat:@”%dx%d%@”,(int) length, (int) length,filename];
NSString *fullPathToMainImage = [subdir stringByAppendingPathComponent:filename];

UIImage *thumbnail;

NSFileManager *fileManager = [NSFileManager defaultManager];

if ([fileManager fileExistsAtPath:fullPathToThumbImage] == YES) {
thumbnail = [UIImage imageWithContentsOfFile:fullPathToThumbImage];

}
else {
//couldn’t find a previously created thumb image so create one first…
UIImage *mainImage = [UIImage imageWithContentsOfFile:fullPathToMainImage];

UIImageView *mainImageView = [[UIImageView alloc] initWithImage:mainImage];

BOOL widthGreaterThanHeight = (mainImage.size.width > mainImage.size.height);
float sideFull = (widthGreaterThanHeight) ? mainImage.size.height : mainImage.size.width;

CGRect clippedRect = CGRectMake(0, 0, sideFull, sideFull);

//creating a square context the size of the final image which we will then
// manipulate and transform before drawing in the original image
UIGraphicsBeginImageContext(CGSizeMake(length, length));
CGContextRef currentContext = UIGraphicsGetCurrentContext();

CGContextClipToRect( currentContext, clippedRect);

CGFloat scaleFactor = length/sideFull;

if (widthGreaterThanHeight) {
//a landscape image – make context shift the original image to the left when drawn into the context
CGContextTranslateCTM(currentContext, -((mainImage.size.width – sideFull) / 2) * scaleFactor, 0);

}
else {
//a portfolio image – make context shift the original image upwards when drawn into the context
CGContextTranslateCTM(currentContext, 0, -((mainImage.size.height – sideFull) / 2) * scaleFactor);

}
//this will automatically scale any CGImage down/up to the required thumbnail side (length) when the CGImage gets drawn into the context on the next line of code
CGContextScaleCTM(currentContext, scaleFactor, scaleFactor);

[mainImageView.layer renderInContext:currentContext];

thumbnail = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(thumbnail);

[imageData writeToFile:fullPathToThumbImage atomically:YES];

thumbnail = [UIImage imageWithContentsOfFile:fullPathToThumbImage];
}
return thumbnail;
}

So if, for example your original image looked like this:

finn

Using the method above you could request a 100×100 pixel thumbnail which would result in this:

finn_thumb



35 Comments

  1. AP

    Dude, this was just what I was looking for. Awesome post. Kudos to you

  2. Marc

    Hi Nick,

    Your method to create a thumbnail is just what I need. May I incorporate this in the iPhone app that I am developing? Will gladly give you credit in the class.

    Thanks, Marc

  3. Nick Kuh

    Hi Marc – yes, of course you can use the code. With or without a credit

  4. Marc

    Thanks again Nick. It is working out great for me.

  5. Nick Kuh

    You’re very welcome 🙂

  6. Elke

    Thanks! This was exactly what I’m looking for! Great work!

  7. Ryan

    Thanks for the great piece of code. You are leaking memory with the mainImageView. You should release it 🙂

    THanks again!

  8. Lucas Still

    Wow, what a great piece of code! Like everyone else has said, this is exactly what I am looking for. It creates thumbnails in almost exactly the same position as those in the standard apple photos app.

    A little hint to those who are having any troubles using the code: if you run into any errors, try typing out the code instead of copy/paste. Sometimes it messes up a few characters.

    Thanks Nick!

  9. Praveen Kansara

    Thanks a lot. The code is very instructive.

  10. Martin Baker

    Thanks, this is great.

    BTW you’ve got en-dashes rather than regular dashes in the middle of the two CGContextTranslateCTM lines, so the code won’t compile.

  11. Li Zhonghao

    Thanks a lot for the useful information. What I need is to thumbnail a view. Is anyone have any idea for that?

    Thanks in advance.

  12. Lionel

    Hi, Thanks for the sample!
    But I have some kind of allocation problem with this code, It seems that the UIImage returned by UIGraphicsGetImageFromCurrentImageContext is never released…
    I’ve used this method to create blocks of 5 thumbnails lazy loaded in a uiscrollView, Everything is released correctly but the memory allocation still grows..
    Any idea?
    (I’ve already ensured to call it from the mean thread by the way)
    Thanks

  13. Abishek

    Is there a way to create thumbnails for a video file similar to this?

  14. x51

    Lionel: I have this same problem did, you find solution?

  15. Nick Kuh

    A Ryan mentioned, mainImageView is leaking memory.

    This change should resolve it:

    [mainImageView.layer renderInContext:currentContext];

    change to :

    [mainImageView.layer renderInContext:currentContext];
    [mainImageView release];mainImageView=nil;

  16. x51

    Hey Nick

    Big Gr33tz for you, I wrote in many places because i had issue whit similar program nobody could help me, I tought that this is some kind of bug in renderInContext, I’m so fucking happy that this nightmare is over 😉

    Regards,
    x51

  17. David

    In the example above I don’t understand why you use -renderInContext: at all. Why not just draw the image itself instead? That would do the exact same thing but without requiring QuartzCore and all the overhead -renderInContext: adds to this.

  18. Victor

    That great, Thanks you very much!!!

  19. Martin

    Huge thanks! It is exactly what I needed!!!!

  20. Manuel

    The following line

    [mainImageView.layer renderInContext:currentContext];

    caused problems for me – this can be solved by importing into the .m file! Hope that helps.

    And thanks so much for the code, really great help!

  21. Candy

    I forgot to moneitn one really important tool I use Jott Assistant.Jott is a call-in service (automated) that asks me who I want to Jott . I can say, me , or the name of anyone else in my address list. I then speak the message (enunciating properly). I can get reminders if I want. Jott then sends the message to me (or whoever I Jotted) through my (or their) email address.So, if I am talking to a client when it is impossible (or very inconvenient) to jot it down, I call Jott and it does it for me. I have it set up under speed dial and it is the first record in my phone’s address book.It saves me time and the experience of having forgotten an appointment.

  22. if you want hair product that does not contain sulfate but do the trick in cleansing and keeping the hair moisturized go to the vitamin health food store and get “sulfate and paraben free” hair product! I love it! I used prairie naturals and giovanni.

  23. cheap car insurance

    This wan character on journik is such an asshole. He pretends to be this wonderfull guru and healer but he is a fake. he is all about money money super collosal money. his ego hisso huge. I went to him for help, and he tore me apart on facebook for no reason. pass it on about this guy.. he is illuminati

  24. car insurance quotes

    Thank you, Subhan. I’ve learned that a schedule helps me stay focused and not take too much time away from my novels. And the novels should take priority.

  25. cheap insurance

    ang buong akala ko noong gabing pinag uusapan ninyo yan ni master dilson ay biro lang,, hehe,, totoo pala.. at noong gabi na yun din sir nag blow out kayo sa foodstrip at natulog sa nursery..hehemusta po sir?

  26. – Hi Becky, Sandy forwarded me the link to your blog and told me about Bloo. What an amazing and special dog. I am so deeply sorry for your loss. And oh how the children will miss him also.Your story about him is so touching and beautiful. If there is anything I can do please let me know.~ Violet

  27. Jane Smith

    Thanks, this is great.

    BTW you’ve got en-dashes rather than regular dashes in the middle of the two CGContextTranslateCTM lines, so the code won’t compile.

  28. Juniba

    To realize your tweet draft again, you will need to hold and drag back on the image gallery. While very flexible, WordPress thumbnails are messy and excessive once you want a selection of image sizes on your website. For instance, you might want to produce a thumbnail of a present image, draw into a buffer so that you are able to save it to a file, etc.

  29. Ruth J. Lewis

    You can create Aplications with iPhone SDK to use on iPod Touch, and a later release update for both will include an application to download other apps, if that is what you meant.

  30. Murtza Abbas

    That great, Thanks you very much!!!

  31. lawrencemax

    impressive article it is useful for me and get the best result for me and also help for my upcoming project keep more update.


Leave a reply