Skip to content

Commit

Permalink
Add avif support to ext/gd
Browse files Browse the repository at this point in the history
This backports avif support from upstream libgd into bundled libgd
and exposes the functionality through new imagecreatefromavif()
and imageavif() functions.

Closes GH-7026.

Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de>
  • Loading branch information
2 people authored and nikic committed Jun 10, 2021
1 parent be92a87 commit 81f6d36
Show file tree
Hide file tree
Showing 16 changed files with 830 additions and 10 deletions.
4 changes: 2 additions & 2 deletions .cirrus.yml
Expand Up @@ -11,10 +11,10 @@ task:
#- sed -i -e 's/quarterly/latest/g' /etc/pkg/FreeBSD.conf
#- pkg upgrade -y
- kldload accf_http
- pkg install -y autoconf bison gmake re2c icu libiconv png freetype2 enchant2 bzip2 krb5 t1lib gmp tidyp libsodium libzip libxml2 libxslt openssl oniguruma pkgconf webp
- pkg install -y autoconf bison gmake re2c icu libiconv png freetype2 enchant2 bzip2 krb5 t1lib gmp tidyp libsodium libzip libxml2 libxslt openssl oniguruma pkgconf webp libavif
script:
- ./buildconf -f
- ./configure --prefix=/usr/local --enable-debug --enable-option-checking=fatal --enable-fpm --with-pdo-sqlite --without-pear --with-bz2 --with-jpeg --with-webp --with-freetype --enable-gd --enable-exif --with-zip --with-zlib --enable-soap --enable-xmlreader --with-xsl --with-libxml --enable-shmop --enable-pcntl --enable-mbstring --with-curl --enable-sockets --with-openssl --with-iconv=/usr/local --enable-bcmath --enable-calendar --enable-ftp --with-kerberos --with-ffi --enable-zend-test --enable-intl --with-mhash --with-sodium --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d
- ./configure --prefix=/usr/local --enable-debug --enable-option-checking=fatal --enable-fpm --with-pdo-sqlite --without-pear --with-bz2 --with-avif --with-jpeg --with-webp --with-freetype --enable-gd --enable-exif --with-zip --with-zlib --enable-soap --enable-xmlreader --with-xsl --with-libxml --enable-shmop --enable-pcntl --enable-mbstring --with-curl --enable-sockets --with-openssl --with-iconv=/usr/local --enable-bcmath --enable-calendar --enable-ftp --with-kerberos --with-ffi --enable-zend-test --enable-intl --with-mhash --with-sodium --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d
- gmake -j2
- mkdir /etc/php.d
- gmake install
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING
Expand Up @@ -201,6 +201,10 @@ PHP 8.1 UPGRADE NOTES
when dynamic pm is selected. The default value is 32 which was the previous
hard coded value.

- GD:
. Avif support is now available through the imagecreatefromavif() and
imageavif() functions, if libgd has been built with avif support.

- hash:
. The following functions have changed signatures:

Expand Down
3 changes: 3 additions & 0 deletions Zend/Optimizer/zend_func_info.c
Expand Up @@ -760,6 +760,9 @@ static const func_info_t func_infos[] = {
#ifdef HAVE_GD_JPG
F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT),
#endif
#ifdef HAVE_GD_AVIF
F1("imagecreatefromavif", MAY_BE_FALSE | MAY_BE_OBJECT),
#endif
#ifdef HAVE_GD_PNG
F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT),
#endif
Expand Down
9 changes: 9 additions & 0 deletions appveyor/build_task.bat
Expand Up @@ -45,6 +45,15 @@ if not exist "%DEPS_DIR%" (
)
if %errorlevel% neq 0 exit /b 3

