How to easily resize/optimize an image size with iOS? - ios

My application is downloading a set of image files from the network, and saving them to the local iPhone disk. Some of those images are pretty big in size (widths larger than 500 pixels, for instance). Since the iPhone doesn't even have a big enough display to show the image in its original size, I'm planning on resizing the image to something a bit smaller to save on space/performance.
Also, some of those images are JPEGs and they are not saved as the usual 60% quality setting.
How can I resize a picture with the iPhone SDK, and how can I change the quality setting of a JPEG image?

A couple of suggestions are provided as answers to this question. I had suggested the technique described in this post, with the relevant code:
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
As far as storage of the image, the fastest image format to use with the iPhone is PNG, because it has optimizations for that format. However, if you want to store these images as JPEGs, you can take your UIImage and do the following:
NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);
This creates an NSData instance containing the raw bytes for a JPEG image at a 60% quality setting. The contents of that NSData instance can then be written to disk or cached in memory.

The easiest and most straightforward way to resize your images would be this
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = 320.0/480.0;
if(imgRatio!=maxRatio){
if(imgRatio < maxRatio){
imgRatio = 480.0 / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = 480.0;
}
else{
imgRatio = 320.0 / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = 320.0;
}
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

The above methods work well for small images, but when you try to resize a very large image, you will quickly run out of memory and crash the app. A much better way is to use CGImageSourceCreateThumbnailAtIndexto resize the image without completely decoding it first.
If you have the path to the image you want to resize, you can use this:
- (void)resizeImageAtPath:(NSString *)imagePath {
// Create the image source (from path)
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
// To create image source from UIImage, use this
// NSData* pngData = UIImagePNGRepresentation(image);
// CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) #{
(id) kCGImageSourceCreateThumbnailWithTransform : #YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(id) kCGImageSourceThumbnailMaxPixelSize : #(640)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
CFRelease(src);
// Write the thumbnail at path
CGImageWriteToFile(thumbnail, imagePath);
}
More details here.

Best way to scale images without losing the aspect ratio (i.e. without stretching the imgage) is to use this method:
//to scale images without changing aspect ratio
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize {
float width = newSize.width;
float height = newSize.height;
UIGraphicsBeginImageContext(newSize);
CGRect rect = CGRectMake(0, 0, width, height);
float widthRatio = image.size.width / width;
float heightRatio = image.size.height / height;
float divisor = widthRatio > heightRatio ? widthRatio : heightRatio;
width = image.size.width / divisor;
height = image.size.height / divisor;
rect.size.width = width;
rect.size.height = height;
//indent in case of width or height difference
float offset = (width - height) / 2;
if (offset > 0) {
rect.origin.y = offset;
}
else {
rect.origin.x = -offset;
}
[image drawInRect: rect];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return smallImage;
}
Add this method to your Utility class so you can use it throughout your project, and access it like so:
xyzImageView.image = [Utility scaleImage:yourUIImage toSize:xyzImageView.frame.size];
This method takes care of scaling while maintaining aspect ratio.
It also adds indents to the image in case the scaled down image has more width than height (or vice versa).

If you have control over the server, I would strongly recommend resizing the images server side with ImageMagik. Downloading large images and resizing them on the phone is a waste of many precious resources - bandwidth, battery and memory. All of which are scarce on phones.

I developed an ultimate solution for image scaling in Swift.
You can use it to resize image to fill, aspect fill or aspect fit specified size.
You can align image to center or any of four edges and four corners.
And also you can trim extra space which is added if aspect ratios of original image and target size are not equal.
enum UIImageAlignment {
case Center, Left, Top, Right, Bottom, TopLeft, BottomRight, BottomLeft, TopRight
}
enum UIImageScaleMode {
case Fill,
AspectFill,
AspectFit(UIImageAlignment)
}
extension UIImage {
func scaleImage(width width: CGFloat? = nil, height: CGFloat? = nil, scaleMode: UIImageScaleMode = .AspectFit(.Center), trim: Bool = false) -> UIImage {
let preWidthScale = width.map { $0 / size.width }
let preHeightScale = height.map { $0 / size.height }
var widthScale = preWidthScale ?? preHeightScale ?? 1
var heightScale = preHeightScale ?? widthScale
switch scaleMode {
case .AspectFit(_):
let scale = min(widthScale, heightScale)
widthScale = scale
heightScale = scale
case .AspectFill:
let scale = max(widthScale, heightScale)
widthScale = scale
heightScale = scale
default:
break
}
let newWidth = size.width * widthScale
let newHeight = size.height * heightScale
let canvasWidth = trim ? newWidth : (width ?? newWidth)
let canvasHeight = trim ? newHeight : (height ?? newHeight)
UIGraphicsBeginImageContextWithOptions(CGSizeMake(canvasWidth, canvasHeight), false, 0)
var originX: CGFloat = 0
var originY: CGFloat = 0
switch scaleMode {
case .AspectFit(let alignment):
switch alignment {
case .Center:
originX = (canvasWidth - newWidth) / 2
originY = (canvasHeight - newHeight) / 2
case .Top:
originX = (canvasWidth - newWidth) / 2
case .Left:
originY = (canvasHeight - newHeight) / 2
case .Bottom:
originX = (canvasWidth - newWidth) / 2
originY = canvasHeight - newHeight
case .Right:
originX = canvasWidth - newWidth
originY = (canvasHeight - newHeight) / 2
case .TopLeft:
break
case .TopRight:
originX = canvasWidth - newWidth
case .BottomLeft:
originY = canvasHeight - newHeight
case .BottomRight:
originX = canvasWidth - newWidth
originY = canvasHeight - newHeight
}
default:
break
}
self.drawInRect(CGRectMake(originX, originY, newWidth, newHeight))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
There are examples of applying this solution below.
Gray rectangle is target site image will be resized to.
Blue circles in light blue rectangle is the image (I used circles because it's easy to see when it's scaled without preserving aspect).
Light orange color marks areas that will be trimmed if you pass trim: true.
Aspect fit before and after scaling:
Another example of aspect fit:
Aspect fit with top alignment:
Aspect fill:
Fill:
I used upscaling in my examples because it's simpler to demonstrate but solution also works for downscaling as in question.
For JPEG compression you should use this :
let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality
if let data = UIImageJPEGRepresentation(image, compressionQuality) {
// ...
}
You can check out my gist with Xcode playground.

For Swift 3, the below code scales the image keeping the aspect ratio. You can read more about the ImageContext in Apple's documentation:
extension UIImage {
class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {
let scale = newHeight / image.size.height
let newWidth = image.size.width * scale
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
To use it, call resizeImage() method:
UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)

Swift Version
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage? {
let scale = newWidth / image.size.width
let newHeight = CGFloat(200.0)
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}

you can use this code to scale image in required size.
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize
{
CGSize actSize = image.size;
float scale = actSize.width/actSize.height;
if (scale < 1) {
newSize.height = newSize.width/scale;
}
else {
newSize.width = newSize.height*scale;
}
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Adding to the slew of answers here, but I have gone for a solution which resizes by file size, rather than dimensions.
This will both reduce the dimensions and quality of the image until it reaches your given size.
func compressTo(toSizeInMB size: Double) -> UIImage? {
let bytes = size * 1024 * 1024
let sizeInBytes = Int(bytes)
var needCompress:Bool = true
var imgData:Data?
var compressingValue:CGFloat = 1.0
while (needCompress) {
if let resizedImage = scaleImage(byMultiplicationFactorOf: compressingValue), let data: Data = UIImageJPEGRepresentation(resizedImage, compressingValue) {
if data.count < sizeInBytes || compressingValue < 0.1 {
needCompress = false
imgData = data
} else {
compressingValue -= 0.1
}
}
}
if let data = imgData {
print("Finished with compression value of: \(compressingValue)")
return UIImage(data: data)
}
return nil
}
private func scaleImage(byMultiplicationFactorOf factor: CGFloat) -> UIImage? {
let size = CGSize(width: self.size.width*factor, height: self.size.height*factor)
UIGraphicsBeginImageContext(size)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
if let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return newImage;
}
return nil
}
Credit for scaling by size answer

According to this session, iOS Memory Deep Dive, we had better use ImageIO to downscale images.
The bad of using UIImage downscale images.
Will decompress original image into memory
Internal coordinate space transforms are expensive
Use ImageIO
ImageIO can read image sizes and metadata information without dirtying memory.
ImageIO can resize images at cost of resized image only.
About Image in memory
Memory use is related to the dimensions of the images, not the file size.
UIGraphicsBeginImageContextWithOptions always uses SRGB rendering-format, which use 4 bytes per pixel.
A image have load -> decode -> render 3 phases.
UIImage is expensive for sizing and to resizing
For the following image, if you use UIGraphicsBeginImageContextWithOptions
we only need 590KB to load a image, while we need
2048 pixels x 1536 pixels x 4 bytes per pixel = 10MB when decoding
while UIGraphicsImageRenderer, introduced in iOS 10, will automatically pick the best graphic format in iOS12. It means, you may save 75% of memory by replacing UIGraphicsBeginImageContextWithOptions with UIGraphicsImageRenderer if you don't need SRGB.
This is my article about iOS images in memory
func resize(url: NSURL?, maxPixelSize: Int) -> CGImage? {
guard let url = url else {
return nil;
}
let imgSource = CGImageSourceCreateWithURL(url, nil)
guard let imageSource = imgSource else {
return nil
}
var scaledImage: CGImage?
let options: [NSString: Any] = [
// The maximum width and height in pixels of a thumbnail.
kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
kCGImageSourceCreateThumbnailFromImageAlways: true,
// Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
kCGImageSourceCreateThumbnailWithTransform: true
]
scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)
return scaledImage
}
DispatchQueue.global().async {
let image: CGImage? = resize(url: NSURL.init(string: "https://i.stack.imgur.com/rPcHQ.jpg"), maxPixelSize: 600)
DispatchQueue.main.async {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0 ,width: 30, height: 30))
if let cgimage = image {
imageView.image = UIImage(cgImage: cgimage);
}
}
}
or
// Downsampling large images for display at smaller size
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions =
[kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
// Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary
let downsampledImage =
CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)!
return UIImage(cgImage: downsampledImage)
}

