FLTImagePickerImageUtil.m 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright 2013 The Flutter Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "FLTImagePickerImageUtil.h"
  5. #import <MobileCoreServices/MobileCoreServices.h>
  6. @interface GIFInfo ()
  7. @property(strong, nonatomic, readwrite) NSArray<UIImage *> *images;
  8. @property(assign, nonatomic, readwrite) NSTimeInterval interval;
  9. @end
  10. @implementation GIFInfo
  11. - (instancetype)initWithImages:(NSArray<UIImage *> *)images interval:(NSTimeInterval)interval;
  12. {
  13. self = [super init];
  14. if (self) {
  15. self.images = images;
  16. self.interval = interval;
  17. }
  18. return self;
  19. }
  20. @end
  21. @implementation FLTImagePickerImageUtil : NSObject
  22. + (UIImage *)scaledImage:(UIImage *)image
  23. maxWidth:(NSNumber *)maxWidth
  24. maxHeight:(NSNumber *)maxHeight {
  25. double originalWidth = image.size.width;
  26. double originalHeight = image.size.height;
  27. bool hasMaxWidth = maxWidth != (id)[NSNull null];
  28. bool hasMaxHeight = maxHeight != (id)[NSNull null];
  29. double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth;
  30. double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight;
  31. bool shouldDownscaleWidth = hasMaxWidth && [maxWidth doubleValue] < originalWidth;
  32. bool shouldDownscaleHeight = hasMaxHeight && [maxHeight doubleValue] < originalHeight;
  33. bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;
  34. if (shouldDownscale) {
  35. double downscaledWidth = floor((height / originalHeight) * originalWidth);
  36. double downscaledHeight = floor((width / originalWidth) * originalHeight);
  37. if (width < height) {
  38. if (!hasMaxWidth) {
  39. width = downscaledWidth;
  40. } else {
  41. height = downscaledHeight;
  42. }
  43. } else if (height < width) {
  44. if (!hasMaxHeight) {
  45. height = downscaledHeight;
  46. } else {
  47. width = downscaledWidth;
  48. }
  49. } else {
  50. if (originalWidth < originalHeight) {
  51. width = downscaledWidth;
  52. } else if (originalHeight < originalWidth) {
  53. height = downscaledHeight;
  54. }
  55. }
  56. }
  57. // Scaling the image always rotate itself based on the current imageOrientation of the original
  58. // Image. Set to orientationUp for the orignal image before scaling, so the scaled image doesn't
  59. // mess up with the pixels.
  60. UIImage *imageToScale = [UIImage imageWithCGImage:image.CGImage
  61. scale:1
  62. orientation:UIImageOrientationUp];
  63. // The image orientation is manually set to UIImageOrientationUp which swapped the aspect ratio in
  64. // some scenarios. For example, when the original image has orientation left, the horizontal
  65. // pixels should be scaled to `width` and the vertical pixels should be scaled to `height`. After
  66. // setting the orientation to up, we end up scaling the horizontal pixels to `height` and vertical
  67. // to `width`. Below swap will solve this issue.
  68. if ([image imageOrientation] == UIImageOrientationLeft ||
  69. [image imageOrientation] == UIImageOrientationRight ||
  70. [image imageOrientation] == UIImageOrientationLeftMirrored ||
  71. [image imageOrientation] == UIImageOrientationRightMirrored) {
  72. double temp = width;
  73. width = height;
  74. height = temp;
  75. }
  76. UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
  77. [imageToScale drawInRect:CGRectMake(0, 0, width, height)];
  78. UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
  79. UIGraphicsEndImageContext();
  80. return scaledImage;
  81. }
  82. + (GIFInfo *)scaledGIFImage:(NSData *)data
  83. maxWidth:(NSNumber *)maxWidth
  84. maxHeight:(NSNumber *)maxHeight {
  85. NSMutableDictionary<NSString *, id> *options = [NSMutableDictionary dictionary];
  86. options[(NSString *)kCGImageSourceShouldCache] = @(YES);
  87. options[(NSString *)kCGImageSourceTypeIdentifierHint] = (NSString *)kUTTypeGIF;
  88. CGImageSourceRef imageSource =
  89. CGImageSourceCreateWithData((CFDataRef)data, (CFDictionaryRef)options);
  90. size_t numberOfFrames = CGImageSourceGetCount(imageSource);
  91. NSMutableArray<UIImage *> *images = [NSMutableArray arrayWithCapacity:numberOfFrames];
  92. NSTimeInterval interval = 0.0;
  93. for (size_t index = 0; index < numberOfFrames; index++) {
  94. CGImageRef imageRef =
  95. CGImageSourceCreateImageAtIndex(imageSource, index, (CFDictionaryRef)options);
  96. NSDictionary *properties = (NSDictionary *)CFBridgingRelease(
  97. CGImageSourceCopyPropertiesAtIndex(imageSource, index, NULL));
  98. NSDictionary *gifProperties = properties[(NSString *)kCGImagePropertyGIFDictionary];
  99. NSNumber *delay = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
  100. if (delay == nil) {
  101. delay = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
  102. }
  103. if (interval == 0.0) {
  104. interval = [delay doubleValue];
  105. }
  106. UIImage *image = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];
  107. image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
  108. [images addObject:image];
  109. CGImageRelease(imageRef);
  110. }
  111. CFRelease(imageSource);
  112. GIFInfo *info = [[GIFInfo alloc] initWithImages:images interval:interval];
  113. return info;
  114. }
  115. @end