Committed by
GitHub
Merge pull request #2067 from dreampiggy/feature_gif_encoding_macOS
Add the animated GIF encoding support for SDWebImageGIFCoder on macOS(use NSImage's API)
Showing
2 changed files
with
29 additions
and
22 deletions
@@ -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 | + } | ||
156 | + } | ||
157 | + if (bitmapRep) { | ||
158 | + frameCount = [[bitmapRep valueForProperty:NSImageFrameCount] unsignedIntegerValue]; | ||
154 | } | 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 |
-
Please register or login to post a comment