├── README.md ├── UIImage+Alpha.h ├── UIImage+Alpha.m ├── UIImage+Resize.h ├── UIImage+Resize.m ├── UIImage+RoundedCorner.h └── UIImage+RoundedCorner.m /README.md: -------------------------------------------------------------------------------- 1 | UIImage-categories 2 | ================== 3 | 4 | These classes are UIImage categories written taken from Trevor's Bike Shed. 5 | 6 | See author post for explanation: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/ 7 | 8 | It provides: 9 | 10 | UIImage+Resize.h, UIImage+Resize.m 11 | ---------------------------------- 12 | Extends the UIImage class to support resizing (optionally preserving the original aspect ratio), cropping, and generating thumbnails. 13 | 14 | UIImage+RoundedCorner.h, UIImage+RoundedCorner.m 15 | ---------------------------------- 16 | Extends the UIImage class to support adding rounded corners to an image. 17 | 18 | UIImage+Alpha.h, UIImage+Alpha.m 19 | ---------------------------------- 20 | Extends the UIImage class with helper methods for working with alpha layers (transparencies). -------------------------------------------------------------------------------- /UIImage+Alpha.h: -------------------------------------------------------------------------------- 1 | // UIImage+Alpha.h 2 | // Created by Trevor Harmon on 9/20/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | // Helper methods for adding an alpha layer to an image 7 | @interface UIImage (Alpha) 8 | - (BOOL)hasAlpha; 9 | - (UIImage *)imageWithAlpha; 10 | - (UIImage *)transparentBorderImage:(NSUInteger)borderSize; 11 | @end 12 | -------------------------------------------------------------------------------- /UIImage+Alpha.m: -------------------------------------------------------------------------------- 1 | // UIImage+Alpha.m 2 | // Created by Trevor Harmon on 9/20/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | #import "UIImage+Alpha.h" 7 | 8 | // Private helper methods 9 | @interface UIImage () 10 | - (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size; 11 | @end 12 | 13 | @implementation UIImage (Alpha) 14 | 15 | // Returns true if the image has an alpha layer 16 | - (BOOL)hasAlpha { 17 | CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage); 18 | return (alpha == kCGImageAlphaFirst || 19 | alpha == kCGImageAlphaLast || 20 | alpha == kCGImageAlphaPremultipliedFirst || 21 | alpha == kCGImageAlphaPremultipliedLast); 22 | } 23 | 24 | // Returns a copy of the given image, adding an alpha channel if it doesn't already have one 25 | - (UIImage *)imageWithAlpha { 26 | if ([self hasAlpha]) { 27 | return self; 28 | } 29 | 30 | CGImageRef imageRef = self.CGImage; 31 | size_t width = CGImageGetWidth(imageRef); 32 | size_t height = CGImageGetHeight(imageRef); 33 | 34 | // The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error 35 | CGContextRef offscreenContext = CGBitmapContextCreate(NULL, 36 | width, 37 | height, 38 | 8, 39 | 0, 40 | CGImageGetColorSpace(imageRef), 41 | kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); 42 | 43 | // Draw the image into the context and retrieve the new image, which will now have an alpha layer 44 | CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef); 45 | CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext); 46 | UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha]; 47 | 48 | // Clean up 49 | CGContextRelease(offscreenContext); 50 | CGImageRelease(imageRefWithAlpha); 51 | 52 | return imageWithAlpha; 53 | } 54 | 55 | // Returns a copy of the image with a transparent border of the given size added around its edges. 56 | // If the image has no alpha layer, one will be added to it. 57 | - (UIImage *)transparentBorderImage:(NSUInteger)borderSize { 58 | // If the image does not have an alpha layer, add one 59 | UIImage *image = [self imageWithAlpha]; 60 | 61 | CGRect newRect = CGRectMake(0, 0, image.size.width + borderSize * 2, image.size.height + borderSize * 2); 62 | 63 | // Build a context that's the same dimensions as the new size 64 | CGContextRef bitmap = CGBitmapContextCreate(NULL, 65 | newRect.size.width, 66 | newRect.size.height, 67 | CGImageGetBitsPerComponent(self.CGImage), 68 | 0, 69 | CGImageGetColorSpace(self.CGImage), 70 | CGImageGetBitmapInfo(self.CGImage)); 71 | 72 | // Draw the image in the center of the context, leaving a gap around the edges 73 | CGRect imageLocation = CGRectMake(borderSize, borderSize, image.size.width, image.size.height); 74 | CGContextDrawImage(bitmap, imageLocation, self.CGImage); 75 | CGImageRef borderImageRef = CGBitmapContextCreateImage(bitmap); 76 | 77 | // Create a mask to make the border transparent, and combine it with the image 78 | CGImageRef maskImageRef = [self newBorderMask:borderSize size:newRect.size]; 79 | CGImageRef transparentBorderImageRef = CGImageCreateWithMask(borderImageRef, maskImageRef); 80 | UIImage *transparentBorderImage = [UIImage imageWithCGImage:transparentBorderImageRef]; 81 | 82 | // Clean up 83 | CGContextRelease(bitmap); 84 | CGImageRelease(borderImageRef); 85 | CGImageRelease(maskImageRef); 86 | CGImageRelease(transparentBorderImageRef); 87 | 88 | return transparentBorderImage; 89 | } 90 | 91 | #pragma mark - 92 | #pragma mark Private helper methods 93 | 94 | // Creates a mask that makes the outer edges transparent and everything else opaque 95 | // The size must include the entire mask (opaque part + transparent border) 96 | // The caller is responsible for releasing the returned reference by calling CGImageRelease 97 | - (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size { 98 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); 99 | 100 | // Build a context that's the same dimensions as the new size 101 | CGContextRef maskContext = CGBitmapContextCreate(NULL, 102 | size.width, 103 | size.height, 104 | 8, // 8-bit grayscale 105 | 0, 106 | colorSpace, 107 | kCGBitmapByteOrderDefault | kCGImageAlphaNone); 108 | 109 | // Start with a mask that's entirely transparent 110 | CGContextSetFillColorWithColor(maskContext, [UIColor blackColor].CGColor); 111 | CGContextFillRect(maskContext, CGRectMake(0, 0, size.width, size.height)); 112 | 113 | // Make the inner part (within the border) opaque 114 | CGContextSetFillColorWithColor(maskContext, [UIColor whiteColor].CGColor); 115 | CGContextFillRect(maskContext, CGRectMake(borderSize, borderSize, size.width - borderSize * 2, size.height - borderSize * 2)); 116 | 117 | // Get an image of the context 118 | CGImageRef maskImageRef = CGBitmapContextCreateImage(maskContext); 119 | 120 | // Clean up 121 | CGContextRelease(maskContext); 122 | CGColorSpaceRelease(colorSpace); 123 | 124 | return maskImageRef; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /UIImage+Resize.h: -------------------------------------------------------------------------------- 1 | // UIImage+Resize.h 2 | // Created by Trevor Harmon on 8/5/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | // Extends the UIImage class to support resizing/cropping 7 | @interface UIImage (Resize) 8 | - (UIImage *)croppedImage:(CGRect)bounds; 9 | - (UIImage *)thumbnailImage:(NSInteger)thumbnailSize 10 | transparentBorder:(NSUInteger)borderSize 11 | cornerRadius:(NSUInteger)cornerRadius 12 | interpolationQuality:(CGInterpolationQuality)quality; 13 | - (UIImage *)resizedImage:(CGSize)newSize 14 | interpolationQuality:(CGInterpolationQuality)quality; 15 | - (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode 16 | bounds:(CGSize)bounds 17 | interpolationQuality:(CGInterpolationQuality)quality; 18 | @end 19 | -------------------------------------------------------------------------------- /UIImage+Resize.m: -------------------------------------------------------------------------------- 1 | // UIImage+Resize.m 2 | // Created by Trevor Harmon on 8/5/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | #import "UIImage+Resize.h" 7 | #import "UIImage+RoundedCorner.h" 8 | #import "UIImage+Alpha.h" 9 | 10 | // Private helper methods 11 | @interface UIImage () 12 | - (UIImage *)resizedImage:(CGSize)newSize 13 | transform:(CGAffineTransform)transform 14 | drawTransposed:(BOOL)transpose 15 | interpolationQuality:(CGInterpolationQuality)quality; 16 | - (CGAffineTransform)transformForOrientation:(CGSize)newSize; 17 | @end 18 | 19 | @implementation UIImage (Resize) 20 | 21 | // Returns a copy of this image that is cropped to the given bounds. 22 | // The bounds will be adjusted using CGRectIntegral. 23 | // This method ignores the image's imageOrientation setting. 24 | - (UIImage *)croppedImage:(CGRect)bounds { 25 | CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds); 26 | UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; 27 | CGImageRelease(imageRef); 28 | return croppedImage; 29 | } 30 | 31 | // Returns a copy of this image that is squared to the thumbnail size. 32 | // If transparentBorder is non-zero, a transparent border of the given size will be added around the edges of the thumbnail. (Adding a transparent border of at least one pixel in size has the side-effect of antialiasing the edges of the image when rotating it using Core Animation.) 33 | - (UIImage *)thumbnailImage:(NSInteger)thumbnailSize 34 | transparentBorder:(NSUInteger)borderSize 35 | cornerRadius:(NSUInteger)cornerRadius 36 | interpolationQuality:(CGInterpolationQuality)quality { 37 | UIImage *resizedImage = [self resizedImageWithContentMode:UIViewContentModeScaleAspectFill 38 | bounds:CGSizeMake(thumbnailSize, thumbnailSize) 39 | interpolationQuality:quality]; 40 | 41 | // Crop out any part of the image that's larger than the thumbnail size 42 | // The cropped rect must be centered on the resized image 43 | // Round the origin points so that the size isn't altered when CGRectIntegral is later invoked 44 | CGRect cropRect = CGRectMake(round((resizedImage.size.width - thumbnailSize) / 2), 45 | round((resizedImage.size.height - thumbnailSize) / 2), 46 | thumbnailSize, 47 | thumbnailSize); 48 | UIImage *croppedImage = [resizedImage croppedImage:cropRect]; 49 | 50 | UIImage *transparentBorderImage = borderSize ? [croppedImage transparentBorderImage:borderSize] : croppedImage; 51 | 52 | return [transparentBorderImage roundedCornerImage:cornerRadius borderSize:borderSize]; 53 | } 54 | 55 | // Returns a rescaled copy of the image, taking into account its orientation 56 | // The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter 57 | - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality { 58 | BOOL drawTransposed; 59 | 60 | switch (self.imageOrientation) { 61 | case UIImageOrientationLeft: 62 | case UIImageOrientationLeftMirrored: 63 | case UIImageOrientationRight: 64 | case UIImageOrientationRightMirrored: 65 | drawTransposed = YES; 66 | break; 67 | 68 | default: 69 | drawTransposed = NO; 70 | } 71 | 72 | return [self resizedImage:newSize 73 | transform:[self transformForOrientation:newSize] 74 | drawTransposed:drawTransposed 75 | interpolationQuality:quality]; 76 | } 77 | 78 | // Resizes the image according to the given content mode, taking into account the image's orientation 79 | - (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode 80 | bounds:(CGSize)bounds 81 | interpolationQuality:(CGInterpolationQuality)quality { 82 | CGFloat horizontalRatio = bounds.width / self.size.width; 83 | CGFloat verticalRatio = bounds.height / self.size.height; 84 | CGFloat ratio; 85 | 86 | switch (contentMode) { 87 | case UIViewContentModeScaleAspectFill: 88 | ratio = MAX(horizontalRatio, verticalRatio); 89 | break; 90 | 91 | case UIViewContentModeScaleAspectFit: 92 | ratio = MIN(horizontalRatio, verticalRatio); 93 | break; 94 | 95 | default: 96 | [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode]; 97 | } 98 | 99 | CGSize newSize = CGSizeMake(self.size.width * ratio, self.size.height * ratio); 100 | 101 | return [self resizedImage:newSize interpolationQuality:quality]; 102 | } 103 | 104 | #pragma mark - 105 | #pragma mark Private helper methods 106 | 107 | // Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size 108 | // The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation 109 | // If the new size is not integral, it will be rounded up 110 | - (UIImage *)resizedImage:(CGSize)newSize 111 | transform:(CGAffineTransform)transform 112 | drawTransposed:(BOOL)transpose 113 | interpolationQuality:(CGInterpolationQuality)quality { 114 | CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); 115 | CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); 116 | CGImageRef imageRef = self.CGImage; 117 | 118 | // Build a context that's the same dimensions as the new size 119 | CGContextRef bitmap = CGBitmapContextCreate(NULL, 120 | newRect.size.width, 121 | newRect.size.height, 122 | CGImageGetBitsPerComponent(imageRef), 123 | 0, 124 | CGImageGetColorSpace(imageRef), 125 | CGImageGetBitmapInfo(imageRef)); 126 | 127 | // Rotate and/or flip the image if required by its orientation 128 | CGContextConcatCTM(bitmap, transform); 129 | 130 | // Set the quality level to use when rescaling 131 | CGContextSetInterpolationQuality(bitmap, quality); 132 | 133 | // Draw into the context; this scales the image 134 | CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); 135 | 136 | // Get the resized image from the context and a UIImage 137 | CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); 138 | UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; 139 | 140 | // Clean up 141 | CGContextRelease(bitmap); 142 | CGImageRelease(newImageRef); 143 | 144 | return newImage; 145 | } 146 | 147 | // Returns an affine transform that takes into account the image orientation when drawing a scaled image 148 | - (CGAffineTransform)transformForOrientation:(CGSize)newSize { 149 | CGAffineTransform transform = CGAffineTransformIdentity; 150 | 151 | switch (self.imageOrientation) { 152 | case UIImageOrientationDown: // EXIF = 3 153 | case UIImageOrientationDownMirrored: // EXIF = 4 154 | transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); 155 | transform = CGAffineTransformRotate(transform, M_PI); 156 | break; 157 | 158 | case UIImageOrientationLeft: // EXIF = 6 159 | case UIImageOrientationLeftMirrored: // EXIF = 5 160 | transform = CGAffineTransformTranslate(transform, newSize.width, 0); 161 | transform = CGAffineTransformRotate(transform, M_PI_2); 162 | break; 163 | 164 | case UIImageOrientationRight: // EXIF = 8 165 | case UIImageOrientationRightMirrored: // EXIF = 7 166 | transform = CGAffineTransformTranslate(transform, 0, newSize.height); 167 | transform = CGAffineTransformRotate(transform, -M_PI_2); 168 | break; 169 | } 170 | 171 | switch (self.imageOrientation) { 172 | case UIImageOrientationUpMirrored: // EXIF = 2 173 | case UIImageOrientationDownMirrored: // EXIF = 4 174 | transform = CGAffineTransformTranslate(transform, newSize.width, 0); 175 | transform = CGAffineTransformScale(transform, -1, 1); 176 | break; 177 | 178 | case UIImageOrientationLeftMirrored: // EXIF = 5 179 | case UIImageOrientationRightMirrored: // EXIF = 7 180 | transform = CGAffineTransformTranslate(transform, newSize.height, 0); 181 | transform = CGAffineTransformScale(transform, -1, 1); 182 | break; 183 | } 184 | 185 | return transform; 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /UIImage+RoundedCorner.h: -------------------------------------------------------------------------------- 1 | // UIImage+RoundedCorner.h 2 | // Created by Trevor Harmon on 9/20/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | // Extends the UIImage class to support making rounded corners 7 | @interface UIImage (RoundedCorner) 8 | - (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize; 9 | @end 10 | -------------------------------------------------------------------------------- /UIImage+RoundedCorner.m: -------------------------------------------------------------------------------- 1 | // UIImage+RoundedCorner.m 2 | // Created by Trevor Harmon on 9/20/09. 3 | // Free for personal or commercial use, with or without modification. 4 | // No warranty is expressed or implied. 5 | 6 | #import "UIImage+RoundedCorner.h" 7 | #import "UIImage+Alpha.h" 8 | 9 | // Private helper methods 10 | @interface UIImage () 11 | - (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight; 12 | @end 13 | 14 | @implementation UIImage (RoundedCorner) 15 | 16 | // Creates a copy of this image with rounded corners 17 | // If borderSize is non-zero, a transparent border of the given size will also be added 18 | // Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ 19 | - (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize { 20 | // If the image does not have an alpha layer, add one 21 | UIImage *image = [self imageWithAlpha]; 22 | 23 | // Build a context that's the same dimensions as the new size 24 | CGContextRef context = CGBitmapContextCreate(NULL, 25 | image.size.width, 26 | image.size.height, 27 | CGImageGetBitsPerComponent(image.CGImage), 28 | 0, 29 | CGImageGetColorSpace(image.CGImage), 30 | CGImageGetBitmapInfo(image.CGImage)); 31 | 32 | // Create a clipping path with rounded corners 33 | CGContextBeginPath(context); 34 | [self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2) 35 | context:context 36 | ovalWidth:cornerSize 37 | ovalHeight:cornerSize]; 38 | CGContextClosePath(context); 39 | CGContextClip(context); 40 | 41 | // Draw the image to the context; the clipping path will make anything outside the rounded rect transparent 42 | CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); 43 | 44 | // Create a CGImage from the context 45 | CGImageRef clippedImage = CGBitmapContextCreateImage(context); 46 | CGContextRelease(context); 47 | 48 | // Create a UIImage from the CGImage 49 | UIImage *roundedImage = [UIImage imageWithCGImage:clippedImage]; 50 | CGImageRelease(clippedImage); 51 | 52 | return roundedImage; 53 | } 54 | 55 | #pragma mark - 56 | #pragma mark Private helper methods 57 | 58 | // Adds a rectangular path to the given context and rounds its corners by the given extents 59 | // Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ 60 | - (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight { 61 | if (ovalWidth == 0 || ovalHeight == 0) { 62 | CGContextAddRect(context, rect); 63 | return; 64 | } 65 | CGContextSaveGState(context); 66 | CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); 67 | CGContextScaleCTM(context, ovalWidth, ovalHeight); 68 | CGFloat fw = CGRectGetWidth(rect) / ovalWidth; 69 | CGFloat fh = CGRectGetHeight(rect) / ovalHeight; 70 | CGContextMoveToPoint(context, fw, fh/2); 71 | CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); 72 | CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); 73 | CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); 74 | CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); 75 | CGContextClosePath(context); 76 | CGContextRestoreGState(context); 77 | } 78 | 79 | @end 80 | --------------------------------------------------------------------------------