Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying interlaced (progressive) Image in UIImageView

I am trying to display a JPEG image as it downloads, using part of the data, similiar to many web browsers do, or the facebook app.

there is a low-quality version of the image(just part of the data) and then display the full image in full quality.

this is best shown in the VIDEO HERE

I followed this SO question:

How do I display a progressive JPEG in an UIImageView while it is being downloaded?

but all I got was a imageview that is being rendered as data keeps comes in, no low-quality version first, no true progressive download and render.

can anyone share a code snippet or point me to where I can find more info as to how this can be implemented in an iOS app ?

tried this link for example which shows JPEG info, it identifies the image as progressive

http://www.webpagetest.org/jpeginfo/jpeginfo.php?url=http://cetus.sakura.ne.jp/softlab/software/spibench/pic_22p.jpg

and I used the correct code sequence

-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    /// Append the data
    [_dataTemp appendData:data];

    /// Get the total bytes downloaded
    const NSUInteger totalSize = [_dataTemp length];
    /// Update the data source, we must pass ALL the data, not just the new bytes
    CGImageSourceUpdateData(_imageSource, (CFDataRef)_dataTemp, (totalSize == _expectedSize) ? true : false);

    /// We know the expected size of the image
    if (_fullHeight > 0 && _fullWidth > 0)
    {
            [_imageView setImage:[UIImage imageWithCGImage:image]];
            CGImageRelease(image);
    }
}

but the code only shows the image when it is finished loading, with other images, it will show it as it downloading, but only top to bottom, no low quality version and then progressively add detail as browsers do.

DEMO PROJECT HERE

like image 884
Moshe Marciano Avatar asked Dec 15 '13 15:12

Moshe Marciano


1 Answers

I have implemented a progressive loading solution for an app I am currently working on. It does not use progressive Jpeg as I needed more flexibility loading different-res versions, but I get the same result (and it works really well, definitely worth implementing).

It's a camera app working in tandem with a server. So the images originate with the iPhone's camera and are stored remotely. When the server gets the image, it gets processed (using imageMagick, but could be any suitable library) and stored in 3 sizes - small thumb (~160 x 120), large thumb (~400x300) and full-size (~ double retina screensize). Target devices are retina iPhones.

I have an ImageStore class which is responsible for loading images asynchronously from wherever they happen to be, trying the fastest location first (live cache, local filesystem cache, asset library, network server).

  typedef void (^RetrieveImage)(UIImage *image);

- (void)  fullsizeImageFromPath:(NSString*)path
                             completion:(RetrieveImage)completionBlock;
- (void)largeThumbImageFromPath:(NSString*)path
                             completion:(RetrieveImage)completionBlock;
- (void)smallThumbImageFromPath:(NSString*)path
                             completion:(RetrieveImage)completionBlock;

Each of these methods will also attempt to load lower-res versions. The completion block actually loads the image into it's imageView.

Thus
fullsizeImageFromPath
will get the fullsized version, and also call largeThumbImageFromPath
largeThumbImageFromPath
will get the large thumb and also call smallThumbImageFromPath
smallThumbImageFromPath
will just get the small thumb

These methods invoke calls that are wrapped in cancellable NSOperations. If a larger-res version arrives before any of it's lower-res siblings, those respective lower-res calls are cancelled. The net result is that fullsizeImageFromPath may end up applying the small thumb, then the large thumb, and finally the full-res image to a single imageView depending on which arrives first. The result is really smooth.

Here is a gist showing the basic idea

This may not suit you as you may not be in control of the server side of the process. Before I had implemented this, I was pursuing the solution that David H describes. This would have been a lot more work, and less useful once I realised I also needed access to lower-res images in their own right.

Another approach which might be closer to your requirements is explained here

This has evolved into NYXProgressiveImageView, a subclass of UIImageView which is distributed as part of NYXImagesKit

Finally ... for a really hacky solution you could use a UIWebView to display progressive PNGs (progressive JPegs do not appear to be supported).

update

After recommending NYXProgressiveImageView, I realised that this is what you have been using. Unfortunately you did not mention this in your original post, so I feel I have been on a bit of a runaround. In fact, reading your post again, I feel you have been a little dishonest. From the text of your post, it looks as if the "DEMO" is a project that you created. In fact you didn't create it, you copied it from here:

http://cocoaintheshell.com/2011/05/progressive-images-download-imageio/ProgressiveImageDownload.zip

which accompanies this blog entry from cocoaintheshell The only changes you have made is one NSLog line, and to alter the JPG test URL. The code snippet that you posted isn't yours, it is copied from this project without attribution. If you had mentioned this in your post it would have saved me a whole heap of time.

Anyway, returning to the post... as you are using this code, you should probably be using the current version, which is on github:

https://github.com/Nyx0uf/NYXImagesKit

see also this blog entry

To keep your life simple, you only need these files from the project:

NYXProgressiveImageView.h
NYXProgressiveImageView.m
NYXImagesHelper.h
NYXImagesHelper.m

Next you need to be sure you are testing with GOOD images

For example, this PNG works well:

http://www.libpng.org/pub/png/img_png/pnglogo-grr.png

You also need to pay attention to this cryptic comment:

/// Note: Progressive JPEG are not supported see #32

There seems to be an issue with JPEG tempImage rendering which I haven't been able to work out - maybe you can. That is the reason why your "Demo" is not working correctly, anyway.

update 2
added gist

like image 200
foundry Avatar answered Oct 03 '22 01:10

foundry