rem install libavif into dependency directory
rem temporary workaround for libavif not being part of the official dependencies
if not exist "%DEPS_DIR%\libavif-0.9.0-%PHP_SDK_VS%-%PHP_SDK_ARCH%.zip" (
curl -fsSL -o %DEPS_DIR%\libavif-0.9.0-%PHP_SDK_VS%-%PHP_SDK_ARCH%.zip https://windows.php.net/downloads/pecl/deps/libavif-0.9.0-%PHP_SDK_VS%-%PHP_SDK_ARCH%.zip
)
if not exist "%DEPS_DIR%\lib\avif.lib" (
7z x %DEPS_DIR%\libavif-0.9.0-%PHP_SDK_VS%-%PHP_SDK_ARCH%.zip -o%DEPS_DIR%
)

cmd /c buildconf.bat --force
if %errorlevel% neq 0 exit /b 3

Expand Down
23 changes: 22 additions & 1 deletion ext/gd/config.m4
Expand Up @@ -13,6 +13,15 @@ PHP_ARG_WITH([external-gd],
[no],
[no])

if test -z "$PHP_AVIF"; then
PHP_ARG_WITH([avif],
[for libavif],
[AS_HELP_STRING([--with-avif],
[GD: Enable AVIF support (only for bundled libgd)])],
[no],
[no])
fi

