123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- //
- // UIImage+animatedGIF
- // Nextcloud
- //
- // Created by Marino Faggiana on 28/05/17.
- // Copyright (c) 2017 Marino Faggiana. All rights reserved.
- //
- // Author Marino Faggiana <marino.faggiana@nextcloud.com>
- // Found in Internet
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- //
- #import "UIImage+animatedGIF.h"
- #if __has_feature(objc_arc)
- #define toCF (__bridge CFTypeRef)
- #define fromCF (__bridge id)
- #else
- #define toCF (CFTypeRef)
- #define fromCF (id)
- #endif
- @implementation UIImage (animatedGIF)
- static int delayCentisecondsForImageAtIndex(CGImageSourceRef const source, size_t const i) {
- int delayCentiseconds = 1;
- CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
- if (properties) {
- CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
- if (gifProperties) {
- NSNumber *number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime);
- if (number == NULL || [number doubleValue] == 0) {
- number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime);
- }
- if ([number doubleValue] > 0) {
- // Even though the GIF stores the delay as an integer number of centiseconds, ImageIO “helpfully” converts that to seconds for us.
- delayCentiseconds = (int)lrint([number doubleValue] * 100);
- }
- }
- CFRelease(properties);
- }
- return delayCentiseconds;
- }
- static void createImagesAndDelays(CGImageSourceRef source, size_t count, CGImageRef imagesOut[count], int delayCentisecondsOut[count]) {
- for (size_t i = 0; i < count; ++i) {
- imagesOut[i] = CGImageSourceCreateImageAtIndex(source, i, NULL);
- delayCentisecondsOut[i] = delayCentisecondsForImageAtIndex(source, i);
- }
- }
- static int sum(size_t const count, int const *const values) {
- int theSum = 0;
- for (size_t i = 0; i < count; ++i) {
- theSum += values[i];
- }
- return theSum;
- }
- static int pairGCD(int a, int b) {
- if (a < b)
- return pairGCD(b, a);
- while (true) {
- int const r = a % b;
- if (r == 0)
- return b;
- a = b;
- b = r;
- }
- }
- static int vectorGCD(size_t const count, int const *const values) {
- int gcd = values[0];
- for (size_t i = 1; i < count; ++i) {
- // Note that after I process the first few elements of the vector, `gcd` will probably be smaller than any remaining element. By passing the smaller value as the second argument to `pairGCD`, I avoid making it swap the arguments.
- gcd = pairGCD(values[i], gcd);
- }
- return gcd;
- }
- static NSArray *frameArray(size_t const count, CGImageRef const images[count], int const delayCentiseconds[count], int const totalDurationCentiseconds) {
- int const gcd = vectorGCD(count, delayCentiseconds);
- size_t const frameCount = totalDurationCentiseconds / gcd;
- UIImage *frames[frameCount];
- for (size_t i = 0, f = 0; i < count; ++i) {
- UIImage *const frame = [UIImage imageWithCGImage:images[i]];
- for (size_t j = delayCentiseconds[i] / gcd; j > 0; --j) {
- frames[f++] = frame;
- }
- }
- return [NSArray arrayWithObjects:frames count:frameCount];
- }
- static void releaseImages(size_t const count, CGImageRef const images[count]) {
- for (size_t i = 0; i < count; ++i) {
- CGImageRelease(images[i]);
- }
- }
- static UIImage *animatedImageWithAnimatedGIFImageSource(CGImageSourceRef const source) {
- size_t const count = CGImageSourceGetCount(source);
- CGImageRef images[count];
- int delayCentiseconds[count]; // in centiseconds
- createImagesAndDelays(source, count, images, delayCentiseconds);
- int const totalDurationCentiseconds = sum(count, delayCentiseconds);
- NSArray *const frames = frameArray(count, images, delayCentiseconds, totalDurationCentiseconds);
- UIImage *const animation = [UIImage animatedImageWithImages:frames duration:(NSTimeInterval)totalDurationCentiseconds / 100.0];
- releaseImages(count, images);
- return animation;
- }
- static UIImage *animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceRef CF_RELEASES_ARGUMENT source) {
- if (source) {
- UIImage *const image = animatedImageWithAnimatedGIFImageSource(source);
- CFRelease(source);
- return image;
- } else {
- return nil;
- }
- }
- + (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)data {
- return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF data, NULL));
- }
- + (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)url {
- return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL));
- }
- @end
|