Przeglądaj źródła

update SDWebImage and other Pods

B. Petersen 1 rok temu
rodzic
commit
7350a44a78
100 zmienionych plików z 3532 dodań i 2312 usunięć
  1. 26 23
      Podfile.lock
  2. 14 0
      Pods/CocoaLumberjack/CHANGELOG.md
  3. 1 1
      Pods/CocoaLumberjack/LICENSE
  4. 2 1
      Pods/CocoaLumberjack/README.md
  5. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m
  6. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m
  7. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m
  8. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m
  9. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h
  10. 26 14
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m
  11. 95 80
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m
  12. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m
  13. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m
  14. 118 165
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m
  15. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m
  16. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m
  17. 21 50
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m
  18. 3 3
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m
  19. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m
  20. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h
  21. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h
  22. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h
  23. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h
  24. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h
  25. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h
  26. 2 2
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h
  27. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h
  28. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h
  29. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h
  30. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h
  31. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h
  32. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h
  33. 69 5
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h
  34. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h
  35. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h
  36. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h
  37. 1 1
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h
  38. 2 2
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h
  39. 26 23
      Pods/Manifest.lock
  40. 1423 1404
      Pods/Pods.xcodeproj/project.pbxproj
  41. 43 2
      Pods/SDWebImage/README.md
  42. 8 18
      Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.m
  43. 19 0
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.h
  44. 6 0
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.m
  45. 1 0
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.h
  46. 37 103
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.m
  47. 10 0
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.h
  48. 16 3
      Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.m
  49. 15 11
      Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.m
  50. 6 0
      Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.h
  51. 12 4
      Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.m
  52. 3 1
      Pods/SDWebImage/SDWebImage/Core/SDImageCache.h
  53. 21 9
      Pods/SDWebImage/SDWebImage/Core/SDImageCache.m
  54. 18 12
      Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.m
  55. 13 0
      Pods/SDWebImage/SDWebImage/Core/SDImageCoder.h
  56. 1 0
      Pods/SDWebImage/SDWebImage/Core/SDImageCoder.m
  57. 76 2
      Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.h
  58. 245 55
      Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.m
  59. 4 5
      Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.m
  60. 64 6
      Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.m
  61. 22 14
      Pods/SDWebImage/SDWebImage/Core/SDImageLoader.m
  62. 6 0
      Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.h
  63. 40 5
      Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.h
  64. 22 14
      Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.m
  65. 7 1
      Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.h
  66. 10 25
      Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.m
  67. 97 63
      Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.m
  68. 1 0
      Pods/SDWebImage/SDWebImage/Core/SDWebImageError.h
  69. 2 0
      Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.h
  70. 19 3
      Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.m
  71. 15 0
      Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.h
  72. 35 71
      Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.m
  73. 6 0
      Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.h
  74. 1 18
      Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.m
  75. 1 0
      Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.h
  76. 13 0
      Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.m
  77. 4 0
      Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.h
  78. 30 9
      Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.m
  79. 13 0
      Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.h
  80. 12 3
      Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.m
  81. 15 0
      Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.h
  82. 11 0
      Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.m
  83. 22 8
      Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.h
  84. 28 14
      Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.m
  85. 0 1
      Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.m
  86. 62 0
      Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.h
  87. 56 0
      Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.m
  88. 1 0
      Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.m
  89. 7 3
      Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.m
  90. 3 1
      Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.m
  91. 40 0
      Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.h
  92. 164 0
      Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.m
  93. 65 0
      Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.h
  94. 1 0
      Pods/SDWebImage/WebImage/SDWebImage.h
  95. 22 0
      Pods/SDWebImageWebPCoder/README.md
  96. 174 23
      Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
  97. BIN
      Pods/SwiftFormat/CommandLineTool/swiftformat
  98. 32 5
      Pods/SwiftFormat/README.md
  99. BIN
      Pods/SwiftLint/swiftlint
  100. 1 1
      Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-Info.plist

+ 26 - 23
Podfile.lock

@@ -1,32 +1,35 @@
 PODS:
-  - CocoaLumberjack (3.8.0):
-    - CocoaLumberjack/Core (= 3.8.0)
-  - CocoaLumberjack/Core (3.8.0)
-  - libwebp (1.2.4):
-    - libwebp/demux (= 1.2.4)
-    - libwebp/mux (= 1.2.4)
-    - libwebp/webp (= 1.2.4)
-  - libwebp/demux (1.2.4):
+  - CocoaLumberjack (3.8.1):
+    - CocoaLumberjack/Core (= 3.8.1)
+  - CocoaLumberjack/Core (3.8.1)
+  - libwebp (1.3.2):
+    - libwebp/demux (= 1.3.2)
+    - libwebp/mux (= 1.3.2)
+    - libwebp/sharpyuv (= 1.3.2)
+    - libwebp/webp (= 1.3.2)
+  - libwebp/demux (1.3.2):
     - libwebp/webp
-  - libwebp/mux (1.2.4):
+  - libwebp/mux (1.3.2):
     - libwebp/demux
-  - libwebp/webp (1.2.4)
+  - libwebp/sharpyuv (1.3.2)
+  - libwebp/webp (1.3.2):
+    - libwebp/sharpyuv
   - ReachabilitySwift (5.0.0)
   - SCSiriWaveformView (1.1.2)
-  - SDWebImage (5.15.6):
-    - SDWebImage/Core (= 5.15.6)
-  - SDWebImage/Core (5.15.6)
+  - SDWebImage (5.18.1):
+    - SDWebImage/Core (= 5.18.1)
+  - SDWebImage/Core (5.18.1)
   - SDWebImageSVGKitPlugin (1.4.0):
     - SDWebImage/Core (~> 5.10)
     - SVGKit (~> 3.0)
-  - SDWebImageWebPCoder (0.11.0):
+  - SDWebImageWebPCoder (0.13.0):
     - libwebp (~> 1.0)
-    - SDWebImage/Core (~> 5.15)
+    - SDWebImage/Core (~> 5.17)
   - SVGKit (3.0.0):
     - CocoaLumberjack (~> 3.0)
   - Swifter (1.5.0)
-  - SwiftFormat/CLI (0.51.8)
-  - SwiftLint (0.51.0)
+  - SwiftFormat/CLI (0.52.4)
+  - SwiftLint (0.52.4)
 
 DEPENDENCIES:
   - ReachabilitySwift
@@ -63,17 +66,17 @@ CHECKOUT OPTIONS:
     :git: https://github.com/httpswift/swifter.git
 
 SPEC CHECKSUMS:
-  CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732
-  libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
+  CocoaLumberjack: 5c7e64cdb877770859bddec4d3d5a0d7c9299df9
+  libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
   ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
   SCSiriWaveformView: 016392911fb442c17d6dbad68e666edb13193c02
-  SDWebImage: d47d81bea8a77187896b620dc79c3c528e8906b9
+  SDWebImage: ebdbcebc7933a45226d9313bd0118bc052ad458b
   SDWebImageSVGKitPlugin: 7542dd07c344ec3415ded0461a1161a6f087e0c9
-  SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499
+  SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102
   SVGKit: 1ad7513f8c74d9652f94ed64ddecda1a23864dea
   Swifter: aa3514bbb8df8980c118f7bb1b80f2da24e39c2b
-  SwiftFormat: 4334264324e20bad415888316165bdc1fc2860bc
-  SwiftLint: 1b7561918a19e23bfed960e40759086e70f4dba5
+  SwiftFormat: 3471ff966ddb724bd7acd091c865aaa6582dde08
+  SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3
 
 PODFILE CHECKSUM: bbaaa9c3242dd69a1c26c334c2eeb92bbece8f27
 

+ 14 - 0
Pods/CocoaLumberjack/CHANGELOG.md

