SDImageLoader.m 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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 "SDImageLoader.h"
  9. #import "SDWebImageCacheKeyFilter.h"
  10. #import "SDImageCodersManager.h"
  11. #import "SDImageCoderHelper.h"
  12. #import "SDAnimatedImage.h"
  13. #import "UIImage+Metadata.h"
  14. #import "objc/runtime.h"
  15. static void * SDImageLoaderProgressiveCoderKey = &SDImageLoaderProgressiveCoderKey;
  16. UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NSURL * _Nonnull imageURL, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
  17. NSCParameterAssert(imageData);
  18. NSCParameterAssert(imageURL);
  19. UIImage *image;
  20. id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];
  21. NSString *cacheKey;
  22. if (cacheKeyFilter) {
  23. cacheKey = [cacheKeyFilter cacheKeyForURL:imageURL];
  24. } else {
  25. cacheKey = imageURL.absoluteString;
  26. }
  27. BOOL decodeFirstFrame = options & SDWebImageDecodeFirstFrameOnly;
  28. NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
  29. CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey);
  30. SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)};
  31. if (context) {
  32. SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy];
  33. [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext];
  34. coderOptions = [mutableCoderOptions copy];
  35. }
  36. if (!decodeFirstFrame) {
  37. // check whether we should use `SDAnimatedImage`
  38. Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
  39. if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) {
  40. image = [[animatedImageClass alloc] initWithData:imageData scale:scale options:coderOptions];
  41. if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
  42. [((id<SDAnimatedImage>)image) preloadAllFrames];
  43. }
  44. }
  45. }
  46. if (!image) {
  47. image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions];
  48. }
  49. if (image) {
  50. BOOL shouldDecode = (options & SDWebImageAvoidDecodeImage) == 0;
  51. if ([image conformsToProtocol:@protocol(SDAnimatedImage)]) {
  52. // `SDAnimatedImage` do not decode
  53. shouldDecode = NO;
  54. } else if (image.sd_isAnimated) {
  55. // animated image do not decode
  56. shouldDecode = NO;
  57. }
  58. if (shouldDecode) {
  59. BOOL shouldScaleDown = options & SDWebImageScaleDownLargeImages;
  60. if (shouldScaleDown) {
  61. image = [SDImageCoderHelper decodedAndScaledDownImageWithImage:image limitBytes:0];
  62. } else {
  63. image = [SDImageCoderHelper decodedImageWithImage:image];
  64. }
  65. }
  66. }
  67. return image;
  68. }
  69. UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull imageData, NSURL * _Nonnull imageURL, BOOL finished, id<SDWebImageOperation> _Nonnull operation, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
  70. NSCParameterAssert(imageData);
  71. NSCParameterAssert(imageURL);
  72. NSCParameterAssert(operation);
  73. UIImage *image;
  74. id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];
  75. NSString *cacheKey;
  76. if (cacheKeyFilter) {
  77. cacheKey = [cacheKeyFilter cacheKeyForURL:imageURL];
  78. } else {
  79. cacheKey = imageURL.absoluteString;
  80. }
  81. BOOL decodeFirstFrame = options & SDWebImageDecodeFirstFrameOnly;
  82. NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
  83. CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey);
  84. SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)};
  85. if (context) {
  86. SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy];
  87. [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext];
  88. coderOptions = [mutableCoderOptions copy];
  89. }
  90. id<SDProgressiveImageCoder> progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey);
  91. if (!progressiveCoder) {
  92. // We need to create a new instance for progressive decoding to avoid conflicts
  93. for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) {
  94. if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] &&
  95. [((id<SDProgressiveImageCoder>)coder) canIncrementalDecodeFromData:imageData]) {
  96. progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions];
  97. break;
  98. }
  99. }
  100. objc_setAssociatedObject(operation, SDImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  101. }
  102. // If we can't find any progressive coder, disable progressive download
  103. if (!progressiveCoder) {
  104. return nil;
  105. }
  106. [progressiveCoder updateIncrementalData:imageData finished:finished];
  107. if (!decodeFirstFrame) {
  108. // check whether we should use `SDAnimatedImage`
  109. Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
  110. if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)] && [progressiveCoder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) {
  111. image = [[animatedImageClass alloc] initWithAnimatedCoder:(id<SDAnimatedImageCoder>)progressiveCoder scale:scale];
  112. }
  113. }
  114. if (!image) {
  115. image = [progressiveCoder incrementalDecodedImageWithOptions:coderOptions];
  116. }
  117. if (image) {
  118. BOOL shouldDecode = (options & SDWebImageAvoidDecodeImage) == 0;
  119. if ([image conformsToProtocol:@protocol(SDAnimatedImage)]) {
  120. // `SDAnimatedImage` do not decode
  121. shouldDecode = NO;
  122. } else if (image.sd_isAnimated) {
  123. // animated image do not decode
  124. shouldDecode = NO;
  125. }
  126. if (shouldDecode) {
  127. image = [SDImageCoderHelper decodedImageWithImage:image];
  128. }
  129. // mark the image as progressive (completionBlock one are not mark as progressive)
  130. image.sd_isIncremental = YES;
  131. }
  132. return image;
  133. }
  134. SDWebImageContextOption const SDWebImageContextLoaderCachedImage = @"loaderCachedImage";