7
7
8
8
#import " SDImageAVIFCoder.h"
9
9
#import < Accelerate/Accelerate.h>
10
+ #import < os/lock.h>
11
+ #import < libkern/OSAtomic.h>
10
12
#if __has_include(<libavif/avif.h>)
11
13
#import < libavif/avif.h>
12
14
#import < libavif/internal.h>
17
19
18
20
#import " Private/Conversion.h"
19
21
20
- @implementation SDImageAVIFCoder
22
+ #define SD_USE_OS_UNFAIR_LOCK TARGET_OS_MACCATALYST ||\
23
+ (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) ||\
24
+ (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12) ||\
25
+ (__TV_OS_VERSION_MIN_REQUIRED >= __TVOS_10_0) ||\
26
+ (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)
27
+
28
+ #ifndef SD_LOCK_DECLARE
29
+ #if SD_USE_OS_UNFAIR_LOCK
30
+ #define SD_LOCK_DECLARE (lock ) os_unfair_lock lock
31
+ #else
32
+ #define SD_LOCK_DECLARE (lock ) os_unfair_lock lock API_AVAILABLE (ios(10.0 ), tvos(10 ), watchos(3 ), macos(10.12 )); \
33
+ OSSpinLock lock##_deprecated;
34
+ #endif
35
+ #endif
36
+
37
+ #ifndef SD_LOCK_INIT
38
+ #if SD_USE_OS_UNFAIR_LOCK
39
+ #define SD_LOCK_INIT (lock ) lock = OS_UNFAIR_LOCK_INIT
40
+ #else
41
+ #define SD_LOCK_INIT (lock ) if (@available(iOS 10 , tvOS 10 , watchOS 3 , macOS 10.12 , *)) lock = OS_UNFAIR_LOCK_INIT; \
42
+ else lock##_deprecated = OS_SPINLOCK_INIT;
43
+ #endif
44
+ #endif
45
+
46
+ #ifndef SD_LOCK
47
+ #if SD_USE_OS_UNFAIR_LOCK
48
+ #define SD_LOCK (lock ) os_unfair_lock_lock(&lock)
49
+ #else
50
+ #define SD_LOCK (lock ) if (@available(iOS 10 , tvOS 10 , watchOS 3 , macOS 10.12 , *)) os_unfair_lock_lock(&lock); \
51
+ else OSSpinLockLock(&lock##_deprecated);
52
+ #endif
53
+ #endif
54
+
55
+ #ifndef SD_UNLOCK
56
+ #if SD_USE_OS_UNFAIR_LOCK
57
+ #define SD_UNLOCK (lock ) os_unfair_lock_unlock(&lock)
58
+ #else
59
+ #define SD_UNLOCK (lock ) if (@available(iOS 10 , tvOS 10 , watchOS 3 , macOS 10.12 , *)) os_unfair_lock_unlock(&lock); \
60
+ else OSSpinLockUnlock(&lock##_deprecated);
61
+ #endif
62
+ #endif
63
+
64
+ @implementation SDImageAVIFCoder {
65
+ avifDecoder *_decoder;
66
+ NSData *_imageData;
67
+ CGFloat _scale;
68
+ NSUInteger _loopCount;
69
+ NSUInteger _frameCount;
70
+ SD_LOCK_DECLARE (_lock);
71
+ }
72
+
73
+ - (void )dealloc {
74
+ if (_decoder) {
75
+ avifDecoderDestroy (_decoder);
76
+ }
77
+ }
21
78
22
79
+ (instancetype )sharedCoder {
23
80
static SDImageAVIFCoder *coder;
@@ -44,23 +101,6 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
44
101
}
45
102
}
46
103
47
- // Currently only support primary image :)
48
- CGImageRef imageRef = [self sd_createAVIFImageWithData: data];
49
- if (!imageRef) {
50
- return nil ;
51
- }
52
-
53
- #if SD_MAC
54
- UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: kCGImagePropertyOrientationUp ];
55
- #else
56
- UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
57
- #endif
58
- CGImageRelease (imageRef);
59
-
60
- return image;
61
- }
62
-
63
- - (nullable CGImageRef)sd_createAVIFImageWithData : (nonnull NSData *)data CF_RETURNS_RETAINED {
64
104
// Decode it
65
105
avifDecoder * decoder = avifDecoderCreate ();
66
106
avifDecoderSetIOMemory (decoder, data.bytes , data.length );
@@ -72,15 +112,54 @@ - (nullable CGImageRef)sd_createAVIFImageWithData:(nonnull NSData *)data CF_RETU
72
112
avifDecoderDestroy (decoder);
73
113
return nil ;
74
114
}
75
- avifResult nextImageResult = avifDecoderNextImage (decoder);
76
- if (nextImageResult != AVIF_RESULT_OK || nextImageResult == AVIF_RESULT_NO_IMAGES_REMAINING) {
77
- NSLog (@" Failed to decode image: %s " , avifResultToString (nextImageResult));
78
- avifDecoderDestroy (decoder);
79
- return nil ;
115
+
116
+ // Static image
117
+ if (decoder->imageCount <= 1 ) {
118
+ avifResult nextImageResult = avifDecoderNextImage (decoder);
119
+ if (nextImageResult != AVIF_RESULT_OK) {
120
+ NSLog (@" Failed to decode image: %s " , avifResultToString (nextImageResult));
121
+ avifDecoderDestroy (decoder);
122
+ return nil ;
123
+ }
124
+ CGImageRef imageRef = SDCreateCGImageFromAVIF (decoder->image );
125
+ if (!imageRef) {
126
+ return nil ;
127
+ }
128
+ #if SD_MAC
129
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: kCGImagePropertyOrientationUp ];
130
+ #else
131
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
132
+ #endif
133
+ CGImageRelease (imageRef);
134
+ return image;
135
+ }
136
+
137
+ // Animated image
138
+ NSMutableArray <SDImageFrame *> *frames = [NSMutableArray array ];
139
+ while (avifDecoderNextImage (decoder) == AVIF_RESULT_OK) {
140
+ @autoreleasepool {
141
+ CGImageRef imageRef = SDCreateCGImageFromAVIF (decoder->image );
142
+ if (!imageRef) {
143
+ continue ;
144
+ }
145
+ #if SD_MAC
146
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: kCGImagePropertyOrientationUp ];
147
+ #else
148
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
149
+ #endif
150
+ NSTimeInterval duration = decoder->imageTiming .duration ; // Should use `decoder->imageTiming`, not the `decoder->duration`, see libavif source code
151
+ SDImageFrame *frame = [SDImageFrame frameWithImage: image duration: duration];
152
+ [frames addObject: frame];
153
+ }
80
154
}
81
- CGImageRef const image = SDCreateCGImageFromAVIF (decoder-> image );
155
+
82
156
avifDecoderDestroy (decoder);
83
- return image;
157
+
158
+ UIImage *animatedImage = [SDImageCoderHelper animatedImageWithFrames: frames];
159
+ animatedImage.sd_imageLoopCount = 0 ;
160
+ animatedImage.sd_imageFormat = SDImageFormatAVIF;
161
+
162
+ return animatedImage;
84
163
}
85
164
86
165
// The AVIF encoding seems slow at the current time, but at least works
@@ -195,6 +274,91 @@ - (nullable NSData *)encodedDataWithImage:(nullable UIImage *)image format:(SDIm
195
274
return imageData;
196
275
}
197
276
277
+ #pragma mark - Animation
278
+ - (instancetype )initWithAnimatedImageData : (NSData *)data options : (SDImageCoderOptions *)options {
279
+ self = [super init ];
280
+ if (self) {
281
+ avifDecoder *decoder = avifDecoderCreate ();
282
+ avifDecoderSetIOMemory (decoder, data.bytes , data.length );
283
+ // Disable strict mode to keep some AVIF image compatible
284
+ decoder->strictFlags = AVIF_STRICT_DISABLED;
285
+ avifResult decodeResult = avifDecoderParse (decoder);
286
+ if (decodeResult != AVIF_RESULT_OK) {
287
+ avifDecoderDestroy (decoder);
288
+ NSLog (@" Failed to decode image: %s " , avifResultToString (decodeResult));
289
+ return nil ;
290
+ }
291
+ // TODO: Optimize the performance like WebPCoder (frame meta cache, etc)
292
+ _frameCount = decoder->imageCount ;
293
+ _loopCount = 0 ;
294
+ CGFloat scale = 1 ;
295
+ NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
296
+ if (scaleFactor != nil ) {
297
+ scale = [scaleFactor doubleValue ];
298
+ if (scale < 1 ) {
299
+ scale = 1 ;
300
+ }
301
+ }
302
+ _scale = scale;
303
+ _decoder = decoder;
304
+ _imageData = data;
305
+ SD_LOCK_INIT (_lock);
306
+ }
307
+ return self;
308
+ }
309
+
310
+ - (NSData *)animatedImageData {
311
+ return _imageData;
312
+ }
313
+
314
+ - (NSUInteger )animatedImageLoopCount {
315
+ return _loopCount;
316
+ }
317
+
318
+ - (NSUInteger )animatedImageFrameCount {
319
+ return _frameCount;
320
+ }
321
+
322
+ - (NSTimeInterval )animatedImageDurationAtIndex : (NSUInteger )index {
323
+ if (index >= _frameCount) {
324
+ return 0 ;
325
+ }
326
+ if (_frameCount <= 1 ) {
327
+ return 0 ;
328
+ }
329
+ SD_LOCK (_lock);
330
+ avifImageTiming timing;
331
+ avifResult decodeResult = avifDecoderNthImageTiming (_decoder, (uint32_t )index, &timing);
332
+ SD_UNLOCK (_lock);
333
+ if (decodeResult != AVIF_RESULT_OK) {
334
+ return 0 ;
335
+ }
336
+ return timing.duration ;
337
+ }
338
+
339
+ - (UIImage *)animatedImageFrameAtIndex : (NSUInteger )index {
340
+ if (index >= _frameCount) {
341
+ return nil ;
342
+ }
343
+ SD_LOCK (_lock);
344
+ avifResult decodeResult = avifDecoderNthImage (_decoder, (uint32_t )index);
345
+ if (decodeResult != AVIF_RESULT_OK) {
346
+ return nil ;
347
+ }
348
+ CGImageRef imageRef = SDCreateCGImageFromAVIF (_decoder->image );
349
+ SD_UNLOCK (_lock);
350
+ if (!imageRef) {
351
+ return nil ;
352
+ }
353
+ #if SD_MAC
354
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: _scale orientation: kCGImagePropertyOrientationUp ];
355
+ #else
356
+ UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: _scale orientation: UIImageOrientationUp];
357
+ #endif
358
+ CGImageRelease (imageRef);
359
+ return image;
360
+ }
361
+
198
362
199
363
#pragma mark - Helper
200
364
+ (BOOL )isAVIFFormatForData : (NSData *)data
0 commit comments