// Copyright 2019 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "FLTImagePickerImageUtil.h" #import @interface GIFInfo () @property(strong, nonatomic, readwrite) NSArray *images; @property(assign, nonatomic, readwrite) NSTimeInterval interval; @end @implementation GIFInfo - (instancetype)initWithImages:(NSArray *)images interval:(NSTimeInterval)interval; { self = [super init]; if (self) { self.images = images; self.interval = interval; } return self; } @end @implementation FLTImagePickerImageUtil : NSObject + (UIImage *)scaledImage:(UIImage *)image maxWidth:(NSNumber *)maxWidth maxHeight:(NSNumber *)maxHeight { double originalWidth = image.size.width; double originalHeight = image.size.height; bool hasMaxWidth = maxWidth != (id)[NSNull null]; bool hasMaxHeight = maxHeight != (id)[NSNull null]; double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth; double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight; bool shouldDownscaleWidth = hasMaxWidth && [maxWidth doubleValue] < originalWidth; bool shouldDownscaleHeight = hasMaxHeight && [maxHeight doubleValue] < originalHeight; bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; if (shouldDownscale) { double downscaledWidth = floor((height / originalHeight) * originalWidth); double downscaledHeight = floor((width / originalWidth) * originalHeight); if (width < height) { if (!hasMaxWidth) { width = downscaledWidth; } else { height = downscaledHeight; } } else if (height < width) { if (!hasMaxHeight) { height = downscaledHeight; } else { width = downscaledWidth; } } else { if (originalWidth < originalHeight) { width = downscaledWidth; } else if (originalHeight < originalWidth) { height = downscaledHeight; } } } // Scaling the image always rotate itself based on the current imageOrientation of the original // Image. Set to orientationUp for the orignal image before scaling, so the scaled image doesn't // mess up with the pixels. UIImage *imageToScale = [UIImage imageWithCGImage:image.CGImage scale:1 orientation:UIImageOrientationUp]; // The image orientation is manually set to UIImageOrientationUp which swapped the aspect ratio in // some scenarios. For example, when the original image has orientation left, the horizontal // pixels should be scaled to `width` and the vertical pixels should be scaled to `height`. After // setting the orientation to up, we end up scaling the horizontal pixels to `height` and vertical // to `width`. Below swap will solve this issue. if ([image imageOrientation] == UIImageOrientationLeft || [image imageOrientation] == UIImageOrientationRight || [image imageOrientation] == UIImageOrientationLeftMirrored || [image imageOrientation] == UIImageOrientationRightMirrored) { double temp = width; width = height; height = temp; } UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0); [imageToScale drawInRect:CGRectMake(0, 0, width, height)]; UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaledImage; } + (GIFInfo *)scaledGIFImage:(NSData *)data maxWidth:(NSNumber *)maxWidth maxHeight:(NSNumber *)maxHeight { NSMutableDictionary *options = [NSMutableDictionary dictionary]; options[(NSString *)kCGImageSourceShouldCache] = @(YES); options[(NSString *)kCGImageSourceTypeIdentifierHint] = (NSString *)kUTTypeGIF; CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, (CFDictionaryRef)options); size_t numberOfFrames = CGImageSourceGetCount(imageSource); NSMutableArray *images = [NSMutableArray arrayWithCapacity:numberOfFrames]; NSTimeInterval interval = 0.0; for (size_t index = 0; index < numberOfFrames; index++) { CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, (CFDictionaryRef)options); NSDictionary *properties = (NSDictionary *)CFBridgingRelease( CGImageSourceCopyPropertiesAtIndex(imageSource, index, NULL)); NSDictionary *gifProperties = properties[(NSString *)kCGImagePropertyGIFDictionary]; NSNumber *delay = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; if (delay == nil) { delay = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; } if (interval == 0.0) { interval = [delay doubleValue]; } UIImage *image = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp]; image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight]; [images addObject:image]; CGImageRelease(imageRef); } CFRelease(imageSource); GIFInfo *info = [[GIFInfo alloc] initWithImages:images interval:interval]; return info; } @end