Improve RCTImageView resizing logic

Summary: @​public

It's less important to reload for downscaling than upscaling, so increase the threshold in that direction. Also, this now considers each dimension separately, and a big enough change either way should result in a reload.

Finally, this considers both the current image size and the inflight target size, so we don't reload if either is already good.

Reviewed By: @tadeuzagallo

Differential Revision: D2470911
This commit is contained in:
Justin Spahr-Summers
2015-09-23 07:30:37 -07:00
committed by facebook-github-bot-8
parent de85bcab0b
commit bcd2ef5dfc

View File

@@ -18,6 +18,21 @@
#import "UIView+React.h" #import "UIView+React.h"
/**
* Determines whether an image of `currentSize` should be reloaded for display
* at `idealSize`.
*/
static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize) {
static const CGFloat upscaleThreshold = 1.2;
static const CGFloat downscaleThreshold = 0.5;
CGFloat widthMultiplier = idealSize.width / currentSize.width;
CGFloat heightMultiplier = idealSize.height / currentSize.height;
return widthMultiplier > upscaleThreshold || widthMultiplier < downscaleThreshold ||
heightMultiplier > upscaleThreshold || heightMultiplier < downscaleThreshold;
}
@interface RCTImageView () @interface RCTImageView ()
@property (nonatomic, copy) RCTDirectEventBlock onLoadStart; @property (nonatomic, copy) RCTDirectEventBlock onLoadStart;
@@ -146,8 +161,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
{ {
[self cancelImageLoad]; [self cancelImageLoad];
if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) { if (_src && self.frame.size.width > 0 && self.frame.size.height > 0) {
if (_onLoadStart) { if (_onLoadStart) {
_onLoadStart(nil); _onLoadStart(nil);
} }
@@ -199,15 +213,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
_targetSize = frame.size; _targetSize = frame.size;
[self reloadImage]; [self reloadImage];
} else if ([RCTImageView srcNeedsReload:_src]) { } else if ([RCTImageView srcNeedsReload:_src]) {
CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size, CGSize imageSize = self.image.size;
RCTScreenScale(), self.contentMode, YES); CGSize idealSize = RCTTargetSize(imageSize, self.image.scale, frame.size, RCTScreenScale(), self.contentMode, YES);
CGFloat widthChangeFraction = ABS(_targetSize.width - idealSize.width) / _targetSize.width;
CGFloat heightChangeFraction = ABS(_targetSize.height - idealSize.height) / _targetSize.height;
// If the combined change is more than 20%, reload the asset in case there is a better size. if (RCTShouldReloadImageForSizeChange(imageSize, idealSize)) {
if (widthChangeFraction + heightChangeFraction > 0.2) { if (RCTShouldReloadImageForSizeChange(_targetSize, idealSize)) {
_targetSize = idealSize; // If the existing image or an image being loaded are not the right size, reload the asset in case there is a
[self reloadImage]; // better size available.
_targetSize = idealSize;
[self reloadImage];
}
} else {
// Our existing image is good enough.
[self cancelImageLoad];
_targetSize = imageSize;
} }
} }
} }