diff --git a/SDWebImage/MKAnnotationView+WebCache.m b/SDWebImage/MKAnnotationView+WebCache.m index a0d5b0d..3e6d2e7 100644 --- a/SDWebImage/MKAnnotationView+WebCache.m +++ b/SDWebImage/MKAnnotationView+WebCache.m @@ -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; diff --git a/SDWebImage/SDWebImageDownloader.h b/SDWebImage/SDWebImageDownloader.h index ffc6fa9..64a006a 100644 --- a/SDWebImage/SDWebImageDownloader.h +++ b/SDWebImage/SDWebImageDownloader.h @@ -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)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority; + (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate userInfo:(id)userInfo; diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index 650ee43..1bf06d2 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -7,8 +7,9 @@ */ #import "SDWebImageDownloader.h" - #import "SDWebImageDecoder.h" +#import + @interface SDWebImageDownloader (ImageDecoder) @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" diff --git a/SDWebImage/SDWebImageDownloaderDelegate.h b/SDWebImage/SDWebImageDownloaderDelegate.h index c33f87b..1895090 100644 --- a/SDWebImage/SDWebImageDownloaderDelegate.h +++ b/SDWebImage/SDWebImageDownloaderDelegate.h @@ -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; diff --git a/SDWebImage/SDWebImageManager.h b/SDWebImage/SDWebImageManager.h index 6c4d8a7..975cdf9 100644 --- a/SDWebImage/SDWebImageManager.h +++ b/SDWebImage/SDWebImageManager.h @@ -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 diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 9d37771..6d957bf 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -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 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); diff --git a/SDWebImage/SDWebImageManagerDelegate.h b/SDWebImage/SDWebImageManagerDelegate.h index 8592076..1d420be 100644 --- a/SDWebImage/SDWebImageManagerDelegate.h +++ b/SDWebImage/SDWebImageManagerDelegate.h @@ -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; diff --git a/SDWebImage/UIButton+WebCache.m b/SDWebImage/UIButton+WebCache.m index 66ad877..1b0cad2 100644 --- a/SDWebImage/UIButton+WebCache.m +++ b/SDWebImage/UIButton+WebCache.m @@ -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]; diff --git a/SDWebImage/UIImageView+WebCache.m b/SDWebImage/UIImageView+WebCache.m index bd0a37a..d62fae0 100644 --- a/SDWebImage/UIImageView+WebCache.m +++ b/SDWebImage/UIImageView+WebCache.m @@ -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;