@@ -50,6 +50,30 @@ static void ConvertAvifImagePlanarToRGB(avifImage * avif, uint8_t * outPixels) {
50
50
}
51
51
}
52
52
53
+ static void FillRGBABufferWithAVIFImage (vImage_Buffer *red, vImage_Buffer *green, vImage_Buffer *blue, vImage_Buffer *alpha, avifImage *img) {
54
+ red->width = img->width ;
55
+ red->height = img->height ;
56
+ red->data = img->rgbPlanes [AVIF_CHAN_R];
57
+ red->rowBytes = img->rgbRowBytes [AVIF_CHAN_R];
58
+
59
+ green->width = img->width ;
60
+ green->height = img->height ;
61
+ green->data = img->rgbPlanes [AVIF_CHAN_G];
62
+ green->rowBytes = img->rgbRowBytes [AVIF_CHAN_G];
63
+
64
+ blue->width = img->width ;
65
+ blue->height = img->height ;
66
+ blue->data = img->rgbPlanes [AVIF_CHAN_B];
67
+ blue->rowBytes = img->rgbRowBytes [AVIF_CHAN_B];
68
+
69
+ if (img->alphaPlane != NULL ) {
70
+ alpha->width = img->width ;
71
+ alpha->height = img->height ;
72
+ alpha->data = img->alphaPlane ;
73
+ alpha->rowBytes = img->alphaRowBytes ;
74
+ }
75
+ }
76
+
53
77
static void FreeImageData (void *info, const void *data, size_t size) {
54
78
free ((void *)data);
55
79
}
@@ -142,11 +166,124 @@ - (nullable CGImageRef)sd_createAVIFImageWithData:(nonnull NSData *)data CF_RETU
142
166
143
167
// The AVIF encoding seems too slow at the current time
144
168
- (BOOL )canEncodeToFormat : (SDImageFormat)format {
145
- return NO ;
169
+ return format == SDImageFormatAVIF ;
146
170
}
147
171
148
- - (nullable NSData *)encodedDataWithImage : (nullable UIImage *)image format : (SDImageFormat)format options : (nullable SDImageCoderOptions *)options {
149
- return nil ;
172
+ - (nullable NSData *)encodedDataWithImage : (nullable UIImage *)image format : (SDImageFormat)format options : (nullable SDImageCoderOptions *)options {
173
+ CGImageRef imageRef = image.CGImage ;
174
+ if (!imageRef) {
175
+ return nil ;
176
+ }
177
+
178
+ size_t width = CGImageGetWidth (imageRef);
179
+ size_t height = CGImageGetHeight (imageRef);
180
+ size_t bitsPerPixel = CGImageGetBitsPerPixel (imageRef);
181
+ size_t bitsPerComponent = CGImageGetBitsPerComponent (imageRef);
182
+ CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo (imageRef);
183
+ CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask ;
184
+ CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask ;
185
+ BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
186
+ alphaInfo == kCGImageAlphaNoneSkipFirst ||
187
+ alphaInfo == kCGImageAlphaNoneSkipLast );
188
+ BOOL byteOrderNormal = NO ;
189
+ switch (byteOrderInfo) {
190
+ case kCGBitmapByteOrderDefault : {
191
+ byteOrderNormal = YES ;
192
+ } break ;
193
+ case kCGBitmapByteOrder32Little : {
194
+ } break ;
195
+ case kCGBitmapByteOrder32Big : {
196
+ byteOrderNormal = YES ;
197
+ } break ;
198
+ default : break ;
199
+ }
200
+
201
+ vImageConverterRef convertor = NULL ;
202
+ vImage_Error v_error = kvImageNoError;
203
+
204
+ vImage_CGImageFormat srcFormat = {
205
+ .bitsPerComponent = (uint32_t )bitsPerComponent,
206
+ .bitsPerPixel = (uint32_t )bitsPerPixel,
207
+ .colorSpace = CGImageGetColorSpace (imageRef),
208
+ .bitmapInfo = bitmapInfo
209
+ };
210
+ vImage_CGImageFormat destFormat = {
211
+ .bitsPerComponent = 8 ,
212
+ .bitsPerPixel = hasAlpha ? 32 : 24 ,
213
+ .colorSpace = [SDImageCoderHelper colorSpaceGetDeviceRGB ],
214
+ .bitmapInfo = hasAlpha ? kCGImageAlphaFirst | kCGBitmapByteOrderDefault : kCGImageAlphaNone | kCGBitmapByteOrderDefault // RGB888/ARGB8888 (Non-premultiplied to works for libbpg)
215
+ };
216
+
217
+ convertor = vImageConverter_CreateWithCGImageFormat (&srcFormat, &destFormat, NULL , kvImageNoFlags, &v_error);
218
+ if (v_error != kvImageNoError) {
219
+ return nil ;
220
+ }
221
+
222
+ vImage_Buffer src;
223
+ v_error = vImageBuffer_InitWithCGImage (&src, &srcFormat, NULL , imageRef, kvImageNoFlags);
224
+ if (v_error != kvImageNoError) {
225
+ return nil ;
226
+ }
227
+ vImage_Buffer dest;
228
+ vImageBuffer_Init (&dest, height, width, hasAlpha ? 32 : 24 , kvImageNoFlags);
229
+ if (!dest.data ) {
230
+ free (src.data );
231
+ return nil ;
232
+ }
233
+
234
+ // Convert input color mode to RGB888/ARGB8888
235
+ v_error = vImageConvert_AnyToAny (convertor, &src, &dest, NULL , kvImageNoFlags);
236
+ free (src.data );
237
+ vImageConverter_Release (convertor);
238
+ if (v_error != kvImageNoError) {
239
+ free (dest.data );
240
+ return nil ;
241
+ }
242
+
243
+ avifPixelFormat avifFormat = AVIF_PIXEL_FORMAT_YUV444;
244
+ enum avifPlanesFlags planesFlags = hasAlpha ? AVIF_PLANES_RGB | AVIF_PLANES_A : AVIF_PLANES_RGB;
245
+
246
+ avifImage *avif = avifImageCreate ((int )width, (int )height, 8 , avifFormat);
247
+ if (!avif) {
248
+ free (dest.data );
249
+ return nil ;
250
+ }
251
+ avifImageAllocatePlanes (avif, planesFlags);
252
+
253
+ NSData *iccProfile = (__bridge_transfer NSData *)CGColorSpaceCopyICCProfile ([SDImageCoderHelper colorSpaceGetDeviceRGB ]);
254
+
255
+ avifImageSetProfileICC (avif, (uint8_t *)iccProfile.bytes , iccProfile.length );
256
+
257
+ vImage_Buffer red, green, blue, alpha;
258
+ FillRGBABufferWithAVIFImage (&red, &green, &blue, &alpha, avif);
259
+
260
+ if (hasAlpha) {
261
+ v_error = vImageConvert_ARGB8888toPlanar8 (&dest, &alpha, &red, &green, &blue, kvImageNoFlags);
262
+ } else {
263
+ v_error = vImageConvert_RGB888toPlanar8 (&dest, &red, &green, &blue, kvImageNoFlags);
264
+ }
265
+ free (dest.data );
266
+ if (v_error != kvImageNoError) {
267
+ return nil ;
268
+ }
269
+
270
+ double compressionQuality = 1 ;
271
+ if (options[SDImageCoderEncodeCompressionQuality]) {
272
+ compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue ];
273
+ }
274
+ int rescaledQuality = 63 - (int )((compressionQuality) * 63 .0f );
275
+
276
+ avifRawData raw = AVIF_RAW_DATA_EMPTY;
277
+ avifResult result = avifImageWrite (avif, &raw, 2 , rescaledQuality);
278
+
279
+ if (result != AVIF_RESULT_OK) {
280
+ return nil ;
281
+ }
282
+
283
+ NSData *imageData = [NSData dataWithBytes: raw.data length: raw.size];
284
+ free (raw.data );
285
+
286
+ return imageData;
150
287
}
151
288
152
289
0 commit comments