Fixed image clipping / resizing logic

This commit is contained in:
Nick Lockwood 2015-08-12 13:07:24 -01:00
parent 672b77f355
commit 7232b1bbc7
7 changed files with 107 additions and 78 deletions

View File

@ -25,7 +25,7 @@
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; }; 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; };
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; }; 141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; };
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; }; 143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; };
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClipRectTests.m */; }; 144D21241B2204C5006DB32B /* RCTImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */; };
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; }; 147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; }; 1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; };
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */; }; 1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */; };
@ -191,7 +191,7 @@
143BC5951B21E3E100462512 /* UIExplorerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 143BC5951B21E3E100462512 /* UIExplorerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
143BC5981B21E3E100462512 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 143BC5981B21E3E100462512 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerSnapshotTests.m; sourceTree = "<group>"; }; 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerSnapshotTests.m; sourceTree = "<group>"; };
144D21231B2204C5006DB32B /* RCTClipRectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTClipRectTests.m; sourceTree = "<group>"; }; 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtilTests.m; sourceTree = "<group>"; };
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAllocationTests.m; sourceTree = "<group>"; }; 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAllocationTests.m; sourceTree = "<group>"; };
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeTests.m; sourceTree = "<group>"; }; 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeTests.m; sourceTree = "<group>"; };
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutorTests.m; sourceTree = "<group>"; }; 1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutorTests.m; sourceTree = "<group>"; };
@ -357,12 +357,12 @@
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */, 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */,
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */, 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */,
138D6A151B53CD440074A87E /* RCTCacheTests.m */, 138D6A151B53CD440074A87E /* RCTCacheTests.m */,
144D21231B2204C5006DB32B /* RCTClipRectTests.m */,
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */, 1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */,
1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */, 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */,
1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */, 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */,
1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */, 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */,
1300627E1B59179B0043FE5A /* RCTGzipTests.m */, 1300627E1B59179B0043FE5A /* RCTGzipTests.m */,
144D21231B2204C5006DB32B /* RCTImageUtilTests.m */,
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */, 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */, 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */, 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
@ -793,7 +793,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */, 1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */,
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */, 144D21241B2204C5006DB32B /* RCTImageUtilTests.m in Sources */,
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */, 1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */,
1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */, 1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */,
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */, 1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,

View File

