| // 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 <MobileCoreServices/MobileCoreServices.h> | 
 |  | 
 | @interface GIFInfo () | 
 |  | 
 | @property(strong, nonatomic, readwrite) NSArray<UIImage *> *images; | 
 | @property(assign, nonatomic, readwrite) NSTimeInterval interval; | 
 |  | 
 | @end | 
 |  | 
 | @implementation GIFInfo | 
 |  | 
 | - (instancetype)initWithImages:(NSArray<UIImage *> *)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]; | 
 |  | 
 |   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<NSString *, id> *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<UIImage *> *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 |