SLImageDecoder.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. //
  2. // SLImageDecoder.m
  3. // SLImageView
  4. //
  5. // Created by 王双龙 on 2018/10/26.
  6. // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea. All rights reserved.
  7. //
  8. #import "SLImageDecoder.h"
  9. #import <pthread.h>
  10. #import <webp/decode.h>
  11. #import <webp/encode.h>
  12. #import <webp/demux.h>
  13. #import <webp/mux.h>
  14. //来源YYImage
  15. #define SL_FOUR_CC(c1,c2,c3,c4) ((uint32_t)(((c4) << 24) | ((c3) << 16) | ((c2) << 8) | (c1)))
  16. #define SL_TWO_CC(c1,c2) ((uint16_t)(((c2) << 8) | (c1)))
  17. @implementation SLImageFrame
  18. @end
  19. @interface SLImageDecoder () {
  20. CGImageSourceRef _imageSource; //图像源数据
  21. WebPDemuxer *_webpSource; //webp格式图片的源数据 https://developers.google.com/speed/webp/docs/api
  22. }
  23. @end
  24. @implementation SLImageDecoder
  25. - (void)decoderWithData:(NSData *)data scale:(CGFloat)scale{
  26. _data = data;
  27. _scale = scale;
  28. _loopCount = 1;
  29. _totalTime = 0;
  30. _frameCount = 1;
  31. _imageType = imageDataType(_data);
  32. [self startDecoder];
  33. }
  34. - (void)startDecoder{
  35. [self imageFrameCount];
  36. switch (_imageType) {
  37. case SLImageTypeGIF:
  38. _loopCount = [self loopCountForGifData:_data];
  39. break;
  40. case SLImageTypeWebP:
  41. _loopCount = [self loopCountForGifData:_data];
  42. break;
  43. case SLImageTypePNG:
  44. _loopCount = [self loopCountForGifData:_data];
  45. break;
  46. default:
  47. break;
  48. }
  49. }
  50. #pragma mark - 图片信息
  51. static void SLCGDataProviderReleaseDataCallback(void *info, const void *data, size_t size) {
  52. if (info) free(info);
  53. }
  54. CGColorSpaceRef SLCGColorSpaceGetDeviceRGB() {
  55. static CGColorSpaceRef space;
  56. static dispatch_once_t onceToken;
  57. dispatch_once(&onceToken, ^{
  58. space = CGColorSpaceCreateDeviceRGB();
  59. });
  60. return space;
  61. }
  62. /**
  63. 返回图片的类型
  64. */
  65. NSInteger imageDataType(NSData * data){
  66. CFDataRef dataRef = (__bridge CFDataRef)data;
  67. if (!data) return SLImageTypeUnknown;
  68. uint64_t length = CFDataGetLength(dataRef);
  69. if (length < 16) return SLImageTypeUnknown;
  70. const char *bytes = (char *)CFDataGetBytePtr(dataRef);
  71. uint32_t magic4 = *((uint32_t *)bytes);
  72. switch (magic4) {
  73. case SL_FOUR_CC(0x4D, 0x4D, 0x00, 0x2A): { // big endian TIFF
  74. return SLImageTypeTIFF;
  75. } break;
  76. case SL_FOUR_CC(0x49, 0x49, 0x2A, 0x00): { // little endian TIFF
  77. return SLImageTypeTIFF;
  78. } break;
  79. case SL_FOUR_CC(0x00, 0x00, 0x01, 0x00): { // ICO
  80. return SLImageTypeICO;
  81. } break;
  82. case SL_FOUR_CC(0x00, 0x00, 0x02, 0x00): { // CUR
  83. return SLImageTypeICO;
  84. } break;
  85. case SL_FOUR_CC('i', 'c', 'n', 's'): { // ICNS
  86. return SLImageTypeICNS;
  87. } break;
  88. case SL_FOUR_CC('G', 'I', 'F', '8'): { // GIF
  89. return SLImageTypeGIF;
  90. } break;
  91. case SL_FOUR_CC(0x89, 'P', 'N', 'G'): { // PNG
  92. uint32_t tmp = *((uint32_t *)(bytes + 4));
  93. if (tmp == SL_FOUR_CC('\r', '\n', 0x1A, '\n')) {
  94. return SLImageTypePNG;
  95. }
  96. } break;
  97. case SL_FOUR_CC('R', 'I', 'F', 'F'): { // WebP
  98. uint32_t tmp = *((uint32_t *)(bytes + 8));
  99. if (tmp == SL_FOUR_CC('W', 'E', 'B', 'P')) {
  100. return SLImageTypeWebP;
  101. }
  102. } break;
  103. }
  104. uint16_t magic2 = *((uint16_t *)bytes);
  105. switch (magic2) {
  106. case SL_TWO_CC('B', 'A'):
  107. case SL_TWO_CC('B', 'M'):
  108. case SL_TWO_CC('I', 'C'):
  109. case SL_TWO_CC('P', 'I'):
  110. case SL_TWO_CC('C', 'I'):
  111. case SL_TWO_CC('C', 'P'): { // BMP
  112. return SLImageTypeBMP;
  113. }
  114. case SL_TWO_CC(0xFF, 0x4F): { // JPEG2000
  115. return SLImageTypeJPEG2000;
  116. }
  117. }
  118. // JPG FF D8 FF
  119. if (memcmp(bytes,"\377\330\377",3) == 0) return SLImageTypeJPEG;
  120. // JP2
  121. if (memcmp(bytes + 4, "\152\120\040\040\015", 5) == 0) return SLImageTypeJPEG2000;
  122. return SLImageTypeUnknown;
  123. };
  124. /**
  125. 每一帧图片的方向
  126. */
  127. UIImageOrientation SLUIImageOrientationFromEXIFValue(NSInteger value) {
  128. switch (value) {
  129. case kCGImagePropertyOrientationUp: return UIImageOrientationUp;
  130. case kCGImagePropertyOrientationDown: return UIImageOrientationDown;
  131. case kCGImagePropertyOrientationLeft: return UIImageOrientationLeft;
  132. case kCGImagePropertyOrientationRight: return UIImageOrientationRight;
  133. case kCGImagePropertyOrientationUpMirrored: return UIImageOrientationUpMirrored;
  134. case kCGImagePropertyOrientationDownMirrored: return UIImageOrientationDownMirrored;
  135. case kCGImagePropertyOrientationLeftMirrored: return UIImageOrientationLeftMirrored;
  136. case kCGImagePropertyOrientationRightMirrored: return UIImageOrientationRightMirrored;
  137. default: return UIImageOrientationUp;
  138. }
  139. }
  140. /**
  141. 获取每一帧图片解压缩后的位图
  142. */
  143. CGImageRef SLCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay) {
  144. if (!imageRef) return NULL;
  145. size_t width = CGImageGetWidth(imageRef);
  146. size_t height = CGImageGetHeight(imageRef);
  147. if (width == 0 || height == 0) return NULL;
  148. if (decodeForDisplay) { //decode with redraw (may lose some precision)
  149. CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
  150. BOOL hasAlpha = NO;
  151. if (alphaInfo == kCGImageAlphaPremultipliedLast ||
  152. alphaInfo == kCGImageAlphaPremultipliedFirst ||
  153. alphaInfo == kCGImageAlphaLast ||
  154. alphaInfo == kCGImageAlphaFirst) {
  155. hasAlpha = YES;
  156. }
  157. // BGRA8888 (premultiplied) or BGRX8888
  158. // same as UIGraphicsBeginImageContext() and -[UIView drawRect:]
  159. CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
  160. bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
  161. CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, SLCGColorSpaceGetDeviceRGB(), bitmapInfo);
  162. if (!context) return NULL;
  163. CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
  164. CGImageRef newImage = CGBitmapContextCreateImage(context);
  165. CFRelease(context);
  166. return newImage;
  167. } else {
  168. CGColorSpaceRef space = CGImageGetColorSpace(imageRef);
  169. size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
  170. size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
  171. size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
  172. CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
  173. if (bytesPerRow == 0 || width == 0 || height == 0) return NULL;
  174. CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
  175. if (!dataProvider) return NULL;
  176. CFDataRef data = CGDataProviderCopyData(dataProvider); // decode
  177. if (!data) return NULL;
  178. CGDataProviderRef newProvider = CGDataProviderCreateWithCFData(data);
  179. CFRelease(data);
  180. if (!newProvider) return NULL;
  181. CGImageRef newImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, newProvider, NULL, false, kCGRenderingIntentDefault);
  182. CFRelease(newProvider);
  183. return newImage;
  184. }
  185. }
  186. /**
  187. 获取动图的图片帧数
  188. */
  189. - (void)imageFrameCount{
  190. if(_imageType == SLImageTypeWebP) {
  191. if (_webpSource) WebPDemuxDelete(_webpSource);
  192. _webpSource = NULL;
  193. WebPData webPData = {0};
  194. webPData.bytes = _data.bytes;
  195. webPData.size = _data.length;
  196. WebPDemuxer *demuxer = WebPDemux(&webPData);
  197. if (!demuxer) _frameCount = 1;
  198. _webpSource = demuxer;
  199. NSInteger frameCout = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
  200. _frameCount = frameCout;
  201. } else {
  202. //将GIF图片转换成对应的图片源
  203. _imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)_data, NULL);
  204. //获取其中图片源个数,即由多少帧图片组成
  205. size_t frameCout = CGImageSourceGetCount(_imageSource);
  206. _frameCount = frameCout;
  207. }
  208. }
  209. /**
  210. 获取某一帧的图片信息:索引、持续时长、宽高、方向、解码后的image
  211. index >= 0
  212. */
  213. - (SLImageFrame *)imageFrameAtIndex:(NSInteger)index{
  214. if(index >= _frameCount || index < 0) return nil;
  215. SLImageFrame * imageFrame = [[SLImageFrame alloc] init];
  216. imageFrame.index = index;
  217. CGFloat width = 0, height = 0;
  218. NSInteger orientation = UIImageOrientationUp;
  219. if(_imageType == SLImageTypeWebP) {
  220. WebPIterator iter = {0};
  221. if (WebPDemuxGetFrame(_webpSource, (int)index, &iter)) {
  222. imageFrame.index = index;
  223. imageFrame.duration = iter.duration / 1000.0;
  224. imageFrame.width = iter.width;
  225. imageFrame.height = iter.height;
  226. imageFrame.imageOrientation = UIImageOrientationUp;
  227. imageFrame.image = [self imageAtIndex:index];
  228. imageFrame.offsetX = iter.x_offset;
  229. imageFrame.offsetY = self.canvasSize.height - iter.y_offset - iter.height;
  230. };
  231. WebPDemuxReleaseIterator(&iter);
  232. }else{
  233. // image属性
  234. CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(_imageSource, index, NULL);
  235. imageFrame.duration = [self imageDurationAtIndex:index];
  236. CFTypeRef valueWidth = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
  237. if (valueWidth){
  238. CFNumberGetValue(valueWidth, kCFNumberCGFloatType, &width);
  239. imageFrame.width = width;
  240. }
  241. CFTypeRef valueHeight = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
  242. if (valueHeight){
  243. CFNumberGetValue(valueHeight, kCFNumberCGFloatType, &height);
  244. imageFrame.height = height;
  245. }
  246. CFTypeRef valueOrientation = CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
  247. if (valueOrientation) {
  248. CFNumberGetValue(valueOrientation, kCFNumberNSIntegerType, &orientation);
  249. imageFrame.imageOrientation = SLUIImageOrientationFromEXIFValue(orientation);
  250. }
  251. imageFrame.image = [self imageAtIndex:index];
  252. CFRelease(imageProperties);
  253. }
  254. return imageFrame;
  255. }
  256. /**
  257. 获取某一帧image
  258. */
  259. - (UIImage *)imageAtIndex:(NSInteger)index{
  260. if(index >= _frameCount || index < 0) return nil;
  261. CGImageRef imageRef;
  262. if(_imageType == SLImageTypeWebP) {
  263. WebPIterator iter;
  264. if (!WebPDemuxGetFrame(_webpSource, (int)(index), &iter)) return NULL; // demux webp frame data
  265. // frame numbers are one-based in webp -----------^
  266. int frameWidth = iter.width;
  267. int frameHeight = iter.height;
  268. if (frameWidth < 1 || frameHeight < 1) return NULL;
  269. int width = frameWidth;
  270. int height = frameHeight;
  271. const uint8_t *payload = iter.fragment.bytes;
  272. size_t payloadSize = iter.fragment.size;
  273. WebPDecoderConfig config;
  274. if (!WebPInitDecoderConfig(&config)) {
  275. WebPDemuxReleaseIterator(&iter);
  276. return NULL;
  277. }
  278. if (WebPGetFeatures(payload , payloadSize, &config.input) != VP8_STATUS_OK) {
  279. WebPDemuxReleaseIterator(&iter);
  280. return NULL;
  281. }
  282. size_t bitsPerComponent = 8;
  283. size_t bitsPerPixel = 32;
  284. size_t bytesPerRow = ((bitsPerPixel / 8 * width + (32 - 1)) / 32) * 32;
  285. size_t length = bytesPerRow * height;
  286. CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst; //bgrA
  287. void *pixels = calloc(1, length);
  288. if (!pixels) {
  289. WebPDemuxReleaseIterator(&iter);
  290. return NULL;
  291. }
  292. config.output.colorspace = MODE_bgrA;
  293. config.output.is_external_memory = 1;
  294. config.output.u.RGBA.rgba = pixels;
  295. config.output.u.RGBA.stride = (int)bytesPerRow;
  296. config.output.u.RGBA.size = length;
  297. VP8StatusCode result = WebPDecode(payload, payloadSize, &config); // decode
  298. if ((result != VP8_STATUS_OK) && (result != VP8_STATUS_NOT_ENOUGH_DATA)) {
  299. WebPDemuxReleaseIterator(&iter);
  300. free(pixels);
  301. return NULL;
  302. }
  303. WebPDemuxReleaseIterator(&iter);
  304. // if (extendToCanvas && (iter.x_offset != 0 || iter.y_offset != 0)) {
  305. // void *tmp = calloc(1, length);
  306. // if (tmp) {
  307. // vImage_Buffer src = {pixels, height, width, bytesPerRow};
  308. // vImage_Buffer dest = {tmp, height, width, bytesPerRow};
  309. // vImage_CGAffineTransform transform = {1, 0, 0, 1, iter.x_offset, -iter.y_offset};
  310. // uint8_t backColor[4] = {0};
  311. // vImage_Error error = vImageAffineWarpCG_ARGB8888(&src, &dest, NULL, &transform, backColor, kvImageBackgroundColorFill);
  312. // if (error == kvImageNoError) {
  313. // memcpy(pixels, tmp, length);
  314. // }
  315. // free(tmp);
  316. // }
  317. // }
  318. CGDataProviderRef provider = CGDataProviderCreateWithData(pixels, pixels, length, SLCGDataProviderReleaseDataCallback);
  319. if (!provider) {
  320. free(pixels);
  321. return NULL;
  322. }
  323. pixels = NULL; // hold by provider
  324. imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, SLCGColorSpaceGetDeviceRGB(), bitmapInfo, provider, NULL, false, kCGRenderingIntentDefault);
  325. CFRelease(provider);
  326. } else {
  327. //从GIF图片中取出某一帧源图片 https://www.jianshu.com/p/e9843d5b70a2
  328. imageRef = CGImageSourceCreateImageAtIndex(_imageSource, index, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO),(id)kCGImageSourceShouldCacheImmediately:@(NO)}); //默认会缓存到内存,和imageNamed一样;这里我们选择不缓存,减少内存占用
  329. }
  330. //将图片源解码后转换成UIImageView能直接使用的图片源NO
  331. CGImageRef imageRefDecoded = SLCGImageCreateDecodedCopy(imageRef, YES);
  332. if (imageRefDecoded) {
  333. CFRelease(imageRef);
  334. imageRef = imageRefDecoded;
  335. }
  336. UIImage* image = [UIImage imageWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
  337. if(imageRef){
  338. CFRelease(imageRef);
  339. }
  340. return image;
  341. }
  342. /**
  343. 某一帧持续时长
  344. */
  345. - (NSTimeInterval)imageDurationAtIndex:(NSUInteger)index{
  346. if(index >= _frameCount || index < 0) return 0;
  347. NSTimeInterval duration = 0;
  348. if(_imageType == SLImageTypeWebP) {
  349. WebPIterator iter = {0};
  350. if (WebPDemuxGetFrame(_webpSource, (int)index, &iter)) {
  351. duration = iter.duration / 1000.0;
  352. };
  353. WebPDemuxReleaseIterator(&iter);
  354. }else{
  355. // image属性
  356. CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(_imageSource, index, NULL);
  357. if (imageProperties) {
  358. CFDictionaryRef gifProperties;
  359. BOOL result = CFDictionaryGetValueIfPresent(imageProperties, kCGImagePropertyGIFDictionary, (const void **)&gifProperties);
  360. if (result) {
  361. const void *durationValue;
  362. if (CFDictionaryGetValueIfPresent(gifProperties, kCGImagePropertyGIFUnclampedDelayTime, &durationValue)) {
  363. duration = [(__bridge NSNumber *)durationValue doubleValue];
  364. if (duration < 0) {
  365. if (CFDictionaryGetValueIfPresent(gifProperties, kCGImagePropertyGIFDelayTime, &durationValue)) {
  366. duration = [(__bridge NSNumber *)durationValue doubleValue];
  367. }
  368. }
  369. }
  370. }
  371. }
  372. CFRelease(imageProperties);
  373. }
  374. // http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility
  375. if (duration < 0.02) duration = 0.1;
  376. return duration;
  377. }
  378. /**
  379. 返回gif文件的循环次数 0表示无限循环
  380. */
  381. - (NSInteger)loopCountForGifData:(NSData *)data{
  382. NSInteger loopCount = 1;
  383. if (_imageType == SLImageTypeWebP) {
  384. loopCount = WebPDemuxGetI(_webpSource, WEBP_FF_LOOP_COUNT);
  385. } else {
  386. //gif相关属性
  387. CFDictionaryRef properties = CGImageSourceCopyProperties(_imageSource, NULL);
  388. if (properties) {
  389. CFDictionaryRef gif = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
  390. if (gif) {
  391. CFTypeRef loop = CFDictionaryGetValue(gif, kCGImagePropertyGIFLoopCount);
  392. if (loop) {
  393. //如果loop == NULL,表示不循环播放,当loopCount == 0时,表示无限循环;
  394. CFNumberGetValue(loop, kCFNumberNSIntegerType, &loopCount);
  395. if(loopCount == 0){
  396. // NSLog(@"无限循环播放");
  397. }else{
  398. // NSLog(@"循环次数:%ld ",loopCount);
  399. }
  400. }else{
  401. // NSLog(@"循环一次");
  402. };
  403. }
  404. }
  405. CFRelease(properties);
  406. }
  407. return loopCount;
  408. }
  409. /**
  410. 循环一次所需的总时长
  411. */
  412. - (NSTimeInterval)totalTimeOfLoopOnce{
  413. NSTimeInterval duration = 0;
  414. @autoreleasepool {
  415. for(int i = 0; i < _frameCount; i++){
  416. duration += [self imageDurationAtIndex:i];
  417. }
  418. }
  419. return duration;
  420. }
  421. #pragma mark - Getter
  422. - (CGSize)canvasSize{
  423. if(_imageType == SLImageTypeWebP) {
  424. CGFloat width = WebPDemuxGetI(_webpSource, WEBP_FF_CANVAS_WIDTH);
  425. CGFloat height = WebPDemuxGetI(_webpSource, WEBP_FF_CANVAS_HEIGHT);
  426. return CGSizeMake(width, height);
  427. }else{
  428. // image属性
  429. CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL);
  430. NSDictionary *properties = (__bridge NSDictionary *)imageProperties;
  431. CGFloat width = [properties[(NSString *)kCGImagePropertyPixelWidth] floatValue];
  432. CGFloat height = [properties[(NSString *)kCGImagePropertyPixelHeight] floatValue];
  433. CFRelease(imageProperties);
  434. return CGSizeMake(width, height);
  435. }
  436. }
  437. - (NSTimeInterval)totalTime{
  438. if (_totalTime == 0) {
  439. _totalTime = [self totalTimeOfLoopOnce];
  440. }
  441. return _totalTime;
  442. }
  443. - (void)dealloc{
  444. if (_imageSource) {
  445. CFRelease(_imageSource);
  446. }
  447. if (_webpSource) {
  448. WebPDemuxDelete(_webpSource);
  449. }
  450. }
  451. @end