首先先簡單介紹一下gif的幾個算是術語吧:
frame(幀):一個gif可以簡單認為是多張image組成的動畫,一幀就是其中一張圖片image.
frameCount(幀數): 就是一個gif有多少幀
loopCount(播放次數):有些gif播放到一定次數就停止了,如果為0就代表gif一直循環播放。
delayTime(延遲時間):每一幀播放的時間,也就是說這幀顯示到delayTime就轉到下一幀。
所以gif播放主要就是把每一幀image解析出來,然后每一幀顯示它對應的delaytime,然后再顯示下一張。如此循環下去。
下面是純粹實現由系統提供的解碼:
-(void)decodeWithFilePath:(NSString *)filePath { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() { NSData *data = [NSData dataWithContentsOfFile:self.path]; [self decodeWithData:data]; }); } -(void)decodeWithData:(NSData *)data { CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef) data, NULL); if (src) { //獲取gif的幀數 NSUInteger frameCount = CGImageSourceGetCount(src); //獲取GfiImage的基本數據 NSDictionary *gifProperties = (NSDictionary *) CGImageSourceCopyProperties(src, NULL); if(gifProperties) { //由GfiImage的基本數據獲取gif數據 NSDictionary *gifDictionary =[gifProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary]; //獲取gif的播放次數 NSUInteger loopCount = [[gifDictionary objectForKey:(NSString*)kCGImagePropertyGIFLoopCount] integerValue]; for (NSUInteger i = 0; i < frameCount; i++) { //得到每一幀的CGImage CGImageRef img = CGImageSourceCreateImageAtIndex(src, (size_t) i, NULL); if (img) { //把CGImage轉化為UIImage UIImage *frameImage = [UIImage imageWithCGImage:img]; //獲取每一幀的圖片信息 NSDictionary *frameProperties = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(src, (size_t) i, NULL); if (frameProperties) { //由每一幀的圖片信息獲取gif信息 NSDictionary *frameDictionary = [frameProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary]; //取出每一幀的delaytime CGFloat delayTime = [[frameDictionary objectForKey:(NSString*)kCGImagePropertyGIFDelayTime] floatValue];
//TODO 這里可以實現邊解碼邊回調播放或者把每一幀image和delayTime存儲起來 CFRelease(frameProperties); } CGImageRelease(img); } } CFRelease(gifProperties); } CFRelease(src); } }
上面我們可以看到系統解碼已經把每一幀的image和delayTime解析出來,並且能知道gif一共的幀數和播放次數。所以我們實現gif播放就是啟動一個timer,可以以一個適當的時間運行,如果發現time激活的時間間隔大於這一幀的delayTime,就把image換成下一幀。如此循環,當然,如果loopCount大於0,並且播放次數大於loopCount,就把timer停止就行了。這樣是可以實現變解碼邊播放的,並且都是調用系統解碼,效率也不錯。
因項目需要,模仿了SDWebImage實現了一個UIImageView的category,只需要提供一個路徑就能實現gif的邊解碼邊播放,而不用考慮timer或者其他處理事件,並且因為是category,所以不直接用UIImageView即可。插一句,SDWebImage這個開源庫的封轉方法是蠻值得學習的。
@interface UIImageView(GifImageView)<GifPlayerDelegate> - (void)setGifFilePath:(NSString*)filePath; - (void)setGifFilePath:(NSString*)filePath placeholderImage:(UIImage *)placeholder; - (void)setGifFilePath:(NSString*)filePath placeholderImage:(UIImage *)placeholder failure:(GifPlayerError)error; - (void)setGifUrlString:(NSString*)urlString; - (void)setGifUrlString:(NSString*)urlString placeholderImage:(UIImage *)placeholder; - (void)setGifUrlString:(NSString*)urlString placeholderImage:(UIImage *)placeholder failure:(GifPlayerError)error; -(void)gifPasue; -(void)gifResume; -(void)gifStop;
僅供參考。代碼整理一下再貼出來。