SDAnimatedImageRep.m 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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 "SDImageGIFCoderInternal.h"
  11. #import "SDImageAPNGCoderInternal.h"
  12. @interface SDAnimatedImageRep ()
  13. @property (nonatomic, assign, readonly, nullable) CGImageSourceRef imageSource;
  14. @end
  15. @implementation SDAnimatedImageRep
  16. // `NSBitmapImageRep`'s `imageRepWithData:` is not designed initlizer
  17. + (instancetype)imageRepWithData:(NSData *)data {
  18. SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
  19. return imageRep;
  20. }
  21. // We should override init method for `NSBitmapImageRep` to do initlize about animated image format
  22. - (instancetype)initWithData:(NSData *)data {
  23. self = [super initWithData:data];
  24. if (self) {
  25. CGImageSourceRef imageSource = self.imageSource;
  26. if (!imageSource) {
  27. return self;
  28. }
  29. NSUInteger frameCount = CGImageSourceGetCount(imageSource);
  30. if (frameCount <= 1) {
  31. return self;
  32. }
  33. CFStringRef type = CGImageSourceGetType(imageSource);
  34. if (!type) {
  35. return self;
  36. }
  37. if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
  38. // GIF
  39. // Do nothing because NSBitmapImageRep support it
  40. } else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
  41. // APNG
  42. // Do initilize about frame count, current frame/duration and loop count
  43. [self setProperty:NSImageFrameCount withValue:@(frameCount)];
  44. [self setProperty:NSImageCurrentFrame withValue:@(0)];
  45. NSUInteger loopCount = [[SDImageAPNGCoder sharedCoder] sd_imageLoopCountWithSource:imageSource];
  46. [self setProperty:NSImageLoopCount withValue:@(loopCount)];
  47. }
  48. }
  49. return self;
  50. }
  51. // `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.
  52. - (void)setProperty:(NSBitmapImageRepPropertyKey)property withValue:(id)value {
  53. [super setProperty:property withValue:value];
  54. if ([property isEqualToString:NSImageCurrentFrame]) {
  55. // Access the image source
  56. CGImageSourceRef imageSource = self.imageSource;
  57. if (!imageSource) {
  58. return;
  59. }
  60. // Check format type
  61. CFStringRef type = CGImageSourceGetType(imageSource);
  62. if (!type) {
  63. return;
  64. }
  65. NSUInteger index = [value unsignedIntegerValue];
  66. float frameDuration = 0;
  67. if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
  68. // GIF
  69. frameDuration = [[SDImageGIFCoder sharedCoder] sd_frameDurationAtIndex:index source:imageSource];
  70. } else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
  71. // APNG
  72. frameDuration = [[SDImageAPNGCoder sharedCoder] sd_frameDurationAtIndex:index source:imageSource];
  73. }
  74. if (!frameDuration) {
  75. return;
  76. }
  77. // Reset super frame duration with the actual frame duration
  78. [super setProperty:NSImageCurrentFrameDuration withValue:@(frameDuration)];
  79. }
  80. }
  81. - (CGImageSourceRef)imageSource {
  82. #pragma GCC diagnostic push
  83. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  84. if (_tiffData) {
  85. return (__bridge CGImageSourceRef)(_tiffData);
  86. }
  87. #pragma GCC diagnostic pop
  88. return NULL;
  89. }
  90. @end
  91. #endif