Domanda

Sometimes images are very large (downloaded from internet), so I need to resize them but don't want to cause memory issue which means I don't want to load all the image data to the memory and resize it within the memory.

I am investigating serval ways to resize image, monitoring their memory usages. Specifically, CGContextDrawImage, CGImageSourceCreateThumbnailAtIndex and CGImageCreate with CGDataProviderRef.

Now I met some problems when using CGImageCreate with CGDataProviderRef, and my code is like this:

//This method read the data from filePath, resize the image and write the resized data to the destPath
- (void)resizeImageFileUsingCGImageCreate:(NSString*)filePath toDestPath:(NSString*)destPath {

    CGFloat factor = 0.2;
    CGSize originalSize = [self sizeOfImageAtURL:[[NSURL alloc] initFileURLWithPath:filePath]];
    CGSize destSize = CGSizeMake((NSUInteger)(originalSize.width * factor), (NSUInteger)(originalSize.height * factor));



    UIImage *srcImage = [UIImage imageWithContentsOfFile:filePath];
    CGImageRef srcImgRef = srcImage.CGImage;
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    CFDataRef cfData = (__bridge_retained CFDataRef)data;
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(cfData);
    CFRelease(cfData);

    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(srcImgRef);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGImageRef imageRef = CGImageCreate(destSize.width, destSize.height, 8, 32, 4 * destSize.width, colorSpace, bitmapInfo, dataProvider, NULL, NO, 0);

    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    UIImage *image = nil;
    if (imageRef != NULL) {
        image = [[UIImage alloc] initWithCGImage:imageRef];
        CGImageRelease(imageRef);
        //Why UIImageJPEGRepresentation(image, 0.5) returns nil?
        [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];


    }


}

In the last line of the code , UIImageJPEGRepresentation(image, 0.5) always returns nil, which made me weird. Could you help me to point out the correct way to resize an image using CGImageCreate with CGDataProviderRef? Thanks very much!

Nessuna soluzione corretta

Altri suggerimenti

Instead of answering your question directly, I'll show you what I think is a better way: use the ImageIO framework.

#import <ImageIO/ImageIO.h>

Here's how to load a reduced size version of an image from disk (imageSource is an NSURL; scale, maxw, and maxh must be set beforehand to the scale and maximum dimensions you want):

CGImageSourceRef src = 
    CGImageSourceCreateWithURL((__bridge CFURLRef)imageSource, nil);
NSDictionary* d = @{
    (id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
    (id)kCGImageSourceThumbnailMaxPixelSize: @((int)(maxw > maxh ? maxw : maxh))
};
CGImageRef imref = 
    CGImageSourceCreateThumbnailAtIndex(src, 0, (__bridge CFDictionaryRef)d);
if (NULL != src)
    CFRelease(src);
UIImage* im = 
    [UIImage imageWithCGImage:imref scale:scale orientation:UIImageOrientationUp];
if (NULL != imref)
    CFRelease(imref);

The great thing about this code is that we never hold the large version of the image in memory; it is never loaded. We end up with a smaller UIImage and we are ready to use it in our interface.

If what you have is NSData from the Internet and you have not saved it to disk, then to create your image source, use this:

src = CGImageSourceCreateWithData((__bridge CFDataRef)imageSource, nil);

But really, if the images are big, you should be using a download task so that the whole image is never in memory, but is saved to disk as it arrives.

instead of doing this:

    [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];

You should be doing this:

NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
if(imageData) 
{
    NSError *error = nil;
    BOOL success = [imageData writeToFile:destPath options:NSDataWritingAtomic error:&error];
    if(NO == success)
    {
        NSLog(@"couldn't write image data to file %@ because of error %@", destPath, error)
    }
}

In my version of your code, I'm putting the image data into a NSData object and then printing out an error if writing the image data fails.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top