Add support for optional progressive downloading using ImageIO (fix #114)
Thanks to @Xenofex for his idea and implementation example
This commit is contained in:
parent
16d661488a
commit
ba71333e17
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue