2015-03-23 20:28:42 +00:00
/ * *
* Copyright ( c ) 2015 - present , Facebook , Inc .
* All rights reserved .
*
* This source code is licensed under the BSD - style license found in the
* LICENSE file in the root directory of this source tree . An additional grant
* of patent rights can be found in the PATENTS file in the same directory .
* /
2015-03-10 00:08:01 +00:00
# import "RCTImageLoader.h"
2015-10-20 12:00:50 +00:00
# import < libkern / OSAtomic . h >
2015-03-10 00:08:01 +00:00
# import < UIKit / UIKit . h >
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
# import < ImageIO / ImageIO . h >
2015-03-10 00:08:01 +00:00
# import "RCTConvert.h"
2015-06-09 19:25:33 +00:00
# import "RCTDefines.h"
2015-10-08 18:32:11 +00:00
# import "RCTImageUtils.h"
2015-03-10 00:08:01 +00:00
# import "RCTLog.h"
2015-10-19 16:04:54 +00:00
# import "RCTNetworking.h"
2015-06-09 19:25:24 +00:00
# import "RCTUtils.h"
2015-03-10 00:08:01 +00:00
2016-05-23 10:31:51 +00:00
static const NSUInteger RCTMaxCachableDecodedImageSizeInBytes = 1048576 ; // 1 MB
static NSString * RCTCacheKeyForImage ( NSString * imageTag , CGSize size ,
CGFloat scale , RCTResizeMode resizeMode )
{
2016-05-25 13:50:16 +00:00
return [ NSString stringWithFormat : @ "%@|%g|%g|%g|%zd" ,
2016-05-23 10:31:51 +00:00
imageTag , size . width , size . height , scale , resizeMode ] ;
}
2015-09-04 11:35:44 +00:00
@ implementation UIImage ( React )
- ( CAKeyframeAnimation * ) reactKeyframeAnimation
{
return objc_getAssociatedObject ( self , _cmd ) ;
}
- ( void ) setReactKeyframeAnimation : ( CAKeyframeAnimation * ) reactKeyframeAnimation
{
objc_setAssociatedObject ( self , @ selector ( reactKeyframeAnimation ) , reactKeyframeAnimation , OBJC_ASSOCIATION _COPY _NONATOMIC ) ;
}
@ end
2015-03-10 00:08:01 +00:00
@ implementation RCTImageLoader
2015-10-19 16:04:54 +00:00
{
2015-11-03 22:45:46 +00:00
NSArray < id < RCTImageURLLoader > > * _loaders ;
NSArray < id < RCTImageDataDecoder > > * _decoders ;
2016-02-10 20:48:41 +00:00
NSOperationQueue * _imageDecodeQueue ;
2015-11-20 13:15:49 +00:00
dispatch_queue _t _URLCacheQueue ;
NSURLCache * _URLCache ;
2016-05-25 13:50:16 +00:00
NSCache * _decodedImageCache ;
2016-02-16 20:41:20 +00:00
NSMutableArray * _pendingTasks ;
NSInteger _activeTasks ;
NSMutableArray * _pendingDecodes ;
NSInteger _scheduledDecodes ;
NSUInteger _activeBytes ;
2015-10-19 16:04:54 +00:00
}
2015-03-10 00:08:01 +00:00
2015-07-27 15:48:31 +00:00
@ synthesize bridge = _bridge ;
RCT_EXPORT _MODULE ( )
2015-11-25 11:09:00 +00:00
- ( void ) setUp
2015-07-14 11:06:17 +00:00
{
2016-02-16 20:41:20 +00:00
// Set defaults
_maxConcurrentLoadingTasks = _maxConcurrentLoadingTasks ? : 4 ;
_maxConcurrentDecodingTasks = _maxConcurrentDecodingTasks ? : 2 ;
2016-06-09 16:55:18 +00:00
_maxConcurrentDecodingBytes = _maxConcurrentDecodingBytes ? : 30 * 1024 * 1024 ; // 30 MB
2016-02-16 20:41:20 +00:00
2016-03-03 10:20:20 +00:00
_URLCacheQueue = dispatch_queue _create ( "com.facebook.react.ImageLoaderURLCacheQueue" , DISPATCH_QUEUE _SERIAL ) ;
2016-05-25 13:50:16 +00:00
_decodedImageCache = [ NSCache new ] ;
_decodedImageCache . totalCostLimit = 5 * 1024 * 1024 ; // 5 MB
// Clear cache in the event of a memory warning , or if app enters background
[ [ NSNotificationCenter defaultCenter ] addObserver : _decodedImageCache selector : @ selector ( removeAllObjects ) name : UIApplicationDidReceiveMemoryWarningNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : _decodedImageCache selector : @ selector ( removeAllObjects ) name : UIApplicationWillResignActiveNotification object : nil ] ;
}
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : _decodedImageCache ] ;
2015-10-19 16:04:54 +00:00
}
- ( id < RCTImageURLLoader > ) imageURLLoaderForURL : ( NSURL * ) URL
{
2016-03-03 10:20:20 +00:00
if ( ! _maxConcurrentLoadingTasks ) {
2015-11-25 11:09:00 +00:00
[ self setUp ] ;
}
2016-03-03 10:20:20 +00:00
if ( ! _loaders ) {
// Get loaders , sorted in reverse priority order ( highest priority first )
RCTAssert ( _bridge , @ "Bridge not set" ) ;
_loaders = [ [ _bridge modulesConformingToProtocol : @ protocol ( RCTImageURLLoader ) ] sortedArrayUsingComparator : ^ NSComparisonResult ( id < RCTImageURLLoader > a , id < RCTImageURLLoader > b ) {
float priorityA = [ a respondsToSelector : @ selector ( loaderPriority ) ] ? [ a loaderPriority ] : 0 ;
float priorityB = [ b respondsToSelector : @ selector ( loaderPriority ) ] ? [ b loaderPriority ] : 0 ;
if ( priorityA > priorityB ) {
return NSOrderedAscending ;
} else if ( priorityA < priorityB ) {
return NSOrderedDescending ;
} else {
return NSOrderedSame ;
}
} ] ;
}
2015-10-19 16:04:54 +00:00
if ( RCT_DEBUG ) {
// Check for handler conflicts
float previousPriority = 0 ;
id < RCTImageURLLoader > previousLoader = nil ;
for ( id < RCTImageURLLoader > loader in _loaders ) {
2015-11-17 15:18:55 +00:00
float priority = [ loader respondsToSelector : @ selector ( loaderPriority ) ] ? [ loader loaderPriority ] : 0 ;
if ( previousLoader && priority < previousPriority ) {
return previousLoader ;
}
2015-10-19 16:04:54 +00:00
if ( [ loader canLoadImageURL : URL ] ) {
if ( previousLoader ) {
if ( priority = = previousPriority ) {
RCTLogError ( @ "The RCTImageURLLoaders %@ and %@ both reported that"
" they can load the URL %@, and have equal priority"
" (%g). This could result in non-deterministic behavior." ,
loader , previousLoader , URL , priority ) ;
}
} else {
previousLoader = loader ;
previousPriority = priority ;
}
}
}
2015-11-17 15:18:55 +00:00
return previousLoader ;
2015-10-19 16:04:54 +00:00
}
// Normal code path
for ( id < RCTImageURLLoader > loader in _loaders ) {
if ( [ loader canLoadImageURL : URL ] ) {
return loader ;
}
}
return nil ;
}
- ( id < RCTImageDataDecoder > ) imageDataDecoderForData : ( NSData * ) data
{
2016-03-03 10:20:20 +00:00
if ( ! _maxConcurrentLoadingTasks ) {
2015-11-25 11:09:00 +00:00
[ self setUp ] ;
}
2016-03-03 10:20:20 +00:00
if ( ! _decoders ) {
// Get decoders , sorted in reverse priority order ( highest priority first )
RCTAssert ( _bridge , @ "Bridge not set" ) ;
_decoders = [ [ _bridge modulesConformingToProtocol : @ protocol ( RCTImageDataDecoder ) ] sortedArrayUsingComparator : ^ NSComparisonResult ( id < RCTImageDataDecoder > a , id < RCTImageDataDecoder > b ) {
float priorityA = [ a respondsToSelector : @ selector ( decoderPriority ) ] ? [ a decoderPriority ] : 0 ;
float priorityB = [ b respondsToSelector : @ selector ( decoderPriority ) ] ? [ b decoderPriority ] : 0 ;
if ( priorityA > priorityB ) {
return NSOrderedAscending ;
} else if ( priorityA < priorityB ) {
return NSOrderedDescending ;
} else {
return NSOrderedSame ;
}
} ] ;
}
2015-10-19 16:04:54 +00:00
if ( RCT_DEBUG ) {
// Check for handler conflicts
float previousPriority = 0 ;
id < RCTImageDataDecoder > previousDecoder = nil ;
for ( id < RCTImageDataDecoder > decoder in _decoders ) {
2015-11-17 15:18:55 +00:00
float priority = [ decoder respondsToSelector : @ selector ( decoderPriority ) ] ? [ decoder decoderPriority ] : 0 ;
if ( previousDecoder && priority < previousPriority ) {
return previousDecoder ;
}
2015-10-19 16:04:54 +00:00
if ( [ decoder canDecodeImageData : data ] ) {
if ( previousDecoder ) {
if ( priority = = previousPriority ) {
RCTLogError ( @ "The RCTImageDataDecoders %@ and %@ both reported that"
" they can decode the data <NSData %p; %tu bytes>, and"
" have equal priority (%g). This could result in"
" non-deterministic behavior." ,
decoder , previousDecoder , data , data . length , priority ) ;
}
} else {
previousDecoder = decoder ;
previousPriority = priority ;
}
}
}
2015-11-17 15:18:55 +00:00
return previousDecoder ;
2015-10-19 16:04:54 +00:00
}
// Normal code path
for ( id < RCTImageDataDecoder > decoder in _decoders ) {
if ( [ decoder canDecodeImageData : data ] ) {
return decoder ;
}
}
return nil ;
}
2016-01-20 19:03:22 +00:00
static UIImage * RCTResizeImageIfNeeded ( UIImage * image ,
CGSize size ,
CGFloat scale ,
RCTResizeMode resizeMode )
{
if ( CGSizeEqualToSize ( size , CGSizeZero ) ||
CGSizeEqualToSize ( image . size , CGSizeZero ) ||
CGSizeEqualToSize ( image . size , size ) ) {
return image ;
}
CAKeyframeAnimation * animation = image . reactKeyframeAnimation ;
CGRect targetSize = RCTTargetRect ( image . size , size , scale , resizeMode ) ;
CGAffineTransform transform = RCTTransformFromTargetRect ( image . size , targetSize ) ;
image = RCTTransformImage ( image , size , scale , transform ) ;
image . reactKeyframeAnimation = animation ;
return image ;
}
2016-06-01 17:32:20 +00:00
- ( RCTImageLoaderCancellationBlock ) loadImageWithURLRequest : ( NSURLRequest * ) imageURLRequest
2015-10-19 16:04:54 +00:00
callback : ( RCTImageLoaderCompletionBlock ) callback
{
2016-06-01 17:32:20 +00:00
return [ self loadImageWithURLRequest : imageURLRequest
size : CGSizeZero
scale : 1
clipped : YES
resizeMode : RCTResizeModeStretch
progressBlock : nil
completionBlock : callback ] ;
2015-07-27 15:48:31 +00:00
}
2016-02-16 20:41:20 +00:00
- ( void ) dequeueTasks
{
dispatch_async ( _URLCacheQueue , ^ {
// Remove completed tasks
for ( RCTNetworkTask * task in _pendingTasks . reverseObjectEnumerator ) {
switch ( task . status ) {
case RCTNetworkTaskFinished :
[ _pendingTasks removeObject : task ] ;
_activeTasks - - ;
break ;
case RCTNetworkTaskPending :
2016-06-09 16:55:18 +00:00
break ;
2016-02-16 20:41:20 +00:00
case RCTNetworkTaskInProgress :
2016-06-09 16:55:18 +00:00
// Check task isn ' t "stuck"
if ( task . requestToken = = nil ) {
RCTLogWarn ( @ "Task orphaned for request %@" , task . request ) ;
[ _pendingTasks removeObject : task ] ;
_activeTasks - - ;
[ task cancel ] ;
}
2016-02-16 20:41:20 +00:00
break ;
}
}
// Start queued decode
NSInteger activeDecodes = _scheduledDecodes - _pendingDecodes . count ;
while ( activeDecodes = = 0 || ( _activeBytes <= _maxConcurrentDecodingBytes &&
activeDecodes <= _maxConcurrentDecodingTasks ) ) {
dispatch_block _t decodeBlock = _pendingDecodes . firstObject ;
if ( decodeBlock ) {
[ _pendingDecodes removeObjectAtIndex : 0 ] ;
decodeBlock ( ) ;
} else {
break ;
}
}
// Start queued tasks
for ( RCTNetworkTask * task in _pendingTasks ) {
if ( MAX ( _activeTasks , _scheduledDecodes ) >= _maxConcurrentLoadingTasks ) {
break ;
}
if ( task . status = = RCTNetworkTaskPending ) {
[ task start ] ;
_activeTasks + + ;
}
}
} ) ;
}
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
/ * *
* This returns either an image , or raw image data , depending on the loading
* path taken . This is useful if you want to skip decoding , e . g . when preloading
* the image , or retrieving metadata .
* /
2016-06-01 17:32:20 +00:00
- ( RCTImageLoaderCancellationBlock ) loadImageOrDataWithURLRequest : ( NSURLRequest * ) imageURLRequest
size : ( CGSize ) size
scale : ( CGFloat ) scale
resizeMode : ( RCTResizeMode ) resizeMode
progressBlock : ( RCTImageLoaderProgressBlock ) progressHandler
completionBlock : ( void ( ^ ) ( NSError * error , id imageOrData ) ) completionBlock
2015-03-10 00:08:01 +00:00
{
2015-10-20 12:00:50 +00:00
__block volatile uint32_t cancelled = 0 ;
2015-11-20 13:15:49 +00:00
__block void ( ^ cancelLoad ) ( void ) = nil ;
__weak RCTImageLoader * weakSelf = self ;
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
void ( ^ completionHandler ) ( NSError * error , id imageOrData ) = ^ ( NSError * error , id imageOrData ) {
2016-06-06 14:57:55 +00:00
if ( RCTIsMainQueue ( ) ) {
2015-10-20 12:00:50 +00:00
// Most loaders do not return on the main thread , so caller is probably not
// expecting it , and may do expensive post - processing in the callback
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
if ( ! cancelled ) {
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
completionBlock ( error , imageOrData ) ;
2015-10-20 12:00:50 +00:00
}
} ) ;
} else if ( ! cancelled ) {
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
completionBlock ( error , imageOrData ) ;
2015-10-20 12:00:50 +00:00
}
2015-10-19 16:04:54 +00:00
} ;
2015-11-20 13:15:49 +00:00
// All access to URL cache must be serialized
2015-11-25 11:09:00 +00:00
if ( ! _URLCacheQueue ) {
2016-03-03 10:20:20 +00:00
[ self setUp ] ;
2015-11-25 11:09:00 +00:00
}
2015-11-20 13:15:49 +00:00
dispatch_async ( _URLCacheQueue , ^ {
2015-11-25 11:09:00 +00:00
if ( ! _URLCache ) {
_URLCache = [ [ NSURLCache alloc ] initWithMemoryCapacity : 5 * 1024 * 1024 // 5 MB
diskCapacity : 200 * 1024 * 1024 // 200 MB
diskPath : @ "React/RCTImageDownloader" ] ;
}
2015-11-20 13:15:49 +00:00
RCTImageLoader * strongSelf = weakSelf ;
if ( cancelled || ! strongSelf ) {
return ;
}
2015-10-19 16:04:54 +00:00
2015-11-20 13:15:49 +00:00
// Find suitable image URL loader
2016-06-01 17:32:20 +00:00
NSURLRequest * request = imageURLRequest ; // Use a local variable so we can reassign it in this block
2015-11-20 13:15:49 +00:00
id < RCTImageURLLoader > loadHandler = [ strongSelf imageURLLoaderForURL : request . URL ] ;
if ( loadHandler ) {
cancelLoad = [ loadHandler loadImageForURL : request . URL
size : size
scale : scale
resizeMode : resizeMode
progressHandler : progressHandler
completionHandler : completionHandler ] ? : ^ { } ;
return ;
}
2015-11-17 15:18:55 +00:00
2015-11-20 13:15:49 +00:00
// Check if networking module is available
if ( RCT_DEBUG && ! [ _bridge respondsToSelector : @ selector ( networking ) ] ) {
RCTLogError ( @ "No suitable image URL loader found for %@. You may need to "
2016-03-21 18:36:36 +00:00
" import the RCTNetwork library in order to load images." ,
2016-06-01 17:32:20 +00:00
request . URL . absoluteString ) ;
2015-11-17 15:18:55 +00:00
return ;
2015-11-20 13:15:49 +00:00
}
// Check if networking module can load image
if ( RCT_DEBUG && ! [ _bridge . networking canHandleRequest : request ] ) {
2016-06-01 17:32:20 +00:00
RCTLogError ( @ "No suitable image URL loader found for %@" , request . URL . absoluteString ) ;
2015-11-17 15:18:55 +00:00
return ;
}
2015-11-05 17:06:53 +00:00
2015-11-20 13:15:49 +00:00
// Use networking module to load image
RCTURLRequestCompletionBlock processResponse =
^ ( NSURLResponse * response , NSData * data , NSError * error ) {
// Check for system errors
if ( error ) {
completionHandler ( error , nil ) ;
return ;
} else if ( ! data ) {
completionHandler ( RCTErrorWithMessage ( @ "Unknown image download error" ) , nil ) ;
2015-11-05 17:06:53 +00:00
return ;
}
2015-11-20 13:15:49 +00:00
// Check for http errors
if ( [ response isKindOfClass : [ NSHTTPURLResponse class ] ] ) {
NSInteger statusCode = ( ( NSHTTPURLResponse * ) response ) . statusCode ;
if ( statusCode ! = 200 ) {
completionHandler ( [ [ NSError alloc ] initWithDomain : NSURLErrorDomain
code : statusCode
userInfo : nil ] , nil ) ;
return ;
}
}
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
// Call handler
completionHandler ( nil , data ) ;
2015-11-20 13:15:49 +00:00
} ;
// Add missing png extension
if ( request . URL . fileURL && request . URL . pathExtension . length = = 0 ) {
NSMutableURLRequest * mutableRequest = [ request mutableCopy ] ;
mutableRequest . URL = [ NSURL fileURLWithPath : [ request . URL . path stringByAppendingPathExtension : @ "png" ] ] ;
request = mutableRequest ;
2015-11-17 15:18:55 +00:00
}
2015-10-19 16:04:54 +00:00
2015-11-20 13:15:49 +00:00
// Check for cached response before reloading
// TODO : move URL cache out of RCTImageLoader into its own module
NSCachedURLResponse * cachedResponse = [ _URLCache cachedResponseForRequest : request ] ;
2016-04-27 21:39:10 +00:00
while ( cachedResponse ) {
if ( [ cachedResponse . response isKindOfClass : [ NSHTTPURLResponse class ] ] ) {
NSHTTPURLResponse * httpResponse = ( NSHTTPURLResponse * ) cachedResponse . response ;
if ( httpResponse . statusCode = = 301 || httpResponse . statusCode = = 302 ) {
NSString * location = httpResponse . allHeaderFields [ @ "Location" ] ;
if ( location = = nil ) {
completionHandler ( RCTErrorWithMessage ( @ "Image redirect without location" ) , nil ) ;
return ;
}
2016-06-07 19:00:29 +00:00
NSURL * redirectURL = [ NSURL URLWithString : location relativeToURL : request . URL ] ;
2016-06-01 17:32:20 +00:00
request = [ NSURLRequest requestWithURL : redirectURL ] ;
2016-04-27 21:39:10 +00:00
cachedResponse = [ _URLCache cachedResponseForRequest : request ] ;
continue ;
}
}
2015-11-20 13:15:49 +00:00
processResponse ( cachedResponse . response , cachedResponse . data , nil ) ;
2015-11-23 12:18:20 +00:00
return ;
2015-11-20 13:15:49 +00:00
}
2015-10-19 16:04:54 +00:00
2015-11-20 13:15:49 +00:00
// Download image
2016-02-16 20:41:20 +00:00
RCTNetworkTask * task = [ _bridge . networking networkTaskWithRequest : request completionBlock : ^ ( NSURLResponse * response , NSData * data , NSError * error ) {
2015-11-20 13:15:49 +00:00
if ( error ) {
completionHandler ( error , nil ) ;
2016-06-09 16:55:18 +00:00
[ weakSelf dequeueTasks ] ;
2015-11-20 13:15:49 +00:00
return ;
}
2015-10-19 16:04:54 +00:00
2015-11-20 13:15:49 +00:00
dispatch_async ( _URLCacheQueue , ^ {
2015-09-03 12:53:16 +00:00
2015-11-20 13:15:49 +00:00
// Cache the response
// TODO : move URL cache out of RCTImageLoader into its own module
BOOL isHTTPRequest = [ request . URL . scheme hasPrefix : @ "http" ] ;
[ strongSelf -> _URLCache storeCachedResponse :
[ [ NSCachedURLResponse alloc ] initWithResponse : response
data : data
userInfo : nil
storagePolicy : isHTTPRequest ? NSURLCacheStorageAllowed : NSURLCacheStorageAllowedInMemoryOnly ]
forRequest : request ] ;
// Process image data
processResponse ( response , data , nil ) ;
2015-11-17 15:18:55 +00:00
2016-06-09 16:55:18 +00:00
// Prepare for next task
2016-02-16 20:41:20 +00:00
[ weakSelf dequeueTasks ] ;
2015-11-20 13:15:49 +00:00
} ) ;
2015-10-19 16:04:54 +00:00
2015-11-20 13:15:49 +00:00
} ] ;
task . downloadProgressBlock = progressHandler ;
2016-02-16 20:41:20 +00:00
if ( ! _pendingTasks ) {
_pendingTasks = [ NSMutableArray new ] ;
}
2016-05-09 15:17:57 +00:00
if ( task ) {
[ _pendingTasks addObject : task ] ;
2016-06-09 16:55:18 +00:00
[ weakSelf dequeueTasks ] ;
2016-02-16 20:41:20 +00:00
}
2015-11-20 13:15:49 +00:00
cancelLoad = ^ {
[ task cancel ] ;
2016-02-16 20:41:20 +00:00
[ weakSelf dequeueTasks ] ;
2015-11-20 13:15:49 +00:00
} ;
} ) ;
2015-07-21 12:39:57 +00:00
2015-11-17 15:18:55 +00:00
return ^ {
2015-11-20 13:15:49 +00:00
if ( cancelLoad ) {
cancelLoad ( ) ;
2015-11-17 15:18:55 +00:00
}
OSAtomicOr32Barrier ( 1 , & cancelled ) ;
} ;
2015-09-02 15:25:10 +00:00
}
2016-06-01 17:32:20 +00:00
- ( RCTImageLoaderCancellationBlock ) loadImageWithURLRequest : ( NSURLRequest * ) imageURLRequest
size : ( CGSize ) size
scale : ( CGFloat ) scale
clipped : ( BOOL ) clipped
resizeMode : ( RCTResizeMode ) resizeMode
progressBlock : ( RCTImageLoaderProgressBlock ) progressHandler
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
{
__block volatile uint32_t cancelled = 0 ;
__block void ( ^ cancelLoad ) ( void ) = nil ;
__weak RCTImageLoader * weakSelf = self ;
2016-05-23 10:31:51 +00:00
// Check decoded image cache
2016-06-01 17:32:20 +00:00
NSString * cacheKey = RCTCacheKeyForImage ( imageURLRequest . URL . absoluteString , size , scale , resizeMode ) ;
2016-05-23 10:31:51 +00:00
{
2016-05-25 13:50:16 +00:00
UIImage * image = [ _decodedImageCache objectForKey : cacheKey ] ;
2016-05-23 10:31:51 +00:00
if ( image ) {
// Most loaders do not return on the main thread , so caller is probably not
// expecting it , and may do expensive post - processing in the callback
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
completionBlock ( nil , image ) ;
} ) ;
return ^ { } ;
}
}
RCTImageLoaderCompletionBlock cacheResultHandler = ^ ( NSError * error , UIImage * image ) {
if ( image ) {
CGFloat bytes = image . size . width * image . size . height * image . scale * image . scale * 4 ;
if ( bytes <= RCTMaxCachableDecodedImageSizeInBytes ) {
2016-05-25 13:50:16 +00:00
[ _decodedImageCache setObject : image forKey : cacheKey cost : bytes ] ;
2016-05-23 10:31:51 +00:00
}
}
completionBlock ( error , image ) ;
} ;
void ( ^ completionHandler ) ( NSError * , id ) = ^ ( NSError * error , id imageOrData ) {
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
if ( ! cancelled ) {
if ( ! imageOrData || [ imageOrData isKindOfClass : [ UIImage class ] ] ) {
2016-05-23 10:31:51 +00:00
cacheResultHandler ( error , imageOrData ) ;
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
} else {
2016-06-01 17:32:20 +00:00
cancelLoad = [ weakSelf decodeImageData : imageOrData
size : size
scale : scale
clipped : clipped
resizeMode : resizeMode
completionBlock : cacheResultHandler ] ;
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
}
}
} ;
2016-06-01 17:32:20 +00:00
cancelLoad = [ self loadImageOrDataWithURLRequest : imageURLRequest
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
size : size
scale : scale
resizeMode : resizeMode
progressBlock : progressHandler
2016-05-23 10:31:51 +00:00
completionBlock : completionHandler ] ;
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
return ^ {
if ( cancelLoad ) {
cancelLoad ( ) ;
}
OSAtomicOr32Barrier ( 1 , & cancelled ) ;
} ;
}
2015-09-02 15:25:10 +00:00
- ( RCTImageLoaderCancellationBlock ) decodeImageData : ( NSData * ) data
size : ( CGSize ) size
scale : ( CGFloat ) scale
2016-06-01 17:32:20 +00:00
clipped : ( BOOL ) clipped
2016-01-20 19:03:22 +00:00
resizeMode : ( RCTResizeMode ) resizeMode
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
2015-09-02 15:25:10 +00:00
{
2015-11-17 15:18:55 +00:00
if ( data . length = = 0 ) {
2016-01-20 19:03:22 +00:00
completionBlock ( RCTErrorWithMessage ( @ "No image data" ) , nil ) ;
2015-11-17 15:18:55 +00:00
return ^ { } ;
}
2016-01-20 19:03:22 +00:00
__block volatile uint32_t cancelled = 0 ;
void ( ^ completionHandler ) ( NSError * , UIImage * ) = ^ ( NSError * error , UIImage * image ) {
2016-06-06 14:57:55 +00:00
if ( RCTIsMainQueue ( ) ) {
2016-01-20 19:03:22 +00:00
// Most loaders do not return on the main thread , so caller is probably not
// expecting it , and may do expensive post - processing in the callback
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
if ( ! cancelled ) {
2016-06-01 17:32:20 +00:00
completionBlock ( error , clipped ? RCTResizeImageIfNeeded ( image , size , scale , resizeMode ) : image ) ;
2016-01-20 19:03:22 +00:00
}
} ) ;
} else if ( ! cancelled ) {
2016-06-01 17:32:20 +00:00
completionBlock ( error , clipped ? RCTResizeImageIfNeeded ( image , size , scale , resizeMode ) : image ) ;
2016-01-20 19:03:22 +00:00
}
} ;
2015-10-19 16:04:54 +00:00
id < RCTImageDataDecoder > imageDecoder = [ self imageDataDecoderForData : data ] ;
2015-09-02 15:25:10 +00:00
if ( imageDecoder ) {
2015-10-19 16:04:54 +00:00
return [ imageDecoder decodeImageData : data
size : size
scale : scale
resizeMode : resizeMode
2016-05-23 10:31:51 +00:00
completionHandler : completionHandler ] ? : ^ { } ;
2015-09-02 15:25:10 +00:00
} else {
2015-10-20 12:00:50 +00:00
2016-03-03 10:20:20 +00:00
if ( ! _URLCacheQueue ) {
[ self setUp ] ;
}
2016-02-16 20:41:20 +00:00
dispatch_async ( _URLCacheQueue , ^ {
dispatch_block _t decodeBlock = ^ {
// Calculate the size , in bytes , that the decompressed image will require
NSInteger decodedImageBytes = ( size . width * scale ) * ( size . height * scale ) * 4 ;
2016-01-20 19:03:22 +00:00
2016-02-16 20:41:20 +00:00
// Mark these bytes as in - use
_activeBytes + = decodedImageBytes ;
// Do actual decompression on a concurrent background queue
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
if ( ! cancelled ) {
// Decompress the image data ( this may be CPU and memory intensive )
UIImage * image = RCTDecodeImageWithData ( data , size , scale , resizeMode ) ;
2016-01-20 19:03:22 +00:00
# if RCT_DEV
2016-02-16 20:41:20 +00:00
CGSize imagePixelSize = RCTSizeInPixels ( image . size , image . scale ) ;
CGSize screenPixelSize = RCTSizeInPixels ( RCTScreenSize ( ) , RCTScreenScale ( ) ) ;
if ( imagePixelSize . width * imagePixelSize . height >
screenPixelSize . width * screenPixelSize . height ) {
RCTLogInfo ( @ "[PERF ASSETS] Loading image at size %@, which is larger "
"than the screen size %@" , NSStringFromCGSize ( imagePixelSize ) ,
NSStringFromCGSize ( screenPixelSize ) ) ;
}
2016-01-20 19:03:22 +00:00
# endif
2016-02-16 20:41:20 +00:00
if ( image ) {
completionHandler ( nil , image ) ;
} else {
NSString * errorMessage = [ NSString stringWithFormat : @ "Error decoding image data <NSData %p; %tu bytes>" , data , data . length ] ;
NSError * finalError = RCTErrorWithMessage ( errorMessage ) ;
completionHandler ( finalError , nil ) ;
}
}
// We ' re no longer retaining the uncompressed data , so now we ' ll mark
// the decoding as complete so that the loading task queue can resume .
dispatch_async ( _URLCacheQueue , ^ {
_scheduledDecodes - - ;
_activeBytes - = decodedImageBytes ;
[ self dequeueTasks ] ;
} ) ;
} ) ;
} ;
// The decode operation retains the compressed image data until it ' s
// complete , so we ' ll mark it as having started , in order to block
// further image loads from happening until we ' re done with the data .
_scheduledDecodes + + ;
if ( ! _pendingDecodes ) {
_pendingDecodes = [ NSMutableArray new ] ;
}
NSInteger activeDecodes = _scheduledDecodes - _pendingDecodes . count - 1 ;
if ( activeDecodes = = 0 || ( _activeBytes <= _maxConcurrentDecodingBytes &&
activeDecodes <= _maxConcurrentDecodingTasks ) ) {
decodeBlock ( ) ;
2015-07-21 05:44:42 +00:00
} else {
2016-02-16 20:41:20 +00:00
[ _pendingDecodes addObject : decodeBlock ] ;
2015-07-21 05:44:42 +00:00
}
2016-02-16 20:41:20 +00:00
} ) ;
2015-10-20 12:00:50 +00:00
return ^ {
OSAtomicOr32Barrier ( 1 , & cancelled ) ;
} ;
2015-03-10 00:08:01 +00:00
}
}
2016-06-01 17:32:20 +00:00
- ( RCTImageLoaderCancellationBlock ) getImageSizeForURLRequest : ( NSURLRequest * ) imageURLRequest
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
block : ( void ( ^ ) ( NSError * error , CGSize size ) ) completionBlock
{
2016-06-01 17:32:20 +00:00
return [ self loadImageOrDataWithURLRequest : imageURLRequest
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
size : CGSizeZero
scale : 1
2016-01-20 19:03:22 +00:00
resizeMode : RCTResizeModeStretch
Added getImageSize method
Summary:
public
This diff adds a `getSize()` method to `Image` to retrieve the width and height of an image prior to displaying it. This is useful when working with images from uncontrolled sources, and has been a much-requested feature.
In order to retrieve the image dimensions, the image may first need to be loaded or downloaded, after which it will be cached. This means that in principle you could use this method to preload images, however it is not optimized for that purpose, and may in future be implemented in a way that does not fully load/download the image data.
A fully supported way to preload images will be provided in a future diff.
The API (separate success and failure callbacks) is far from ideal, but until we agree on a unified standard, this was the most conventional way I could think of to implement it. If it returned a promise or something similar, it would be unique among all such APIS in the framework.
Please note that this has been a long time coming, in part due to much bikeshedding about what the API should look like, so while it's not unlikely that the API may change in future, I think having *some* way to do this is better than waiting until we can define the "perfect" way.
Reviewed By: vjeux
Differential Revision: D2797365
fb-gh-sync-id: 11eb1b8547773b1f8be0bc55ddf6dfedebf7fc0a
2016-01-01 02:50:26 +00:00
progressBlock : nil
completionBlock : ^ ( NSError * error , id imageOrData ) {
CGSize size ;
if ( [ imageOrData isKindOfClass : [ NSData class ] ] ) {
NSDictionary * meta = RCTGetImageMetadata ( imageOrData ) ;
size = ( CGSize ) {
[ meta [ ( id ) kCGImagePropertyPixelWidth ] doubleValue ] ,
[ meta [ ( id ) kCGImagePropertyPixelHeight ] doubleValue ] ,
} ;
} else {
UIImage * image = imageOrData ;
size = ( CGSize ) {
image . size . width * image . scale ,
image . size . height * image . scale ,
} ;
}
completionBlock ( error , size ) ;
} ] ;
}
2015-09-02 15:25:10 +00:00
# pragma mark - RCTURLRequestHandler
- ( BOOL ) canHandleRequest : ( NSURLRequest * ) request
2015-07-14 11:06:17 +00:00
{
2016-01-20 19:03:22 +00:00
NSURL * requestURL = request . URL ;
for ( id < RCTImageURLLoader > loader in _loaders ) {
// Don ' t use RCTImageURLLoader protocol for modules that already conform to
// RCTURLRequestHandler as it ' s inefficient to decode an image and then
// convert it back into data
if ( ! [ loader conformsToProtocol : @ protocol ( RCTURLRequestHandler ) ] &&
[ loader canLoadImageURL : requestURL ] ) {
return YES ;
}
}
return NO ;
2015-07-15 20:17:13 +00:00
}
2015-09-02 15:25:10 +00:00
- ( id ) sendRequest : ( NSURLRequest * ) request withDelegate : ( id < RCTURLRequestDelegate > ) delegate
2015-07-15 20:17:13 +00:00
{
2015-09-02 15:25:10 +00:00
__block RCTImageLoaderCancellationBlock requestToken ;
2016-06-01 17:32:20 +00:00
requestToken = [ self loadImageWithURLRequest : request callback : ^ ( NSError * error , UIImage * image ) {
2015-09-02 15:25:10 +00:00
if ( error ) {
[ delegate URLRequest : requestToken didCompleteWithError : error ] ;
return ;
}
NSString * mimeType = nil ;
NSData * imageData = nil ;
if ( RCTImageHasAlpha ( image . CGImage ) ) {
mimeType = @ "image/png" ;
imageData = UIImagePNGRepresentation ( image ) ;
} else {
mimeType = @ "image/jpeg" ;
imageData = UIImageJPEGRepresentation ( image , 1.0 ) ;
}
NSURLResponse * response = [ [ NSURLResponse alloc ] initWithURL : request . URL
MIMEType : mimeType
expectedContentLength : imageData . length
textEncodingName : nil ] ;
[ delegate URLRequest : requestToken didReceiveResponse : response ] ;
[ delegate URLRequest : requestToken didReceiveData : imageData ] ;
[ delegate URLRequest : requestToken didCompleteWithError : nil ] ;
} ] ;
return requestToken ;
}
- ( void ) cancelRequest : ( id ) requestToken
{
if ( requestToken ) {
( ( RCTImageLoaderCancellationBlock ) requestToken ) ( ) ;
}
2015-07-14 11:06:17 +00:00
}
2015-03-10 00:08:01 +00:00
@ end
2015-07-27 15:48:31 +00:00
2016-06-01 17:32:20 +00:00
@ implementation RCTImageLoader ( Deprecated )
- ( RCTImageLoaderCancellationBlock ) loadImageWithTag : ( NSString * ) imageTag
callback : ( RCTImageLoaderCompletionBlock ) callback
{
RCTLogWarn ( @ "[RCTImageLoader loadImageWithTag:callback:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:callback:]" ) ;
return [ self loadImageWithURLRequest : [ RCTConvert NSURLRequest : imageTag ]
callback : callback ] ;
}
- ( RCTImageLoaderCancellationBlock ) loadImageWithTag : ( NSString * ) imageTag
size : ( CGSize ) size
scale : ( CGFloat ) scale
resizeMode : ( RCTResizeMode ) resizeMode
progressBlock : ( RCTImageLoaderProgressBlock ) progressBlock
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
{
RCTLogWarn ( @ "[RCTImageLoader loadImageWithTag:size:scale:resizeMode:progressBlock:completionBlock:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock:]" ) ;
return [ self loadImageWithURLRequest : [ RCTConvert NSURLRequest : imageTag ]
size : size
scale : scale
clipped : YES
resizeMode : resizeMode
progressBlock : progressBlock
completionBlock : completionBlock ] ;
}
- ( RCTImageLoaderCancellationBlock ) loadImageWithoutClipping : ( NSString * ) imageTag
size : ( CGSize ) size
scale : ( CGFloat ) scale
resizeMode : ( RCTResizeMode ) resizeMode
progressBlock : ( RCTImageLoaderProgressBlock ) progressBlock
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
{
RCTLogWarn ( @ "[RCTImageLoader loadImageWithoutClipping:size:scale:resizeMode:progressBlock:completionBlock:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock:]" ) ;
return [ self loadImageWithURLRequest : [ RCTConvert NSURLRequest : imageTag ]
size : size
scale : scale
clipped : NO
resizeMode : resizeMode
progressBlock : progressBlock
completionBlock : completionBlock ] ;
}
- ( RCTImageLoaderCancellationBlock ) decodeImageData : ( NSData * ) imageData
size : ( CGSize ) size
scale : ( CGFloat ) scale
resizeMode : ( RCTResizeMode ) resizeMode
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
{
RCTLogWarn ( @ "[RCTImageLoader decodeImageData:size:scale:resizeMode:completionBlock:] is deprecated. Instead use [RCTImageLoader decodeImageData:size:scale:clipped:resizeMode:completionBlock:]" ) ;
return [ self decodeImageData : imageData
size : size
scale : scale
clipped : NO
resizeMode : resizeMode
completionBlock : completionBlock ] ;
}
- ( RCTImageLoaderCancellationBlock ) decodeImageDataWithoutClipping : ( NSData * ) imageData
size : ( CGSize ) size
scale : ( CGFloat ) scale
resizeMode : ( RCTResizeMode ) resizeMode
completionBlock : ( RCTImageLoaderCompletionBlock ) completionBlock
{
RCTLogWarn ( @ "[RCTImageLoader decodeImageDataWithoutClipping:size:scale:resizeMode:completionBlock:] is deprecated. Instead use [RCTImageLoader decodeImageData:size:scale:clipped:resizeMode:completionBlock:]" ) ;
return [ self decodeImageData : imageData
size : size
scale : scale
clipped : NO
resizeMode : resizeMode
completionBlock : completionBlock ] ;
}
- ( RCTImageLoaderCancellationBlock ) getImageSize : ( NSString * ) imageTag
block : ( void ( ^ ) ( NSError * error , CGSize size ) ) completionBlock
{
RCTLogWarn ( @ "[RCTImageLoader getImageSize:block:] is deprecated. Instead use [RCTImageLoader getImageSizeForURLRequest:block:]" ) ;
return [ self getImageSizeForURLRequest : [ RCTConvert NSURLRequest : imageTag ]
block : completionBlock ] ;
}
@ end
2015-07-27 15:48:31 +00:00
@ implementation RCTBridge ( RCTImageLoader )
- ( RCTImageLoader * ) imageLoader
{
2015-11-25 11:09:00 +00:00
return [ self moduleForClass : [ RCTImageLoader class ] ] ;
2015-07-27 15:48:31 +00:00
}
@ end