[image_picker] ios: support GIF animation and the scaling (#1638)
diff --git a/packages/image_picker/CHANGELOG.md b/packages/image_picker/CHANGELOG.md
index 455ef52..ae67398 100644
--- a/packages/image_picker/CHANGELOG.md
+++ b/packages/image_picker/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.0+10
+
+* iOS: support picking GIF from gallery.
+
## 0.6.0+9
* Add missing template type parameter to `invokeMethod` calls.
diff --git a/packages/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/example/ios/Runner.xcodeproj/project.pbxproj
index cbe546c..bb359cc 100644
--- a/packages/image_picker/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/image_picker/example/ios/Runner.xcodeproj/project.pbxproj
@@ -24,6 +24,9 @@
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; };
+ 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; };
+ 9FC8F0EE229FB90B00C8D58F /* ImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */; };
F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC32F6993F4529982D9519F1 /* libPods-Runner.a */; };
/* End PBXBuildFile section */
@@ -77,6 +80,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifImage.gif; sourceTree = "<group>"; };
+ 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageUtilTests.m; sourceTree = "<group>"; };
EC32F6993F4529982D9519F1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -105,6 +110,7 @@
isa = PBXGroup;
children = (
6800491B2280D368006DD6AB /* Info.plist */,
+ 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */,
680049252280D736006DD6AB /* MetaDataUtilTests.m */,
68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */,
);
@@ -114,6 +120,7 @@
680049282280E33D006DD6AB /* TestImages */ = {
isa = PBXGroup;
children = (
+ 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */,
680049362280F2B8006DD6AB /* jpgImage.jpg */,
680049352280F2B8006DD6AB /* pngImage.png */,
);
@@ -291,6 +298,7 @@
buildActionMask = 2147483647;
files = (
680049272280D79A006DD6AB /* Assets.xcassets in Resources */,
+ 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */,
680049382280F2B9006DD6AB /* pngImage.png in Resources */,
680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */,
);
@@ -301,6 +309,7 @@
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */,
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
@@ -382,6 +391,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9FC8F0EE229FB90B00C8D58F /* ImageUtilTests.m in Sources */,
680049262280D736006DD6AB /* MetaDataUtilTests.m in Sources */,
68F4B464228B3AB500C25614 /* PhotoAssetUtilTests.m in Sources */,
);
diff --git a/packages/image_picker/example/ios/TestImages/gifImage.gif b/packages/image_picker/example/ios/TestImages/gifImage.gif
new file mode 100644
index 0000000..5f989fc
--- /dev/null
+++ b/packages/image_picker/example/ios/TestImages/gifImage.gif
Binary files differ
diff --git a/packages/image_picker/example/ios/image_picker_exampleTests/ImageUtilTests.m b/packages/image_picker/example/ios/image_picker_exampleTests/ImageUtilTests.m
new file mode 100644
index 0000000..b554f4d
--- /dev/null
+++ b/packages/image_picker/example/ios/image_picker_exampleTests/ImageUtilTests.m
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium 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 <XCTest/XCTest.h>
+#import "FLTImagePickerImageUtil.h"
+
+@interface ImageUtilTests : XCTestCase
+
+@property(strong, nonatomic) NSBundle *testBundle;
+
+@end
+
+@implementation ImageUtilTests
+
+- (void)setUp {
+ self.testBundle = [NSBundle bundleForClass:self.class];
+}
+
+- (void)testScaledImage_ShouldBeScaled {
+ NSData *data = [NSData dataWithContentsOfFile:[self.testBundle pathForResource:@"jpgImage"
+ ofType:@"jpg"]];
+ UIImage *image = [UIImage imageWithData:data];
+ UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image maxWidth:@3 maxHeight:@2];
+
+ XCTAssertEqual(newImage.size.width, 3);
+ XCTAssertEqual(newImage.size.height, 2);
+}
+
+- (void)testScaledGIFImage_ShouldBeScaled {
+ // gif image that frame size is 3 and the duration is 1 second.
+ NSData *data = [NSData dataWithContentsOfFile:[self.testBundle pathForResource:@"gifImage"
+ ofType:@"gif"]];
+ GIFInfo info = [FLTImagePickerImageUtil scaledGIFImage:data maxWidth:@3 maxHeight:@2];
+
+ NSArray<UIImage *> *images = info.images;
+ NSTimeInterval duration = info.interval;
+
+ XCTAssertEqual(images.count, 3);
+ XCTAssertEqual(duration, 1);
+
+ for (UIImage *newImage in images) {
+ XCTAssertEqual(newImage.size.width, 3);
+ XCTAssertEqual(newImage.size.height, 2);
+ }
+}
+
+@end
diff --git a/packages/image_picker/example/ios/image_picker_exampleTests/MetaDataUtilTests.m b/packages/image_picker/example/ios/image_picker_exampleTests/MetaDataUtilTests.m
index 351f3f2..bc2fa79 100644
--- a/packages/image_picker/example/ios/image_picker_exampleTests/MetaDataUtilTests.m
+++ b/packages/image_picker/example/ios/image_picker_exampleTests/MetaDataUtilTests.m
@@ -29,6 +29,12 @@
ofType:@"png"]];
XCTAssertEqual([FLTImagePickerMetaDataUtil getImageMIMETypeFromImageData:dataPNG],
FLTImagePickerMIMETypePNG);
+
+ // test gif
+ NSData *dataGIF = [NSData dataWithContentsOfFile:[self.testBundle pathForResource:@"gifImage"
+ ofType:@"gif"]];
+ XCTAssertEqual([FLTImagePickerMetaDataUtil getImageMIMETypeFromImageData:dataGIF],
+ FLTImagePickerMIMETypeGIF);
}
- (void)testSuffixFromType {
@@ -40,6 +46,10 @@
XCTAssertEqualObjects(
[FLTImagePickerMetaDataUtil imageTypeSuffixFromType:FLTImagePickerMIMETypePNG], @".png");
+ // test gif
+ XCTAssertEqualObjects(
+ [FLTImagePickerMetaDataUtil imageTypeSuffixFromType:FLTImagePickerMIMETypeGIF], @".gif");
+
// test other
XCTAssertNil([FLTImagePickerMetaDataUtil imageTypeSuffixFromType:FLTImagePickerMIMETypeOther]);
}
diff --git a/packages/image_picker/example/ios/image_picker_exampleTests/PhotoAssetUtilTests.m b/packages/image_picker/example/ios/image_picker_exampleTests/PhotoAssetUtilTests.m
index e0eaf21..f0ff04d 100644
--- a/packages/image_picker/example/ios/image_picker_exampleTests/PhotoAssetUtilTests.m
+++ b/packages/image_picker/example/ios/image_picker_exampleTests/PhotoAssetUtilTests.m
@@ -24,7 +24,9 @@
ofType:@"jpg"]];
UIImage *imageJPG = [UIImage imageWithData:dataJPG];
NSString *savedPathJPG = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:dataJPG
- image:imageJPG];
+ image:imageJPG
+ maxWidth:nil
+ maxHeight:nil];
XCTAssertNotNil(savedPathJPG);
XCTAssertEqualObjects([savedPathJPG substringFromIndex:savedPathJPG.length - 4], @".jpg");
@@ -38,7 +40,9 @@
ofType:@"png"]];
UIImage *imagePNG = [UIImage imageWithData:dataPNG];
NSString *savedPathPNG = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:dataPNG
- image:imagePNG];
+ image:imagePNG
+ maxWidth:nil
+ maxHeight:nil];
XCTAssertNotNil(savedPathPNG);
XCTAssertEqualObjects([savedPathPNG substringFromIndex:savedPathPNG.length - 4], @".png");
@@ -80,4 +84,57 @@
@"aNote");
}
+- (void)testSaveImageWithOriginalImageData_ShouldSaveAsGifAnimation {
+ // test gif
+ NSData *dataGIF = [NSData dataWithContentsOfFile:[self.testBundle pathForResource:@"gifImage"
+ ofType:@"gif"]];
+ UIImage *imageGIF = [UIImage imageWithData:dataGIF];
+ CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)dataGIF, nil);
+
+ size_t numberOfFrames = CGImageSourceGetCount(imageSource);
+
+ NSNumber *nilSize = (NSNumber *)[NSNull null];
+ NSString *savedPathGIF = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:dataGIF
+ image:imageGIF
+ maxWidth:nilSize
+ maxHeight:nilSize];
+ XCTAssertNotNil(savedPathGIF);
+ XCTAssertEqualObjects([savedPathGIF substringFromIndex:savedPathGIF.length - 4], @".gif");
+
+ NSData *newDataGIF = [NSData dataWithContentsOfFile:savedPathGIF];
+
+ CGImageSourceRef newImageSource = CGImageSourceCreateWithData((CFDataRef)newDataGIF, nil);
+
+ size_t newNumberOfFrames = CGImageSourceGetCount(newImageSource);
+
+ XCTAssertEqual(numberOfFrames, newNumberOfFrames);
+}
+
+- (void)testSaveImageWithOriginalImageData_ShouldSaveAsScalledGifAnimation {
+ // test gif
+ NSData *dataGIF = [NSData dataWithContentsOfFile:[self.testBundle pathForResource:@"gifImage"
+ ofType:@"gif"]];
+ UIImage *imageGIF = [UIImage imageWithData:dataGIF];
+
+ CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)dataGIF, nil);
+
+ size_t numberOfFrames = CGImageSourceGetCount(imageSource);
+
+ NSString *savedPathGIF = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:dataGIF
+ image:imageGIF
+ maxWidth:@3
+ maxHeight:@2];
+ NSData *newDataGIF = [NSData dataWithContentsOfFile:savedPathGIF];
+ UIImage *newImage = [[UIImage alloc] initWithData:newDataGIF];
+
+ XCTAssertEqual(newImage.size.width, 3);
+ XCTAssertEqual(newImage.size.height, 2);
+
+ CGImageSourceRef newImageSource = CGImageSourceCreateWithData((CFDataRef)newDataGIF, nil);
+
+ size_t newNumberOfFrames = CGImageSourceGetCount(newImageSource);
+
+ XCTAssertEqual(numberOfFrames, newNumberOfFrames);
+}
+
@end
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.h b/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.h
new file mode 100644
index 0000000..d9abec8
--- /dev/null
+++ b/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium 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 <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef struct GIFInfo {
+ // frames of animation
+ NSArray<UIImage *> *images;
+ NSTimeInterval interval;
+} GIFInfo;
+
+@interface FLTImagePickerImageUtil : NSObject
+
++ (UIImage *)scaledImage:(UIImage *)image
+ maxWidth:(NSNumber *)maxWidth
+ maxHeight:(NSNumber *)maxHeight;
+
+// Resize all gif animation frames.
++ (GIFInfo)scaledGIFImage:(NSData *)data
+ maxWidth:(NSNumber *)maxWidth
+ maxHeight:(NSNumber *)maxHeight;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.m b/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.m
new file mode 100644
index 0000000..cd4d1ad
--- /dev/null
+++ b/packages/image_picker/ios/Classes/FLTImagePickerImageUtil.m
@@ -0,0 +1,108 @@
+// Copyright 2019 The Chromium 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>
+
+@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;
+ }
+ }
+ }
+
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
+ [image 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) {
+ 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;
+ info.images = images;
+ info.interval = interval;
+
+ return info;
+}
+
+@end
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h b/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h
index 93a1af7..bc12f46 100644
--- a/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h
+++ b/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h
@@ -10,6 +10,7 @@
typedef enum : NSUInteger {
FLTImagePickerMIMETypePNG,
FLTImagePickerMIMETypeJPEG,
+ FLTImagePickerMIMETypeGIF,
FLTImagePickerMIMETypeOther,
} FLTImagePickerMIMEType;
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m b/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m
index 8cb4e4a..5123dec 100644
--- a/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m
+++ b/packages/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m
@@ -7,6 +7,7 @@
static const uint8_t kFirstByteJPEG = 0xFF;
static const uint8_t kFirstBytePNG = 0x89;
+static const uint8_t kFirstByteGIF = 0x47;
NSString *const kFLTImagePickerDefaultSuffix = @".jpg";
const FLTImagePickerMIMEType kFLTImagePickerMIMETypeDefault = FLTImagePickerMIMETypeJPEG;
@@ -21,6 +22,8 @@
return FLTImagePickerMIMETypeJPEG;
case kFirstBytePNG:
return FLTImagePickerMIMETypePNG;
+ case kFirstByteGIF:
+ return FLTImagePickerMIMETypeGIF;
}
return FLTImagePickerMIMETypeOther;
}
@@ -31,6 +34,8 @@
return @".jpg";
case FLTImagePickerMIMETypePNG:
return @".png";
+ case FLTImagePickerMIMETypeGIF:
+ return @".gif";
default:
return nil;
}
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h b/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h
index 52b78d4..c03f1d5 100644
--- a/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h
+++ b/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h
@@ -5,6 +5,8 @@
#import <Foundation/Foundation.h>
#import <Photos/Photos.h>
+#import "FLTImagePickerImageUtil.h"
+
NS_ASSUME_NONNULL_BEGIN
@interface FLTImagePickerPhotoAssetUtil : NSObject
@@ -12,7 +14,11 @@
+ (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info;
// Save image with correct meta data and extention copied from the original asset.
-+ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData image:(UIImage *)image;
+// maxWidth and maxHeight are used only for GIF images.
++ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData
+ image:(UIImage *)image
+ maxWidth:(nullable NSNumber *)maxWidth
+ maxHeight:(nullable NSNumber *)maxHeight;
// Save image with correct meta data and extention copied from image picker result info.
+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info image:(UIImage *)image;
diff --git a/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m
index e13436c..b5b0723 100644
--- a/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m
+++ b/packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m
@@ -3,8 +3,11 @@
// found in the LICENSE file.
#import "FLTImagePickerPhotoAssetUtil.h"
+#import "FLTImagePickerImageUtil.h"
#import "FLTImagePickerMetaDataUtil.h"
+@import MobileCoreServices;
+
@implementation FLTImagePickerPhotoAssetUtil
+ (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info {
@@ -17,7 +20,10 @@
return result.firstObject;
}
-+ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData image:(UIImage *)image {
++ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData
+ image:(UIImage *)image
+ maxWidth:(NSNumber *)maxWidth
+ maxHeight:(NSNumber *)maxHeight {
NSString *suffix = kFLTImagePickerDefaultSuffix;
FLTImagePickerMIMEType type = kFLTImagePickerMIMETypeDefault;
NSDictionary *metaData = nil;
@@ -28,7 +34,15 @@
[FLTImagePickerMetaDataUtil imageTypeSuffixFromType:type] ?: kFLTImagePickerDefaultSuffix;
metaData = [FLTImagePickerMetaDataUtil getMetaDataFromImageData:originalImageData];
}
- return [self saveImageWithMetaData:metaData image:image suffix:suffix type:type];
+ if (type == FLTImagePickerMIMETypeGIF) {
+ GIFInfo gifInfo = [FLTImagePickerImageUtil scaledGIFImage:originalImageData
+ maxWidth:maxWidth
+ maxHeight:maxHeight];
+
+ return [self saveImageWithMetaData:metaData gifInfo:gifInfo suffix:suffix];
+ } else {
+ return [self saveImageWithMetaData:metaData image:image suffix:suffix type:type];
+ }
}
+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info image:(UIImage *)image {
@@ -40,6 +54,13 @@
}
+ (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
+ gifInfo:(GIFInfo)gifInfo
+ suffix:(NSString *)suffix {
+ NSString *path = [self temporaryFilePath:suffix];
+ return [self saveImageWithMetaData:metaData gifInfo:gifInfo path:path];
+}
+
++ (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
image:(UIImage *)image
suffix:(NSString *)suffix
type:(FLTImagePickerMIMEType)type {
@@ -57,11 +78,64 @@
data = [FLTImagePickerMetaDataUtil updateMetaData:metaData toImage:data];
}
+ return [self createFile:data suffix:suffix];
+}
+
++ (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
+ gifInfo:(GIFInfo)gifInfo
+ path:(NSString *)path {
+ CGImageDestinationRef destination = CGImageDestinationCreateWithURL(
+ (CFURLRef)[NSURL fileURLWithPath:path], kUTTypeGIF, gifInfo.images.count, NULL);
+
+ NSDictionary *frameProperties = [NSDictionary
+ dictionaryWithObject:[NSDictionary
+ dictionaryWithObject:[NSNumber numberWithFloat:gifInfo.interval]
+ forKey:(NSString *)kCGImagePropertyGIFDelayTime]
+ forKey:(NSString *)kCGImagePropertyGIFDictionary];
+
+ NSMutableDictionary *gifMetaProperties = [NSMutableDictionary dictionaryWithDictionary:metaData];
+ NSMutableDictionary *gifProperties =
+ (NSMutableDictionary *)gifMetaProperties[(NSString *)kCGImagePropertyGIFDictionary];
+ if (gifMetaProperties == nil) {
+ gifProperties = [NSMutableDictionary dictionary];
+ }
+
+ gifProperties[(NSString *)kCGImagePropertyGIFLoopCount] = [NSNumber numberWithFloat:0];
+
+ CGImageDestinationSetProperties(destination, (CFDictionaryRef)gifMetaProperties);
+
+ CGImagePropertyOrientation orientation = (CGImagePropertyOrientation)[metaData[(
+ __bridge NSString *)kCGImagePropertyOrientation] integerValue];
+
+ for (NSInteger index = 0; index < gifInfo.images.count; index++) {
+ UIImage *image = (UIImage *)[gifInfo.images objectAtIndex:index];
+ UIImage *newImage = [UIImage
+ imageWithCGImage:[image CGImage]
+ scale:1.0
+ orientation:
+ [FLTImagePickerMetaDataUtil
+ getNormalizedUIImageOrientationFromCGImagePropertyOrientation:orientation]];
+
+ CGImageDestinationAddImage(destination, newImage.CGImage, (CFDictionaryRef)frameProperties);
+ }
+
+ CGImageDestinationFinalize(destination);
+ CFRelease(destination);
+
+ return path;
+}
+
++ (NSString *)temporaryFilePath:(NSString *)suffix {
NSString *fileExtension = [@"image_picker_%@" stringByAppendingString:suffix];
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *tmpFile = [NSString stringWithFormat:fileExtension, guid];
NSString *tmpDirectory = NSTemporaryDirectory();
NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
+ return tmpPath;
+}
+
++ (NSString *)createFile:(NSData *)data suffix:(NSString *)suffix {
+ NSString *tmpPath = [self temporaryFilePath:suffix];
if ([[NSFileManager defaultManager] createFileAtPath:tmpPath contents:data attributes:nil]) {
return tmpPath;
} else {
diff --git a/packages/image_picker/ios/Classes/ImagePickerPlugin.m b/packages/image_picker/ios/Classes/ImagePickerPlugin.m
index d4d5d7b..741e41f 100644
--- a/packages/image_picker/ios/Classes/ImagePickerPlugin.m
+++ b/packages/image_picker/ios/Classes/ImagePickerPlugin.m
@@ -9,6 +9,7 @@
#import <Photos/Photos.h>
#import <UIKit/UIKit.h>
+#import "FLTImagePickerImageUtil.h"
#import "FLTImagePickerMetaDataUtil.h"
#import "FLTImagePickerPhotoAssetUtil.h"
@@ -249,7 +250,7 @@
NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"];
if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) {
- image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
+ image = [FLTImagePickerImageUtil scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
}
PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info];
@@ -263,7 +264,11 @@
options:nil
resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI,
UIImageOrientation orientation, NSDictionary *_Nullable info) {
- [weakSelf saveImageWithOriginalImageData:imageData image:image];
+ // maxWidth and maxHeight are used only for GIF images.
+ [weakSelf saveImageWithOriginalImageData:imageData
+ image:image
+ maxWidth:maxWidth
+ maxHeight:maxHeight];
}];
}
}
@@ -278,59 +283,15 @@
_arguments = nil;
}
-- (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;
- }
- }
- }
-
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
- [image drawInRect:CGRectMake(0, 0, width, height)];
-
- UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- return scaledImage;
-}
-
-- (void)saveImageWithOriginalImageData:(NSData *)originalImageData image:(UIImage *)image {
+- (void)saveImageWithOriginalImageData:(NSData *)originalImageData
+ image:(UIImage *)image
+ maxWidth:(NSNumber *)maxWidth
+ maxHeight:(NSNumber *)maxHeight {
NSString *savedPath =
- [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:originalImageData image:image];
+ [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:originalImageData
+ image:image
+ maxWidth:maxWidth
+ maxHeight:maxHeight];
[self handleSavedPath:savedPath];
}
diff --git a/packages/image_picker/pubspec.yaml b/packages/image_picker/pubspec.yaml
index 9b75a8f..6cddfa1 100755
--- a/packages/image_picker/pubspec.yaml
+++ b/packages/image_picker/pubspec.yaml
@@ -5,7 +5,7 @@
- Flutter Team <flutter-dev@googlegroups.com>
- Rhodes Davis Jr. <rody.davis.jr@gmail.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker
-version: 0.6.0+9
+version: 0.6.0+10
flutter:
plugin: