Skip to content

Commit 03252bd

Browse files
authored
Merge pull request #69 from SDWebImage/feature/avif_loop_count
Support libavif loop count
2 parents 1c25e7a + 2b09dfc commit 03252bd

File tree

6 files changed

+87
-22
lines changed

6 files changed

+87
-22
lines changed

Cartfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
github "SDWebImage/SDWebImage" ~> 5.10
2-
github "SDWebImage/libavif-Xcode" >= 0.11.2-rc1
2+
github "SDWebImage/libavif-Xcode" >= 1.0.0

Package.resolved

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let package = Package(
1818
// Dependencies declare other packages that this package depends on.
1919
// .package(url: /* package url */, from: "1.0.0"),
2020
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.10.0"),
21-
.package(url: "https://github.com/SDWebImage/libavif-Xcode.git", from: "0.11.0")
21+
.package(url: "https://github.com/SDWebImage/libavif-Xcode.git", from: "1.0.0")
2222
],
2323
targets: [
2424
// Targets are the basic building blocks of a package. A target can define a module or a test suite.

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,24 @@ let avifData = SDImageAVIFCoder.shared.encodedData(with: image, format: .avif, o
267267
let lossyAVIFData = SDImageAVIFCoder.shared.encodedData(with: image, format: .avif, options: [.encodeCompressionQuality: 0.1]) // [0, 1] compression quality
268268
```
269269

270+
### Thumbnail Encoding (0.12.0+)
271+
272+
+ Objective-C
273+
274+
```objective-c
275+
// AVIF image thumbnail encoding
276+
UIImage *image;
277+
NSData *thumbnailAVIFData = [[SDImageAVIFCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatAVIF options:@{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(200, 200))}]; // encoding max pixel size
278+
```
279+
280+
+ Swift
281+
282+
```swift
283+
// AVIF image thumbnail encoding
284+
let image: UIImage
285+
let thumbnailAVIFData = SDImageAVIFCoder.shared.encodedData(with: image, format: .AVIF, options: [.encodeMaxPixelSize: CGSize(width: 200, height: 200)]) // encoding max pixel size
286+
```
287+
270288
## Screenshot
271289

272290
<img src="https://raw.githubusercontent.com/SDWebImage/SDWebImageAVIFCoder/master/Example/Screenshot/AVIFDemo-iOS.png" width="300" />

SDWebImageAVIFCoder.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'SDWebImageAVIFCoder'
11-
s.version = '0.11.1'
11+
s.version = '0.12.0'
1212
s.summary = 'A SDWebImage coder plugin to support AVIF(AV1 Image File Format) image'
1313

1414
# This description is used to generate tags and improve search results.
@@ -40,6 +40,6 @@ Which is built based on the open-sourced libavif codec.
4040
}
4141

4242
s.dependency 'SDWebImage', '~> 5.10'
43-
s.dependency 'libavif/core', '>= 0.11.0'
43+
s.dependency 'libavif/core', '>= 1.0.0'
4444
s.libraries = 'c++'
4545
end

SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
182182

183183
// Animated image
184184
NSMutableArray<SDImageFrame *> *frames = [NSMutableArray array];
185+
// if repetitionCount is a non-negative integer `n`, then the image sequence should be played back `n + 1` times.
186+
int loopCount = decoder->repetitionCount + 1;
187+
if (loopCount < 0) {
188+
loopCount = 0;
189+
}
185190
while (avifDecoderNextImage(decoder) == AVIF_RESULT_OK) {
186191
@autoreleasepool {
187192
CGImageRef originImageRef = SDCreateCGImageFromAVIF(decoder->image);
@@ -209,7 +214,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
209214
avifDecoderDestroy(decoder);
210215

211216
UIImage *animatedImage = [SDImageCoderHelper animatedImageWithFrames:frames];
212-
animatedImage.sd_imageLoopCount = 0;
217+
animatedImage.sd_imageLoopCount = loopCount;
213218
animatedImage.sd_imageFormat = SDImageFormatAVIF;
214219

215220
return animatedImage;
@@ -305,24 +310,62 @@ - (nullable NSData *)encodedDataWithImage:(nullable UIImage *)image format:(SDIm
305310
avifImageRGBToYUV(avif, &rgb);
306311
free(dest.data);
307312

308-
NSData *iccProfile = (__bridge_transfer NSData *)CGColorSpaceCopyICCProfile([SDImageCoderHelper colorSpaceGetDeviceRGB]);
309-
310-
avifImageSetProfileICC(avif, (uint8_t *)iccProfile.bytes, iccProfile.length);
313+
// We must prefer the input CGImage's color space, which may contains ICC profile
314+
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
315+
// We only supports RGB colorspace, filter the un-supported one (like Monochrome, CMYK, etc)
316+
if (CGColorSpaceGetModel(colorSpace) != kCGColorSpaceModelRGB) {
317+
// Ignore and convert, we don't know how to encode this colorspace directlly to WebP
318+
// This may cause little visible difference because of colorpsace conversion
319+
colorSpace = NULL;
320+
}
321+
if (!colorSpace) {
322+
colorSpace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
323+
}
324+
// Add ICC profile if present
325+
CFDataRef iccData = NULL;
326+
if (colorSpace) {
327+
if (@available(iOS 10, tvOS 10, macOS 10.12, watchOS 3, *)) {
328+
iccData = CGColorSpaceCopyICCData(colorSpace);
329+
}
330+
}
331+
if (iccData && CFDataGetLength(iccData) > 0) {
332+
avifImageSetProfileICC(avif, CFDataGetBytePtr(iccData), CFDataGetLength(iccData));
333+
}
311334

312335
double compressionQuality = 1;
313336
if (options[SDImageCoderEncodeCompressionQuality]) {
314337
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue];
315338
}
316-
int rescaledQuality = AVIF_QUANTIZER_WORST_QUALITY - (int)((compressionQuality) * AVIF_QUANTIZER_WORST_QUALITY);
339+
int quality = compressionQuality * (AVIF_QUALITY_BEST - AVIF_QUALITY_WORST);
340+
CGSize maxPixelSize = CGSizeZero;
341+
NSValue *maxPixelSizeValue = options[SDImageCoderEncodeMaxPixelSize];
342+
if (maxPixelSizeValue != nil) {
343+
#if SD_MAC
344+
maxPixelSize = maxPixelSizeValue.sizeValue;
345+
#else
346+
maxPixelSize = maxPixelSizeValue.CGSizeValue;
347+
#endif
348+
}
317349

318350
avifRWData raw = AVIF_DATA_EMPTY;
319351
avifEncoder *encoder = avifEncoderCreate();
320352
encoder->codecChoice = codecChoice;
321-
encoder->minQuantizer = rescaledQuality;
322-
encoder->maxQuantizer = rescaledQuality;
323-
encoder->minQuantizerAlpha = rescaledQuality;
324-
encoder->maxQuantizerAlpha = rescaledQuality;
353+
encoder->quality = quality;
354+
encoder->qualityAlpha = quality;
325355
encoder->maxThreads = 2;
356+
// Check if need to scale pixel size
357+
CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:maxPixelSize preserveAspectRatio:YES shouldScaleUp:NO];
358+
if (!CGSizeEqualToSize(scaledSize, CGSizeMake(width, height))) {
359+
// Thumbnail Encoding
360+
assert(scaledSize.width <= width);
361+
assert(scaledSize.height <= height);
362+
avifScalingMode scale;
363+
scale.horizontal.n = (int)scaledSize.width;
364+
scale.horizontal.d = (int)width;
365+
scale.vertical.n = (int)scaledSize.height;
366+
scale.vertical.d = (int)height;
367+
encoder->scalingMode = scale;
368+
}
326369
avifResult result = avifEncoderWrite(encoder, avif, &raw);
327370

328371
avifImageDestroy(avif);
@@ -361,7 +404,11 @@ - (instancetype)initWithAnimatedImageData:(NSData *)data options:(SDImageCoderOp
361404
}
362405
// TODO: Optimize the performance like WebPCoder (frame meta cache, etc)
363406
_frameCount = decoder->imageCount;
364-
_loopCount = 0;
407+
int loopCount = decoder->repetitionCount + 1;
408+
if (loopCount < 0) {
409+
loopCount = 0;
410+
}
411+
_loopCount = loopCount;
365412
_hasAnimation = decoder->imageCount > 1;
366413
CGFloat scale = 1;
367414
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];

0 commit comments

Comments
 (0)