Skip to content

Commit 52a3141

Browse files
authored
Merge pull request #29 from SDWebImage/support_libavif_0.8.3+
Support libavif 0.8.3+
2 parents df69635 + 6aa0e94 commit 52a3141

File tree

7 files changed

+189
-14
lines changed

7 files changed

+189
-14
lines changed

.github/workflows/check-image-decoding.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ jobs:
3232
CMD="../Example/CLI.xcarchive/Products/usr/local/bin/SDWebImageAVIFCoder_Example CLI"
3333
for file in $(find . -name \*.avif); do
3434
file=$(basename ${file})
35-
if (echo ${file} | grep "profile"); then
35+
if (echo ${file} | grep "\(monochrome\|crop\|rotate\|mirror\)"); then
36+
# FIXME(ledyba-z): Check them.
37+
echo "Ignore: ${file}"
38+
continue
39+
elif (echo ${file} | grep "profile"); then
3640
# FIXME(ledyba-z): https://github.com/SDWebImage/SDWebImageAVIFCoder/issues/21
3741
echo "Ignore: ${file}"
3842
continue

Example/SDWebImageAVIFCoder/SDViewController.m

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ - (void)viewDidLoad
2222

2323
SDImageAVIFCoder *AVIFCoder = [SDImageAVIFCoder sharedCoder];
2424
[[SDImageCodersManager sharedManager] addCoder:AVIFCoder];
25-
NSURL *AVIFURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/AOMediaCodec/av1-avif/master/testFiles/Microsoft/kids_720p.avif"];
26-
NSURL *HDRAVIFURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/AOMediaCodec/av1-avif/master/testFiles/Microsoft/Chimera_10bit_cropped_to_1920x1008.avif"];
27-
25+
NSURL *AVIFURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/link-u/avif-sample-images/master/fox.profile0.8bpc.yuv420.avif"];
26+
NSURL *HDRAVIFURL = [NSURL URLWithString:@"https://raw.githubusercontent.com/link-u/avif-sample-images/master/hato.profile2.12bpc.yuv422.avif"];
2827
CGSize screenSize = [UIScreen mainScreen].bounds.size;
2928

3029
UIImageView *imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, screenSize.height / 2)];

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ AVIF image spec is still in evolve. And the current upstream AVIF codec is a sim
2121

2222
Since we rely on the external codec libavif. We may periodically update the dependency and bump version. Make sure you're using the latest version as possible as you can :)
2323

24-
## aom && dav1d && rav1e
24+
## AV1 Codec
2525

2626
libavif is a still image codec. But AVIF is based on the AV1 Video standard. So it need a AV1 codec for support. This relationship is just like HEIF(image) and HEVC(video) codec.
2727

