2015-07-14 11:06:17 +00:00
|
|
|
/**
|
|
|
|
* 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 "RCTImageUtils.h"
|
|
|
|
|
|
|
|
#import "RCTLog.h"
|
|
|
|
|
2015-07-21 12:40:06 +00:00
|
|
|
static CGFloat RCTCeilValue(CGFloat value, CGFloat scale)
|
|
|
|
{
|
|
|
|
return ceil(value * scale) / scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CGFloat RCTFloorValue(CGFloat value, CGFloat scale)
|
|
|
|
{
|
|
|
|
return floor(value * scale) / scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CGSize RCTCeilSize(CGSize size, CGFloat scale)
|
|
|
|
{
|
|
|
|
return (CGSize){
|
|
|
|
RCTCeilValue(size.width, scale),
|
|
|
|
RCTCeilValue(size.height, scale)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-07-14 11:06:17 +00:00
|
|
|
CGSize RCTTargetSizeForClipRect(CGRect clipRect)
|
|
|
|
{
|
|
|
|
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)) {
|
|
|
|
// Assume we require the largest size available
|
|
|
|
return (CGRect){CGPointZero, sourceSize};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Precompensate for scale
|
|
|
|
CGFloat scale = sourceScale / destScale;
|
|
|
|
sourceSize.width *= scale;
|
|
|
|
sourceSize.height *= scale;
|
|
|
|
|
|
|
|
// Calculate aspect ratios if needed (don't bother if resizeMode == stretch)
|
|
|
|
CGFloat aspect = 0.0, targetAspect = 0.0;
|
|
|
|
if (resizeMode != UIViewContentModeScaleToFill) {
|
|
|
|
aspect = sourceSize.width / sourceSize.height;
|
|
|
|
targetAspect = destSize.width / destSize.height;
|
|
|
|
if (aspect == targetAspect) {
|
|
|
|
resizeMode = UIViewContentModeScaleToFill;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (resizeMode) {
|
|
|
|
case UIViewContentModeScaleToFill: // stretch
|
|
|
|
|
|
|
|
sourceSize.width = MIN(destSize.width, sourceSize.width);
|
|
|
|
sourceSize.height = MIN(destSize.height, sourceSize.height);
|
2015-07-21 12:40:06 +00:00
|
|
|
return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)};
|
2015-07-14 11:06:17 +00:00
|
|
|
|
|
|
|
case UIViewContentModeScaleAspectFit: // contain
|
|
|
|
|
|
|
|
if (targetAspect <= aspect) { // target is taller than content
|
|
|
|
|
|
|
|
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
|
|
|
sourceSize.height = sourceSize.width / aspect;
|
|
|
|
|
|
|
|
} else { // target is wider than content
|
|
|
|
|
|
|
|
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
|
|
|
sourceSize.width = sourceSize.height * aspect;
|
|
|
|
}
|
2015-07-21 12:40:06 +00:00
|
|
|
return (CGRect){CGPointZero, RCTCeilSize(sourceSize, destScale)};
|
2015-07-14 11:06:17 +00:00
|
|
|
|
|
|
|
case UIViewContentModeScaleAspectFill: // cover
|
|
|
|
|
|
|
|
if (targetAspect <= aspect) { // target is taller than content
|
|
|
|
|
|
|
|
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
|
|
|
sourceSize.width = sourceSize.height * aspect;
|
|
|
|
destSize.width = destSize.height * targetAspect;
|
2015-07-21 12:40:06 +00:00
|
|
|
return (CGRect){
|
|
|
|
{RCTFloorValue((destSize.width - sourceSize.width) / 2, destScale), 0},
|
|
|
|
RCTCeilSize(sourceSize, destScale)
|
|
|
|
};
|
2015-07-14 11:06:17 +00:00
|
|
|
|
|
|
|
} else { // target is wider than content
|
|
|
|
|
|
|
|
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
|
|
|
sourceSize.height = sourceSize.width / aspect;
|
|
|
|
destSize.height = destSize.width / targetAspect;
|
2015-07-21 12:40:06 +00:00
|
|
|
return (CGRect){
|
|
|
|
{0, RCTFloorValue((destSize.height - sourceSize.height) / 2, destScale)},
|
|
|
|
RCTCeilSize(sourceSize, destScale)
|
|
|
|
};
|
2015-07-14 11:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
2015-07-21 12:40:06 +00:00
|
|
|
return (CGRect){CGPointZero, RCTCeilSize(destSize, destScale)};
|
2015-07-14 11:06:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXTERN BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
|
|
|
|
CGSize destSize, CGFloat destScale,
|
|
|
|
UIViewContentMode resizeMode)
|
|
|
|
{
|
|
|
|
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
|
|
|
|
// Assume we require the largest size available
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Precompensate for scale
|
|
|
|
CGFloat scale = sourceScale / destScale;
|
|
|
|
sourceSize.width *= scale;
|
|
|
|
sourceSize.height *= scale;
|
|
|
|
|
|
|
|
// Calculate aspect ratios if needed (don't bother if resizeMode == stretch)
|
|
|
|
CGFloat aspect = 0.0, targetAspect = 0.0;
|
|
|
|
if (resizeMode != UIViewContentModeScaleToFill) {
|
|
|
|
aspect = sourceSize.width / sourceSize.height;
|
|
|
|
targetAspect = destSize.width / destSize.height;
|
|
|
|
if (aspect == targetAspect) {
|
|
|
|
resizeMode = UIViewContentModeScaleToFill;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (resizeMode) {
|
|
|
|
case UIViewContentModeScaleToFill: // stretch
|
|
|
|
|
|
|
|
return destSize.width > sourceSize.width || destSize.height > sourceSize.height;
|
|
|
|
|
|
|
|
case UIViewContentModeScaleAspectFit: // contain
|
|
|
|
|
|
|
|
if (targetAspect <= aspect) { // target is taller than content
|
|
|
|
|
|
|
|
return destSize.width > sourceSize.width;
|
|
|
|
|
|
|
|
} else { // target is wider than content
|
|
|
|
|
|
|
|
return destSize.height > sourceSize.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
case UIViewContentModeScaleAspectFill: // cover
|
|
|
|
|
|
|
|
if (targetAspect <= aspect) { // target is taller than content
|
|
|
|
|
|
|
|
return destSize.height > sourceSize.height;
|
|
|
|
|
|
|
|
} else { // target is wider than content
|
|
|
|
|
|
|
|
return destSize.width > sourceSize.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|