mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 19:44:13 +00:00
56d25bbbdd
Summary: The CameraRoll-related APIs were mixed in with the Image classes due to legacy coupling issues. Now that the APIs have been decoupled, it makes more sense for the CameraRoll classes to live in a separate library. This will be a breaking change for apps using the CameraRoll or related APIs. Fix is to add the RCTCameraRoll lib to your project.
156 lines
5.3 KiB
Objective-C
156 lines
5.3 KiB
Objective-C
/**
|
|
* 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 <UIKit/UIKit.h>
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTConvert.h"
|
|
#import "RCTImageLoader.h"
|
|
#import "RCTImageUtils.h"
|
|
#import "RCTUtils.h"
|
|
|
|
static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void);
|
|
static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error);
|
|
|
|
@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.lowercaseString isEqualToString:@"assets-library"];
|
|
}
|
|
|
|
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
|
{
|
|
[[self assetsLibrary] assetForURL:imageURL resultBlock:^(ALAsset *asset) {
|
|
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(), ^{
|
|
// 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];
|
|
|
|
UIImage *image;
|
|
NSError *error = nil;
|
|
if (useMaximumSize) {
|
|
image = [UIImage imageWithCGImage:representation.fullResolutionImage
|
|
scale:scale
|
|
orientation:(UIImageOrientation)representation.orientation];
|
|
} else {
|
|
image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error);
|
|
}
|
|
|
|
completionHandler(error, image);
|
|
}
|
|
});
|
|
} else {
|
|
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL];
|
|
NSError *error = RCTErrorWithMessage(errorText);
|
|
completionHandler(error, nil);
|
|
}
|
|
} failureBlock:^(NSError *loadError) {
|
|
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError];
|
|
NSError *error = RCTErrorWithMessage(errorText);
|
|
completionHandler(error, nil);
|
|
}];
|
|
|
|
return ^{};
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTBridge (RCTAssetsLibraryImageLoader)
|
|
|
|
- (RCTAssetsLibraryImageLoader *)assetsLibraryImageLoader
|
|
{
|
|
return self.modules[RCTBridgeModuleNameForClass([RCTAssetsLibraryImageLoader class])];
|
|
}
|
|
|
|
- (ALAssetsLibrary *)assetsLibrary
|
|
{
|
|
return [self.assetsLibraryImageLoader 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;
|
|
}
|
|
|
|
// Why use a custom scaling method? Greater efficiency, reduced memory overhead:
|
|
// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill
|
|
|
|
static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error)
|
|
{
|
|
NSUInteger length = (NSUInteger)representation.size;
|
|
NSMutableData *data = [NSMutableData dataWithLength:length];
|
|
if (![representation getBytes:data.mutableBytes
|
|
fromOffset:0
|
|
length:length
|
|
error:error]) {
|
|
return nil;
|
|
}
|
|
|
|
CGSize sourceSize = representation.dimensions;
|
|
CGSize targetSize = RCTTargetSize(sourceSize, representation.scale,
|
|
size, scale, resizeMode, NO);
|
|
|
|
NSDictionary *options = @{
|
|
(id)kCGImageSourceShouldAllowFloat: @YES,
|
|
(id)kCGImageSourceCreateThumbnailWithTransform: @YES,
|
|
(id)kCGImageSourceCreateThumbnailFromImageAlways: @YES,
|
|
(id)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * scale)
|
|
};
|
|
|
|
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
|
|
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
|
|
if (sourceRef) {
|
|
CFRelease(sourceRef);
|
|
}
|
|
|
|
if (imageRef) {
|
|
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale
|
|
orientation:UIImageOrientationUp];
|
|
CGImageRelease(imageRef);
|
|
return image;
|
|
}
|
|
|
|
return nil;
|
|
}
|