mirror of
https://github.com/status-im/react-native.git
synced 2025-01-25 00:39:03 +00:00
2b657003b7
Summary: Hi, I'm currently building an app that changes metadata, does some resizes, maybe watermarking ...etc. I want to use RCTImageStoreManager to store the original image in memory and allow me to command different modifications from javascript as it gives me more flexibility. As RCTImageEditingManager does for example. But currently the RTCImageStoreManager uses UIImage to store the image, the problem is that UIImage losses metadata. So i suggest we change it to NSData. Additionally I added a method to remove an image from the store. A related PR can be found here https://github.com/lwansbrough/react-native-camera/pull/100. Closes https://github.com/facebook/react-native/pull/3290 Reviewed By: javache Differential Revision: D2647271 Pulled By: nicklockwood fb-gh-sync-id: e66353ae3005423beee72ec22189dcb117fc719f
233 lines
6.8 KiB
Objective-C
233 lines
6.8 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 "RCTImageStoreManager.h"
|
|
|
|
#import <ImageIO/ImageIO.h>
|
|
#import <libkern/OSAtomic.h>
|
|
#import <MobileCoreServices/UTType.h>
|
|
|
|
#import "RCTAssert.h"
|
|
#import "RCTImageUtils.h"
|
|
#import "RCTLog.h"
|
|
#import "RCTUtils.h"
|
|
|
|
static NSString *const RCTImageStoreURLScheme = @"rct-image-store";
|
|
|
|
@implementation RCTImageStoreManager
|
|
{
|
|
NSMutableDictionary<NSString *, NSData *> *_store;
|
|
NSUInteger *_id;
|
|
}
|
|
|
|
@synthesize methodQueue = _methodQueue;
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
- (instancetype)init
|
|
{
|
|
if ((self = [super init])) {
|
|
_store = [NSMutableDictionary new];
|
|
_id = 0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)removeImageForTag:(NSString *)imageTag withBlock:(void (^)())block
|
|
{
|
|
dispatch_async(_methodQueue, ^{
|
|
[self removeImageForTag:imageTag];
|
|
if (block) {
|
|
block();
|
|
}
|
|
});
|
|
}
|
|
|
|
- (NSString *)_storeImageData:(NSData *)imageData
|
|
{
|
|
RCTAssertThread(_methodQueue, @"Must be called on RCTImageStoreManager thread");
|
|
NSString *imageTag = [NSString stringWithFormat:@"%@://%tu", RCTImageStoreURLScheme, _id++];
|
|
_store[imageTag] = imageData;
|
|
return imageTag;
|
|
}
|
|
|
|
- (void)storeImageData:(NSData *)imageData withBlock:(void (^)(NSString *imageTag))block
|
|
{
|
|
RCTAssertParam(block);
|
|
dispatch_async(_methodQueue, ^{
|
|
block([self _storeImageData:imageData]);
|
|
});
|
|
}
|
|
|
|
- (void)getImageDataForTag:(NSString *)imageTag withBlock:(void (^)(NSData *imageData))block
|
|
{
|
|
RCTAssertParam(block);
|
|
dispatch_async(_methodQueue, ^{
|
|
block(_store[imageTag]);
|
|
});
|
|
}
|
|
|
|
- (void)storeImage:(UIImage *)image withBlock:(void (^)(NSString *imageTag))block
|
|
{
|
|
RCTAssertParam(block);
|
|
dispatch_async(_methodQueue, ^{
|
|
NSString *imageTag = [self _storeImageData:RCTGetImageData(image.CGImage, 0.75)];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
block(imageTag);
|
|
});
|
|
});
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(removeImageForTag:(NSString *)imageTag)
|
|
{
|
|
[_store removeObjectForKey:imageTag];
|
|
}
|
|
|
|
// TODO (#5906496): Name could be more explicit - something like getBase64EncodedDataForTag:?
|
|
RCT_EXPORT_METHOD(getBase64ForTag:(NSString *)imageTag
|
|
successCallback:(RCTResponseSenderBlock)successCallback
|
|
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
|
{
|
|
NSData *imageData = _store[imageTag];
|
|
if (!imageData) {
|
|
errorCallback(RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]));
|
|
return;
|
|
}
|
|
// Dispatching to a background thread to perform base64 encoding
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
successCallback(@[[imageData base64EncodedStringWithOptions:0]]);
|
|
});
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String
|
|
successCallback:(RCTResponseSenderBlock)successCallback
|
|
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
|
|
|
{
|
|
// Dispatching to a background thread to perform base64 decoding
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
|
|
if (imageData) {
|
|
dispatch_async(_methodQueue, ^{
|
|
successCallback(@[[self _storeImageData:imageData]]);
|
|
});
|
|
} else {
|
|
errorCallback(RCTErrorWithMessage(@"Failed to add image from base64String"));
|
|
}
|
|
});
|
|
}
|
|
|
|
#pragma mark - RCTURLRequestHandler
|
|
|
|
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
|
{
|
|
return [request.URL.scheme caseInsensitiveCompare:RCTImageStoreURLScheme] == NSOrderedSame;
|
|
}
|
|
|
|
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
|
|
{
|
|
__block volatile uint32_t cancelled = 0;
|
|
void (^cancellationBlock)(void) = ^{
|
|
OSAtomicOr32Barrier(1, &cancelled);
|
|
};
|
|
|
|
// Dispatch async to give caller time to cancel the request
|
|
dispatch_async(_methodQueue, ^{
|
|
if (cancelled) {
|
|
return;
|
|
}
|
|
|
|
NSString *imageTag = request.URL.absoluteString;
|
|
NSData *imageData = _store[imageTag];
|
|
if (!imageData) {
|
|
NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]);
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:error];
|
|
return;
|
|
}
|
|
|
|
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
|
|
if (!sourceRef) {
|
|
NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Unable to decode data for imageTag: %@", imageTag]);
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:error];
|
|
return;
|
|
}
|
|
CFStringRef UTI = CGImageSourceGetType(sourceRef);
|
|
CFRelease(sourceRef);
|
|
|
|
NSString *MIMEType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
|
|
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
|
|
MIMEType:MIMEType
|
|
expectedContentLength:imageData.length
|
|
textEncodingName:nil];
|
|
|
|
[delegate URLRequest:cancellationBlock didReceiveResponse:response];
|
|
[delegate URLRequest:cancellationBlock didReceiveData:imageData];
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:nil];
|
|
|
|
});
|
|
|
|
return cancellationBlock;
|
|
}
|
|
|
|
- (void)cancelRequest:(id)requestToken
|
|
{
|
|
if (requestToken) {
|
|
((void (^)(void))requestToken)();
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTImageStoreManager (Deprecated)
|
|
|
|
- (NSString *)storeImage:(UIImage *)image
|
|
{
|
|
RCTAssertMainThread();
|
|
RCTLogWarn(@"RCTImageStoreManager.storeImage() is deprecated and has poor performance. Use an alternative method instead.");
|
|
__block NSString *imageTag;
|
|
dispatch_sync(_methodQueue, ^{
|
|
imageTag = [self _storeImageData:RCTGetImageData(image.CGImage, 0.75)];
|
|
});
|
|
return imageTag;
|
|
}
|
|
|
|
- (UIImage *)imageForTag:(NSString *)imageTag
|
|
{
|
|
RCTAssertMainThread();
|
|
RCTLogWarn(@"RCTImageStoreManager.imageForTag() is deprecated and has poor performance. Use an alternative method instead.");
|
|
__block NSData *imageData;
|
|
dispatch_sync(_methodQueue, ^{
|
|
imageData = _store[imageTag];
|
|
});
|
|
return [UIImage imageWithData:imageData];
|
|
}
|
|
|
|
- (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image))block
|
|
{
|
|
RCTAssertParam(block);
|
|
dispatch_async(_methodQueue, ^{
|
|
NSData *imageData = _store[imageTag];
|
|
UIImage *image = [UIImage imageWithData:imageData];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
block(image);
|
|
});
|
|
});
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTBridge (RCTImageStoreManager)
|
|
|
|
- (RCTImageStoreManager *)imageStoreManager
|
|
{
|
|
return self.modules[RCTBridgeModuleNameForClass([RCTImageStoreManager class])];
|
|
}
|
|
|
|
@end
|