Merge pull request #2955 from dreampiggy/feature_context_option_cache_loader_coder

Feature add context option for cache, loader and coder, deprecated SDWebImageContextCustomManager
This commit is contained in:
DreamPiggy 2020-03-05 17:42:52 +08:00 committed by GitHub
commit c4a5f63463
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 23 deletions

View File

@ -38,6 +38,14 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
mutableCoderOptions[SDImageCoderWebImageContext] = context; mutableCoderOptions[SDImageCoderWebImageContext] = context;
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy]; SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
// Grab the image coder
id<SDImageCoder> imageCoder;
if ([context[SDWebImageContextImageCoder] conformsToProtocol:@protocol(SDImageCoder)]) {
imageCoder = context[SDWebImageContextImageCoder];
} else {
imageCoder = [SDImageCodersManager sharedManager];
}
if (!decodeFirstFrame) { if (!decodeFirstFrame) {
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
// check whether we should use `SDAnimatedImage` // check whether we should use `SDAnimatedImage`
@ -57,7 +65,7 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
} }
} }
if (!image) { if (!image) {
image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions]; image = [imageCoder decodedImageWithData:imageData options:coderOptions];
} }
if (image) { if (image) {
BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage); BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage);

View File

@ -52,6 +52,14 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
mutableCoderOptions[SDImageCoderWebImageContext] = context; mutableCoderOptions[SDImageCoderWebImageContext] = context;
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy]; SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
// Grab the image coder
id<SDImageCoder> imageCoder;
if ([context[SDWebImageContextImageCoder] conformsToProtocol:@protocol(SDImageCoder)]) {
imageCoder = context[SDWebImageContextImageCoder];
} else {
imageCoder = [SDImageCodersManager sharedManager];
}
if (!decodeFirstFrame) { if (!decodeFirstFrame) {
// check whether we should use `SDAnimatedImage` // check whether we should use `SDAnimatedImage`
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
@ -71,7 +79,7 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
} }
} }
if (!image) { if (!image) {
image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions]; image = [imageCoder decodedImageWithData:imageData options:coderOptions];
} }
if (image) { if (image) {
BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage); BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage);
@ -127,14 +135,21 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
mutableCoderOptions[SDImageCoderWebImageContext] = context; mutableCoderOptions[SDImageCoderWebImageContext] = context;
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy]; SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
// Grab the progressive image coder
id<SDProgressiveImageCoder> progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey); id<SDProgressiveImageCoder> progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey);
if (!progressiveCoder) { if (!progressiveCoder) {
// We need to create a new instance for progressive decoding to avoid conflicts id<SDProgressiveImageCoder> imageCoder = context[SDWebImageContextImageCoder];
for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) { // Check the progressive coder if provided
if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] && if ([imageCoder conformsToProtocol:@protocol(SDProgressiveImageCoder)]) {
[((id<SDProgressiveImageCoder>)coder) canIncrementalDecodeFromData:imageData]) { progressiveCoder = [[[imageCoder class] alloc] initIncrementalWithOptions:coderOptions];
progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions]; } else {
break; // We need to create a new instance for progressive decoding to avoid conflicts
for (id<SDImageCoder> coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) {
if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] &&
[((id<SDProgressiveImageCoder>)coder) canIncrementalDecodeFromData:imageData]) {
progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions];
break;
}
} }
} }
objc_setAssociatedObject(operation, SDImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_setAssociatedObject(operation, SDImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

View File

@ -213,8 +213,28 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextSetIma
/** /**
A SDWebImageManager instance to control the image download and cache process using in UIImageView+WebCache category and likes. If not provided, use the shared manager (SDWebImageManager *) A SDWebImageManager instance to control the image download and cache process using in UIImageView+WebCache category and likes. If not provided, use the shared manager (SDWebImageManager *)
@deprecated Deprecated. This context options can be replaced by other context option control like `.imageCache`, `.imageLoader`, `.imageTransofmer` (See below), which already matches all the properties in SDWebImageManager.
*/ */
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextCustomManager; FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextCustomManager __deprecated_msg("Use individual context option like .imageCache, .imageLoader and .imageTransformer instead");
/**
A id<SDImageCache> instance which conforms to `SDImageCache` protocol. It's used to override the image mananger's cache during the image loading pipeline.
In other word, if you just want to specify a custom cache during image loading, you don't need to re-create a dummy SDWebImageManager instance with the cache. If not provided, use the image manager's cache (id<SDImageCache>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageCache;
/**
A id<SDImageLoader> instance which conforms to `SDImageLoader` protocol. It's used to override the image mananger's loader during the image loading pipeline.
In other word, if you just want to specify a custom loader during image loading, you don't need to re-create a dummy SDWebImageManager instance with the loader. If not provided, use the image manager's cache (id<SDImageLoader>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageLoader;
/**
A id<SDImageCoder> instance which conforms to `SDImageCoder` protocol. It's used to override the default image codre for image decoding(including progressive) and encoding during the image loading process.
If you use this context option, we will not always use `SDImageCodersManager.shared` to loop through all registered coders and find the suitable one. Instead, we will arbitrarily use the exact provided coder without extra checking (We may not call `canDecodeFromData:`).
@note This is only useful for cases which you can ensure the loading url matches your coder, or you find it's too hard to write a common coder which can used for generic usage. This will bind the loading url with the coder logic, which is not always a good design, but possible. (id<SDImageCache>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageCoder;
/** /**
A id<SDImageTransformer> instance which conforms `SDImageTransformer` protocol. It's used for image transform after the image load finished and store the transformed image to cache. If you provide one, it will ignore the `transformer` in manager and use provided one instead. (id<SDImageTransformer>) A id<SDImageTransformer> instance which conforms `SDImageTransformer` protocol. It's used for image transform after the image load finished and store the transformed image to cache. If you provide one, it will ignore the `transformer` in manager and use provided one instead. (id<SDImageTransformer>)

View File

@ -120,6 +120,9 @@ inline UIImage * _Nullable SDScaledImageForScaleFactor(CGFloat scale, UIImage *
SDWebImageContextOption const SDWebImageContextSetImageOperationKey = @"setImageOperationKey"; SDWebImageContextOption const SDWebImageContextSetImageOperationKey = @"setImageOperationKey";
SDWebImageContextOption const SDWebImageContextCustomManager = @"customManager"; SDWebImageContextOption const SDWebImageContextCustomManager = @"customManager";
SDWebImageContextOption const SDWebImageContextImageCache = @"imageCache";
SDWebImageContextOption const SDWebImageContextImageLoader = @"imageLoader";
SDWebImageContextOption const SDWebImageContextImageCoder = @"imageCoder";
SDWebImageContextOption const SDWebImageContextImageTransformer = @"imageTransformer"; SDWebImageContextOption const SDWebImageContextImageTransformer = @"imageTransformer";
SDWebImageContextOption const SDWebImageContextImageScaleFactor = @"imageScaleFactor"; SDWebImageContextOption const SDWebImageContextImageScaleFactor = @"imageScaleFactor";
SDWebImageContextOption const SDWebImageContextImagePreserveAspectRatio = @"imagePreserveAspectRatio"; SDWebImageContextOption const SDWebImageContextImagePreserveAspectRatio = @"imagePreserveAspectRatio";

View File

@ -211,12 +211,19 @@ static id<SDImageLoader> _defaultImageLoader;
context:(nullable SDWebImageContext *)context context:(nullable SDWebImageContext *)context
progress:(nullable SDImageLoaderProgressBlock)progressBlock progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock { completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image cache to use
id<SDImageCache> imageCache;
if ([context[SDWebImageContextImageCache] conformsToProtocol:@protocol(SDImageCache)]) {
imageCache = context[SDWebImageContextImageCache];
} else {
imageCache = self.imageCache;
}
// Check whether we should query cache // Check whether we should query cache
BOOL shouldQueryCache = !SD_OPTIONS_CONTAINS(options, SDWebImageFromLoaderOnly); BOOL shouldQueryCache = !SD_OPTIONS_CONTAINS(options, SDWebImageFromLoaderOnly);
if (shouldQueryCache) { if (shouldQueryCache) {
NSString *key = [self cacheKeyForURL:url context:context]; NSString *key = [self cacheKeyForURL:url context:context];
@weakify(operation); @weakify(operation);
operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) { operation.cacheOperation = [imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
@strongify(operation); @strongify(operation);
if (!operation || operation.isCancelled) { if (!operation || operation.isCancelled) {
// Image combined operation cancelled by user // Image combined operation cancelled by user
@ -243,11 +250,18 @@ static id<SDImageLoader> _defaultImageLoader;
cacheType:(SDImageCacheType)cacheType cacheType:(SDImageCacheType)cacheType
progress:(nullable SDImageLoaderProgressBlock)progressBlock progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock { completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image loader to use
id<SDImageLoader> imageLoader;
if ([context[SDWebImageContextImageLoader] conformsToProtocol:@protocol(SDImageLoader)]) {
imageLoader = context[SDWebImageContextImageLoader];
} else {
imageLoader = self.imageLoader;
}
// Check whether we should download image from network // Check whether we should download image from network
BOOL shouldDownload = !SD_OPTIONS_CONTAINS(options, SDWebImageFromCacheOnly); BOOL shouldDownload = !SD_OPTIONS_CONTAINS(options, SDWebImageFromCacheOnly);
shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached); shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached);
shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]); shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
shouldDownload &= [self.imageLoader canRequestImageForURL:url]; shouldDownload &= [imageLoader canRequestImageForURL:url];
if (shouldDownload) { if (shouldDownload) {
if (cachedImage && options & SDWebImageRefreshCached) { if (cachedImage && options & SDWebImageRefreshCached) {
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
@ -265,7 +279,7 @@ static id<SDImageLoader> _defaultImageLoader;
} }
@weakify(operation); @weakify(operation);
operation.loaderOperation = [self.imageLoader requestImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) { operation.loaderOperation = [imageLoader requestImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
@strongify(operation); @strongify(operation);
if (!operation || operation.isCancelled) { if (!operation || operation.isCancelled) {
// Image combined operation cancelled by user // Image combined operation cancelled by user
@ -277,7 +291,7 @@ static id<SDImageLoader> _defaultImageLoader;
[self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url]; [self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url];
} else if (error) { } else if (error) {
[self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url]; [self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url];
BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error]; BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error options:options context:context];
if (shouldBlockFailedURL) { if (shouldBlockFailedURL) {
SD_LOCK(self.failedURLsLock); SD_LOCK(self.failedURLsLock);
@ -336,7 +350,6 @@ static id<SDImageLoader> _defaultImageLoader;
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)); shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage)); shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage));
BOOL shouldCacheOriginal = downloadedImage && finished; BOOL shouldCacheOriginal = downloadedImage && finished;
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// if available, store original image to cache // if available, store original image to cache
if (shouldCacheOriginal) { if (shouldCacheOriginal) {
@ -346,14 +359,14 @@ static id<SDImageLoader> _defaultImageLoader;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool { @autoreleasepool {
NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url]; NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url];
[self storeImage:downloadedImage imageData:cacheData forKey:key cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{ [self storeImage:downloadedImage imageData:cacheData forKey:key cacheType:targetStoreCacheType options:options context:context completion:^{
// Continue transform process // Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
}]; }];
} }
}); });
} else { } else {
[self storeImage:downloadedImage imageData:downloadedData forKey:key cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{ [self storeImage:downloadedImage imageData:downloadedData forKey:key cacheType:targetStoreCacheType options:options context:context completion:^{
// Continue transform process // Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
}]; }];
@ -385,7 +398,6 @@ static id<SDImageLoader> _defaultImageLoader;
BOOL shouldTransformImage = originalImage && transformer; BOOL shouldTransformImage = originalImage && transformer;
shouldTransformImage = shouldTransformImage && (!originalImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)); shouldTransformImage = shouldTransformImage && (!originalImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
shouldTransformImage = shouldTransformImage && (!originalImage.sd_isVector || (options & SDWebImageTransformVectorImage)); shouldTransformImage = shouldTransformImage && (!originalImage.sd_isVector || (options & SDWebImageTransformVectorImage));
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// if available, store transformed image to cache // if available, store transformed image to cache
if (shouldTransformImage) { if (shouldTransformImage) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@ -404,7 +416,7 @@ static id<SDImageLoader> _defaultImageLoader;
} }
// keep the original image format and extended data // keep the original image format and extended data
SDImageCopyAssociatedObject(originalImage, transformedImage); SDImageCopyAssociatedObject(originalImage, transformedImage);
[self storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType waitStoreCache:waitStoreCache completion:^{ [self storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType options:options context:context completion:^{
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; [self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}]; }];
} else { } else {
@ -432,10 +444,18 @@ static id<SDImageLoader> _defaultImageLoader;
imageData:(nullable NSData *)data imageData:(nullable NSData *)data
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
cacheType:(SDImageCacheType)cacheType cacheType:(SDImageCacheType)cacheType
waitStoreCache:(BOOL)waitStoreCache options:(SDWebImageOptions)options
context:(nullable SDWebImageContext *)context
completion:(nullable SDWebImageNoParamsBlock)completion { completion:(nullable SDWebImageNoParamsBlock)completion {
id<SDImageCache> imageCache;
if ([context[SDWebImageContextImageCache] conformsToProtocol:@protocol(SDImageCache)]) {
imageCache = context[SDWebImageContextImageCache];
} else {
imageCache = self.imageCache;
}
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// Check whether we should wait the store cache finished. If not, callback immediately // Check whether we should wait the store cache finished. If not, callback immediately
[self.imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{ [imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{
if (waitStoreCache) { if (waitStoreCache) {
if (completion) { if (completion) {
completion(); completion();
@ -472,13 +492,21 @@ static id<SDImageLoader> _defaultImageLoader;
} }
- (BOOL)shouldBlockFailedURLWithURL:(nonnull NSURL *)url - (BOOL)shouldBlockFailedURLWithURL:(nonnull NSURL *)url
error:(nonnull NSError *)error { error:(nonnull NSError *)error
options:(SDWebImageOptions)options
context:(nullable SDWebImageContext *)context {
id<SDImageLoader> imageLoader;
if ([context[SDWebImageContextImageLoader] conformsToProtocol:@protocol(SDImageLoader)]) {
imageLoader = context[SDWebImageContextImageLoader];
} else {
imageLoader = self.imageLoader;
}
// Check whether we should block failed url // Check whether we should block failed url
BOOL shouldBlockFailedURL; BOOL shouldBlockFailedURL;
if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) { if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error]; shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
} else { } else {
shouldBlockFailedURL = [self.imageLoader shouldBlockFailedURLWithURL:url error:error]; shouldBlockFailedURL = [imageLoader shouldBlockFailedURLWithURL:url error:error];
} }
return shouldBlockFailedURL; return shouldBlockFailedURL;

View File

@ -80,11 +80,13 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
[self sd_startImageIndicator]; [self sd_startImageIndicator];
id<SDWebImageIndicator> imageIndicator = self.sd_imageIndicator; id<SDWebImageIndicator> imageIndicator = self.sd_imageIndicator;
#endif #endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
SDWebImageManager *manager = context[SDWebImageContextCustomManager]; SDWebImageManager *manager = context[SDWebImageContextCustomManager];
if (!manager) { if (!manager) {
manager = [SDWebImageManager sharedManager]; manager = [SDWebImageManager sharedManager];
} }
#pragma clang diagnostic pop
SDImageLoaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { SDImageLoaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
if (imageProgress) { if (imageProgress) {