mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-10 22:45:24 +08:00
Generalized image decoding and resizing logic
Summary: public Standardises the image decoding logic for all image sources, meaning we get the benefits of efficient downscaling of images from all sources, not just ALAssets. Reviewed By: javache Differential Revision: D2647083 fb-gh-sync-id: e41456f838e4c6ab709b1c1523f651a86ff6e623
This commit is contained in:
committed by
facebook-github-bot-5
parent
9b87e6c860
commit
21fcbbc32c
@@ -46,6 +46,7 @@ var ImageCapInsetsExample = React.createClass({
|
||||
<Image
|
||||
source={require('image!story-background')}
|
||||
style={styles.storyBackground}
|
||||
resizeMode={Image.resizeMode.stretch}
|
||||
capInsets={{left: 15, right: 15, bottom: 15, top: 15}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.8">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -51,10 +51,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -90,11 +90,11 @@
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
@@ -113,10 +113,10 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -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 <RCTImageURLLoader>
|
||||
|
||||
@@ -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 <RCTImageDataDecoder>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
||||
|
||||
id<RCTImageURLLoader> 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<RCTImageURLLoader> 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<RCTImageURLLoader> 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<RCTImageDataDecoder> 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<RCTImageDataDecoder> 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<RCTImageDataDecoder> 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);
|
||||
}];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <AssetsLibrary/AssetsLibrary.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <RCTImageURLLoader>
|
||||
@class ALAssetsLibrary;
|
||||
|
||||
@interface RCTAssetsLibraryRequestHandler : NSObject <RCTURLRequestHandler>
|
||||
|
||||
@end
|
||||
|
||||
114
Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.m
Normal file
114
Libraries/CameraRoll/RCTAssetsLibraryRequestHandler.m
Normal file
@@ -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 <AssetsLibrary/AssetsLibrary.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#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<RCTURLRequestDelegate>)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
|
||||
@@ -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 = "<group>"; };
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryRequestHandler.h; sourceTree = "<group>"; };
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryRequestHandler.m; sourceTree = "<group>"; };
|
||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
/* 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 */,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTAssetsLibraryImageLoader.h"
|
||||
#import "RCTAssetsLibraryRequestHandler.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTImageLoader.h"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
|
||||
139A38821C4D57AD00862840 /* RCTResizeMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTResizeMode.h; sourceTree = "<group>"; };
|
||||
139A38831C4D587C00862840 /* RCTResizeMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTResizeMode.m; sourceTree = "<group>"; };
|
||||
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowVirtualImage.h; sourceTree = "<group>"; };
|
||||
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowVirtualImage.m; sourceTree = "<group>"; };
|
||||
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualImageManager.h; sourceTree = "<group>"; };
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<RCTImageDataDecoder> 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<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;
|
||||
}
|
||||
|
||||
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
|
||||
@@ -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]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,28 +11,36 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#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<NSString *, id> *__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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RCTImageComponent.h"
|
||||
#import "RCTResizeMode.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTImageSource;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
22
Libraries/Image/RCTResizeMode.h
Normal file
22
Libraries/Image/RCTResizeMode.h
Normal file
@@ -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
|
||||
20
Libraries/Image/RCTResizeMode.m
Normal file
20
Libraries/Image/RCTResizeMode.m
Normal file
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<NSString *> *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);
|
||||
|
||||
|
||||
@@ -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<NSString *, id> *errorInfo = @{NSLocalizedDescriptionKey: message};
|
||||
|
||||
@@ -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<RCTComponent> view, __unused id json){};
|
||||
}
|
||||
switch (typeSignature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_CASE(_value, _type) \
|
||||
|
||||
Reference in New Issue
Block a user