SDAnimatedImageRep.m 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. * This file is part of the SDWebImage package.
  3. * (c) Olivier Poitrey <rs@dailymotion.com>
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. #import "SDAnimatedImageRep.h"
  9. #if SD_MAC
  10. #import "SDImageIOAnimatedCoderInternal.h"
  11. #import "SDImageGIFCoder.h"
  12. #import "SDImageAPNGCoder.h"
  13. #import "SDImageHEICCoder.h"
  14. #import "SDImageHEICCoderInternal.h"
  15. @implementation SDAnimatedImageRep {
  16. CGImageSourceRef _imageSource;
  17. }
  18. - (void)dealloc {
  19. if (_imageSource) {
  20. CFRelease(_imageSource);
  21. _imageSource = NULL;
  22. }
  23. }
  24. // `NSBitmapImageRep`'s `imageRepWithData:` is not designed initlizer
  25. + (instancetype)imageRepWithData:(NSData *)data {
  26. SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
  27. return imageRep;
  28. }
  29. // We should override init method for `NSBitmapImageRep` to do initlize about animated image format
  30. - (instancetype)initWithData:(NSData *)data {
  31. self = [super initWithData:data];
  32. if (self) {
  33. CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef) data, NULL);
  34. if (!imageSource) {
  35. return self;
  36. }
  37. _imageSource = imageSource;
  38. NSUInteger frameCount = CGImageSourceGetCount(imageSource);
  39. if (frameCount <= 1) {
  40. return self;
  41. }
  42. CFStringRef type = CGImageSourceGetType(imageSource);
  43. if (!type) {
  44. return self;
  45. }
  46. if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
  47. // GIF
  48. // Fix the `NSBitmapImageRep` GIF loop count calculation issue
  49. // Which will use 0 when there are no loop count information metadata in GIF data
  50. NSUInteger loopCount = [SDImageGIFCoder imageLoopCountWithSource:imageSource];
  51. [self setProperty:NSImageLoopCount withValue:@(loopCount)];
  52. } else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
  53. // APNG
  54. // Do initilize about frame count, current frame/duration and loop count
  55. [self setProperty:NSImageFrameCount withValue:@(frameCount)];
  56. [self setProperty:NSImageCurrentFrame withValue:@(0)];
  57. NSUInteger loopCount = [SDImageAPNGCoder imageLoopCountWithSource:imageSource];
  58. [self setProperty:NSImageLoopCount withValue:@(loopCount)];
  59. } else if (CFStringCompare(type, kSDUTTypeHEICS, 0) == kCFCompareEqualTo) {
  60. // HEIC
  61. // Do initilize about frame count, current frame/duration and loop count
  62. [self setProperty:NSImageFrameCount withValue:@(frameCount)];
  63. [self setProperty:NSImageCurrentFrame withValue:@(0)];
  64. NSUInteger loopCount = [SDImageHEICCoder imageLoopCountWithSource:imageSource];
  65. [self setProperty:NSImageLoopCount withValue:@(loopCount)];
  66. }
  67. }
  68. return self;
  69. }
  70. // `NSBitmapImageRep` will use `kCGImagePropertyGIFDelayTime` whenever you call `setProperty:withValue:` with `NSImageCurrentFrame` to change the current frame. We override it and use the actual `kCGImagePropertyGIFUnclampedDelayTime` if need.
  71. - (void)setProperty:(NSBitmapImageRepPropertyKey)property withValue:(id)value {
  72. [super setProperty:property withValue:value];
  73. if ([property isEqualToString:NSImageCurrentFrame]) {
  74. // Access the image source
  75. CGImageSourceRef imageSource = _imageSource;
  76. if (!imageSource) {
  77. return;
  78. }
  79. // Check format type
  80. CFStringRef type = CGImageSourceGetType(imageSource);
  81. if (!type) {
  82. return;
  83. }
  84. NSUInteger index = [value unsignedIntegerValue];
  85. NSTimeInterval frameDuration = 0;
  86. if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
  87. // GIF
  88. frameDuration = [SDImageGIFCoder frameDurationAtIndex:index source:imageSource];
  89. } else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
  90. // APNG
  91. frameDuration = [SDImageAPNGCoder frameDurationAtIndex:index source:imageSource];
  92. } else if (CFStringCompare(type, kSDUTTypeHEICS, 0) == kCFCompareEqualTo) {
  93. // HEIC
  94. frameDuration = [SDImageHEICCoder frameDurationAtIndex:index source:imageSource];
  95. }
  96. if (!frameDuration) {
  97. return;
  98. }
  99. // Reset super frame duration with the actual frame duration
  100. [super setProperty:NSImageCurrentFrameDuration withValue:@(frameDuration)];
  101. }
  102. }
  103. @end
  104. #endif