1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <limits.h>
9 #include <math.h>
10
11 #include "gd.h"
12 #include "gd_errors.h"
13 #include "gdhelpers.h"
14 #include "gd_intern.h"
15
16 #ifdef HAVE_LIBAVIF
17 #include <avif/avif.h>
18
19 /*
20 Define defaults for encoding images:
21 CHROMA_SUBSAMPLING_DEFAULT: 4:2:0 is commonly used for Chroma subsampling.
22 CHROMA_SUBAMPLING_HIGH_QUALITY: Use 4:4:4, or no subsampling, when a sufficient high quality is requested.
23 SUBAMPLING_HIGH_QUALITY_THRESHOLD: At or above this value, use CHROMA_SUBAMPLING_HIGH_QUALITY
24 QUANTIZER_DEFAULT:
25 We need more testing to really know what quantizer settings are optimal,
26 but teams at Google have been using maximum=30 as a starting point.
27 QUALITY_DEFAULT: following gd conventions, -1 indicates the default.
28 SPEED_DEFAULT:
29 AVIF_SPEED_DEFAULT is simply the default encoding speed of the AV1 codec.
30 This could be as slow as 0. So we use 6, which is currently considered to be a fine default.
31 */
32
33 #define CHROMA_SUBSAMPLING_DEFAULT AVIF_PIXEL_FORMAT_YUV420
34 #define CHROMA_SUBAMPLING_HIGH_QUALITY AVIF_PIXEL_FORMAT_YUV444
35 #define HIGH_QUALITY_SUBSAMPLING_THRESHOLD 90
36 #define QUANTIZER_DEFAULT 30
37 #define QUALITY_DEFAULT -1
38 #define SPEED_DEFAULT 6
39
40 // This initial size for the gdIOCtx is standard among GD image conversion functions.
41 #define NEW_DYNAMIC_CTX_SIZE 2048
42
43 // Our quality param ranges from 0 to 100.
44 // To calculate quality, we convert from AVIF's quantizer scale, which runs from 63 to 0.
45 #define MAX_QUALITY 100
46
47 // These constants are for computing the number of tiles and threads to use during encoding.
48 // Maximum threads are from libavif/contrib/gkd-pixbuf/loader.c.
49 #define MIN_TILE_AREA (512 * 512)
50 #define MAX_TILES 8
51 #define MAX_THREADS 64
52
53 /*** Macros ***/
54
55 /*
56 From gd_png.c:
57 convert the 7-bit alpha channel to an 8-bit alpha channel.
58 We do a little bit-flipping magic, repeating the MSB
59 as the LSB, to ensure that 0 maps to 0 and
60 127 maps to 255. We also have to invert to match
61 PNG's convention in which 255 is opaque.
62 */
63 #define alpha7BitTo8Bit(alpha7Bit) \
64 (alpha7Bit == 127 ? \
65 0 : \
66 255 - ((alpha7Bit << 1) + (alpha7Bit >> 6)))
67
68 #define alpha8BitTo7Bit(alpha8Bit) (gdAlphaMax - (alpha8Bit >> 1))
69
70
71 /*** Helper functions ***/
72
73 /* Convert the quality param we expose to the quantity params used by libavif.
74 The *Quantizer* params values can range from 0 to 63, with 0 = highest quality and 63 = worst.
75 We make the scale 0-100, and we reverse this, so that 0 = worst quality and 100 = highest.
76
77 Values below 0 are set to 0, and values below MAX_QUALITY are set to MAX_QUALITY.
78 */
quality2Quantizer(int quality)79 static int quality2Quantizer(int quality) {
80 int clampedQuality = CLAMP(quality, 0, MAX_QUALITY);
81
82 float scaleFactor = (float) AVIF_QUANTIZER_WORST_QUALITY / (float) MAX_QUALITY;
83
84 return round(scaleFactor * (MAX_QUALITY - clampedQuality));
85 }
86
87 /*
88 As of February 2021, this algorithm reflects the latest research on how many tiles
89 and threads to include for a given image size.
90 This is subject to change as research continues.
91
92 Returns false if there was an error, true if all was well.
93 */
setEncoderTilesAndThreads(avifEncoder * encoder,avifRGBImage * rgb)94 static avifBool setEncoderTilesAndThreads(avifEncoder *encoder, avifRGBImage *rgb) {
95 int imageArea, tiles, tilesLog2, encoderTiles;
96
97 // _gdImageAvifCtx(), the calling function, checks this operation for overflow
98 imageArea = rgb->width * rgb->height;
99
100 tiles = (int) ceil((double) imageArea / MIN_TILE_AREA);
101 tiles = MIN(tiles, MAX_TILES);
102 tiles = MIN(tiles, MAX_THREADS);
103
104 // The number of tiles in any dimension will always be a power of 2. We can only specify log(2)tiles.
105
106 tilesLog2 = floor(log2(tiles));
107
108 // If the image's width is greater than the height, use more tile columns
109 // than tile rows to make the tile size close to a square.
110
111 if (rgb->width >= rgb->height) {
112 encoder->tileRowsLog2 = tilesLog2 / 2;
113 encoder->tileColsLog2 = tilesLog2 - encoder->tileRowsLog2;
114 } else {
115 encoder->tileColsLog2 = tilesLog2 / 2;
116 encoder->tileRowsLog2 = tilesLog2 - encoder->tileColsLog2;
117 }
118
119 // It's good to have one thread per tile.
120 encoderTiles = (1 << encoder->tileRowsLog2) * (1 << encoder->tileColsLog2);
121 encoder->maxThreads = encoderTiles;
122
123 return AVIF_TRUE;
124 }
125
126 /*
127 We can handle AVIF images whose color profile is sRGB, or whose color profile isn't set.
128 */
isAvifSrgbImage(avifImage * avifIm)129 static avifBool isAvifSrgbImage(avifImage *avifIm) {
130 return
131 (avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_BT709 ||
132 avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) &&
133 (avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_SRGB ||
134 avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED)
135 ;
136 }
137
138 /*
139 Check the result from an Avif function to see if it's an error.
140 If so, decode the error and output it, and return true.
141 Otherwise, return false.
142 */
isAvifError(avifResult result,const char * msg)143 static avifBool isAvifError(avifResult result, const char *msg) {
144 if (result != AVIF_RESULT_OK) {
145 gd_error("avif error - %s: %s\n", msg, avifResultToString(result));
146 return AVIF_TRUE;
147 }
148
149 return AVIF_FALSE;
150 }
151
152
153 typedef struct avifIOCtxReader {
154 avifIO io; // this must be the first member for easy casting to avifIO*
155 avifROData rodata;
156 } avifIOCtxReader;
157
158 /*
159 <readfromCtx> implements the avifIOReadFunc interface by calling the relevant functions
160 in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead().
161 We don't know whether we're reading from a file or from memory. We don't have to know,
162 since we rely on the helper functions in the gdIOCtx.
163 We assume we've stashed the gdIOCtx in io->data, as we do in createAvifIOFromCtx().
164
165 We ignore readFlags, just as the avifIO*ReaderRead() functions do.
166
167 If there's a problem, this returns an avifResult error.
168 If things go well, return AVIF_RESULT_OK.
169 Of course these AVIF codes shouldn't be returned by any top-level GD function.
170 */
readFromCtx(avifIO * io,uint32_t readFlags,uint64_t offset,size_t size,avifROData * out)171 static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, avifROData *out)
172 {
173 gdIOCtx *ctx = (gdIOCtx *) io->data;
174 avifIOCtxReader *reader = (avifIOCtxReader *) io;
175
176 // readFlags is unsupported
177 if (readFlags != 0) {
178 return AVIF_RESULT_IO_ERROR;
179 }
180
181 // TODO: if we set sizeHint, this will be more efficient.
182
183 if (offset > INT_MAX || size > INT_MAX)
184 return AVIF_RESULT_IO_ERROR;
185
186 // Try to seek offset bytes forward. If we pass the end of the buffer, throw an error.
187 if (!ctx->seek(ctx, (int) offset))
188 return AVIF_RESULT_IO_ERROR;
189
190 if (size > reader->rodata.size) {
191 reader->rodata.data = gdRealloc((void *) reader->rodata.data, size);
192 reader->rodata.size = size;
193 }
194 if (!reader->rodata.data) {
195 gd_error("avif error - couldn't allocate memory");
196 return AVIF_RESULT_UNKNOWN_ERROR;
197 }
198
199 // Read the number of bytes requested.
200 // If getBuf() returns a negative value, that means there was an error.
201 int charsRead = ctx->getBuf(ctx, (void *) reader->rodata.data, (int) size);
202 if (charsRead < 0) {
203 return AVIF_RESULT_IO_ERROR;
204 }
205
206 out->data = reader->rodata.data;
207 out->size = charsRead;
208 return AVIF_RESULT_OK;
209 }
210
211 // avif.h says this is optional, but it seemed easy to implement.
destroyAvifIO(struct avifIO * io)212 static void destroyAvifIO(struct avifIO *io) {
213 avifIOCtxReader *reader = (avifIOCtxReader *) io;
214 if (reader->rodata.data != NULL) {
215 gdFree((void *) reader->rodata.data);
216 }
217 gdFree(reader);
218 }
219
220 /* Set up an avifIO object.
221 The functions in the gdIOCtx struct may point either to a file or a memory buffer.
222 To us, that's immaterial.
223 Our task is simply to assign avifIO functions to the proper functions from gdIOCtx.
224 The destroy function needs to destroy the avifIO object and anything else it uses.
225
226 Returns NULL if memory for the object can't be allocated.
227 */
228
229 // TODO: can we get sizeHint somehow?
createAvifIOFromCtx(gdIOCtx * ctx)230 static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) {
231 struct avifIOCtxReader *reader;
232
233 reader = gdMalloc(sizeof(*reader));
234 if (reader == NULL)
235 return NULL;
236
237 // TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary?
238 reader->io.persistent = AVIF_FALSE;
239 reader->io.read = readFromCtx;
240 reader->io.write = NULL; // this function is currently unused; see avif.h
241 reader->io.destroy = destroyAvifIO;
242 reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
243 reader->io.data = ctx;
244 reader->rodata.data = NULL;
245 reader->rodata.size = 0;
246
247 return (avifIO *) reader;
248 }
249
250
251 /*** Decoding functions ***/
252
253 /*
254 Function: gdImageCreateFromAvif
255
256 <gdImageCreateFromAvif> is called to load truecolor images from
257 AVIF format files. Invoke <gdImageCreateFromAvif> with an
258 already opened pointer to a file containing the desired
259 image. <gdImageCreateFromAvif> returns a <gdImagePtr> to the new
260 truecolor image, or NULL if unable to load the image (most often
261 because the file is corrupt or does not contain a AVIF
262 image). <gdImageCreateFromAvif> does not close the file.
263
264 This function creates a gdIOCtx struct from the file pointer it's passed.
265 And then it relies on <gdImageCreateFromAvifCtx> to do the real decoding work.
266 If the file contains an image sequence, we simply read the first one, discarding the rest.
267
268 Variants:
269
270 <gdImageCreateFromAvifPtr> creates an image from AVIF data
271 already in memory.
272
273 <gdImageCreateFromAvifCtx> reads data from the function
274 pointers in a <gdIOCtx> structure.
275
276 Parameters:
277
278 infile - pointer to the input file
279
280 Returns:
281
282 A pointer to the new truecolor image. This will need to be
283 destroyed with <gdImageDestroy> once it is no longer needed.
284
285 On error, returns 0.
286 */
gdImageCreateFromAvif(FILE * infile)287 gdImagePtr gdImageCreateFromAvif(FILE *infile)
288 {
289 gdImagePtr im;
290 gdIOCtx *ctx = gdNewFileCtx(infile);
291
292 if (!ctx)
293 return NULL;
294
295 im = gdImageCreateFromAvifCtx(ctx);
296 ctx->gd_free(ctx);
297
298 return im;
299 }
300
301 /*
302 Function: gdImageCreateFromAvifPtr
303
304 See <gdImageCreateFromAvif>.
305
306 Parameters:
307
308 size - size of Avif data in bytes.
309 data - pointer to Avif data.
310 */
gdImageCreateFromAvifPtr(int size,void * data)311 gdImagePtr gdImageCreateFromAvifPtr(int size, void *data)
312 {
313 gdImagePtr im;
314 gdIOCtx *ctx = gdNewDynamicCtxEx(size, data, 0);
315
316 if (!ctx)
317 return 0;
318
319 im = gdImageCreateFromAvifCtx(ctx);
320 ctx->gd_free(ctx);
321
322 return im;
323 }
324
325 /*
326 Function: gdImageCreateFromAvifCtx
327
328 See <gdImageCreateFromAvif>.
329
330 Additional details: the AVIF library comes with functions to create an IO object from
331 a file and from a memory pointer. Of course, it doesn't have a way to create an IO object
332 from a gdIOCtx. So, here, we use our own helper function, <createAvifIOfromCtx>.
333
334 Otherwise, we create the image by calling AVIF library functions in order:
335 * avifDecoderCreate(), to create the decoder
336 * avifDecoderSetIO(), to tell libavif how to read from our data structure
337 * avifDecoderParse(), to parse the image
338 * avifDecoderNextImage(), to read the first image from the decoder
339 * avifRGBImageSetDefaults(), to create the avifRGBImage
340 * avifRGBImageAllocatePixels(), to allocate memory for the pixels
341 * avifImageYUVToRGB(), to convert YUV to RGB
342
343 Finally, we create a new gd image and copy over the pixel data.
344
345 Parameters:
346
347 ctx - a gdIOCtx struct
348 */
gdImageCreateFromAvifCtx(gdIOCtx * ctx)349 gdImagePtr gdImageCreateFromAvifCtx (gdIOCtx *ctx)
350 {
351 uint32_t x, y;
352 gdImage *im = NULL;
353 avifResult result;
354 avifIO *io;
355 avifDecoder *decoder;
356 avifRGBImage rgb;
357
358 // this lets us know that memory hasn't been allocated yet for the pixels
359 rgb.pixels = NULL;
360
361 decoder = avifDecoderCreate();
362
363 // Check if libavif version is >= 0.9.1
364 // If so, allow the PixelInformationProperty ('pixi') to be missing in AV1 image
365 // items. libheif v1.11.0 or older does not add the 'pixi' item property to
366 // AV1 image items. (This issue has been corrected in libheif v1.12.0.)
367
368 #if AVIF_VERSION >= 90100
369 decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED;
370 #endif
371
372 io = createAvifIOFromCtx(ctx);
373 if (!io) {
374 gd_error("avif error - Could not allocate memory");
375 goto cleanup;
376 }
377
378 avifDecoderSetIO(decoder, io);
379
380 result = avifDecoderParse(decoder);
381 if (isAvifError(result, "Could not parse image"))
382 goto cleanup;
383
384 // Note again that, for an image sequence, we read only the first image, ignoring the rest.
385 result = avifDecoderNextImage(decoder);
386 if (isAvifError(result, "Could not decode image"))
387 goto cleanup;
388
389 if (!isAvifSrgbImage(decoder->image))
390 gd_error_ex(GD_NOTICE, "Image's color profile is not sRGB");
391
392 // Set up the avifRGBImage, and convert it from YUV to an 8-bit RGB image.
393 // (While AVIF image pixel depth can be 8, 10, or 12 bits, GD truecolor images are 8-bit.)
394 avifRGBImageSetDefaults(&rgb, decoder->image);
395 rgb.depth = 8;
396 #if AVIF_VERSION >= 1000000
397 result = avifRGBImageAllocatePixels(&rgb);
398 if (isAvifError(result, "Allocating RGB pixels failed"))
399 goto cleanup;
400 #else
401 avifRGBImageAllocatePixels(&rgb);
402 #endif
403
404 result = avifImageYUVToRGB(decoder->image, &rgb);
405 if (isAvifError(result, "Conversion from YUV to RGB failed"))
406 goto cleanup;
407
408 im = gdImageCreateTrueColor(decoder->image->width, decoder->image->height);
409 if (!im) {
410 gd_error("avif error - Could not create GD truecolor image");
411 goto cleanup;
412 }
413
414 im->saveAlphaFlag = 1;
415
416 // Read the pixels from the AVIF image and copy them into the GD image.
417
418 uint8_t *p = rgb.pixels;
419
420 for (y = 0; y < decoder->image->height; y++) {
421 for (x = 0; x < decoder->image->width; x++) {
422 uint8_t r = *(p++);
423 uint8_t g = *(p++);
424 uint8_t b = *(p++);
425 uint8_t a = alpha8BitTo7Bit(*(p++));
426 im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a);
427 }
428 }
429
430 cleanup:
431 // if io has been allocated, this frees it
432 avifDecoderDestroy(decoder);
433
434 if (rgb.pixels)
435 avifRGBImageFreePixels(&rgb);
436
437 return im;
438 }
439
440
441 /*** Encoding functions ***/
442
443 /*
444 Function: gdImageAvifEx
445
446 <gdImageAvifEx> outputs the specified image to the specified file in
447 AVIF format. The file must be open for writing. Under MSDOS and
448 all versions of Windows, it is important to use "wb" as opposed to
449 simply "w" as the mode when opening the file, and under Unix there
450 is no penalty for doing so. <gdImageAvifEx> does not close the file;
451 your code must do so.
452
453 Variants:
454
455 <gdImageAvifEx> writes the image to a file, encoding with the default quality and speed.
456
457 <gdImageAvifPtrEx> stores the image in RAM.
458
459 <gdImageAvifPtr> stores the image in RAM, encoding with the default quality and speed.
460
461 <gdImageAvifCtx> stores the image using a <gdIOCtx> struct.
462
463 Parameters:
464
465 im - The image to save.
466 outFile - The FILE pointer to write to.
467 quality - Compression quality (0-100). 0 is lowest-quality, 100 is highest.
468 speed - The speed of compression (0-10). 0 is slowest, 10 is fastest.
469
470 Notes on parameters:
471 quality - If quality = -1, we use a default quality as defined in QUALITY_DEFAULT.
472 For information on how we convert this quality to libavif's quantity param, see <quality2Quantizer>.
473
474 speed - At slower speeds, encoding may be quite slow. Use judiciously.
475
476 Qualities or speeds that are lower than the minimum value get clamped to the minimum value,
477 and qualities or speeds that are lower than the maximum value get clamped to the maxmum value.
478 Note that AVIF_SPEED_DEFAULT is -1. If we ever set SPEED_DEFAULT = AVIF_SPEED_DEFAULT,
479 we'd want to add a conditional to ensure that value doesn't get clamped.
480
481
482 Returns:
483
484 * for <gdImageAvifEx>, <gdImageAvif>, and <gdImageAvifCtx>, nothing.
485 * for <gdImageAvifPtrEx> and <gdImageAvifPtr>, a pointer to the image in memory.
486 */
487
488 /*
489 If we're passed the QUALITY_DEFAULT of -1, set the quantizer params to QUANTIZER_DEFAULT.
490 */
gdImageAvifCtx(gdImagePtr im,gdIOCtx * outfile,int quality,int speed)491 void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed)
492 {
493 avifResult result;
494 avifRGBImage rgb;
495 avifRWData avifOutput = AVIF_DATA_EMPTY;
496 avifBool lossless = quality == 100;
497 avifEncoder *encoder = NULL;
498
499 uint32_t val;
500 uint8_t *p;
501 uint32_t x, y;
502
503 if (im == NULL)
504 return;
505
506 if (!gdImageTrueColor(im)) {
507 gd_error("avif error - avif doesn't support palette images");
508 return;
509 }
510
511 if (!gdImageSX(im) || !gdImageSY(im)) {
512 gd_error("avif error - image dimensions must not be zero");
513 return;
514 }
515
516 if (overflow2(gdImageSX(im), gdImageSY(im))) {
517 gd_error("avif error - image dimensions are too large");
518 return;
519 }
520
521 speed = CLAMP(speed, AVIF_SPEED_SLOWEST, AVIF_SPEED_FASTEST);
522
523 avifPixelFormat subsampling = quality >= HIGH_QUALITY_SUBSAMPLING_THRESHOLD ?
524 CHROMA_SUBAMPLING_HIGH_QUALITY : CHROMA_SUBSAMPLING_DEFAULT;
525
526 // Create the AVIF image.
527 // Set the ICC to sRGB, as that's what gd supports right now.
528 // Note that MATRIX_COEFFICIENTS_IDENTITY enables lossless conversion from RGB to YUV.
529
530 avifImage *avifIm = avifImageCreate(gdImageSX(im), gdImageSY(im), 8, subsampling);
531 #if AVIF_VERSION >= 1000000
532 if (avifIm == NULL) {
533 gd_error("avif error - Creating image failed\n");
534 goto cleanup;
535 }
536 #endif
537 avifIm->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
538 avifIm->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
539 avifIm->matrixCoefficients = lossless ? AVIF_MATRIX_COEFFICIENTS_IDENTITY : AVIF_MATRIX_COEFFICIENTS_BT709;
540
541 avifRGBImageSetDefaults(&rgb, avifIm);
542 // this allocates memory, and sets rgb.rowBytes and rgb.pixels.
543 #if AVIF_VERSION >= 1000000
544 result = avifRGBImageAllocatePixels(&rgb);
545 if (isAvifError(result, "Allocating RGB pixels failed"))
546 goto cleanup;
547 #else
548 avifRGBImageAllocatePixels(&rgb);
549 #endif
550
551 // Parse RGB data from the GD image, and copy it into the AVIF RGB image.
552 // Convert 7-bit GD alpha channel values to 8-bit AVIF values.
553
554 p = rgb.pixels;
555 for (y = 0; y < rgb.height; y++) {
556 for (x = 0; x < rgb.width; x++) {
557 val = im->tpixels[y][x];
558
559 *(p++) = gdTrueColorGetRed(val);
560 *(p++) = gdTrueColorGetGreen(val);
561 *(p++) = gdTrueColorGetBlue(val);
562 *(p++) = alpha7BitTo8Bit(gdTrueColorGetAlpha(val));
563 }
564 }
565
566 // Convert the RGB image to YUV.
567
568 result = avifImageRGBToYUV(avifIm, &rgb);
569 if (isAvifError(result, "Could not convert image to YUV"))
570 goto cleanup;
571
572 // Encode the image in AVIF format.
573
574 encoder = avifEncoderCreate();
575 #if AVIF_VERSION >= 1000000
576 if (encoder == NULL) {
577 gd_error("avif error - Creating encoder failed\n");
578 goto cleanup;
579 }
580 #endif
581 int quantizerQuality = quality == QUALITY_DEFAULT ?
582 QUANTIZER_DEFAULT : quality2Quantizer(quality);
583
584 encoder->minQuantizer = quantizerQuality;
585 encoder->maxQuantizer = quantizerQuality;
586 encoder->minQuantizerAlpha = quantizerQuality;
587 encoder->maxQuantizerAlpha = quantizerQuality;
588 encoder->speed = speed;
589
590 if (!setEncoderTilesAndThreads(encoder, &rgb))
591 goto cleanup;
592
593 //TODO: is there a reason to use timeSscales != 1?
594 result = avifEncoderAddImage(encoder, avifIm, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
595 if (isAvifError(result, "Could not encode image"))
596 goto cleanup;
597
598 result = avifEncoderFinish(encoder, &avifOutput);
599 if (isAvifError(result, "Could not finish encoding"))
600 goto cleanup;
601
602 // Write the AVIF image bytes to the GD ctx.
603
604 gdPutBuf(avifOutput.data, avifOutput.size, outfile);
605
606 cleanup:
607 if (rgb.pixels)
608 avifRGBImageFreePixels(&rgb);
609
610 if (encoder)
611 avifEncoderDestroy(encoder);
612
613 if (avifOutput.data)
614 avifRWDataFree(&avifOutput);
615
616 if (avifIm)
617 avifImageDestroy(avifIm);
618 }
619
gdImageAvifEx(gdImagePtr im,FILE * outFile,int quality,int speed)620 void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed)
621 {
622 gdIOCtx *out = gdNewFileCtx(outFile);
623
624 if (out != NULL) {
625 gdImageAvifCtx(im, out, quality, speed);
626 out->gd_free(out);
627 }
628 }
629
gdImageAvif(gdImagePtr im,FILE * outFile)630 void gdImageAvif(gdImagePtr im, FILE *outFile)
631 {
632 gdImageAvifEx(im, outFile, QUALITY_DEFAULT, SPEED_DEFAULT);
633 }
634
gdImageAvifPtrEx(gdImagePtr im,int * size,int quality,int speed)635 void * gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed)
636 {
637 void *rv;
638 gdIOCtx *out = gdNewDynamicCtx(NEW_DYNAMIC_CTX_SIZE, NULL);
639
640 if (out == NULL) {
641 return NULL;
642 }
643
644 gdImageAvifCtx(im, out, quality, speed);
645 rv = gdDPExtractData(out, size);
646
647 out->gd_free(out);
648 return rv;
649 }
650
gdImageAvifPtr(gdImagePtr im,int * size)651 void * gdImageAvifPtr(gdImagePtr im, int *size)
652 {
653 return gdImageAvifPtrEx(im, size, QUALITY_DEFAULT, AVIF_SPEED_DEFAULT);
654 }
655
656 #endif /* HAVE_LIBAVIF */
657