@@ -1,3 +1,17 @@
+# [3.8.1 - Xcode 14.3 on Aug 21st, 2023](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.8.1)
+
+### Public
+
+- Silence double conversion warnings for 32-bit watchOS (#1320)
+- Enable ALLOW_TARGET_PLATFORM_SPECIALIZATION (#1321)
+- Update to swift-log 1.5.2, implement metadata providers (#1329)
+- Preserve `messageFormat` in `DDLogMessage` (#1347)
+
+### Internal
+
+- Update copyright for 2023
+
+
 ## [3.8.0 - Xcode 14.1 on Nov 2nd, 2022](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.8.0)
 
 ### Public

+ 1 - 1
Pods/CocoaLumberjack/LICENSE

@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2010-2022, Deusty, LLC
+Copyright (c) 2010-2023, Deusty, LLC
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

+ 2 - 1
Pods/CocoaLumberjack/README.md

@@ -204,7 +204,7 @@ The current version of Lumberjack requires:
 - watchOS 4 or later
 
 ### Backwards compatibility
-- for Xcode 12 and Swift up to 5.4, use the 3.7.4 version
+- for iOS/tvOS up to 10, watchOS up to 3, macOS up to 10.12, Xcode up to 13 and Swift up to 5.4, use the 3.7.4 version
 - for Xcode 11 and Swift up to 5.2, use the 3.6.2 version
 - for Xcode 10 and Swift 4.2, use the 3.5.2 version
 - for iOS 8, use the 3.6.1 version
@@ -289,6 +289,7 @@ _Example_: `DDLogInfo("User: \(myUser)")` will add the `myUser` info to the logs
 - CocoaLumberjack is available under the BSD 3 license. See the [LICENSE file](LICENSE).
 
 ## Extensions
+- [Birch-Lumberjack](https://github.com/gruffins/birch-lumberjack) A remote logger for CocoaLumberjack
 - [BugfenderSDK-CocoaLumberjack](https://github.com/bugfender/BugfenderSDK-CocoaLumberjack) A Bugfender logger for CocoaLumberjack
 - [LogIO-CocoaLumberjack](https://github.com/s4nchez/LogIO-CocoaLumberjack) A log.io logger for CocoaLumberjack
 - [XCDLumberjackNSLogger](https://github.com/0xced/XCDLumberjackNSLogger) CocoaLumberjack logger which sends logs to NSLogger

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 26 - 14
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -1137,6 +1137,13 @@ NSTimeInterval     const kDDRollingLeeway              = 1.0;              // 1s
     NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
     NSAssert(_currentLogFileHandle, @"Can not monitor without handle.");
 
+    // This seems to work around crashes when an active source is replaced / released.
+    // See https://github.com/CocoaLumberjack/CocoaLumberjack/issues/1341
+    // And https://stackoverflow.com/questions/36296528/what-does-this-dispatch-xref-dispose-error-mean
+    if (_currentLogFileVnode) {
+        dispatch_source_cancel(_currentLogFileVnode);
+    }
+
     _currentLogFileVnode = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
                                                   (uintptr_t)[_currentLogFileHandle fileDescriptor],
                                                   DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
@@ -1730,15 +1737,15 @@ static NSString *_xattrToExtensionName(NSString *attrName) {
 
     if (result < 0) {
         if (errno != ENOENT) {
-            NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %s",
+            NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %@",
                        attrName,
                        filePath,
-                       strerror(errno));
+                       @(strerror(errno)));
         } else {
-            NSLogDebug(@"DDLogFileInfo: File does not exist in setxattr(%@, %@): error = %s",
+            NSLogDebug(@"DDLogFileInfo: File does not exist in setxattr(%@, %@): error = %@",
                        attrName,
                        filePath,
-                       strerror(errno));
+                       @(strerror(errno)));
         }
     }
 #if TARGET_IPHONE_SIMULATOR
@@ -1755,10 +1762,10 @@ static NSString *_xattrToExtensionName(NSString *attrName) {
     int result = removexattr(path, name, 0);
 
     if (result < 0 && errno != ENOATTR) {
-        NSLogError(@"DDLogFileInfo: removexattr(%@, %@): error = %s",
+        NSLogError(@"DDLogFileInfo: removexattr(%@, %@): error = %@",
                    attrName,
                    self.fileName,
-                   strerror(errno));
+                   @(strerror(errno)));
     }
 
 #if TARGET_IPHONE_SIMULATOR
@@ -1784,16 +1791,21 @@ static NSString *_xattrToExtensionName(NSString *attrName) {
     return [filePath hash];
 }
 
+- (NSComparisonResult)reverseCompareDatesUs:(NSDate *_Nullable)us them:(NSDate *_Nullable)them {
+    if (us != nil && them != nil) {
+        return [them compare:(NSDate * _Nonnull)us];
+    } else if (us == nil && them == nil) {
+       return NSOrderedSame;
+    }
+    return them == nil ? NSOrderedAscending : NSOrderedDescending;
+}
+
 - (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another {
-    __auto_type us = [self creationDate];
-    __auto_type them = [another creationDate];
-    return [them compare:us];
+    return [self reverseCompareDatesUs:[self creationDate] them:[another creationDate]];
 }
 
 - (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another {
-    __auto_type us = [self modificationDate];
-    __auto_type them = [another modificationDate];
-    return [them compare:us];
+    return [self reverseCompareDatesUs:[self modificationDate] them:[another modificationDate]];
 }
 
 @end
@@ -1806,7 +1818,7 @@ static NSString *_xattrToExtensionName(NSString *attrName) {
  * want (even if device is locked). Thats why that attribute have to be changed to
  * NSFileProtectionCompleteUntilFirstUserAuthentication.
  */
-BOOL doesAppRunInBackground() {
+BOOL doesAppRunInBackground(void) {
     BOOL answer = NO;
 
     NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];

+ 95 - 80
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -349,21 +349,16 @@ static NSUInteger _numProcessors;
     if (format) {
         va_start(args, format);
 
-        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
-
-        va_end(args);
-
-        va_start(args, format);
-
         [self log:asynchronous
-          message:message
             level:level
              flag:flag
           context:context
              file:file
          function:function
              line:line
-              tag:tag];
+              tag:tag
+           format:format
+             args:args];
 
         va_end(args);
     }
@@ -383,21 +378,16 @@ static NSUInteger _numProcessors;
     if (format) {
         va_start(args, format);
 
-        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
-
-        va_end(args);
-
-        va_start(args, format);
-
         [self log:asynchronous
-          message:message
             level:level
              flag:flag
           context:context
              file:file
          function:function
              line:line
-              tag:tag];
+              tag:tag
+           format:format
+             args:args];
 
         va_end(args);
     }
@@ -427,56 +417,24 @@ static NSUInteger _numProcessors;
      format:(NSString *)format
        args:(va_list)args {
     if (format) {
-        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
-        [self log:asynchronous
-          message:message
-            level:level
-             flag:flag
-          context:context
-             file:file
-         function:function
-             line:line
-              tag:tag];
-    }
-}
-
-+ (void)log:(BOOL)asynchronous
-    message:(NSString *)message
-      level:(DDLogLevel)level
-       flag:(DDLogFlag)flag
-    context:(NSInteger)context
-       file:(const char *)file
-   function:(const char *)function
-       line:(NSUInteger)line
-        tag:(id)tag {
-    [self.sharedInstance log:asynchronous message:message level:level flag:flag context:context file:file function:function line:line tag:tag];
-}
-
-- (void)log:(BOOL)asynchronous
-    message:(NSString *)message
-      level:(DDLogLevel)level
-       flag:(DDLogFlag)flag
-    context:(NSInteger)context
-       file:(const char *)file
-   function:(const char *)function
-       line:(NSUInteger)line
-        tag:(id)tag {
-// Nullity checks are handled by -initWithMessage:
+        // Nullity checks are handled by -initWithMessage:
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
-    DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message
-                                                               level:level
-                                                                flag:flag
-                                                             context:context
-                                                                file:@(file)
-                                                            function:@(function)
-                                                                line:line
-                                                                 tag:tag
-                                                             options:(DDLogMessageOptions)0
-                                                           timestamp:nil];
+        DDLogMessage *logMessage = [[DDLogMessage alloc] initWithFormat:format
+                                                                   args:args
+                                                                  level:level
+                                                                   flag:flag
+                                                                context:context
+                                                                   file:@(file)
+                                                               function:@(function)
+                                                                   line:line
+                                                                    tag:tag
+                                                                options:(DDLogMessageOptions)0
+                                                              timestamp:nil];
 #pragma clang diagnostic pop
 
-    [self queueLogMessage:logMessage asynchronously:asynchronous];
+        [self queueLogMessage:logMessage asynchronously:asynchronous];
+    }
 }
 
 + (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
@@ -978,25 +936,28 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
     return self;
 }
 
-- (instancetype)initWithMessage:(NSString *)message
-                          level:(DDLogLevel)level
-                           flag:(DDLogFlag)flag
-                        context:(NSInteger)context
-                           file:(NSString *)file
-                       function:(NSString *)function
-                           line:(NSUInteger)line
-                            tag:(id)tag
-                        options:(DDLogMessageOptions)options
-                      timestamp:(NSDate *)timestamp {
+- (instancetype)initWithFormat:(NSString *)messageFormat
+                     formatted:(NSString *)message
+                         level:(DDLogLevel)level
+                          flag:(DDLogFlag)flag
+                       context:(NSInteger)context
+                          file:(NSString *)file
+                      function:(NSString *)function
+                          line:(NSUInteger)line
+                           tag:(id)tag
+                       options:(DDLogMessageOptions)options
+                     timestamp:(NSDate *)timestamp {
+    NSParameterAssert(messageFormat);
     NSParameterAssert(message);
     NSParameterAssert(file);
 
     if ((self = [super init])) {
         BOOL copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
-        _message      = copyMessage ? [message copy] : message;
-        _level        = level;
-        _flag         = flag;
-        _context      = context;
+        _messageFormat = copyMessage ? [messageFormat copy] : messageFormat;
+        _message       = copyMessage ? [message copy] : message;
+        _level         = level;
+        _flag          = flag;
+        _context       = context;
 
         BOOL copyFile = (options & DDLogMessageCopyFile) != 0;
         _file = copyFile ? [file copy] : file;
@@ -1013,13 +974,13 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
 #pragma clang diagnostic pop
 #endif
         _options      = options;
-        _timestamp    = timestamp ?: [NSDate new];
+        _timestamp    = timestamp ?: [NSDate date];
 
         __uint64_t tid;
         if (pthread_threadid_np(NULL, &tid) == 0) {
             _threadID = [[NSString alloc] initWithFormat:@"%llu", tid];
         } else {
-            _threadID = @"missing threadId";
+            _threadID = @"N/A";
         }
         _threadName   = NSThread.currentThread.name;
 
@@ -1032,12 +993,63 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
         }
 
         // Try to get the current queue's label
-        _queueLabel = [[NSString alloc] initWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
+        _queueLabel = @(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL));
         _qos = (NSUInteger) qos_class_self();
     }
     return self;
 }
 
+- (instancetype)initWithFormat:(NSString *)messageFormat
+                          args:(va_list)messageArgs
+                         level:(DDLogLevel)level
+                          flag:(DDLogFlag)flag
+                       context:(NSInteger)context
+                          file:(NSString *)file
+                      function:(NSString *)function
+                          line:(NSUInteger)line
+                           tag:(id)tag
+                       options:(DDLogMessageOptions)options
+                     timestamp:(NSDate *)timestamp {
+    BOOL copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
+    NSString *format = copyMessage ? [messageFormat copy] : messageFormat;
+    self = [self initWithFormat:format
+                      formatted:[[NSString alloc] initWithFormat:format arguments:messageArgs]
+                          level:level
+                           flag:flag
+                        context:context
+                           file:file
+                       function:function
+                           line:line
+                            tag:tag
+                        options:options | DDLogMessageDontCopyMessage // we already did the copying if needed.
+                      timestamp:timestamp];
+    return self;
+}
+
+- (instancetype)initWithMessage:(NSString *)message
+                          level:(DDLogLevel)level
+                           flag:(DDLogFlag)flag
+                        context:(NSInteger)context
+                           file:(NSString *)file
+                       function:(NSString *)function
+                           line:(NSUInteger)line
+                            tag:(id)tag
+                        options:(DDLogMessageOptions)options
+                      timestamp:(NSDate *)timestamp {
+    self = [self initWithFormat:message
+                      formatted:message
+                          level:level
+                           flag:flag
+                        context:context
+                           file:file
+                       function:function
+                           line:line
+                            tag:tag
+                        options:options
+                      timestamp:timestamp];
+    return self;
+}
+
 NS_INLINE BOOL _nullable_strings_equal(NSString* _Nullable lhs, NSString* _Nullable rhs)
 {
     if (lhs == nil) {
@@ -1058,6 +1070,7 @@ NS_INLINE BOOL _nullable_strings_equal(NSString* _Nullable lhs, NSString* _Nulla
     } else {
         __auto_type otherMsg = (DDLogMessage *)other;
         return [otherMsg->_message isEqualToString:_message]
+        && [otherMsg->_messageFormat isEqualToString:_messageFormat]
         && otherMsg->_level == _level
         && otherMsg->_flag == _flag
         && otherMsg->_context == _context
@@ -1076,6 +1089,7 @@ NS_INLINE BOOL _nullable_strings_equal(NSString* _Nullable lhs, NSString* _Nulla
     // Subclasses of NSObject should not call [super hash] here.
     // See https://stackoverflow.com/questions/36593038/confused-about-the-default-isequal-and-hash-implements
     return _message.hash
+    ^ _messageFormat.hash
     ^ _level
     ^ _flag
     ^ _context
@@ -1092,6 +1106,7 @@ NS_INLINE BOOL _nullable_strings_equal(NSString* _Nullable lhs, NSString* _Nulla
 - (id)copyWithZone:(NSZone * __attribute__((unused)))zone {
     DDLogMessage *newMessage = [DDLogMessage new];
 
+    newMessage->_messageFormat = _messageFormat;
     newMessage->_message = _message;
     newMessage->_level = _level;
     newMessage->_flag = _flag;

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 118 - 165
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -79,23 +79,18 @@
 #define MAP_TO_TERMINAL_APP_COLORS 1
 
 typedef struct {
-  uint8_t r;
-  uint8_t g;
-  uint8_t b;
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
 } DDRGBColor;
 
 @interface DDTTYLoggerColorProfile : NSObject {
-    @public
+@public
     DDLogFlag mask;
     NSInteger context;
 
-    uint8_t fg_r;
-    uint8_t fg_g;
-    uint8_t fg_b;
-
-    uint8_t bg_r;
-    uint8_t bg_g;
-    uint8_t bg_b;
+    DDRGBColor fg;
+    DDRGBColor bg;
 
     NSUInteger fgCodeIndex;
     NSString *fgCodeRaw;
@@ -117,19 +112,15 @@ typedef struct {
 
 @end
 
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
 @interface DDTTYLogger () {
     NSString *_appName;
     char *_app;
     size_t _appLen;
-    
+
     NSString *_processID;
     char *_pid;
     size_t _pidLen;
-    
+
     BOOL _colorsEnabled;
     NSMutableArray *_colorProfilesArray;
     NSMutableDictionary *_colorProfilesDict;
@@ -137,6 +128,7 @@ typedef struct {
 
 @end
 
+#pragma mark -
 
 @implementation DDTTYLogger
 
@@ -144,30 +136,30 @@ static BOOL isaColorTTY;
 static BOOL isaColor256TTY;
 static BOOL isaXcodeColorTTY;
 
-static NSArray *codes_fg = nil;
-static NSArray *codes_bg = nil;
+static NSArray *codesFg = nil;
+static NSArray *codesBg = nil;
 static NSArray *colors   = nil;
 
 static DDTTYLogger *sharedInstance;
 
 /**
- * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode.
+ * Initializes the colors array, as well as the `codesFg` and `codesBg` arrays, for 16 color mode.
  *
  * This method is used when the application is running from within a shell that only supports 16 color mode.
  * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  **/
-+ (void)initialize_colors_16 {
-    if (codes_fg || codes_bg || colors) {
++ (void)initializeColors16 {
+    if (codesFg || codesBg || colors) {
         return;
     }
 
-    NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:16];
+    NSMutableArray *mColors = [NSMutableArray arrayWithCapacity:16];
 
     // In a standard shell only 16 colors are supported.
     //
     // More information about ansi escape codes can be found online.
     // http://en.wikipedia.org/wiki/ANSI_escape_code
-    codes_fg = @[
+    codesFg = @[
         @"30m",  // normal - black
         @"31m",  // normal - red
         @"32m",  // normal - green
@@ -186,7 +178,7 @@ static DDTTYLogger *sharedInstance;
         @"1;37m",  // bright - white
     ];
 
-    codes_bg = @[
+    codesBg = @[
         @"40m",  // normal - black
         @"41m",  // normal - red
         @"42m",  // normal - green
@@ -205,13 +197,12 @@ static DDTTYLogger *sharedInstance;
         @"1;47m",  // bright - white
     ];
 
-
 #if MAP_TO_TERMINAL_APP_COLORS
 
     // Standard Terminal.app colors:
     //
     // These are the default colors used by Apple's Terminal.app.
-    DDRGBColor rgbColors[] = {
+    const DDRGBColor rgbColors[] = {
         {  0,   0,   0}, // normal - black
         {194,  54,  33}, // normal - red
         { 37, 188,  36}, // normal - green
@@ -235,8 +226,7 @@ static DDTTYLogger *sharedInstance;
     // Standard xterm colors:
     //
     // These are the default colors used by most xterm shells.
-
-    DDRGBColor rgbColors[] = {
+    const DDRGBColor rgbColors[] = {
         {  0,   0,   0}, // normal - black
         {205,   0,   0}, // normal - red
         {  0, 205,   0}, // normal - green
@@ -257,30 +247,30 @@ static DDTTYLogger *sharedInstance;
 #endif /* if MAP_TO_TERMINAL_APP_COLORS */
 
     for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
-        [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
+        [mColors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
     }
-    colors   = [m_colors   copy];
+    colors = [mColors copy];
 
-    NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
-    NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
+    NSAssert([codesFg count] == [codesBg count], @"Invalid colors/codes array(s)");
+    NSAssert([codesFg count] == [colors count],   @"Invalid colors/codes array(s)");
 }
 
 /**
- * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode.
+ * Initializes the colors array, as well as the `codesFg` and `codesBg` arrays, for 256 color mode.
  *
  * This method is used when the application is running from within a shell that supports 256 color mode.
  * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  **/
-+ (void)initialize_colors_256 {
-    if (codes_fg || codes_bg || colors) {
++ (void)initializeColors256 {
+    if (codesFg || codesBg || colors) {
         return;
     }
 
-    NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256 - 16)];
-    NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256 - 16)];
-    NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:(256 - 16)];
+    NSMutableArray *mCodesFg = [NSMutableArray arrayWithCapacity:(256 - 16)];
+    NSMutableArray *mCodesBg = [NSMutableArray arrayWithCapacity:(256 - 16)];
+    NSMutableArray *mColors  = [NSMutableArray arrayWithCapacity:(256 - 16)];
 
-    #if MAP_TO_TERMINAL_APP_COLORS
+#if MAP_TO_TERMINAL_APP_COLORS
 
     // Standard Terminal.app colors:
     //
@@ -307,7 +297,7 @@ static DDTTYLogger *sharedInstance;
     // http://en.wikipedia.org/wiki/ANSI_escape_code
 
     // Colors
-    DDRGBColor rgbColors[] = {
+    const DDRGBColor rgbColors[] = {
         { 47,  49,  49},
         { 60,  42, 144},
         { 66,  44, 183},
@@ -592,21 +582,19 @@ static DDTTYLogger *sharedInstance;
     };
 
     for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
-        [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
+        [mColors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
     }
 
     // Color codes
-
     int index = 16;
-
     while (index < 256) {
-        [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
-        [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+        [mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+        [mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
 
         index++;
     }
 
-    #else /* if MAP_TO_TERMINAL_APP_COLORS */
+#else /* if MAP_TO_TERMINAL_APP_COLORS */
 
     // Standard xterm colors:
     //
@@ -652,9 +640,9 @@ static DDTTYLogger *sharedInstance;
             for (bi = 0; bi < 6; bi++) {
                 b = (bi == 0) ? 0 : 95 + (40 * (bi - 1));
 
-                [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
-                [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
-                [m_colors addObject:DDMakeColor(r, g, b)];
+                [mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+                [mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+                [mColors  addObject:DDMakeColor(r, g, b)];
 
                 index++;
             }
@@ -668,9 +656,9 @@ static DDTTYLogger *sharedInstance;
     b = 8;
 
     while (index < 256) {
-        [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
-        [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
-        [m_colors addObject:DDMakeColor(r, g, b)];
+        [mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+        [mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+        [mColor s addObject:DDMakeColor(r, g, b)];
 
         r += 10;
         g += 10;
@@ -679,21 +667,20 @@ static DDTTYLogger *sharedInstance;
         index++;
     }
 
-    #endif /* if MAP_TO_TERMINAL_APP_COLORS */
+#endif /* if MAP_TO_TERMINAL_APP_COLORS */
 
-    codes_fg = [m_codes_fg copy];
-    codes_bg = [m_codes_bg copy];
-    colors   = [m_colors   copy];
+    codesFg = [mCodesFg copy];
+    codesBg = [mCodesBg copy];
+    colors  = [mColors  copy];
 
-    NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
-    NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
+    NSAssert([codesFg count] == [codesBg count], @"Invalid colors/codes array(s)");
+    NSAssert([codesFg count] == [colors count],   @"Invalid colors/codes array(s)");
 }
 
 + (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(DDColor *)color {
-    #if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
 
     // iOS
-
     BOOL done = NO;
 
     if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
@@ -715,11 +702,9 @@ static DDTTYLogger *sharedInstance;
         if (rPtr) {
             *rPtr = pixel[0] / 255.0;
         }
-
         if (gPtr) {
             *gPtr = pixel[1] / 255.0;
         }
-
         if (bPtr) {
             *bPtr = pixel[2] / 255.0;
         }
@@ -728,20 +713,27 @@ static DDTTYLogger *sharedInstance;
         CGColorSpaceRelease(rgbColorSpace);
     }
 
-    #elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
+#elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
 
     // OS X without AppKit
-
     [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
 
-    #else /* if TARGET_OS_IPHONE */
+#else /* if TARGET_OS_IPHONE */
 
     // OS X with AppKit
-
-    NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+    NSColor *safeColor;
+    if (@available(macOS 10.14,*)) {
+        safeColor = [color colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace];
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+#pragma clang diagnostic pop
+    }
 
     [safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
-    #endif /* if TARGET_OS_IPHONE */
+
+#endif /* if TARGET_OS_IPHONE */
 }
 
 /**
@@ -750,7 +742,7 @@ static DDTTYLogger *sharedInstance;
  *
  * This method loops through the known supported color set, and calculates the closest color.
  * The array index of that color, within the colors array, is then returned.
- * This array index may also be used as the index within the codes_fg and codes_bg arrays.
+ * This array index may also be used as the index within the `codesFg` and `codesBg` arrays.
  **/
 + (NSUInteger)codeIndexForColor:(DDColor *)inColor {
     CGFloat inR, inG, inB;
@@ -768,14 +760,14 @@ static DDTTYLogger *sharedInstance;
         CGFloat r, g, b;
         [self getRed:&r green:&g blue:&b fromColor:color];
 
-    #if CGFLOAT_IS_DOUBLE
+#if CGFLOAT_IS_DOUBLE
         CGFloat distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0));
-    #else
+#else
         CGFloat distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f));
-    #endif
+#endif
 
         NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f",
-                     (unsigned long)i, inR, inG, inB, r, g, b, distance);
+                     (unsigned long)i, (double)inR, (double)inG, (double)inB, (double)r, (double)g, (double)b, (double)distance);
 
         if (distance < lowestDistance) {
             bestIndex = i;
@@ -799,10 +791,10 @@ static DDTTYLogger *sharedInstance;
         //
         // PS - Please read the header file before diving into the source code.
 
-        char *xcode_colors = getenv("XcodeColors");
+        char *xcodeColors = getenv("XcodeColors");
         char *term = getenv("TERM");
 
-        if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) {
+        if (xcodeColors && (strcmp(xcodeColors, "YES") == 0)) {
             isaXcodeColorTTY = YES;
         } else if (term) {
             if (strcasestr(term, "color") != NULL) {
@@ -810,9 +802,9 @@ static DDTTYLogger *sharedInstance;
                 isaColor256TTY = (strcasestr(term, "256") != NULL);
 
                 if (isaColor256TTY) {
-                    [self initialize_colors_256];
+                    [self initializeColors256];
                 } else {
-                    [self initialize_colors_16];
+                    [self initializeColors16];
                 }
             }
         }
@@ -840,9 +832,7 @@ static DDTTYLogger *sharedInstance;
 
     if ((self = [super init])) {
         // Initialize 'app' variable (char *)
-
         _appName = [[NSProcessInfo processInfo] processName];
-
         _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
 
         if (_appLen == 0) {
@@ -851,14 +841,12 @@ static DDTTYLogger *sharedInstance;
         }
 
         _app = (char *)calloc(_appLen + 1, sizeof(char));
-
         if (_app == NULL) {
             return nil;
         }
 
         BOOL processedAppName = [_appName getCString:_app maxLength:(_appLen + 1) encoding:NSUTF8StringEncoding];
-
-        if (NO == processedAppName) {
+        if (!processedAppName) {
             free(_app);
             return nil;
         }
@@ -876,8 +864,7 @@ static DDTTYLogger *sharedInstance;
         }
 
         BOOL processedID = [_processID getCString:_pid maxLength:(_pidLen + 1) encoding:NSUTF8StringEncoding];
-
-        if (NO == processedID) {
+        if (!processedID) {
             free(_app);
             free(_pid);
             return nil;
@@ -918,11 +905,8 @@ static DDTTYLogger *sharedInstance;
     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
 
-    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
-
     __block BOOL result;
-
-    dispatch_sync(globalLoggingQueue, ^{
+    dispatch_sync([DDLog loggingQueue], ^{
         dispatch_sync(self.loggerQueue, ^{
             result = self->_colorsEnabled;
         });
@@ -955,9 +939,7 @@ static DDTTYLogger *sharedInstance;
     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
 
-    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
-
-    dispatch_async(globalLoggingQueue, ^{
+    dispatch_async([DDLog loggingQueue], ^{
         dispatch_async(self.loggerQueue, block);
     });
 }
@@ -969,11 +951,10 @@ static DDTTYLogger *sharedInstance;
 - (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt {
     dispatch_block_t block = ^{
         @autoreleasepool {
-            DDTTYLoggerColorProfile *newColorProfile =
-                [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
-                                                         backgroundColor:bgColor
-                                                                    flag:mask
-                                                                 context:ctxt];
+            DDTTYLoggerColorProfile *newColorProfile = [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
+                                                                                                backgroundColor:bgColor
+                                                                                                           flag:mask
+                                                                                                        context:ctxt];
 
             NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
 
@@ -1001,25 +982,22 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
 }
 
 - (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id <NSCopying>)tag {
-    NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
+    NSAssert([(id <NSObject>)tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
 
     dispatch_block_t block = ^{
         @autoreleasepool {
-            DDTTYLoggerColorProfile *newColorProfile =
-                [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
-                                                         backgroundColor:bgColor
-                                                                    flag:(DDLogFlag)0
-                                                                 context:0];
+            DDTTYLoggerColorProfile *newColorProfile = [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
+                                                                                                backgroundColor:bgColor
+                                                                                                           flag:(DDLogFlag)0
+                                                                                                        context:0];
 
             NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
 
@@ -1033,10 +1011,8 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
@@ -1071,17 +1047,15 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
 }
 
 - (void)clearColorsForTag:(id <NSCopying>)tag {
-    NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
+    NSAssert([(id <NSObject>) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
 
     dispatch_block_t block = ^{
         @autoreleasepool {
@@ -1095,10 +1069,8 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
@@ -1117,10 +1089,8 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
@@ -1139,10 +1109,8 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
@@ -1162,10 +1130,8 @@ static DDTTYLogger *sharedInstance;
     if ([self isOnInternalLoggerQueue]) {
         block();
     } else {
-        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
-
-        dispatch_async(globalLoggingQueue, ^{
+        dispatch_async([DDLog loggingQueue], ^{
             dispatch_async(self.loggerQueue, block);
         });
     }
@@ -1242,8 +1208,9 @@ static DDTTYLogger *sharedInstance;
 
         if (isFormatted) {
             // The log message has already been formatted.
-            const int iovec_len = (_automaticallyAppendNewlineForCustomFormatters) ? 5 : 4;
-            struct iovec v[iovec_len];
+            const size_t maxIovecLen = 5;
+            size_t iovecLen = _automaticallyAppendNewlineForCustomFormatters ? 5 : 4;
+            struct iovec v[maxIovecLen] = { 0 };
 
             if (colorProfile) {
                 v[0].iov_base = colorProfile->fgCode;
@@ -1252,28 +1219,20 @@ static DDTTYLogger *sharedInstance;
                 v[1].iov_base = colorProfile->bgCode;
                 v[1].iov_len = colorProfile->bgCodeLen;
 
-                v[iovec_len - 1].iov_base = colorProfile->resetCode;
-                v[iovec_len - 1].iov_len = colorProfile->resetCodeLen;
-            } else {
-                v[0].iov_base = "";
-                v[0].iov_len = 0;
-
-                v[1].iov_base = "";
-                v[1].iov_len = 0;
-
-                v[iovec_len - 1].iov_base = "";
-                v[iovec_len - 1].iov_len = 0;
+                v[maxIovecLen - 1].iov_base = colorProfile->resetCode;
+                v[maxIovecLen - 1].iov_len = colorProfile->resetCodeLen;
             }
 
             v[2].iov_base = msg;
-            v[2].iov_len = msgLen;
+            v[2].iov_len = (msgLen > SIZE_MAX - 1) ? SIZE_MAX - 1 : msgLen;
 
-            if (iovec_len == 5) {
+            if (_automaticallyAppendNewlineForCustomFormatters && (v[2].iov_len == 0 || msg[v[2].iov_len - 1] != '\n')) {
                 v[3].iov_base = "\n";
-                v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
+                v[3].iov_len = 1;
+                iovecLen = 5;
             }
 
-            writev(STDERR_FILENO, v, iovec_len);
+            writev(STDERR_FILENO, v, (int)iovecLen);
         } else {
             // The log message is unformatted, so apply standard NSLog style formatting.
 
@@ -1381,7 +1340,7 @@ static DDTTYLogger *sharedInstance;
 
 @end
 
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
 
 @implementation DDTTYLoggerColorProfile
 
@@ -1395,31 +1354,31 @@ static DDTTYLogger *sharedInstance;
         if (fgColor) {
             [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor];
 
-            fg_r = (uint8_t)(r * 255.0);
-            fg_g = (uint8_t)(g * 255.0);
-            fg_b = (uint8_t)(b * 255.0);
+            fg.r = (uint8_t)(r * (CGFloat)255.0);
+            fg.g = (uint8_t)(g * (CGFloat)255.0);
+            fg.b = (uint8_t)(b * (CGFloat)255.0);
         }
 
         if (bgColor) {
             [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor];
 
-            bg_r = (uint8_t)(r * 255.0);
-            bg_g = (uint8_t)(g * 255.0);
-            bg_b = (uint8_t)(b * 255.0);
+            bg.r = (uint8_t)(r * (CGFloat)255.0);
+            bg.g = (uint8_t)(g * (CGFloat)255.0);
+            bg.b = (uint8_t)(b * (CGFloat)255.0);
         }
 
         if (fgColor && isaColorTTY) {
             // Map foreground color to closest available shell color
 
             fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor];
-            fgCodeRaw   = codes_fg[fgCodeIndex];
+            fgCodeRaw   = codesFg[fgCodeIndex];
 
-            NSString *escapeSeq = @"\033[";
+            const NSString *escapeSeq = @"\033[";
 
             NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
             NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
 
-            BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode)      maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
+            BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
             BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
 
             if (!escapeSeqEnc || !fgCodeRawEsc) {
@@ -1429,14 +1388,11 @@ static DDTTYLogger *sharedInstance;
             fgCodeLen = len1 + len2;
         } else if (fgColor && isaXcodeColorTTY) {
             // Convert foreground color to color code sequence
-
             const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
-
-            int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b);
+            int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg.r, fg.g, fg.b);
             fgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
         } else {
             // No foreground color or no color support
-
             fgCode[0] = '\0';
             fgCodeLen = 0;
         }
@@ -1445,14 +1401,14 @@ static DDTTYLogger *sharedInstance;
             // Map background color to closest available shell color
 
             bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor];
-            bgCodeRaw   = codes_bg[bgCodeIndex];
+            bgCodeRaw   = codesBg[bgCodeIndex];
 
-            NSString *escapeSeq = @"\033[";
+            const NSString *escapeSeq = @"\033[";
 
             NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
             NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
 
-            BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode)      maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
+            BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
             BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
 
             if (!escapeSeqEnc || !bgCodeRawEsc) {
@@ -1462,14 +1418,11 @@ static DDTTYLogger *sharedInstance;
             bgCodeLen = len1 + len2;
         } else if (bgColor && isaXcodeColorTTY) {
             // Convert background color to color code sequence
-
             const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
-
-            int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b);
+            int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg.r, bg.g, bg.b);
             bgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
         } else {
             // No background color or no color support
-
             bgCode[0] = '\0';
             bgCodeLen = 0;
         }
@@ -1490,7 +1443,7 @@ static DDTTYLogger *sharedInstance;
 - (NSString *)description {
     return [NSString stringWithFormat:
             @"<DDTTYLoggerColorProfile: %p mask:%i ctxt:%ld fg:%u,%u,%u bg:%u,%u,%u fgCode:%@ bgCode:%@>",
-            self, (int)mask, (long)context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw];
+            self, (int)mask, (long)context, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, fgCodeRaw, bgCodeRaw];
 }
 
 @end

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 21 - 50
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -134,98 +134,69 @@ static DDQualityOfServiceName _qos_name(NSUInteger qos) {
 - (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage {
     // As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
 
-    NSUInteger minQueueLength = self.minQueueLength;
-    NSUInteger maxQueueLength = self.maxQueueLength;
-
-    // Get the name of the queue, thread, or machID (whichever we are to use).
-
-    NSString *queueThreadLabel = nil;
-
-    BOOL useQueueLabel = YES;
-    BOOL useThreadName = NO;
-
+    BOOL useQueueLabel = NO;
     if (logMessage->_queueLabel) {
+        useQueueLabel = YES;
+
         // If you manually create a thread, it's dispatch_queue will have one of the thread names below.
         // Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
-
-        NSArray *names = @[
+        const NSArray *names = @[
             @"com.apple.root.low-priority",
             @"com.apple.root.default-priority",
             @"com.apple.root.high-priority",
             @"com.apple.root.low-overcommit-priority",
             @"com.apple.root.default-overcommit-priority",
             @"com.apple.root.high-overcommit-priority",
-            @"com.apple.root.default-qos.overcommit"
+            @"com.apple.root.default-qos.overcommit",
         ];
-
-        for (NSString * name in names) {
+        for (NSString *name in names) {
             if ([logMessage->_queueLabel isEqualToString:name]) {
                 useQueueLabel = NO;
-                useThreadName = [logMessage->_threadName length] > 0;
                 break;
             }
         }
-    } else {
-        useQueueLabel = NO;
-        useThreadName = [logMessage->_threadName length] > 0;
     }
 
-    if (useQueueLabel || useThreadName) {
-        NSString *fullLabel;
-        NSString *abrvLabel;
-
-        if (useQueueLabel) {
-            fullLabel = logMessage->_queueLabel;
-        } else {
-            fullLabel = logMessage->_threadName;
-        }
+    // Get the name of the queue, thread, or machID (whichever we are to use).
+    NSString *queueThreadLabel;
+    if (useQueueLabel || [logMessage->_threadName length] > 0) {
+        NSString *fullLabel = useQueueLabel ? logMessage->_queueLabel : logMessage->_threadName;
 
+        NSString *abrvLabel;
         pthread_mutex_lock(&_mutex);
         {
             abrvLabel = _replacements[fullLabel];
         }
         pthread_mutex_unlock(&_mutex);
 
-        if (abrvLabel) {
-            queueThreadLabel = abrvLabel;
-        } else {
-            queueThreadLabel = fullLabel;
-        }
+        queueThreadLabel = abrvLabel ?: fullLabel;
     } else {
         queueThreadLabel = logMessage->_threadID;
     }
 
     // Now use the thread label in the output
-
-    NSUInteger labelLength = [queueThreadLabel length];
-
     // labelLength > maxQueueLength : truncate
     // labelLength < minQueueLength : padding
     //                              : exact
-
-    if ((maxQueueLength > 0) && (labelLength > maxQueueLength)) {
+    NSUInteger minQueueLength = self.minQueueLength;
+    NSUInteger maxQueueLength = self.maxQueueLength;
+    NSUInteger labelLength = [queueThreadLabel length];
+    if (maxQueueLength > 0 && labelLength > maxQueueLength) {
         // Truncate
-
         return [queueThreadLabel substringToIndex:maxQueueLength];
     } else if (labelLength < minQueueLength) {
         // Padding
-
-        NSUInteger numSpaces = minQueueLength - labelLength;
-
-        char spaces[numSpaces + 1];
-        memset(spaces, ' ', numSpaces);
-        spaces[numSpaces] = '\0';
-
-        return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces];
+        return [queueThreadLabel stringByPaddingToLength:minQueueLength
+                                              withString:@" "
+                                         startingAtIndex:0];
     } else {
         // Exact
-
         return queueThreadLabel;
     }
 }
 
 - (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
-    NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)];
+    NSString *timestamp = [self stringFromDate:logMessage->_timestamp];
     NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
 
     return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message];

+ 3 - 3
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -41,7 +41,7 @@ static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
     return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
 }
 
-static NSUInteger DDGetMaxBufferSizeBytes() {
+static NSUInteger DDGetMaxBufferSizeBytes(void) {
     static NSUInteger maxBufferSize = 0;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
@@ -50,7 +50,7 @@ static NSUInteger DDGetMaxBufferSizeBytes() {
     return maxBufferSize;
 }
 
-static NSUInteger DDGetDefaultBufferSizeBytes() {
+static NSUInteger DDGetDefaultBufferSizeBytes(void) {
     static NSUInteger defaultBufferSize = 0;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 2 - 2
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -22,7 +22,7 @@
             DDLogError(@"%@", description);                                           \
             NSAssert(NO, @"%@", description);                                         \
         }
-#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)
+#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %@", @(#condition))
 
 /**
  * Analog to `DDAssertionFailure` from DDAssert.swift for use in Objective C

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 69 - 5
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -791,6 +791,7 @@ DD_SENDABLE
     // Direct accessors to be used only for performance
     @public
     NSString *_message;
+    NSString *_messageFormat;
     DDLogLevel _level;
     DDLogFlag _flag;
     NSInteger _context;
@@ -798,9 +799,9 @@ DD_SENDABLE
     NSString *_fileName;
     NSString *_function;
     NSUInteger _line;
-    #if DD_LEGACY_MESSAGE_TAG
+#if DD_LEGACY_MESSAGE_TAG
     id _tag __attribute__((deprecated("Use _representedObject instead", "_representedObject")));
-    #endif
+#endif
     id _representedObject;
     DDLogMessageOptions _options;
     NSDate * _timestamp;
@@ -829,6 +830,64 @@ DD_SENDABLE
  * so it makes sense to optimize and skip the unnecessary allocations.
  * However, if you need them to be copied you may use the options parameter to specify this.
  *
+ *  @param messageFormat   the message format
+ *  @param message  the formatted message
+ *  @param level     the log level
+ *  @param flag      the log flag
+ *  @param context   the context (if any is defined)
+ *  @param file      the current file
+ *  @param function  the current function
+ *  @param line      the current code line
+ *  @param tag       potential tag
+ *  @param options   a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
+ *  @param timestamp the log timestamp
+ *
+ *  @return a new instance of a log message model object
+ */
+- (instancetype)initWithFormat:(NSString *)messageFormat
+                     formatted:(NSString *)message
+                         level:(DDLogLevel)level
+                          flag:(DDLogFlag)flag
+                       context:(NSInteger)context
+                          file:(NSString *)file
+                      function:(nullable NSString *)function
+                          line:(NSUInteger)line
+                           tag:(nullable id)tag
+                       options:(DDLogMessageOptions)options
+                     timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
+
+/**
+ *     Convenience initializer taking a `va_list` as arguments to create the formatted message.
+ *
+ *  @param messageFormat   the message format
+ *  @param messageArgs   the message arguments.
+ *  @param level     the log level
+ *  @param flag      the log flag
+ *  @param context   the context (if any is defined)
+ *  @param file      the current file
+ *  @param function  the current function
+ *  @param line      the current code line
+ *  @param tag       potential tag
+ *  @param options   a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
+ *  @param timestamp the log timestamp
+ *
+ *  @return a new instance of a log message model object
+ */
+- (instancetype)initWithFormat:(NSString *)messageFormat
+                          args:(va_list)messageArgs
+                         level:(DDLogLevel)level
+                          flag:(DDLogFlag)flag
+                       context:(NSInteger)context
+                          file:(NSString *)file
+                      function:(nullable NSString *)function
+                          line:(NSUInteger)line
+                           tag:(nullable id)tag
+                       options:(DDLogMessageOptions)options
+                     timestamp:(nullable NSDate *)timestamp;
+
+/**
+ *  Deprecated initialier. See initWithFormat:args:formatted:level:flag:context:file:function:line:tag:options:timestamp:.
+ *
  *  @param message   the message
  *  @param level     the log level
  *  @param flag      the log flag
@@ -851,16 +910,21 @@ DD_SENDABLE
                            line:(NSUInteger)line
                             tag:(nullable id)tag
                         options:(DDLogMessageOptions)options
-                      timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
+                      timestamp:(nullable NSDate *)timestamp
+__attribute__((deprecated("Use initializer taking unformatted message and args instead", "initWithFormat:formatted:level:flag:context:file:function:line:tag:options:timestamp:")));
 
 /**
  * Read-only properties
  **/
 
 /**
- *  The log message
+ *  The log message.
  */
 @property (readonly, nonatomic) NSString *message;
+/**
+ * The message format. When the deprecated initializer is used, this might be the same as `message`.
+ */
+@property (readonly, nonatomic) NSString *messageFormat;
 @property (readonly, nonatomic) DDLogLevel level;
 @property (readonly, nonatomic) DDLogFlag flag;
 @property (readonly, nonatomic) NSInteger context;

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 1 - 1
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,

+ 2 - 2
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h

@@ -1,6 +1,6 @@
 // Software License Agreement (BSD License)
 //
-// Copyright (c) 2010-2022, Deusty, LLC
+// Copyright (c) 2010-2023, Deusty, LLC
 // All rights reserved.
 //
 // Redistribution and use of this software in source and binary forms,
@@ -28,7 +28,7 @@
     // iOS or tvOS or watchOS
     #import <UIKit/UIColor.h>
     typedef UIColor DDColor;
-    static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0];}
+    static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/(CGFloat)255.0) green:(g/(CGFloat)255.0) blue:(b/(CGFloat)255.0) alpha:1.0];}
 #elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
     // OS X CLI
     #import <CocoaLumberjack/CLIColor.h>

+ 26 - 23
Pods/Manifest.lock

@@ -1,32 +1,35 @@
 PODS:
-  - CocoaLumberjack (3.8.0):
-    - CocoaLumberjack/Core (= 3.8.0)
-  - CocoaLumberjack/Core (3.8.0)
-  - libwebp (1.2.4):
-    - libwebp/demux (= 1.2.4)
-    - libwebp/mux (= 1.2.4)
-    - libwebp/webp (= 1.2.4)
-  - libwebp/demux (1.2.4):
+  - CocoaLumberjack (3.8.1):
+    - CocoaLumberjack/Core (= 3.8.1)
+  - CocoaLumberjack/Core (3.8.1)
+  - libwebp (1.3.2):
+    - libwebp/demux (= 1.3.2)
+    - libwebp/mux (= 1.3.2)
+    - libwebp/sharpyuv (= 1.3.2)
+    - libwebp/webp (= 1.3.2)
+  - libwebp/demux (1.3.2):
     - libwebp/webp
-  - libwebp/mux (1.2.4):
+  - libwebp/mux (1.3.2):
     - libwebp/demux
-  - libwebp/webp (1.2.4)
+  - libwebp/sharpyuv (1.3.2)
+  - libwebp/webp (1.3.2):
+    - libwebp/sharpyuv
   - ReachabilitySwift (5.0.0)
   - SCSiriWaveformView (1.1.2)
-  - SDWebImage (5.15.6):
-    - SDWebImage/Core (= 5.15.6)
-  - SDWebImage/Core (5.15.6)
+  - SDWebImage (5.18.1):
+    - SDWebImage/Core (= 5.18.1)
+  - SDWebImage/Core (5.18.1)
   - SDWebImageSVGKitPlugin (1.4.0):
     - SDWebImage/Core (~> 5.10)
     - SVGKit (~> 3.0)
-  - SDWebImageWebPCoder (0.11.0):
+  - SDWebImageWebPCoder (0.13.0):
     - libwebp (~> 1.0)
-    - SDWebImage/Core (~> 5.15)
+    - SDWebImage/Core (~> 5.17)
   - SVGKit (3.0.0):
     - CocoaLumberjack (~> 3.0)
   - Swifter (1.5.0)
-  - SwiftFormat/CLI (0.51.8)
-  - SwiftLint (0.51.0)
+  - SwiftFormat/CLI (0.52.4)
+  - SwiftLint (0.52.4)
 
 DEPENDENCIES:
   - ReachabilitySwift
@@ -63,17 +66,17 @@ CHECKOUT OPTIONS:
     :git: https://github.com/httpswift/swifter.git
 
 SPEC CHECKSUMS:
-  CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732
-  libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
+  CocoaLumberjack: 5c7e64cdb877770859bddec4d3d5a0d7c9299df9
+  libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
   ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
   SCSiriWaveformView: 016392911fb442c17d6dbad68e666edb13193c02
-  SDWebImage: d47d81bea8a77187896b620dc79c3c528e8906b9
+  SDWebImage: ebdbcebc7933a45226d9313bd0118bc052ad458b
   SDWebImageSVGKitPlugin: 7542dd07c344ec3415ded0461a1161a6f087e0c9
-  SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499
+  SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102
   SVGKit: 1ad7513f8c74d9652f94ed64ddecda1a23864dea
   Swifter: aa3514bbb8df8980c118f7bb1b80f2da24e39c2b
-  SwiftFormat: 4334264324e20bad415888316165bdc1fc2860bc
-  SwiftLint: 1b7561918a19e23bfed960e40759086e70f4dba5
+  SwiftFormat: 3471ff966ddb724bd7acd091c865aaa6582dde08
+  SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3
 
 PODFILE CHECKSUM: bbaaa9c3242dd69a1c26c334c2eeb92bbece8f27
 

Plik diff jest za duży
+ 1423 - 1404
Pods/Pods.xcodeproj/project.pbxproj


+ 43 - 2
Pods/SDWebImage/README.md

@@ -37,6 +37,16 @@ Note: `SD` is the prefix for **Simple Design** (which is the team name in Daily
 - [x] Modern Objective-C and better Swift support 
 - [x] Performances!
 
+## For Apple visionOS
+
+From 5.18.0, SDWebImage can be compiled for visionOS platform. However, it's still in beta and may contains issues unlike the stable iOS UIKit support. Welcome to have a try and [report issue](https://github.com/SDWebImage/SDWebImage/issues).
+
+To build on visionOS, currently we only support the standard Xcode integration.
+
+See `Installation with Swift Package Manager` and `Manual Installation Guide` below.
+
+Once the visionOS toolchain is stable, we will add the other package manager support later (include CocoaPods).
+
 ## Supported Image Formats
 
 - Image formats supported by Apple system (JPEG, PNG, TIFF, BMP, ...), including [GIF](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#gif-coder)/[APNG](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#apng-coder) animated image
@@ -279,7 +289,31 @@ let package = Package(
 
 ### Manual Installation Guide
 
-See more on [Manual install Guide](https://github.com/SDWebImage/SDWebImage/wiki/Installation-Guide#manual-installation-guide)
++ Check your command line Xcode version
+
+```
+sudo xcode-select -s /path/to/Xcode.app
+```
+
+or
+
+```
+export DEVELOPER_DIR=/path/to/Xcode.app/Contents/Developer
+```
+
++ Run the script to build frameworks
+
+```
+./Scripts/build-frameworks.sh
+```
+
++ Run the script to merge XCFramework
+
+```
+./Scripts/create-xcframework.sh
+```
+
+See more on wiki: [Manual install Guide](https://github.com/SDWebImage/SDWebImage/wiki/Installation-Guide#manual-installation-guide)
 
 ### Import headers in your source files
 
@@ -301,7 +335,14 @@ At this point your workspace should build without error. If you are having probl
 community can help you solve it.
 
 ## Data Collection Practices
-As required by the [App privacy details on the App Store](https://developer.apple.com/app-store/app-privacy-details/), here's SDWebImage's list of [Data Collection Practices](https://sdwebimage.github.io/DataCollection/index.html).
+
+From Xcode 15, we provide the new `PrivacyInfo.xcprivacy` file for privacy details, see [Describing data use in privacy manifests](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests?language=objc)
+
+You can exports the privacy report after archive your App by integrate SDWebImage via SwiftPM/XCFramework (Note: CocoaPods does not support currently).
+
+For old version, as required by the [App privacy details on the App Store](https://developer.apple.com/app-store/app-privacy-details/), here's SDWebImage's list of [Data Collection Practices](https://sdwebimage.github.io/DataCollection/index.html).
+
+
 
 ## Author
 - [Olivier Poitrey](https://github.com/rs)

+ 8 - 18
Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.m

@@ -12,11 +12,10 @@
 
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCacheState.h"
 #import "UIView+WebCache.h"
 #import "SDInternalMacros.h"
 
-static NSString * const SDAlternateImageOperationKey = @"NSButtonAlternateImageOperation";
-
 @implementation NSButton (WebCache)
 
 #pragma mark - Image
@@ -59,7 +58,6 @@ static NSString * const SDAlternateImageOperationKey = @"NSButtonAlternateImageO
                    context:(nullable SDWebImageContext *)context
                   progress:(nullable SDImageLoaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    self.sd_currentImageURL = url;
     [self sd_internalSetImageWithURL:url
                     placeholderImage:placeholder
                              options:options
@@ -113,15 +111,13 @@ static NSString * const SDAlternateImageOperationKey = @"NSButtonAlternateImageO
                             context:(nullable SDWebImageContext *)context
                            progress:(nullable SDImageLoaderProgressBlock)progressBlock
                           completed:(nullable SDExternalCompletionBlock)completedBlock {
-    self.sd_currentAlternateImageURL = url;
-    
     SDWebImageMutableContext *mutableContext;
     if (context) {
         mutableContext = [context mutableCopy];
     } else {
         mutableContext = [NSMutableDictionary dictionary];
     }
-    mutableContext[SDWebImageContextSetImageOperationKey] = SDAlternateImageOperationKey;
+    mutableContext[SDWebImageContextSetImageOperationKey] = @keypath(self, alternateImage);
     @weakify(self);
     [self sd_internalSetImageWithURL:url
                     placeholderImage:placeholder
@@ -142,29 +138,23 @@ static NSString * const SDAlternateImageOperationKey = @"NSButtonAlternateImageO
 #pragma mark - Cancel
 
 - (void)sd_cancelCurrentImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
+    [self sd_cancelImageLoadOperationWithKey:nil];
 }
 
 - (void)sd_cancelCurrentAlternateImageLoad {
-    [self sd_cancelImageLoadOperationWithKey:SDAlternateImageOperationKey];
+    [self sd_cancelImageLoadOperationWithKey:@keypath(self, alternateImage)];
 }
 
-#pragma mark - Private
+#pragma mark - State
 
 - (NSURL *)sd_currentImageURL {
-    return objc_getAssociatedObject(self, @selector(sd_currentImageURL));
+    return [self sd_imageLoadStateForKey:nil].url;
 }
 
-- (void)setSd_currentImageURL:(NSURL *)sd_currentImageURL {
-    objc_setAssociatedObject(self, @selector(sd_currentImageURL), sd_currentImageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
+#pragma mark - Alternate State
 
 - (NSURL *)sd_currentAlternateImageURL {
-    return objc_getAssociatedObject(self, @selector(sd_currentAlternateImageURL));
-}
-
-- (void)setSd_currentAlternateImageURL:(NSURL *)sd_currentAlternateImageURL {
-    objc_setAssociatedObject(self, @selector(sd_currentAlternateImageURL), sd_currentAlternateImageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    return [self sd_imageLoadStateForKey:@keypath(self, alternateImage)].url;
 }
 
 @end

+ 19 - 0
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.h

@@ -88,6 +88,7 @@
 
 /**
  Current animated image format.
+ @note This format is only valid when `animatedImageData` not nil
  */
 @property (nonatomic, assign, readonly) SDImageFormat animatedImageFormat;
 
@@ -107,8 +108,26 @@
 
 // By default, animated image frames are returned by decoding just in time without keeping into memory. But you can choose to preload them into memory as well, See the description in `SDAnimatedImage` protocol.
 // After preloaded, there is no huge difference on performance between this and UIImage's `animatedImageWithImages:duration:`. But UIImage's animation have some issues such like blanking and pausing during segue when using in `UIImageView`. It's recommend to use only if need.
+/**
+ Pre-load all animated image frame into memory. Then later frame image request can directly return the frame for index without decoding.
+ This method may be called on background thread.
+ 
+ @note If one image instance is shared by lots of imageViews, the CPU performance for large animated image will drop down because the request frame index will be random (not in order) and the decoder should take extra effort to keep it re-entrant. You can use this to reduce CPU usage if need. Attention this will consume more memory usage.
+ */
 - (void)preloadAllFrames;
+
+/**
+ Unload all animated image frame from memory if are already pre-loaded. Then later frame image request need decoding. You can use this to free up the memory usage if need.
+ */
 - (void)unloadAllFrames;
+/**
+ Returns a Boolean value indicating whether all animated image frames are already pre-loaded into memory.
+ */
 @property (nonatomic, assign, readonly, getter=isAllFramesLoaded) BOOL allFramesLoaded;
+/**
+ Return the animated image coder if the image is created with `initWithAnimatedCoder:scale:` method.
+ @note We use this with animated coder which conforms to `SDProgressiveImageCoder` for progressive animation decoding.
+ */
+@property (nonatomic, strong, readonly, nullable) id<SDAnimatedImageCoder> animatedCoder;
 
 @end

+ 6 - 0
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.m

@@ -54,9 +54,15 @@ static CGFloat SDImageScaleFromPath(NSString *string) {
 
 #if __has_include(<UIKit/UITraitCollection.h>)
 + (instancetype)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle compatibleWithTraitCollection:(UITraitCollection *)traitCollection {
+#if SD_VISION
+    if (!traitCollection) {
+        traitCollection = UITraitCollection.currentTraitCollection;
+    }
+#else
     if (!traitCollection) {
         traitCollection = UIScreen.mainScreen.traitCollection;
     }
+#endif
     CGFloat scale = traitCollection.displayScale;
     return [self imageNamed:name inBundle:bundle scale:scale];
 }

+ 1 - 0
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.h

@@ -10,6 +10,7 @@
 #import "SDWebImageCompat.h"
 #import "SDImageCoder.h"
 
+/// Animated image playback mode
 typedef NS_ENUM(NSUInteger, SDAnimatedImagePlaybackMode) {
     /**
      * From first to last frame and stop or next loop.

+ 37 - 103
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.m

@@ -10,24 +10,24 @@
 #import "NSImage+Compatibility.h"
 #import "SDDisplayLink.h"
 #import "SDDeviceHelper.h"
+#import "SDImageFramePool.h"
 #import "SDInternalMacros.h"
 
 @interface SDAnimatedImagePlayer () {
-    SD_LOCK_DECLARE(_lock);
     NSRunLoopMode _runLoopMode;
 }
 
+@property (nonatomic, strong) SDImageFramePool *framePool;
+
 @property (nonatomic, strong, readwrite) UIImage *currentFrame;
 @property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
 @property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
 @property (nonatomic, strong) id<SDAnimatedImageProvider> animatedProvider;
-@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
+@property (nonatomic, assign) NSUInteger currentFrameBytes;
 @property (nonatomic, assign) NSTimeInterval currentTime;
 @property (nonatomic, assign) BOOL bufferMiss;
 @property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable;
 @property (nonatomic, assign) BOOL shouldReverse;
-@property (nonatomic, assign) NSUInteger maxBufferCount;
-@property (nonatomic, strong) NSOperationQueue *fetchQueue;
 @property (nonatomic, strong) SDDisplayLink *displayLink;
 
 @end
@@ -47,10 +47,7 @@
         self.totalLoopCount = provider.animatedImageLoopCount;
         self.animatedProvider = provider;
         self.playbackRate = 1.0;
-        SD_LOCK_INIT(_lock);
-#if SD_UIKIT
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
-#endif
+        self.framePool = [SDImageFramePool registerProvider:provider];
     }
     return self;
 }
@@ -60,47 +57,12 @@
     return player;
 }
 
-#pragma mark - Life Cycle
-
 - (void)dealloc {
-#if SD_UIKIT
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
-#endif
-}
-
-- (void)didReceiveMemoryWarning:(NSNotification *)notification {
-    [_fetchQueue cancelAllOperations];
-    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
-        NSNumber *currentFrameIndex = @(self.currentFrameIndex);
-        SD_LOCK(self->_lock);
-        NSArray *keys = self.frameBuffer.allKeys;
-        // only keep the next frame for later rendering
-        for (NSNumber * key in keys) {
-            if (![key isEqualToNumber:currentFrameIndex]) {
-                [self.frameBuffer removeObjectForKey:key];
-            }
-        }
-        SD_UNLOCK(self->_lock);
-    }];
-    [_fetchQueue addOperation:operation];
+    // Dereference the frame pool, when zero the frame pool for provider will dealloc
+    [SDImageFramePool unregisterProvider:self.animatedProvider];
 }
 
 #pragma mark - Private
-- (NSOperationQueue *)fetchQueue {
-    if (!_fetchQueue) {
-        _fetchQueue = [[NSOperationQueue alloc] init];
-        _fetchQueue.maxConcurrentOperationCount = 1;
-        _fetchQueue.name = @"com.hackemist.SDAnimatedImagePlayer.fetchQueue";
-    }
-    return _fetchQueue;
-}
-
-- (NSMutableDictionary<NSNumber *,UIImage *> *)frameBuffer {
-    if (!_frameBuffer) {
-        _frameBuffer = [NSMutableDictionary dictionary];
-    }
-    return _frameBuffer;
-}
 
 - (SDDisplayLink *)displayLink {
     if (!_displayLink) {
@@ -153,11 +115,11 @@
         UIImage *posterFrame = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
         #endif
         if (posterFrame) {
+            // Calculate max buffer size
+            [self calculateMaxBufferCountWithFrame:posterFrame];
             // HACK: The first frame should not check duration and immediately display
             self.needsDisplayWhenImageBecomesAvailable = YES;
-            SD_LOCK(self->_lock);
-            self.frameBuffer[@(self.currentFrameIndex)] = posterFrame;
-            SD_UNLOCK(self->_lock);
+            [self.framePool setFrame:posterFrame atIndex:self.currentFrameIndex];
         }
     }
     
@@ -174,9 +136,7 @@
 }
 
 - (void)clearFrameBuffer {
-    SD_LOCK(_lock);
-    [_frameBuffer removeAllObjects];
-    SD_UNLOCK(_lock);
+    [self.framePool removeAllFrames];
 }
 
 #pragma mark - Animation Control
@@ -184,12 +144,9 @@
     [self.displayLink start];
     // Setup frame
     [self setupCurrentFrame];
-    // Calculate max buffer size
-    [self calculateMaxBufferCount];
 }
 
 - (void)stopPlaying {
-    [_fetchQueue cancelAllOperations];
     // Using `_displayLink` here because when UIImageView dealloc, it may trigger `[self stopAnimating]`, we already release the display link in SDAnimatedImageView's dealloc method.
     [_displayLink stop];
     // We need to reset the frame status, but not trigger any handle. This can ensure next time's playing status correct.
@@ -197,7 +154,6 @@
 }
 
 - (void)pausePlaying {
-    [_fetchQueue cancelAllOperations];
     [_displayLink stop];
 }
 
@@ -259,26 +215,11 @@
     
     
     // Check if we need to display new frame firstly
-    BOOL bufferFull = NO;
     if (self.needsDisplayWhenImageBecomesAvailable) {
-        UIImage *currentFrame;
-        SD_LOCK(_lock);
-        currentFrame = self.frameBuffer[@(currentFrameIndex)];
-        SD_UNLOCK(_lock);
+        UIImage *currentFrame = [self.framePool frameAtIndex:currentFrameIndex];
         
         // Update the current frame
         if (currentFrame) {
-            SD_LOCK(_lock);
-            // Remove the frame buffer if need
-            if (self.frameBuffer.count > self.maxBufferCount) {
-                self.frameBuffer[@(currentFrameIndex)] = nil;
-            }
-            // Check whether we can stop fetch
-            if (self.frameBuffer.count == totalFrameCount) {
-                bufferFull = YES;
-            }
-            SD_UNLOCK(_lock);
-            
             // Update the current frame immediately
             self.currentFrame = currentFrame;
             [self handleFrameChange];
@@ -300,8 +241,7 @@
         if (self.currentTime < currentDuration) {
             // Current frame timestamp not reached, prefetch frame in advance.
             [self prefetchFrameAtIndex:currentFrameIndex
-                             nextIndex:nextFrameIndex
-                            bufferFull:bufferFull];
+                             nextIndex:nextFrameIndex];
             return;
         }
         
@@ -337,43 +277,29 @@
     }
     
     [self prefetchFrameAtIndex:currentFrameIndex
-                     nextIndex:nextFrameIndex
-                    bufferFull:bufferFull];
+                     nextIndex:nextFrameIndex];
 }
 
 // Check if we should prefetch next frame or current frame
 // When buffer miss, means the decode speed is slower than render speed, we fetch current miss frame
 // Or, most cases, the decode speed is faster than render speed, we fetch next frame
 - (void)prefetchFrameAtIndex:(NSUInteger)currentIndex
-                   nextIndex:(NSUInteger)nextIndex
-                  bufferFull:(BOOL)bufferFull {
+                   nextIndex:(NSUInteger)nextIndex {
     NSUInteger fetchFrameIndex = currentIndex;
     UIImage *fetchFrame = nil;
     if (!self.bufferMiss) {
         fetchFrameIndex = nextIndex;
-        SD_LOCK(_lock);
-        fetchFrame = self.frameBuffer[@(nextIndex)];
-        SD_UNLOCK(_lock);
+        fetchFrame = [self.framePool frameAtIndex:nextIndex];
     }
-    if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) {
-        // Prefetch next frame in background queue
-        id<SDAnimatedImageProvider> animatedProvider = self.animatedProvider;
-        @weakify(self);
-        NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
-            @strongify(self);
-            if (!self) {
-                return;
-            }
-            UIImage *frame = [animatedProvider animatedImageFrameAtIndex:fetchFrameIndex];
-
-            BOOL isAnimating = self.displayLink.isRunning;
-            if (isAnimating) {
-                SD_LOCK(self->_lock);
-                self.frameBuffer[@(fetchFrameIndex)] = frame;
-                SD_UNLOCK(self->_lock);
-            }
-        }];
-        [self.fetchQueue addOperation:operation];
+    BOOL bufferFull = NO;
+    if (self.framePool.currentFrameCount == self.totalFrameCount) {
+        bufferFull = YES;
+    }
+    if (!fetchFrame && !bufferFull) {
+        // Calculate max buffer size
+        [self calculateMaxBufferCountWithFrame:self.currentFrame];
+        // Prefetch next frame
+        [self.framePool prefetchFrameAtIndex:fetchFrameIndex];
     }
 }
 
@@ -390,9 +316,17 @@
 }
 
 #pragma mark - Util
-- (void)calculateMaxBufferCount {
-    NSUInteger bytes = CGImageGetBytesPerRow(self.currentFrame.CGImage) * CGImageGetHeight(self.currentFrame.CGImage);
-    if (bytes == 0) bytes = 1024;
+- (void)calculateMaxBufferCountWithFrame:(nonnull UIImage *)frame {
+    NSUInteger bytes = self.currentFrameBytes;
+    if (bytes == 0) {
+        bytes = CGImageGetBytesPerRow(frame.CGImage) * CGImageGetHeight(frame.CGImage);
+        if (bytes == 0) {
+            bytes = 1024;
+        } else {
+            // Cache since most animated image each frame bytes is the same
+            self.currentFrameBytes = bytes;
+        }
+    }
     
     NSUInteger max = 0;
     if (self.maxBufferSize > 0) {
@@ -410,7 +344,7 @@
         maxBufferCount = 1;
     }
     
-    self.maxBufferCount = maxBufferCount;
+    self.framePool.maxBufferCount = maxBufferCount;
 }
 
 + (NSString *)defaultRunLoopMode {

+ 10 - 0
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.h

@@ -10,6 +10,8 @@
 
 #if SD_MAC
 
+#import "NSData+ImageContentType.h"
+
 /**
  A subclass of `NSBitmapImageRep` to fix that GIF duration issue because `NSBitmapImageRep` will reset `NSImageCurrentFrameDuration` by using `kCGImagePropertyGIFDelayTime` but not `kCGImagePropertyGIFUnclampedDelayTime`.
  This also fix the GIF loop count issue, which will use the Netscape standard (See http://www6.uniovi.es/gifanim/gifabout.htm)  to only place once when the `kCGImagePropertyGIFLoopCount` is nil. This is what modern browser's behavior.
@@ -18,6 +20,14 @@
  */
 @interface SDAnimatedImageRep : NSBitmapImageRep
 
+/// Current animated image format.
+/// @note This format is only valid when `animatedImageData` not nil
+@property (nonatomic, assign, readonly) SDImageFormat animatedImageFormat;
+
+/// This allows to retrive the compressed data like GIF using `sd_imageData` on parent `NSImage`, without re-encoding (waste CPU and RAM)
+/// @note This is typically nonnull when you create with `initWithData:`, even it's marked as weak, because ImageIO retain it
+@property (nonatomic, readonly, nullable, weak) NSData *animatedImageData;
+
 @end
 
 #endif

+ 16 - 3
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.m

@@ -19,6 +19,7 @@
 @interface SDAnimatedImageRep ()
 /// This wrap the animated image frames for legacy animated image coder API (`encodedDataWithImage:`).
 @property (nonatomic, readwrite, weak) NSArray<SDImageFrame *> *frames;
+@property (nonatomic, assign, readwrite) SDImageFormat animatedImageFormat;
 @end
 
 @implementation SDAnimatedImageRep {
@@ -33,9 +34,12 @@
 }
 
 - (instancetype)copyWithZone:(NSZone *)zone {
-  SDAnimatedImageRep *imageRep = [super copyWithZone:zone];
-  CFRetain(imageRep->_imageSource);
-  return imageRep;
+    SDAnimatedImageRep *imageRep = [super copyWithZone:zone];
+    // super will copy all ivars
+    if (imageRep->_imageSource) {
+        CFRetain(imageRep->_imageSource);
+    }
+    return imageRep;
 }
 
 // `NSBitmapImageRep`'s `imageRepWithData:` is not designed initializer
@@ -63,15 +67,19 @@
         if (!type) {
             return self;
         }
+        _animatedImageData = data; // CGImageSource will retain the data internally, no extra copy
+        SDImageFormat format = SDImageFormatUndefined;
         if (CFStringCompare(type, kSDUTTypeGIF, 0) == kCFCompareEqualTo) {
             // GIF
             // Fix the `NSBitmapImageRep` GIF loop count calculation issue
             // Which will use 0 when there are no loop count information metadata in GIF data
+            format = SDImageFormatGIF;
             NSUInteger loopCount = [SDImageGIFCoder imageLoopCountWithSource:imageSource];
             [self setProperty:NSImageLoopCount withValue:@(loopCount)];
         } else if (CFStringCompare(type, kSDUTTypePNG, 0) == kCFCompareEqualTo) {
             // APNG
             // Do initialize about frame count, current frame/duration and loop count
+            format = SDImageFormatPNG;
             [self setProperty:NSImageFrameCount withValue:@(frameCount)];
             [self setProperty:NSImageCurrentFrame withValue:@(0)];
             NSUInteger loopCount = [SDImageAPNGCoder imageLoopCountWithSource:imageSource];
@@ -79,6 +87,7 @@
         } else if (CFStringCompare(type, kSDUTTypeHEICS, 0) == kCFCompareEqualTo) {
             // HEIC
             // Do initialize about frame count, current frame/duration and loop count
+            format = SDImageFormatHEIC;
             [self setProperty:NSImageFrameCount withValue:@(frameCount)];
             [self setProperty:NSImageCurrentFrame withValue:@(0)];
             NSUInteger loopCount = [SDImageHEICCoder imageLoopCountWithSource:imageSource];
@@ -86,11 +95,15 @@
         } else if (CFStringCompare(type, kSDUTTypeWebP, 0) == kCFCompareEqualTo) {
             // WebP
             // Do initialize about frame count, current frame/duration and loop count
+            format = SDImageFormatWebP;
             [self setProperty:NSImageFrameCount withValue:@(frameCount)];
             [self setProperty:NSImageCurrentFrame withValue:@(0)];
             NSUInteger loopCount = [SDImageAWebPCoder imageLoopCountWithSource:imageSource];
             [self setProperty:NSImageLoopCount withValue:@(loopCount)];
+        } else {
+            format = [NSData sd_imageFormatForImageData:data];
         }
+        _animatedImageFormat = format;
     }
     return self;
 }

+ 15 - 11
Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.m

@@ -20,7 +20,9 @@ static void SDReleaseBlock(void *context) {
     CFRelease(context);
 }
 
-static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block_t _Nonnull block, BOOL async) {
+static void SDSafeExecute(SDCallbackQueue *callbackQueue, dispatch_block_t _Nonnull block, BOOL async) {
+    // Extendc gcd queue's life cycle
+    dispatch_queue_t queue = callbackQueue.queue;
     // Special handle for main queue label only (custom queue can have the same label)
     const char *label = dispatch_queue_get_label(queue);
     if (label && label == dispatch_queue_get_label(dispatch_get_main_queue())) {
@@ -32,16 +34,18 @@ static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block
     }
     // Check specific to detect queue equal
     void *specific = dispatch_queue_get_specific(queue, SDCallbackQueueKey);
-    void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
-    if (specific && currentSpecific && CFGetTypeID(specific) == CFUUIDGetTypeID() && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
-        block();
-    } else {
-        if (async) {
-            dispatch_async(queue, block);
-        } else {
-            dispatch_sync(queue, block);
+    if (specific && CFGetTypeID(specific) == CFUUIDGetTypeID()) {
+        void *currentSpecific = dispatch_get_specific(SDCallbackQueueKey);
+        if (currentSpecific && CFGetTypeID(currentSpecific) == CFUUIDGetTypeID() && CFEqual(specific, currentSpecific)) {
+            block();
+            return;
         }
     }
+    if (async) {
+        dispatch_async(queue, block);
+    } else {
+        dispatch_sync(queue, block);
+    }
 }
 
 @implementation SDCallbackQueue
@@ -82,7 +86,7 @@ static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block
 - (void)sync:(nonnull dispatch_block_t)block {
     switch (self.policy) {
         case SDCallbackPolicySafeExecute:
-            SDSafeExecute(self.queue, block, NO);
+            SDSafeExecute(self, block, NO);
             break;
         case SDCallbackPolicyDispatch:
             dispatch_sync(self.queue, block);
@@ -96,7 +100,7 @@ static void inline SDSafeExecute(dispatch_queue_t _Nonnull queue, dispatch_block
 - (void)async:(nonnull dispatch_block_t)block {
     switch (self.policy) {
         case SDCallbackPolicySafeExecute:
-            SDSafeExecute(self.queue, block, YES);
+            SDSafeExecute(self, block, YES);
             break;
         case SDCallbackPolicyDispatch:
             dispatch_async(self.queue, block);

+ 6 - 0
Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.h

@@ -17,11 +17,17 @@
  For others (macOS/watchOS or iOS/tvOS 10-), these method use the `SDImageGraphics.h` to implements the same behavior (but without dynamic bitmap support)
 */
 
+/// A closure for drawing an image.
 typedef void (^SDGraphicsImageDrawingActions)(CGContextRef _Nonnull context);
+/// Constants that specify the color range of the image renderer context.
 typedef NS_ENUM(NSInteger, SDGraphicsImageRendererFormatRange) {
+    /// The image renderer context doesn’t specify a color range.
     SDGraphicsImageRendererFormatRangeUnspecified = -1,
+    /// The system automatically chooses the image renderer context’s pixel format according to the color range of its content.
     SDGraphicsImageRendererFormatRangeAutomatic = 0,
+    /// The image renderer context supports wide color.
     SDGraphicsImageRendererFormatRangeExtended,
+    /// The image renderer context doesn’t support extended colors.
     SDGraphicsImageRendererFormatRangeStandard
 };
 

+ 12 - 4
Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.m

@@ -72,7 +72,9 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 - (SDGraphicsImageRendererFormatRange)preferredRange {
-#if SD_UIKIT
+#if SD_VISION
+  return (SDGraphicsImageRendererFormatRange)self.uiformat.preferredRange;
+#elif SD_UIKIT
     if (@available(iOS 10.0, tvOS 10.10, *)) {
         if (@available(iOS 12.0, tvOS 12.0, *)) {
             return (SDGraphicsImageRendererFormatRange)self.uiformat.preferredRange;
@@ -93,7 +95,9 @@
 }
 
 - (void)setPreferredRange:(SDGraphicsImageRendererFormatRange)preferredRange {
-#if SD_UIKIT
+#if SD_VISION
+  self.uiformat.preferredRange = (UIGraphicsImageRendererFormatRange)preferredRange;
+#elif SD_UIKIT
     if (@available(iOS 10.0, tvOS 10.10, *)) {
         if (@available(iOS 12.0, tvOS 12.0, *)) {
             self.uiformat.preferredRange = (UIGraphicsImageRendererFormatRange)preferredRange;
@@ -127,7 +131,9 @@
             self.uiformat = uiformat;
         } else {
 #endif
-#if SD_WATCH
+#if SD_VISION
+            CGFloat screenScale = UITraitCollection.currentTraitCollection.displayScale;
+#elif SD_WATCH
             CGFloat screenScale = [WKInterfaceDevice currentDevice].screenScale;
 #elif SD_UIKIT
             CGFloat screenScale = [UIScreen mainScreen].scale;
@@ -167,7 +173,9 @@
             self.uiformat = uiformat;
         } else {
 #endif
-#if SD_WATCH
+#if SD_VISION
+            CGFloat screenScale = UITraitCollection.currentTraitCollection.displayScale;
+#elif SD_WATCH
             CGFloat screenScale = [WKInterfaceDevice currentDevice].screenScale;
 #elif SD_UIKIT
             CGFloat screenScale = [UIScreen mainScreen].scale;

+ 3 - 1
Pods/SDWebImage/SDWebImage/Core/SDImageCache.h

@@ -37,8 +37,10 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
     /**
      * By default, we will decode the image in the background during cache query and download from the network. This can help to improve performance because when rendering image on the screen, it need to be firstly decoded. But this happen on the main queue by Core Animation.
      * However, this process may increase the memory usage as well. If you are experiencing a issue due to excessive memory consumption, This flag can prevent decode the image.
+     * @note 5.14.0 introduce `SDImageCoderDecodeUseLazyDecoding`, use that for better control from codec, instead of post-processing. Which acts the similar like this option but works for SDAnimatedImage as well (this one does not)
+     * @deprecated Deprecated in v5.17.0, if you don't want force-decode, pass [.imageForceDecodePolicy] = [SDImageForceDecodePolicy.never] in context option
      */
-    SDImageCacheAvoidDecodeImage = 1 << 4,
+    SDImageCacheAvoidDecodeImage API_DEPRECATED("Use SDWebImageContextImageForceDecodePolicy instead", macos(10.10, 10.10), ios(8.0, 8.0), tvos(9.0, 9.0), watchos(2.0, 2.0)) = 1 << 4,
     /**
      * By default, we decode the animated image. This flag can force decode the first frame only and produce the static image.
      */

+ 21 - 9
Pods/SDWebImage/SDWebImage/Core/SDImageCache.m

@@ -262,16 +262,16 @@ static NSString * _defaultDiskCacheDirectory;
             SDImageFormat format = image.sd_imageFormat;
             if (format == SDImageFormatUndefined) {
                 // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
-                if (image.sd_isAnimated) {
+                if (image.sd_imageFrameCount > 1) {
                     format = SDImageFormatGIF;
                 } else {
                     // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
                     format = [SDImageCoderHelper CGImageContainsAlpha:image.CGImage] ? SDImageFormatPNG : SDImageFormatJPEG;
                 }
             }
-            NSData *data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:context[SDWebImageContextImageEncodeOptions]];
+            NSData *encodedData = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:context[SDWebImageContextImageEncodeOptions]];
             dispatch_async(self.ioQueue, ^{
-                [self _storeImageDataToDisk:data forKey:key];
+                [self _storeImageDataToDisk:encodedData forKey:key];
                 [self _archivedDataWithImage:image forKey:key];
                 if (completionBlock) {
                     [(queue ?: SDCallbackQueue.mainQueue) async:^{
@@ -461,7 +461,7 @@ static NSString * _defaultDiskCacheDirectory;
     if (image) {
         if (options & SDImageCacheDecodeFirstFrameOnly) {
             // Ensure static image
-            if (image.sd_isAnimated) {
+            if (image.sd_imageFrameCount > 1) {
 #if SD_MAC
                 image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
 #else
@@ -593,7 +593,7 @@ static NSString * _defaultDiskCacheDirectory;
     if (image) {
         if (options & SDImageCacheDecodeFirstFrameOnly) {
             // Ensure static image
-            if (image.sd_isAnimated) {
+            if (image.sd_imageFrameCount > 1) {
 #if SD_MAC
                 image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
 #else
@@ -668,11 +668,17 @@ static NSString * _defaultDiskCacheDirectory;
                 // Query full size cache key which generate a thumbnail, should not write back to full size memory cache
                 shouldCacheToMomery = NO;
             }
+            // Special case: If user query image in list for the same URL, to avoid decode and write **same** image object into disk cache multiple times, we query and check memory cache here again.
+            if (shouldCacheToMomery && self.config.shouldCacheImagesInMemory) {
+                diskImage = [self.memoryCache objectForKey:key];
+            }
             // decode image data only if in-memory cache missed
-            diskImage = [self diskImageForKey:key data:diskData options:options context:context];
-            if (shouldCacheToMomery && diskImage && self.config.shouldCacheImagesInMemory) {
-                NSUInteger cost = diskImage.sd_memoryCost;
-                [self.memoryCache setObject:diskImage forKey:key cost:cost];
+            if (!diskImage) {
+                diskImage = [self diskImageForKey:key data:diskData options:options context:context];
+                if (shouldCacheToMomery && diskImage && self.config.shouldCacheImagesInMemory) {
+                    NSUInteger cost = diskImage.sd_memoryCost;
+                    [self.memoryCache setObject:diskImage forKey:key cost:cost];
+                }
             }
         }
         return diskImage;
@@ -877,6 +883,8 @@ static NSString * _defaultDiskCacheDirectory;
 }
 
 #pragma mark - Helper
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 + (SDWebImageOptions)imageOptionsFromCacheOptions:(SDImageCacheOptions)cacheOptions {
     SDWebImageOptions options = 0;
     if (cacheOptions & SDImageCacheScaleDownLargeImages) options |= SDWebImageScaleDownLargeImages;
@@ -887,6 +895,7 @@ static NSString * _defaultDiskCacheDirectory;
     
     return options;
 }
+#pragma clang diagnostic pop
 
 @end
 
@@ -898,6 +907,8 @@ static NSString * _defaultDiskCacheDirectory;
     return [self queryImageForKey:key options:options context:context cacheType:SDImageCacheTypeAll completion:completionBlock];
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 - (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
     SDImageCacheOptions cacheOptions = 0;
     if (options & SDWebImageQueryMemoryData) cacheOptions |= SDImageCacheQueryMemoryData;
@@ -911,6 +922,7 @@ static NSString * _defaultDiskCacheDirectory;
     
     return [self queryCacheOperationForKey:key options:cacheOptions context:context cacheType:cacheType done:completionBlock];
 }
+#pragma clang diagnostic pop
 
 - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
     [self storeImage:image imageData:imageData forKey:key options:0 context:nil cacheType:cacheType completion:completionBlock];

+ 18 - 12
Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.m

@@ -18,14 +18,14 @@
 SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext * _Nullable context, SDWebImageOptions options, NSString * _Nonnull cacheKey) {
     BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
     NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
-    CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey);
+    CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey); // Use cache key to detect scale
     NSNumber *preserveAspectRatioValue = context[SDWebImageContextImagePreserveAspectRatio];
     NSValue *thumbnailSizeValue;
     BOOL shouldScaleDown = SD_OPTIONS_CONTAINS(options, SDWebImageScaleDownLargeImages);
-    if (shouldScaleDown) {
-        CGFloat thumbnailPixels = SDImageCoderHelper.defaultScaleDownLimitBytes / 4;
-        CGFloat dimension = ceil(sqrt(thumbnailPixels));
-        thumbnailSizeValue = @(CGSizeMake(dimension, dimension));
+    NSNumber *scaleDownLimitBytesValue = context[SDWebImageContextImageScaleDownLimitBytes];
+    if (scaleDownLimitBytesValue == nil && shouldScaleDown) {
+        // Use the default limit bytes
+        scaleDownLimitBytesValue = @(SDImageCoderHelper.defaultScaleDownLimitBytes);
     }
     if (context[SDWebImageContextImageThumbnailPixelSize]) {
         thumbnailSizeValue = context[SDWebImageContextImageThumbnailPixelSize];
@@ -56,6 +56,7 @@ SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext *
     mutableCoderOptions[SDImageCoderDecodeThumbnailPixelSize] = thumbnailSizeValue;
     mutableCoderOptions[SDImageCoderDecodeTypeIdentifierHint] = typeIdentifierHint;
     mutableCoderOptions[SDImageCoderDecodeFileExtensionHint] = fileExtensionHint;
+    mutableCoderOptions[SDImageCoderDecodeScaleDownLimitBytes] = scaleDownLimitBytesValue;
     
     return [mutableCoderOptions copy];
 }
@@ -70,6 +71,7 @@ void SDSetDecodeOptionsToContext(SDWebImageMutableContext * _Nonnull mutableCont
     mutableContext[SDWebImageContextImageScaleFactor] = decodeOptions[SDImageCoderDecodeScaleFactor];
     mutableContext[SDWebImageContextImagePreserveAspectRatio] = decodeOptions[SDImageCoderDecodePreserveAspectRatio];
     mutableContext[SDWebImageContextImageThumbnailPixelSize] = decodeOptions[SDImageCoderDecodeThumbnailPixelSize];
+    mutableContext[SDWebImageContextImageScaleDownLimitBytes] = decodeOptions[SDImageCoderDecodeScaleDownLimitBytes];
     
     NSString *typeIdentifierHint = decodeOptions[SDImageCoderDecodeTypeIdentifierHint];
     if (!typeIdentifierHint) {
@@ -121,15 +123,19 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
         image = [imageCoder decodedImageWithData:imageData options:coderOptions];
     }
     if (image) {
-        BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage);
-        BOOL lazyDecode = [coderOptions[SDImageCoderDecodeUseLazyDecoding] boolValue];
-        if (lazyDecode) {
-            // lazyDecode = NO means we should not forceDecode, highest priority
-            shouldDecode = NO;
+        SDImageForceDecodePolicy policy = SDImageForceDecodePolicyAutomatic;
+        NSNumber *polivyValue = context[SDWebImageContextImageForceDecodePolicy];
+        if (polivyValue != nil) {
+            policy = polivyValue.unsignedIntegerValue;
         }
-        if (shouldDecode) {
-            image = [SDImageCoderHelper decodedImageWithImage:image];
+        // TODO: Deprecated, remove in SD 6.0...
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        if (SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage)) {
+            policy = SDImageForceDecodePolicyNever;
         }
+#pragma clang diagnostic pop
+        image = [SDImageCoderHelper decodedImageWithImage:image policy:policy];
         // assign the decode options, to let manager check whether to re-decode if needed
         image.sd_decodeOptions = coderOptions;
     }

+ 13 - 0
Pods/SDWebImage/SDWebImage/Core/SDImageCoder.h

@@ -75,6 +75,19 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeTypeIdenti
  */
 FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeUseLazyDecoding;
 
+/**
+ A NSUInteger value to provide the limit bytes during decoding. This can help to avoid OOM on large frame count animated image or large pixel static image when you don't know how much RAM it occupied before decoding
+ The decoder will do these logic based on limit bytes:
+ 1. Get the total frame count (static image means 1)
+ 2. Calculate the `framePixelSize` width/height to `sqrt(limitBytes / frameCount / bytesPerPixel)`, keeping aspect ratio (at least 1x1)
+ 3. If the `framePixelSize < originalImagePixelSize`, then do thumbnail decoding (see `SDImageCoderDecodeThumbnailPixelSize`) use the `framePixelSize` and `preseveAspectRatio = YES`
+ 4. Else, use the full pixel decoding (small than limit bytes)
+ 5. Whatever result, this does not effect the animated/static behavior of image. So even if you set `limitBytes = 1 && frameCount = 100`, we will stll create animated image with each frame `1x1` pixel size.
+ @note You can use the logic from `+[SDImageCoder scaledSizeWithImageSize:limitBytes:bytesPerPixel:frameCount:]`
+ @note This option has higher priority than `.decodeThumbnailPixelSize`
+ */
+FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeScaleDownLimitBytes;
+
 // These options are for image encoding
 /**
  A Boolean value indicating whether to encode the first frame only for animated image during encoding. (NSNumber). If not provide, encode animated image if need.

+ 1 - 0
Pods/SDWebImage/SDWebImage/Core/SDImageCoder.m

@@ -15,6 +15,7 @@ SDImageCoderOption const SDImageCoderDecodeThumbnailPixelSize = @"decodeThumbnai
 SDImageCoderOption const SDImageCoderDecodeFileExtensionHint = @"decodeFileExtensionHint";
 SDImageCoderOption const SDImageCoderDecodeTypeIdentifierHint = @"decodeTypeIdentifierHint";
 SDImageCoderOption const SDImageCoderDecodeUseLazyDecoding = @"decodeUseLazyDecoding";
+SDImageCoderOption const SDImageCoderDecodeScaleDownLimitBytes = @"decodeScaleDownLimitBytes";
 
 SDImageCoderOption const SDImageCoderEncodeFirstFrameOnly = @"encodeFirstFrameOnly";
 SDImageCoderOption const SDImageCoderEncodeCompressionQuality = @"encodeCompressionQuality";

+ 76 - 2
Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.h

@@ -10,6 +10,7 @@
 #import "SDWebImageCompat.h"
 #import "SDImageFrame.h"
 
+/// The options controls how we force pre-draw the image (to avoid lazy-decoding). Which need OS's framework compatibility
 typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
     /// automatically choose the solution based on image format, hardware, OS version. This keep balance for compatibility and performance. Default after SDWebImage 5.13.0
     SDImageCoderDecodeSolutionAutomatic,
@@ -19,6 +20,35 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
     SDImageCoderDecodeSolutionUIKit
 };
 
+/// The policy to force-decode the origin CGImage (produced by Image Coder Plugin)
+/// Some CGImage may be lazy, or not lazy, but need extra copy to render on screen
+/// The force-decode step help to `pre-process` to get the best suitable CGImage to render, which can increase frame rate
+/// The downside is that force-decode may consume RAM and CPU, and may loss the `lazy` support (lazy CGImage can be purged when memory warning, and re-created if need), see more: `SDImageCoderDecodeUseLazyDecoding`
+typedef NS_ENUM(NSUInteger, SDImageForceDecodePolicy) {
+    /// Based on input CGImage's colorspace, alignment, bitmapinfo, if it may trigger `CA::copy_image` extra copy, we will force-decode, else don't
+    SDImageForceDecodePolicyAutomatic,
+    /// Never force decode input CGImage
+    SDImageForceDecodePolicyNever,
+    /// Always force decode input CGImage (only once)
+    SDImageForceDecodePolicyAlways
+};
+
+/// Byte alignment the bytes size with alignment
+/// - Parameters:
+///   - size: The bytes size
+///   - alignment: The alignment, in bytes
+static inline size_t SDByteAlign(size_t size, size_t alignment) {
+    return ((size + (alignment - 1)) / alignment) * alignment;
+}
+
+/// The pixel format about the information to call `CGImageCreate` suitable for current hardware rendering
+typedef struct SDImagePixelFormat {
+    /// Typically is pre-multiplied RGBA8888 for alpha image, RGBX8888 for non-alpha image.
+    CGBitmapInfo bitmapInfo;
+    /// Typically is 32, the 8 pixels bytesPerRow.
+    size_t alignment;
+} SDImagePixelFormat;
+
 /**
  Provide some common helper methods for building the image decoder/encoder.
  */
@@ -44,16 +74,31 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
  */
 + (NSArray<SDImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage NS_SWIFT_NAME(frames(from:));
 
+#pragma mark - Preferred Rendering Format
+/// For coders who use `CGImageCreate`, use the information below to create an effient CGImage which can be render on GPU without Core Animation's extra copy (`CA::Render::copy_image`), which can be debugged using `Color Copied Image` in Xcode Instruments
+/// `CGImageCreate`'s `bytesPerRow`, `space`, `bitmapInfo` params should use the information below.
 /**
  Return the shared device-dependent RGB color space. This follows The Get Rule.
- On iOS, it's created with deviceRGB (if available, use sRGB).
- On macOS, it's from the screen colorspace (if failed, use deviceRGB)
  Because it's shared, you should not retain or release this object.
+ Typically is sRGB for iOS, screen color space (like Color LCD) for macOS.
  
  @return The device-dependent RGB color space
  */
 + (CGColorSpaceRef _Nonnull)colorSpaceGetDeviceRGB CF_RETURNS_NOT_RETAINED;
 
+/**
+ Tthis returns the pixel format **Preferred from current hardward && OS using runtime detection**
+ @param containsAlpha Whether the image to render contains alpha channel
+ */
++ (SDImagePixelFormat)preferredPixelFormat:(BOOL)containsAlpha;
+
+/**
+ Check whether CGImage is hardware supported to rendering on screen, without the trigger of `CA::Render::copy_image`
+ You can debug the copied image by using Xcode's `Color Copied Image`, the copied image will turn Cyan and occupy double RAM for bitmap buffer.
+ Typically, when the CGImage's using the method above (`colorspace` / `alignment` / `bitmapInfo`) can render withtout the copy.
+ */
++ (BOOL)CGImageIsHardwareSupported:(_Nonnull CGImageRef)cgImage;
+
 /**
  Check whether CGImage contains alpha channel.
  
@@ -86,6 +131,8 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
  Create a scaled CGImage by the provided CGImage and size. This follows The Create Rule and you are response to call release after usage.
  It will detect whether the image size matching the scale size, if not, stretch the image to the target size.
  @note If you need to keep aspect ratio, you can calculate the scale size by using `scaledSizeWithImageSize` first.
+ @note This scale does not change bits per components (which means RGB888 in, RGB888 out), supports 8/16/32(float) bpc. But the method in UIImage+Transform does not gurantee this.
+ @note All supported CGImage pixel format: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
  
  @param cgImage The CGImage
  @param size The scale size in pixel.
@@ -103,23 +150,50 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
  */
 + (CGSize)scaledSizeWithImageSize:(CGSize)imageSize scaleSize:(CGSize)scaleSize preserveAspectRatio:(BOOL)preserveAspectRatio shouldScaleUp:(BOOL)shouldScaleUp;
 
+/// Calculate the limited image size with the bytes, when using `SDImageCoderDecodeScaleDownLimitBytes`. This preserve aspect ratio and never scale up
+/// @param imageSize The image size (in pixel or point defined by caller)
+/// @param limitBytes The limit bytes
+/// @param bytesPerPixel The bytes per pixel
+/// @param frameCount The image frame count, 0 means 1 frame as well
++ (CGSize)scaledSizeWithImageSize:(CGSize)imageSize limitBytes:(NSUInteger)limitBytes bytesPerPixel:(NSUInteger)bytesPerPixel frameCount:(NSUInteger)frameCount;
 /**
  Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image. On iOS 15+, this may use `UIImage.preparingForDisplay()` to use CMPhoto for better performance than the old solution.
  @param image The image to be decoded
+ @note This translate to `decodedImageWithImage:policy:` with automatic policy
  @return The decoded image
  */
 + (UIImage * _Nullable)decodedImageWithImage:(UIImage * _Nullable)image;
 
+/**
+ Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image. On iOS 15+, this may use `UIImage.preparingForDisplay()` to use CMPhoto for better performance than the old solution.
+ @param image The image to be decoded
+ @param policy The force decode policy to decode image, will effect the check whether input image need decode
+ @return The decoded image
+ */
++ (UIImage * _Nullable)decodedImageWithImage:(UIImage * _Nullable)image policy:(SDImageForceDecodePolicy)policy;
+
 /**
  Return the decoded and probably scaled down image by the provided image. If the image pixels bytes size large than the limit bytes, will try to scale down. Or just works as `decodedImageWithImage:`, never scale up.
  @warning You should not pass too small bytes, the suggestion value should be larger than 1MB. Even we use Tile Decoding to avoid OOM, however, small bytes will consume much more CPU time because we need to iterate more times to draw each tile.
 
  @param image The image to be decoded and scaled down
  @param bytes The limit bytes size. Provide 0 to use the build-in limit.
+ @note This translate to `decodedAndScaledDownImageWithImage:limitBytes:policy:` with automatic policy
  @return The decoded and probably scaled down image
  */
 + (UIImage * _Nullable)decodedAndScaledDownImageWithImage:(UIImage * _Nullable)image limitBytes:(NSUInteger)bytes;
 
+/**
+ Return the decoded and probably scaled down image by the provided image. If the image pixels bytes size large than the limit bytes, will try to scale down. Or just works as `decodedImageWithImage:`, never scale up.
+ @warning You should not pass too small bytes, the suggestion value should be larger than 1MB. Even we use Tile Decoding to avoid OOM, however, small bytes will consume much more CPU time because we need to iterate more times to draw each tile.
+
+ @param image The image to be decoded and scaled down
+ @param bytes The limit bytes size. Provide 0 to use the build-in limit.
+ @param policy The force decode policy to decode image, will effect the check whether input image need decode
+ @return The decoded and probably scaled down image
+ */
++ (UIImage * _Nullable)decodedAndScaledDownImageWithImage:(UIImage * _Nullable)image limitBytes:(NSUInteger)bytes policy:(SDImageForceDecodePolicy)policy;
+
 /**
  Control the default force decode solution. Available solutions  in `SDImageCoderDecodeSolution`.
  @note Defaults to `SDImageCoderDecodeSolutionAutomatic`, which prefers to use UIKit for JPEG/HEIF, and fallback on CoreGraphics. If you want control on your hand, set the other solution.

+ 245 - 55
Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.m

@@ -17,11 +17,10 @@
 #import "SDInternalMacros.h"
 #import "SDGraphicsImageRenderer.h"
 #import "SDInternalMacros.h"
+#import "SDDeviceHelper.h"
 #import <Accelerate/Accelerate.h>
 
-static inline size_t SDByteAlign(size_t size, size_t alignment) {
-    return ((size + (alignment - 1)) / alignment) * alignment;
-}
+#define kCGColorSpaceDeviceRGB CFSTR("kCGColorSpaceDeviceRGB")
 
 #if SD_UIKIT
 static inline UIImage *SDImageDecodeUIKit(UIImage *image) {
@@ -72,6 +71,42 @@ static inline BOOL SDImageSupportsHardwareHEVCDecoder(void) {
 }
 #endif
 
+static UIImage * _Nonnull SDImageGetAlphaDummyImage(void) {
+    static dispatch_once_t onceToken;
+    static UIImage *dummyImage;
+    dispatch_once(&onceToken, ^{
+        SDGraphicsImageRendererFormat *format = [SDGraphicsImageRendererFormat preferredFormat];
+        format.scale = 1;
+        format.opaque = NO;
+        CGSize size = CGSizeMake(1, 1);
+        SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
+        dummyImage = [renderer imageWithActions:^(CGContextRef  _Nonnull context) {
+            CGContextSetFillColorWithColor(context, UIColor.redColor.CGColor);
+            CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
+        }];
+        NSCAssert(dummyImage, @"The sample alpha image (1x1 pixels) returns nil, OS bug ?");
+    });
+    return dummyImage;
+}
+
+static UIImage * _Nonnull SDImageGetNonAlphaDummyImage(void) {
+    static dispatch_once_t onceToken;
+    static UIImage *dummyImage;
+    dispatch_once(&onceToken, ^{
+        SDGraphicsImageRendererFormat *format = [SDGraphicsImageRendererFormat preferredFormat];
+        format.scale = 1;
+        format.opaque = YES;
+        CGSize size = CGSizeMake(1, 1);
+        SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
+        dummyImage = [renderer imageWithActions:^(CGContextRef  _Nonnull context) {
+            CGContextSetFillColorWithColor(context, UIColor.redColor.CGColor);
+            CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
+        }];
+        NSCAssert(dummyImage, @"The sample non-alpha image (1x1 pixels) returns nil, OS bug ?");
+    });
+    return dummyImage;
+}
+
 static SDImageCoderDecodeSolution kDefaultDecodeSolution = SDImageCoderDecodeSolutionAutomatic;
 
 static const size_t kBytesPerPixel = 4;
@@ -258,11 +293,82 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     static CGColorSpaceRef colorSpace;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
+#if SD_MAC
+        NSScreen *mainScreen = nil;
+        if (@available(macOS 10.12, *)) {
+            mainScreen = [NSScreen mainScreen];
+        } else {
+            mainScreen = [NSScreen screens].firstObject;
+        }
+        colorSpace = mainScreen.colorSpace.CGColorSpace;
+#else
         colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
+#endif
     });
     return colorSpace;
 }
 
++ (SDImagePixelFormat)preferredPixelFormat:(BOOL)containsAlpha {
+    CGImageRef cgImage;
+    if (containsAlpha) {
+        cgImage = SDImageGetAlphaDummyImage().CGImage;
+    } else {
+        cgImage = SDImageGetNonAlphaDummyImage().CGImage;
+    }
+    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage);
+    size_t bitsPerPixel = 8;
+    if (SD_OPTIONS_CONTAINS(bitmapInfo, kCGBitmapFloatComponents)) {
+        bitsPerPixel = 16;
+    }
+    size_t components = 4; // Hardcode now
+    // https://github.com/path/FastImageCache#byte-alignment
+    // A properly aligned bytes-per-row value must be a multiple of 8 pixels × bytes per pixel.
+    size_t alignment = (bitsPerPixel / 8) * components * 8;
+    SDImagePixelFormat pixelFormat = {
+        .bitmapInfo = bitmapInfo,
+        .alignment = alignment
+    };
+    return pixelFormat;
+}
+
++ (BOOL)CGImageIsHardwareSupported:(CGImageRef)cgImage {
+    BOOL supported = YES;
+    // 1. Check byte alignment
+    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
+    BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
+    SDImagePixelFormat pixelFormat = [self preferredPixelFormat:hasAlpha];
+    if (SDByteAlign(bytesPerRow, pixelFormat.alignment) == bytesPerRow) {
+        // byte aligned, OK
+        supported &= YES;
+    } else {
+        // not aligned
+        supported &= NO;
+    }
+    if (!supported) return supported;
+    
+    // 2. Check color space
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
+    CGColorSpaceRef perferredColorSpace = [self colorSpaceGetDeviceRGB];
+    if (colorSpace == perferredColorSpace) {
+        return supported;
+    } else {
+        if (@available(iOS 10.0, tvOS 10.0, macOS 10.6, watchOS 3.0, *)) {
+            NSString *colorspaceName = (__bridge_transfer NSString *)CGColorSpaceCopyName(colorSpace);
+            // Seems sRGB/deviceRGB always supported, P3 not always
+            if ([colorspaceName isEqualToString:(__bridge NSString *)kCGColorSpaceDeviceRGB]
+                || [colorspaceName isEqualToString:(__bridge NSString *)kCGColorSpaceSRGB]) {
+                supported &= YES;
+            } else {
+                supported &= NO;
+            }
+            return supported;
+        } else {
+            // Fallback on earlier versions
+            return supported;
+        }
+    }
+}
+
 + (BOOL)CGImageContainsAlpha:(CGImageRef)cgImage {
     if (!cgImage) {
         return NO;
@@ -307,16 +413,8 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
     // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
     // Check #3330 for more detail about why this bitmap is choosen.
-    CGBitmapInfo bitmapInfo;
-    if (hasAlpha) {
-        // iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
-        // BGRA8888
-        bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
-    } else {
-        // BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
-        // RGB888
-        bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
-    }
+    // From v5.17.0, use runtime detection of bitmap info instead of hardcode.
+    CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
     CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
     if (!context) {
         return NULL;
@@ -336,53 +434,110 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     if (!cgImage) {
         return NULL;
     }
+    if (size.width == 0 || size.height == 0) {
+        return NULL;
+    }
     size_t width = CGImageGetWidth(cgImage);
     size_t height = CGImageGetHeight(cgImage);
     if (width == size.width && height == size.height) {
+        // Already same size
         CGImageRetain(cgImage);
         return cgImage;
     }
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
+    if (bitsPerComponent != 8 && bitsPerComponent != 16 && bitsPerComponent != 32) {
+        // Unsupported
+        return NULL;
+    }
+    size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
+    CGColorRenderingIntent renderingIntent = CGImageGetRenderingIntent(cgImage);
+    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage);
+    CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
+    CGImageByteOrderInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
+    CGBitmapInfo alphaBitmapInfo = (uint32_t)byteOrderInfo;
     
+    // Input need to convert with alpha
+    if (alphaInfo == kCGImageAlphaNone) {
+        // Convert RGB8/16/F -> ARGB8/16/F
+        alphaBitmapInfo |= kCGImageAlphaFirst;
+    } else {
+        alphaBitmapInfo |= alphaInfo;
+    }
+    uint32_t components;
+    if (alphaInfo == kCGImageAlphaOnly) {
+        // Alpha only, simple to 1 channel
+        components = 1;
+    } else {
+        components = 4;
+    }
+    if (SD_OPTIONS_CONTAINS(bitmapInfo, kCGBitmapFloatComponents)) {
+        // Keep float components
+        alphaBitmapInfo |= kCGBitmapFloatComponents;
+    }
     __block vImage_Buffer input_buffer = {}, output_buffer = {};
     @onExit {
         if (input_buffer.data) free(input_buffer.data);
         if (output_buffer.data) free(output_buffer.data);
     };
-    BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
-    // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
-    // Check #3330 for more detail about why this bitmap is choosen.
-    CGBitmapInfo bitmapInfo;
-    if (hasAlpha) {
-        // iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
-        // BGRA8888
-        bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
-    } else {
-        // BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
-        // RGB888
-        bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
-    }
+    // Always provide alpha channel
     vImage_CGImageFormat format = (vImage_CGImageFormat) {
-        .bitsPerComponent = 8,
-        .bitsPerPixel = 32,
-        .colorSpace = NULL,
-        .bitmapInfo = bitmapInfo,
+        .bitsPerComponent = (uint32_t)bitsPerComponent,
+        .bitsPerPixel = (uint32_t)bitsPerComponent * components,
+        .colorSpace = colorSpace,
+        .bitmapInfo = alphaBitmapInfo,
         .version = 0,
         .decode = NULL,
-        .renderingIntent = CGImageGetRenderingIntent(cgImage)
+        .renderingIntent = renderingIntent
     };
-    
-    vImage_Error a_ret = vImageBuffer_InitWithCGImage(&input_buffer, &format, NULL, cgImage, kvImageNoFlags);
-    if (a_ret != kvImageNoError) return NULL;
-    output_buffer.width = MAX(size.width, 0);
-    output_buffer.height = MAX(size.height, 0);
-    output_buffer.rowBytes = SDByteAlign(output_buffer.width * 4, 64);
-    output_buffer.data = malloc(output_buffer.rowBytes * output_buffer.height);
+    // input
+    vImage_Error ret = vImageBuffer_InitWithCGImage(&input_buffer, &format, NULL, cgImage, kvImageNoFlags);
+    if (ret != kvImageNoError) return NULL;
+    // output
+    vImageBuffer_Init(&output_buffer, size.height, size.width, (uint32_t)bitsPerComponent * components, kvImageNoFlags);
     if (!output_buffer.data) return NULL;
     
-    vImage_Error ret = vImageScale_ARGB8888(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+    if (components == 4) {
+        if (bitsPerComponent == 32) {
+            ret = vImageScale_ARGBFFFF(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        } else if (bitsPerComponent == 16) {
+            ret = vImageScale_ARGB16U(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        } else if (bitsPerComponent == 8) {
+            ret = vImageScale_ARGB8888(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        }
+    } else {
+        if (bitsPerComponent == 32) {
+            ret = vImageScale_PlanarF(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        } else if (bitsPerComponent == 16) {
+            ret = vImageScale_Planar16U(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        } else if (bitsPerComponent == 8) {
+            ret = vImageScale_Planar8(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
+        }
+    }
     if (ret != kvImageNoError) return NULL;
     
-    CGImageRef outputImage = vImageCreateCGImageFromBuffer(&output_buffer, &format, NULL, NULL, kvImageNoFlags, &ret);
+    // Convert back to non-alpha for RGB input to preserve pixel format
+    if (alphaInfo == kCGImageAlphaNone) {
+        // in-place, no extra allocation
+        if (bitsPerComponent == 32) {
+            ret = vImageConvert_ARGBFFFFtoRGBFFF(&output_buffer, &output_buffer, kvImageNoFlags);
+        } else if (bitsPerComponent == 16) {
+            ret = vImageConvert_ARGB16UtoRGB16U(&output_buffer, &output_buffer, kvImageNoFlags);
+        } else if (bitsPerComponent == 8) {
+            ret = vImageConvert_ARGB8888toRGB888(&output_buffer, &output_buffer, kvImageNoFlags);
+        }
+        if (ret != kvImageNoError) return NULL;
+    }
+    vImage_CGImageFormat output_format = (vImage_CGImageFormat) {
+        .bitsPerComponent = (uint32_t)bitsPerComponent,
+        .bitsPerPixel = (uint32_t)bitsPerPixel,
+        .colorSpace = colorSpace,
+        .bitmapInfo = bitmapInfo,
+        .version = 0,
+        .decode = NULL,
+        .renderingIntent = renderingIntent
+    };
+    CGImageRef outputImage = vImageCreateCGImageFromBuffer(&output_buffer, &output_format, NULL, NULL, kvImageNoFlags, &ret);
     if (ret != kvImageNoError) {
         CGImageRelease(outputImage);
         return NULL;
@@ -428,8 +583,25 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     return CGSizeMake(resultWidth, resultHeight);
 }
 
++ (CGSize)scaledSizeWithImageSize:(CGSize)imageSize limitBytes:(NSUInteger)limitBytes bytesPerPixel:(NSUInteger)bytesPerPixel frameCount:(NSUInteger)frameCount {
+    if (CGSizeEqualToSize(imageSize, CGSizeZero)) return CGSizeMake(1, 1);
+    NSUInteger totalFramePixelSize = limitBytes / bytesPerPixel / (frameCount ?: 1);
+    CGFloat ratio = imageSize.height / imageSize.width;
+    CGFloat width = sqrt(totalFramePixelSize / ratio);
+    CGFloat height = width * ratio;
+    width = MAX(1, floor(width));
+    height = MAX(1, floor(height));
+    CGSize size = CGSizeMake(width, height);
+    
+    return size;
+}
+
 + (UIImage *)decodedImageWithImage:(UIImage *)image {
-    if (![self shouldDecodeImage:image]) {
+    return [self decodedImageWithImage:image policy:SDImageForceDecodePolicyAutomatic];
+}
+
++ (UIImage *)decodedImageWithImage:(UIImage *)image policy:(SDImageForceDecodePolicy)policy {
+    if (![self shouldDecodeImage:image policy:policy]) {
         return image;
     }
     
@@ -486,7 +658,11 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
 }
 
 + (UIImage *)decodedAndScaledDownImageWithImage:(UIImage *)image limitBytes:(NSUInteger)bytes {
-    if (![self shouldDecodeImage:image]) {
+    return [self decodedAndScaledDownImageWithImage:image limitBytes:bytes policy:SDImageForceDecodePolicyAutomatic];
+}
+
++ (UIImage *)decodedAndScaledDownImageWithImage:(UIImage *)image limitBytes:(NSUInteger)bytes policy:(SDImageForceDecodePolicy)policy {
+    if (![self shouldDecodeImage:image policy:policy]) {
         return image;
     }
     
@@ -550,16 +726,8 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
         
         // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
         // Check #3330 for more detail about why this bitmap is choosen.
-        CGBitmapInfo bitmapInfo;
-        if (hasAlpha) {
-            // iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
-            // BGRA8888
-            bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
-        } else {
-            // BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
-            // RGB888
-            bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
-        }
+        // From v5.17.0, use runtime detection of bitmap info instead of hardcode.
+        CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
         CGContextRef destContext = CGBitmapContextCreate(NULL,
                                                          destResolution.width,
                                                          destResolution.height,
@@ -618,7 +786,7 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
             sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile );
             if( y == iterations - 1 && remainder ) {
                 float dify = destTile.size.height;
-                destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale;
+                destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale + kDestSeemOverlap;
                 dify -= destTile.size.height;
                 destTile.origin.y = MIN(0, destTile.origin.y + dify);
             }
@@ -733,11 +901,15 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
 #endif
 
 #pragma mark - Helper Function
-+ (BOOL)shouldDecodeImage:(nullable UIImage *)image {
++ (BOOL)shouldDecodeImage:(nullable UIImage *)image policy:(SDImageForceDecodePolicy)policy {
     // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
     if (image == nil) {
         return NO;
     }
+    // Check policy (never)
+    if (policy == SDImageForceDecodePolicyNever) {
+        return NO;
+    }
     // Avoid extra decode
     if (image.sd_isDecoded) {
         return NO;
@@ -750,7 +922,25 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     if (image.sd_isVector) {
         return NO;
     }
-    
+    // Check policy (always)
+    if (policy == SDImageForceDecodePolicyAlways) {
+        return YES;
+    } else {
+        // Check policy (automatic)
+        CGImageRef cgImage = image.CGImage;
+        if (cgImage) {
+            CFStringRef uttype = CGImageGetUTType(cgImage);
+            if (uttype) {
+                // Only ImageIO can set `com.apple.ImageIO.imageSourceTypeIdentifier`
+                return YES;
+            } else {
+                // Now, let's check if the CGImage is hardware supported (not byte-aligned will cause extra copy)
+                BOOL isSupported = [SDImageCoderHelper CGImageIsHardwareSupported:cgImage];
+                return !isSupported;
+            }
+        }
+    }
+
     return YES;
 }
 

+ 4 - 5
Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.m

@@ -32,14 +32,13 @@ static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGF
     CGColorSpaceRef space = [SDImageCoderHelper colorSpaceGetDeviceRGB];
     // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
     // Check #3330 for more detail about why this bitmap is choosen.
+    // From v5.17.0, use runtime detection of bitmap info instead of hardcode.
+    // However, macOS's runtime detection will also call this function, cause recursive, so still hardcode here
     CGBitmapInfo bitmapInfo;
     if (!opaque) {
-        // iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
-        // BGRA8888
-        bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
+        // [NSImage imageWithSize:flipped:drawingHandler:] returns float(16-bits) RGBA8888 on alpha image, which we don't need
+        bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
     } else {
-        // BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
-        // RGB888
         bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
     }
     CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, bitmapInfo);

+ 64 - 6
Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.m

@@ -27,6 +27,8 @@ static CGImageSourceRef (*SDCGImageGetImageSource)(CGImageRef);
 
 // Specify File Size for lossy format encoding, like JPEG
 static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestinationRequestedFileSize";
+// Avoid ImageIO translate JFIF orientation to EXIF orientation which cause bug because returned CGImage already apply the orientation transform
+static NSString * kSDCGImageSourceSkipMetadata = @"kCGImageSourceSkipMetadata";
 
 // This strip the un-wanted CGImageProperty, like the internal CGImageSourceRef in iOS 15+
 // However, CGImageCreateCopy still keep those CGImageProperty, not suit for our use case
@@ -70,6 +72,7 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
     BOOL _finished;
     BOOL _preserveAspectRatio;
     CGSize _thumbnailSize;
+    NSUInteger _limitBytes;
     BOOL _lazyDecode;
 }
 
@@ -231,7 +234,7 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
         }
     }
     // Parse the image properties
-    NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, index, NULL);
+    NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, index, (__bridge CFDictionaryRef)@{kSDCGImageSourceSkipMetadata : @(YES)});
     CGFloat pixelWidth = [properties[(__bridge NSString *)kCGImagePropertyPixelWidth] doubleValue];
     CGFloat pixelHeight = [properties[(__bridge NSString *)kCGImagePropertyPixelHeight] doubleValue];
     CGImagePropertyOrientation exifOrientation = (CGImagePropertyOrientation)[properties[(__bridge NSString *)kCGImagePropertyOrientation] unsignedIntegerValue];
@@ -370,10 +373,16 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
         lazyDecode = lazyDecodeValue.boolValue;
     }
     
+    NSUInteger limitBytes = 0;
+    NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+    if (limitBytesValue != nil) {
+        limitBytes = limitBytesValue.unsignedIntegerValue;
+    }
+    
 #if SD_MAC
     // If don't use thumbnail, prefers the built-in generation of frames (GIF/APNG)
     // Which decode frames in time and reduce memory usage
-    if (thumbnailSize.width == 0 || thumbnailSize.height == 0) {
+    if (limitBytes == 0 && (thumbnailSize.width == 0 || thumbnailSize.height == 0)) {
         SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
         if (imageRep) {
             NSSize size = NSMakeSize(imageRep.pixelsWide / scale, imageRep.pixelsHigh / scale);
@@ -415,16 +424,30 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
         return nil;
     }
     
-    size_t count = CGImageSourceGetCount(source);
+    size_t frameCount = CGImageSourceGetCount(source);
     UIImage *animatedImage;
     
+    // Parse the image properties
+    NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
+    size_t width = [properties[(__bridge NSString *)kCGImagePropertyPixelWidth] doubleValue];
+    size_t height = [properties[(__bridge NSString *)kCGImagePropertyPixelHeight] doubleValue];
+    // Scale down to limit bytes if need
+    if (limitBytes > 0) {
+        // Hack since ImageIO public API (not CGImageDecompressor/CMPhoto) always return back RGBA8888 CGImage
+        CGSize imageSize = CGSizeMake(width, height);
+        CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:limitBytes bytesPerPixel:4 frameCount:frameCount];
+        // Override thumbnail size
+        thumbnailSize = framePixelSize;
+        preserveAspectRatio = YES;
+    }
+    
     BOOL decodeFirstFrame = [options[SDImageCoderDecodeFirstFrameOnly] boolValue];
-    if (decodeFirstFrame || count <= 1) {
+    if (decodeFirstFrame || frameCount <= 1) {
         animatedImage = [self.class createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode animatedImage:NO];
     } else {
-        NSMutableArray<SDImageFrame *> *frames = [NSMutableArray arrayWithCapacity:count];
+        NSMutableArray<SDImageFrame *> *frames = [NSMutableArray arrayWithCapacity:frameCount];
         
-        for (size_t i = 0; i < count; i++) {
+        for (size_t i = 0; i < frameCount; i++) {
             UIImage *image = [self.class createFrameAtIndex:i source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode animatedImage:NO];
             if (!image) {
                 continue;
@@ -481,6 +504,12 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
             preserveAspectRatio = preserveAspectRatioValue.boolValue;
         }
         _preserveAspectRatio = preserveAspectRatio;
+        NSUInteger limitBytes = 0;
+        NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+        if (limitBytesValue != nil) {
+            limitBytes = limitBytesValue.unsignedIntegerValue;
+        }
+        _limitBytes = limitBytes;
         BOOL lazyDecode = NO; // Defaults NO for animated image coder
         NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
         if (lazyDecodeValue != nil) {
@@ -524,6 +553,16 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
     // For animated image progressive decoding because the frame count and duration may be changed.
     [self scanAndCheckFramesValidWithImageSource:_imageSource];
     SD_UNLOCK(_lock);
+    
+    // Scale down to limit bytes if need
+    if (_limitBytes > 0) {
+        // Hack since ImageIO public API (not CGImageDecompressor/CMPhoto) always return back RGBA8888 CGImage
+        CGSize imageSize = CGSizeMake(_width, _height);
+        CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:_limitBytes bytesPerPixel:4 frameCount:_frameCount];
+        // Override thumbnail size
+        _thumbnailSize = framePixelSize;
+        _preserveAspectRatio = YES;
+    }
 }
 
 - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
@@ -710,6 +749,25 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
             preserveAspectRatio = preserveAspectRatioValue.boolValue;
         }
         _preserveAspectRatio = preserveAspectRatio;
+        NSUInteger limitBytes = 0;
+        NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+        if (limitBytesValue != nil) {
+            limitBytes = limitBytesValue.unsignedIntegerValue;
+        }
+        _limitBytes = limitBytes;
+        // Parse the image properties
+        NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
+        _width = [properties[(__bridge NSString *)kCGImagePropertyPixelWidth] doubleValue];
+        _height = [properties[(__bridge NSString *)kCGImagePropertyPixelHeight] doubleValue];
+        // Scale down to limit bytes if need
+        if (_limitBytes > 0) {
+            // Hack since ImageIO public API (not CGImageDecompressor/CMPhoto) always return back RGBA8888 CGImage
+            CGSize imageSize = CGSizeMake(_width, _height);
+            CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:_limitBytes bytesPerPixel:4 frameCount:_frameCount];
+            // Override thumbnail size
+            _thumbnailSize = framePixelSize;
+            _preserveAspectRatio = YES;
+        }
         BOOL lazyDecode = NO; // Defaults NO for animated image coder
         NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
         if (lazyDecodeValue != nil) {

+ 22 - 14
Pods/SDWebImage/SDWebImage/Core/SDImageLoader.m

@@ -74,15 +74,19 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
         image = [imageCoder decodedImageWithData:imageData options:coderOptions];
     }
     if (image) {
-        BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage);
-        BOOL lazyDecode = [coderOptions[SDImageCoderDecodeUseLazyDecoding] boolValue];
-        if (lazyDecode) {
-            // lazyDecode = NO means we should not forceDecode, highest priority
-            shouldDecode = NO;
+        SDImageForceDecodePolicy policy = SDImageForceDecodePolicyAutomatic;
+        NSNumber *polivyValue = context[SDWebImageContextImageForceDecodePolicy];
+        if (polivyValue != nil) {
+            policy = polivyValue.unsignedIntegerValue;
         }
-        if (shouldDecode) {
-            image = [SDImageCoderHelper decodedImageWithImage:image];
+        // TODO: Deprecated, remove in SD 6.0...
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        if (SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage)) {
+            policy = SDImageForceDecodePolicyNever;
         }
+#pragma clang diagnostic pop
+        image = [SDImageCoderHelper decodedImageWithImage:image policy:policy];
         // assign the decode options, to let manager check whether to re-decode if needed
         image.sd_decodeOptions = coderOptions;
     }
@@ -151,15 +155,19 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
         image = [progressiveCoder incrementalDecodedImageWithOptions:coderOptions];
     }
     if (image) {
-        BOOL shouldDecode = !SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage);
-        BOOL lazyDecode = [coderOptions[SDImageCoderDecodeUseLazyDecoding] boolValue];
-        if (lazyDecode) {
-            // lazyDecode = NO means we should not forceDecode, highest priority
-            shouldDecode = NO;
+        SDImageForceDecodePolicy policy = SDImageForceDecodePolicyAutomatic;
+        NSNumber *polivyValue = context[SDWebImageContextImageForceDecodePolicy];
+        if (polivyValue != nil) {
+            policy = polivyValue.unsignedIntegerValue;
         }
-        if (shouldDecode) {
-            image = [SDImageCoderHelper decodedImageWithImage:image];
+        // TODO: Deprecated, remove in SD 6.0...
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        if (SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage)) {
+            policy = SDImageForceDecodePolicyNever;
         }
+#pragma clang diagnostic pop
+        image = [SDImageCoderHelper decodedImageWithImage:image policy:policy];
         // assign the decode options, to let manager check whether to re-decode if needed
         image.sd_decodeOptions = coderOptions;
         // mark the image as progressive (completed one are not mark as progressive)

+ 6 - 0
Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.h

@@ -47,6 +47,12 @@
     #define SD_WATCH 0
 #endif
 
+// Supports Xcode 14 to suppress warning
+#ifdef TARGET_OS_VISION
+#if TARGET_OS_VISION
+    #define SD_VISION 1
+#endif
+#endif
 
 #if SD_MAC
     #import <AppKit/AppKit.h>

+ 40 - 5
Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.h

@@ -107,6 +107,7 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * of the placeholder image until after the image has finished loading.
      * @note This is used to treate placeholder as an **Error Placeholder** but not **Loading Placeholder** by defaults. if the image loading is cancelled or error, the placeholder will be always set.
      * @note Therefore, if you want both **Error Placeholder** and **Loading Placeholder** exist, use `SDWebImageAvoidAutoSetImage` to manually set the two placeholders and final loaded image by your hand depends on loading result.
+     * @note This options is UI level options, has no usage on ImageManager or other components.
      */
     SDWebImageDelayPlaceholder = 1 << 8,
     
@@ -120,6 +121,7 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * By default, image is added to the imageView after download. But in some cases, we want to
      * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
      * Use this flag if you want to manually set the image in the completion when success
+     * @note This options is UI level options, has no usage on ImageManager or other components.
      */
     SDWebImageAvoidAutoSetImage = 1 << 10,
     
@@ -127,9 +129,10 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * By default, images are decoded respecting their original size.
      * This flag will scale down the images to a size compatible with the constrained memory of devices.
      * To control the limit memory bytes, check `SDImageCoderHelper.defaultScaleDownLimitBytes` (Defaults to 60MB on iOS)
-     * This will actually translate to use context option `.imageThumbnailPixelSize` from v5.5.0 (Defaults to (3966, 3966) on iOS). Previously does not.
-     * This flags effect the progressive and animated images as well from v5.5.0. Previously does not.
-     * @note If you need detail controls, it's better to use context option `imageThumbnailPixelSize` and `imagePreserveAspectRatio` instead.
+     * (from 5.16.0) This will actually translate to use context option `SDWebImageContextImageScaleDownLimitBytes`, which check and calculate the thumbnail pixel size occupied small than limit bytes (including animated image)
+     * (from 5.5.0) This flags effect the progressive and animated images as well
+     * @note If you need detail controls, it's better to use context option `imageScaleDownBytes` instead.
+     * @warning This does not effect the cache key. So which means, this will effect the global cache even next time you query without this option. Pay attention when you use this on global options (It's always recommended to use request-level option for different pipeline)
      */
     SDWebImageScaleDownLargeImages = 1 << 11,
     
@@ -164,6 +167,7 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
     /**
      * By default, when you use `SDWebImageTransition` to do some view transition after the image load finished, this transition is only applied for image when the callback from manager is asynchronous (from network, or disk cache query)
      * This mask can force to apply view transition for any cases, like memory cache query, or sync disk cache query.
+     * @note This options is UI level options, has no usage on ImageManager or other components.
      */
     SDWebImageForceTransition = 1 << 17,
     
@@ -171,8 +175,9 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * By default, we will decode the image in the background during cache query and download from the network. This can help to improve performance because when rendering image on the screen, it need to be firstly decoded. But this happen on the main queue by Core Animation.
      * However, this process may increase the memory usage as well. If you are experiencing an issue due to excessive memory consumption, This flag can prevent decode the image.
      * @note 5.14.0 introduce `SDImageCoderDecodeUseLazyDecoding`, use that for better control from codec, instead of post-processing. Which acts the similar like this option but works for SDAnimatedImage as well (this one does not)
+     * @deprecated Deprecated in v5.17.0, if you don't want force-decode, pass [.imageForceDecodePolicy] = [SDImageForceDecodePolicy.never] in context option
      */
-    SDWebImageAvoidDecodeImage = 1 << 18,
+    SDWebImageAvoidDecodeImage API_DEPRECATED("Use SDWebImageContextImageForceDecodePolicy instead", macos(10.10, 10.10), ios(8.0, 8.0), tvos(9.0, 9.0), watchos(2.0, 2.0)) = 1 << 18,
     
     /**
      * By default, we decode the animated image. This flag can force decode the first frame only and produce the static image.
@@ -204,7 +209,15 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * We usually don't apply transform on vector images, because vector images supports dynamically changing to any size, rasterize to a fixed size will loss details. To modify vector images, you can process the vector data at runtime (such as modifying PDF tag / SVG element).
      * Use this flag to transform them anyway.
      */
-    SDWebImageTransformVectorImage = 1 << 23
+    SDWebImageTransformVectorImage = 1 << 23,
+    
+    /**
+     * By defaults, when you use UI-level category like `sd_setImageWithURL:` on UIImageView, it will cancel the loading image requests.
+     * However, some users may choose to not cancel the loading image requests and always start new pipeline.
+     * Use this flag to disable automatic cancel behavior.
+     * @note This options is UI level options, has no usage on ImageManager or other components.
+     */
+    SDWebImageAvoidAutoCancelImage = 1 << 24,
 };
 
 
@@ -255,6 +268,15 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageC
  */
 FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageTransformer;
 
+#pragma mark - Force Decode Options
+
+/**
+ A  NSNumber instance which store the`SDImageForceDecodePolicy` enum. This is used to control how current image loading should force-decode the decoded image (CGImage, typically). See more what's force-decode means in `SDImageForceDecodePolicy` comment.
+ Defaults to `SDImageForceDecodePolicyAutomatic`, which will detect the input CGImage's metadata, and only force-decode if the input CGImage can not directly render on screen (need extra CoreAnimation Copied Image and increase RAM usage).
+ @note If you want to always the force-decode for this image request, pass `SDImageForceDecodePolicyAlways`, for example, some WebP images which does not created by ImageIO.
+ */
+FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageForceDecodePolicy;
+
 #pragma mark - Image Decoder Context Options
 
 /**
@@ -293,6 +315,19 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageT
  */
 FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageTypeIdentifierHint;
 
+/**
+ A NSUInteger value to provide the limit bytes during decoding. This can help to avoid OOM on large frame count animated image or large pixel static image when you don't know how much RAM it occupied before decoding
+ The decoder will do these logic based on limit bytes:
+ 1. Get the total frame count (static image means 1)
+ 2. Calculate the `framePixelSize` width/height to `sqrt(limitBytes / frameCount / bytesPerPixel)`, keeping aspect ratio (at least 1x1)
+ 3. If the `framePixelSize < originalImagePixelSize`, then do thumbnail decoding (see `SDImageCoderDecodeThumbnailPixelSize`) use the `framePixelSize` and `preseveAspectRatio = YES`
+ 4. Else, use the full pixel decoding (small than limit bytes)
+ 5. Whatever result, this does not effect the animated/static behavior of image. So even if you set `limitBytes = 1 && frameCount = 100`, we will stll create animated image with each frame `1x1` pixel size.
+ @note This option has higher priority than `.imageThumbnailPixelSize`
+ @warning This does not effect the cache key. So which means, this will effect the global cache even next time you query without this option. Pay attention when you use this on global options (It's always recommended to use request-level option for different pipeline)
+ */
+FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageScaleDownLimitBytes;
+
 #pragma mark - Cache Context Options
 
 /**

+ 22 - 14
Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.m

@@ -9,6 +9,7 @@
 #import "SDWebImageDefine.h"
 #import "UIImage+Metadata.h"
 #import "NSImage+Compatibility.h"
+#import "SDAnimatedImage.h"
 #import "SDAssociatedObject.h"
 
 #pragma mark - Image scale
@@ -22,20 +23,7 @@ inline CGFloat SDImageScaleFactorForKey(NSString * _Nullable key) {
     if (!key) {
         return scale;
     }
-    // Check if target OS support scale
-#if SD_WATCH
-    if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)])
-#elif SD_UIKIT
-    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
-#elif SD_MAC
-    NSScreen *mainScreen = nil;
-    if (@available(macOS 10.12, *)) {
-        mainScreen = [NSScreen mainScreen];
-    } else {
-        mainScreen = [NSScreen screens].firstObject;
-    }
-    if ([mainScreen respondsToSelector:@selector(backingScaleFactor)])
-#endif
+    // Now all OS supports retina display scale system
     {
         // a@2x.png -> 8
         if (key.length >= 8) {
@@ -81,6 +69,24 @@ inline UIImage * _Nullable SDScaledImageForScaleFactor(CGFloat scale, UIImage *
         return image;
     }
     UIImage *scaledImage;
+    // Check SDAnimatedImage support for shortcut
+    if ([image.class conformsToProtocol:@protocol(SDAnimatedImage)]) {
+        if ([image respondsToSelector:@selector(animatedCoder)]) {
+            id<SDAnimatedImageCoder> coder = [(id<SDAnimatedImage>)image animatedCoder];
+            if (coder) {
+                scaledImage = [[image.class alloc] initWithAnimatedCoder:coder scale:scale];
+            }
+        } else {
+            // Some class impl does not support `animatedCoder`, keep for compatibility
+            NSData *data = [(id<SDAnimatedImage>)image animatedImageData];
+            if (data) {
+                scaledImage = [[image.class alloc] initWithData:data scale:scale];
+            }
+        }
+        if (scaledImage) {
+            return scaledImage;
+        }
+    }
     if (image.sd_isAnimated) {
         UIImage *animatedImage;
 #if SD_UIKIT || SD_WATCH
@@ -132,11 +138,13 @@ SDWebImageContextOption const SDWebImageContextImageCache = @"imageCache";
 SDWebImageContextOption const SDWebImageContextImageLoader = @"imageLoader";
 SDWebImageContextOption const SDWebImageContextImageCoder = @"imageCoder";
 SDWebImageContextOption const SDWebImageContextImageTransformer = @"imageTransformer";
+SDWebImageContextOption const SDWebImageContextImageForceDecodePolicy = @"imageForceDecodePolicy";
 SDWebImageContextOption const SDWebImageContextImageDecodeOptions = @"imageDecodeOptions";
 SDWebImageContextOption const SDWebImageContextImageScaleFactor = @"imageScaleFactor";
 SDWebImageContextOption const SDWebImageContextImagePreserveAspectRatio = @"imagePreserveAspectRatio";
 SDWebImageContextOption const SDWebImageContextImageThumbnailPixelSize = @"imageThumbnailPixelSize";
 SDWebImageContextOption const SDWebImageContextImageTypeIdentifierHint = @"imageTypeIdentifierHint";
+SDWebImageContextOption const SDWebImageContextImageScaleDownLimitBytes = @"imageScaleDownLimitBytes";
 SDWebImageContextOption const SDWebImageContextImageEncodeOptions = @"imageEncodeOptions";
 SDWebImageContextOption const SDWebImageContextQueryCacheType = @"queryCacheType";
 SDWebImageContextOption const SDWebImageContextStoreCacheType = @"storeCacheType";

+ 7 - 1
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.h

@@ -74,8 +74,10 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
     /**
      * By default, we will decode the image in the background during cache query and download from the network. This can help to improve performance because when rendering image on the screen, it need to be firstly decoded. But this happen on the main queue by Core Animation.
      * However, this process may increase the memory usage as well. If you are experiencing a issue due to excessive memory consumption, This flag can prevent decode the image.
+     * @note 5.14.0 introduce `SDImageCoderDecodeUseLazyDecoding`, use that for better control from codec, instead of post-processing. Which acts the similar like this option but works for SDAnimatedImage as well (this one does not)
+     * @deprecated Deprecated in v5.17.0, if you don't want force-decode, pass [.imageForceDecodePolicy] = [SDImageForceDecodePolicy.never] in context option
      */
-    SDWebImageDownloaderAvoidDecodeImage = 1 << 9,
+    SDWebImageDownloaderAvoidDecodeImage API_DEPRECATED("Use SDWebImageContextImageForceDecodePolicy instead", macos(10.10, 10.10), ios(8.0, 8.0), tvos(9.0, 9.0), watchos(2.0, 2.0)) = 1 << 9,
     
     /**
      * By default, we decode the animated image. This flag can force decode the first frame only and produce the static image.
@@ -95,9 +97,13 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
     SDWebImageDownloaderMatchAnimatedImageClass = 1 << 12,
 };
 
+/// Posed when URLSessionTask started (`resume` called))
 FOUNDATION_EXPORT NSNotificationName _Nonnull const SDWebImageDownloadStartNotification;
+/// Posed when URLSessionTask get HTTP response (`didReceiveResponse:completionHandler:` called)
 FOUNDATION_EXPORT NSNotificationName _Nonnull const SDWebImageDownloadReceiveResponseNotification;
+/// Posed when URLSessionTask stoped (`didCompleteWithError:` with error or `cancel` called)
 FOUNDATION_EXPORT NSNotificationName _Nonnull const SDWebImageDownloadStopNotification;
+/// Posed when URLSessionTask finished with success  (`didCompleteWithError:` without error)
 FOUNDATION_EXPORT NSNotificationName _Nonnull const SDWebImageDownloadFinishNotification;
 
 typedef SDImageLoaderProgressBlock SDWebImageDownloaderProgressBlock;

+ 10 - 25
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.m

@@ -21,22 +21,6 @@ NSNotificationName const SDWebImageDownloadStopNotification = @"SDWebImageDownlo
 NSNotificationName const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";
 
 static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
-static void * SDWebImageDownloaderOperationKey = &SDWebImageDownloaderOperationKey;
-
-BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation> operation) {
-    NSCParameterAssert(operation);
-    NSNumber *value = objc_getAssociatedObject(operation, SDWebImageDownloaderOperationKey);
-    if (value != nil) {
-        return value.boolValue;
-    } else {
-        return NO;
-    }
-}
-
-void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation> operation, BOOL isCompleted) {
-    NSCParameterAssert(operation);
-    objc_setAssociatedObject(operation, SDWebImageDownloaderOperationKey, @(isCompleted), OBJC_ASSOCIATION_RETAIN);
-}
 
 @interface SDWebImageDownloadToken ()
 
@@ -120,11 +104,12 @@ void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation>
         _URLOperations = [NSMutableDictionary new];
         NSMutableDictionary<NSString *, NSString *> *headerDictionary = [NSMutableDictionary dictionary];
         NSString *userAgent = nil;
-#if SD_UIKIT
         // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
+#if SD_VISION
+        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; visionOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], UITraitCollection.currentTraitCollection.displayScale];
+#elif SD_UIKIT
         userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
 #elif SD_WATCH
-        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
         userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
 #elif SD_MAC
         userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
@@ -239,7 +224,7 @@ void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation>
     BOOL shouldNotReuseOperation;
     if (operation) {
         @synchronized (operation) {
-            shouldNotReuseOperation = operation.isFinished || operation.isCancelled || SDWebImageDownloaderOperationGetCompleted(operation);
+            shouldNotReuseOperation = operation.isFinished || operation.isCancelled;
         }
     } else {
         shouldNotReuseOperation = YES;
@@ -288,6 +273,8 @@ void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation>
 }
 
 #pragma mark Helper methods
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 + (SDWebImageOptions)imageOptionsFromDownloaderOptions:(SDWebImageDownloaderOptions)downloadOptions {
     SDWebImageOptions options = 0;
     if (downloadOptions & SDWebImageDownloaderScaleDownLargeImages) options |= SDWebImageScaleDownLargeImages;
@@ -298,6 +285,7 @@ void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation>
     
     return options;
 }
+#pragma clang diagnostic pop
 
 - (nullable NSOperation<SDWebImageDownloaderOperation> *)createDownloaderOperationWithUrl:(nonnull NSURL *)url
                                                                                   options:(SDWebImageDownloaderOptions)options
@@ -515,12 +503,6 @@ didReceiveResponse:(NSURLResponse *)response
     
     // Identify the operation that runs this task and pass it the delegate method
     NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:task];
-    if (dataOperation) {
-        @synchronized (dataOperation) {
-            // Mark the downloader operation `isCompleted = YES`, no longer re-use this operation when new request comes in
-            SDWebImageDownloaderOperationSetCompleted(dataOperation, YES);
-        }
-    }
     if ([dataOperation respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
         [dataOperation URLSession:session task:task didCompleteWithError:error];
     }
@@ -625,6 +607,8 @@ didReceiveResponse:(NSURLResponse *)response
     return YES;
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 - (id<SDWebImageOperation>)requestImageWithURL:(NSURL *)url options:(SDWebImageOptions)options context:(SDWebImageContext *)context progress:(SDImageLoaderProgressBlock)progressBlock completed:(SDImageLoaderCompletedBlock)completedBlock {
     UIImage *cachedImage = context[SDWebImageContextLoaderCachedImage];
     
@@ -651,6 +635,7 @@ didReceiveResponse:(NSURLResponse *)response
     
     return [self downloadImageWithURL:url options:downloaderOptions context:context progress:progressBlock completed:completedBlock];
 }
+#pragma clang diagnostic pop
 
 - (BOOL)shouldBlockFailedURLWithURL:(NSURL *)url error:(NSError *)error {
     return [self shouldBlockFailedURLWithURL:url error:error options:0 context:nil];

+ 97 - 63
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.m

@@ -14,8 +14,6 @@
 #import "SDImageCacheDefine.h"
 #import "SDCallbackQueue.h"
 
-BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation> operation); // Private currently, mark open if needed
-
 // A handler to represent individual request
 @interface SDWebImageDownloaderOperationToken : NSObject
 
@@ -62,6 +60,8 @@ BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation>
 @property (strong, nonatomic, nullable) NSError *responseError;
 @property (assign, nonatomic) double previousProgress; // previous progress percent
 
+@property (assign, nonatomic, getter = isDownloadCompleted) BOOL downloadCompleted;
+
 @property (strong, nonatomic, nullable) id<SDWebImageDownloaderResponseModifier> responseModifier; // modify original URLResponse
 @property (strong, nonatomic, nullable) id<SDWebImageDownloaderDecryptor> decryptor; // decrypt image data
 
@@ -112,6 +112,7 @@ BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation>
         _finished = NO;
         _expectedSize = 0;
         _unownedSession = session;
+        _downloadCompleted = NO;
         _coderQueue = [[NSOperationQueue alloc] init];
         _coderQueue.maxConcurrentOperationCount = 1;
         _coderQueue.name = @"com.hackemist.SDWebImageDownloaderOperation.coderQueue";
@@ -338,6 +339,90 @@ BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation>
     return YES;
 }
 
+// Check for unprocessed tokens.
+// if all tokens have been processed call [self done].
+- (void)checkDoneWithImageData:(NSData *)imageData
+                finishedTokens:(NSArray<SDWebImageDownloaderOperationToken *> *)finishedTokens {
+    @synchronized (self) {
+        NSMutableArray<SDWebImageDownloaderOperationToken *> *tokens = [self.callbackTokens mutableCopy];
+        [finishedTokens enumerateObjectsUsingBlock:^(SDWebImageDownloaderOperationToken * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            [tokens removeObjectIdenticalTo:obj];
+        }];
+        if (tokens.count == 0) {
+            [self done];
+        } else {
+            // If there are new tokens added during the decoding operation, the decoding operation is supplemented with these new tokens.
+            [self startCoderOperationWithImageData:imageData pendingTokens:tokens finishedTokens:finishedTokens];
+        }
+    }
+}
+
+- (void)startCoderOperationWithImageData:(NSData *)imageData
+                           pendingTokens:(NSArray<SDWebImageDownloaderOperationToken *> *)pendingTokens
+                          finishedTokens:(NSArray<SDWebImageDownloaderOperationToken *> *)finishedTokens {
+    @weakify(self);
+    for (SDWebImageDownloaderOperationToken *token in pendingTokens) {
+        [self.coderQueue addOperationWithBlock:^{
+            @strongify(self);
+            if (!self) {
+                return;
+            }
+            UIImage *image;
+            // check if we already decode this variant of image for current callback
+            if (token.decodeOptions) {
+                image = [self.imageMap objectForKey:token.decodeOptions];
+            }
+            if (!image) {
+                // check if we already use progressive decoding, use that to produce faster decoding
+                id<SDProgressiveImageCoder> progressiveCoder = SDImageLoaderGetProgressiveCoder(self);
+                SDWebImageOptions options = [[self class] imageOptionsFromDownloaderOptions:self.options];
+                SDWebImageContext *context;
+                if (token.decodeOptions) {
+                    SDWebImageMutableContext *mutableContext = [NSMutableDictionary dictionaryWithDictionary:self.context];
+                    SDSetDecodeOptionsToContext(mutableContext, &options, token.decodeOptions);
+                    context = [mutableContext copy];
+                } else {
+                    context = self.context;
+                }
+                if (progressiveCoder) {
+                    image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, YES, self, options, context);
+                } else {
+                    image = SDImageLoaderDecodeImageData(imageData, self.request.URL, options, context);
+                }
+                if (image && token.decodeOptions) {
+                    [self.imageMap setObject:image forKey:token.decodeOptions];
+                }
+            }
+            CGSize imageSize = image.size;
+            if (imageSize.width == 0 || imageSize.height == 0) {
+                NSString *description = image == nil ? @"Downloaded image decode failed" : @"Downloaded image has 0 pixels";
+                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : description}];
+                [self callCompletionBlockWithToken:token image:nil imageData:nil error:error finished:YES];
+            } else {
+                [self callCompletionBlockWithToken:token image:image imageData:imageData error:nil finished:YES];
+            }
+        }];
+    }
+    // call [self done] after all completed block was dispatched
+    dispatch_block_t doneBlock = ^{
+        @strongify(self);
+        if (!self) {
+            return;
+        }
+        // Check for new tokens added during the decode operation.
+        [self checkDoneWithImageData:imageData
+                      finishedTokens:[finishedTokens arrayByAddingObjectsFromArray:pendingTokens]];
+    };
+    if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) {
+        // seems faster than `addOperationWithBlock`
+        [self.coderQueue addBarrierBlock:doneBlock];
+    } else {
+        // serial queue, this does the same effect in semantics
+        [self.coderQueue addOperationWithBlock:doneBlock];
+    }
+
+}
+
 #pragma mark NSURLSessionDataDelegate
 
 - (void)URLSession:(NSURLSession *)session
@@ -468,7 +553,7 @@ didReceiveResponse:(NSURLResponse *)response
         NSData *imageData = self.imageData;
         
         // keep maximum one progressive decode process during download
-        if (self.coderQueue.operationCount == 0) {
+        if (imageData && self.coderQueue.operationCount == 0) {
             // NSOperation have autoreleasepool, don't need to create extra one
             @weakify(self);
             [self.coderQueue addOperationWithBlock:^{
@@ -478,7 +563,7 @@ didReceiveResponse:(NSURLResponse *)response
                 }
                 // When cancelled or transfer finished (`didCompleteWithError`), cancel the progress callback, only completed block is called and enough
                 @synchronized (self) {
-                    if (self.isCancelled || SDWebImageDownloaderOperationGetCompleted(self)) {
+                    if (self.isCancelled || self.isDownloadCompleted) {
                         return;
                     }
                 }
@@ -521,6 +606,8 @@ didReceiveResponse:(NSURLResponse *)response
     // If we already cancel the operation or anything mark the operation finished, don't callback twice
     if (self.isFinished) return;
     
+    self.downloadCompleted = YES;
+    
     NSArray<SDWebImageDownloaderOperationToken *> *tokens;
     @synchronized (self) {
         tokens = [self.callbackTokens copy];
@@ -545,7 +632,6 @@ didReceiveResponse:(NSURLResponse *)response
     } else {
         if (tokens.count > 0) {
             NSData *imageData = self.imageData;
-            self.imageData = nil;
             // data decryptor
             if (imageData && self.decryptor) {
                 imageData = [self.decryptor decryptedDataWithData:imageData response:self.response];
@@ -565,64 +651,9 @@ didReceiveResponse:(NSURLResponse *)response
                 } else {
                     // decode the image in coder queue, cancel all previous decoding process
                     [self.coderQueue cancelAllOperations];
-                    @weakify(self);
-                    for (SDWebImageDownloaderOperationToken *token in tokens) {
-                        [self.coderQueue addOperationWithBlock:^{
-                            @strongify(self);
-                            if (!self) {
-                                return;
-                            }
-                            UIImage *image;
-                            // check if we already decode this variant of image for current callback
-                            if (token.decodeOptions) {
-                                image = [self.imageMap objectForKey:token.decodeOptions];
-                            }
-                            if (!image) {
-                                // check if we already use progressive decoding, use that to produce faster decoding
-                                id<SDProgressiveImageCoder> progressiveCoder = SDImageLoaderGetProgressiveCoder(self);
-                                SDWebImageOptions options = [[self class] imageOptionsFromDownloaderOptions:self.options];
-                                SDWebImageContext *context;
-                                if (token.decodeOptions) {
-                                    SDWebImageMutableContext *mutableContext = [NSMutableDictionary dictionaryWithDictionary:self.context];
-                                    SDSetDecodeOptionsToContext(mutableContext, &options, token.decodeOptions);
-                                    context = [mutableContext copy];
-                                } else {
-                                    context = self.context;
-                                }
-                                if (progressiveCoder) {
-                                    image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, YES, self, options, context);
-                                } else {
-                                    image = SDImageLoaderDecodeImageData(imageData, self.request.URL, options, context);
-                                }
-                                if (image && token.decodeOptions) {
-                                    [self.imageMap setObject:image forKey:token.decodeOptions];
-                                }
-                            }
-                            CGSize imageSize = image.size;
-                            if (imageSize.width == 0 || imageSize.height == 0) {
-                                NSString *description = image == nil ? @"Downloaded image decode failed" : @"Downloaded image has 0 pixels";
-                                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : description}];
-                                [self callCompletionBlockWithToken:token image:nil imageData:nil error:error finished:YES];
-                            } else {
-                                [self callCompletionBlockWithToken:token image:image imageData:imageData error:nil finished:YES];
-                            }
-                        }];
-                    }
-                    // call [self done] after all completed block was dispatched
-                    dispatch_block_t doneBlock = ^{
-                        @strongify(self);
-                        if (!self) {
-                            return;
-                        }
-                        [self done];
-                    };
-                    if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) {
-                        // seems faster than `addOperationWithBlock`
-                        [self.coderQueue addBarrierBlock:doneBlock];
-                    } else {
-                        // serial queue, this does the same effect in semantics
-                        [self.coderQueue addOperationWithBlock:doneBlock];
-                    }
+                    [self startCoderOperationWithImageData:imageData
+                                             pendingTokens:tokens
+                                            finishedTokens:@[]];
                 }
             } else {
                 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
@@ -671,6 +702,8 @@ didReceiveResponse:(NSURLResponse *)response
 }
 
 #pragma mark Helper methods
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 + (SDWebImageOptions)imageOptionsFromDownloaderOptions:(SDWebImageDownloaderOptions)downloadOptions {
     SDWebImageOptions options = 0;
     if (downloadOptions & SDWebImageDownloaderScaleDownLargeImages) options |= SDWebImageScaleDownLargeImages;
@@ -681,6 +714,7 @@ didReceiveResponse:(NSURLResponse *)response
     
     return options;
 }
+#pragma clang diagnostic pop
 
 - (BOOL)shouldContinueWhenAppEntersBackground {
     return SD_OPTIONS_CONTAINS(self.options, SDWebImageDownloaderContinueInBackground);

+ 1 - 0
Pods/SDWebImage/SDWebImage/Core/SDWebImageError.h

@@ -9,6 +9,7 @@
 
 #import "SDWebImageCompat.h"
 
+/// An error domain represent SDWebImage loading system with custom codes
 FOUNDATION_EXPORT NSErrorDomain const _Nonnull SDWebImageErrorDomain;
 
 /// The response instance for invalid download response (NSURLResponse *)

+ 2 - 0
Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.h

@@ -66,6 +66,7 @@
  */
 @interface SDWebImageActivityIndicator (Conveniences)
 
+#if !SD_VISION
 /// These indicator use the fixed color without dark mode support
 /// gray-style activity indicator
 @property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *grayIndicator;
@@ -75,6 +76,7 @@
 @property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *whiteIndicator;
 /// large white-style activity indicator
 @property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *whiteLargeIndicator;
+#endif
 /// These indicator use the system style, supports dark mode if available (iOS 13+/macOS 10.14+)
 /// large activity indicator
 @property (nonatomic, class, nonnull, readonly) SDWebImageActivityIndicator *largeIndicator;

+ 19 - 3
Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.m

@@ -41,7 +41,17 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 - (void)commonInit {
-    self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
+#if SD_VISION
+    UIActivityIndicatorViewStyle style = UIActivityIndicatorViewStyleMedium;
+#else
+    UIActivityIndicatorViewStyle style;
+    if (@available(iOS 13.0, tvOS 13.0, *)) {
+        style = UIActivityIndicatorViewStyleMedium;
+    } else {
+        style = UIActivityIndicatorViewStyleWhite;
+    }
+#endif
+    self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style];
     self.indicatorView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
 }
 #pragma clang diagnostic pop
@@ -79,6 +89,7 @@
 
 @implementation SDWebImageActivityIndicator (Conveniences)
 
+#if !SD_VISION
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 + (SDWebImageActivityIndicator *)grayIndicator {
@@ -134,10 +145,13 @@
 #endif
     return indicator;
 }
+#endif
 
 + (SDWebImageActivityIndicator *)largeIndicator {
     SDWebImageActivityIndicator *indicator = [SDWebImageActivityIndicator new];
-#if SD_UIKIT
+#if SD_VISION
+    indicator.indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleLarge;
+#elif SD_UIKIT
     if (@available(iOS 13.0, tvOS 13.0, *)) {
         indicator.indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleLarge;
     } else {
@@ -152,7 +166,9 @@
 
 + (SDWebImageActivityIndicator *)mediumIndicator {
     SDWebImageActivityIndicator *indicator = [SDWebImageActivityIndicator new];
-#if SD_UIKIT
+#if SD_VISION
+    indicator.indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
+#elif SD_UIKIT
     if (@available(iOS 13.0, tvOS 13.0, *)) {
         indicator.indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
     } else {

+ 15 - 0
Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.h

@@ -21,6 +21,7 @@
 
 /**
  * Get the current image URL.
+ * This simply translate to `[self sd_imageURLForState:self.state]` from v5.18.0
  */
 @property (nonatomic, strong, readonly, nullable) NSURL *sd_currentImageURL;
 
@@ -31,6 +32,13 @@
  */
 - (nullable NSURL *)sd_imageURLForState:(UIControlState)state;
 
+/**
+ * Get the image operation key for a control state.
+ *
+ * @param state Which state you want to know the URL for. The values are described in UIControlState.
+ */
+- (nonnull NSString *)sd_imageOperationKeyForState:(UIControlState)state;
+
 /**
  * Set the button `image` with an `url`.
  *
@@ -202,6 +210,13 @@
  */
 @property (nonatomic, strong, readonly, nullable) NSURL *sd_currentBackgroundImageURL;
 
+/**
+ * Get the background image operation key for a control state.
+ *
+ * @param state Which state you want to know the URL for. The values are described in UIControlState.
+ */
+- (nonnull NSString *)sd_backgroundImageOperationKeyForState:(UIControlState)state;
+
 /**
  * Get the background image URL for a control state.
  * 

+ 35 - 71
Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.m

@@ -12,47 +12,14 @@
 
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCacheState.h"
 #import "UIView+WebCache.h"
 #import "SDInternalMacros.h"
 
-static char imageURLStorageKey;
-
-typedef NSMutableDictionary<NSString *, NSURL *> SDStateImageURLDictionary;
-
-static inline NSString * imageURLKeyForState(UIControlState state) {
-    return [NSString stringWithFormat:@"image_%lu", (unsigned long)state];
-}
-
-static inline NSString * backgroundImageURLKeyForState(UIControlState state) {
-    return [NSString stringWithFormat:@"backgroundImage_%lu", (unsigned long)state];
-}
-
-static inline NSString * imageOperationKeyForState(UIControlState state) {
-    return [NSString stringWithFormat:@"UIButtonImageOperation%lu", (unsigned long)state];
-}
-
-static inline NSString * backgroundImageOperationKeyForState(UIControlState state) {
-    return [NSString stringWithFormat:@"UIButtonBackgroundImageOperation%lu", (unsigned long)state];
-}
-
 @implementation UIButton (WebCache)
 
 #pragma mark - Image
 
-- (nullable NSURL *)sd_currentImageURL {
-    NSURL *url = self.sd_imageURLStorage[imageURLKeyForState(self.state)];
-
-    if (!url) {
-        url = self.sd_imageURLStorage[imageURLKeyForState(UIControlStateNormal)];
-    }
-
-    return url;
-}
-
-- (nullable NSURL *)sd_imageURLForState:(UIControlState)state {
-    return self.sd_imageURLStorage[imageURLKeyForState(state)];
-}
-
 - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
     [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
 }
@@ -92,19 +59,13 @@ static inline NSString * backgroundImageOperationKeyForState(UIControlState stat
                    context:(nullable SDWebImageContext *)context
                   progress:(nullable SDImageLoaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock {
-    if (!url) {
-        [self.sd_imageURLStorage removeObjectForKey:imageURLKeyForState(state)];
-    } else {
-        self.sd_imageURLStorage[imageURLKeyForState(state)] = url;
-    }
-    
     SDWebImageMutableContext *mutableContext;
     if (context) {
         mutableContext = [context mutableCopy];
     } else {
         mutableContext = [NSMutableDictionary dictionary];
     }
-    mutableContext[SDWebImageContextSetImageOperationKey] = imageOperationKeyForState(state);
+    mutableContext[SDWebImageContextSetImageOperationKey] = [self sd_imageOperationKeyForState:state];
     @weakify(self);
     [self sd_internalSetImageWithURL:url
                     placeholderImage:placeholder
@@ -124,20 +85,6 @@ static inline NSString * backgroundImageOperationKeyForState(UIControlState stat
 
 #pragma mark - Background Image
 
-- (nullable NSURL *)sd_currentBackgroundImageURL {
-    NSURL *url = self.sd_imageURLStorage[backgroundImageURLKeyForState(self.state)];
-    
-    if (!url) {
-        url = self.sd_imageURLStorage[backgroundImageURLKeyForState(UIControlStateNormal)];
-    }
-    
-    return url;
-}
-
-- (nullable NSURL *)sd_backgroundImageURLForState:(UIControlState)state {
-    return self.sd_imageURLStorage[backgroundImageURLKeyForState(state)];
-}
-
 - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
     [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
 }
@@ -177,19 +124,13 @@ static inline NSString * backgroundImageOperationKeyForState(UIControlState stat
                              context:(nullable SDWebImageContext *)context
                             progress:(nullable SDImageLoaderProgressBlock)progressBlock
                            completed:(nullable SDExternalCompletionBlock)completedBlock {
-    if (!url) {
-        [self.sd_imageURLStorage removeObjectForKey:backgroundImageURLKeyForState(state)];
-    } else {
-        self.sd_imageURLStorage[backgroundImageURLKeyForState(state)] = url;
-    }
-    
     SDWebImageMutableContext *mutableContext;
     if (context) {
         mutableContext = [context mutableCopy];
     } else {
         mutableContext = [NSMutableDictionary dictionary];
     }
-    mutableContext[SDWebImageContextSetImageOperationKey] = backgroundImageOperationKeyForState(state);
+    mutableContext[SDWebImageContextSetImageOperationKey] = [self sd_backgroundImageOperationKeyForState:state];
     @weakify(self);
     [self sd_internalSetImageWithURL:url
                     placeholderImage:placeholder
@@ -210,23 +151,46 @@ static inline NSString * backgroundImageOperationKeyForState(UIControlState stat
 #pragma mark - Cancel
 
 - (void)sd_cancelImageLoadForState:(UIControlState)state {
-    [self sd_cancelImageLoadOperationWithKey:imageOperationKeyForState(state)];
+    [self sd_cancelImageLoadOperationWithKey:[self sd_imageOperationKeyForState:state]];
 }
 
 - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state {
-    [self sd_cancelImageLoadOperationWithKey:backgroundImageOperationKeyForState(state)];
+    [self sd_cancelImageLoadOperationWithKey:[self sd_backgroundImageOperationKeyForState:state]];
 }
 
-#pragma mark - Private
+#pragma mark - State
+
+- (NSString *)sd_imageOperationKeyForState:(UIControlState)state {
+    return [NSString stringWithFormat:@"UIButtonImageOperation%lu", (unsigned long)state];
+}
 
-- (SDStateImageURLDictionary *)sd_imageURLStorage {
-    SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
-    if (!storage) {
-        storage = [NSMutableDictionary dictionary];
-        objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+- (NSString *)sd_backgroundImageOperationKeyForState:(UIControlState)state {
+    return [NSString stringWithFormat:@"UIButtonBackgroundImageOperation%lu", (unsigned long)state];
+}
+
+- (NSURL *)sd_currentImageURL {
+    NSURL *url = [self sd_imageURLForState:self.state];
+    if (!url) {
+        [self sd_imageURLForState:UIControlStateNormal];
+    }
+    return url;
+}
+
+- (NSURL *)sd_imageURLForState:(UIControlState)state {
+    return [self sd_imageLoadStateForKey:[self sd_imageOperationKeyForState:state]].url;
+}
+#pragma mark - Background State
+
+- (NSURL *)sd_currentBackgroundImageURL {
+    NSURL *url = [self sd_backgroundImageURLForState:self.state];
+    if (!url) {
+        url = [self sd_backgroundImageURLForState:UIControlStateNormal];
     }
+    return url;
+}
 
-    return storage;
+- (NSURL *)sd_backgroundImageURLForState:(UIControlState)state {
+    return [self sd_imageLoadStateForKey:[self sd_backgroundImageOperationKeyForState:state]].url;
 }
 
 @end

+ 6 - 0
Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.h

@@ -15,6 +15,12 @@
 
 /**
  A bool value indicating whether the image has already been decoded. This can help to avoid extra force decode.
+ Force decode is used for 2 cases:
+ -- 1. for ImageIO created image (via `CGImageCreateWithImageSource` SPI), it's lazy and we trigger the decode before rendering
+ -- 2. for non-ImageIO created image (via `CGImageCreate` API), we can ensure it's alignment is suitable to render on screen without copy by CoreAnimation
+ @note For coder plugin developer, always use the SDImageCoderHelper's `colorSpaceGetDeviceRGB`/`preferredPixelFormat` to create CGImage.
+ @note For more information why force decode, see: https://github.com/path/FastImageCache#byte-alignment
+ @note From v5.17.0, the default value is always NO. Use `SDImageForceDecodePolicy` to control complicated policy.
  */
 @property (nonatomic, assign) BOOL sd_isDecoded;
 

+ 1 - 18
Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.m

@@ -15,24 +15,7 @@
 
 - (BOOL)sd_isDecoded {
     NSNumber *value = objc_getAssociatedObject(self, @selector(sd_isDecoded));
-    if (value != nil) {
-        return value.boolValue;
-    } else {
-        // Assume only CGImage based can use lazy decoding
-        CGImageRef cgImage = self.CGImage;
-        if (cgImage) {
-            CFStringRef uttype = CGImageGetUTType(self.CGImage);
-            if (uttype) {
-                // Only ImageIO can set `com.apple.ImageIO.imageSourceTypeIdentifier`
-                return NO;
-            } else {
-                // Thumbnail or CGBitmapContext drawn image
-                return YES;
-            }
-        }
-    }
-    // Assume others as non-decoded
-    return NO;
+    return [value boolValue];
 }
 
 - (void)setSd_isDecoded:(BOOL)sd_isDecoded {

+ 1 - 0
Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.h

@@ -46,6 +46,7 @@
  Encode the current image to the data, the image format is unspecified
 
  @note If the receiver is `SDAnimatedImage`, this will return the animated image data if available. No more extra encoding process.
+ @note For macOS, if the receiver contains only `SDAnimatedImageRep`, this will return the animated image data if available. No more extra encoding process.
  @return The encoded data. If can't encode, return nil
  */
 - (nullable NSData *)sd_imageData;

+ 13 - 0
Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.m

@@ -8,6 +8,7 @@
 
 #import "UIImage+MultiFormat.h"
 #import "SDImageCodersManager.h"
+#import "SDAnimatedImageRep.h"
 
 @implementation UIImage (MultiFormat)
 
@@ -28,6 +29,18 @@
 }
 
 - (nullable NSData *)sd_imageData {
+#if SD_MAC
+    NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
+    NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];
+    // Check weak animated data firstly
+    if ([imageRep isKindOfClass:[SDAnimatedImageRep class]]) {
+        SDAnimatedImageRep *animatedImageRep = (SDAnimatedImageRep *)imageRep;
+        NSData *imageData = [animatedImageRep animatedImageData];
+        if (imageData) {
+            return imageData;
+        }
+    }
+#endif
     return [self sd_imageDataAsFormat:SDImageFormatUndefined];
 }
 

+ 4 - 0
Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.h

@@ -8,9 +8,13 @@
 
 #import "SDWebImageCompat.h"
 
+/// The scale mode to apply when image drawing on a container with different sizes.
 typedef NS_ENUM(NSUInteger, SDImageScaleMode) {
+    /// The option to scale the content to fit the size of itself by changing the aspect ratio of the content if necessary.
     SDImageScaleModeFill = 0,
+    /// The option to scale the content to fit the size of the view by maintaining the aspect ratio. Any remaining area of the view’s bounds is transparent.
     SDImageScaleModeAspectFit = 1,
+    /// The option to scale the content to fill the size of the view. Some portion of the content may be clipped to fill the view’s bounds.
     SDImageScaleModeAspectFill = 2
 };
 

+ 30 - 9
Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.m

@@ -57,7 +57,7 @@ static inline CGRect SDCGRectFitWithScaleMode(CGRect rect, CGSize size, SDImageS
     return rect;
 }
 
-static inline UIColor * SDGetColorFromGrayscale(Pixel_88 pixel, CGBitmapInfo bitmapInfo) {
+static inline UIColor * SDGetColorFromGrayscale(Pixel_88 pixel, CGBitmapInfo bitmapInfo, CGColorSpaceRef cgColorSpace) {
     // Get alpha info, byteOrder info
     CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
     CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
@@ -135,11 +135,18 @@ static inline UIColor * SDGetColorFromGrayscale(Pixel_88 pixel, CGBitmapInfo bit
         default:
             break;
     }
-    
+#if SD_MAC
+    // Mac supports ColorSync, to ensure the same bahvior, we convert color to sRGB
+    NSColorSpace *colorSpace = [[NSColorSpace alloc] initWithCGColorSpace:cgColorSpace];
+    CGFloat components[2] = {w, a};
+    NSColor *color = [NSColor colorWithColorSpace:colorSpace components:components count:2];
+    return [color colorUsingColorSpace:NSColorSpace.genericGamma22GrayColorSpace];
+#else
     return [UIColor colorWithWhite:w alpha:a];
+#endif
 }
 
-static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmapInfo) {
+static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmapInfo, CGColorSpaceRef cgColorSpace) {
     // Get alpha info, byteOrder info
     CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
     CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
@@ -150,8 +157,10 @@ static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmap
         case kCGBitmapByteOrderDefault: {
             byteOrderNormal = YES;
         } break;
+        case kCGBitmapByteOrder16Little:
         case kCGBitmapByteOrder32Little: {
         } break;
+        case kCGBitmapByteOrder16Big:
         case kCGBitmapByteOrder32Big: {
             byteOrderNormal = YES;
         } break;
@@ -242,8 +251,15 @@ static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmap
         default:
             break;
     }
-    
+#if SD_MAC
+    // Mac supports ColorSync, to ensure the same bahvior, we convert color to sRGB
+    NSColorSpace *colorSpace = [[NSColorSpace alloc] initWithCGColorSpace:cgColorSpace];
+    CGFloat components[4] = {r, g, b, a};
+    NSColor *color = [NSColor colorWithColorSpace:colorSpace components:components count:4];
+    return [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
+#else
     return [UIColor colorWithRed:r green:g blue:b alpha:a];
+#endif
 }
 
 #if SD_UIKIT || SD_MAC
@@ -558,6 +574,9 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull
         CGImageRelease(imageRef);
         return nil;
     }
+    // Get color space for transform
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
+    
     // greyscale
     if (components == 2) {
         Pixel_88 pixel = {0};
@@ -565,7 +584,7 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull
         CFRelease(data);
         CGImageRelease(imageRef);
         // Convert to color
-        return SDGetColorFromGrayscale(pixel, bitmapInfo);
+        return SDGetColorFromGrayscale(pixel, bitmapInfo, colorSpace);
     } else if (components == 3 || components == 4) {
         // RGB/RGBA
         Pixel_8888 pixel = {0};
@@ -573,7 +592,7 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull
         CFRelease(data);
         CGImageRelease(imageRef);
         // Convert to color
-        return SDGetColorFromRGBA(pixel, bitmapInfo);
+        return SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
     } else {
         NSLog(@"Unsupported components: %zu", components);
         CFRelease(data);
@@ -637,6 +656,8 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull
     // Convert to color
     CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
     NSMutableArray<UIColor *> *colors = [NSMutableArray arrayWithCapacity:CGRectGetWidth(rect) * CGRectGetHeight(rect)];
+    // ColorSpace
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
     for (size_t index = start; index < end; index += components) {
         if (index >= row * bytesPerRow + col * components) {
             // Index beyond the end of current row, go next row
@@ -648,14 +669,14 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull
         UIColor *color;
         if (components == 2) {
             Pixel_88 pixel = {pixels[index], pixel[index+1]};
-            color = SDGetColorFromGrayscale(pixel, bitmapInfo);
+            color = SDGetColorFromGrayscale(pixel, bitmapInfo, colorSpace);
         } else {
             if (components == 3) {
                 Pixel_8888 pixel = {pixels[index], pixels[index+1], pixels[index+2], 0};
-                color = SDGetColorFromRGBA(pixel, bitmapInfo);
+                color = SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
             } else if (components == 4) {
                 Pixel_8888 pixel = {pixels[index], pixels[index+1], pixels[index+2], pixels[index+3]};
-                color = SDGetColorFromRGBA(pixel, bitmapInfo);
+                color = SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
             } else {
                 NSLog(@"Unsupported components: %zu", components);
             }

+ 13 - 0
Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.h

@@ -17,6 +17,13 @@
  */
 @interface UIImageView (HighlightedWebCache)
 
+#pragma mark - Highlighted Image
+
+/**
+ * Get the current highlighted image URL.
+ */
+@property (nonatomic, strong, readonly, nullable) NSURL *sd_currentHighlightedImageURL;
+
 /**
  * Set the imageView `highlightedImage` with an `url`.
  *
@@ -124,6 +131,12 @@
                              progress:(nullable SDImageLoaderProgressBlock)progressBlock
                             completed:(nullable SDExternalCompletionBlock)completedBlock;
 
+/**
+ * Cancel the current highlighted image load (for `UIImageView.highlighted`)
+ * @note For normal image, use `sd_cancelCurrentImageLoad`
+ */
+- (void)sd_cancelCurrentHighlightedImageLoad;
+
 @end
 
 #endif

+ 12 - 3
Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.m

@@ -11,11 +11,10 @@
 #if SD_UIKIT
 
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCacheState.h"
 #import "UIView+WebCache.h"
 #import "SDInternalMacros.h"
 
-static NSString * const SDHighlightedImageOperationKey = @"UIImageViewImageOperationHighlighted";
-
 @implementation UIImageView (HighlightedWebCache)
 
 - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url {
@@ -54,7 +53,7 @@ static NSString * const SDHighlightedImageOperationKey = @"UIImageViewImageOpera
     } else {
         mutableContext = [NSMutableDictionary dictionary];
     }
-    mutableContext[SDWebImageContextSetImageOperationKey] = SDHighlightedImageOperationKey;
+    mutableContext[SDWebImageContextSetImageOperationKey] = @keypath(self, highlightedImage);
     [self sd_internalSetImageWithURL:url
                     placeholderImage:nil
                              options:options
@@ -71,6 +70,16 @@ static NSString * const SDHighlightedImageOperationKey = @"UIImageViewImageOpera
                            }];
 }
 
+#pragma mark - Highlighted State
+
+- (NSURL *)sd_currentHighlightedImageURL {
+    return [self sd_imageLoadStateForKey:@keypath(self, highlightedImage)].url;
+}
+
+- (void)sd_cancelCurrentHighlightedImageLoad {
+    return [self sd_cancelImageLoadOperationWithKey:@keypath(self, highlightedImage)];
+}
+
 @end
 
 #endif

+ 15 - 0
Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.h

@@ -45,6 +45,15 @@
  */
 @interface UIImageView (WebCache)
 
+#pragma mark - Image State
+
+/**
+ * Get the current image URL.
+ */
+@property (nonatomic, strong, readonly, nullable) NSURL *sd_currentImageURL;
+
+#pragma mark - Image Loading
+
 /**
  * Set the imageView `image` with an `url`.
  *
@@ -191,4 +200,10 @@
                   progress:(nullable SDImageLoaderProgressBlock)progressBlock
                  completed:(nullable SDExternalCompletionBlock)completedBlock;
 
+/**
+ * Cancel the current normal image load (for `UIImageView.image`)
+ * @note For highlighted image, use `sd_cancelCurrentHighlightedImageLoad`
+ */
+- (void)sd_cancelCurrentImageLoad;
+
 @end

+ 11 - 0
Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.m

@@ -9,6 +9,7 @@
 #import "UIImageView+WebCache.h"
 #import "objc/runtime.h"
 #import "UIView+WebCacheOperation.h"
+#import "UIView+WebCacheState.h"
 #import "UIView+WebCache.h"
 
 @implementation UIImageView (WebCache)
@@ -64,4 +65,14 @@
                            }];
 }
 
+#pragma mark - State
+
+- (NSURL *)sd_currentImageURL {
+    return [self sd_imageLoadStateForKey:nil].url;
+}
+
+- (void)sd_cancelCurrentImageLoad {
+    return [self sd_cancelImageLoadOperationWithKey:nil];
+}
+
 @end

+ 22 - 8
Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.h

@@ -11,6 +11,8 @@
 #import "SDWebImageManager.h"
 #import "SDWebImageTransition.h"
 #import "SDWebImageIndicator.h"
+#import "UIView+WebCacheOperation.h"
+#import "UIView+WebCacheState.h"
 
 /**
  The value specify that the image progress unit count cannot be determined because the progressBlock is not been called.
@@ -24,27 +26,34 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
  */
 @interface UIView (WebCache)
 
-/**
- * Get the current image URL.
- *
- * @note Note that because of the limitations of categories this property can get out of sync if you use setImage: directly.
- */
-@property (nonatomic, strong, readonly, nullable) NSURL *sd_imageURL;
-
 /**
  * Get the current image operation key. Operation key is used to identify the different queries for one view instance (like UIButton).
  * See more about this in `SDWebImageContextSetImageOperationKey`.
- * If you cancel current image load, the key will be set to nil.
+ *
  * @note You can use method `UIView+WebCacheOperation` to investigate different queries' operation.
+ * @note For the history version compatible, when current UIView has property exactly called `image`, the operation key will use `NSStringFromClass(self.class)`. Include `UIImageView.image/NSImageView.image/NSButton.image` (without `UIButton`)
+ * @warning This property should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), check their header to call correct API, like `-[UIButton sd_imageOperationKeyForState:]`
  */
 @property (nonatomic, strong, readonly, nullable) NSString *sd_latestOperationKey;
 
+#pragma mark - State
+
+/**
+ * Get the current image URL.
+ * This simply translate to `[self sd_imageLoadStateForKey:self.sd_latestOperationKey].url` from v5.18.0
+ *
+ * @note Note that because of the limitations of categories this property can get out of sync if you use setImage: directly.
+ * @warning This property should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), use `sd_imageLoadStateForKey:` instead. See `UIView+WebCacheState.h` for more information.
+ */
+@property (nonatomic, strong, readonly, nullable) NSURL *sd_imageURL;
+
 /**
  * The current image loading progress associated to the view. The unit count is the received size and excepted size of download.
  * The `totalUnitCount` and `completedUnitCount` will be reset to 0 after a new image loading start (change from current queue). And they will be set to `SDWebImageProgressUnitCountUnknown` if the progressBlock not been called but the image loading success to mark the progress finished (change from main queue).
  * @note You can use Key-Value Observing on the progress, but you should take care that the change to progress is from a background queue during download(the same as progressBlock). If you want to using KVO and update the UI, make sure to dispatch on the main queue. And it's recommend to use some KVO libs like KVOController because it's more safe and easy to use.
  * @note The getter will create a progress instance if the value is nil. But by default, we don't create one. If you need to use Key-Value Observing, you must trigger the getter or set a custom progress instance before the loading start. The default value is nil.
  * @note Note that because of the limitations of categories this property can get out of sync if you update the progress directly.
+ * @warning This property should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), use `sd_imageLoadStateForKey:` instead. See `UIView+WebCacheState.h` for more information.
  */
 @property (nonatomic, strong, null_resettable) NSProgress *sd_imageProgress;
 
@@ -83,6 +92,9 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
 
 /**
  * Cancel the current image load
+ * This simply translate to `[self sd_cancelImageLoadOperationWithKey:self.sd_latestOperationKey]` from v5.18.0
+ * 
+ * @warning This method should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), use `sd_cancelImageLoadOperationWithKey:` instead. See `UIView+WebCacheOperation.h` for more information.
  */
 - (void)sd_cancelCurrentImageLoad;
 
@@ -93,6 +105,7 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
 /**
  The image transition when image load finished. See `SDWebImageTransition`.
  If you specify nil, do not do transition. Defaults to nil.
+ @warning This property should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), write your own implementation in `setImageBlock:`, and check current stateful view's state to render the UI.
  */
 @property (nonatomic, strong, nullable) SDWebImageTransition *sd_imageTransition;
 
@@ -102,6 +115,7 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
  The image indicator during the image loading. If you do not need indicator, specify nil. Defaults to nil
  The setter will remove the old indicator view and add new indicator view to current view's subview.
  @note Because this is UI related, you should access only from the main queue.
+ @warning This property should be only used for single state view, like `UIImageView` without highlighted state. For stateful view like `UIBUtton` (one view can have multiple images loading), write your own implementation in `setImageBlock:`, and check current stateful view's state to render the UI.
  */
 @property (nonatomic, strong, nullable) id<SDWebImageIndicator> sd_imageIndicator;
 

+ 28 - 14
Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.m

@@ -18,14 +18,6 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
 
 @implementation UIView (WebCache)
 
-- (nullable NSURL *)sd_imageURL {
-    return objc_getAssociatedObject(self, @selector(sd_imageURL));
-}
-
-- (void)setSd_imageURL:(NSURL * _Nullable)sd_imageURL {
-    objc_setAssociatedObject(self, @selector(sd_imageURL), sd_imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
-
 - (nullable NSString *)sd_latestOperationKey {
     return objc_getAssociatedObject(self, @selector(sd_latestOperationKey));
 }
@@ -34,8 +26,15 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
     objc_setAssociatedObject(self, @selector(sd_latestOperationKey), sd_latestOperationKey, OBJC_ASSOCIATION_COPY_NONATOMIC);
 }
 
+#pragma mark - State
+
+- (NSURL *)sd_imageURL {
+    return [self sd_imageLoadStateForKey:self.sd_latestOperationKey].url;
+}
+
 - (NSProgress *)sd_imageProgress {
-    NSProgress *progress = objc_getAssociatedObject(self, @selector(sd_imageProgress));
+    SDWebImageLoadState *loadState = [self sd_imageLoadStateForKey:self.sd_latestOperationKey];
+    NSProgress *progress = loadState.progress;
     if (!progress) {
         progress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
         self.sd_imageProgress = progress;
@@ -44,7 +43,15 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
 }
 
 - (void)setSd_imageProgress:(NSProgress *)sd_imageProgress {
-    objc_setAssociatedObject(self, @selector(sd_imageProgress), sd_imageProgress, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    if (!sd_imageProgress) {
+        return;
+    }
+    SDWebImageLoadState *loadState = [self sd_imageLoadStateForKey:self.sd_latestOperationKey];
+    if (!loadState) {
+        loadState = [SDWebImageLoadState new];
+    }
+    loadState.progress = sd_imageProgress;
+    [self sd_setImageLoadState:loadState forKey:self.sd_latestOperationKey];
 }
 
 - (nullable id<SDWebImageOperation>)sd_internalSetImageWithURL:(nullable NSURL *)url
@@ -69,8 +76,16 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
         context = [mutableContext copy];
     }
     self.sd_latestOperationKey = validOperationKey;
-    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
-    self.sd_imageURL = url;
+    if (!(SD_OPTIONS_CONTAINS(options, SDWebImageAvoidAutoCancelImage))) {
+        // cancel previous loading for the same set-image operation key by default
+        [self sd_cancelImageLoadOperationWithKey:validOperationKey];
+    }
+    SDWebImageLoadState *loadState = [self sd_imageLoadStateForKey:validOperationKey];
+    if (!loadState) {
+        loadState = [SDWebImageLoadState new];
+    }
+    loadState.url = url;
+    [self sd_setImageLoadState:loadState forKey:validOperationKey];
     
     SDWebImageManager *manager = context[SDWebImageContextCustomManager];
     if (!manager) {
@@ -103,7 +118,7 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
     
     if (url) {
         // reset the progress
-        NSProgress *imageProgress = objc_getAssociatedObject(self, @selector(sd_imageProgress));
+        NSProgress *imageProgress = loadState.progress;
         if (imageProgress) {
             imageProgress.totalUnitCount = 0;
             imageProgress.completedUnitCount = 0;
@@ -242,7 +257,6 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
 
 - (void)sd_cancelCurrentImageLoad {
     [self sd_cancelImageLoadOperationWithKey:self.sd_latestOperationKey];
-    self.sd_latestOperationKey = nil;
 }
 
 - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL {

+ 0 - 1
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.m

@@ -40,7 +40,6 @@ typedef NSMapTable<NSString *, id<SDWebImageOperation>> SDOperationsDictionary;
 
 - (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key {
     if (key) {
-        [self sd_cancelImageLoadOperationWithKey:key];
         if (operation) {
             SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
             @synchronized (self) {

+ 62 - 0
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.h

@@ -0,0 +1,62 @@
+/*
+ * This file is part of the SDWebImage package.
+ * (c) Olivier Poitrey <rs@dailymotion.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+#import <Foundation/Foundation.h>
+#import "SDWebImageCompat.h"
+
+/**
+ A loading state to manage View Category which contains multiple states. Like UIImgeView.image && UIImageView.highlightedImage
+ @code
+ SDWebImageLoadState *loadState = [self sd_imageLoadStateForKey:@keypath(self, highlitedImage)];
+ NSProgress *highlitedImageProgress = loadState.progress;
+ @endcode
+ */
+@interface SDWebImageLoadState : NSObject
+
+/**
+ Image loading URL
+ */
+@property (nonatomic, strong, nullable) NSURL *url;
+/**
+ Image loading progress. The unit count is the received size and excepted size of download.
+ */
+@property (nonatomic, strong, nullable) NSProgress *progress;
+
+@end
+
+/**
+ These methods are used for WebCache view which have multiple states for image loading, for example, `UIButton` or `UIImageView.highlightedImage`
+ It maitain the state container for per-operation, make it possible for control and check each image loading operation's state.
+ @note For developer who want to add SDWebImage View Category support for their own stateful class, learn more on Wiki.
+ */
+@interface UIView (WebCacheState)
+
+/**
+ Get the image loading state container for specify operation key
+
+ @param key key for identifying the operations
+ @return The image loading state container
+ */
+- (nullable SDWebImageLoadState *)sd_imageLoadStateForKey:(nullable NSString *)key;
+
+/**
+ Set the image loading state container for specify operation key
+
+ @param state The image loading state container
+ @param key key for identifying the operations
+ */
+- (void)sd_setImageLoadState:(nullable SDWebImageLoadState *)state forKey:(nullable NSString *)key;
+
+/**
+ Rmove the image loading state container for specify operation key
+
+ @param key key for identifying the operations
+ */
+- (void)sd_removeImageLoadStateForKey:(nullable NSString *)key;
+
+@end

+ 56 - 0
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.m

@@ -0,0 +1,56 @@
+/*
+ * This file is part of the SDWebImage package.
+ * (c) Olivier Poitrey <rs@dailymotion.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+#import "UIView+WebCacheState.h"
+#import "objc/runtime.h"
+
+typedef NSMutableDictionary<NSString *, SDWebImageLoadState *> SDStatesDictionary;
+
+@implementation SDWebImageLoadState
+
+@end
+
+@implementation UIView (WebCacheState)
+
+- (SDStatesDictionary *)sd_imageLoadStateDictionary {
+    SDStatesDictionary *states = objc_getAssociatedObject(self, @selector(sd_imageLoadStateDictionary));
+    if (!states) {
+        states = [NSMutableDictionary dictionary];
+        objc_setAssociatedObject(self, @selector(sd_imageLoadStateDictionary), states, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    }
+    return states;
+}
+
+- (SDWebImageLoadState *)sd_imageLoadStateForKey:(NSString *)key {
+    if (!key) {
+        key = NSStringFromClass(self.class);
+    }
+    @synchronized(self) {
+        return [self.sd_imageLoadStateDictionary objectForKey:key];
+    }
+}
+
+- (void)sd_setImageLoadState:(SDWebImageLoadState *)state forKey:(NSString *)key {
+    if (!key) {
+        key = NSStringFromClass(self.class);
+    }
+    @synchronized(self) {
+        self.sd_imageLoadStateDictionary[key] = state;
+    }
+}
+
+- (void)sd_removeImageLoadStateForKey:(NSString *)key {
+    if (!key) {
+        key = NSStringFromClass(self.class);
+    }
+    @synchronized(self) {
+        self.sd_imageLoadStateDictionary[key] = nil;
+    }
+}
+
+@end

+ 1 - 0
Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.m

@@ -8,6 +8,7 @@
 
 #import "SDDeviceHelper.h"
 #import <mach/mach.h>
+#import <sys/sysctl.h>
 
 @implementation SDDeviceHelper
 

+ 7 - 3
Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.m

@@ -45,6 +45,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
 - (void)dealloc {
 #if SD_MAC
     if (_displayLink) {
+        CVDisplayLinkStop(_displayLink);
         CVDisplayLinkRelease(_displayLink);
         _displayLink = NULL;
     }
@@ -62,14 +63,15 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
     if (self) {
         _target = target;
         _selector = sel;
+        // CA/CV/NSTimer will retain to the target, we need to break this using weak proxy
+        SDWeakProxy *weakProxy = [SDWeakProxy proxyWithTarget:self];
 #if SD_MAC
         CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
-        CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void *)self);
+        // Simulate retain for target, the target is weak proxy to self
+        CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge_retained void *)weakProxy);
 #elif SD_IOS || SD_TV
-        SDWeakProxy *weakProxy = [SDWeakProxy proxyWithTarget:self];
         _displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayLinkDidRefresh:)];
 #else
-        SDWeakProxy *weakProxy = [SDWeakProxy proxyWithTarget:self];
         _displayLink = [NSTimer timerWithTimeInterval:kSDDisplayLinkInterval target:weakProxy selector:@selector(displayLinkDidRefresh:) userInfo:nil repeats:YES];
 #endif
     }
@@ -238,7 +240,9 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
 #if SD_MAC
 static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
     // CVDisplayLink callback is not on main queue
+    // Actually `SDWeakProxy` but not `SDDisplayLink`
     SDDisplayLink *object = (__bridge SDDisplayLink *)displayLinkContext;
+    if (!object) return kCVReturnSuccess;
     // CVDisplayLink does not use runloop, but we can provide similar behavior for modes
     // May use `default` runloop to avoid extra callback when in `eventTracking` (mouse drag, scroll) or `modalPanel` (modal panel)
     NSString *runloopMode = object.runloopMode;

+ 3 - 1
Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.m

@@ -13,7 +13,9 @@ static NSArray *SDBundlePreferredScales(void) {
     static NSArray *scales;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
-#if SD_WATCH
+#if SD_VISION
+        CGFloat screenScale = UITraitCollection.currentTraitCollection.displayScale;
+#elif SD_WATCH
         CGFloat screenScale = [WKInterfaceDevice currentDevice].screenScale;
 #elif SD_UIKIT
         CGFloat screenScale = [UIScreen mainScreen].scale;

+ 40 - 0
Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.h

@@ -0,0 +1,40 @@
+/*
+* This file is part of the SDWebImage package.
+* (c) Olivier Poitrey <rs@dailymotion.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+#import <Foundation/Foundation.h>
+#import "SDWebImageCompat.h"
+#import "SDImageCoder.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A per-provider (provider means, AnimatedImage object) based frame pool, each player who use the same provider share the same frame buffer
+@interface SDImageFramePool : NSObject
+
+/// Register and return back a frame pool, also increase reference count
++ (instancetype)registerProvider:(id<SDAnimatedImageProvider>)provider;
+/// Unregister a frame pool, also decrease reference count, if zero dealloc the frame pool
++ (void)unregisterProvider:(id<SDAnimatedImageProvider>)provider;
+
+/// Prefetch the current frame, query using `frameAtIndex:` by caller to check whether finished.
+- (void)prefetchFrameAtIndex:(NSUInteger)index;
+
+/// Control the max buffer count for current frame pool, used for RAM/CPU balance, default unlimited
+@property (nonatomic, assign) NSUInteger maxBufferCount;
+/// Control the max concurrent fetch queue operation count, used for CPU balance, default 1
+@property (nonatomic, assign) NSUInteger maxConcurrentCount;
+
+// Frame Operations
+@property (nonatomic, readonly) NSUInteger currentFrameCount;
+- (nullable UIImage *)frameAtIndex:(NSUInteger)index;
+- (void)setFrame:(nullable UIImage *)frame atIndex:(NSUInteger)index;
+- (void)removeFrameAtIndex:(NSUInteger)index;
+- (void)removeAllFrames;
+
+NS_ASSUME_NONNULL_END
+
+@end

+ 164 - 0
Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.m

@@ -0,0 +1,164 @@
+/*
+* This file is part of the SDWebImage package.
+* (c) Olivier Poitrey <rs@dailymotion.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+#import "SDImageFramePool.h"
+#import "SDInternalMacros.h"
+#import "objc/runtime.h"
+
+@interface SDImageFramePool ()
+
+@property (class, readonly) NSMapTable *providerFramePoolMap;
+
+@property (weak) id<SDAnimatedImageProvider> provider;
+@property (atomic) NSUInteger registerCount;
+
+@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
+@property (nonatomic, strong) NSOperationQueue *fetchQueue;
+
+@end
+
+// Lock to ensure atomic behavior
+SD_LOCK_DECLARE_STATIC(_providerFramePoolMapLock);
+
+@implementation SDImageFramePool
+
++ (NSMapTable *)providerFramePoolMap {
+    static NSMapTable *providerFramePoolMap;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        providerFramePoolMap = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality valueOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality];
+    });
+    return providerFramePoolMap;
+}
+
+#pragma mark - Life Cycle
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        _frameBuffer = [NSMutableDictionary dictionary];
+        _fetchQueue = [[NSOperationQueue alloc] init];
+        _fetchQueue.maxConcurrentOperationCount = 1;
+        _fetchQueue.name = @"com.hackemist.SDImageFramePool.fetchQueue";
+#if SD_UIKIT
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
+#endif
+    }
+    return self;
+}
+
+- (void)dealloc {
+#if SD_UIKIT
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
+#endif
+}
+
+- (void)didReceiveMemoryWarning:(NSNotification *)notification {
+    [self removeAllFrames];
+}
+
++ (void)initialize {
+    // Lock to ensure atomic behavior
+    SD_LOCK_INIT(_providerFramePoolMapLock);
+}
+
++ (instancetype)registerProvider:(id<SDAnimatedImageProvider>)provider {
+    // Lock to ensure atomic behavior
+    SD_LOCK(_providerFramePoolMapLock);
+    SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
+    if (!framePool) {
+        framePool = [[SDImageFramePool alloc] init];
+        framePool.provider = provider;
+        [self.providerFramePoolMap setObject:framePool forKey:provider];
+    }
+    framePool.registerCount += 1;
+    SD_UNLOCK(_providerFramePoolMapLock);
+    return framePool;
+}
+
++ (void)unregisterProvider:(id<SDAnimatedImageProvider>)provider {
+    // Lock to ensure atomic behavior
+    SD_LOCK(_providerFramePoolMapLock);
+    SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
+    if (!framePool) {
+        SD_UNLOCK(_providerFramePoolMapLock);
+        return;
+    }
+    framePool.registerCount -= 1;
+    if (framePool.registerCount == 0) {
+        [self.providerFramePoolMap removeObjectForKey:provider];
+    }
+    SD_UNLOCK(_providerFramePoolMapLock);
+}
+
+- (void)prefetchFrameAtIndex:(NSUInteger)index {
+    @synchronized (self) {
+        NSUInteger frameCount = self.frameBuffer.count;
+        if (frameCount > self.maxBufferCount) {
+            // Remove the frame buffer if need
+            // TODO, use LRU or better algorithm to detect which frames to clear
+            self.frameBuffer[@(index - 1)] = nil;
+            self.frameBuffer[@(index + 1)] = nil;
+        }
+    }
+    
+    if (self.fetchQueue.operationCount == 0) {
+        // Prefetch next frame in background queue
+        id<SDAnimatedImageProvider> animatedProvider = self.provider;
+        @weakify(self);
+        NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+            @strongify(self);
+            if (!self) {
+                return;
+            }
+            UIImage *frame = [animatedProvider animatedImageFrameAtIndex:index];
+            
+            [self setFrame:frame atIndex:index];
+        }];
+        [self.fetchQueue addOperation:operation];
+    }
+}
+
+- (void)setMaxConcurrentCount:(NSUInteger)maxConcurrentCount {
+    self.fetchQueue.maxConcurrentOperationCount = maxConcurrentCount;
+}
+
+- (NSUInteger)currentFrameCount {
+    NSUInteger frameCount = 0;
+    @synchronized (self) {
+        frameCount = self.frameBuffer.count;
+    }
+    return frameCount;
+}
+
+- (void)setFrame:(UIImage *)frame atIndex:(NSUInteger)index {
+    @synchronized (self) {
+        self.frameBuffer[@(index)] = frame;
+    }
+}
+
+- (UIImage *)frameAtIndex:(NSUInteger)index {
+    UIImage *frame;
+    @synchronized (self) {
+        frame = self.frameBuffer[@(index)];
+    }
+    return frame;
+}
+
+- (void)removeFrameAtIndex:(NSUInteger)index {
+    @synchronized (self) {
+        self.frameBuffer[@(index)] = nil;
+    }
+}
+
+- (void)removeAllFrames {
+    @synchronized (self) {
+        [self.frameBuffer removeAllObjects];
+    }
+}
+
+@end

+ 65 - 0
Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.h

@@ -120,3 +120,68 @@ extern "C" {
 #if defined(__cplusplus)
 }
 #endif
+
+/**
+ * \@keypath allows compile-time verification of key paths. Given a real object
+ * receiver and key path:
+ *
+ * @code
+
+NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String);
+// => @"lowercaseString.UTF8String"
+
+NSString *versionPath = @keypath(NSObject, version);
+// => @"version"
+
+NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString);
+// => @"lowercaseString"
+
+ * @endcode
+ *
+ * ... the macro returns an \c NSString containing all but the first path
+ * component or argument (e.g., @"lowercaseString.UTF8String", @"version").
+ *
+ * In addition to simply creating a key path, this macro ensures that the key
+ * path is valid at compile-time (causing a syntax error if not), and supports
+ * refactoring, such that changing the name of the property will also update any
+ * uses of \@keypath.
+ */
+#define keypath(...) \
+    _Pragma("clang diagnostic push") \
+    _Pragma("clang diagnostic ignored \"-Warc-repeated-use-of-weak\"") \
+    (NO).boolValue ? ((NSString * _Nonnull)nil) : ((NSString * _Nonnull)@(cStringKeypath(__VA_ARGS__))) \
+    _Pragma("clang diagnostic pop") \
+
+#define cStringKeypath(...) \
+    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))
+
+#define keypath1(PATH) \
+    (((void)(NO && ((void)PATH, NO)), \
+    ({ char *__extobjckeypath__ = strchr(# PATH, '.'); NSCAssert(__extobjckeypath__, @"Provided key path is invalid."); __extobjckeypath__ + 1; })))
+
+#define keypath2(OBJ, PATH) \
+    (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
+
+/**
+ * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object
+ * receiver, collection object receiver and related keypaths:
+ *
+ * @code
+ 
+ NSString *employeesFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName)
+ // => @"employees.firstName"
+ 
+ NSString *employeesFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName)
+ // => @"employees.firstName"
+
+ * @endcode
+ *
+ */
+#define collectionKeypath(...) \
+    metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__))
+
+#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) \
+    (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil
+
+#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) \
+    (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(OBJ, PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil

+ 1 - 0
Pods/SDWebImage/WebImage/SDWebImage.h

@@ -40,6 +40,7 @@ FOUNDATION_EXPORT const unsigned char SDWebImageVersionString[];
 #import <SDWebImage/UIButton+WebCache.h>
 #import <SDWebImage/SDWebImagePrefetcher.h>
 #import <SDWebImage/UIView+WebCacheOperation.h>
+#import <SDWebImage/UIView+WebCacheState.h>
 #import <SDWebImage/UIImage+Metadata.h>
 #import <SDWebImage/UIImage+MultiFormat.h>
 #import <SDWebImage/UIImage+MemoryCacheCost.h>

+ 22 - 0
Pods/SDWebImageWebPCoder/README.md

@@ -168,6 +168,28 @@ let thumbnailSize = CGSize(width: 300, height: 300)
 let image = SDImageWebPCoder.shared.decodedImage(with: data, options: [.decodeThumbnailPixelSize: thumbnailSize])
 ```
 
+### Decoding with limit bytes (0.12.0+)
+
++ Objective-C
+
+```objective-c
+// WebP thumbnail image decoding
+NSData *webpData;
+NSUInteger limitBytes = 1024 * 1024; // 1MB
+UIImage *image = [[SDImageWebPCoder sharedCoder] decodedImageWithData:webpData options:@{SDImageCoderDecodeScaleDownLimitBytes : @(limitBytes)}];
+// The image pixel buffer is guaranteed to less than 1MB in RAM (may scale down or full size), suitable for large image
+```
+
++ Swift
+
+```swift
+// WebP thumbnail image decoding
+let webpData: Data
+let limitBytes = 1024 * 1024 // 1MB
+let image = SDImageWebPCoder.shared.decodedImage(with: data, options: [.decodeScaleDownLimitBytes: limitBytes])
+// The image pixel buffer is guaranteed to less than 1MB in RAM (may scale down or full size), suitable for large image
+```
+
 ### Encoding
 
 + Objective-c

+ 174 - 23
Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m

@@ -71,8 +71,8 @@ else OSSpinLockUnlock(&lock##_deprecated);
 /// Used for animated WebP, which need a canvas for decoding (rendering), possible apply a scale transform for thumbnail decoding (avoiding post-rescale using vImage)
 /// See more in #73
 static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canvasSize, CGSize thumbnailSize, BOOL preserveAspectRatio) {
-    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
-    bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
+    // From SDWebImage v5.17.0, use runtime detection of bitmap info instead of hardcode.
+    CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
     // Check whether we need to use thumbnail
     CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(canvasSize.width, canvasSize.height) scaleSize:thumbnailSize preserveAspectRatio:preserveAspectRatio shouldScaleUp:NO];
     CGContextRef canvas = CGBitmapContextCreate(NULL, scaledSize.width, scaledSize.height, 8, 0, [SDImageCoderHelper colorSpaceGetDeviceRGB], bitmapInfo);
@@ -88,6 +88,89 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     return canvas;
 }
 
+WEBP_CSP_MODE ConvertCSPMode(CGBitmapInfo bitmapInfo) {
+    // Get alpha info, byteOrder info
+    CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
+    CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
+    BOOL byteOrderNormal = NO;
+    switch (byteOrderInfo) {
+        case kCGBitmapByteOrderDefault: {
+            byteOrderNormal = YES;
+        } break;
+        case kCGBitmapByteOrder32Little: {
+        } break;
+        case kCGBitmapByteOrder32Big: {
+            byteOrderNormal = YES;
+        } break;
+        default: break;
+    }
+    switch (alphaInfo) {
+        case kCGImageAlphaPremultipliedFirst: {
+            if (byteOrderNormal) {
+                // ARGB8888, premultiplied
+                return MODE_Argb;
+            } else {
+                // BGRA8888, premultiplied
+                return MODE_bgrA;
+            }
+        }
+            break;
+        case kCGImageAlphaPremultipliedLast: {
+            if (byteOrderNormal) {
+                // RGBA8888, premultiplied
+                return MODE_rgbA;
+            } else {
+                // ABGR8888, premultiplied
+                // Unsupported!
+                return MODE_LAST;
+            }
+        }
+            break;
+        case kCGImageAlphaNone: {
+            if (byteOrderNormal) {
+                // RGB
+                return MODE_RGB;
+            } else {
+                // BGR
+                return MODE_BGR;
+            }
+        }
+            break;
+        case kCGImageAlphaLast:
+        case kCGImageAlphaNoneSkipLast: {
+            if (byteOrderNormal) {
+                // RGBA or RGBX
+                return MODE_RGBA;
+            } else {
+                // ABGR or XBGR
+                // Unsupported!
+                return MODE_LAST;
+            }
+        }
+            break;
+        case kCGImageAlphaFirst:
+        case kCGImageAlphaNoneSkipFirst: {
+            if (byteOrderNormal) {
+                // ARGB or XRGB
+                return MODE_ARGB;
+            } else {
+                // BGRA or BGRX
+                return MODE_BGRA;
+            }
+        }
+            break;
+        case kCGImageAlphaOnly: {
+            // A
+            // Unsupported
+            return MODE_LAST;
+        }
+            break;
+        default:
+            break;
+    }
+    return MODE_LAST;
+}
+
 @interface SDWebPCoderFrame : NSObject
 
 @property (nonatomic, assign) NSUInteger index; // Frame index (zero based)
@@ -126,6 +209,7 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     NSUInteger _currentBlendIndex;
     BOOL _preserveAspectRatio;
     CGSize _thumbnailSize;
+    BOOL _limitBytes;
 }
 
 - (void)dealloc {
@@ -218,6 +302,24 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     CGColorSpaceRef colorSpace = [self sd_createColorSpaceWithDemuxer:demuxer];
     int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
     int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
+    uint32_t frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
+    int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
+    
+    NSUInteger limitBytes = 0;
+    NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+    if (limitBytesValue != nil) {
+        limitBytes = limitBytesValue.unsignedIntegerValue;
+    }
+    // Scale down to limit bytes if need
+    if (limitBytes > 0) {
+        // Hack 32 BitsPerPixel
+        CGSize imageSize = CGSizeMake(canvasWidth, canvasHeight);
+        CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:limitBytes bytesPerPixel:4 frameCount:frameCount];
+        // Override thumbnail size
+        thumbnailSize = framePixelSize;
+        preserveAspectRatio = YES;
+    }
+    
     // Check whether we need to use thumbnail
     CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(canvasWidth, canvasHeight) scaleSize:thumbnailSize preserveAspectRatio:preserveAspectRatio shouldScaleUp:NO];
     
@@ -245,7 +347,6 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
         return nil;
     }
     
-    int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
     NSMutableArray<SDImageFrame *> *frames = [NSMutableArray array];
     
     do {
@@ -285,8 +386,8 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
 - (instancetype)initIncrementalWithOptions:(nullable SDImageCoderOptions *)options {
     self = [super init];
     if (self) {
-        // Progressive images need transparent, so always use premultiplied BGRA
-        _idec = WebPINewRGB(MODE_bgrA, NULL, 0, 0);
+        // Progressive images need transparent, so always use premultiplied RGBA
+        _idec = WebPINewRGB(MODE_rgbA, NULL, 0, 0);
         CGFloat scale = 1;
         NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
         if (scaleFactor != nil) {
@@ -312,6 +413,12 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
             preserveAspectRatio = preserveAspectRatioValue.boolValue;
         }
         _preserveAspectRatio = preserveAspectRatio;
+        NSUInteger limitBytes = 0;
+        NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+        if (limitBytesValue != nil) {
+            limitBytes = limitBytesValue.unsignedIntegerValue;
+        }
+        _limitBytes = limitBytes;
         _currentBlendIndex = NSNotFound;
         SD_LOCK_INIT(_lock);
     }
@@ -352,6 +459,15 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
         [self scanAndCheckFramesValidWithDemuxer:_demux];
     }
     SD_UNLOCK(_lock);
+    // Scale down to limit bytes if need
+    if (_limitBytes > 0) {
+        // Hack 32 BitsPerPixel
+        CGSize imageSize = CGSizeMake(_canvasWidth, _canvasHeight);
+        CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:_limitBytes bytesPerPixel:4 frameCount:_frameCount];
+        // Override thumbnail size
+        _thumbnailSize = framePixelSize;
+        _preserveAspectRatio = YES;
+    }
 }
 
 - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
@@ -381,9 +497,10 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
         CGDataProviderRef provider =
         CGDataProviderCreateWithData(NULL, rgba, rgbaSize, NULL);
         CGColorSpaceRef colorSpaceRef = [SDImageCoderHelper colorSpaceGetDeviceRGB];
-        
-        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
+        // Because _idec use MODE_rgbA
+        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
         size_t components = 4;
+        BOOL shouldInterpolate = YES;
         CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
         // Why to use last_y for image height is because of libwebp's bug (https://bugs.chromium.org/p/webp/issues/detail?id=362)
         // It will not keep memory barrier safe on x86 architechure (macOS & iPhone simulator) but on ARM architecture (iPhone & iPad & tv & watch) it works great
@@ -391,7 +508,7 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
         // So this will cause our drawed image looks strange(above is the current part but below is the previous part)
         // We only grab the last_y height and draw the last_y height instead of total height image
         // Besides fix, this can enhance performance since we do not need to create extra bitmap
-        CGImageRef imageRef = CGImageCreate(width, last_y, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
+        CGImageRef imageRef = CGImageCreate(width, last_y, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, shouldInterpolate, renderingIntent);
         
         CGDataProviderRelease(provider);
         
@@ -499,20 +616,45 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     }
     
     BOOL hasAlpha = config.input.has_alpha;
-    // iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
-    // use this bitmapInfo, combined with right colorspace, even without decode, can still avoid extra CA::Render::copy_image(which marked `Color Copied Images` from Instruments)
-    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
-    bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
+    // From SDWebImage v5.17.0, use runtime detection of bitmap info instead of hardcode.
+    SDImagePixelFormat pixelFormat = [SDImageCoderHelper preferredPixelFormat:hasAlpha];
+    CGBitmapInfo bitmapInfo = pixelFormat.bitmapInfo;
+    WEBP_CSP_MODE mode = ConvertCSPMode(bitmapInfo);
+    if (mode == MODE_LAST) {
+        NSAssert(NO, @"Unsupported libwebp preferred CGBitmapInfo: %d", bitmapInfo);
+        return nil;
+    }
+    config.output.colorspace = mode;
     config.options.use_threads = 1;
-    config.output.colorspace = MODE_bgrA;
+    
     
     // Use scaling for thumbnail
+    size_t width = config.input.width;
+    size_t height = config.input.height;
     if (scaledSize.width != 0 && scaledSize.height != 0) {
         config.options.use_scaling = 1;
         config.options.scaled_width = scaledSize.width;
         config.options.scaled_height = scaledSize.height;
+        width = scaledSize.width;
+        height = scaledSize.height;
     }
     
+    // We alloc the buffer and do byte alignment by ourself. libwebp defaults does not byte alignment to `bitsPerPixel`, which cause the CoreAnimation unhappy and always trigger the `CA::Render::copy_image`
+    size_t bitsPerComponent = 8;
+    size_t components = (mode == MODE_RGB || mode == MODE_BGR) ? 3 : 4; // Actually always 4
+    size_t bitsPerPixel = bitsPerComponent * components;
+    // Read: https://github.com/path/FastImageCache#byte-alignment
+    // A properly aligned bytes-per-row value must be a multiple of 8 pixels × bytes per pixel
+    // For a typical ARGB image, the aligned bytes-per-row value is a multiple of 64.
+    size_t alignment = pixelFormat.alignment;
+    size_t bytesPerRow = SDByteAlign(width * (bitsPerPixel / 8), alignment);
+    
+    void *rgba = WebPMalloc(bytesPerRow * height);
+    config.output.is_external_memory = 1;
+    config.output.u.RGBA.rgba = rgba;
+    config.output.u.RGBA.stride = (int)bytesPerRow;
+    config.output.u.RGBA.size = height * bytesPerRow;
+    
     // Decode the WebP image data into a RGBA value array
     if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
         return nil;
@@ -521,13 +663,9 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     // Construct a UIImage from the decoded RGBA value array
     CGDataProviderRef provider =
     CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData);
-    size_t bitsPerComponent = 8;
-    size_t bitsPerPixel = 32;
-    size_t bytesPerRow = config.output.u.RGBA.stride;
-    size_t width = config.output.width;
-    size_t height = config.output.height;
+    BOOL shouldInterpolate = YES;
     CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
-    CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
+    CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, shouldInterpolate, renderingIntent);
     
     CGDataProviderRelease(provider);
     
@@ -709,9 +847,6 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
     }
     
     size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
-    size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
-    size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
-    size_t components = bitsPerPixel / bitsPerComponent;
     CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
     CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
     CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
@@ -844,7 +979,7 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv
 }
 
 static void FreeImageData(void *info, const void *data, size_t size) {
-    free((void *)data);
+    WebPFree((void *)data);
 }
 
 static int GetIntValueForKey(NSDictionary * _Nonnull dictionary, NSString * _Nonnull key, int defaultValue) {
@@ -911,6 +1046,21 @@ static float GetFloatValueForKey(NSDictionary * _Nonnull dictionary, NSString *
             preserveAspectRatio = preserveAspectRatioValue.boolValue;
         }
         _preserveAspectRatio = preserveAspectRatio;
+        NSUInteger limitBytes = 0;
+        NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes];
+        if (limitBytesValue != nil) {
+            limitBytes = limitBytesValue.unsignedIntegerValue;
+        }
+        _limitBytes = limitBytes;
+        // Scale down to limit bytes if need
+        if (_limitBytes > 0) {
+            // Hack 32 BitsPerPixel
+            CGSize imageSize = CGSizeMake(_canvasWidth, _canvasHeight);
+            CGSize framePixelSize = [SDImageCoderHelper scaledSizeWithImageSize:imageSize limitBytes:_limitBytes bytesPerPixel:4 frameCount:_frameCount];
+            // Override thumbnail size
+            _thumbnailSize = framePixelSize;
+            _preserveAspectRatio = YES;
+        }
         _scale = scale;
         _demux = demuxer;
         _imageData = data;
@@ -1174,6 +1324,7 @@ static float GetFloatValueForKey(NSDictionary * _Nonnull dictionary, NSString *
 #else
     image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
 #endif
+    image.sd_imageFormat = SDImageFormatWebP;
     CGImageRelease(imageRef);
     
     WebPDemuxReleaseIterator(&iter);

BIN
Pods/SwiftFormat/CommandLineTool/swiftformat


+ 32 - 5
Pods/SwiftFormat/README.md

@@ -3,7 +3,7 @@
 [![PayPal](https://img.shields.io/badge/paypal-donate-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9ZGWNK5FEZFF6&source=url)
 [![Build](https://github.com/nicklockwood/SwiftFormat/actions/workflows/build.yml/badge.svg)](https://github.com/nicklockwood/SwiftFormat/actions/workflows/build.yml)
 [![Codecov](https://codecov.io/gh/nicklockwood/SwiftFormat/graphs/badge.svg)](https://codecov.io/gh/nicklockwood/SwiftFormat)
-[![Swift 4.2](https://img.shields.io/badge/swift-4.2-red.svg?style=flat)](https://developer.apple.com/swift)
+[![Swift 5.1](https://img.shields.io/badge/swift-5.1-red.svg?style=flat)](https://developer.apple.com/swift)
 [![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://opensource.org/licenses/MIT)
 [![Mastodon](https://img.shields.io/badge/mastodon-@nicklockwood@mastodon.social-636dff.svg)](https://mastodon.social/@nicklockwood)
 
@@ -43,7 +43,7 @@ Table of Contents
 What is this?
 ----------------
 
-SwiftFormat is a code library and command-line tool for reformatting Swift code on macOS or Linux.
+SwiftFormat is a code library and command-line tool for reformatting Swift code on macOS, Linux or Windows.
 
 SwiftFormat goes above and beyond what you might expect from a code formatter. In addition to adjusting white space it can insert or remove implicit `self`, remove redundant parentheses, and correct many other deviations from the standard Swift idioms.
 
@@ -91,7 +91,7 @@ $ brew upgrade swiftformat
 Alternatively, you can install the tool on macOS or Linux by using [Mint](https://github.com/yonaskolb/Mint) as follows:
 
 ```bash
-$ mint install nicklockwood/SwiftFormat
+$ mint install nicklockwood/SwiftFormat@main
 ```
 
 Or if you prefer, you can check out and build SwiftFormat manually on macOS, Linux or Windows as follows:
@@ -314,6 +314,32 @@ fi
 
 This is not recommended for shared projects however, as different team members using different versions of SwiftFormat may result in noise in the commit history as code gets reformatted inconsistently.
 
+If you installed SwiftFormat via Homebrew on Apple Silicon, you might experience this warning:
+
+> warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat
+
+That is because Homebrew on Apple Silicon installs the binaries into the `/opt/homebrew/bin`
+folder by default. To instruct Xcode where to find SwiftFormat, you can either add
+`/opt/homebrew/bin` to the `PATH` environment variable in your build phase
+
+```bash
+if [[ "$(uname -m)" == arm64 ]]; then
+    export PATH="/opt/homebrew/bin:$PATH"
+fi
+
+if which swiftformat > /dev/null; then
+  swiftformat .
+else
+  echo "warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat"
+fi
+```
+
+or you can create a symbolic link in `/usr/local/bin` pointing to the actual binary:
+
+```bash
+ln -s /opt/homebrew/bin/swiftlint /usr/local/bin/swiftlint
+```
+
 Swift Package Manager plugin
 -----------------------------
 
@@ -480,7 +506,7 @@ The options available in SwiftFormat can be displayed using the `--options` comm
 
 Rules are configured by adding `--[option_name] [value]` to your command-line arguments, or by creating a `.swiftformat` [config file](#config-file) and placing it in your project directory.
 
-A given option may affect multiple rules. Use `--ruleinfo [rule_name]` command for details about which options affect a given rule, or see the [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md) file.
+A given option may affect multiple rules. Use `--ruleinfo [rule_name]` command for details about which options affect a given rule, or see the [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md) file.
 
 You can configure options for specific files or code ranges by using `swiftformat:options` directive in comments inside your Swift file. To temporarily set one or more options inside a source file, use:
 
@@ -499,7 +525,7 @@ doTheThing(); print("Did the thing")
 Rules
 -----
 
-SwiftFormat includes over 50 rules, and new ones are added all the time. An up-to-date list can be found in [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md) along with documentation for how they are used.
+SwiftFormat includes over 50 rules, and new ones are added all the time. An up-to-date list can be found in [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md) along with documentation for how they are used.
 
 The list of available rules can be displayed within the command-line app using the `--rules` argument. Rules can be either enabled or disabled. Most are enabled by default. Disabled rules are marked with "(disabled)" when displayed using `--rules`.
 
@@ -929,6 +955,7 @@ Credits
 * [Daniele Formichelli](https://github.com/danyf90) - JSON reporter
 * [Jonas Boberg](https://github.com/bobergj) - Github actions log reporter
 * [Mahdi Bchatnia](https://github.com/inket) - Linux build workflow
+* [Saleem Abdulrasool](https://github.com/compnerd) - Windows build workflow
 * [Arthur Semenyutin](https://github.com/vox-humana) - Docker image
 * [Marco Eidinger](https://github.com/MarcoEidinger) - Swift Package Manager plugin
 * [Nick Lockwood](https://github.com/nicklockwood) - Everything else

BIN
Pods/SwiftLint/swiftlint


+ 1 - 1
Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-Info.plist

@@ -15,7 +15,7 @@
   <key>CFBundlePackageType</key>
   <string>FMWK</string>
   <key>CFBundleShortVersionString</key>
-  <string>3.8.0</string>
+  <string>3.8.1</string>
   <key>CFBundleSignature</key>
   <string>????</string>
   <key>CFBundleVersion</key>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików