Skip to content

Commit

Permalink
Generalized image decoding and resizing logic
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nicklockwood authored and facebook-github-bot-5 committed Jan 20, 2016
1 parent 9b87e6c commit 21fcbbc
Show file tree
Hide file tree
Showing 30 changed files with 510 additions and 379 deletions.
1 change: 1 addition & 0 deletions Examples/UIExplorer/ImageCapInsetsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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">
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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"
Expand All @@ -113,10 +113,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -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>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ - (BOOL)canLoadImageURL:(NSURL *)requestURL
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);
}
Expand Down Expand Up @@ -92,7 +92,7 @@ - (BOOL)canDecodeImageData:(NSData *)imageData
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);
}
Expand Down
20 changes: 10 additions & 10 deletions Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ - (void)testImageLoading

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;
Expand All @@ -50,7 +50,7 @@ - (void)testImageLoading
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) {
Expand All @@ -65,23 +65,23 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority

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;
}];

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;
}];

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) {
Expand All @@ -97,7 +97,7 @@ - (void)testImageDecoding

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;
Expand All @@ -106,7 +106,7 @@ - (void)testImageDecoding
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);
}];
Expand All @@ -120,23 +120,23 @@ - (void)testImageLoaderUsesImageDecoderWithHighestPriority

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;
}];

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;
}];

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);
}];
Expand Down
28 changes: 14 additions & 14 deletions Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ - (void)testLandscapeSourceLandscapeTarget

{
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);
}
}
Expand All @@ -70,19 +70,19 @@ - (void)testPortraitSourceLandscapeTarget

{
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);
}
}
Expand All @@ -94,19 +94,19 @@ - (void)testPortraitSourcePortraitTarget

{
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);
}
}
Expand All @@ -118,7 +118,7 @@ - (void)testRounding

{
CGRect expected = {{0, -75}, {20, 200}};
CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover);
RCTAssertEqualRects(expected, result);
}
}
Expand All @@ -129,7 +129,7 @@ - (void)testScaling
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);
}

Expand Down
Loading

0 comments on commit 21fcbbc

Please sign in to comment.