@@ -37,6 +37,12 @@ See more about [explanation for why starting a new project but not improving aom
3737

3838
From v0.3.0, libavif can built with dav1d. For CocoaPods user, you can simply use the subspec for this. Carthage for optional dav1d codec is not supported currently.
3939

40+
### libgav1 (Decoding)
41+
42+
[libgav1](https://chromium.googlesource.com/codecs/libgav1/) libgav1 is a Main profile (0) & High profile (1) compliant AV1 decoder. More information on the AV1 video format can be found at aomedia.org.
43+
44+
From v0.8.3, libavif can built with libgav1. For For CocoaPods user, you can simply use the subspec for this.
45+
4046
### rav1e (Encoding)
4147

4248
[rav1e](https://github.com/xiph/rav1e) is the fastest and safest AV1 encoder. Which use [Rust programming](https://www.rust-lang.org/) to provide fast and safe codec compared to aom. Its current form it is most suitable for cases where libaom (the reference encoder) is too slow.
@@ -54,6 +60,15 @@ brew install git-lfs
5460
git lfs install
5561
```
5662

63+
### SVT-AV1 (Encoding)
64+
65+
[SVT-AV1](https://gitlab.com/AOMediaCodec/SVT-AV1) is the Scalable Video Technology for AV1 (SVT-AV1 Encoder and Decoder) is an AV1-compliant encoder/decoder library core.
66+
67+
From v0.8.3, libavif can built with STV-AV1. For For CocoaPods user, you can simply use the subspec for this.
68+
69+
### SVT-AV1
70+
71+
5772
## Requirements
5873

5974
+ iOS 9
@@ -81,6 +96,16 @@ pod 'libavif', :subpsecs => [
8196
]
8297
```
8398

99+
or, for libgav1 && SVT-AV1, use:
100+
101+
```ruby
102+
pod 'SDWebImageAVIFCoder'
103+
pod 'libavif', :subpsecs => [
104+
'libgva1',
105+
'SVT-AV1'
106+
]
107+
```
108+
84109
Note: From version 0.2.0, the dependency libavif and libaom use the portable C implementation to works on Apple platforms. If you need the pre-built library with SIMD/AVX and assembly optimization, try the 0.1.0 version.
85110

86111
#### Carthage

SDWebImageAVIFCoder.podspec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ Which is built based on the open-sourced libavif codec.
3535
s.source_files = 'SDWebImageAVIFCoder/Classes/**/*', 'SDWebImageAVIFCoder/Module/SDWebImageAVIFCoder.h'
3636
s.public_header_files = 'SDWebImageAVIFCoder/Classes/Public/*.{h,m}', 'SDWebImageAVIFCoder/Module/SDWebImageAVIFCoder.h'
3737
s.private_header_files = 'SDWebImageAVIFCoder/Classes/Private/*.{h,m}'
38+
s.pod_target_xcconfig = {
39+
'HEADER_SEARCH_PATHS' => '$(inherited) ${PODS_ROOT}/libavif/include',
40+
}
3841

3942
s.dependency 'SDWebImage', '~> 5.10'
40-
s.dependency 'libavif', '>= 0.8'
43+
s.dependency 'libavif', '>= 0.8.3'
44+
s.libraries = 'c++'
4145
end

SDWebImageAVIFCoder/Classes/ColorSpace.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#import <Accelerate/Accelerate.h>
1010
#if __has_include(<libavif/avif.h>)
1111
#import <libavif/avif.h>
12+
#import <libavif/internal.h>
1213
#else
13-
#import "avif/avif.h"
14+
#import "avif/avifs.h"
15+
#import "avif/internal.h"
1416
#endif
1517

1618
static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* const white) {

SDWebImageAVIFCoder/Classes/Conversion.m

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#import <Accelerate/Accelerate.h>
1010
#if __has_include(<libavif/avif.h>)
1111
#import <libavif/avif.h>
12+
#import <libavif/internal.h>
1213
#else
13-
#import "avif/avif.h"
14+
#import "avif/avifs.h"
15+
#import "avif/internal.h"
1416
#endif
1517
#import "Private/ColorSpace.h"
1618

@@ -53,6 +55,146 @@ static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result)
5355
return imageRef;
5456
}
5557

58+
static avifBool avifPrepareReformatState(const avifImage * image, const avifRGBImage * rgb, avifReformatState * state)
59+
{
60+
if ((image->depth != 8) && (image->depth != 10) && (image->depth != 12)) {
61+
return AVIF_FALSE;
62+
}
63+
if ((rgb->depth != 8) && (rgb->depth != 10) && (rgb->depth != 12) && (rgb->depth != 16)) {
64+
return AVIF_FALSE;
65+
}
66+
67+
// These matrix coefficients values are currently unsupported. Revise this list as more support is added.
68+
//
69+
// YCgCo performs limited-full range adjustment on R,G,B but the current implementation performs range adjustment
70+
// on Y,U,V. So YCgCo with limited range is unsupported.
71+
if ((image->matrixCoefficients == 3 /* CICP reserved */) ||
72+
((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO) && (image->yuvRange == AVIF_RANGE_LIMITED)) ||
73+
(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT2020_CL) ||
74+
(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_SMPTE2085) ||
75+
(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL) ||
76+
(image->matrixCoefficients >= AVIF_MATRIX_COEFFICIENTS_ICTCP)) { // Note the >= catching "future" CICP values here too
77+
return AVIF_FALSE;
78+
}
79+
80+
if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444)) {
81+
return AVIF_FALSE;
82+
}
83+
84+
if (image->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
85+
return AVIF_FALSE;
86+
}
87+
88+
avifGetPixelFormatInfo(image->yuvFormat, &state->formatInfo);
89+
avifCalcYUVCoefficients(image, &state->kr, &state->kg, &state->kb);
90+
state->mode = AVIF_REFORMAT_MODE_YUV_COEFFICIENTS;
91+
92+
if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) {
93+
state->mode = AVIF_REFORMAT_MODE_IDENTITY;
94+
} else if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO) {
95+
state->mode = AVIF_REFORMAT_MODE_YCGCO;
96+
}
97+
98+
if (state->mode != AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) {
99+
state->kr = 0.0f;
100+
state->kg = 0.0f;
101+
state->kb = 0.0f;
102+
}
103+
104+
state->yuvChannelBytes = (image->depth > 8) ? 2 : 1;
105+
state->rgbChannelBytes = (rgb->depth > 8) ? 2 : 1;
106+
state->rgbChannelCount = avifRGBFormatChannelCount(rgb->format);
107+
state->rgbPixelBytes = state->rgbChannelBytes * state->rgbChannelCount;
108+
109+
switch (rgb->format) {
110+
case AVIF_RGB_FORMAT_RGB:
111+
state->rgbOffsetBytesR = state->rgbChannelBytes * 0;
112+
state->rgbOffsetBytesG = state->rgbChannelBytes * 1;
113+
state->rgbOffsetBytesB = state->rgbChannelBytes * 2;
114+
state->rgbOffsetBytesA = 0;
115+
break;
116+
case AVIF_RGB_FORMAT_RGBA:
117+
state->rgbOffsetBytesR = state->rgbChannelBytes * 0;
118+
state->rgbOffsetBytesG = state->rgbChannelBytes * 1;
119+
state->rgbOffsetBytesB = state->rgbChannelBytes * 2;
120+
state->rgbOffsetBytesA = state->rgbChannelBytes * 3;
121+
break;
122+
case AVIF_RGB_FORMAT_ARGB:
123+
state->rgbOffsetBytesA = state->rgbChannelBytes * 0;
124+
state->rgbOffsetBytesR = state->rgbChannelBytes * 1;
125+
state->rgbOffsetBytesG = state->rgbChannelBytes * 2;
126+
state->rgbOffsetBytesB = state->rgbChannelBytes * 3;
127+
break;
128+
case AVIF_RGB_FORMAT_BGR:
129+
state->rgbOffsetBytesB = state->rgbChannelBytes * 0;
130+
state->rgbOffsetBytesG = state->rgbChannelBytes * 1;
131+
state->rgbOffsetBytesR = state->rgbChannelBytes * 2;
132+
state->rgbOffsetBytesA = 0;
133+
break;
134+
case AVIF_RGB_FORMAT_BGRA:
135+
state->rgbOffsetBytesB = state->rgbChannelBytes * 0;
136+
state->rgbOffsetBytesG = state->rgbChannelBytes * 1;
137+
state->rgbOffsetBytesR = state->rgbChannelBytes * 2;
138+
state->rgbOffsetBytesA = state->rgbChannelBytes * 3;
139+
break;
140+
case AVIF_RGB_FORMAT_ABGR:
141+
state->rgbOffsetBytesA = state->rgbChannelBytes * 0;
142+
state->rgbOffsetBytesB = state->rgbChannelBytes * 1;
143+
state->rgbOffsetBytesG = state->rgbChannelBytes * 2;
144+
state->rgbOffsetBytesR = state->rgbChannelBytes * 3;
145+
break;
146+
147+
default:
148+
return AVIF_FALSE;
149+
}
150+
151+
state->yuvDepth = image->depth;
152+
state->yuvRange = image->yuvRange;
153+
state->yuvMaxChannel = (1 << image->depth) - 1;
154+
state->rgbMaxChannel = (1 << rgb->depth) - 1;
155+
state->rgbMaxChannelF = (float)state->rgbMaxChannel;
156+
state->biasY = (state->yuvRange == AVIF_RANGE_LIMITED) ? (float)(16 << (state->yuvDepth - 8)) : 0.0f;
157+
state->biasUV = (float)(1 << (state->yuvDepth - 1));
158+
state->biasA = (image->alphaRange == AVIF_RANGE_LIMITED) ? (float)(16 << (state->yuvDepth - 8)) : 0.0f;
159+
state->rangeY = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (219 << (state->yuvDepth - 8)) : state->yuvMaxChannel);
160+
state->rangeUV = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (224 << (state->yuvDepth - 8)) : state->yuvMaxChannel);
161+
state->rangeA = (float)((image->alphaRange == AVIF_RANGE_LIMITED) ? (219 << (state->yuvDepth - 8)) : state->yuvMaxChannel);
162+
163+
uint32_t cpCount = 1 << image->depth;
164+
if (state->mode == AVIF_REFORMAT_MODE_IDENTITY) {
165+
for (uint32_t cp = 0; cp < cpCount; ++cp) {
166+
state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY;
167+
state->unormFloatTableUV[cp] = ((float)cp - state->biasY) / state->rangeY;
168+
}
169+
} else {
170+
for (uint32_t cp = 0; cp < cpCount; ++cp) {
171+
// Review this when implementing YCgCo limited range support.
172+
state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY;
173+
state->unormFloatTableUV[cp] = ((float)cp - state->biasUV) / state->rangeUV;
174+
}
175+
}
176+
177+
state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP;
178+
if (image->alphaPlane) {
179+
if (!avifRGBFormatHasAlpha(rgb->format) || rgb->ignoreAlpha) {
180+
// if we are converting some image with alpha into a format without alpha, we should do 'premultiply alpha' before
181+
// discarding alpha plane. This has the same effect of rendering this image on a black background, which makes sense.
182+
if (!image->alphaPremultiplied) {
183+
state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY;
184+
}
185+
} else {
186+
if (!image->alphaPremultiplied && rgb->alphaPremultiplied) {
187+
state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY;
188+
} else if (image->alphaPremultiplied && !rgb->alphaPremultiplied) {
189+
state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY;
190+
}
191+
}
192+
}
193+
194+
return AVIF_TRUE;
195+
}
196+
197+
56198
static void SetupConversionInfo(avifImage * avif,
57199
avifReformatState* state,
58200
vImage_YpCbCrToARGBMatrix* matrix,

SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#import <Accelerate/Accelerate.h>
1010
#if __has_include(<libavif/avif.h>)
1111
#import <libavif/avif.h>
12+
#import <libavif/internal.h>
1213
#else
13-
#import "avif/avif.h"
14+
#import "avif/avifs.h"
15+
#import "avif/internal.h"
1416
#endif
1517

1618
#import "Private/Conversion.h"
@@ -60,12 +62,9 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
6062

6163
- (nullable CGImageRef)sd_createAVIFImageWithData:(nonnull NSData *)data CF_RETURNS_RETAINED {
6264
// Decode it
63-
avifROData rawData = {
64-
.data = (uint8_t *)data.bytes,
65-
.size = data.length
66-
};
6765
avifDecoder * decoder = avifDecoderCreate();
68-
avifResult decodeResult = avifDecoderParse(decoder, &rawData);
66+
avifDecoderSetIOMemory(decoder, data.bytes, data.length);
67+
avifResult decodeResult = avifDecoderParse(decoder);
6968
if (decodeResult != AVIF_RESULT_OK) {
7069
NSLog(@"Failed to decode image: %s", avifResultToString(decodeResult));
7170
avifDecoderDestroy(decoder);

0 commit comments

Comments
 (0)