@ -33,11 +33,11 @@ RCTAssertEqualPoints(a.origin, b.origin); \
RCTAssertEqualSizes(a.size, b.size); \ RCTAssertEqualSizes(a.size, b.size); \
} }
@interface RCTClipRectTests : XCTestCase @interface RCTImageUtilTests : XCTestCase
@end @end
@implementation RCTClipRectTests @implementation RCTImageUtilTests
- (void)testLandscapeSourceLandscapeTarget - (void)testLandscapeSourceLandscapeTarget
{ {
@ -46,19 +46,19 @@ RCTAssertEqualSizes(a.size, b.size); \
{ {
CGRect expected = {CGPointZero, {100, 20}}; CGRect expected = {CGPointZero, {100, 20}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {CGPointZero, {100, 10}}; CGRect expected = {CGPointZero, {100, 10}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {{-50, 0}, {200, 20}}; CGRect expected = {{-50, 0}, {200, 20}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
} }
@ -69,20 +69,20 @@ RCTAssertEqualSizes(a.size, b.size); \
CGSize target = {100, 20}; CGSize target = {100, 20};
{ {
CGRect expected = {CGPointZero, {10, 20}}; CGRect expected = {CGPointZero, {100, 20}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {CGPointZero, {2, 20}}; CGRect expected = {CGPointZero, {2, 20}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {{0, -49}, {10, 100}}; CGRect expected = {{0, -490}, {100, 1000}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
} }
@ -93,20 +93,20 @@ RCTAssertEqualSizes(a.size, b.size); \
CGSize target = {20, 50}; CGSize target = {20, 50};
{ {
CGRect expected = {CGPointZero, {10, 50}}; CGRect expected = {CGPointZero, {20, 50}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {CGPointZero, {5, 50}}; CGRect expected = {CGPointZero, {5, 50}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
{ {
CGRect expected = {{0, -37.5}, {10, 100}}; CGRect expected = {{0, -75}, {20, 200}};
CGRect result = RCTClipRect(content, 2, target, 2, UIViewContentModeScaleAspectFill); CGRect result = RCTTargetRect(content, target, 2, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
} }
@ -117,8 +117,8 @@ RCTAssertEqualSizes(a.size, b.size); \
CGSize target = {20, 50}; CGSize target = {20, 50};
{ {
CGRect expected = {{0, -38}, {10, 100}}; CGRect expected = {{0, -75}, {20, 200}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }
} }
@ -129,7 +129,7 @@ RCTAssertEqualSizes(a.size, b.size); \
CGSize target = {3, 3}; CGSize target = {3, 3};
CGRect expected = {CGPointZero, {3, 3}}; CGRect expected = {CGPointZero, {3, 3}};
CGRect result = RCTClipRect(content, 2, target, 1, UIViewContentModeScaleToFill); CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill);
RCTAssertEqualRects(expected, result); RCTAssertEqualRects(expected, result);
} }

View File

@ -15,9 +15,6 @@
#import "RCTNetworking.h" #import "RCTNetworking.h"
#import "RCTUtils.h" #import "RCTUtils.h"
CGSize RCTTargetSizeForClipRect(CGRect);
CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
@implementation RCTImageDownloader @implementation RCTImageDownloader
{ {
NSURLCache *_cache; NSURLCache *_cache;
@ -131,14 +128,14 @@ RCT_EXPORT_MODULE()
UIImage *image = [UIImage imageWithData:data scale:scale]; UIImage *image = [UIImage imageWithData:data scale:scale];
if (image && !CGSizeEqualToSize(size, CGSizeZero)) { if (image && !CGSizeEqualToSize(size, CGSizeZero)) {
// Get scale and size // Get destination size
CGRect imageRect = RCTClipRect(image.size, scale, size, scale, resizeMode); CGSize targetSize = RCTTargetSize(image.size, image.scale,
CGSize destSize = RCTTargetSizeForClipRect(imageRect); size, scale, resizeMode, NO);
// Decompress image at required size // Decompress image at required size
BOOL opaque = !RCTImageHasAlpha(image.CGImage); BOOL opaque = !RCTImageHasAlpha(image.CGImage);
UIGraphicsBeginImageContextWithOptions(destSize, opaque, scale); UIGraphicsBeginImageContextWithOptions(targetSize, opaque, scale);
[image drawInRect:imageRect]; [image drawInRect:(CGRect){CGPointZero, targetSize}];
image = UIGraphicsGetImageFromCurrentImageContext(); image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); UIGraphicsEndImageContext();
} }

View File

@ -85,8 +85,8 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
} }
CGSize sourceSize = representation.dimensions; CGSize sourceSize = representation.dimensions;
CGRect targetRect = RCTClipRect(sourceSize, representation.scale, size, scale, resizeMode); CGSize targetSize = RCTTargetSize(sourceSize, representation.scale,
CGSize targetSize = targetRect.size; size, scale, resizeMode, NO);
NSDictionary *options = @{ NSDictionary *options = @{
(id)kCGImageSourceShouldAllowFloat: @YES, (id)kCGImageSourceShouldAllowFloat: @YES,

View File

@ -13,20 +13,24 @@
#import "RCTDefines.h" #import "RCTDefines.h"
/** /**
* Returns the optimal context size for an image drawn using the clip rect * This function takes an input content size (typically from an image), a target
* returned by RCTClipRect. * 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.
*/ */
RCT_EXTERN CGSize RCTTargetSizeForClipRect(CGRect clipRect); RCT_EXTERN CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
CGFloat destScale, UIViewContentMode resizeMode);
/** /**
* This function takes an input content size & scale (typically from an image), * This function takes an input content size & scale (typically from an image),
* a target size & scale that it will be drawn into (typically a CGContext) and * a target size & scale at which it will be displayed (typically in a
* then calculates the optimal rectangle to draw the image into so that it will * UIImageView) and then calculates the optimal size at which to redraw the
* be sized and positioned correctly if drawn using the specified content mode. * image so that it will be displayed correctly with the specified content mode.
*/ */
RCT_EXTERN CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale, RCT_EXTERN CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale, CGSize destSize, CGFloat destScale,
UIViewContentMode resizeMode); UIViewContentMode resizeMode,
BOOL allowUpscaling);
/** /**
* This function takes an input content size & scale (typically from an image), * This function takes an input content size & scale (typically from an image),

View File

@ -29,28 +29,14 @@ static CGSize RCTCeilSize(CGSize size, CGFloat scale)
}; };
} }
CGSize RCTTargetSizeForClipRect(CGRect clipRect) CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
{ CGFloat destScale, UIViewContentMode resizeMode)
return (CGSize){
clipRect.size.width + clipRect.origin.x * 2,
clipRect.size.height + clipRect.origin.y * 2
};
}
CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
UIViewContentMode resizeMode)
{ {
if (CGSizeEqualToSize(destSize, CGSizeZero)) { if (CGSizeEqualToSize(destSize, CGSizeZero)) {
// Assume we require the largest size available // Assume we require the largest size available
return (CGRect){CGPointZero, sourceSize}; return (CGRect){CGPointZero, sourceSize};
} }
// Precompensate for scale
CGFloat scale = sourceScale / destScale;
sourceSize.width *= scale;
sourceSize.height *= scale;
CGFloat aspect = sourceSize.width / sourceSize.height; CGFloat aspect = sourceSize.width / sourceSize.height;
// If only one dimension in destSize is non-zero (for example, an Image // If only one dimension in destSize is non-zero (for example, an Image
// with `flex: 1` whose height is indeterminate), calculate the unknown // with `flex: 1` whose height is indeterminate), calculate the unknown
@ -61,7 +47,7 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
if (destSize.height == 0) { if (destSize.height == 0) {
destSize.height = destSize.width / aspect; destSize.height = destSize.width / aspect;
} }
// Calculate target aspect ratio if needed (don't bother if resizeMode == stretch) // Calculate target aspect ratio if needed (don't bother if resizeMode == stretch)
CGFloat targetAspect = 0.0; CGFloat targetAspect = 0.0;
if (resizeMode != UIViewContentModeScaleToFill) { if (resizeMode != UIViewContentModeScaleToFill) {
@ -74,20 +60,18 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
switch (resizeMode) { switch (resizeMode) {
case UIViewContentModeScaleToFill: // stretch case UIViewContentModeScaleToFill: // stretch
sourceSize.width = MIN(destSize.width, sourceSize.width); return (CGRect){CGPointZero, RCTCeilSize(destSize, destScale)};
sourceSize.height = MIN(destSize.height, sourceSize.height);
return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)};
case UIViewContentModeScaleAspectFit: // contain case UIViewContentModeScaleAspectFit: // contain
if (targetAspect <= aspect) { // target is taller than content if (targetAspect <= aspect) { // target is taller than content
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width); sourceSize.width = destSize.width = destSize.width;
sourceSize.height = sourceSize.width / aspect; sourceSize.height = sourceSize.width / aspect;
} else { // target is wider than content } else { // target is wider than content
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height); sourceSize.height = destSize.height = destSize.height;
sourceSize.width = sourceSize.height * aspect; sourceSize.width = sourceSize.height * aspect;
} }
return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)}; return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)};
@ -96,7 +80,7 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
if (targetAspect <= aspect) { // target is taller than content if (targetAspect <= aspect) { // target is taller than content
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height); sourceSize.height = destSize.height = destSize.height;
sourceSize.width = sourceSize.height * aspect; sourceSize.width = sourceSize.height * aspect;
destSize.width = destSize.height * targetAspect; destSize.width = destSize.height * targetAspect;
return (CGRect){ return (CGRect){
@ -106,7 +90,7 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
} else { // target is wider than content } else { // target is wider than content
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width); sourceSize.width = destSize.width = destSize.width;
sourceSize.height = sourceSize.width / aspect; sourceSize.height = sourceSize.width / aspect;
destSize.height = destSize.width / targetAspect; destSize.height = destSize.width / targetAspect;
return (CGRect){ return (CGRect){
@ -122,9 +106,39 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
} }
} }
RCT_EXTERN BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale, CGSize RCTTargetSize(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale, CGSize destSize, CGFloat destScale,
UIViewContentMode resizeMode) UIViewContentMode resizeMode,
BOOL allowUpscaling)
{
switch (resizeMode) {
case UIViewContentModeScaleToFill: // stretch
if (!allowUpscaling) {
CGFloat scale = sourceScale / destScale;
destSize.width = MIN(sourceSize.width * scale, destSize.width);
destSize.height = MIN(sourceSize.height * scale, destSize.height);
}
return RCTCeilSize(destSize, destScale);
default: {
// Get target size
CGSize size = RCTTargetRect(sourceSize, destSize, destScale, resizeMode).size;
if (!allowUpscaling) {
// return sourceSize if target size is larger
if (sourceSize.width * sourceScale < size.width * destScale) {
return sourceSize;
}
}
return size;
}
}
}
BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
CGSize destSize, CGFloat destScale,
UIViewContentMode resizeMode)
{ {
if (CGSizeEqualToSize(destSize, CGSizeZero)) { if (CGSizeEqualToSize(destSize, CGSizeZero)) {
// Assume we require the largest size available // Assume we require the largest size available

View File

@ -14,6 +14,7 @@
#import "RCTEventDispatcher.h" #import "RCTEventDispatcher.h"
#import "RCTGIFImage.h" #import "RCTGIFImage.h"
#import "RCTImageLoader.h" #import "RCTImageLoader.h"
#import "RCTImageUtils.h"
#import "RCTUtils.h" #import "RCTUtils.h"
#import "UIView+React.h" #import "UIView+React.h"
@ -43,7 +44,7 @@
RCT_NOT_IMPLEMENTED(-init) RCT_NOT_IMPLEMENTED(-init)
- (void)_updateImage - (void)updateImage
{ {
UIImage *image = self.image; UIImage *image = self.image;
if (!image) { if (!image) {
@ -72,7 +73,7 @@ RCT_NOT_IMPLEMENTED(-init)
image = image ?: _defaultImage; image = image ?: _defaultImage;
if (image != super.image) { if (image != super.image) {
super.image = image; super.image = image;
[self _updateImage]; [self updateImage];
} }
} }
@ -80,7 +81,7 @@ RCT_NOT_IMPLEMENTED(-init)
{ {
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, capInsets)) { if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, capInsets)) {
_capInsets = capInsets; _capInsets = capInsets;
[self _updateImage]; [self updateImage];
} }
} }
@ -88,7 +89,7 @@ RCT_NOT_IMPLEMENTED(-init)
{ {
if (_renderingMode != renderingMode) { if (_renderingMode != renderingMode) {
_renderingMode = renderingMode; _renderingMode = renderingMode;
[self _updateImage]; [self updateImage];
} }
} }
@ -100,6 +101,16 @@ RCT_NOT_IMPLEMENTED(-init)
} }
} }
- (void)setContentMode:(UIViewContentMode)contentMode
{
if (self.contentMode != contentMode) {
super.contentMode = contentMode;
if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) {
[self reloadImage];
}
}
}
- (void)reloadImage - (void)reloadImage
{ {
if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) { if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) {
@ -165,12 +176,15 @@ RCT_NOT_IMPLEMENTED(-init)
if (self.image == nil) { if (self.image == nil) {
[self reloadImage]; [self reloadImage];
} else if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) { } else if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) {
CGSize imageSize = {
self.image.size.width / RCTScreenScale(), // Get optimal image size
self.image.size.height / RCTScreenScale() CGSize currentSize = self.image.size;
}; CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size,
CGFloat widthChangeFraction = imageSize.width ? ABS(imageSize.width - frame.size.width) / imageSize.width : 1; RCTScreenScale(), self.contentMode, YES);
CGFloat heightChangeFraction = imageSize.height ? ABS(imageSize.height - frame.size.height) / imageSize.height : 1;
CGFloat widthChangeFraction = ABS(currentSize.width - idealSize.width) / currentSize.width;
CGFloat heightChangeFraction = ABS(currentSize.height - idealSize.height) / currentSize.height;
// If the combined change is more than 20%, reload the asset in case there is a better size. // If the combined change is more than 20%, reload the asset in case there is a better size.
if (widthChangeFraction + heightChangeFraction > 0.2) { if (widthChangeFraction + heightChangeFraction > 0.2) {
[self reloadImage]; [self reloadImage];