Non-lazy image loading in iOS

后端 未结 3 598
抹茶落季
抹茶落季 2020-11-29 15:46

I\'m trying to load UIImages in a background thread and then display them on the iPad. However, there\'s a stutter when I set the imageViews\' view property to the image. I

相关标签:
3条回答
  • 2020-11-29 16:12

    UIKit may be used on the main thread only. Your code is therefore technically invalid, since you use UIImage from a thread other than the main thread. You should use CoreGraphics alone to load (and non-lazily decode) graphics on a background thread, post the CGImageRef to the main thread and turn it into a UIImage there. It may appear to work (albeit with the stutter you don't want) in your current implementation, but it isn't guaranteed to. There seems to be a lot of superstition and bad practice advocated around this area, so it's not surprising you've managed to find some bad advice...

    Recommended to run on a background thread:

    // get a data provider referencing the relevant file
    CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(filename);
    
    // use the data provider to get a CGImage; release the data provider
    CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, 
                                                        kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProvider);
    
    // make a bitmap context of a suitable size to draw to, forcing decode
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    unsigned char *imageBuffer = (unsigned char *)malloc(width*height*4);
    
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
    
    CGContextRef imageContext =
        CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
                      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
    
    CGColorSpaceRelease(colourSpace);
    
    // draw the image to the context, release it
    CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
    CGImageRelease(image);
    
    // now get an image ref from the context
    CGImageRef outputImage = CGBitmapContextCreateImage(imageContext);
    
    // post that off to the main thread, where you might do something like
    // [UIImage imageWithCGImage:outputImage]
    [self performSelectorOnMainThread:@selector(haveThisImage:) 
             withObject:[NSValue valueWithPointer:outputImage] waitUntilDone:YES];
    
    // clean up
    CGImageRelease(outputImage);
    CGContextRelease(imageContext);
    free(imageBuffer);
    

    There's no need to do the malloc/free if you're on iOS 4 or later, you can just pass NULL as the relevant parameter of CGBitmapContextCreate, and let CoreGraphics sort out its own storage.

    This differs from the solution you post to because it:

    1. creates a CGImage from a PNG data source — lazy loading applies, so this isn't necessarily a fully loaded and decompressed image
    2. creates a bitmap context of the same size as the PNG
    3. draws the CGImage from the PNG data source onto the bitmap context — this should force full loading and decompression since the actual colour values have to be put somewhere we could access them from a C array. This step is as far as the forceLoad you link to goes.
    4. converts the bitmap context into an image
    5. posts that image off to the main thread, presumably to become a UIImage

    So there's no continuity of object between the thing loaded and the thing displayed; pixel data goes through a C array (so, no opportunity for hidden shenanigans) and only if it was put into the array correctly is it possible to make the final image.

    0 讨论(0)
  • 2020-11-29 16:16

    How about UIImage+ImmediateLoad.m?

    It decodes images immediately. Besides UIImage -initWithContentsOfFile:, -imageWithCGImage: and CG* are thread-safe after iOS4.

    0 讨论(0)
  • 2020-11-29 16:23

    Ok, figured it out - with a lot of help by Tommy. Thank you!

    If you create your context with

            CGContextRef imageContext =
            CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
                                  kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
    

    the main run loop won't cause any conversions on the main thread any more. Displaying is now buttery smooth. (The flags are a bit counterintuitive, does anyone know why you have to choose kCGImageAlphaPremultipliedFirst?)

    Edit:

    Uploaded the fixed sample project: http://www.jasamer.com/files/SwapTest-Fixed.zip. If you have problems with image performance, this is a great starting point!

    0 讨论(0)
提交回复
热议问题