if test -z "$PHP_WEBP"; then
PHP_ARG_WITH([webp],
[for libwebp],
Expand Down Expand Up @@ -71,6 +80,16 @@ AC_DEFUN([PHP_GD_PNG],[
AC_DEFINE(HAVE_LIBPNG, 1, [ ])
])

AC_DEFUN([PHP_GD_AVIF],[
if test "$PHP_AVIF" != "no"; then
PKG_CHECK_MODULES([AVIF], [libavif])
PHP_EVAL_LIBLINE($AVIF_LIBS, GD_SHARED_LIBADD)
PHP_EVAL_INCLINE($AVIF_CFLAGS)
AC_DEFINE(HAVE_LIBAVIF, 1, [ ])
AC_DEFINE(HAVE_GD_AVIF, 1, [ ])
fi
])

AC_DEFUN([PHP_GD_WEBP],[
if test "$PHP_WEBP" != "no"; then
PKG_CHECK_MODULES([WEBP], [libwebp])
Expand Down Expand Up @@ -121,6 +140,7 @@ AC_DEFUN([PHP_GD_JISX0208],[

AC_DEFUN([PHP_GD_CHECK_VERSION],[
PHP_CHECK_LIBRARY(gd, gdImageCreateFromPng, [AC_DEFINE(HAVE_GD_PNG, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdImageCreateFromAvif, [AC_DEFINE(HAVE_GD_AVIF, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdImageCreateFromWebp, [AC_DEFINE(HAVE_GD_WEBP, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdImageCreateFromJpeg, [AC_DEFINE(HAVE_GD_JPG, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdImageCreateFromXpm, [AC_DEFINE(HAVE_GD_XPM, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
Expand All @@ -141,7 +161,7 @@ if test "$PHP_GD" != "no"; then
dnl Disable strict prototypes as GD takes advantages of variadic function signatures for function pointers.
GD_CFLAGS="-Wno-strict-prototypes"
extra_sources="libgd/gd.c libgd/gd_gd.c libgd/gd_gd2.c libgd/gd_io.c libgd/gd_io_dp.c \
libgd/gd_io_file.c libgd/gd_ss.c libgd/gd_io_ss.c libgd/gd_webp.c \
libgd/gd_io_file.c libgd/gd_ss.c libgd/gd_io_ss.c libgd/gd_webp.c libgd/gd_avif.c \
libgd/gd_png.c libgd/gd_jpeg.c libgd/gdxpm.c libgd/gdfontt.c libgd/gdfonts.c \
libgd/gdfontmb.c libgd/gdfontl.c libgd/gdfontg.c libgd/gdtables.c libgd/gdft.c \
libgd/gdcache.c libgd/gdkanji.c libgd/wbmp.c libgd/gd_wbmp.c libgd/gdhelpers.c \
Expand All @@ -162,6 +182,7 @@ dnl These are always available with bundled library
dnl Various checks for GD features
PHP_GD_ZLIB
PHP_GD_PNG
PHP_GD_AVIF
PHP_GD_WEBP
PHP_GD_JPEG
PHP_GD_XPM
Expand Down
11 changes: 10 additions & 1 deletion ext/gd/config.w32
Expand Up @@ -2,6 +2,7 @@

ARG_WITH("gd", "Bundled GD support", "yes,shared");
ARG_WITH("libwebp", "webp support", "yes");
ARG_WITH("libavif", "avif support", "yes");

if (PHP_GD != "no") {
if (
Expand Down Expand Up @@ -30,6 +31,14 @@ if (PHP_GD != "no") {
WARNING("libwebp not enabled; libraries and headers not found");
}
}
if (PHP_LIBAVIF != "no") {
if (CHECK_LIB("avif.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("avif.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\avif")) {
ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBAVIF /D HAVE_GD_AVIF");
} else {
WARNING("libavif not enabled; libraries and headers not found");
}
}
CHECK_LIB("User32.lib", "gd", PHP_GD);
CHECK_LIB("Gdi32.lib", "gd", PHP_GD);

Expand All @@ -39,7 +48,7 @@ if (PHP_GD != "no") {
gdft.c gd_gd2.c gd_gd.c gd_gif_in.c gd_gif_out.c gdhelpers.c gd_io.c gd_io_dp.c \
gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \
gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c gd_xbm.c gd_security.c gd_transform.c \
gd_filter.c gd_pixelate.c gd_rotate.c gd_color_match.c gd_webp.c \
gd_filter.c gd_pixelate.c gd_rotate.c gd_color_match.c gd_webp.c gd_avif.c \
gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c gd_tga.c", "gd");
AC_DEFINE('HAVE_LIBGD', 1, 'GD support');
ADD_FLAG("CFLAGS_GD", " \
Expand Down
54 changes: 51 additions & 3 deletions ext/gd/gd.c
Expand Up @@ -54,7 +54,6 @@
# include <X11/xpm.h>
#endif


#include "gd_compat.h"

#ifdef HAVE_GD_BUNDLED
Expand Down Expand Up @@ -371,6 +370,7 @@ PHP_MINIT_FUNCTION(gd)

REGISTER_INI_ENTRIES();

REGISTER_LONG_CONSTANT("IMG_AVIF", PHP_IMG_AVIF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_GIF", PHP_IMG_GIF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_JPG", PHP_IMG_JPG, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_JPEG", PHP_IMG_JPEG, CONST_CS | CONST_PERSISTENT);
Expand Down Expand Up @@ -601,6 +601,9 @@ PHP_MINFO_FUNCTION(gd)
#ifdef HAVE_GD_BMP
php_info_print_table_row(2, "BMP Support", "enabled");
#endif
#ifdef HAVE_GD_AVIF
php_info_print_table_row(2, "AVIF Support", "enabled");
#endif
#ifdef HAVE_GD_TGA
php_info_print_table_row(2, "TGA Read Support", "enabled");
#endif
Expand Down Expand Up @@ -655,6 +658,11 @@ PHP_FUNCTION(gd_info)
#else
add_assoc_bool(return_value, "BMP Support", 0);
#endif
#ifdef HAVE_GD_AVIF
add_assoc_bool(return_value, "AVIF Support", 1);
#else
add_assoc_bool(return_value, "AVIF Support", 0);
#endif
#ifdef HAVE_GD_TGA
add_assoc_bool(return_value, "TGA Read Support", 1);
#else
Expand Down Expand Up @@ -1407,7 +1415,7 @@ PHP_FUNCTION(imagecreate)
}
/* }}} */

/* {{{ Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM */
/* {{{ Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM, etc */
PHP_FUNCTION(imagetypes)
{
int ret = 0;
Expand All @@ -1431,6 +1439,9 @@ PHP_FUNCTION(imagetypes)
#ifdef HAVE_GD_TGA
ret |= PHP_IMG_TGA;
#endif
#ifdef HAVE_GD_AVIF
ret |= PHP_IMG_AVIF;
#endif

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
Expand Down Expand Up @@ -1589,6 +1600,15 @@ PHP_FUNCTION(imagecreatefromstring)
RETURN_FALSE;
#endif

case PHP_GDIMG_TYPE_AVIF:
#ifdef HAVE_GD_AVIF
im = _php_image_create_from_string(data, "AVIF", gdImageCreateFromAvifCtx);
break;
#else
php_error_docref(NULL, E_WARNING, "No AVIF support in this PHP build");
RETURN_FALSE;
#endif

default:
php_error_docref(NULL, E_WARNING, "Data is not in a recognized format");
RETURN_FALSE;
Expand Down Expand Up @@ -1769,6 +1789,15 @@ PHP_FUNCTION(imagecreatefromxbm)
}
/* }}} */

#ifdef HAVE_GD_AVIF
/* {{{ Create a new image from AVIF file or URL */
PHP_FUNCTION(imagecreatefromavif)
{
_php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_AVIF, "AVIF", gdImageCreateFromAvif, gdImageCreateFromAvifCtx);
}
/* }}} */
#endif /* HAVE_GD_AVIF */

#ifdef HAVE_GD_XPM
/* {{{ Create a new image from XPM file or URL */
PHP_FUNCTION(imagecreatefromxpm)
Expand Down Expand Up @@ -1996,6 +2025,15 @@ PHP_FUNCTION(imagewebp)
/* }}} */
#endif /* HAVE_GD_WEBP */

#ifdef HAVE_GD_AVIF
/* {{{ Output AVIF image to browser or file */
PHP_FUNCTION(imageavif)
{
_php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_AVIF, "AVIF", gdImageAvifCtx);
}
/* }}} */
#endif /* HAVE_GD_AVIF */

#ifdef HAVE_GD_JPG
/* {{{ Output JPEG image to browser or file */
PHP_FUNCTION(imagejpeg)
Expand Down Expand Up @@ -4178,7 +4216,7 @@ static gdIOCtx *create_output_context() {
static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)())
{
zval *imgind;
zend_long quality = -1, basefilter = -1;
zend_long quality = -1, basefilter = -1, speed = -1;
gdImagePtr im;
gdIOCtx *ctx = NULL;
zval *to_zval = NULL;
Expand All @@ -4191,6 +4229,10 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type,
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!ll", &imgind, gd_image_ce, &to_zval, &quality, &basefilter) == FAILURE) {
RETURN_THROWS();
}
} else if (image_type == PHP_GDIMG_TYPE_AVIF) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!ll", &imgind, gd_image_ce, &to_zval, &quality, &speed) == FAILURE) {
RETURN_THROWS();
}
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!l", &imgind, gd_image_ce, &to_zval, &quality) == FAILURE) {
RETURN_THROWS();
Expand Down Expand Up @@ -4218,6 +4260,12 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type,
}
(*func_p)(im, ctx, (int) quality);
break;
case PHP_GDIMG_TYPE_AVIF:
if (speed == -1) {
speed = 6;
}
(*func_p)(im, ctx, (int) quality, (int) speed);
break;
case PHP_GDIMG_TYPE_PNG:
(*func_p)(im, ctx, (int) quality, (int) basefilter);
break;
Expand Down
9 changes: 9 additions & 0 deletions ext/gd/gd.stub.php
Expand Up @@ -66,6 +66,10 @@ function imagetypes(): int {}

function imagecreatefromstring(string $data): GdImage|false {}

#ifdef HAVE_GD_AVIF
function imagecreatefromavif(string $filename): GdImage|false {}
#endif

function imagecreatefromgif(string $filename): GdImage|false {}

#ifdef HAVE_GD_JPG
Expand Down Expand Up @@ -104,6 +108,11 @@ function imagecreatefromtga(string $filename): GdImage|false {}

function imagexbm(GdImage $image, ?string $filename, ?int $foreground_color = null): bool {}

#ifdef HAVE_GD_AVIF
/** @param resource|string|null $file */
function imageavif(GdImage $image, $file = null, int $quality = -1, int $speed = -1): bool {}
#endif

/** @param resource|string|null $file */
function imagegif(GdImage $image, $file = null): bool {}

Expand Down
29 changes: 28 additions & 1 deletion ext/gd/gd_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 4db5a04f57436fffff4d34f4e44db7b7fdc39874 */
* Stub hash: 6c091ec5dc43771d26c3dac479ede7b38aa6e574 */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_gd_info, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
Expand Down Expand Up @@ -144,6 +144,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_imagecreatefromstring, 0, 1,
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_END_ARG_INFO()

#if defined(HAVE_GD_AVIF)
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_imagecreatefromavif, 0, 1, GdImage, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_imagecreatefromgif, 0, 1, GdImage, MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
ZEND_END_ARG_INFO()
Expand Down Expand Up @@ -206,6 +212,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_imagexbm, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, foreground_color, IS_LONG, 1, "null")
ZEND_END_ARG_INFO()

#if defined(HAVE_GD_AVIF)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_imageavif, 0, 1, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, image, GdImage, 0)
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, file, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, quality, IS_LONG, 0, "-1")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, speed, IS_LONG, 0, "-1")
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_imagegif, 0, 1, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, image, GdImage, 0)
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, file, "null")
Expand Down Expand Up @@ -593,6 +608,9 @@ ZEND_FUNCTION(imagesetbrush);
ZEND_FUNCTION(imagecreate);
ZEND_FUNCTION(imagetypes);
ZEND_FUNCTION(imagecreatefromstring);
#if defined(HAVE_GD_AVIF)
ZEND_FUNCTION(imagecreatefromavif);
#endif
ZEND_FUNCTION(imagecreatefromgif);
#if defined(HAVE_GD_JPG)
ZEND_FUNCTION(imagecreatefromjpeg);
Expand All @@ -618,6 +636,9 @@ ZEND_FUNCTION(imagecreatefrombmp);
ZEND_FUNCTION(imagecreatefromtga);
#endif
ZEND_FUNCTION(imagexbm);
#if defined(HAVE_GD_AVIF)
ZEND_FUNCTION(imageavif);
#endif
ZEND_FUNCTION(imagegif);
#if defined(HAVE_GD_PNG)
ZEND_FUNCTION(imagepng);
Expand Down Expand Up @@ -728,6 +749,9 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(imagecreate, arginfo_imagecreate)
ZEND_FE(imagetypes, arginfo_imagetypes)
ZEND_FE(imagecreatefromstring, arginfo_imagecreatefromstring)
#if defined(HAVE_GD_AVIF)
ZEND_FE(imagecreatefromavif, arginfo_imagecreatefromavif)
#endif
ZEND_FE(imagecreatefromgif, arginfo_imagecreatefromgif)
#if defined(HAVE_GD_JPG)
ZEND_FE(imagecreatefromjpeg, arginfo_imagecreatefromjpeg)
Expand All @@ -753,6 +777,9 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(imagecreatefromtga, arginfo_imagecreatefromtga)
#endif
ZEND_FE(imagexbm, arginfo_imagexbm)
#if defined(HAVE_GD_AVIF)
ZEND_FE(imageavif, arginfo_imageavif)
#endif
ZEND_FE(imagegif, arginfo_imagegif)
#if defined(HAVE_GD_PNG)
ZEND_FE(imagepng, arginfo_imagepng)
Expand Down

0 comments on commit 81f6d36

Please sign in to comment.