开发者

Any suggestions on how to handle this crash in CGImageDestinationFinalize?

开发者 https://www.devze.com 2023-03-09 13:53 出处:网络
My application reads and resizes images that are loaded from the internet; and unfortunately I can\'t control the creation of these images.

My application reads and resizes images that are loaded from the internet; and unfortunately I can't control the creation of these images.

Recently I had a crash that I am not sure how best to be able to handle. In this case the image was a corrupt GIF file. It wasn't badly corrupted but it was reporting a resolution size (height x width) that wasn't accurate. The image was supposed to be a 400x600 image but was reporting something like 1111x999.

The code snippet that crashed is:

- (void) saveRawImageRefToDisk:(CGImageRef)image withUUID:(NSString *)uuid andType:(NSString *)type {
    if (!uuid || !type) {
        return;
    }

    NSDictionary *imageDestinationWriteOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                              [NSNumber numberWithInt:1], (id)kCGImagePropertyOrientation,
                                              (id)kCFBooleanFalse, (id)kCGImagePropertyHasAlpha,
                                              [NSNumber numberWithInt:1.0], kCGImageDestinationLossyCompressionQuality,
                                              nil];

    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL((CFURLRef)[NSURL fileURLWithPath:[self getRawImagePathForUUID:uuid]], (CFStringRef)type, 1, nil);
    if (imageDestination) {
        CGImageDestinationAddImage(imageDestination, image, (CFDictionaryRef)imageDestinationWriteOptions);
        CGImageDestinationFinalize(imageDestination); //<--- EXC_BAD_ACCESS in here
        CFRelease(imageDestination);
    }
}

The snippet of the crash log is:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x06980000
Crashed Thread:  8

Thread 8 name:  Dispatch queue: com.apple.root.default-priority
Thread 8 Crashed:
0   libsystem_c.dylib               0x348cf6a4 memcpy$VARIANT$CortexA9 + 212
1   CoreGraphics                    0x366de30c CGAccessSessionGetBytes + 112
2   ImageIO                         0x32941b04 GenerateFromRGBImageWu + 256
3   ImageIO                         0x329432aa _CGImagePluginWriteGIF + 3390
4   ImageIO                         0x32932b3a CGImageDestinationFinalize + 118

The image in question was a GIF so I had the type in my method set to @"com.compuserve.gif". As far as I can tell the CGImageDestinationRef imageDestination was a valid object.

Part of the logic of my app is to keep images in the same format as they are read. So in this case I was resizing and writing back out a GIF.

I tried forcing to write out the CGImageRef as a PNG and interestingly the application didn't crash. It wrote out the PNG (which wasn't valid -- just a blank black image), but it didn't crash. But I've no idea in normal course of execution to know that I should write a GIF as a PNG.

Anybody got any ideas as to how I can handle or trap this condition? I'd greatly appreaciate any suggestions as this is a worry for me re: application stability. Or is this just a straight up "report as a radar to Apple"?

Edits & supplementary code:

All of this processing occurs within a NSOperationQueue and not on the main thread

Image in question is available at http://dribbble.com/system/users/1409/screenshots/182540/htg-logo-tiny.gif?1306878218

Here is the function where I create the CGImageRef:

- (CGImageRef) createIpadSizedImageRefFromImageSource:(CGImageSourceRef)imageSource withSourceSizeOf(CGSize)imageSourceSize {
    NSDictionary *imageCreationOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                      (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                                      (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
                                      [NSNumber numberWithInt:1024], kCGImageSourceThumbnailMaxPixelSize,
                                      (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
                                      nil];

    CGImageRef image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (CFDictionaryRef)imageCreationOptions);

    if (!image) {
        return nil;
    } else {
        [(id) image autorelease];
        return image;
    }
}

and the CGImageSourceRef is created from a filepath like:

- (CGImageSourceRef) createImageSourceFromURL:(NSURL *)imageLocationOnDisk {
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageLocationOnDisk, NULL);

    if (!imageSource) {
        DebugLog(@"Issue creating image source for image at: %@", imageLocationOnDisk);
        return nil;
    } else {
        [(id) imageSource autorelease];
     开发者_如何学Python   return imageSource;
    }
}


I created a test project from your description which loads and saves the image from the provided URL. It runs without problems in the simulator and on an iPhone 4 (iOS 4.3.2).

Could you try to run the following method in your project/environment:

- (void)checkImage
{
    NSURL *imageLocationOnDisk = [[NSBundle mainBundle] URLForResource:@"htg-logo-tiny"
                                                         withExtension:@"gif"];
    assert(imageLocationOnDisk);

    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageLocationOnDisk, NULL);
    assert(imageSource);

    id options = [NSDictionary dictionaryWithObjectsAndKeys:
                  (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                  (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
                  [NSNumber numberWithInt:1024], kCGImageSourceThumbnailMaxPixelSize,
                  (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
                  nil];

    CGImageRef image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (CFDictionaryRef)options);
    assert(image);

    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* file = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"foo.png"];

    options = [NSDictionary dictionaryWithObjectsAndKeys:
               [NSNumber numberWithInt:1], (id)kCGImagePropertyOrientation,
               (id)kCFBooleanFalse, (id)kCGImagePropertyHasAlpha,
               [NSNumber numberWithInt:1.0], kCGImageDestinationLossyCompressionQuality,
               nil];
    CGImageDestinationRef dest = CGImageDestinationCreateWithURL((CFURLRef)[NSURL fileURLWithPath:file],
                                                                 kUTTypePNG,
                                                                 1,
                                                                 NULL);
    assert(dest);
    CGImageDestinationAddImage(dest, image, (CFDictionaryRef)options);
    CGImageDestinationFinalize(dest);
}

To run the test you have to copy the image to your project's resources.

As I said above code works fine on my machine. I also tried it successfully with CGImageSourceCreateImageAtIndex.

The fact that the error happens in your app might be related to some other kind of error. Perhaps there's an issue with your threaded use of the ImageIO API.

One thing I noticed was the options argument you passed in the CGImageDestinationCreateWithURL call. According to the documentation it should be NULL.

Edit: The image in question has a size of 792 × 1111 (as reported by both PhotoShop and Preview.app) and is mostly black.


What you can do is storing the image in a UIImage object, drawing it in a new context (using the size property) and then making a CGImageRef object of it. This will fix the corruption. Though you need to do this for every image you receive and this will be a small performance penalty.

Note that you must send messages to UIImage from the main thread.


I'm gonna publish the soution of similar crash I found after hour of debugging, maybe it would be useful for somebody... The reason was in so stupid thing. I had two placeholders in NSLog and only one real variable.

NSLog(@"lastItemDate = %@ unixtime = %@", lastItemDate);
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号