A problem that might occur on retina displays is that the scale of the image is set by ImageCapture or so. The resize functions above will not change that. In these cases the resize will work not properly.
In the code below, the scale is set to 1 (not scaled) and the returned image has the size that you would expect. This is done in the UIGraphicsBeginImageContextWithOptions call.
-(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize {
UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0);
[theImage drawInRect:CGRectMake(0, 0, theNewSize.width, theNewSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

I ended up using Brads technique to create a scaleToFitWidth method in UIImage+Extensions if that's useful to anyone...
-(UIImage *)scaleToFitWidth:(CGFloat)width
{
CGFloat ratio = width / self.size.width;
CGFloat height = self.size.height * ratio;
NSLog(#"W:%f H:%f",width,height);
UIGraphicsBeginImageContext(CGSizeMake(width, height));
[self drawInRect:CGRectMake(0.0f,0.0f,width,height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
then wherever you like
#import "UIImage+Extensions.h"
UIImage *newImage = [image scaleToFitWidth:100.0f];
Also worth noting you could move this further down into a UIView+Extensions class if you want to render images from a UIView

I just wanted to answer that question for Cocoa Swift programmers. This function returns NSImage with new size. You can use that function like this.
let sizeChangedImage = changeImageSize(image, ratio: 2)
// changes image size
func changeImageSize (image: NSImage, ratio: CGFloat) -> NSImage {
// getting the current image size
let w = image.size.width
let h = image.size.height
// calculating new size
let w_new = w / ratio
let h_new = h / ratio
// creating size constant
let newSize = CGSizeMake(w_new ,h_new)
//creating rect
let rect = NSMakeRect(0, 0, w_new, h_new)
// creating a image context with new size
let newImage = NSImage.init(size:newSize)
newImage.lockFocus()
// drawing image with new size in context
image.drawInRect(rect)
newImage.unlockFocus()
return newImage
}

If you image is in document directory, Add this URL extension:
extension URL {
func compressedImageURL(quality: CGFloat = 0.3) throws -> URL? {
let imageData = try Data(contentsOf: self)
debugPrint("Image file size before compression: \(imageData.count) bytes")
let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".jpg")
guard let actualImage = UIImage(data: imageData) else { return nil }
guard let compressedImageData = UIImageJPEGRepresentation(actualImage, quality) else {
return nil
}
debugPrint("Image file size after compression: \(compressedImageData.count) bytes")
do {
try compressedImageData.write(to: compressedURL)
return compressedURL
} catch {
return nil
}
}
}
Usage:
guard let localImageURL = URL(string: "< LocalImagePath.jpg >") else {
return
}
//Here you will get URL of compressed image
guard let compressedImageURL = try localImageURL.compressedImageURL() else {
return
}
debugPrint("compressedImageURL: \(compressedImageURL.absoluteString)")
Note:- Change < LocalImagePath.jpg > with your local jpg image path.

If anyone still looking for better option
-(UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {
UIImage *sourceImage = image;
UIImage *newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor < heightFactor)
scaleFactor = widthFactor;
else
scaleFactor = heightFactor;
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor < heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
} else if (widthFactor > heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
// this is actually the interesting part:
UIGraphicsBeginImageContext(targetSize);
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if(newImage == nil) NSLog(#"could not scale image");
return newImage ;
}

- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = image.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(context, flipVertical);
CGContextDrawImage(context, newRect, imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}

To resize an image I have better (graphical) results by using this function in stead of DrawInRect:
- (UIImage*) reduceImageSize:(UIImage*) pImage newwidth:(float) pWidth
{
float lScale = pWidth / pImage.size.width;
CGImageRef cgImage = pImage.CGImage;
UIImage *lResult = [UIImage imageWithCGImage:cgImage scale:lScale
orientation:UIImageOrientationRight];
return lResult;
}
Aspect ratio is taken care for automatically

Related

Resize Image without losing quality

I'm using the functions below to resize my images width & height but I noticed that it ruins the image quality.
class func imageWithSize(image: UIImage,size: CGSize)->UIImage{
if UIScreen.mainScreen().respondsToSelector("scale"){
UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.mainScreen().scale);
}
else
{
UIGraphicsBeginImageContext(size);
}
image.drawInRect(CGRectMake(0, 0, size.width, size.height));
var newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
class func resizeImageWithAspect(image: UIImage,scaledToMaxWidth width:CGFloat,maxHeight height :CGFloat)->UIImage
{
let scaleFactor = width / height;
let newSize = CGSizeMake(width, height);
return imageWithSize(image, size: newSize);
}
Is there another way to resize images without ruining the quality? Or how can I fix my functions below so it doesn't ruin the image quality after resizing?
Here is my code in swift. For squared images, it works really well and without any loss of quality!
extension UIImage {
func resize(targetSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size:targetSize).image { _ in
self.draw(in: CGRect(origin: .zero, size: targetSize))
}
}
}
Here is my Objective-C code. It's work for me.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize isAspectRation:(BOOL)aspect {
if (!image) {
return nil;
}
//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.
CGFloat originRatio = image.size.width / image.size.height;
CGFloat newRatio = newSize.width / newSize.height;
CGSize sz;
if (!aspect) {
sz = newSize;
}else {
if (originRatio < newRatio) {
sz.height = newSize.height;
sz.width = newSize.height * originRatio;
}else {
sz.width = newSize.width;
sz.height = newSize.width / originRatio;
}
}
CGFloat scale = 1.0;
// if([[UIScreen mainScreen]respondsToSelector:#selector(scale)]) {
// CGFloat tmp = [[UIScreen mainScreen]scale];
// if (tmp > 1.5) {
// scale = 2.0;
// }
// }
sz.width /= scale;
sz.height /= scale;
UIGraphicsBeginImageContextWithOptions(sz, NO, scale);
[image drawInRect:CGRectMake(0, 0, sz.width, sz.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I saw your code in Swift. I just convert my Objective-C to Swift without test. Can you try it and let's me know. Thanks!
struct CommonUtils {
static func imageWithImage(image: UIImage, scaleToSize newSize: CGSize, isAspectRation aspect: Bool) -> UIImage{
let originRatio = image.size.width / image.size.height;//CGFloat
let newRatio = newSize.width / newSize.height;
var sz: CGSize = CGSizeZero
if (!aspect) {
sz = newSize
}else {
if (originRatio < newRatio) {
sz.height = newSize.height
sz.width = newSize.height * originRatio
}else {
sz.width = newSize.width
sz.height = newSize.width / originRatio
}
}
let scale: CGFloat = 1.0
sz.width /= scale
sz.height /= scale
UIGraphicsBeginImageContextWithOptions(sz, false, scale)
image.drawInRect(CGRectMake(0, 0, sz.width, sz.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
I just created this useful UIImage extension with swift 5. You can use it to resize your UIImages without losing quality.
I added a couple of methods in order to be able to resize an image to a width or a height keeping the aspect ratio.
extension UIImage {
func resize(targetSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size:targetSize).image { _ in
self.draw(in: CGRect(origin: .zero, size: targetSize))
}
}
func resize(scaledToWidth desiredWidth: CGFloat) -> UIImage {
let oldWidth = size.width
let scaleFactor = desiredWidth / oldWidth
let newHeight = size.height * scaleFactor
let newWidth = oldWidth * scaleFactor
let newSize = CGSize(width: newWidth, height: newHeight)
return resize(targetSize: newSize)
}
func resize(scaledToHeight desiredHeight: CGFloat) -> UIImage {
let scaleFactor = desiredHeight / size.height
let newWidth = size.width * scaleFactor
let newSize = CGSize(width: newWidth, height: desiredHeight)
return resize(targetSize: newSize)
}
}
func image(image:UIImage,imageSize:CGSize)->UIImage
{
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0)
[image .drawInRect(CGRectMake(0, 3, imageSize.width, imageSize.height))]
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
return newImage;
}

Resizing UIImage Causes UIImage's Data Size Increase

I am trying to resize a UIImage with NYXImageKit and it causes the Image's Data size to increase
P.S.: originalImage's dimension is 1024 * 1024
var originalImage = image;
//originalImage SIZE 108 KB
var resizedImage = image.scaleToFillSize(CGSize(width: 256, height: 256));
//resizedImage BECAME 620 KB
Any ideas?
The code below is from NYXImageKit class
UIImage+Resizing.m
-(UIImage*)scaleToFillSize:(CGSize)newSize
{
size_t destWidth = (size_t)(newSize.width * self.scale);
size_t destHeight = (size_t)(newSize.height * self.scale);
if (self.imageOrientation == UIImageOrientationLeft
|| self.imageOrientation == UIImageOrientationLeftMirrored
|| self.imageOrientation == UIImageOrientationRight
|| self.imageOrientation == UIImageOrientationRightMirrored)
{
size_t temp = destWidth;
destWidth = destHeight;
destHeight = temp;
}
/// Create an ARGB bitmap context
CGContextRef bmContext = NYXCreateARGBBitmapContext(destWidth, destHeight, destWidth * kNyxNumberOfComponentsPerARBGPixel, NYXImageHasAlpha(self.CGImage));
if (!bmContext)
return nil;
/// Image quality
CGContextSetShouldAntialias(bmContext, true);
CGContextSetAllowsAntialiasing(bmContext, true);
CGContextSetInterpolationQuality(bmContext, kCGInterpolationHigh);
/// Draw the image in the bitmap context
UIGraphicsPushContext(bmContext);
CGContextDrawImage(bmContext, CGRectMake(0.0f, 0.0f, destWidth, destHeight), self.CGImage);
UIGraphicsPopContext();
/// Create an image object from the context
CGImageRef scaledImageRef = CGBitmapContextCreateImage(bmContext);
UIImage* scaled = [UIImage imageWithCGImage:scaledImageRef scale:self.scale orientation:self.imageOrientation];
/// Cleanup
CGImageRelease(scaledImageRef);
CGContextRelease(bmContext);
return scaled;
}
Swift Image Resizer
import UIKit
func RBSquareImageTo(image: UIImage, size: CGSize) -> UIImage {
return RBResizeImage(RBSquareImage(image), size)
}
func RBSquareImage(image: UIImage) -> UIImage {
var originalWidth = image.size.width
var originalHeight = image.size.height
var edge: CGFloat
if originalWidth > originalHeight {
edge = originalHeight
} else {
edge = originalWidth
}
var posX = (originalWidth - edge) / 2.0
var posY = (originalHeight - edge) / 2.0
var cropSquare = CGRectMake(posX, posY, edge, edge)
var imageRef = CGImageCreateWithImageInRect(image.CGImage, cropSquare);
return UIImage(CGImage: imageRef, scale: UIScreen.mainScreen().scale, orientation: image.imageOrientation)
}
func RBResizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio)
} else {
newSize = CGSizeMake(size.width * widthRatio, size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
I hope it helps you,
public func resizeImage(width width: CGFloat, height: CGFloat) -> UIImage {
let newSize: CGRect = CGRectMake(0, 0, width, height)
UIGraphicsBeginImageContext(newSize.size)
self.drawInRect(newSize)
let resizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage
}

Getting a resized image from an UIImageView

I need to upload image data to my Parse.com backend, but I found out that my pictures are way too big, dimensions wise. NSHipster had an article on image resizing with this code:
import ImageIO
if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) {
let options: CFDictionary = [
kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
kCGImageSourceCreateThumbnailFromImageIfAbsent: true
]
let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) }
}
Unfortunately, this code does not compile in Swift 1.2. In particular there are 2 problems. The CFDictionary could not be initialized, and flatMap method was not recognized.
Here is some functions for resize your image:
import UIKit
func RBSquareImageTo(image: UIImage, size: CGSize) -> UIImage {
return RBResizeImage(RBSquareImage(image), size)
}
func RBSquareImage(image: UIImage) -> UIImage {
var originalWidth = image.size.width
var originalHeight = image.size.height
var edge: CGFloat
if originalWidth > originalHeight {
edge = originalHeight
} else {
edge = originalWidth
}
var posX = (originalWidth - edge) / 2.0
var posY = (originalHeight - edge) / 2.0
var cropSquare = CGRectMake(posX, posY, edge, edge)
var imageRef = CGImageCreateWithImageInRect(image.CGImage, cropSquare);
return UIImage(CGImage: imageRef, scale: UIScreen.mainScreen().scale, orientation: image.imageOrientation)
}
func RBResizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio)
} else {
newSize = CGSizeMake(size.width * widthRatio, size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
From HERE.
Here is the Objective C code that scale the image given by parameters.
-(UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Resize UIImage without squashing

I would like to resize a UIImage to be able to upload it to parse.com. I would like to resize it without squashing it. How can I make sure that it is small enough to upload to parse.com (10485760 bytes) but not squash it to a set size.
This is the code I tried below but obviously sets the image size exactly.
Any ideas?
var newSize:CGSize = CGSize(width: 600,height: 600)
let rect = CGRectMake(0,0, newSize.width, newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
// image is a variable of type UIImage
profileImage?.drawInRect(rect)
profileImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Try using this method to scale your image while maintaining the aspect ratio:
func scaleImage(image: UIImage, maxDimension: CGFloat) -> UIImage {
var scaledSize = CGSize(width: maxDimension, height: maxDimension)
var scaleFactor: CGFloat
if image.size.width > image.size.height {
scaleFactor = image.size.height / image.size.width
scaledSize.width = maxDimension
scaledSize.height = scaledSize.width * scaleFactor
} else {
scaleFactor = image.size.width / image.size.height
scaledSize.height = maxDimension
scaledSize.width = scaledSize.height * scaleFactor
}
UIGraphicsBeginImageContext(scaledSize)
image.drawInRect(CGRectMake(0, 0, scaledSize.width, scaledSize.height))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
In this case, you would call it like so:
profileImage = scaleImage(profileImage, newSize: CGSizeMake(600, 600))

Crop the bottom of a UIImage after resizing

I have this image (original size)
And I want to resize the image preserving the aspect ratio to 600 x 600. After that, I would like to crop just the bottom to this frame: {0, 400, 600, 200} in order to get the bottom part of the image.
Resizing with preserving the aspect ratio works well, but I'm unable to crop the bottom of the image. I'm using this code to resize with aspect ratio:
CGFloat horizontalRatio = bounds.width / self.size.width;
CGFloat verticalRatio = bounds.height / self.size.height;
CGFloat ratio;
switch (contentMode) {
case UIViewContentModeScaleAspectFill:
ratio = MAX(horizontalRatio, verticalRatio);
break;
case UIViewContentModeScaleAspectFit:
ratio = MIN(horizontalRatio, verticalRatio);
break;
default:
[NSException raise:NSInvalidArgumentException format:#"Unsupported content mode: %d", contentMode];
}
CGSize newSize = CGSizeMake(self.size.width * ratio, self.size.height * ratio);
UIImage *resizedImg = [self resizedImage:newSize interpolationQuality:quality];
if (UIViewContentModeScaleAspectFill == contentMode && (newSize.height > bounds.height || newSize.width > bounds.width)) {
CGRect newRect = CGRectMake((newSize.width - bounds.width) / 2, (newSize.height - bounds.height) / 2, bounds.width, bounds.height);
CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return croppedImage;
}
return resizedImg;
And this code to crop the bottom
CGRect rect = CGRectMake(0, 400, 600, 200);
CGImageRef imageRef = CGImageCreateWithImageInRect([newImage CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
And this is the result I'm getting (I should get the same image, I want to make that crop to blur the bottom crop area to display some text):
This helped me to solve the issue. I was initializing the frame of the image with wrong values.
its very simple
- (UIImage *)cropImage:(UIImage *)oldImage {
CGSize imageSize = oldImage.size;
UIGraphicsBeginImageContextWithOptions( CGSizeMake( imageSize.width,
imageSize.height - 200),
NO,
0.);
[oldImage drawAtPoint:CGPointMake( 0, -100)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
https://developer.apple.com/documentation/coregraphics/cgimage/1454683-cropping
This is what saved me!!
func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
{
let imageViewScale = max(inputImage.size.width / viewWidth,
inputImage.size.height / viewHeight)
// Scale cropRect to handle images larger than shown-on-screen size
let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,
y:cropRect.origin.y * imageViewScale,
width:cropRect.size.width * imageViewScale,
height:cropRect.size.height * imageViewScale)
// Perform cropping in Core Graphics
guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
else {
return nil
}
// Return image to UIImage
let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
return croppedImage
}

Resources