Authored by DreamPiggy

Add the animated GIF encoding support for SDWebImageGIFCoder on macOS(use NSImage’s API)

@@ -45,8 +45,7 @@ @@ -45,8 +45,7 @@
45 45
46 if (count <= 1) { 46 if (count <= 1) {
47 animatedImage = [[UIImage alloc] initWithData:data]; 47 animatedImage = [[UIImage alloc] initWithData:data];
48 - }  
49 - else { 48 + } else {
50 NSMutableArray *images = [NSMutableArray array]; 49 NSMutableArray *images = [NSMutableArray array];
51 50
52 NSTimeInterval duration = 0.0f; 51 NSTimeInterval duration = 0.0f;
@@ -103,9 +102,7 @@ @@ -103,9 +102,7 @@
103 NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; 102 NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
104 if (delayTimeUnclampedProp) { 103 if (delayTimeUnclampedProp) {
105 frameDuration = [delayTimeUnclampedProp floatValue]; 104 frameDuration = [delayTimeUnclampedProp floatValue];
106 - }  
107 - else {  
108 - 105 + } else {
109 NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; 106 NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
110 if (delayTimeProp) { 107 if (delayTimeProp) {
111 frameDuration = [delayTimeProp floatValue]; 108 frameDuration = [delayTimeProp floatValue];
@@ -147,11 +144,22 @@ @@ -147,11 +144,22 @@
147 } 144 }
148 145
149 NSMutableData *imageData = [NSMutableData data]; 146 NSMutableData *imageData = [NSMutableData data];
  147 + NSUInteger frameCount = 0; // assume static images by default
150 CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:format]; 148 CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:format];
151 - NSUInteger frameCount = 1;  
152 - if (image.images) {  
153 - frameCount = image.images.count; 149 +#if SD_MAC
  150 + NSBitmapImageRep *bitmapRep;
  151 + for (NSImageRep *imageRep in image.representations) {
  152 + if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
  153 + bitmapRep = (NSBitmapImageRep *)imageRep;
  154 + break;
  155 + }
154 } 156 }
  157 + if (bitmapRep) {
  158 + frameCount = [[bitmapRep valueForProperty:NSImageFrameCount] unsignedIntegerValue];
  159 + }
  160 +#else
  161 + frameCount = image.images.count;
  162 +#endif
155 163
156 // Create an image destination. GIF does not support EXIF image orientation 164 // Create an image destination. GIF does not support EXIF image orientation
157 CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL); 165 CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL);
@@ -160,29 +168,30 @@ @@ -160,29 +168,30 @@
160 return nil; 168 return nil;
161 } 169 }
162 170
163 -#ifdef SD_MAC  
164 - CGImageDestinationAddImage(imageDestination, image.CGImage, nil);  
165 -#else  
166 - if (!image.images) { 171 + if (frameCount == 0) {
167 // for static single GIF images 172 // for static single GIF images
168 CGImageDestinationAddImage(imageDestination, image.CGImage, nil); 173 CGImageDestinationAddImage(imageDestination, image.CGImage, nil);
169 } else { 174 } else {
170 // for animated GIF images 175 // for animated GIF images
171 NSUInteger loopCount = image.sd_imageLoopCount; 176 NSUInteger loopCount = image.sd_imageLoopCount;
172 - NSTimeInterval totalDuration = image.duration;  
173 - NSTimeInterval frameDuration = totalDuration / frameCount;  
174 NSDictionary *gifProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary: @{(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount : @(loopCount)}}; 177 NSDictionary *gifProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary: @{(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount : @(loopCount)}};
175 CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)gifProperties); 178 CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)gifProperties);
176 for (size_t i = 0; i < frameCount; i++) { 179 for (size_t i = 0; i < frameCount; i++) {
177 @autoreleasepool { 180 @autoreleasepool {
178 - NSDictionary *frameProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary : @{(__bridge_transfer NSString *)kCGImagePropertyGIFUnclampedDelayTime : @(frameDuration)}}; 181 +#if SD_MAC
  182 + // NSBitmapImageRep need to manually change frame. "Good taste" API
  183 + [bitmapRep setProperty:NSImageCurrentFrame withValue:@(i)];
  184 + float frameDuration = [[bitmapRep valueForProperty:NSImageCurrentFrameDuration] floatValue];
  185 + CGImageRef frameImageRef = bitmapRep.CGImage;
  186 +#else
  187 + float frameDuration = image.duration / frameCount;
179 CGImageRef frameImageRef = image.images[i].CGImage; 188 CGImageRef frameImageRef = image.images[i].CGImage;
  189 +#endif
  190 + NSDictionary *frameProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary : @{(__bridge_transfer NSString *)kCGImagePropertyGIFUnclampedDelayTime : @(frameDuration)}};
180 CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties); 191 CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
181 } 192 }
182 } 193 }
183 } 194 }
184 -#endif  
185 -  
186 // Finalize the destination. 195 // Finalize the destination.
187 if (CGImageDestinationFinalize(imageDestination) == NO) { 196 if (CGImageDestinationFinalize(imageDestination) == NO) {
188 // Handle failure. 197 // Handle failure.
@@ -89,11 +89,11 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -89,11 +89,11 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
89 return nil; 89 return nil;
90 } 90 }
91 91
  92 + UIImage *image = [[UIImage alloc] initWithData:data];
  93 +
92 #if SD_MAC 94 #if SD_MAC
93 - return [[UIImage alloc] initWithData:data]; 95 + return image;
94 #else 96 #else
95 -  
96 - UIImage *image = [[UIImage alloc] initWithData:data];  
97 if (!image) { 97 if (!image) {
98 return nil; 98 return nil;
99 } 99 }
@@ -105,14 +105,12 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -105,14 +105,12 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
105 image = [UIImage animatedImageWithImages:@[image] duration:image.duration]; 105 image = [UIImage animatedImageWithImages:@[image] duration:image.duration];
106 return image; 106 return image;
107 } 107 }
108 -#if SD_UIKIT || SD_WATCH  
109 UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data]; 108 UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data];
110 if (orientation != UIImageOrientationUp) { 109 if (orientation != UIImageOrientationUp) {
111 image = [UIImage imageWithCGImage:image.CGImage 110 image = [UIImage imageWithCGImage:image.CGImage
112 scale:image.scale 111 scale:image.scale
113 orientation:orientation]; 112 orientation:orientation];
114 } 113 }
115 -#endif  
116 114
117 return image; 115 return image;
118 #endif 116 #endif