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
41#define NEW_DYNAMIC_CTX_SIZE 2048
45#define MAX_QUALITY 100
49#define MIN_TILE_AREA (512 * 512)
63#define alpha7BitTo8Bit(alpha7Bit) \
66 255 - ((alpha7Bit << 1) + (alpha7Bit >> 6)))
68#define alpha8BitTo7Bit(alpha8Bit) (gdAlphaMax - (alpha8Bit >> 1))
79static int quality2Quantizer(
int quality) {
80 int clampedQuality =
CLAMP(quality, 0, MAX_QUALITY);
82 float scaleFactor = (float) AVIF_QUANTIZER_WORST_QUALITY / (
float) MAX_QUALITY;
84 return round(scaleFactor * (MAX_QUALITY - clampedQuality));
94static avifBool setEncoderTilesAndThreads(avifEncoder *encoder, avifRGBImage *rgb) {
95 int imageArea, tiles, tilesLog2, encoderTiles;
98 imageArea = rgb->width * rgb->height;
100 tiles = (int)
ceil((
double) imageArea / MIN_TILE_AREA);
101 tiles =
MIN(tiles, MAX_TILES);
102 tiles =
MIN(tiles, MAX_THREADS);
106 tilesLog2 =
floor(log2(tiles));
111 if (rgb->width >= rgb->height) {
112 encoder->tileRowsLog2 = tilesLog2 / 2;
113 encoder->tileColsLog2 = tilesLog2 - encoder->tileRowsLog2;
115 encoder->tileColsLog2 = tilesLog2 / 2;
116 encoder->tileRowsLog2 = tilesLog2 - encoder->tileColsLog2;
120 encoderTiles = (1 << encoder->tileRowsLog2) * (1 << encoder->tileColsLog2);
121 encoder->maxThreads = encoderTiles;
129static avifBool isAvifSrgbImage(avifImage *avifIm) {
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)
143static avifBool isAvifError(avifResult
result,
const char *
msg) {
144 if (
result != AVIF_RESULT_OK) {
153typedef struct avifIOCtxReader {
171static avifResult readFromCtx(avifIO *
io, uint32_t readFlags, uint64_t
offset,
size_t size, avifROData *
out)
174 avifIOCtxReader *reader = (avifIOCtxReader *)
io;
177 if (readFlags != 0) {
178 return AVIF_RESULT_IO_ERROR;
184 return AVIF_RESULT_IO_ERROR;
187 if (!ctx->seek(ctx, (
int)
offset))
188 return AVIF_RESULT_IO_ERROR;
190 if (
size > reader->rodata.size) {
191 reader->rodata.data =
gdRealloc((
void *) reader->rodata.data,
size);
192 reader->rodata.size =
size;
194 if (!reader->rodata.data) {
195 gd_error(
"avif error - couldn't allocate memory");
196 return AVIF_RESULT_UNKNOWN_ERROR;
201 int charsRead = ctx->getBuf(ctx, (
void *) reader->rodata.data, (
int)
size);
203 return AVIF_RESULT_IO_ERROR;
206 out->data = reader->rodata.data;
207 out->size = charsRead;
208 return AVIF_RESULT_OK;
212static void destroyAvifIO(
struct avifIO *
io) {
213 avifIOCtxReader *reader = (avifIOCtxReader *)
io;
214 if (reader->rodata.data !=
NULL) {
215 gdFree((
void *) reader->rodata.data);
230static avifIO *createAvifIOFromCtx(
gdIOCtx *ctx) {
231 struct avifIOCtxReader *reader;
238 reader->io.persistent = AVIF_FALSE;
239 reader->io.read = readFromCtx;
240 reader->io.write =
NULL;
241 reader->io.destroy = destroyAvifIO;
242 reader->io.sizeHint = 0;
243 reader->io.data = ctx;
244 reader->rodata.data =
NULL;
245 reader->rodata.size = 0;
247 return (avifIO *) reader;
355 avifDecoder *decoder;
361 decoder = avifDecoderCreate();
368#if AVIF_VERSION >= 90100
369 decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED;
372 io = createAvifIOFromCtx(ctx);
374 gd_error(
"avif error - Could not allocate memory");
378 avifDecoderSetIO(decoder,
io);
380 result = avifDecoderParse(decoder);
381 if (isAvifError(
result,
"Could not parse image"))
385 result = avifDecoderNextImage(decoder);
386 if (isAvifError(
result,
"Could not decode image"))
389 if (!isAvifSrgbImage(decoder->image))
394 avifRGBImageSetDefaults(&rgb, decoder->image);
396#if AVIF_VERSION >= 1000000
397 result = avifRGBImageAllocatePixels(&rgb);
398 if (isAvifError(
result,
"Allocating RGB pixels failed"))
401 avifRGBImageAllocatePixels(&rgb);
404 result = avifImageYUVToRGB(decoder->image, &rgb);
405 if (isAvifError(
result,
"Conversion from YUV to RGB failed"))
410 gd_error(
"avif error - Could not create GD truecolor image");
418 uint8_t *
p = rgb.pixels;
420 for (y = 0; y < decoder->image->height; y++) {
421 for (x = 0; x < decoder->image->width; x++) {
425 uint8_t
a = alpha8BitTo7Bit(*(
p++));
432 avifDecoderDestroy(decoder);
435 avifRGBImageFreePixels(&rgb);
495 avifRWData avifOutput = AVIF_DATA_EMPTY;
496 avifBool lossless = quality == 100;
497 avifEncoder *encoder =
NULL;
507 gd_error(
"avif error - avif doesn't support palette images");
512 gd_error(
"avif error - image dimensions must not be zero");
517 gd_error(
"avif error - image dimensions are too large");
521 speed =
CLAMP(speed, AVIF_SPEED_SLOWEST, AVIF_SPEED_FASTEST);
523 avifPixelFormat subsampling = quality >= HIGH_QUALITY_SUBSAMPLING_THRESHOLD ?
524 CHROMA_SUBAMPLING_HIGH_QUALITY : CHROMA_SUBSAMPLING_DEFAULT;
531#if AVIF_VERSION >= 1000000
532 if (avifIm ==
NULL) {
533 gd_error(
"avif error - Creating image failed\n");
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;
541 avifRGBImageSetDefaults(&rgb, avifIm);
543#if AVIF_VERSION >= 1000000
544 result = avifRGBImageAllocatePixels(&rgb);
545 if (isAvifError(
result,
"Allocating RGB pixels failed"))
548 avifRGBImageAllocatePixels(&rgb);
555 for (y = 0; y < rgb.height; y++) {
556 for (x = 0; x < rgb.width; x++) {
568 result = avifImageRGBToYUV(avifIm, &rgb);
569 if (isAvifError(
result,
"Could not convert image to YUV"))
574 encoder = avifEncoderCreate();
575#if AVIF_VERSION >= 1000000
576 if (encoder ==
NULL) {
577 gd_error(
"avif error - Creating encoder failed\n");
581 int quantizerQuality = quality == QUALITY_DEFAULT ?
582 QUANTIZER_DEFAULT : quality2Quantizer(quality);
584 encoder->minQuantizer = quantizerQuality;
585 encoder->maxQuantizer = quantizerQuality;
586 encoder->minQuantizerAlpha = quantizerQuality;
587 encoder->maxQuantizerAlpha = quantizerQuality;
588 encoder->speed = speed;
590 if (!setEncoderTilesAndThreads(encoder, &rgb))
594 result = avifEncoderAddImage(encoder, avifIm, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
595 if (isAvifError(
result,
"Could not encode image"))
598 result = avifEncoderFinish(encoder, &avifOutput);
599 if (isAvifError(
result,
"Could not finish encoding"))
604 gdPutBuf(avifOutput.data, avifOutput.size, outfile);
608 avifRGBImageFreePixels(&rgb);
611 avifEncoderDestroy(encoder);
614 avifRWDataFree(&avifOutput);
617 avifImageDestroy(avifIm);
int overflow2(int a, int b)
#define gdTrueColorGetBlue(c)
void gdImageAvifEx(gdImagePtr im, FILE *outfile, int quality, int speed)
void * gdDPExtractData(struct gdIOCtx *ctx, int *size)
#define gdTrueColorGetGreen(c)
#define gdImageTrueColor(im)
#define gdTrueColorGetAlpha(c)
void * gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed)
gdImagePtr gdImageCreateFromAvifCtx(gdIOCtx *infile)
void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed)
#define gdTrueColorGetRed(c)
void gdImageAvif(gdImagePtr im, FILE *outfile)
gdImagePtr gdImageCreateFromAvif(FILE *infile)
gdIOCtx * gdNewFileCtx(FILE *)
#define gdTrueColorAlpha(r, g, b, a)
gdImagePtr gdImageCreateFromAvifPtr(int size, void *data)
gdIOCtx * gdNewDynamicCtx(int, void *)
struct gdImageStruct gdImage
void * gdImageAvifPtr(gdImagePtr im, int *size)
gdIOCtx * gdNewDynamicCtxEx(int size, void *data, int freeFlag)
#define CLAMP(x, low, high)
int gdPutBuf(const void *buf, int size, gdIOCtx *ctx)
#define gdRealloc(ptr, size)
#define round(tables, k1, k2)
void gd_error(const char *format,...)
void gd_error_ex(int priority, const char *format,...)
gdImagePtr gdImageCreateTrueColor(int sx, int sy)
struct @234323133100145062121301312242002332057146367313 io[PHPDBG_IO_FDS]
void(* gd_free)(struct gdIOCtx *)