1 // Copyright 2019 Joe Drago. All rights reserved.
2 // SPDX-License-Identifier: BSD-2-Clause
3 
4 #include "avif/internal.h"
5 
6 #include <string.h>
7 
8 #define STR_HELPER(x) #x
9 #define STR(x) STR_HELPER(x)
10 #define AVIF_VERSION_STRING (STR(AVIF_VERSION_MAJOR) "." STR(AVIF_VERSION_MINOR) "." STR(AVIF_VERSION_PATCH))
11 
avifVersion(void)12 const char * avifVersion(void)
13 {
14     return AVIF_VERSION_STRING;
15 }
16 
avifPixelFormatToString(avifPixelFormat format)17 const char * avifPixelFormatToString(avifPixelFormat format)
18 {
19     switch (format) {
20         case AVIF_PIXEL_FORMAT_YUV444:
21             return "YUV444";
22         case AVIF_PIXEL_FORMAT_YUV420:
23             return "YUV420";
24         case AVIF_PIXEL_FORMAT_YUV422:
25             return "YUV422";
26         case AVIF_PIXEL_FORMAT_YUV400:
27             return "YUV400";
28         case AVIF_PIXEL_FORMAT_NONE:
29         default:
30             break;
31     }
32     return "Unknown";
33 }
34 
avifGetPixelFormatInfo(avifPixelFormat format,avifPixelFormatInfo * info)35 void avifGetPixelFormatInfo(avifPixelFormat format, avifPixelFormatInfo * info)
36 {
37     memset(info, 0, sizeof(avifPixelFormatInfo));
38 
39     switch (format) {
40         case AVIF_PIXEL_FORMAT_YUV444:
41             info->chromaShiftX = 0;
42             info->chromaShiftY = 0;
43             break;
44 
45         case AVIF_PIXEL_FORMAT_YUV422:
46             info->chromaShiftX = 1;
47             info->chromaShiftY = 0;
48             break;
49 
50         case AVIF_PIXEL_FORMAT_YUV420:
51             info->chromaShiftX = 1;
52             info->chromaShiftY = 1;
53             break;
54 
55         case AVIF_PIXEL_FORMAT_YUV400:
56             info->chromaShiftX = 1;
57             info->chromaShiftY = 1;
58             info->monochrome = AVIF_TRUE;
59             break;
60 
61         case AVIF_PIXEL_FORMAT_NONE:
62         default:
63             break;
64     }
65 }
66 
avifResultToString(avifResult result)67 const char * avifResultToString(avifResult result)
68 {
69     // clang-format off
70     switch (result) {
71         case AVIF_RESULT_OK:                            return "OK";
72         case AVIF_RESULT_INVALID_FTYP:                  return "Invalid ftyp";
73         case AVIF_RESULT_NO_CONTENT:                    return "No content";
74         case AVIF_RESULT_NO_YUV_FORMAT_SELECTED:        return "No YUV format selected";
75         case AVIF_RESULT_REFORMAT_FAILED:               return "Reformat failed";
76         case AVIF_RESULT_UNSUPPORTED_DEPTH:             return "Unsupported depth";
77         case AVIF_RESULT_ENCODE_COLOR_FAILED:           return "Encoding of color planes failed";
78         case AVIF_RESULT_ENCODE_ALPHA_FAILED:           return "Encoding of alpha plane failed";
79         case AVIF_RESULT_BMFF_PARSE_FAILED:             return "BMFF parsing failed";
80         case AVIF_RESULT_NO_AV1_ITEMS_FOUND:            return "No AV1 items found";
81         case AVIF_RESULT_DECODE_COLOR_FAILED:           return "Decoding of color planes failed";
82         case AVIF_RESULT_DECODE_ALPHA_FAILED:           return "Decoding of alpha plane failed";
83         case AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH:     return "Color and alpha planes size mismatch";
84         case AVIF_RESULT_ISPE_SIZE_MISMATCH:            return "Plane sizes don't match ispe values";
85         case AVIF_RESULT_NO_CODEC_AVAILABLE:            return "No codec available";
86         case AVIF_RESULT_NO_IMAGES_REMAINING:           return "No images remaining";
87         case AVIF_RESULT_INVALID_EXIF_PAYLOAD:          return "Invalid Exif payload";
88         case AVIF_RESULT_INVALID_IMAGE_GRID:            return "Invalid image grid";
89         case AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION: return "Invalid codec-specific option";
90         case AVIF_RESULT_TRUNCATED_DATA:                return "Truncated data";
91         case AVIF_RESULT_IO_NOT_SET:                    return "IO not set";
92         case AVIF_RESULT_IO_ERROR:                      return "IO Error";
93         case AVIF_RESULT_WAITING_ON_IO:                 return "Waiting on IO";
94         case AVIF_RESULT_UNKNOWN_ERROR:
95         default:
96             break;
97     }
98     // clang-format on
99     return "Unknown Error";
100 }
101 
102 // This function assumes nothing in this struct needs to be freed; use avifImageClear() externally
avifImageSetDefaults(avifImage * image)103 static void avifImageSetDefaults(avifImage * image)
104 {
105     memset(image, 0, sizeof(avifImage));
106     image->yuvRange = AVIF_RANGE_FULL;
107     image->alphaRange = AVIF_RANGE_FULL;
108     image->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
109     image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
110     image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED;
111 }
112 
avifImageCreate(int width,int height,int depth,avifPixelFormat yuvFormat)113 avifImage * avifImageCreate(int width, int height, int depth, avifPixelFormat yuvFormat)
114 {
115     avifImage * image = (avifImage *)avifAlloc(sizeof(avifImage));
116     avifImageSetDefaults(image);
117     image->width = width;
118     image->height = height;
119     image->depth = depth;
120     image->yuvFormat = yuvFormat;
121     return image;
122 }
123 
avifImageCreateEmpty(void)124 avifImage * avifImageCreateEmpty(void)
125 {
126     return avifImageCreate(0, 0, 0, AVIF_PIXEL_FORMAT_NONE);
127 }
128 
avifImageCopy(avifImage * dstImage,const avifImage * srcImage,uint32_t planes)129 void avifImageCopy(avifImage * dstImage, const avifImage * srcImage, uint32_t planes)
130 {
131     avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
132 
133     dstImage->width = srcImage->width;
134     dstImage->height = srcImage->height;
135     dstImage->depth = srcImage->depth;
136     dstImage->yuvFormat = srcImage->yuvFormat;
137     dstImage->yuvRange = srcImage->yuvRange;
138     dstImage->yuvChromaSamplePosition = srcImage->yuvChromaSamplePosition;
139     dstImage->alphaRange = srcImage->alphaRange;
140 
141     dstImage->colorPrimaries = srcImage->colorPrimaries;
142     dstImage->transferCharacteristics = srcImage->transferCharacteristics;
143     dstImage->matrixCoefficients = srcImage->matrixCoefficients;
144 
145     dstImage->transformFlags = srcImage->transformFlags;
146     memcpy(&dstImage->pasp, &srcImage->pasp, sizeof(dstImage->pasp));
147     memcpy(&dstImage->clap, &srcImage->clap, sizeof(dstImage->clap));
148     memcpy(&dstImage->irot, &srcImage->irot, sizeof(dstImage->irot));
149     memcpy(&dstImage->imir, &srcImage->imir, sizeof(dstImage->pasp));
150 
151     avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size);
152 
153     avifImageSetMetadataExif(dstImage, srcImage->exif.data, srcImage->exif.size);
154     avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size);
155 
156     if ((planes & AVIF_PLANES_YUV) && srcImage->yuvPlanes[AVIF_CHAN_Y]) {
157         avifImageAllocatePlanes(dstImage, AVIF_PLANES_YUV);
158 
159         avifPixelFormatInfo formatInfo;
160         avifGetPixelFormatInfo(srcImage->yuvFormat, &formatInfo);
161         uint32_t uvHeight = (dstImage->height + formatInfo.chromaShiftY) >> formatInfo.chromaShiftY;
162         for (int yuvPlane = 0; yuvPlane < 3; ++yuvPlane) {
163             uint32_t planeHeight = (yuvPlane == AVIF_CHAN_Y) ? dstImage->height : uvHeight;
164 
165             if (!srcImage->yuvRowBytes[yuvPlane]) {
166                 // plane is absent. If we're copying from a source without
167                 // them, mimic the source image's state by removing our copy.
168                 avifFree(dstImage->yuvPlanes[yuvPlane]);
169                 dstImage->yuvPlanes[yuvPlane] = NULL;
170                 dstImage->yuvRowBytes[yuvPlane] = 0;
171                 continue;
172             }
173 
174             for (uint32_t j = 0; j < planeHeight; ++j) {
175                 uint8_t * srcRow = &srcImage->yuvPlanes[yuvPlane][j * srcImage->yuvRowBytes[yuvPlane]];
176                 uint8_t * dstRow = &dstImage->yuvPlanes[yuvPlane][j * dstImage->yuvRowBytes[yuvPlane]];
177                 memcpy(dstRow, srcRow, dstImage->yuvRowBytes[yuvPlane]);
178             }
179         }
180     }
181 
182     if ((planes & AVIF_PLANES_A) && srcImage->alphaPlane) {
183         avifImageAllocatePlanes(dstImage, AVIF_PLANES_A);
184         for (uint32_t j = 0; j < dstImage->height; ++j) {
185             uint8_t * srcAlphaRow = &srcImage->alphaPlane[j * srcImage->alphaRowBytes];
186             uint8_t * dstAlphaRow = &dstImage->alphaPlane[j * dstImage->alphaRowBytes];
187             memcpy(dstAlphaRow, srcAlphaRow, dstImage->alphaRowBytes);
188         }
189     }
190 }
191 
avifImageDestroy(avifImage * image)192 void avifImageDestroy(avifImage * image)
193 {
194     avifImageFreePlanes(image, AVIF_PLANES_ALL);
195     avifRWDataFree(&image->icc);
196     avifRWDataFree(&image->exif);
197     avifRWDataFree(&image->xmp);
198     avifFree(image);
199 }
200 
avifImageSetProfileICC(avifImage * image,const uint8_t * icc,size_t iccSize)201 void avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize)
202 {
203     avifRWDataSet(&image->icc, icc, iccSize);
204 }
205 
avifImageSetMetadataExif(avifImage * image,const uint8_t * exif,size_t exifSize)206 void avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize)
207 {
208     avifRWDataSet(&image->exif, exif, exifSize);
209 }
210 
avifImageSetMetadataXMP(avifImage * image,const uint8_t * xmp,size_t xmpSize)211 void avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize)
212 {
213     avifRWDataSet(&image->xmp, xmp, xmpSize);
214 }
215 
avifImageAllocatePlanes(avifImage * image,uint32_t planes)216 void avifImageAllocatePlanes(avifImage * image, uint32_t planes)
217 {
218     int channelSize = avifImageUsesU16(image) ? 2 : 1;
219     int fullRowBytes = channelSize * image->width;
220     int fullSize = fullRowBytes * image->height;
221     if ((planes & AVIF_PLANES_YUV) && (image->yuvFormat != AVIF_PIXEL_FORMAT_NONE)) {
222         avifPixelFormatInfo info;
223         avifGetPixelFormatInfo(image->yuvFormat, &info);
224 
225         int shiftedW = (image->width + info.chromaShiftX) >> info.chromaShiftX;
226         int shiftedH = (image->height + info.chromaShiftY) >> info.chromaShiftY;
227 
228         int uvRowBytes = channelSize * shiftedW;
229         int uvSize = uvRowBytes * shiftedH;
230 
231         if (!image->yuvPlanes[AVIF_CHAN_Y]) {
232             image->yuvRowBytes[AVIF_CHAN_Y] = fullRowBytes;
233             image->yuvPlanes[AVIF_CHAN_Y] = avifAlloc(fullSize);
234         }
235 
236         if (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) {
237             if (!image->yuvPlanes[AVIF_CHAN_U]) {
238                 image->yuvRowBytes[AVIF_CHAN_U] = uvRowBytes;
239                 image->yuvPlanes[AVIF_CHAN_U] = avifAlloc(uvSize);
240             }
241             if (!image->yuvPlanes[AVIF_CHAN_V]) {
242                 image->yuvRowBytes[AVIF_CHAN_V] = uvRowBytes;
243                 image->yuvPlanes[AVIF_CHAN_V] = avifAlloc(uvSize);
244             }
245         }
246         image->imageOwnsYUVPlanes = AVIF_TRUE;
247     }
248     if (planes & AVIF_PLANES_A) {
249         if (!image->alphaPlane) {
250             image->alphaRowBytes = fullRowBytes;
251             image->alphaPlane = avifAlloc(fullSize);
252         }
253         image->imageOwnsAlphaPlane = AVIF_TRUE;
254     }
255 }
256 
avifImageFreePlanes(avifImage * image,uint32_t planes)257 void avifImageFreePlanes(avifImage * image, uint32_t planes)
258 {
259     if ((planes & AVIF_PLANES_YUV) && (image->yuvFormat != AVIF_PIXEL_FORMAT_NONE)) {
260         if (image->imageOwnsYUVPlanes) {
261             avifFree(image->yuvPlanes[AVIF_CHAN_Y]);
262             avifFree(image->yuvPlanes[AVIF_CHAN_U]);
263             avifFree(image->yuvPlanes[AVIF_CHAN_V]);
264         }
265         image->yuvPlanes[AVIF_CHAN_Y] = NULL;
266         image->yuvRowBytes[AVIF_CHAN_Y] = 0;
267         image->yuvPlanes[AVIF_CHAN_U] = NULL;
268         image->yuvRowBytes[AVIF_CHAN_U] = 0;
269         image->yuvPlanes[AVIF_CHAN_V] = NULL;
270         image->yuvRowBytes[AVIF_CHAN_V] = 0;
271         image->imageOwnsYUVPlanes = AVIF_FALSE;
272     }
273     if (planes & AVIF_PLANES_A) {
274         if (image->imageOwnsAlphaPlane) {
275             avifFree(image->alphaPlane);
276         }
277         image->alphaPlane = NULL;
278         image->alphaRowBytes = 0;
279         image->imageOwnsAlphaPlane = AVIF_FALSE;
280     }
281 }
282 
avifImageStealPlanes(avifImage * dstImage,avifImage * srcImage,uint32_t planes)283 void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, uint32_t planes)
284 {
285     avifImageFreePlanes(dstImage, planes);
286 
287     if (planes & AVIF_PLANES_YUV) {
288         dstImage->yuvPlanes[AVIF_CHAN_Y] = srcImage->yuvPlanes[AVIF_CHAN_Y];
289         dstImage->yuvRowBytes[AVIF_CHAN_Y] = srcImage->yuvRowBytes[AVIF_CHAN_Y];
290         dstImage->yuvPlanes[AVIF_CHAN_U] = srcImage->yuvPlanes[AVIF_CHAN_U];
291         dstImage->yuvRowBytes[AVIF_CHAN_U] = srcImage->yuvRowBytes[AVIF_CHAN_U];
292         dstImage->yuvPlanes[AVIF_CHAN_V] = srcImage->yuvPlanes[AVIF_CHAN_V];
293         dstImage->yuvRowBytes[AVIF_CHAN_V] = srcImage->yuvRowBytes[AVIF_CHAN_V];
294 
295         srcImage->yuvPlanes[AVIF_CHAN_Y] = NULL;
296         srcImage->yuvRowBytes[AVIF_CHAN_Y] = 0;
297         srcImage->yuvPlanes[AVIF_CHAN_U] = NULL;
298         srcImage->yuvRowBytes[AVIF_CHAN_U] = 0;
299         srcImage->yuvPlanes[AVIF_CHAN_V] = NULL;
300         srcImage->yuvRowBytes[AVIF_CHAN_V] = 0;
301 
302         dstImage->yuvFormat = srcImage->yuvFormat;
303         dstImage->imageOwnsYUVPlanes = srcImage->imageOwnsYUVPlanes;
304         srcImage->imageOwnsYUVPlanes = AVIF_FALSE;
305     }
306     if (planes & AVIF_PLANES_A) {
307         dstImage->alphaPlane = srcImage->alphaPlane;
308         dstImage->alphaRowBytes = srcImage->alphaRowBytes;
309 
310         srcImage->alphaPlane = NULL;
311         srcImage->alphaRowBytes = 0;
312 
313         dstImage->imageOwnsAlphaPlane = srcImage->imageOwnsAlphaPlane;
314         srcImage->imageOwnsAlphaPlane = AVIF_FALSE;
315     }
316 }
317 
avifImageUsesU16(const avifImage * image)318 avifBool avifImageUsesU16(const avifImage * image)
319 {
320     return (image->depth > 8);
321 }
322 
323 // avifCodecCreate*() functions are in their respective codec_*.c files
324 
avifCodecDestroy(avifCodec * codec)325 void avifCodecDestroy(avifCodec * codec)
326 {
327     if (codec && codec->destroyInternal) {
328         codec->destroyInternal(codec);
329     }
330     avifFree(codec);
331 }
332 
333 // ---------------------------------------------------------------------------
334 // avifRGBImage
335 
avifRGBFormatHasAlpha(avifRGBFormat format)336 avifBool avifRGBFormatHasAlpha(avifRGBFormat format)
337 {
338     return (format != AVIF_RGB_FORMAT_RGB) && (format != AVIF_RGB_FORMAT_BGR);
339 }
340 
avifRGBFormatChannelCount(avifRGBFormat format)341 uint32_t avifRGBFormatChannelCount(avifRGBFormat format)
342 {
343     return avifRGBFormatHasAlpha(format) ? 4 : 3;
344 }
345 
avifRGBImagePixelSize(const avifRGBImage * rgb)346 uint32_t avifRGBImagePixelSize(const avifRGBImage * rgb)
347 {
348     return avifRGBFormatChannelCount(rgb->format) * ((rgb->depth > 8) ? 2 : 1);
349 }
350 
avifRGBImageSetDefaults(avifRGBImage * rgb,const avifImage * image)351 void avifRGBImageSetDefaults(avifRGBImage * rgb, const avifImage * image)
352 {
353     rgb->width = image->width;
354     rgb->height = image->height;
355     rgb->depth = image->depth;
356     rgb->format = AVIF_RGB_FORMAT_RGBA;
357     rgb->chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
358     rgb->ignoreAlpha = AVIF_FALSE;
359     rgb->pixels = NULL;
360     rgb->rowBytes = 0;
361 }
362 
avifRGBImageAllocatePixels(avifRGBImage * rgb)363 void avifRGBImageAllocatePixels(avifRGBImage * rgb)
364 {
365     if (rgb->pixels) {
366         avifFree(rgb->pixels);
367     }
368 
369     rgb->rowBytes = rgb->width * avifRGBImagePixelSize(rgb);
370     rgb->pixels = avifAlloc(rgb->rowBytes * rgb->height);
371 }
372 
avifRGBImageFreePixels(avifRGBImage * rgb)373 void avifRGBImageFreePixels(avifRGBImage * rgb)
374 {
375     if (rgb->pixels) {
376         avifFree(rgb->pixels);
377     }
378 
379     rgb->pixels = NULL;
380     rgb->rowBytes = 0;
381 }
382 
383 // ---------------------------------------------------------------------------
384 // avifCodecSpecificOption
385 
avifStrdup(const char * str)386 static char * avifStrdup(const char * str)
387 {
388     size_t len = strlen(str);
389     char * dup = avifAlloc(len + 1);
390     memcpy(dup, str, len + 1);
391     return dup;
392 }
393 
avifCodecSpecificOptionsCreate(void)394 avifCodecSpecificOptions * avifCodecSpecificOptionsCreate(void)
395 {
396     avifCodecSpecificOptions * ava = avifAlloc(sizeof(avifCodecSpecificOptions));
397     avifArrayCreate(ava, sizeof(avifCodecSpecificOption), 4);
398     return ava;
399 }
400 
avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions)401 void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions)
402 {
403     if (!csOptions) {
404         return;
405     }
406 
407     for (uint32_t i = 0; i < csOptions->count; ++i) {
408         avifCodecSpecificOption * entry = &csOptions->entries[i];
409         avifFree(entry->key);
410         avifFree(entry->value);
411     }
412     avifArrayDestroy(csOptions);
413     avifFree(csOptions);
414 }
415 
avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions,const char * key,const char * value)416 void avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value)
417 {
418     // Check to see if a key must be replaced
419     for (uint32_t i = 0; i < csOptions->count; ++i) {
420         avifCodecSpecificOption * entry = &csOptions->entries[i];
421         if (!strcmp(entry->key, key)) {
422             if (value) {
423                 // Update the value
424                 avifFree(entry->value);
425                 entry->value = avifStrdup(value);
426             } else {
427                 // Delete the value
428                 avifFree(entry->key);
429                 avifFree(entry->value);
430                 --csOptions->count;
431                 if (csOptions->count > 0) {
432                     memmove(&csOptions->entries[i], &csOptions->entries[i + 1], (csOptions->count - i) * csOptions->elementSize);
433                 }
434             }
435             return;
436         }
437     }
438 
439     // Add a new key
440     avifCodecSpecificOption * entry = (avifCodecSpecificOption *)avifArrayPushPtr(csOptions);
441     entry->key = avifStrdup(key);
442     entry->value = avifStrdup(value);
443 }
444 
445 // ---------------------------------------------------------------------------
446 // Codec availability and versions
447 
448 typedef const char * (*versionFunc)(void);
449 typedef avifCodec * (*avifCodecCreateFunc)(void);
450 
451 struct AvailableCodec
452 {
453     avifCodecChoice choice;
454     const char * name;
455     versionFunc version;
456     avifCodecCreateFunc create;
457     uint32_t flags;
458 };
459 
460 // This is the main codec table; it determines all usage/availability in libavif.
461 
462 static struct AvailableCodec availableCodecs[] = {
463 // Ordered by preference (for AUTO)
464 
465 #if defined(AVIF_CODEC_DAV1D)
466     { AVIF_CODEC_CHOICE_DAV1D, "dav1d", avifCodecVersionDav1d, avifCodecCreateDav1d, AVIF_CODEC_FLAG_CAN_DECODE },
467 #endif
468 #if defined(AVIF_CODEC_LIBGAV1)
469     { AVIF_CODEC_CHOICE_LIBGAV1, "libgav1", avifCodecVersionGav1, avifCodecCreateGav1, AVIF_CODEC_FLAG_CAN_DECODE },
470 #endif
471 #if defined(AVIF_CODEC_AOM)
472     { AVIF_CODEC_CHOICE_AOM,
473       "aom",
474       avifCodecVersionAOM,
475       avifCodecCreateAOM,
476 #if defined(AVIF_CODEC_AOM_DECODE) && defined(AVIF_CODEC_AOM_ENCODE)
477       AVIF_CODEC_FLAG_CAN_DECODE | AVIF_CODEC_FLAG_CAN_ENCODE
478 #elif defined(AVIF_CODEC_AOM_DECODE)
479       AVIF_CODEC_FLAG_CAN_DECODE
480 #elif defined(AVIF_CODEC_AOM_ENCODE)
481       AVIF_CODEC_FLAG_CAN_ENCODE
482 #else
483 #error AVIF_CODEC_AOM_DECODE or AVIF_CODEC_AOM_ENCODE must be defined
484 #endif
485     },
486 #endif
487 #if defined(AVIF_CODEC_RAV1E)
488     { AVIF_CODEC_CHOICE_RAV1E, "rav1e", avifCodecVersionRav1e, avifCodecCreateRav1e, AVIF_CODEC_FLAG_CAN_ENCODE },
489 #endif
490 #if defined(AVIF_CODEC_SVT)
491     { AVIF_CODEC_CHOICE_SVT, "svt", avifCodecVersionSvt, avifCodecCreateSvt, AVIF_CODEC_FLAG_CAN_ENCODE },
492 #endif
493     { AVIF_CODEC_CHOICE_AUTO, NULL, NULL, NULL, 0 }
494 };
495 
496 static const int availableCodecsCount = (sizeof(availableCodecs) / sizeof(availableCodecs[0])) - 1;
497 
findAvailableCodec(avifCodecChoice choice,uint32_t requiredFlags)498 static struct AvailableCodec * findAvailableCodec(avifCodecChoice choice, uint32_t requiredFlags)
499 {
500     for (int i = 0; i < availableCodecsCount; ++i) {
501         if ((choice != AVIF_CODEC_CHOICE_AUTO) && (availableCodecs[i].choice != choice)) {
502             continue;
503         }
504         if (requiredFlags && ((availableCodecs[i].flags & requiredFlags) != requiredFlags)) {
505             continue;
506         }
507         return &availableCodecs[i];
508     }
509     return NULL;
510 }
511 
avifCodecName(avifCodecChoice choice,uint32_t requiredFlags)512 const char * avifCodecName(avifCodecChoice choice, uint32_t requiredFlags)
513 {
514     struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags);
515     if (availableCodec) {
516         return availableCodec->name;
517     }
518     return NULL;
519 }
520 
avifCodecChoiceFromName(const char * name)521 avifCodecChoice avifCodecChoiceFromName(const char * name)
522 {
523     for (int i = 0; i < availableCodecsCount; ++i) {
524         if (!strcmp(availableCodecs[i].name, name)) {
525             return availableCodecs[i].choice;
526         }
527     }
528     return AVIF_CODEC_CHOICE_AUTO;
529 }
530 
avifCodecCreate(avifCodecChoice choice,uint32_t requiredFlags)531 avifCodec * avifCodecCreate(avifCodecChoice choice, uint32_t requiredFlags)
532 {
533     struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags);
534     if (availableCodec) {
535         return availableCodec->create();
536     }
537     return NULL;
538 }
539 
append(char ** writePos,size_t * remainingLen,const char * appendStr)540 static void append(char ** writePos, size_t * remainingLen, const char * appendStr)
541 {
542     size_t appendLen = strlen(appendStr);
543     if (appendLen > *remainingLen) {
544         appendLen = *remainingLen;
545     }
546 
547     memcpy(*writePos, appendStr, appendLen);
548     *remainingLen -= appendLen;
549     *writePos += appendLen;
550     *(*writePos) = 0;
551 }
552 
avifCodecVersions(char outBuffer[256])553 void avifCodecVersions(char outBuffer[256])
554 {
555     size_t remainingLen = 255;
556     char * writePos = outBuffer;
557     *writePos = 0;
558 
559     for (int i = 0; i < availableCodecsCount; ++i) {
560         if (i > 0) {
561             append(&writePos, &remainingLen, ", ");
562         }
563         append(&writePos, &remainingLen, availableCodecs[i].name);
564         if ((availableCodecs[i].flags & (AVIF_CODEC_FLAG_CAN_ENCODE | AVIF_CODEC_FLAG_CAN_DECODE)) ==
565             (AVIF_CODEC_FLAG_CAN_ENCODE | AVIF_CODEC_FLAG_CAN_DECODE)) {
566             append(&writePos, &remainingLen, " [enc/dec]");
567         } else if (availableCodecs[i].flags & AVIF_CODEC_FLAG_CAN_ENCODE) {
568             append(&writePos, &remainingLen, " [enc]");
569         } else if (availableCodecs[i].flags & AVIF_CODEC_FLAG_CAN_DECODE) {
570             append(&writePos, &remainingLen, " [dec]");
571         }
572         append(&writePos, &remainingLen, ":");
573         append(&writePos, &remainingLen, availableCodecs[i].version());
574     }
575 }
576