diff --git a/Examples/UIExplorer/ImageCapInsetsExample.js b/Examples/UIExplorer/ImageCapInsetsExample.js
index 8c6ab243e..f75f7da98 100644
--- a/Examples/UIExplorer/ImageCapInsetsExample.js
+++ b/Examples/UIExplorer/ImageCapInsetsExample.js
@@ -46,6 +46,7 @@ var ImageCapInsetsExample = React.createClass({
diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
index e2f84182e..52560f0d6 100644
--- a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
+++ b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
@@ -1,7 +1,7 @@
+ version = "1.3">
@@ -51,10 +51,10 @@
+ shouldUseLaunchSchemeArgsEnv = "YES">
@@ -90,11 +90,11 @@
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h
index 6ee68b5c3..fe68f0592 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h
@@ -15,7 +15,7 @@
#import "RCTImageLoader.h"
typedef BOOL (^RCTImageURLLoaderCanLoadImageURLHandler)(NSURL *requestURL);
-typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler);
+typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler);
@interface RCTConcreteImageURLLoader : NSObject
@@ -26,7 +26,7 @@ typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(
@end
typedef BOOL (^RCTImageDataDecoderCanDecodeImageDataHandler)(NSData *imageData);
-typedef RCTImageLoaderCancellationBlock (^RCTImageDataDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
+typedef RCTImageLoaderCancellationBlock (^RCTImageDataDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
@interface RCTConcreteImageDecoder : NSObject
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m
index e41b09f3d..e3b4cb086 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m
@@ -47,7 +47,7 @@
return _canLoadImageURLHandler(requestURL);
}
-- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
+- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
}
@@ -92,7 +92,7 @@
return _canDecodeImageDataHandler(imageData);
}
-- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
+- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler);
}
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
index 04f367312..5713d0347 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
@@ -41,7 +41,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id loader = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
return YES;
- } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
+ } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
progressHandler(1, 1);
completionHandler(nil, image);
return nil;
@@ -50,7 +50,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader, imageLoader]; } launchOptions:nil];
- [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) {
+ [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
@@ -65,7 +65,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id loader1 = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
return YES;
- } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
+ } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
progressHandler(1, 1);
completionHandler(nil, image);
return nil;
@@ -73,7 +73,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id loader2 = [[RCTImageLoaderTestsURLLoader2 alloc] initWithPriority:0.5 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
return YES;
- } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) {
+ } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) {
XCTFail(@"Should not have used loader2");
return nil;
}];
@@ -81,7 +81,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2, imageLoader]; } launchOptions:nil];
- [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) {
+ [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
@@ -97,7 +97,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
- } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
+ } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
completionHandler(nil, image);
return nil;
@@ -106,7 +106,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder, imageLoader]; } launchOptions:nil];
- RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) {
+ RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
@@ -120,7 +120,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
- } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
+ } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
completionHandler(nil, image);
return nil;
@@ -128,7 +128,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
id decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
- } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
+ } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
XCTFail(@"Should not have used decoder2");
return nil;
}];
@@ -136,7 +136,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2, imageLoader]; } launchOptions:nil];
- RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) {
+ RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m
index f1effc8d6..19229d045 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m
@@ -46,19 +46,19 @@ RCTAssertEqualSizes(a.size, b.size); \
{
CGRect expected = {CGPointZero, {100, 20}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch);
RCTAssertEqualRects(expected, result);
}
{
- CGRect expected = {CGPointZero, {100, 10}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
+ CGRect expected = {{0, 5}, {100, 10}};
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain);
RCTAssertEqualRects(expected, result);
}
{
CGRect expected = {{-50, 0}, {200, 20}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover);
RCTAssertEqualRects(expected, result);
}
}
@@ -70,19 +70,19 @@ RCTAssertEqualSizes(a.size, b.size); \
{
CGRect expected = {CGPointZero, {100, 20}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch);
RCTAssertEqualRects(expected, result);
}
{
- CGRect expected = {CGPointZero, {2, 20}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
+ CGRect expected = {{49, 0}, {2, 20}};
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain);
RCTAssertEqualRects(expected, result);
}
{
CGRect expected = {{0, -490}, {100, 1000}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover);
RCTAssertEqualRects(expected, result);
}
}
@@ -94,19 +94,19 @@ RCTAssertEqualSizes(a.size, b.size); \
{
CGRect expected = {CGPointZero, {20, 50}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch);
RCTAssertEqualRects(expected, result);
}
{
- CGRect expected = {CGPointZero, {5, 50}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
+ CGRect expected = {{7,0}, {5, 50}};
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain);
RCTAssertEqualRects(expected, result);
}
{
CGRect expected = {{0, -75}, {20, 200}};
- CGRect result = RCTTargetRect(content, target, 2, UIViewContentModeScaleAspectFill);
+ CGRect result = RCTTargetRect(content, target, 2, RCTResizeModeCover);
RCTAssertEqualRects(expected, result);
}
}
@@ -118,7 +118,7 @@ RCTAssertEqualSizes(a.size, b.size); \
{
CGRect expected = {{0, -75}, {20, 200}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover);
RCTAssertEqualRects(expected, result);
}
}
@@ -129,7 +129,7 @@ RCTAssertEqualSizes(a.size, b.size); \
CGSize target = {3, 3};
CGRect expected = {CGPointZero, {3, 3}};
- CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
+ CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch);
RCTAssertEqualRects(expected, result);
}
diff --git a/Libraries/CameraRoll/RCTAssetsLibraryImageLoader.m b/Libraries/CameraRoll/RCTAssetsLibraryImageLoader.m
deleted file mode 100644
index f5cd3cb25..000000000
--- a/Libraries/CameraRoll/RCTAssetsLibraryImageLoader.m
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * 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.
- */
-
-#import "RCTAssetsLibraryImageLoader.h"
-
-#import
-#import
-#import
-#import
-
-#import "RCTBridge.h"
-#import "RCTConvert.h"
-#import "RCTImageLoader.h"
-#import "RCTImageUtils.h"
-#import "RCTUtils.h"
-
-static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void);
-
-@implementation RCTAssetsLibraryImageLoader
-{
- ALAssetsLibrary *_assetsLibrary;
-}
-
-RCT_EXPORT_MODULE()
-
-@synthesize bridge = _bridge;
-
-- (ALAssetsLibrary *)assetsLibrary
-{
- return _assetsLibrary ?: (_assetsLibrary = [ALAssetsLibrary new]);
-}
-
-#pragma mark - RCTImageLoader
-
-- (BOOL)canLoadImageURL:(NSURL *)requestURL
-{
- return [requestURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame;
-}
-
-- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
- progressHandler:(RCTImageLoaderProgressBlock)progressHandler
- completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
-{
- __block volatile uint32_t cancelled = 0;
-
- [[self assetsLibrary] assetForURL:imageURL resultBlock:^(ALAsset *asset) {
- if (cancelled) {
- return;
- }
-
- if (asset) {
- // ALAssetLibrary API is async and will be multi-threaded. Loading a few full
- // resolution images at once will spike the memory up to store the image data,
- // and might trigger memory warnings and/or OOM crashes.
- // To improve this, process the loaded asset in a serial queue.
- dispatch_async(RCTAssetsLibraryImageLoaderQueue(), ^{
- if (cancelled) {
- return;
- }
-
- // Also make sure the image is released immediately after it's used so it
- // doesn't spike the memory up during the process.
- @autoreleasepool {
- BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero);
- ALAssetRepresentation *representation = [asset defaultRepresentation];
-
-#if RCT_DEV
-
- CGSize sizeBeingLoaded = size;
- if (useMaximumSize) {
- CGSize pointSize = representation.dimensions;
- sizeBeingLoaded = CGSizeMake(pointSize.width * representation.scale, pointSize.height * representation.scale);
- }
-
- CGSize screenSize;
- if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedDescending) {
- screenSize = [UIScreen mainScreen].nativeBounds.size;
- } else {
- CGSize mainScreenSize = [UIScreen mainScreen].bounds.size;
- CGFloat mainScreenScale = [[UIScreen mainScreen] scale];
- screenSize = CGSizeMake(mainScreenSize.width * mainScreenScale, mainScreenSize.height * mainScreenScale);
- }
- CGFloat maximumPixelDimension = fmax(screenSize.width, screenSize.height);
-
- if (sizeBeingLoaded.width > maximumPixelDimension || sizeBeingLoaded.height > maximumPixelDimension) {
- RCTLogInfo(@"[PERF ASSETS] Loading %@ at size %@, which is larger than screen size %@",
- representation.filename, NSStringFromCGSize(sizeBeingLoaded), NSStringFromCGSize(screenSize));
- }
-
-#endif
-
- UIImage *image = nil;
- NSError *error = nil;
- if (useMaximumSize) {
-
- image = [UIImage imageWithCGImage:representation.fullResolutionImage
- scale:scale
- orientation:(UIImageOrientation)representation.orientation];
- } else {
-
- NSUInteger length = (NSUInteger)representation.size;
- uint8_t *buffer = (uint8_t *)malloc((size_t)length);
- if ([representation getBytes:buffer
- fromOffset:0
- length:length
- error:&error]) {
-
- NSData *data = [[NSData alloc] initWithBytesNoCopy:buffer
- length:length
- freeWhenDone:YES];
-
- image = RCTDecodeImageWithData(data, size, scale, resizeMode);
- } else {
- free(buffer);
- }
- }
-
- completionHandler(error, image);
- }
- });
- } else {
- NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL];
- completionHandler(RCTErrorWithMessage(errorText), nil);
- }
- } failureBlock:^(NSError *loadError) {
- if (cancelled) {
- return;
- }
-
- NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError];
- completionHandler(RCTErrorWithMessage(errorText), nil);
- }];
-
- return ^{
- OSAtomicOr32Barrier(1, &cancelled);
- };
-}
-
-@end
-
-@implementation RCTBridge (RCTAssetsLibraryImageLoader)
-
-- (ALAssetsLibrary *)assetsLibrary
-{
- return [[self moduleForClass:[RCTAssetsLibraryImageLoader class]] assetsLibrary];
-}
-
-@end
-
-static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void)
-{
- static dispatch_queue_t queue;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- queue = dispatch_queue_create("com.facebook.RCTAssetsLibraryImageLoader", DISPATCH_QUEUE_SERIAL);
- });
-
- return queue;
-}
diff --git a/Libraries/CameraRoll/RCTAssetsLibraryImageLoader.h b/Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.h
similarity index 75%
rename from Libraries/CameraRoll/RCTAssetsLibraryImageLoader.h
rename to Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.h
index 0cc1c5353..4ce68bc3b 100644
--- a/Libraries/CameraRoll/RCTAssetsLibraryImageLoader.h
+++ b/Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.h
@@ -7,9 +7,12 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-#import "RCTImageLoader.h"
+#import "RCTBridge.h"
+#import "RCTURLRequestHandler.h"
-@interface RCTAssetsLibraryImageLoader : NSObject
+@class ALAssetsLibrary;
+
+@interface RCTAssetsLibraryRequestHandler : NSObject
@end
diff --git a/Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.m b/Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.m
new file mode 100644
index 000000000..d818f336d
--- /dev/null
+++ b/Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.m
@@ -0,0 +1,114 @@
+/**
+ * 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.
+ */
+
+#import "RCTAssetsLibraryRequestHandler.h"
+
+#import
+#import
+
+#import "RCTBridge.h"
+#import "RCTUtils.h"
+
+@implementation RCTAssetsLibraryRequestHandler
+{
+ ALAssetsLibrary *_assetsLibrary;
+}
+
+RCT_EXPORT_MODULE()
+
+@synthesize bridge = _bridge;
+
+- (ALAssetsLibrary *)assetsLibrary
+{
+ return _assetsLibrary ?: (_assetsLibrary = [ALAssetsLibrary new]);
+}
+
+#pragma mark - RCTURLRequestHandler
+
+- (BOOL)canHandleRequest:(NSURLRequest *)request
+{
+ return [request.URL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame;
+}
+
+- (id)sendRequest:(NSURLRequest *)request
+ withDelegate:(id)delegate
+{
+ __block volatile uint32_t cancelled = 0;
+ void (^cancellationBlock)(void) = ^{
+ OSAtomicOr32Barrier(1, &cancelled);
+ };
+
+ [[self assetsLibrary] assetForURL:request.URL resultBlock:^(ALAsset *asset) {
+ if (cancelled) {
+ return;
+ }
+
+ if (asset) {
+
+ ALAssetRepresentation *representation = [asset defaultRepresentation];
+ NSInteger length = (NSInteger)representation.size;
+
+ NSURLResponse *response =
+ [[NSURLResponse alloc] initWithURL:request.URL
+ MIMEType:representation.UTI
+ expectedContentLength:length
+ textEncodingName:nil];
+
+ [delegate URLRequest:cancellationBlock didReceiveResponse:response];
+
+ NSError *error = nil;
+ uint8_t *buffer = (uint8_t *)malloc((size_t)length);
+ if ([representation getBytes:buffer
+ fromOffset:0
+ length:length
+ error:&error]) {
+
+ NSData *data = [[NSData alloc] initWithBytesNoCopy:buffer
+ length:length
+ freeWhenDone:YES];
+
+ [delegate URLRequest:cancellationBlock didReceiveData:data];
+ [delegate URLRequest:cancellationBlock didCompleteWithError:nil];
+
+ } else {
+ free(buffer);
+ [delegate URLRequest:cancellationBlock didCompleteWithError:error];
+ }
+
+ } else {
+ NSString *errorMessage = [NSString stringWithFormat:@"Failed to load asset"
+ " at URL %@ with no error message.", request.URL];
+ NSError *error = RCTErrorWithMessage(errorMessage);
+ [delegate URLRequest:cancellationBlock didCompleteWithError:error];
+ }
+ } failureBlock:^(NSError *loadError) {
+ if (cancelled) {
+ return;
+ }
+ [delegate URLRequest:cancellationBlock didCompleteWithError:loadError];
+ }];
+
+ return cancellationBlock;
+}
+
+- (void)cancelRequest:(id)requestToken
+{
+ ((void (^)(void))requestToken)();
+}
+
+@end
+
+@implementation RCTBridge (RCTAssetsLibraryImageLoader)
+
+- (ALAssetsLibrary *)assetsLibrary
+{
+ return [[self moduleForClass:[RCTAssetsLibraryRequestHandler class]] assetsLibrary];
+}
+
+@end
diff --git a/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj b/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj
index 56cced214..a8bb79d92 100644
--- a/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj
+++ b/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj
@@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
- 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; };
+ 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m */; };
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; };
/* End PBXBuildFile section */
@@ -31,8 +31,8 @@
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = ""; };
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = ""; };
58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTCameraRoll.a; sourceTree = BUILT_PRODUCTS_DIR; };
- 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = ""; };
- 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = ""; };
+ 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryRequestHandler.h; sourceTree = ""; };
+ 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryRequestHandler.m; sourceTree = ""; };
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = ""; };
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = ""; };
/* End PBXFileReference section */
@@ -51,8 +51,8 @@
58B511541A9E6B3D00147676 = {
isa = PBXGroup;
children = (
- 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */,
- 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */,
+ 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.h */,
+ 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m */,
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
137620331B31C53500677FF0 /* RCTImagePickerManager.h */,
@@ -129,7 +129,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */,
+ 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m in Sources */,
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */,
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
diff --git a/Libraries/CameraRoll/RCTCameraRollManager.m b/Libraries/CameraRoll/RCTCameraRollManager.m
index b1c285186..ad4ad1713 100644
--- a/Libraries/CameraRoll/RCTCameraRollManager.m
+++ b/Libraries/CameraRoll/RCTCameraRollManager.m
@@ -13,7 +13,7 @@
#import
#import
-#import "RCTAssetsLibraryImageLoader.h"
+#import "RCTAssetsLibraryRequestHandler.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTImageLoader.h"
diff --git a/Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m b/Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m
index 33a9cb31b..3cb4d1c3b 100644
--- a/Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m
+++ b/Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m
@@ -30,7 +30,7 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
@@ -71,7 +71,7 @@ RCT_EXPORT_MODULE()
}
PHImageContentMode contentMode = PHImageContentModeAspectFill;
- if (resizeMode == UIViewContentModeScaleAspectFit) {
+ if (resizeMode == RCTResizeModeContain) {
contentMode = PHImageContentModeAspectFit;
}
diff --git a/Libraries/Image/RCTGIFImageDecoder.m b/Libraries/Image/RCTGIFImageDecoder.m
index b90375856..e578421a7 100644
--- a/Libraries/Image/RCTGIFImageDecoder.m
+++ b/Libraries/Image/RCTGIFImageDecoder.m
@@ -30,7 +30,7 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
index 0c404f31c..8c9c61861 100644
--- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
+++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
@@ -11,9 +11,10 @@
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
- 13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */; settings = {ASSET_TAGS = (); }; };
- 13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */; settings = {ASSET_TAGS = (); }; };
- 13EF7F7F1BC825B1003F47DD /* RCTXCAssetImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */; settings = {ASSET_TAGS = (); }; };
+ 139A38841C4D587C00862840 /* RCTResizeMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 139A38831C4D587C00862840 /* RCTResizeMode.m */; };
+ 13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */; };
+ 13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */; };
+ 13EF7F7F1BC825B1003F47DD /* RCTXCAssetImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */; };
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
@@ -40,6 +41,8 @@
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = ""; };
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = ""; };
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = ""; };
+ 139A38821C4D57AD00862840 /* RCTResizeMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTResizeMode.h; sourceTree = ""; };
+ 139A38831C4D587C00862840 /* RCTResizeMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTResizeMode.m; sourceTree = ""; };
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowVirtualImage.h; sourceTree = ""; };
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowVirtualImage.m; sourceTree = ""; };
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualImageManager.h; sourceTree = ""; };
@@ -69,6 +72,8 @@
58B511541A9E6B3D00147676 = {
isa = PBXGroup;
children = (
+ 139A38821C4D57AD00862840 /* RCTResizeMode.h */,
+ 139A38831C4D587C00862840 /* RCTResizeMode.m */,
13EF7F7D1BC825B1003F47DD /* RCTXCAssetImageLoader.h */,
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
@@ -165,6 +170,7 @@
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
+ 139A38841C4D587C00862840 /* RCTResizeMode.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */,
13EF7F7F1BC825B1003F47DD /* RCTXCAssetImageLoader.m in Sources */,
diff --git a/Libraries/Image/RCTImageEditingManager.m b/Libraries/Image/RCTImageEditingManager.m
index 76fbc2703..2a2ea9671 100644
--- a/Libraries/Image/RCTImageEditingManager.m
+++ b/Libraries/Image/RCTImageEditingManager.m
@@ -14,6 +14,7 @@
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTUtils.h"
+#import "RCTImageUtils.h"
#import "RCTImageStoreManager.h"
#import "RCTImageLoader.h"
@@ -44,8 +45,6 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
[RCTConvert CGSize:cropData[@"size"]]
};
- NSString *resizeMode = cropData[@"resizeMode"] ?: @"contain";
-
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image) {
if (error) {
errorCallback(error);
@@ -53,17 +52,21 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
}
// Crop image
- CGRect rectToDrawIn = {{-rect.origin.x, -rect.origin.y}, image.size};
- UIGraphicsBeginImageContextWithOptions(rect.size, !RCTImageHasAlpha(image.CGImage), image.scale);
- [image drawInRect:rectToDrawIn];
- UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
+ CGSize targetSize = rect.size;
+ CGRect targetRect = {{-rect.origin.x, -rect.origin.y}, image.size};
+ CGAffineTransform transform = RCTTransformFromTargetRect(image.size, targetRect);
+ UIImage *croppedImage = RCTTransformImage(image, targetSize, image.scale, transform);
+ // Scale image
if (cropData[@"displaySize"]) {
- CGSize targetSize = [RCTConvert CGSize:cropData[@"displaySize"]];
- croppedImage = [self scaleImage:croppedImage targetSize:targetSize resizeMode:resizeMode];
+ targetSize = [RCTConvert CGSize:cropData[@"displaySize"]]; // in pixels
+ RCTResizeMode resizeMode = [RCTConvert RCTResizeMode:cropData[@"resizeMode"] ?: @"contain"];
+ targetRect = RCTTargetRect(croppedImage.size, targetSize, 1, resizeMode);
+ transform = RCTTransformFromTargetRect(image.size, targetRect);
+ croppedImage = RCTTransformImage(image, targetSize, image.scale, transform);
}
+ // Store image
[_bridge.imageStoreManager storeImage:croppedImage withBlock:^(NSString *croppedImageTag) {
if (!croppedImageTag) {
NSString *errorMessage = @"Error storing cropped image in RCTImageStoreManager";
@@ -76,49 +79,4 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
}];
}
-- (UIImage *)scaleImage:(UIImage *)image targetSize:(CGSize)targetSize resizeMode:(NSString *)resizeMode
-{
- if (CGSizeEqualToSize(image.size, targetSize)) {
- return image;
- }
-
- CGFloat imageRatio = image.size.width / image.size.height;
- CGFloat targetRatio = targetSize.width / targetSize.height;
-
- CGFloat newWidth = targetSize.width;
- CGFloat newHeight = targetSize.height;
-
- // contain vs cover
- // http://blog.vjeux.com/2013/image/css-container-and-cover.html
- if ([resizeMode isEqualToString:@"contain"]) {
- if (imageRatio <= targetRatio) {
- newWidth = targetSize.height * imageRatio;
- newHeight = targetSize.height;
- } else {
- newWidth = targetSize.width;
- newHeight = targetSize.width / imageRatio;
- }
- } else if ([resizeMode isEqualToString:@"cover"]) {
- if (imageRatio <= targetRatio) {
- newWidth = targetSize.width;
- newHeight = targetSize.width / imageRatio;
- } else {
- newWidth = targetSize.height * imageRatio;
- newHeight = targetSize.height;
- }
- } // else assume we're stretching the image
-
- // prevent upscaling
- newWidth = MIN(newWidth, image.size.width);
- newHeight = MIN(newHeight, image.size.height);
-
- // perform the scaling @1x because targetSize is in actual pixel width/height
- UIGraphicsBeginImageContextWithOptions(targetSize, NO, 1.0f);
- [image drawInRect:CGRectMake(0.f, 0.f, newWidth, newHeight)];
- UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- return scaledImage;
-}
-
@end
diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h
index c9bea5437..e7805b135 100644
--- a/Libraries/Image/RCTImageLoader.h
+++ b/Libraries/Image/RCTImageLoader.h
@@ -11,6 +11,7 @@
#import "RCTBridge.h"
#import "RCTURLRequestHandler.h"
+#import "RCTResizeMode.h"
@class ALAssetsLibrary;
@@ -40,10 +41,20 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
+/**
+ * Loads an image without clipping the result to fit - used by RCTImageView.
+ */
+- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
+
/**
* Finds an appropriate image decoder and passes the target size, scale and
* resizeMode for optimal image decoding. Can be called from any thread,
@@ -52,7 +63,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
/**
@@ -96,7 +107,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
@@ -134,7 +145,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
@optional
diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m
index cd681b113..0d7b06f5c 100644
--- a/Libraries/Image/RCTImageLoader.m
+++ b/Libraries/Image/RCTImageLoader.m
@@ -173,13 +173,31 @@ RCT_EXPORT_MODULE()
return nil;
}
+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;
+}
+
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
callback:(RCTImageLoaderCompletionBlock)callback
{
return [self loadImageWithTag:imageTag
size:CGSizeZero
scale:1
- resizeMode:UIViewContentModeScaleToFill
+ resizeMode:RCTResizeModeStretch
progressBlock:nil
completionBlock:callback];
}
@@ -192,7 +210,7 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(void (^)(NSError *error, id imageOrData))completionBlock
{
@@ -352,9 +370,26 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ return [self loadImageWithoutClipping:imageTag
+ size:size
+ scale:scale
+ resizeMode:resizeMode
+ progressBlock:progressHandler
+ completionBlock:^(NSError *error, UIImage *image) {
+ completionBlock(error, RCTResizeImageIfNeeded(image, size, scale, resizeMode));
+ }];
+}
+
+- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressHandler
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
__block volatile uint32_t cancelled = 0;
__block void(^cancelLoad)(void) = nil;
@@ -365,11 +400,11 @@ RCT_EXPORT_MODULE()
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
completionBlock(error, imageOrData);
} else {
- cancelLoad = [weakSelf decodeImageData:imageOrData
- size:size
- scale:scale
- resizeMode:resizeMode
- completionBlock:completionBlock] ?: ^{};
+ cancelLoad = [weakSelf decodeImageDataWithoutClipping:imageOrData
+ size:size
+ scale:scale
+ resizeMode:resizeMode
+ completionBlock:completionBlock] ?: ^{};
}
}
};
@@ -391,17 +426,47 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
- completionBlock:(RCTImageLoaderCompletionBlock)completionHandler
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ return [self decodeImageDataWithoutClipping:data
+ size:size
+ scale:scale
+ resizeMode:resizeMode
+ completionBlock:^(NSError *error, UIImage *image) {
+ completionBlock(error, RCTResizeImageIfNeeded(image, size, scale, resizeMode));
+ }];
+}
+
+- (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)data
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
if (data.length == 0) {
- completionHandler(RCTErrorWithMessage(@"No image data"), nil);
+ completionBlock(RCTErrorWithMessage(@"No image data"), nil);
return ^{};
}
+ __block volatile uint32_t cancelled = 0;
+ void (^completionHandler)(NSError *, UIImage *) = ^(NSError *error, UIImage *image) {
+ if ([NSThread isMainThread]) {
+
+ // 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) {
+ completionBlock(error, image);
+ }
+ });
+ } else if (!cancelled) {
+ completionBlock(error, image);
+ }
+ };
+
id imageDecoder = [self imageDataDecoderForData:data];
if (imageDecoder) {
-
return [imageDecoder decodeImageData:data
size:size
scale:scale
@@ -409,12 +474,26 @@ RCT_EXPORT_MODULE()
completionHandler:completionHandler];
} else {
- __block volatile uint32_t cancelled = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (cancelled) {
return;
}
+
UIImage *image = RCTDecodeImageWithData(data, size, scale, resizeMode);
+
+#if RCT_DEV
+
+ 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));
+ }
+
+#endif
+
if (image) {
completionHandler(nil, image);
} else {
@@ -436,7 +515,7 @@ RCT_EXPORT_MODULE()
return [self loadImageOrDataWithTag:imageTag
size:CGSizeZero
scale:1
- resizeMode:UIViewContentModeScaleToFill
+ resizeMode:RCTResizeModeStretch
progressBlock:nil
completionBlock:^(NSError *error, id imageOrData) {
CGSize size;
@@ -461,7 +540,17 @@ RCT_EXPORT_MODULE()
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
- return [self imageURLLoaderForURL:request.URL] != nil;
+ NSURL *requestURL = request.URL;
+ for (id 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;
}
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate
diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m
index 1827bd285..4c6441a64 100644
--- a/Libraries/Image/RCTImageStoreManager.m
+++ b/Libraries/Image/RCTImageStoreManager.m
@@ -217,6 +217,7 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String
dispatch_async(_methodQueue, ^{
NSData *imageData = _store[imageTag];
dispatch_async(dispatch_get_main_queue(), ^{
+ // imageWithData: is not thread-safe, so we can't do this on methodQueue
block([UIImage imageWithData:imageData]);
});
});
diff --git a/Libraries/Image/RCTImageUtils.h b/Libraries/Image/RCTImageUtils.h
index cb65449db..f870bda67 100644
--- a/Libraries/Image/RCTImageUtils.h
+++ b/Libraries/Image/RCTImageUtils.h
@@ -11,28 +11,36 @@
#import
#import "RCTDefines.h"
+#import "RCTResizeMode.h"
NS_ASSUME_NONNULL_BEGIN
/**
- * This function takes an input content size (typically from an image), a target
- * size and scale that it will be drawn at (typically in a CGContext) and then
+ * This function takes an source size (typically from an image), a target size
+ * and scale that it will be drawn at (typically in a CGContext) and then
* calculates the rectangle to draw the image into so that it will be sized and
- * positioned correctly if drawn using the specified content mode.
+ * positioned correctly according to the specified resizeMode.
*/
RCT_EXTERN CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
- CGFloat destScale, UIViewContentMode resizeMode);
+ CGFloat destScale, RCTResizeMode resizeMode);
+
+/**
+ * This function takes a source size (typically from an image), a target rect
+ * that it will be drawn into (typically relative to a CGContext), and works out
+ * the transform needed to draw the image at the correct scale and position.
+ */
+RCT_EXTERN CGAffineTransform RCTTransformFromTargetRect(CGSize sourceSize,
+ CGRect targetRect);
/**
* This function takes an input content size & scale (typically from an image),
* a target size & scale at which it will be displayed (typically in a
* UIImageView) and then calculates the optimal size at which to redraw the
- * image so that it will be displayed correctly with the specified content mode.
+ * image so that it will be displayed correctly with the specified resizeMode.
*/
RCT_EXTERN CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
- UIViewContentMode resizeMode,
- BOOL allowUpscaling);
+ RCTResizeMode resizeMode, BOOL allowUpscaling);
/**
* This function takes an input content size & scale (typically from an image),
@@ -41,12 +49,7 @@ RCT_EXTERN CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
*/
RCT_EXTERN BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
- UIViewContentMode resizeMode);
-/**
- * This function takes a source size and scale and returns the size in pixels.
- * Note that the pixel width/height is rounded up to the nearest integral size.
- */
-RCT_EXTERN CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale);
+ RCTResizeMode resizeMode);
/**
* This function takes the source data for an image and decodes it at the
@@ -58,7 +61,7 @@ RCT_EXTERN CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale);
RCT_EXTERN UIImage *__nullable RCTDecodeImageWithData(NSData *data,
CGSize destSize,
CGFloat destScale,
- UIViewContentMode resizeMode);
+ RCTResizeMode resizeMode);
/**
* This function takes the source data for an image and decodes just the
@@ -75,4 +78,19 @@ RCT_EXTERN NSDictionary *__nullable RCTGetImageMetadata(NSData *
*/
RCT_EXTERN NSData *__nullable RCTGetImageData(CGImageRef image, float quality);
+/**
+ * This function transforms an image. `destSize` is the size of the final image,
+ * and `destScale` is its scale. The `transform` argument controls how the
+ * source image will be mapped to the destination image.
+ */
+RCT_EXTERN UIImage *__nullable RCTTransformImage(UIImage *image,
+ CGSize destSize,
+ CGFloat destScale,
+ CGAffineTransform transform);
+
+/*
+ * Return YES if image has an alpha component
+ */
+RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image);
+
NS_ASSUME_NONNULL_END
diff --git a/Libraries/Image/RCTImageUtils.m b/Libraries/Image/RCTImageUtils.m
index 0b51eca72..d484ea238 100644
--- a/Libraries/Image/RCTImageUtils.m
+++ b/Libraries/Image/RCTImageUtils.m
@@ -35,7 +35,7 @@ static CGSize RCTCeilSize(CGSize size, CGFloat scale)
}
CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
- CGFloat destScale, UIViewContentMode resizeMode)
+ CGFloat destScale, RCTResizeMode resizeMode)
{
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
// Assume we require the largest size available
@@ -58,16 +58,16 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
if (resizeMode != UIViewContentModeScaleToFill) {
targetAspect = destSize.width / destSize.height;
if (aspect == targetAspect) {
- resizeMode = UIViewContentModeScaleToFill;
+ resizeMode = RCTResizeModeStretch;
}
}
switch (resizeMode) {
- case UIViewContentModeScaleToFill: // stretch
+ case RCTResizeModeStretch:
return (CGRect){CGPointZero, RCTCeilSize(destSize, destScale)};
- case UIViewContentModeScaleAspectFit: // contain
+ case RCTResizeModeContain:
if (targetAspect <= aspect) { // target is taller than content
@@ -79,9 +79,15 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
sourceSize.height = destSize.height = destSize.height;
sourceSize.width = sourceSize.height * aspect;
}
- return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)};
+ return (CGRect){
+ {
+ RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale),
+ RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale),
+ },
+ RCTCeilSize(sourceSize, destScale)
+ };
- case UIViewContentModeScaleAspectFill: // cover
+ case RCTResizeModeCover:
if (targetAspect <= aspect) { // target is taller than content
@@ -103,21 +109,28 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
RCTCeilSize(sourceSize, destScale)
};
}
-
- default:
-
- RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
- return (CGRect){CGPointZero, RCTCeilSize(destSize, destScale)};
}
}
+CGAffineTransform RCTTransformFromTargetRect(CGSize sourceSize, CGRect targetRect)
+{
+ CGAffineTransform transform = CGAffineTransformIdentity;
+ transform = CGAffineTransformTranslate(transform,
+ targetRect.origin.x,
+ targetRect.origin.y);
+ transform = CGAffineTransformScale(transform,
+ targetRect.size.width / sourceSize.width,
+ targetRect.size.height / sourceSize.height);
+ return transform;
+}
+
CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
- UIViewContentMode resizeMode,
+ RCTResizeMode resizeMode,
BOOL allowUpscaling)
{
switch (resizeMode) {
- case UIViewContentModeScaleToFill: // stretch
+ case RCTResizeModeStretch:
if (!allowUpscaling) {
CGFloat scale = sourceScale / destScale;
@@ -143,7 +156,7 @@ CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
- UIViewContentMode resizeMode)
+ RCTResizeMode resizeMode)
{
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
// Assume we require the largest size available
@@ -161,16 +174,16 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
aspect = sourceSize.width / sourceSize.height;
targetAspect = destSize.width / destSize.height;
if (aspect == targetAspect) {
- resizeMode = UIViewContentModeScaleToFill;
+ resizeMode = RCTResizeModeStretch;
}
}
switch (resizeMode) {
- case UIViewContentModeScaleToFill: // stretch
+ case RCTResizeModeStretch:
return destSize.width > sourceSize.width || destSize.height > sourceSize.height;
- case UIViewContentModeScaleAspectFit: // contain
+ case RCTResizeModeContain:
if (targetAspect <= aspect) { // target is taller than content
@@ -181,7 +194,7 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
return destSize.height > sourceSize.height;
}
- case UIViewContentModeScaleAspectFill: // cover
+ case RCTResizeModeCover:
if (targetAspect <= aspect) { // target is taller than content
@@ -191,33 +204,20 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
return destSize.width > sourceSize.width;
}
-
- default:
-
- RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
- return NO;
}
}
-CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale)
-{
- return (CGSize){
- ceil(pointSize.width * scale),
- ceil(pointSize.height * scale),
- };
-}
-
UIImage *__nullable RCTDecodeImageWithData(NSData *data,
CGSize destSize,
CGFloat destScale,
- UIViewContentMode resizeMode)
+ RCTResizeMode resizeMode)
{
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
if (!sourceRef) {
return nil;
}
- // get original image size
+ // Get original image size
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(sourceRef, 0, NULL);
if (!imageProperties) {
CFRelease(sourceRef);
@@ -237,8 +237,14 @@ UIImage *__nullable RCTDecodeImageWithData(NSData *data,
destScale = RCTScreenScale();
}
- // calculate target size
- CGSize targetSize = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, YES);
+ if (resizeMode == UIViewContentModeScaleToFill) {
+ // Decoder cannot change aspect ratio, so RCTResizeModeStretch is equivalent
+ // to RCTResizeModeCover for our purposes
+ resizeMode = RCTResizeModeCover;
+ }
+
+ // Calculate target size
+ CGSize targetSize = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, NO);
CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
CGFloat maxPixelSize = fmax(fmin(sourceSize.width, targetPixelSize.width),
fmin(sourceSize.height, targetPixelSize.height));
@@ -250,14 +256,14 @@ UIImage *__nullable RCTDecodeImageWithData(NSData *data,
(id)kCGImageSourceThumbnailMaxPixelSize: @(maxPixelSize),
};
- // get thumbnail
+ // Get thumbnail
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
CFRelease(sourceRef);
if (!imageRef) {
return nil;
}
- // return image
+ // Return image
UIImage *image = [UIImage imageWithCGImage:imageRef
scale:destScale
orientation:UIImageOrientationUp];
@@ -298,3 +304,33 @@ NSData *__nullable RCTGetImageData(CGImageRef image, float quality)
CFRelease(destination);
return (__bridge_transfer NSData *)imageData;
}
+
+UIImage *__nullable RCTTransformImage(UIImage *image,
+ CGSize destSize,
+ CGFloat destScale,
+ CGAffineTransform transform)
+{
+ if (destSize.width <= 0 | destSize.height <= 0 || destScale <= 0) {
+ return nil;
+ }
+
+ UIGraphicsBeginImageContextWithOptions(destSize, NO, destScale);
+ CGContextRef currentContext = UIGraphicsGetCurrentContext();
+ CGContextConcatCTM(currentContext, transform);
+ [image drawAtPoint:CGPointZero];
+ UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsGetCurrentContext();
+ return result;
+}
+
+BOOL RCTImageHasAlpha(CGImageRef image)
+{
+ switch (CGImageGetAlphaInfo(image)) {
+ case kCGImageAlphaNone:
+ case kCGImageAlphaNoneSkipLast:
+ case kCGImageAlphaNoneSkipFirst:
+ return NO;
+ default:
+ return YES;
+ }
+}
diff --git a/Libraries/Image/RCTImageView.h b/Libraries/Image/RCTImageView.h
index 8b5b993a7..769f9c965 100644
--- a/Libraries/Image/RCTImageView.h
+++ b/Libraries/Image/RCTImageView.h
@@ -9,6 +9,7 @@
#import
#import "RCTImageComponent.h"
+#import "RCTResizeMode.h"
@class RCTBridge;
@class RCTImageSource;
diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m
index 712f7e7a5..36506d348 100644
--- a/Libraries/Image/RCTImageView.m
+++ b/Libraries/Image/RCTImageView.m
@@ -103,8 +103,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)setCapInsets:(UIEdgeInsets)capInsets
{
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, capInsets)) {
- _capInsets = capInsets;
- [self updateImage];
+ if (UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero) ||
+ UIEdgeInsetsEqualToEdgeInsets(capInsets, UIEdgeInsetsZero)) {
+ _capInsets = capInsets;
+ // Need to reload image when enabling or disabling capInsets
+ [self reloadImage];
+ } else {
+ _capInsets = capInsets;
+ [self updateImage];
+ }
}
}
@@ -126,12 +133,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)sourceNeedsReload
{
- NSString *scheme = _source.imageURL.scheme;
- return
- [scheme isEqualToString:@"http"] ||
- [scheme isEqualToString:@"https"] ||
- [scheme isEqualToString:@"assets-library"] ||
- [scheme isEqualToString:@"ph"];
+ // If capInsets are set, image doesn't need reloading when resized
+ return UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero);
}
- (void)setContentMode:(UIViewContentMode)contentMode
@@ -179,14 +182,22 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
};
}
+ CGSize imageSize = self.bounds.size;
+ CGFloat imageScale = RCTScreenScale();
+ if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero)) {
+ // Don't resize images that use capInsets
+ imageSize = CGSizeZero;
+ imageScale = _source.scale;
+ }
+
RCTImageSource *source = _source;
__weak RCTImageView *weakSelf = self;
- _reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithTag:_source.imageURL.absoluteString
- size:self.bounds.size
- scale:RCTScreenScale()
- resizeMode:self.contentMode
- progressBlock:progressHandler
- completionBlock:^(NSError *error, UIImage *image) {
+ _reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithoutClipping:_source.imageURL.absoluteString
+ size:imageSize
+ scale:imageScale
+ resizeMode:(RCTResizeMode)self.contentMode
+ progressBlock:progressHandler
+ completionBlock:^(NSError *error, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
RCTImageView *strongSelf = weakSelf;
if (![source isEqual:strongSelf.source]) {
@@ -227,14 +238,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
[self reloadImage];
} else if ([self sourceNeedsReload]) {
CGSize imageSize = self.image.size;
- CGSize idealSize = RCTTargetSize(imageSize, self.image.scale, frame.size, RCTScreenScale(), self.contentMode, YES);
+ CGSize idealSize = RCTTargetSize(imageSize, self.image.scale, frame.size,
+ RCTScreenScale(), (RCTResizeMode)self.contentMode, YES);
if (RCTShouldReloadImageForSizeChange(imageSize, idealSize)) {
if (RCTShouldReloadImageForSizeChange(_targetSize, idealSize)) {
RCTLogInfo(@"[PERF IMAGEVIEW] Reloading image %@ as size %@", _source.imageURL, NSStringFromCGSize(idealSize));
- // If the existing image or an image being loaded are not the right size, reload the asset in case there is a
- // better size available.
+ // If the existing image or an image being loaded are not the right
+ // size, reload the asset in case there is a better size available.
_targetSize = idealSize;
[self reloadImage];
}
@@ -251,8 +263,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
[super didMoveToWindow];
if (!self.window) {
- // Don't keep self alive through the asynchronous dispatch, if the intention was to remove the view so it would
- // deallocate.
+ // Don't keep self alive through the asynchronous dispatch, if the intention
+ // was to remove the view so it would deallocate.
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
@@ -261,7 +273,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
return;
}
- // If we haven't been re-added to a window by this run loop iteration, clear out the image to save memory.
+ // If we haven't been re-added to a window by this run loop iteration,
+ // clear out the image to save memory.
if (!strongSelf.window) {
[strongSelf clearImage];
}
diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m
index 7ef593195..65e18b5a2 100644
--- a/Libraries/Image/RCTImageViewManager.m
+++ b/Libraries/Image/RCTImageViewManager.m
@@ -32,7 +32,7 @@ RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock)
-RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
+RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, RCTResizeMode)
RCT_EXPORT_VIEW_PROPERTY(source, RCTImageSource)
RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView)
{
diff --git a/Libraries/Image/RCTResizeMode.h b/Libraries/Image/RCTResizeMode.h
new file mode 100644
index 000000000..81e9d925f
--- /dev/null
+++ b/Libraries/Image/RCTResizeMode.h
@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+#import "RCTConvert.h"
+
+typedef NS_ENUM(NSInteger, RCTResizeMode) {
+ RCTResizeModeCover = UIViewContentModeScaleAspectFill,
+ RCTResizeModeContain = UIViewContentModeScaleAspectFit,
+ RCTResizeModeStretch = UIViewContentModeScaleToFill,
+};
+
+@interface RCTConvert(RCTResizeMode)
+
++ (RCTResizeMode)RCTResizeMode:(id)json;
+
+@end
diff --git a/Libraries/Image/RCTResizeMode.m b/Libraries/Image/RCTResizeMode.m
new file mode 100644
index 000000000..b80839bb4
--- /dev/null
+++ b/Libraries/Image/RCTResizeMode.m
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+#import "RCTResizeMode.h"
+
+@implementation RCTConvert(RCTResizeMode)
+
+RCT_ENUM_CONVERTER(RCTResizeMode, (@{
+ @"cover": @(RCTResizeModeCover),
+ @"contain": @(RCTResizeModeContain),
+ @"stretch": @(RCTResizeModeStretch),
+}), RCTResizeModeStretch, integerValue)
+
+@end
diff --git a/Libraries/Image/RCTShadowVirtualImage.m b/Libraries/Image/RCTShadowVirtualImage.m
index f3486f9a2..d6b0153d1 100644
--- a/Libraries/Image/RCTShadowVirtualImage.m
+++ b/Libraries/Image/RCTShadowVirtualImage.m
@@ -46,7 +46,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
_cancellationBlock = [_bridge.imageLoader loadImageWithTag:source.imageURL.absoluteString
size:source.size
scale:source.scale
- resizeMode:UIViewContentModeScaleToFill
+ resizeMode:RCTResizeModeStretch
progressBlock:nil
completionBlock:^(NSError *error, UIImage *image) {
diff --git a/Libraries/Image/RCTXCAssetImageLoader.m b/Libraries/Image/RCTXCAssetImageLoader.m
index 799798a36..750a1f848 100644
--- a/Libraries/Image/RCTXCAssetImageLoader.m
+++ b/Libraries/Image/RCTXCAssetImageLoader.m
@@ -25,7 +25,7 @@ RCT_EXPORT_MODULE()
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
+ resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h
index 36383e8c6..7c8aaa511 100644
--- a/React/Base/RCTUtils.h
+++ b/React/Base/RCTUtils.h
@@ -40,6 +40,9 @@ RCT_EXTERN CGFloat RCTRoundPixelValue(CGFloat value);
RCT_EXTERN CGFloat RCTCeilPixelValue(CGFloat value);
RCT_EXTERN CGFloat RCTFloorPixelValue(CGFloat value);
+// Convert a size in points to pixels, rounded up to the nearest integral size
+RCT_EXTERN CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale);
+
// Method swizzling
RCT_EXTERN void RCTSwapClassMethods(Class cls, SEL original, SEL replacement);
RCT_EXTERN void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
@@ -78,9 +81,6 @@ RCT_EXTERN UIAlertView *RCTAlertView(NSString *title,
NSString *cancelButtonTitle,
NSArray *otherButtonTitles);
-// Return YES if image has an alpha component
-RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image);
-
// Create an NSError in the RCTErrorDomain
RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);
diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m
index cf0d8b8b0..b13b3f951 100644
--- a/React/Base/RCTUtils.m
+++ b/React/Base/RCTUtils.m
@@ -219,7 +219,13 @@ CGSize RCTScreenSize()
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTExecuteOnMainThread(^{
- size = [UIScreen mainScreen].bounds.size;
+ if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedDescending) {
+ CGSize pixelSize = [UIScreen mainScreen].nativeBounds.size;
+ CGFloat scale = RCTScreenScale();
+ size = (CGSize){pixelSize.width / scale, pixelSize.height / scale};
+ } else {
+ size = [UIScreen mainScreen].bounds.size;
+ }
}, YES);
});
@@ -244,6 +250,14 @@ CGFloat RCTFloorPixelValue(CGFloat value)
return floor(value * scale) / scale;
}
+CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale)
+{
+ return (CGSize){
+ ceil(pointSize.width * scale),
+ ceil(pointSize.height * scale),
+ };
+}
+
void RCTSwapClassMethods(Class cls, SEL original, SEL replacement)
{
Method originalMethod = class_getClassMethod(cls, original);
@@ -401,18 +415,6 @@ UIAlertView *RCTAlertView(NSString *title,
return alertView;
}
-BOOL RCTImageHasAlpha(CGImageRef image)
-{
- switch (CGImageGetAlphaInfo(image)) {
- case kCGImageAlphaNone:
- case kCGImageAlphaNoneSkipLast:
- case kCGImageAlphaNoneSkipFirst:
- return NO;
- default:
- return YES;
- }
-}
-
NSError *RCTErrorWithMessage(NSString *message)
{
NSDictionary *errorInfo = @{NSLocalizedDescriptionKey: message};
diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m
index 789527006..34f2742a6 100644
--- a/React/Views/RCTComponentData.m
+++ b/React/Views/RCTComponentData.m
@@ -172,6 +172,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
// Ordinary property handlers
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
+ if (!typeSignature) {
+ RCTLogError(@"No +[RCTConvert %@] function found.", NSStringFromSelector(type));
+ return ^(__unused id view, __unused id json){};
+ }
switch (typeSignature.methodReturnType[0]) {
#define RCT_CASE(_value, _type) \