Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS]:- fixed image resize mode in release mode for iOS #44763

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <memory>

#import <React/RCTUtils.h>
#import <React/RCTImageUtils.h>
#import <ReactCommon/RCTTurboModule.h>

#import "RCTImagePlugins.h"
Expand Down Expand Up @@ -49,7 +50,7 @@ - (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
UIImage *image = RCTDecodeImageWithLocalAssetURL(imageURL, size, scale, resizeMode);
if (image) {
if (progressHandler) {
progressHandler(1, 1);
Expand Down
10 changes: 10 additions & 0 deletions packages/react-native/Libraries/Image/RCTImageUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ RCT_EXTERN BOOL RCTUpscalingRequired(
RCT_EXTERN UIImage *__nullable
RCTDecodeImageWithData(NSData *data, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode);

/**
* This function takes the source url for an image and decodes it at the
* specified size. If the original image is smaller than the destination size,
* the resultant image's scale will be decreased to compensate, so the
* width/height of the returned image is guaranteed to be >= destSize.
* Pass a destSize of CGSizeZero to decode the image at its original size.
*/
RCT_EXTERN UIImage *__nullable
RCTDecodeImageWithLocalAssetURL(NSURL *url, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode);

/**
* This function takes the source data for an image and decodes just the
* metadata, without decompressing the image itself.
Expand Down
125 changes: 77 additions & 48 deletions packages/react-native/Libraries/Image/RCTImageUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,68 @@ static CGImagePropertyOrientation CGImagePropertyOrientationFromUIImageOrientati
}
}

static UIImage* decodeImageFromCGImageSourceRef(
CGImageSourceRef sourceRef,
CGSize destSize,
CGFloat destScale,
RCTResizeMode resizeMode)
{
if (!sourceRef) {
Biki-das marked this conversation as resolved.
Show resolved Hide resolved
return nil;
}

// Get original image size
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(sourceRef, 0, NULL);
if (!imageProperties) {
CFRelease(sourceRef);
return nil;
}
NSNumber *width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSNumber *height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
CGSize sourceSize = {width.doubleValue, height.doubleValue};
CFRelease(imageProperties);

if (CGSizeEqualToSize(destSize, CGSizeZero)) {
destSize = sourceSize;
if (!destScale) {
destScale = 1;
}
} else if (!destScale) {
destScale = RCTScreenScale();
}

if (resizeMode == RCTResizeModeStretch) {
// Decoder cannot change aspect ratio, so RCTResizeModeStretch is equivalent
// to RCTResizeModeCover for our purposes
resizeMode = RCTResizeModeCover;
}

// Calculate target size
CGSize targetSize = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, NO);
CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
CGFloat maxPixelSize =
fmax(fmin(sourceSize.width, targetPixelSize.width), fmin(sourceSize.height, targetPixelSize.height));

NSDictionary<NSString *, NSNumber *> *options = @{
(id)kCGImageSourceShouldAllowFloat : @YES,
(id)kCGImageSourceCreateThumbnailWithTransform : @YES,
(id)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(id)kCGImageSourceThumbnailMaxPixelSize : @(maxPixelSize),
};

// Get thumbnail
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
CFRelease(sourceRef);
if (!imageRef) {
return nil;
}

// Return image
UIImage *image = [UIImage imageWithCGImage:imageRef scale:destScale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
return image;
}

CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode)
{
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
Expand Down Expand Up @@ -257,60 +319,27 @@ BOOL RCTUpscalingRequired(
UIImage *__nullable RCTDecodeImageWithData(NSData *data, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode)
{
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
if (!sourceRef) {
return nil;
}
return decodeImageFromCGImageSourceRef(sourceRef, destSize, destScale, resizeMode);

// Get original image size
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(sourceRef, 0, NULL);
if (!imageProperties) {
CFRelease(sourceRef);
return nil;
}
NSNumber *width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSNumber *height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
CGSize sourceSize = {width.doubleValue, height.doubleValue};
CFRelease(imageProperties);

if (CGSizeEqualToSize(destSize, CGSizeZero)) {
destSize = sourceSize;
if (!destScale) {
destScale = 1;
}
UIImage *__nullable RCTDecodeImageWithLocalAssetURL(NSURL *url, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode)
{
// If the resizeMode is the default (assuming it's RCTResizeModeCover), directly return the image from the local asset URL
if (resizeMode == RCTResizeModeCover) {
return RCTImageFromLocalAssetURL(url);
}
} else if (!destScale) {
destScale = RCTScreenScale();
}

if (resizeMode == RCTResizeModeStretch) {
// Decoder cannot change aspect ratio, so RCTResizeModeStretch is equivalent
// to RCTResizeModeCover for our purposes
resizeMode = RCTResizeModeCover;
}

// Calculate target size
CGSize targetSize = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, NO);
CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
CGFloat maxPixelSize =
fmax(fmin(sourceSize.width, targetPixelSize.width), fmin(sourceSize.height, targetPixelSize.height));
// Otherwise, proceed with the original logic using decodeImageFromCGImageSourceRef
CGImageSourceRef sourceRef = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
UIImage *image = decodeImageFromCGImageSourceRef(sourceRef, destSize, destScale, resizeMode);

NSDictionary<NSString *, NSNumber *> *options = @{
(id)kCGImageSourceShouldAllowFloat : @YES,
(id)kCGImageSourceCreateThumbnailWithTransform : @YES,
(id)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(id)kCGImageSourceThumbnailMaxPixelSize : @(maxPixelSize),
};

// Get thumbnail
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
CFRelease(sourceRef);
if (!imageRef) {
return nil;
}
// If decoding the image failed, fallback to getting the image from the local asset URL
if (!image) {
image = RCTImageFromLocalAssetURL(url);
}

// Return image
UIImage *image = [UIImage imageWithCGImage:imageRef scale:destScale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
return image;
return image;
}

NSDictionary<NSString *, id> *__nullable RCTGetImageMetadata(NSData *data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <memory>

#import <React/RCTUtils.h>
#import <React/RCTImageUtils.h>
#import <ReactCommon/RCTTurboModule.h>

#import "RCTImagePlugins.h"
Expand Down Expand Up @@ -49,7 +50,7 @@ - (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
UIImage *image = RCTDecodeImageWithLocalAssetURL(imageURL, size, scale, resizeMode);
if (image) {
if (progressHandler) {
progressHandler(1, 1);
Expand Down
Loading