Added support to encode APNG instead of GIF, when no animated image format provided#3867
Conversation
…ormat provided Check on supported OS version
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughWhen caching animated images with undefined format, the code now prefers PNG on iOS 12+/tvOS 12+/macOS 10.14+/watchOS 5.0+ and falls back to GIF on older OS versions. Documentation and tests were updated; gain-map handling received ARC bridging and control-flow adjustments. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
2737a2b to
6bd6b6e
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
Tests/Tests/SDImageCacheTests.m (2)
506-522:⚠️ Potential issue | 🟡 MinorStale test description and comment.
Line 441 (
expectation5's description) still says "should use GIF", and Line 506's comment says "should use GIF not APNG". Both are now inaccurate since newer OS versions will expect PNG. Update them to reflect the version-dependent behavior.📝 Suggested updates
- XCTestExpectation *expectation5 = [self expectationWithDescription:@"StoreImage UIAnimatedImage without sd_imageFormat should use GIF"]; + XCTestExpectation *expectation5 = [self expectationWithDescription:@"StoreImage UIAnimatedImage without sd_imageFormat should use APNG on iOS 12+/GIF otherwise"];- // Case 5: UIAnimatedImage without sd_imageFormat should use GIF not APNG + // Case 5: UIAnimatedImage without sd_imageFormat should use APNG on newer OS, GIF on older OS
514-517:⚠️ Potential issue | 🟡 MinorStale inline comment on Line 516.
The comment
// Should save to GIFis no longer accurate — on newer OS versions it saves to PNG/APNG.SDWebImage/Core/SDImageCoderHelper.m (1)
178-197:⚠️ Potential issue | 🔴 CriticalBug:
formatvariable is declared but never used — animated frames always encoded as GIF.Lines 178–183 compute
formatbased on OS version (PNG for iOS 12+ / macOS 10.14+, GIF otherwise), but line 184 ignores it and hardcodesSDImageFormatGIF:CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatGIF]; // always GIFAdditionally, line 196 uses GIF-specific property keys (
kCGImagePropertyGIFDictionary/kCGImagePropertyGIFDelayTime) regardless of the format. For APNG, these should use the PNG-specific equivalents nested insidekCGImagePropertyPNGDictionary(e.g.,kCGImagePropertyAPNGDelayTime).As a result,
animatedImageWithFrames:always produces GIF data regardless of OS version, defeating the purpose of this PR.🐛 Proposed fix
SDImageFormat format; if (`@available`(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) { format = SDImageFormatPNG; } else { format = SDImageFormatGIF; } - CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatGIF]; - // Create an image destination. GIF does not support EXIF image orientation + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; + // Create an image destination. GIF/APNG does not support EXIF image orientation CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL); if (!imageDestination) { // Handle failure. return nil; } for (size_t i = 0; i < frameCount; i++) { SDImageFrame *frame = frames[i]; NSTimeInterval frameDuration = frame.duration; CGImageRef frameImageRef = frame.image.CGImage; - NSDictionary *frameProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary : @{(__bridge NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}}; + NSDictionary *frameProperties; + if (format == SDImageFormatPNG) { + frameProperties = @{(__bridge NSString *)kCGImagePropertyPNGDictionary : @{(__bridge NSString *)kCGImagePropertyAPNGDelayTime : @(frameDuration)}}; + } else { + frameProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary : @{(__bridge NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}}; + } CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties); }
🤖 Fix all issues with AI agents
In `@SDWebImage/Core/SDImageCache.m`:
- Around line 276-283: Update the stale comment near the SDImageFormatUndefined
check to reflect the conditional choice of PNG/APNG on newer OSes: mention that
if image.sd_imageFrameCount > 1 the code uses PNG (APNG on supported OS
versions) when available and falls back to GIF on older OSes (SDImageFormatPNG
vs SDImageFormatGIF), so modify the comment to describe that behavior instead of
saying "use GIF (APNG may be better...)".
In `@SDWebImage/Core/SDImageCoderHelper.h`:
- Around line 71-72: Update the doc comment in SDImageCoderHelper.h to match the
implementation's availability check: change the "before macOS 10.13" phrasing to
"before macOS 10.14" (or rephrase to "macOS prior to 10.14") and indicate that
APNG is used on macOS 10.14 and later so macOS 10.13 is clearly included in the
GIF path; edit the comment block around the NSImage/animate description to use
"before macOS 10.14" and "macOS 10.14 and later" to align with the
`@available`(macOS 10.14, *) check in the implementation.
| if (format == SDImageFormatUndefined) { | ||
| // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14) | ||
| if (image.sd_imageFrameCount > 1) { | ||
| format = SDImageFormatGIF; | ||
| if (@available(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) { | ||
| format = SDImageFormatPNG; | ||
| } else { | ||
| format = SDImageFormatGIF; | ||
| } |
There was a problem hiding this comment.
Stale comment on Line 277.
The comment still reads "use GIF (APNG may be better, but has bugs before macOS 10.14)" but the code now conditionally uses PNG/APNG on newer OS versions. Update the comment to reflect the new behavior.
📝 Suggested comment update
- // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
+ // If image is animated, prefer APNG on newer OS versions for better quality; fall back to GIF on older OS versions (APNG has bugs before macOS 10.14)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (format == SDImageFormatUndefined) { | |
| // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14) | |
| if (image.sd_imageFrameCount > 1) { | |
| format = SDImageFormatGIF; | |
| if (@available(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) { | |
| format = SDImageFormatPNG; | |
| } else { | |
| format = SDImageFormatGIF; | |
| } | |
| if (format == SDImageFormatUndefined) { | |
| // If image is animated, prefer APNG on newer OS versions for better quality; fall back to GIF on older OS versions (APNG has bugs before macOS 10.14) | |
| if (image.sd_imageFrameCount > 1) { | |
| if (`@available`(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) { | |
| format = SDImageFormatPNG; | |
| } else { | |
| format = SDImageFormatGIF; | |
| } |
🤖 Prompt for AI Agents
In `@SDWebImage/Core/SDImageCache.m` around lines 276 - 283, Update the stale
comment near the SDImageFormatUndefined check to reflect the conditional choice
of PNG/APNG on newer OSes: mention that if image.sd_imageFrameCount > 1 the code
uses PNG (APNG on supported OS versions) when available and falls back to GIF on
older OSes (SDImageFormatPNG vs SDImageFormatGIF), so modify the comment to
describe that behavior instead of saying "use GIF (APNG may be better...)".
| For AppKit before macOS 10.13, NSImage does not support animates other than GIF. This will try to encode the frames to GIF format and then create an animated NSImage for rendering. Attention the animated image may loss some detail if the input frames contain full alpha channel because GIF only supports 1 bit alpha channel. (For 1 pixel, either transparent or not) | ||
| For AppKit after macOS 10.14, use APNG format, which has a better render result. |
There was a problem hiding this comment.
Documentation gap for macOS 10.13.
The doc says "before macOS 10.13" uses GIF and "after macOS 10.14" uses APNG, but says nothing about macOS 10.13 itself. Since the @available check in the implementation uses macOS 10.14, macOS 10.13 falls into the GIF path. Consider rewording to "before macOS 10.14" for clarity and consistency with the implementation.
📝 Suggested doc fix
- For AppKit before macOS 10.13, NSImage does not support animates other than GIF. This will try to encode the frames to GIF format and then create an animated NSImage for rendering. Attention the animated image may loss some detail if the input frames contain full alpha channel because GIF only supports 1 bit alpha channel. (For 1 pixel, either transparent or not)
- For AppKit after macOS 10.14, use APNG format, which has a better render result.
+ For AppKit before macOS 10.14, NSImage does not support animates other than GIF. This will try to encode the frames to GIF format and then create an animated NSImage for rendering. Attention the animated image may loss some detail if the input frames contain full alpha channel because GIF only supports 1 bit alpha channel. (For 1 pixel, either transparent or not)
+ For AppKit on macOS 10.14+, use APNG format, which has a better render result.🤖 Prompt for AI Agents
In `@SDWebImage/Core/SDImageCoderHelper.h` around lines 71 - 72, Update the doc
comment in SDImageCoderHelper.h to match the implementation's availability
check: change the "before macOS 10.13" phrasing to "before macOS 10.14" (or
rephrase to "macOS prior to 10.14") and indicate that APNG is used on macOS
10.14 and later so macOS 10.13 is clearly included in the GIF path; edit the
comment block around the NSImage/animate description to use "before macOS 10.14"
and "macOS 10.14 and later" to align with the `@available`(macOS 10.14, *) check
in the implementation.
6bd6b6e to
77f2d03
Compare
New Pull Request Checklist
I have read and understood the CONTRIBUTING guide
I have read the Documentation
I have searched for a similar pull request in the project and found none
I have updated this branch with the latest master to avoid conflicts (via merge from master or rebase)
I have added the required tests to prove the fix/feature I am adding
I have updated the documentation (if necessary)
I have run the tests and they pass
I have run the lint and it passes (
pod lib lint)This merge request fixes / refers to the following issues: ...
Pull Request Description
This can get better animation encoding quality, APNG is far more better than GIF. And since it's already used by many other platform, I think it's time to convert our solution for some cases.
Summary by CodeRabbit
New Features
Documentation
Tests