Add support for optional progressive downloading using ImageIO (fix #114)

Thanks to @Xenofex for his idea and implementation example
This commit is contained in:
Olivier Poitrey 2012-05-09 16:29:13 +02:00
parent 16d661488a
commit ba71333e17
9 changed files with 134 additions and 5 deletions

View File

@ -67,6 +67,11 @@
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
{
self.image = image;
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
self.image = image;

View File

@ -22,6 +22,9 @@ extern NSString *const SDWebImageDownloadStopNotification;
NSMutableData *imageData;
id userInfo;
BOOL lowPriority;
NSUInteger expectedSize;
BOOL progressive;
size_t width, height;
}
@property (nonatomic, retain) NSURL *url;
@ -29,6 +32,7 @@ extern NSString *const SDWebImageDownloadStopNotification;
@property (nonatomic, retain) NSMutableData *imageData;
@property (nonatomic, retain) id userInfo;
@property (nonatomic, readwrite) BOOL lowPriority;
@property (nonatomic, readwrite) BOOL progressive;
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority;
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo;

View File

@ -7,8 +7,9 @@
*/
#import "SDWebImageDownloader.h"
#import "SDWebImageDecoder.h"
#import <ImageIO/ImageIO.h>
@interface SDWebImageDownloader (ImageDecoder) <SDWebImageDecoderDelegate>
@end
@ -20,7 +21,7 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
@end
@implementation SDWebImageDownloader
@synthesize url, delegate, connection, imageData, userInfo, lowPriority;
@synthesize url, delegate, connection, imageData, userInfo, lowPriority, progressive;
#pragma mark Public Methods
@ -89,7 +90,6 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
if (connection)
{
self.imageData = [NSMutableData data];
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:nil];
}
else
@ -115,7 +115,12 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response
{
if ([response respondsToSelector:@selector(statusCode)] && [((NSHTTPURLResponse *)response) statusCode] >= 400)
if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400)
{
expectedSize = response.expectedContentLength > 0 ? response.expectedContentLength : 0;
self.imageData = SDWIReturnAutoreleased([[NSMutableData alloc] initWithCapacity:expectedSize]);
}
else
{
[aConnection cancel];
@ -138,6 +143,78 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data
{
[imageData appendData:data];
if (CGImageSourceCreateImageAtIndex == NULL)
{
// ImageIO isn't present in iOS < 4
self.progressive = NO;
}
if (self.progressive && expectedSize > 0 && [delegate respondsToSelector:@selector(imageDownloader:didUpdatePartialImage:)])
{
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
/// Get the total bytes downloaded
const NSUInteger totalSize = [imageData length];
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL);
CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, totalSize == expectedSize);
if (width + height == 0)
{
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties)
{
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
CFRelease(properties);
}
}
if (width + height > 0 && totalSize < expectedSize)
{
/// Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image
if (partialImageRef)
{
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext)
{
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else
{
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
}
#endif
if (partialImageRef)
{
UIImage *image = [[UIImage alloc] initWithCGImage:partialImageRef];
[delegate imageDownloader:self didUpdatePartialImage:image];
SDWIRelease(image);
CGImageRelease(partialImageRef);
}
}
CFRelease(imageSource);
}
}
#pragma GCC diagnostic ignored "-Wundeclared-selector"

View File

@ -15,6 +15,7 @@
@optional
- (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader;
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image;
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image;
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error;

View File

@ -15,7 +15,8 @@ typedef enum
{
SDWebImageRetryFailed = 1 << 0,
SDWebImageLowPriority = 1 << 1,
SDWebImageCacheMemoryOnly = 1 << 2
SDWebImageCacheMemoryOnly = 1 << 2,
SDWebImageProgressiveDownload = 1 << 3
} SDWebImageOptions;
@interface SDWebImageManager : NSObject <SDWebImageDownloaderDelegate, SDImageCacheDelegate>

View File

@ -264,12 +264,39 @@ static SDWebImageManager *instance;
downloader.lowPriority = (options & SDWebImageLowPriority);
}
if ((options & SDWebImageProgressiveDownload) && !downloader.progressive)
{
// Turn progressive download support on demand
downloader.progressive = YES;
}
[downloadDelegates addObject:delegate];
[downloaders addObject:downloader];
}
#pragma mark SDWebImageDownloaderDelegate
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image
{
// Notify all the downloadDelegates with this downloader
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
{
NSUInteger uidx = (NSUInteger)idx;
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
if (aDownloader == downloader)
{
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
SDWIRetain(delegate);
SDWIAutorelease(delegate);
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url);
}
}
}
}
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image
{
SDWIRetain(downloader);

View File

@ -13,6 +13,7 @@
@optional
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url;
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image;
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url;
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error;

View File

@ -68,6 +68,14 @@
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
{
[self setImage:image forState:UIControlStateNormal];
[self setImage:image forState:UIControlStateSelected];
[self setImage:image forState:UIControlStateHighlighted];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
[self setImage:image forState:UIControlStateNormal];

View File

@ -67,6 +67,11 @@
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
{
self.image = image;
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
self.image = image;