Skip to content

Support libavif loop count#69

Merged
dreampiggy merged 5 commits intomasterfrom
feature/avif_loop_count
Feb 6, 2026
Merged

Support libavif loop count#69
dreampiggy merged 5 commits intomasterfrom
feature/avif_loop_count

Conversation

@dreampiggy
Copy link
Collaborator

@dreampiggy dreampiggy commented Feb 6, 2026

This requires libavif 1.0.0+

This close #68

Summary by CodeRabbit

  • New Features

    • Improved AVIF encoding: better quality handling and optional thumbnail scaling for smaller outputs.
  • Bug Fixes

    • Animated AVIF images now respect their encoded loop counts, fixing playback/looping behavior.
  • Chores

    • Bumped package and pod versions and updated native dependencies for compatibility (libavif → 1.0.0, SDWebImage → 5.21.5, podspec → 0.12.0).

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

Warning

Rate limit exceeded

@dreampiggy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 50 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Dependency versions bumped to libavif 1.0.0 across manifests and podspec (pod version → 0.12.0). Added +sharedCoder to SDImageAVIFCoder and changed animated AVIF loopCount derivation to use decoder->repetitionCount (mapped to loopCount). Encoding path updated to use quality and optional max-pixel scaling.

Changes

Cohort / File(s) Summary
Dependency Manifests
Cartfile, Package.resolved, Package.swift
Updated SDWebImage/libavif-Xcode and libavif-related resolutions to 1.0.0 (and bumped related native libs: libaom, libvmaf, SDWebImage in Package.resolved).
CocoaPod Spec
SDWebImageAVIFCoder.podspec
Pod version bumped 0.11.10.12.0; libavif/core dependency constraint updated to >= 1.0.0.
AVIF Coder Implementation
SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m
Added + (instancetype)sharedCoder; changed animated decoding to compute loopCount = repetitionCount + 1 (clamped ≥0) and propagate to animated image; encoding logic revised to use unified quality for color/alpha, support SDImageCoderEncodeMaxPixelSize scaling/thumbnail path, and apply scaling before encode when needed (replacing prior min/max quantizer handling).

Sequence Diagram(s)

(omitted — changes are limited to dependency bumps and internal encoder/decoder logic; no multi-component sequential flow requiring diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble at bytes and hop in delight,
Versions climb up to one-oh-oh tonight.
A shared coder springs, loops count just right,
Images shrink or shine, encoding bright.
Hooray—tiny hops, big leaps in sight! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Changes include dependency updates (Cartfile, Package.swift, Package.resolved, podspec) and the addition of a sharedCoder method alongside the core loop count fix, which appear to be supporting infrastructure rather than out-of-scope work. Clarify whether the sharedCoder accessor method addition and all dependency version bumps (libavif, libaom, libvmaf, SDWebImage) are required for the loop count feature or represent separate enhancements.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Support libavif loop count' directly reflects the main change: adding support for libavif loop count functionality as described in the objectives and code changes.
Linked Issues check ✅ Passed The code changes address issue #68 by replacing hardcoded _loopCount = 0 with proper assignment from decoder->repetitionCount, computing loopCount as repetitionCount + 1 with clamping.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/avif_loop_count

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

This requires libavif 1.0.0+
@dreampiggy dreampiggy force-pushed the feature/avif_loop_count branch from f4c63cd to 634f480 Compare February 6, 2026 08:40
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m`:
- Around line 364-365: The assignment in the animated image initializer
improperly converts a signed int to NSUInteger: when setting _loopCount from
decoder->repetitionCount do the same guard used earlier for the
signed-to-unsigned issue: check if decoder->repetitionCount is negative and set
_loopCount to 0 in that case, otherwise safely cast/assign the non-negative
decoder->repetitionCount to the NSUInteger _loopCount; update the block that
sets _frameCount and _loopCount (referencing _frameCount, _loopCount, and
decoder->repetitionCount) to include this guard.
🧹 Nitpick comments (1)
SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m (1)

320-326: Pre-existing: deprecated quantizer fields should be migrated to quality/qualityAlpha.

libavif 1.0.0 deprecated avifEncoder's minQuantizer, maxQuantizer, minQuantizerAlpha, and maxQuantizerAlpha fields — quality and qualityAlpha should be used instead.

Since this PR bumps the minimum libavif to 1.0.0, these fields will still compile but may produce warnings and could be removed in a future release. Consider migrating in a follow-up.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m`:
- Around line 338-351: The local variable scaledSize is being recomputed and
shadowed inside the if block; remove the inner "CGSize scaledSize" declaration
so the block uses the previously computed scaledSize from the call to
[SDImageCoderHelper
scaledSizeWithImageSize:scaleSize:preserveAspectRatio:shouldScaleUp:] and then
set encoder->scalingMode accordingly (assign scale.horizontal/vertical using
scaledSize and original width/height). Also replace the strict asserts
(assert(scaledSize.width < width); assert(scaledSize.height < height);) with
non-fatal checks (use <= or remove the asserts) to avoid debug aborts due to
floating-point rounding—ensure the code handles equality gracefully before
assigning encoder->scalingMode.
🧹 Nitpick comments (1)
SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m (1)

317-321: Consider clamping compressionQuality to [0.0, 1.0].

If the caller passes an out-of-range value via SDImageCoderEncodeCompressionQuality, the computed quality could exceed AVIF_QUALITY_BEST or go negative. A simple MIN(MAX(..., 0), 1) clamp would make this defensive.

🛡️ Suggested clamp
     double compressionQuality = 1;
     if (options[SDImageCoderEncodeCompressionQuality]) {
         compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue];
     }
-    int quality = compressionQuality * (AVIF_QUALITY_BEST - AVIF_QUALITY_WORST);
+    compressionQuality = MAX(MIN(compressionQuality, 1.0), 0.0);
+    int quality = (int)(compressionQuality * (AVIF_QUALITY_BEST - AVIF_QUALITY_WORST));

Comment on lines 338 to 368
// Check if need to scale pixel size
CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:maxPixelSize preserveAspectRatio:YES shouldScaleUp:NO];
if (!CGSizeEqualToSize(scaledSize, CGSizeMake(width, height))) {
// Thumbnail Encoding
CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:maxPixelSize preserveAspectRatio:YES shouldScaleUp:NO];
assert(scaledSize.width < width);
assert(scaledSize.height < height);
avifScalingMode scale;
scale.horizontal.n = (int)scaledSize.width;
scale.horizontal.d = (int)width;
scale.vertical.n = (int)scaledSize.height;
scale.vertical.d = (int)height;
encoder->scalingMode = scale;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Shadowed variable scaledSize on line 342 duplicates the computation from line 339.

Inside the if block, CGSize scaledSize is re-declared with the exact same expression already evaluated three lines above. This shadows the outer scaledSize and is dead/redundant code. Remove the inner declaration so the block uses the already-computed value.

Additionally, the assert on lines 343–344 uses strict <. With floating-point rounding in aspect-ratio-preserving scaling, one dimension could conceivably equal the original, causing a debug-mode abort. Consider <= or removing the asserts in favor of a graceful early-return.

🔧 Proposed fix — remove duplicate declaration
     // Check if need to scale pixel size
     CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:maxPixelSize preserveAspectRatio:YES shouldScaleUp:NO];
     if (!CGSizeEqualToSize(scaledSize, CGSizeMake(width, height))) {
         // Thumbnail Encoding
-        CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:maxPixelSize preserveAspectRatio:YES shouldScaleUp:NO];
-        assert(scaledSize.width < width);
-        assert(scaledSize.height < height);
+        assert(scaledSize.width <= width);
+        assert(scaledSize.height <= height);
         avifScalingMode scale;
         scale.horizontal.n = (int)scaledSize.width;
         scale.horizontal.d = (int)width;
🤖 Prompt for AI Agents
In `@SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m` around lines 338 - 351, The
local variable scaledSize is being recomputed and shadowed inside the if block;
remove the inner "CGSize scaledSize" declaration so the block uses the
previously computed scaledSize from the call to [SDImageCoderHelper
scaledSizeWithImageSize:scaleSize:preserveAspectRatio:shouldScaleUp:] and then
set encoder->scalingMode accordingly (assign scale.horizontal/vertical using
scaledSize and original width/height). Also replace the strict asserts
(assert(scaledSize.width < width); assert(scaledSize.height < height);) with
non-fatal checks (use <= or remove the asserts) to avoid debug aborts due to
floating-point rounding—ensure the code handles equality gracefully before
assigning encoder->scalingMode.

@dreampiggy dreampiggy force-pushed the feature/avif_loop_count branch from 462a8bd to 6247df1 Compare February 6, 2026 09:16
@dreampiggy dreampiggy force-pushed the feature/avif_loop_count branch from 6247df1 to 2b09dfc Compare February 6, 2026 09:18
@dreampiggy dreampiggy merged commit 03252bd into master Feb 6, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

loopCount赋值问题

1 participant