diff options
Diffstat (limited to 'ext/gd')
| -rw-r--r-- | ext/gd/config.m4 | 163 | ||||
| -rw-r--r-- | ext/gd/config.w32 | 24 | ||||
| -rw-r--r-- | ext/gd/gd.c | 1068 | ||||
| -rw-r--r-- | ext/gd/gd_compat.c | 63 | ||||
| -rw-r--r-- | ext/gd/gd_compat.h | 14 | ||||
| -rw-r--r-- | ext/gd/gd_ctx.c | 4 | ||||
| -rw-r--r-- | ext/gd/gdcache.c | 7 | ||||
| -rw-r--r-- | ext/gd/libgd/gd.c | 87 | ||||
| -rw-r--r-- | ext/gd/libgd/gd.h | 208 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_compat.c | 35 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_compat.h | 66 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_crop.c | 356 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_interpolation.c | 2547 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_jpeg.c | 25 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_matrix.c | 334 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_png.c | 2 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_transform.c | 73 | ||||
| -rw-r--r-- | ext/gd/libgd/gd_webp.c | 33 | ||||
| -rw-r--r-- | ext/gd/php_gd.h | 23 | ||||
| -rw-r--r-- | ext/gd/tests/bug43073.phpt | 8 | ||||
| -rw-r--r-- | ext/gd/tests/bug48801.phpt | 4 | ||||
| -rw-r--r-- | ext/gd/tests/imagecrop_auto.phpt | 84 | ||||
| -rw-r--r-- | ext/gd/tests/imageflip.phpt | 30 | ||||
| -rw-r--r-- | ext/gd/tests/imageloadfont_invalid.phpt | 1 | ||||
| -rw-r--r-- | ext/gd/tests/logo_noise.png | bin | 0 -> 25182 bytes |
25 files changed, 4595 insertions, 664 deletions
diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index 0e35eceba3..c9e080faab 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -7,12 +7,11 @@ dnl Configure options dnl PHP_ARG_WITH(gd, for GD support, -[ --with-gd[=DIR] Include GD support. DIR is the GD library base +[ --with-gd[=DIR] Include GD support. DIR is the GD library base install directory [BUNDLED]]) - if test -z "$PHP_VPX_DIR"; then PHP_ARG_WITH(vpx-dir, for the location of libvpx, - [ --with-vpx-dir[=DIR] GD: Set the path to libvpx install prefix], no, no) + [ --with-vpx-dir[=DIR] GD: Set the path to libvpx install prefix], no, no) fi if test -z "$PHP_JPEG_DIR"; then @@ -40,10 +39,10 @@ PHP_ARG_WITH(t1lib, for T1lib support, [ --with-t1lib[=DIR] GD: Include T1lib support. T1lib version >= 5.0.0 required], no, no) PHP_ARG_ENABLE(gd-native-ttf, whether to enable truetype string function in GD, -[ --enable-gd-native-ttf GD: Enable TrueType string function], no, no) +[ --enable-gd-native-ttf GD: Enable TrueType string function], no, no) PHP_ARG_ENABLE(gd-jis-conv, whether to enable JIS-mapped Japanese font support in GD, -[ --enable-gd-jis-conv GD: Enable JIS-mapped Japanese font support], no, no) +[ --enable-gd-jis-conv GD: Enable JIS-mapped Japanese font support], no, no) dnl dnl Checks for the configure options @@ -159,18 +158,11 @@ AC_DEFUN([PHP_GD_XPM],[ if test "$PHP_XPM_DIR" != "no"; then for i in $PHP_XPM_DIR /usr/local /usr/X11R6 /usr; do - test -f $i/$PHP_LIBDIR/libXpm.$SHLIB_SUFFIX_NAME || test -f $i/$PHP_LIBDIR/libXpm.a && GD_XPM_DIR=$i && break + test -f $i/include/xpm.h && GD_XPM_DIR=$i && GD_XPM_INC=$i && break + test -f $i/include/X11/xpm.h && GD_XPM_DIR=$i && GD_XPM_INC=$i/X11 && break done if test -z "$GD_XPM_DIR"; then - AC_MSG_ERROR([libXpm.(a|so) not found.]) - fi - - for i in include include/X11; do - test -f $GD_XPM_DIR/$i/xpm.h && GD_XPM_INC=$GD_XPM_DIR/include - done - - if test -z "$GD_XPM_INC"; then AC_MSG_ERROR([xpm.h not found.]) fi @@ -259,50 +251,21 @@ AC_DEFUN([PHP_GD_JISX0208],[ ]) AC_DEFUN([PHP_GD_CHECK_VERSION],[ - PHP_CHECK_LIBRARY(gd, gdImageString16, [AC_DEFINE(HAVE_LIBGD13, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImagePaletteCopy, [AC_DEFINE(HAVE_LIBGD15, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateFromPng, [AC_DEFINE(HAVE_GD_PNG, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateFromGif, [AC_DEFINE(HAVE_GD_GIF_READ, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageGif, [AC_DEFINE(HAVE_GD_GIF_CREATE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageWBMP, [AC_DEFINE(HAVE_GD_WBMP, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateFromJpeg, [AC_DEFINE(HAVE_GD_JPG, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateFromXpm, [AC_DEFINE(HAVE_GD_XPM, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateFromGd2, [AC_DEFINE(HAVE_GD_GD2, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageCreateTrueColor, [AC_DEFINE(HAVE_LIBGD20, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageSetTile, [AC_DEFINE(HAVE_GD_IMAGESETTILE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageEllipse, [AC_DEFINE(HAVE_GD_IMAGEELLIPSE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageSetBrush, [AC_DEFINE(HAVE_GD_IMAGESETBRUSH, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageStringTTF, [AC_DEFINE(HAVE_GD_STRINGTTF, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageStringFT, [AC_DEFINE(HAVE_GD_STRINGFT, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageStringFTEx, [AC_DEFINE(HAVE_GD_STRINGFTEX, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageColorClosestHWB, [AC_DEFINE(HAVE_COLORCLOSESTHWB, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageColorResolve, [AC_DEFINE(HAVE_GDIMAGECOLORRESOLVE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageGifCtx, [AC_DEFINE(HAVE_GD_GIF_CTX, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdCacheCreate, [AC_DEFINE(HAVE_GD_CACHE_CREATE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdFontCacheShutdown, [AC_DEFINE(HAVE_GD_FONTCACHESHUTDOWN,1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdFreeFontCache, [AC_DEFINE(HAVE_GD_FREEFONTCACHE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdFontCacheMutexSetup, [AC_DEFINE(HAVE_GD_FONTMUTEX, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdNewDynamicCtxEx, [AC_DEFINE(HAVE_GD_DYNAMIC_CTX_EX, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImageConvolution, [AC_DEFINE(HAVE_GD_IMAGE_CONVOLUTION, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) - PHP_CHECK_LIBRARY(gd, gdImagePixelate, [AC_DEFINE(HAVE_GD_IMAGE_PIXELATE, 1, [ ])], [], [ -L$GD_LIB $GD_SHARED_LIBADD ]) + PHP_CHECK_LIBRARY(gd, gdImageCreateFromPng, [AC_DEFINE(HAVE_GD_PNG, 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 ]) + PHP_CHECK_LIBRARY(gd, gdImageStringFT, [AC_DEFINE(HAVE_GD_FREETYPE, 1, [ ])], [], [ $GD_SHARED_LIBADD ]) ]) dnl dnl Main GD configure dnl -if test "$PHP_GD" = "yes"; then - GD_MODULE_TYPE=builtin - 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/webpimg.c libgd/gd_webp.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 \ - libgd/gd_topal.c libgd/gd_gif_in.c libgd/xbm.c libgd/gd_gif_out.c libgd/gd_security.c \ - libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c libgd/gd_rotate.c libgd/gd_color.c" - -dnl check for fabsf and floorf which are available since C99 - AC_CHECK_FUNCS(fabsf floorf) +dnl +dnl Common for both builtin and external GD +dnl +if test "$PHP_GD" != "no"; then dnl PNG is required by GD library test "$PHP_PNG_DIR" = "no" && PHP_PNG_DIR=yes @@ -310,36 +273,33 @@ dnl PNG is required by GD library dnl Various checks for GD features PHP_GD_ZLIB PHP_GD_TTSTR - PHP_GD_JISX0208 - PHP_GD_JPEG PHP_GD_VPX + PHP_GD_JPEG PHP_GD_PNG PHP_GD_XPM PHP_GD_FREETYPE2 PHP_GD_T1LIB + PHP_GD_JISX0208 +fi + +if test "$PHP_GD" = "yes"; then + GD_MODULE_TYPE=builtin + 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/webpimg.c libgd/gd_webp.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 \ + libgd/gd_topal.c libgd/gd_gif_in.c libgd/xbm.c libgd/gd_gif_out.c libgd/gd_security.c \ + libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c libgd/gd_rotate.c libgd/gd_color.c \ + libgd/gd_transform.c libgd/gd_crop.c libgd/gd_interpolation.c libgd/gd_matrix.c" + +dnl check for fabsf and floorf which are available since C99 + AC_CHECK_FUNCS(fabsf floorf) dnl These are always available with bundled library - AC_DEFINE(HAVE_LIBGD, 1, [ ]) - AC_DEFINE(HAVE_LIBGD13, 1, [ ]) - AC_DEFINE(HAVE_LIBGD15, 1, [ ]) - AC_DEFINE(HAVE_LIBGD20, 1, [ ]) - AC_DEFINE(HAVE_LIBGD204, 1, [ ]) - AC_DEFINE(HAVE_GD_IMAGESETTILE, 1, [ ]) - AC_DEFINE(HAVE_GD_IMAGESETBRUSH, 1, [ ]) - AC_DEFINE(HAVE_GDIMAGECOLORRESOLVE, 1, [ ]) - AC_DEFINE(HAVE_COLORCLOSESTHWB, 1, [ ]) - AC_DEFINE(HAVE_GD_WBMP, 1, [ ]) - AC_DEFINE(HAVE_GD_GD2, 1, [ ]) - AC_DEFINE(HAVE_GD_PNG, 1, [ ]) - AC_DEFINE(HAVE_GD_XBM, 1, [ ]) AC_DEFINE(HAVE_GD_BUNDLED, 1, [ ]) - AC_DEFINE(HAVE_GD_GIF_READ, 1, [ ]) - AC_DEFINE(HAVE_GD_GIF_CREATE, 1, [ ]) - AC_DEFINE(HAVE_GD_IMAGEELLIPSE, 1, [ ]) - AC_DEFINE(HAVE_GD_FONTCACHESHUTDOWN,1, [ ]) - AC_DEFINE(HAVE_GD_FONTMUTEX, 1, [ ]) - AC_DEFINE(HAVE_GD_DYNAMIC_CTX_EX, 1, [ ]) - AC_DEFINE(HAVE_GD_GIF_CTX, 1, [ ]) + AC_DEFINE(HAVE_GD_PNG, 1, [ ]) + AC_DEFINE(HAVE_GD_CACHE_CREATE, 1, [ ]) dnl Make sure the libgd/ is first in the include path GDLIB_CFLAGS="-DHAVE_LIBPNG" @@ -363,8 +323,7 @@ dnl enable the support in bundled GD library fi if test -n "$FREETYPE2_DIR"; then - AC_DEFINE(HAVE_GD_STRINGFT, 1, [ ]) - AC_DEFINE(HAVE_GD_STRINGFTEX, 1, [ ]) + AC_DEFINE(HAVE_GD_FREETYPE, 1, [ ]) AC_DEFINE(ENABLE_GD_TTF, 1, [ ]) GDLIB_CFLAGS="$GDLIB_CFLAGS -DHAVE_LIBFREETYPE -DENABLE_GD_TTF" fi @@ -378,8 +337,7 @@ else if test "$PHP_GD" != "no"; then GD_MODULE_TYPE=external - extra_sources="gdcache.c libgd/gd_compat.c libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c \ - libgd/gd_rotate.c libgd/gd_color.c" + extra_sources="gd_compat.c" dnl Various checks for GD features PHP_GD_ZLIB @@ -392,44 +350,28 @@ dnl Various checks for GD features PHP_GD_T1LIB dnl Header path - for i in include/gd1.3 include/gd include gd1.3 gd ""; do + for i in include/gd include gd ""; do test -f "$PHP_GD/$i/gd.h" && GD_INCLUDE="$PHP_GD/$i" done -dnl Library path - for i in $PHP_LIBDIR/gd1.3 $PHP_LIBDIR/gd $PHP_LIBDIR gd1.3 gd ""; do - test -f "$PHP_GD/$i/libgd.$SHLIB_SUFFIX_NAME" || test -f "$PHP_GD/$i/libgd.a" && GD_LIB="$PHP_GD/$i" - done - - if test -n "$GD_INCLUDE" && test -n "$GD_LIB"; then - PHP_ADD_LIBRARY_WITH_PATH(gd, $GD_LIB, GD_SHARED_LIBADD) - AC_DEFINE(HAVE_LIBGD,1,[ ]) - PHP_GD_CHECK_VERSION - elif test -z "$GD_INCLUDE"; then + if test -z "$GD_INCLUDE"; then AC_MSG_ERROR([Unable to find gd.h anywhere under $PHP_GD]) - else - AC_MSG_ERROR([Unable to find libgd.(a|so) anywhere under $PHP_GD]) fi - PHP_EXPAND_PATH($GD_INCLUDE, GD_INCLUDE) +dnl Library path - dnl - dnl Check for gd 2.0.4 greater availability - dnl - old_CPPFLAGS=$CPPFLAGS - CPPFLAGS=-I$GD_INCLUDE - AC_TRY_COMPILE([ -#include <gd.h> -#include <stdlib.h> - ], [ -gdIOCtx *ctx; -ctx = malloc(sizeof(gdIOCtx)); -ctx->gd_free = 1; - ], [ - AC_DEFINE(HAVE_LIBGD204, 1, [ ]) + PHP_CHECK_LIBRARY(gd, gdSetErrorMethod, + [ + PHP_ADD_LIBRARY_WITH_PATH(gd, $PHP_GD/$PHP_LIBDIR, GD_SHARED_LIBADD) + AC_DEFINE(HAVE_LIBGD, 1, [ ]) + ],[ + AC_MSG_ERROR([Unable to find libgd.(a|so) >= 2.1.0 anywhere under $PHP_GD]) + ],[ + -L$PHP_GD/$PHP_LIBDIR ]) - CPPFLAGS=$old_CPPFLAGS + PHP_GD_CHECK_VERSION + PHP_EXPAND_PATH($GD_INCLUDE, GD_INCLUDE) fi fi @@ -439,9 +381,8 @@ dnl if test "$PHP_GD" != "no"; then PHP_NEW_EXTENSION(gd, gd.c $extra_sources, $ext_shared,, \\$(GDLIB_CFLAGS)) - PHP_ADD_BUILD_DIR($ext_builddir/libgd) - - if test "$GD_MODULE_TYPE" = "builtin"; then + if test "$GD_MODULE_TYPE" = "builtin"; then + PHP_ADD_BUILD_DIR($ext_builddir/libgd) GDLIB_CFLAGS="-I$ext_srcdir/libgd $GDLIB_CFLAGS" GD_HEADER_DIRS="ext/gd/ ext/gd/libgd/" @@ -454,7 +395,7 @@ if test "$PHP_GD" != "no"; then PHP_ADD_INCLUDE($GD_INCLUDE) PHP_CHECK_LIBRARY(gd, gdImageCreate, [], [ AC_MSG_ERROR([GD build test failed. Please check the config.log for details.]) - ], [ -L$GD_LIB $GD_SHARED_LIBADD ]) + ], [ $GD_SHARED_LIBADD ]) fi PHP_INSTALL_HEADERS([$GD_HEADER_DIRS]) diff --git a/ext/gd/config.w32 b/ext/gd/config.w32 index 8c932a037c..1420b11e6e 100644 --- a/ext/gd/config.w32 +++ b/ext/gd/config.w32 @@ -3,15 +3,16 @@ ARG_WITH("gd", "Bundled GD support", "yes,shared"); ARG_WITH("t1lib", "t1lib support", "yes"); +ARG_WITH("libvpx", "vpx support", "yes"); if (PHP_GD != "no") { if ( - CHECK_LIB("vpxmt.lib", "gd", PHP_GD) && CHECK_LIB("libjpeg_a.lib;libjpeg.lib", "gd", PHP_GD) && CHECK_LIB("freetype_a.lib;freetype.lib", "gd", PHP_GD) && CHECK_LIB("libpng_a.lib;libpng.lib", "gd", PHP_GD) && CHECK_HEADER_ADD_INCLUDE("gd.h", "CFLAGS_GD", PHP_GD + ";ext\\gd\\libgd") && - CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng12") && + (CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng15") || + CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng12")) && (CHECK_LIB("libiconv_a.lib;libiconv.lib", "gd", PHP_GD) || CHECK_LIB("iconv_a.lib;iconv.lib", "gd", PHP_GD)) && CHECK_HEADER_ADD_INCLUDE("iconv.h", "CFLAGS_GD", PHP_GD) && (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "gd", PHP_GD) )) || @@ -29,6 +30,14 @@ if (PHP_GD != "no") { } } + if (PHP_LIBVPX != "no") { + if (CHECK_LIB("vpxmt.lib", "gd", PHP_GD) && + CHECK_HEADER_ADD_INCLUDE("vp8.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\vpx")) { + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBVPX /D HAVE_GD_WEBP"); + } else { + WARNING("libvpx not enabled; libraries and headers not found"); + } + } CHECK_LIB("User32.lib", "gd", PHP_GD); CHECK_LIB("Gdi32.lib", "gd", PHP_GD); @@ -37,8 +46,9 @@ if (PHP_GD != "no") { gdcache.c gdfontg.c gdfontl.c gdfontmb.c gdfonts.c gdfontt.c \ 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 xbm.c gd_security.c \ - gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c gd_color.c webpimg.c gd_webp.c ", "gd"); + gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c xbm.c gd_security.c gd_transform.c \ + gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c gd_color.c webpimg.c gd_webp.c \ + gd_crop.c gd_interpolation.c gd_matrix.c", "gd"); AC_DEFINE('HAVE_LIBGD', 1, 'GD support'); ADD_FLAG("CFLAGS_GD", " \ /D HAVE_GD_DYNAMIC_CTX_EX=1 \ @@ -59,14 +69,12 @@ if (PHP_GD != "no") { /D HAVE_GD_WBMP \ /D HAVE_GD_XBM \ /D HAVE_GD_XPM \ -/D HAVE_GD_WEBP \ -/D HAVE_LIBFREETYPE=1 \ +/D HAVE_GD_FREETYPE=1 \ /D HAVE_LIBGD13=1 \ /D HAVE_LIBGD15=1 \ /D HAVE_LIBGD20=1 \ /D HAVE_LIBGD204=1 \ /D HAVE_LIBJPEG \ -/D HAVE_LIBVPX \ /D HAVE_LIBPNG \ /D HAVE_XPM \ /D HAVE_COLORCLOSESTHWB \ @@ -76,7 +84,7 @@ if (PHP_GD != "no") { "); PHP_INSTALL_HEADERS("", "ext/gd ext/gd/libgd" ); - } else { + } else { WARNING("gd not enabled; libraries and headers not found"); } } diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 3279463b7e..8f32ad5067 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -53,11 +53,12 @@ # include <Wingdi.h> #endif -#if HAVE_LIBGD -#if !HAVE_GD_BUNDLED -# include "libgd/gd_compat.h" +#ifdef HAVE_GD_XPM +# include <X11/xpm.h> #endif +# include "gd_compat.h" + static int le_gd, le_gd_font; #if HAVE_LIBT1 @@ -74,9 +75,6 @@ static void php_free_ps_enc(zend_rsrc_list_entry *rsrc TSRMLS_DC); #include <gdfontl.h> /* 4 Large font */ #include <gdfontg.h> /* 5 Giant font */ -#ifdef HAVE_GD_WBMP -#include "libgd/wbmp.h" -#endif #ifdef ENABLE_GD_TTF # ifdef HAVE_LIBFREETYPE # include <ft2build.h> @@ -92,38 +90,7 @@ static void php_free_ps_enc(zend_rsrc_list_entry *rsrc TSRMLS_DC); static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int, int); #endif -#if HAVE_LIBGD15 -/* it's >= 1.5, i.e. has IOCtx */ -#define USE_GD_IOCTX 1 -#else -#undef USE_GD_IOCTX -#endif - -#ifdef USE_GD_IOCTX #include "gd_ctx.c" -#else -#define gdImageCreateFromGdCtx NULL -#define gdImageCreateFromGd2Ctx NULL -#define gdImageCreateFromGd2partCtx NULL -#define gdImageCreateFromGifCtx NULL -#define gdImageCreateFromJpegCtx NULL -#define gdImageCreateFromPngCtx NULL -#define gdImageCreateFromWBMPCtx NULL -typedef FILE gdIOCtx; -#define CTX_PUTC(c, fp) fputc(c, fp) -#endif - -#ifndef HAVE_GDIMAGECOLORRESOLVE -extern int gdImageColorResolve(gdImagePtr, int, int, int); -#endif - -#if HAVE_COLORCLOSESTHWB -int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b); -#endif - -#ifndef HAVE_GD_DYNAMIC_CTX_EX -#define gdNewDynamicCtxEx(len, data, val) gdNewDynamicCtx(len, data) -#endif /* Section Filters Declarations */ /* IMPORTANT NOTE FOR NEW FILTER @@ -195,6 +162,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagetruecolortopalette, 0) ZEND_ARG_INFO(0, colorsWanted) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_imagepalettetotruecolor, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO(arginfo_imagecolormatch, 0) ZEND_ARG_INFO(0, im1) ZEND_ARG_INFO(0, im2) @@ -236,12 +207,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagesavealpha, 0) ZEND_ARG_INFO(0, save) ZEND_END_ARG_INFO() -#if HAVE_GD_BUNDLED ZEND_BEGIN_ARG_INFO(arginfo_imagelayereffect, 0) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, effect) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecolorallocatealpha, 0) ZEND_ARG_INFO(0, im) @@ -305,19 +274,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_imagerotate, 0, 0, 3) ZEND_ARG_INFO(0, ignoretransparent) ZEND_END_ARG_INFO() -#if HAVE_GD_IMAGESETTILE ZEND_BEGIN_ARG_INFO(arginfo_imagesettile, 0) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, tile) ZEND_END_ARG_INFO() -#endif -#if HAVE_GD_IMAGESETBRUSH ZEND_BEGIN_ARG_INFO(arginfo_imagesetbrush, 0) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, brush) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecreate, 0) ZEND_ARG_INFO(0, x_size) @@ -327,17 +292,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_imagetypes, 0) ZEND_END_ARG_INFO() -#if HAVE_LIBGD15 ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromstring, 0) ZEND_ARG_INFO(0, image) ZEND_END_ARG_INFO() -#endif -#ifdef HAVE_GD_GIF_READ ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgif, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#endif #ifdef HAVE_GD_JPG ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromjpeg, 0) @@ -357,29 +318,24 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromwebp, 0) ZEND_END_ARG_INFO() #endif -#ifdef HAVE_GD_XBM ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromxbm, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#endif -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromxpm, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() #endif -#ifdef HAVE_GD_WBMP ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromwbmp, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#ifdef HAVE_GD_GD2 ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd2, 0) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() @@ -391,22 +347,17 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd2part, 0) ZEND_ARG_INFO(0, width) ZEND_ARG_INFO(0, height) ZEND_END_ARG_INFO() -#endif -#if HAVE_GD_BUNDLED ZEND_BEGIN_ARG_INFO_EX(arginfo_imagexbm, 0, 0, 2) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, foreground) ZEND_END_ARG_INFO() -#endif -#ifdef HAVE_GD_GIF_CREATE ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegif, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#endif #ifdef HAVE_GD_PNG ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepng, 0, 0, 1) @@ -430,27 +381,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_imagejpeg, 0, 0, 1) ZEND_END_ARG_INFO() #endif -#ifdef HAVE_GD_WBMP ZEND_BEGIN_ARG_INFO_EX(arginfo_imagewbmp, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, foreground) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_END_ARG_INFO() -#ifdef HAVE_GD_GD2 ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd2, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, chunk_size) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagedestroy, 0) ZEND_ARG_INFO(0, im) @@ -463,12 +410,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecolorallocate, 0) ZEND_ARG_INFO(0, blue) ZEND_END_ARG_INFO() -#if HAVE_LIBGD15 ZEND_BEGIN_ARG_INFO(arginfo_imagepalettecopy, 0) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, src) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecolorat, 0) ZEND_ARG_INFO(0, im) @@ -483,14 +428,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecolorclosest, 0) ZEND_ARG_INFO(0, blue) ZEND_END_ARG_INFO() -#if HAVE_COLORCLOSESTHWB ZEND_BEGIN_ARG_INFO(arginfo_imagecolorclosesthwb, 0) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, red) ZEND_ARG_INFO(0, green) ZEND_ARG_INFO(0, blue) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecolordeallocate, 0) ZEND_ARG_INFO(0, im) @@ -692,7 +635,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecopy, 0) ZEND_ARG_INFO(0, src_h) ZEND_END_ARG_INFO() -#if HAVE_LIBGD15 ZEND_BEGIN_ARG_INFO(arginfo_imagecopymerge, 0) ZEND_ARG_INFO(0, src_im) ZEND_ARG_INFO(0, dst_im) @@ -716,7 +658,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecopymergegray, 0) ZEND_ARG_INFO(0, src_h) ZEND_ARG_INFO(0, pct) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_INFO(arginfo_imagecopyresized, 0) ZEND_ARG_INFO(0, dst_im) @@ -740,7 +681,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagesy, 0) ZEND_END_ARG_INFO() #ifdef ENABLE_GD_TTF -#if HAVE_LIBFREETYPE && HAVE_GD_STRINGFTEX +#if HAVE_LIBFREETYPE ZEND_BEGIN_ARG_INFO_EX(arginfo_imageftbbox, 0, 0, 4) ZEND_ARG_INFO(0, size) ZEND_ARG_INFO(0, angle) @@ -836,15 +777,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepsbbox, 0, 0, 3) ZEND_END_ARG_INFO() #endif -#ifdef HAVE_GD_WBMP ZEND_BEGIN_ARG_INFO_EX(arginfo_image2wbmp, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, threshold) ZEND_END_ARG_INFO() -#endif -#if defined(HAVE_GD_JPG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_JPG) ZEND_BEGIN_ARG_INFO(arginfo_jpeg2wbmp, 0) ZEND_ARG_INFO(0, f_org) ZEND_ARG_INFO(0, f_dest) @@ -854,7 +793,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_jpeg2wbmp, 0) ZEND_END_ARG_INFO() #endif -#if defined(HAVE_GD_PNG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_PNG) ZEND_BEGIN_ARG_INFO(arginfo_png2wbmp, 0) ZEND_ARG_INFO(0, f_org) ZEND_ARG_INFO(0, f_dest) @@ -880,6 +819,11 @@ ZEND_BEGIN_ARG_INFO(arginfo_imageconvolution, 0) ZEND_ARG_INFO(0, offset) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_imageflip, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + #ifdef HAVE_GD_BUNDLED ZEND_BEGIN_ARG_INFO(arginfo_imageantialias, 0) ZEND_ARG_INFO(0, im) @@ -887,6 +831,46 @@ ZEND_BEGIN_ARG_INFO(arginfo_imageantialias, 0) ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_INFO(arginfo_imagecrop, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, rect) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagecropauto, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, mode) + ZEND_ARG_INFO(0, threshold) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagescale, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, new_width) + ZEND_ARG_INFO(0, new_height) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageaffine, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, affine) + ZEND_ARG_INFO(0, clip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageaffinematrixget, 0, 0, 1) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageaffinematrixconcat, 0) + ZEND_ARG_INFO(0, m1) + ZEND_ARG_INFO(0, m2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetinterpolation, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + /* }}} */ /* {{{ gd_functions[] @@ -899,14 +883,10 @@ const zend_function_entry gd_functions[] = { PHP_FE(imagecharup, arginfo_imagecharup) PHP_FE(imagecolorat, arginfo_imagecolorat) PHP_FE(imagecolorallocate, arginfo_imagecolorallocate) -#if HAVE_LIBGD15 PHP_FE(imagepalettecopy, arginfo_imagepalettecopy) PHP_FE(imagecreatefromstring, arginfo_imagecreatefromstring) -#endif PHP_FE(imagecolorclosest, arginfo_imagecolorclosest) -#if HAVE_COLORCLOSESTHWB PHP_FE(imagecolorclosesthwb, arginfo_imagecolorclosesthwb) -#endif PHP_FE(imagecolordeallocate, arginfo_imagecolordeallocate) PHP_FE(imagecolorresolve, arginfo_imagecolorresolve) PHP_FE(imagecolorexact, arginfo_imagecolorexact) @@ -915,15 +895,14 @@ const zend_function_entry gd_functions[] = { PHP_FE(imagecolorstotal, arginfo_imagecolorstotal) PHP_FE(imagecolorsforindex, arginfo_imagecolorsforindex) PHP_FE(imagecopy, arginfo_imagecopy) -#if HAVE_LIBGD15 PHP_FE(imagecopymerge, arginfo_imagecopymerge) PHP_FE(imagecopymergegray, arginfo_imagecopymergegray) -#endif PHP_FE(imagecopyresized, arginfo_imagecopyresized) PHP_FE(imagecreate, arginfo_imagecreate) PHP_FE(imagecreatetruecolor, arginfo_imagecreatetruecolor) PHP_FE(imageistruecolor, arginfo_imageistruecolor) PHP_FE(imagetruecolortopalette, arginfo_imagetruecolortopalette) + PHP_FE(imagepalettetotruecolor, arginfo_imagepalettetotruecolor) PHP_FE(imagesetthickness, arginfo_imagesetthickness) PHP_FE(imagefilledarc, arginfo_imagefilledarc) PHP_FE(imagefilledellipse, arginfo_imagefilledellipse) @@ -941,19 +920,20 @@ const zend_function_entry gd_functions[] = { #endif PHP_FE(imagerotate, arginfo_imagerotate) + PHP_FE(imageflip, arginfo_imageflip) #ifdef HAVE_GD_BUNDLED PHP_FE(imageantialias, arginfo_imageantialias) #endif - -#if HAVE_GD_IMAGESETTILE + PHP_FE(imagecrop, arginfo_imagecrop) + PHP_FE(imagecropauto, arginfo_imagecropauto) + PHP_FE(imagescale, arginfo_imagescale) + PHP_FE(imageaffine, arginfo_imageaffine) + PHP_FE(imageaffinematrixconcat, arginfo_imageaffinematrixconcat) + PHP_FE(imageaffinematrixget, arginfo_imageaffinematrixget) + PHP_FE(imagesetinterpolation, arginfo_imagesetinterpolation) PHP_FE(imagesettile, arginfo_imagesettile) -#endif - -#if HAVE_GD_IMAGESETBRUSH PHP_FE(imagesetbrush, arginfo_imagesetbrush) -#endif - PHP_FE(imagesetstyle, arginfo_imagesetstyle) #ifdef HAVE_GD_PNG @@ -962,45 +942,31 @@ const zend_function_entry gd_functions[] = { #ifdef HAVE_GD_WEBP PHP_FE(imagecreatefromwebp, arginfo_imagecreatefromwebp) #endif -#ifdef HAVE_GD_GIF_READ PHP_FE(imagecreatefromgif, arginfo_imagecreatefromgif) -#endif #ifdef HAVE_GD_JPG PHP_FE(imagecreatefromjpeg, arginfo_imagecreatefromjpeg) #endif -#ifdef HAVE_GD_WBMP PHP_FE(imagecreatefromwbmp, arginfo_imagecreatefromwbmp) -#endif -#ifdef HAVE_GD_XBM PHP_FE(imagecreatefromxbm, arginfo_imagecreatefromxbm) -#endif -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) PHP_FE(imagecreatefromxpm, arginfo_imagecreatefromxpm) #endif PHP_FE(imagecreatefromgd, arginfo_imagecreatefromgd) -#ifdef HAVE_GD_GD2 PHP_FE(imagecreatefromgd2, arginfo_imagecreatefromgd2) PHP_FE(imagecreatefromgd2part, arginfo_imagecreatefromgd2part) -#endif #ifdef HAVE_GD_PNG PHP_FE(imagepng, arginfo_imagepng) #endif #ifdef HAVE_GD_WEBP PHP_FE(imagewebp, arginfo_imagewebp) #endif -#ifdef HAVE_GD_GIF_CREATE PHP_FE(imagegif, arginfo_imagegif) -#endif #ifdef HAVE_GD_JPG PHP_FE(imagejpeg, arginfo_imagejpeg) #endif -#ifdef HAVE_GD_WBMP PHP_FE(imagewbmp, arginfo_imagewbmp) -#endif PHP_FE(imagegd, arginfo_imagegd) -#ifdef HAVE_GD_GD2 PHP_FE(imagegd2, arginfo_imagegd2) -#endif PHP_FE(imagedestroy, arginfo_imagedestroy) PHP_FE(imagegammacorrect, arginfo_imagegammacorrect) @@ -1025,7 +991,7 @@ const zend_function_entry gd_functions[] = { #ifdef ENABLE_GD_TTF PHP_FE(imagettfbbox, arginfo_imagettfbbox) PHP_FE(imagettftext, arginfo_imagettftext) -#if HAVE_LIBFREETYPE && HAVE_GD_STRINGFTEX +#if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_FE(imageftbbox, arginfo_imageftbbox) PHP_FE(imagefttext, arginfo_imagefttext) #endif @@ -1045,19 +1011,15 @@ const zend_function_entry gd_functions[] = { #endif PHP_FE(imagetypes, arginfo_imagetypes) -#if defined(HAVE_GD_JPG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_JPG) PHP_FE(jpeg2wbmp, arginfo_jpeg2wbmp) #endif -#if defined(HAVE_GD_PNG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_PNG) PHP_FE(png2wbmp, arginfo_png2wbmp) #endif -#ifdef HAVE_GD_WBMP PHP_FE(image2wbmp, arginfo_image2wbmp) -#endif -#if HAVE_GD_BUNDLED PHP_FE(imagelayereffect, arginfo_imagelayereffect) PHP_FE(imagexbm, arginfo_imagexbm) -#endif PHP_FE(imagecolormatch, arginfo_imagecolormatch) @@ -1074,13 +1036,13 @@ zend_module_entry gd_module_entry = { "gd", gd_functions, PHP_MINIT(gd), -#if HAVE_LIBT1 || HAVE_GD_FONTMUTEX +#if HAVE_LIBT1 PHP_MSHUTDOWN(gd), #else NULL, #endif NULL, -#if HAVE_GD_STRINGFT && (HAVE_LIBFREETYPE && (HAVE_GD_FONTCACHESHUTDOWN || HAVE_GD_FREEFONTCACHE)) +#if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_RSHUTDOWN(gd), #else NULL, @@ -1122,15 +1084,25 @@ static void php_free_gd_font(zend_rsrc_list_entry *rsrc TSRMLS_DC) } /* }}} */ +#ifndef HAVE_GD_BUNDLED +/* {{{ php_gd_error_method + */ +void php_gd_error_method(int type, const char *format, va_list args) +{ + TSRMLS_FETCH(); + + php_verror(NULL, "", type, format, args TSRMLS_CC); +} +/* }}} */ +#endif + /* {{{ PHP_MSHUTDOWN_FUNCTION */ -#if HAVE_LIBT1 || HAVE_GD_FONTMUTEX +#if HAVE_LIBT1 PHP_MSHUTDOWN_FUNCTION(gd) { -#if HAVE_LIBT1 T1_CloseLib(); -#endif -#if HAVE_GD_FONTMUTEX && HAVE_LIBFREETYPE +#if HAVE_GD_BUNDLED && HAVE_LIBFREETYPE gdFontCacheMutexShutdown(); #endif UNREGISTER_INI_ENTRIES(); @@ -1147,7 +1119,7 @@ PHP_MINIT_FUNCTION(gd) le_gd = zend_register_list_destructors_ex(php_free_gd_image, NULL, "gd", module_number); le_gd_font = zend_register_list_destructors_ex(php_free_gd_font, NULL, "gd font", module_number); -#if HAVE_GD_FONTMUTEX && HAVE_LIBFREETYPE +#if HAVE_GD_BUNDLED && HAVE_LIBFREETYPE gdFontCacheMutexSetup(); #endif #if HAVE_LIBT1 @@ -1157,7 +1129,9 @@ PHP_MINIT_FUNCTION(gd) le_ps_font = zend_register_list_destructors_ex(php_free_ps_font, NULL, "gd PS font", module_number); le_ps_enc = zend_register_list_destructors_ex(php_free_ps_enc, NULL, "gd PS encoding", module_number); #endif - +#ifndef HAVE_GD_BUNDLED + gdSetErrorMethod(php_gd_error_method); +#endif REGISTER_INI_ENTRIES(); REGISTER_LONG_CONSTANT("IMG_GIF", 1, CONST_CS | CONST_PERSISTENT); @@ -1166,14 +1140,14 @@ PHP_MINIT_FUNCTION(gd) REGISTER_LONG_CONSTANT("IMG_PNG", 4, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_WBMP", 8, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_XPM", 16, CONST_CS | CONST_PERSISTENT); -#ifdef gdTiled + /* special colours for gd */ REGISTER_LONG_CONSTANT("IMG_COLOR_TILED", gdTiled, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_COLOR_STYLED", gdStyled, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_COLOR_BRUSHED", gdBrushed, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_COLOR_STYLEDBRUSHED", gdStyledBrushed, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_COLOR_TRANSPARENT", gdTransparent, CONST_CS | CONST_PERSISTENT); -#endif + /* for imagefilledarc */ REGISTER_LONG_CONSTANT("IMG_ARC_ROUNDED", gdArc, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_ARC_PIE", gdPie, CONST_CS | CONST_PERSISTENT); @@ -1181,18 +1155,54 @@ PHP_MINIT_FUNCTION(gd) REGISTER_LONG_CONSTANT("IMG_ARC_NOFILL", gdNoFill, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_ARC_EDGED", gdEdged, CONST_CS | CONST_PERSISTENT); -/* GD2 image format types */ -#ifdef GD2_FMT_RAW + /* GD2 image format types */ REGISTER_LONG_CONSTANT("IMG_GD2_RAW", GD2_FMT_RAW, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef GD2_FMT_COMPRESSED REGISTER_LONG_CONSTANT("IMG_GD2_COMPRESSED", GD2_FMT_COMPRESSED, CONST_CS | CONST_PERSISTENT); -#endif -#if HAVE_GD_BUNDLED + REGISTER_LONG_CONSTANT("IMG_FLIP_HORIZONTAL", GD_FLIP_HORINZONTAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FLIP_VERTICAL", GD_FLIP_VERTICAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FLIP_BOTH", GD_FLIP_BOTH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_EFFECT_REPLACE", gdEffectReplace, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_EFFECT_ALPHABLEND", gdEffectAlphaBlend, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_EFFECT_NORMAL", gdEffectNormal, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_EFFECT_OVERLAY", gdEffectOverlay, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("IMG_CROP_DEFAULT", GD_CROP_DEFAULT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_TRANSPARENT", GD_CROP_TRANSPARENT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_BLACK", GD_CROP_BLACK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_WHITE", GD_CROP_WHITE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_SIDES", GD_CROP_SIDES, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_THRESHOLD", GD_CROP_THRESHOLD, CONST_CS | CONST_PERSISTENT); + + + REGISTER_LONG_CONSTANT("IMG_BELL", GD_BELL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BESSEL", GD_BESSEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BILINEAR_FIXED", GD_BILINEAR_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC", GD_BICUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC_FIXED", GD_BICUBIC_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BLACKMAN", GD_BLACKMAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BOX", GD_BOX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BSPLINE", GD_BSPLINE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CATMULLROM", GD_CATMULLROM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GAUSSIAN", GD_GAUSSIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GENERALIZED_CUBIC", GD_GENERALIZED_CUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HERMITE", GD_HERMITE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HAMMING", GD_HAMMING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HANNING", GD_HANNING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_MITCHELL", GD_MITCHELL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_POWER", GD_POWER, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_QUADRATIC", GD_QUADRATIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_SINC", GD_SINC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_NEAREST_NEIGHBOUR", GD_NEAREST_NEIGHBOUR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_WEIGHTED4", GD_WEIGHTED4, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_TRIANGLE", GD_TRIANGLE, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("IMG_AFFINE_TRANSLATE", GD_AFFINE_TRANSLATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SCALE", GD_AFFINE_SCALE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_ROTATE", GD_AFFINE_ROTATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_HORIZONTAL", GD_AFFINE_SHEAR_HORIZONTAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_VERTICAL", GD_AFFINE_SHEAR_VERTICAL, CONST_CS | CONST_PERSISTENT); + +#if defined(HAVE_GD_BUNDLED) REGISTER_LONG_CONSTANT("GD_BUNDLED", 1, CONST_CS | CONST_PERSISTENT); #else REGISTER_LONG_CONSTANT("GD_BUNDLED", 0, CONST_CS | CONST_PERSISTENT); @@ -1247,27 +1257,19 @@ PHP_MINIT_FUNCTION(gd) /* {{{ PHP_RSHUTDOWN_FUNCTION */ -#if HAVE_GD_STRINGFT && (HAVE_LIBFREETYPE && (HAVE_GD_FONTCACHESHUTDOWN || HAVE_GD_FREEFONTCACHE)) +#if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_RSHUTDOWN_FUNCTION(gd) { -#if HAVE_GD_FONTCACHESHUTDOWN gdFontCacheShutdown(); -#else - gdFreeFontCache(); -#endif return SUCCESS; } #endif /* }}} */ -#if HAVE_GD_BUNDLED +#if defined(HAVE_GD_BUNDLED) #define PHP_GD_VERSION_STRING "bundled (2.1.0 compatible)" #else -# ifdef GD_VERSION_STRING -# define PHP_GD_VERSION_STRING GD_VERSION_STRING -# else -# define PHP_GD_VERSION_STRING "2.0" -# endif +# define PHP_GD_VERSION_STRING GD_VERSION_STRING #endif /* {{{ PHP_MINFO_FUNCTION @@ -1306,20 +1308,13 @@ PHP_MINFO_FUNCTION(gd) php_info_print_table_row(2, "T1Lib Support", "enabled"); #endif -/* this next part is stupid ... if I knew better, I'd put them all on one row (cmv) */ - -#ifdef HAVE_GD_GIF_READ php_info_print_table_row(2, "GIF Read Support", "enabled"); -#endif -#ifdef HAVE_GD_GIF_CREATE php_info_print_table_row(2, "GIF Create Support", "enabled"); -#endif + #ifdef HAVE_GD_JPG { - char tmp[12]; - snprintf(tmp, sizeof(tmp), "%s", gdJpegGetVersionString()); php_info_print_table_row(2, "JPEG Support", "enabled"); - php_info_print_table_row(2, "libJPEG Version", tmp); + php_info_print_table_row(2, "libJPEG Version", gdJpegGetVersionString()); } #endif @@ -1327,10 +1322,8 @@ PHP_MINFO_FUNCTION(gd) php_info_print_table_row(2, "PNG Support", "enabled"); php_info_print_table_row(2, "libPNG Version", gdPngGetVersionString()); #endif -#ifdef HAVE_GD_WBMP php_info_print_table_row(2, "WBMP Support", "enabled"); -#endif -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) php_info_print_table_row(2, "XPM Support", "enabled"); { char tmp[12]; @@ -1338,12 +1331,13 @@ PHP_MINFO_FUNCTION(gd) php_info_print_table_row(2, "libXpm Version", tmp); } #endif -#ifdef HAVE_GD_XBM php_info_print_table_row(2, "XBM Support", "enabled"); -#endif -#if defined(USE_GD_JISX0208) && defined(HAVE_GD_BUNDLED) +#if defined(USE_GD_JISX0208) php_info_print_table_row(2, "JIS-mapped Japanese Font Support", "enabled"); #endif +#ifdef HAVE_GD_WEBP + php_info_print_table_row(2, "WebP Support", "enabled"); +#endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } @@ -1377,16 +1371,8 @@ PHP_FUNCTION(gd_info) #else add_assoc_bool(return_value, "T1Lib Support", 0); #endif -#ifdef HAVE_GD_GIF_READ add_assoc_bool(return_value, "GIF Read Support", 1); -#else - add_assoc_bool(return_value, "GIF Read Support", 0); -#endif -#ifdef HAVE_GD_GIF_CREATE add_assoc_bool(return_value, "GIF Create Support", 1); -#else - add_assoc_bool(return_value, "GIF Create Support", 0); -#endif #ifdef HAVE_GD_JPG add_assoc_bool(return_value, "JPEG Support", 1); #else @@ -1397,22 +1383,14 @@ PHP_FUNCTION(gd_info) #else add_assoc_bool(return_value, "PNG Support", 0); #endif -#ifdef HAVE_GD_WBMP add_assoc_bool(return_value, "WBMP Support", 1); -#else - add_assoc_bool(return_value, "WBMP Support", 0); -#endif -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) add_assoc_bool(return_value, "XPM Support", 1); #else add_assoc_bool(return_value, "XPM Support", 0); #endif -#ifdef HAVE_GD_XBM add_assoc_bool(return_value, "XBM Support", 1); -#else - add_assoc_bool(return_value, "XBM Support", 0); -#endif -#if defined(USE_GD_JISX0208) && defined(HAVE_GD_BUNDLED) +#if defined(USE_GD_JISX0208) add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 1); #else add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 0); @@ -1427,62 +1405,6 @@ PHP_GD_API int phpi_get_le_gd(void) } /* }}} */ -#ifndef HAVE_GDIMAGECOLORRESOLVE - -/* {{{ gdImageColorResolve - */ -/********************************************************************/ -/* gdImageColorResolve is a replacement for the old fragment: */ -/* */ -/* if ((color=gdImageColorExact(im,R,G,B)) < 0) */ -/* if ((color=gdImageColorAllocate(im,R,G,B)) < 0) */ -/* color=gdImageColorClosest(im,R,G,B); */ -/* */ -/* in a single function */ - -int gdImageColorResolve(gdImagePtr im, int r, int g, int b) -{ - int c; - int ct = -1; - int op = -1; - long rd, gd, bd, dist; - long mindist = 3*255*255; /* init to max poss dist */ - - for (c = 0; c < im->colorsTotal; c++) { - if (im->open[c]) { - op = c; /* Save open slot */ - continue; /* Color not in use */ - } - rd = (long) (im->red [c] - r); - gd = (long) (im->green[c] - g); - bd = (long) (im->blue [c] - b); - dist = rd * rd + gd * gd + bd * bd; - if (dist < mindist) { - if (dist == 0) { - return c; /* Return exact match color */ - } - mindist = dist; - ct = c; - } - } - /* no exact match. We now know closest, but first try to allocate exact */ - if (op == -1) { - op = im->colorsTotal; - if (op == gdMaxColors) { /* No room for more colors */ - return ct; /* Return closest available color */ - } - im->colorsTotal++; - } - im->red [op] = r; - im->green[op] = g; - im->blue [op] = b; - im->open [op] = 0; - return op; /* Return newly allocated color */ -} -/* }}} */ - -#endif - #define FLIPWORD(a) (((a & 0xff000000) >> 24) | ((a & 0x00ff0000) >> 8) | ((a & 0x0000ff00) << 8) | ((a & 0x000000ff) << 24)) /* {{{ proto int imageloadfont(string filename) @@ -1545,13 +1467,7 @@ PHP_FUNCTION(imageloadfont) body_size = font->w * font->h * font->nchars; } - if (overflow2(font->nchars, font->h)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading font, invalid font header"); - efree(font); - php_stream_close(stream); - RETURN_FALSE; - } - if (overflow2(font->nchars * font->h, font->w )) { + if (overflow2(font->nchars, font->h) || overflow2(font->nchars * font->h, font->w )) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading font, invalid font header"); efree(font); php_stream_close(stream); @@ -1703,6 +1619,29 @@ PHP_FUNCTION(imagetruecolortopalette) } /* }}} */ + + +/* {{{ proto void imagetruecolortopalette(resource im, bool ditherFlag, int colorsWanted) + Convert a true colour image to a palette based image with a number of colours, optionally using dithering. */ +PHP_FUNCTION(imagepalettetotruecolor) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &IM) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + if (gdImagePaletteToTrueColor(im) == 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto bool imagecolormatch(resource im1, resource im2) Makes the colors of the palette version of an image more closely match the true color version */ PHP_FUNCTION(imagecolormatch) @@ -1851,7 +1790,6 @@ PHP_FUNCTION(imagesavealpha) } /* }}} */ -#if HAVE_GD_BUNDLED /* {{{ proto bool imagelayereffect(resource im, int effect) Set the alpha blending flag to use the bundled libgd layering effects */ PHP_FUNCTION(imagelayereffect) @@ -1870,7 +1808,6 @@ PHP_FUNCTION(imagelayereffect) RETURN_TRUE; } /* }}} */ -#endif /* {{{ proto int imagecolorallocatealpha(resource im, int red, int green, int blue, int alpha) Allocate a color with an alpha level. Works for true color and palette based images */ @@ -2145,7 +2082,7 @@ PHP_FUNCTION(imagerotate) ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); - im_dst = gdImageRotate(im_src, degrees, color, ignoretransparent); + im_dst = gdImageRotateInterpolated(im_src, (float)degrees, color); if (im_dst != NULL) { ZEND_REGISTER_RESOURCE(return_value, im_dst, le_gd); @@ -2155,7 +2092,6 @@ PHP_FUNCTION(imagerotate) } /* }}} */ -#if HAVE_GD_IMAGESETTILE /* {{{ proto bool imagesettile(resource image, resource tile) Set the tile image to $tile when filling $image with the "IMG_COLOR_TILED" color */ PHP_FUNCTION(imagesettile) @@ -2175,9 +2111,7 @@ PHP_FUNCTION(imagesettile) RETURN_TRUE; } /* }}} */ -#endif -#if HAVE_GD_IMAGESETBRUSH /* {{{ proto bool imagesetbrush(resource image, resource brush) Set the brush image to $brush when filling $image with the "IMG_COLOR_BRUSHED" color */ PHP_FUNCTION(imagesetbrush) @@ -2197,7 +2131,6 @@ PHP_FUNCTION(imagesetbrush) RETURN_TRUE; } /* }}} */ -#endif /* {{{ proto resource imagecreate(int x_size, int y_size) Create a new image */ @@ -2230,19 +2163,15 @@ PHP_FUNCTION(imagecreate) PHP_FUNCTION(imagetypes) { int ret=0; -#ifdef HAVE_GD_GIF_CREATE ret = 1; -#endif #ifdef HAVE_GD_JPG ret |= 2; #endif #ifdef HAVE_GD_PNG ret |= 4; #endif -#ifdef HAVE_GD_WBMP ret |= 8; -#endif -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) ret |= 16; #endif @@ -2254,13 +2183,31 @@ PHP_FUNCTION(imagetypes) } /* }}} */ +/* {{{ _php_ctx_getmbi + */ + +static int _php_ctx_getmbi(gdIOCtx *ctx) +{ + int i, mbi = 0; + + do { + i = (ctx->getC)(ctx); + if (i < 0) { + return -1; + } + mbi = (mbi << 7) | (i & 0x7f); + } while (i & 0x80); + + return mbi; +} +/* }}} */ + /* {{{ _php_image_type */ static const char php_sig_gd2[3] = {'g', 'd', '2'}; static int _php_image_type (char data[8]) { -#ifdef HAVE_LIBGD15 /* Based on ext/standard/image.c */ if (data == NULL) { @@ -2278,34 +2225,22 @@ static int _php_image_type (char data[8]) } else if (!memcmp(data, php_sig_gif, 3)) { return PHP_GDIMG_TYPE_GIF; } -#ifdef HAVE_GD_WBMP else { gdIOCtx *io_ctx; io_ctx = gdNewDynamicCtxEx(8, data, 0); if (io_ctx) { - if (getmbi((int(*)(void *)) gdGetC, io_ctx) == 0 && skipheader((int(*)(void *)) gdGetC, io_ctx) == 0 ) { -#if HAVE_LIBGD204 + if (_php_ctx_getmbi(io_ctx) == 0 && _php_ctx_getmbi(io_ctx) >= 0) { io_ctx->gd_free(io_ctx); -#else - io_ctx->free(io_ctx); -#endif return PHP_GDIMG_TYPE_WBM; } else { -#if HAVE_LIBGD204 io_ctx->gd_free(io_ctx); -#else - io_ctx->free(io_ctx); -#endif } } } -#endif return -1; -#endif } /* }}} */ -#ifdef HAVE_LIBGD15 /* {{{ _php_image_create_from_string */ gdImagePtr _php_image_create_from_string(zval **data, char *tn, gdImagePtr (*ioctx_func_p)() TSRMLS_DC) @@ -2322,19 +2257,11 @@ gdImagePtr _php_image_create_from_string(zval **data, char *tn, gdImagePtr (*ioc im = (*ioctx_func_p)(io_ctx); if (!im) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed data is not in '%s' format", tn); -#if HAVE_LIBGD204 io_ctx->gd_free(io_ctx); -#else - io_ctx->free(io_ctx); -#endif return NULL; } -#if HAVE_LIBGD204 io_ctx->gd_free(io_ctx); -#else - io_ctx->free(io_ctx); -#endif return im; } @@ -2383,30 +2310,15 @@ PHP_FUNCTION(imagecreatefromstring) break; case PHP_GDIMG_TYPE_GIF: -#ifdef HAVE_GD_GIF_READ im = _php_image_create_from_string(data, "GIF", gdImageCreateFromGifCtx TSRMLS_CC); -#else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "No GIF support in this PHP build"); - RETURN_FALSE; -#endif break; case PHP_GDIMG_TYPE_WBM: -#ifdef HAVE_GD_WBMP im = _php_image_create_from_string(data, "WBMP", gdImageCreateFromWBMPCtx TSRMLS_CC); -#else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "No WBMP support in this PHP build"); - RETURN_FALSE; -#endif break; case PHP_GDIMG_TYPE_GD2: -#ifdef HAVE_GD_GD2 im = _php_image_create_from_string(data, "GD2", gdImageCreateFromGd2Ctx TSRMLS_CC); -#else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "No GD2 support in this PHP build"); - RETURN_FALSE; -#endif break; default: @@ -2422,7 +2334,6 @@ PHP_FUNCTION(imagecreatefromstring) ZEND_REGISTER_RESOURCE(return_value, im, le_gd); } /* }}} */ -#endif /* {{{ _php_image_create_from */ @@ -2434,9 +2345,8 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, gdImagePtr im = NULL; php_stream *stream; FILE * fp = NULL; -#ifdef HAVE_GD_JPG long ignore_warning; -#endif + if (image_type == PHP_GDIMG_TYPE_GD2PART) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sllll", &file, &file_len, &srcx, &srcy, &width, &height) == FAILURE) { return; @@ -2451,39 +2361,18 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, } } + stream = php_stream_open_wrapper(file, "rb", REPORT_ERRORS|IGNORE_PATH|IGNORE_URL_WIN, NULL); if (stream == NULL) { RETURN_FALSE; } -#ifndef USE_GD_IOCTX - ioctx_func_p = NULL; /* don't allow sockets without IOCtx */ -#endif - - if (image_type == PHP_GDIMG_TYPE_WEBP) { - size_t buff_size; - char *buff; - - /* needs to be malloc (persistent) - GD will free() it later */ - buff_size = php_stream_copy_to_mem(stream, &buff, PHP_STREAM_COPY_ALL, 1); - if (!buff_size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot read image data"); - goto out_err; - } - im = (*ioctx_func_p)(buff_size, buff); - if (!im) { - goto out_err; - } - goto register_im; - } - /* try and avoid allocating a FILE* if the stream is not naturally a FILE* */ if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) { if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS)) { goto out_err; } } else if (ioctx_func_p) { -#ifdef USE_GD_IOCTX /* we can create an io context */ gdIOCtx* io_ctx; size_t buff_size; @@ -2509,15 +2398,10 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, } else { im = (*ioctx_func_p)(io_ctx); } -#if HAVE_LIBGD204 io_ctx->gd_free(io_ctx); -#else - io_ctx->free(io_ctx); -#endif pefree(buff, 1); -#endif } - else { + else if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO)) { /* try and force the stream to be FILE* */ if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void **) &fp, REPORT_ERRORS)) { goto out_err; @@ -2529,7 +2413,7 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, case PHP_GDIMG_TYPE_GD2PART: im = (*func_p)(fp, srcx, srcy, width, height); break; -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) case PHP_GDIMG_TYPE_XPM: im = gdImageCreateFromXpm(file); break; @@ -2538,11 +2422,7 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, #ifdef HAVE_GD_JPG case PHP_GDIMG_TYPE_JPG: ignore_warning = INI_INT("gd.jpeg_ignore_warning"); -#ifdef HAVE_GD_BUNDLED - im = gdImageCreateFromJpeg(fp, ignore_warning); -#else - im = gdImageCreateFromJpeg(fp); -#endif + im = gdImageCreateFromJpegEx(fp, ignore_warning); break; #endif @@ -2569,7 +2449,6 @@ out_err: } /* }}} */ -#ifdef HAVE_GD_GIF_READ /* {{{ proto resource imagecreatefromgif(string filename) Create a new image from GIF file or URL */ PHP_FUNCTION(imagecreatefromgif) @@ -2577,7 +2456,6 @@ PHP_FUNCTION(imagecreatefromgif) _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF", gdImageCreateFromGif, gdImageCreateFromGifCtx); } /* }}} */ -#endif /* HAVE_GD_GIF_READ */ #ifdef HAVE_GD_JPG /* {{{ proto resource imagecreatefromjpeg(string filename) @@ -2600,16 +2478,15 @@ PHP_FUNCTION(imagecreatefrompng) #endif /* HAVE_GD_PNG */ #ifdef HAVE_GD_WEBP -/* {{{ proto resource imagecreatefrompng(string filename) - Create a new image from PNG file or URL */ +/* {{{ proto resource imagecreatefromwebp(string filename) + Create a new image from WEBP file or URL */ PHP_FUNCTION(imagecreatefromwebp) { - _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageCreateFromWebpPtr, gdImageCreateFromWebpPtr); + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageCreateFromWebp, gdImageCreateFromWebpCtx); } /* }}} */ #endif /* HAVE_GD_VPX */ -#ifdef HAVE_GD_XBM /* {{{ proto resource imagecreatefromxbm(string filename) Create a new image from XBM file or URL */ PHP_FUNCTION(imagecreatefromxbm) @@ -2617,9 +2494,8 @@ PHP_FUNCTION(imagecreatefromxbm) _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM", gdImageCreateFromXbm, NULL); } /* }}} */ -#endif /* HAVE_GD_XBM */ -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) /* {{{ proto resource imagecreatefromxpm(string filename) Create a new image from XPM file or URL */ PHP_FUNCTION(imagecreatefromxpm) @@ -2629,7 +2505,6 @@ PHP_FUNCTION(imagecreatefromxpm) /* }}} */ #endif -#ifdef HAVE_GD_WBMP /* {{{ proto resource imagecreatefromwbmp(string filename) Create a new image from WBMP file or URL */ PHP_FUNCTION(imagecreatefromwbmp) @@ -2637,7 +2512,6 @@ PHP_FUNCTION(imagecreatefromwbmp) _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP", gdImageCreateFromWBMP, gdImageCreateFromWBMPCtx); } /* }}} */ -#endif /* HAVE_GD_WBMP */ /* {{{ proto resource imagecreatefromgd(string filename) Create a new image from GD file or URL */ @@ -2647,7 +2521,6 @@ PHP_FUNCTION(imagecreatefromgd) } /* }}} */ -#ifdef HAVE_GD_GD2 /* {{{ proto resource imagecreatefromgd2(string filename) Create a new image from GD2 file or URL */ PHP_FUNCTION(imagecreatefromgd2) @@ -2663,7 +2536,6 @@ PHP_FUNCTION(imagecreatefromgd2part) _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2PART, "GD2", gdImageCreateFromGd2Part, gdImageCreateFromGd2PartCtx); } /* }}} */ -#endif /* HAVE_GD_GD2 */ /* {{{ _php_image_output */ @@ -2708,7 +2580,6 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } switch (image_type) { -#ifdef HAVE_GD_WBMP case PHP_GDIMG_CONVERT_WBM: if (q == -1) { q = 0; @@ -2718,7 +2589,6 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } gdImageWBMP(im, q, fp); break; -#endif case PHP_GDIMG_TYPE_JPG: (*func_p)(im, fp, q); break; @@ -2734,14 +2604,12 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } (*func_p)(im, fp); break; -#ifdef HAVE_GD_GD2 case PHP_GDIMG_TYPE_GD2: if (q == -1) { q = 128; } (*func_p)(im, fp, q, t); break; -#endif default: if (q == -1) { q = 128; @@ -2764,7 +2632,6 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } switch (image_type) { -#ifdef HAVE_GD_WBMP case PHP_GDIMG_CONVERT_WBM: if (q == -1) { q = 0; @@ -2774,7 +2641,6 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } gdImageWBMP(im, q, tmp); break; -#endif case PHP_GDIMG_TYPE_JPG: (*func_p)(im, tmp, q); break; @@ -2792,14 +2658,12 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char } (*func_p)(im, tmp); break; -#ifdef HAVE_GD_GD2 case PHP_GDIMG_TYPE_GD2: if (q == -1) { q = 128; } (*func_p)(im, tmp, q, t); break; -#endif default: (*func_p)(im, tmp); break; @@ -2827,15 +2691,12 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char /* {{{ proto int imagexbm(int im, string filename [, int foreground]) Output XBM image to browser or file */ -#if HAVE_GD_BUNDLED PHP_FUNCTION(imagexbm) { _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM", gdImageXbmCtx); } -#endif /* }}} */ -#ifdef HAVE_GD_GIF_CREATE /* {{{ proto bool imagegif(resource im [, string filename]) Output GIF image to browser or file */ PHP_FUNCTION(imagegif) @@ -2843,7 +2704,6 @@ PHP_FUNCTION(imagegif) _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF", gdImageGifCtx); } /* }}} */ -#endif /* HAVE_GD_GIF_CREATE */ #ifdef HAVE_GD_PNG /* {{{ proto bool imagepng(resource im [, string filename]) @@ -2858,7 +2718,7 @@ PHP_FUNCTION(imagepng) #ifdef HAVE_GD_WEBP /* {{{ proto bool imagewebp(resource im [, string filename[, quality]] ) - Output PNG image to browser or file */ + Output WEBP image to browser or file */ PHP_FUNCTION(imagewebp) { _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageWebpCtx); @@ -2877,7 +2737,6 @@ PHP_FUNCTION(imagejpeg) /* }}} */ #endif /* HAVE_GD_JPG */ -#ifdef HAVE_GD_WBMP /* {{{ proto bool imagewbmp(resource im [, string filename, [, int foreground]]) Output WBMP image to browser or file */ PHP_FUNCTION(imagewbmp) @@ -2885,7 +2744,6 @@ PHP_FUNCTION(imagewbmp) _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP", gdImageWBMPCtx); } /* }}} */ -#endif /* HAVE_GD_WBMP */ /* {{{ proto bool imagegd(resource im [, string filename]) Output GD image to browser or file */ @@ -2895,7 +2753,6 @@ PHP_FUNCTION(imagegd) } /* }}} */ -#ifdef HAVE_GD_GD2 /* {{{ proto bool imagegd2(resource im [, string filename, [, int chunk_size, [, int type]]]) Output GD2 image to browser or file */ PHP_FUNCTION(imagegd2) @@ -2903,7 +2760,6 @@ PHP_FUNCTION(imagegd2) _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2, "GD2", gdImageGd2); } /* }}} */ -#endif /* HAVE_GD_GD2 */ /* {{{ proto bool imagedestroy(resource im) Destroy an image */ @@ -2948,7 +2804,6 @@ PHP_FUNCTION(imagecolorallocate) } /* }}} */ -#if HAVE_LIBGD15 /* {{{ proto void imagepalettecopy(resource dst, resource src) Copy the palette from the src image onto the dst image */ PHP_FUNCTION(imagepalettecopy) @@ -2966,7 +2821,6 @@ PHP_FUNCTION(imagepalettecopy) gdImagePaletteCopy(dst, src); } /* }}} */ -#endif /* {{{ proto int imagecolorat(resource im, int x, int y) Get the index of the color of a pixel */ @@ -3018,7 +2872,6 @@ PHP_FUNCTION(imagecolorclosest) } /* }}} */ -#if HAVE_COLORCLOSESTHWB /* {{{ proto int imagecolorclosesthwb(resource im, int red, int green, int blue) Get the index of the color which has the hue, white and blackness nearest to the given color */ PHP_FUNCTION(imagecolorclosesthwb) @@ -3036,7 +2889,6 @@ PHP_FUNCTION(imagecolorclosesthwb) RETURN_LONG(gdImageColorClosestHWB(im, red, green, blue)); } /* }}} */ -#endif /* {{{ proto bool imagecolordeallocate(resource im, int index) De-allocate a color for an image */ @@ -3758,7 +3610,6 @@ PHP_FUNCTION(imagecopy) } /* }}} */ -#if HAVE_LIBGD15 /* {{{ proto bool imagecopymerge(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct) Merge one part of an image with another */ PHP_FUNCTION(imagecopymerge) @@ -3816,7 +3667,6 @@ PHP_FUNCTION(imagecopymergegray) RETURN_TRUE; } /* }}} */ -#endif /* {{{ proto bool imagecopyresized(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h) Copy and resize part of an image */ @@ -3894,7 +3744,7 @@ PHP_FUNCTION(imagesy) #ifdef ENABLE_GD_TTF -#if HAVE_LIBFREETYPE && HAVE_GD_STRINGFTEX +#if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE /* {{{ proto array imageftbbox(float size, float angle, string font_file, string text [, array extrainfo]) Give the bounding box of a text using fonts via freetype2 */ PHP_FUNCTION(imageftbbox) @@ -3910,7 +3760,7 @@ PHP_FUNCTION(imagefttext) php_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_DRAW, 1); } /* }}} */ -#endif +#endif /* HAVE_GD_FREETYPE && HAVE_LIBFREETYPE */ /* {{{ proto array imagettfbbox(float size, float angle, string font_file, string text) Give the bounding box of a text using TrueType fonts */ @@ -3940,13 +3790,7 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int char *str = NULL, *fontname = NULL; char *error = NULL; int argc = ZEND_NUM_ARGS(); -#if HAVE_GD_STRINGFTEX gdFTStringExtra strex = {0}; -#endif - -#if !HAVE_GD_STRINGFTEX - assert(!extended); -#endif if (mode == TTFTEXT_BBOX) { if (argc < 4 || argc > ((extended) ? 5 : 4)) { @@ -3966,7 +3810,6 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int /* convert angle to radians */ angle = angle * (M_PI/180); -#if HAVE_GD_STRINGFTEX if (extended && EXT) { /* parse extended info */ HashPosition pos; @@ -3993,7 +3836,6 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int } while (zend_hash_move_forward_ex(HASH_OF(EXT), &pos) == SUCCESS); } -#endif #ifdef VIRTUAL_DIR { @@ -4003,25 +3845,18 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int fontname = NULL; } } -#endif +#endif /* VIRTUAL_DIR */ PHP_GD_CHECK_OPEN_BASEDIR(fontname, "Invalid font filename"); -#ifdef USE_GD_IMGSTRTTF -# if HAVE_GD_STRINGFTEX +#ifdef HAVE_GD_FREETYPE if (extended) { error = gdImageStringFTEx(im, brect, col, fontname, ptsize, angle, x, y, str, &strex); } else -# endif + error = gdImageStringFT(im, brect, col, fontname, ptsize, angle, x, y, str); -# if HAVE_GD_STRINGFT - error = gdImageStringFT(im, brect, col, fontname, ptsize, angle, x, y, str); -# elif HAVE_GD_STRINGTTF - error = gdImageStringTTF(im, brect, col, fontname, ptsize, angle, x, y, str); -# endif - -#endif +#endif /* HAVE_GD_FREETYPE */ if (error) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", error); @@ -4492,7 +4327,6 @@ PHP_FUNCTION(imagepsbbox) /* }}} */ #endif -#ifdef HAVE_GD_WBMP /* {{{ proto bool image2wbmp(resource im [, string filename [, int threshold]]) Output WBMP image to browser or file */ PHP_FUNCTION(image2wbmp) @@ -4500,9 +4334,8 @@ PHP_FUNCTION(image2wbmp) _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_CONVERT_WBM, "WBMP", _php_image_bw_convert); } /* }}} */ -#endif /* HAVE_GD_WBMP */ -#if defined(HAVE_GD_JPG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_JPG) /* {{{ proto bool jpeg2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold) Convert JPEG image to WBMP image */ PHP_FUNCTION(jpeg2wbmp) @@ -4512,7 +4345,7 @@ PHP_FUNCTION(jpeg2wbmp) /* }}} */ #endif -#if defined(HAVE_GD_PNG) && defined(HAVE_GD_WBMP) +#if defined(HAVE_GD_PNG) /* {{{ proto bool png2wbmp (string f_org, string f_dest, int d_height, int d_width, int threshold) Convert PNG image to WBMP image */ PHP_FUNCTION(png2wbmp) @@ -4522,7 +4355,6 @@ PHP_FUNCTION(png2wbmp) /* }}} */ #endif -#ifdef HAVE_GD_WBMP /* {{{ _php_image_bw_convert * It converts a gd Image to bw using a threshold value */ static void _php_image_bw_convert(gdImagePtr im_org, gdIOCtx *out, int threshold) @@ -4569,11 +4401,7 @@ static void _php_image_bw_convert(gdImagePtr im_org, gdIOCtx *out, int threshold gdImageSetPixel (im_dest, x, y, color); } } -#ifdef USE_GD_IOCTX gdImageWBMPCtx (im_dest, black, out); -#else - gdImageWBMP (im_dest, black, out); -#endif } /* }}} */ @@ -4597,9 +4425,7 @@ static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) int int_threshold; int x, y; float x_ratio, y_ratio; -#ifdef HAVE_GD_JPG long ignore_warning; -#endif if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "pplll", &f_org, &f_org_len, &f_dest, &f_dest_len, &height, &width, &threshold) == FAILURE) { return; @@ -4638,7 +4464,6 @@ static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) } switch (image_type) { -#ifdef HAVE_GD_GIF_READ case PHP_GDIMG_TYPE_GIF: im_org = gdImageCreateFromGif(org); if (im_org == NULL) { @@ -4646,16 +4471,11 @@ static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) RETURN_FALSE; } break; -#endif /* HAVE_GD_GIF_READ */ #ifdef HAVE_GD_JPG case PHP_GDIMG_TYPE_JPG: ignore_warning = INI_INT("gd.jpeg_ignore_warning"); -#ifdef HAVE_GD_BUNDLED - im_org = gdImageCreateFromJpeg(org, ignore_warning); -#else - im_org = gdImageCreateFromJpeg(org); -#endif + im_org = gdImageCreateFromJpegEx(org, ignore_warning); if (im_org == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open '%s' Not a valid JPEG file", fn_dest); RETURN_FALSE; @@ -4663,7 +4483,6 @@ static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) break; #endif /* HAVE_GD_JPG */ - #ifdef HAVE_GD_PNG case PHP_GDIMG_TYPE_PNG: im_org = gdImageCreateFromPng(org); @@ -4764,9 +4583,6 @@ static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) RETURN_TRUE; } /* }}} */ -#endif /* HAVE_GD_WBMP */ - -#endif /* HAVE_LIBGD */ /* Section Filters */ #define PHP_GD_SINGLE_RES \ @@ -5045,7 +4861,7 @@ PHP_FUNCTION(imageconvolution) if (zend_hash_index_find(Z_ARRVAL_PP(var), (j), (void **) &var2) == SUCCESS) { SEPARATE_ZVAL(var2); convert_to_double(*var2); - matrix[i][j] = Z_DVAL_PP(var2); + matrix[i][j] = (float)Z_DVAL_PP(var2); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must have a 3x3 matrix"); RETURN_FALSE; @@ -5053,7 +4869,7 @@ PHP_FUNCTION(imageconvolution) } } } - res = gdImageConvolution(im_src, matrix, div, offset); + res = gdImageConvolution(im_src, matrix, (float)div, (float)offset); if (res) { RETURN_TRUE; @@ -5064,6 +4880,42 @@ PHP_FUNCTION(imageconvolution) /* }}} */ /* End section: Filters */ +/* {{{ proto void imageflip(resource im, int mode) + Flip an image (in place) horizontally, vertically or both directions. */ +PHP_FUNCTION(imageflip) +{ + zval *IM; + long mode; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &IM, &mode) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + switch (mode) { + case GD_FLIP_VERTICAL: + gdImageFlipVertical(im); + break; + + case GD_FLIP_HORINZONTAL: + gdImageFlipHorizontal(im); + break; + + case GD_FLIP_BOTH: + gdImageFlipBoth(im); + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown flip mode"); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + #ifdef HAVE_GD_BUNDLED /* {{{ proto bool imageantialias(resource im, bool on) Should antialiased functions used or not*/ @@ -5084,6 +4936,428 @@ PHP_FUNCTION(imageantialias) /* }}} */ #endif +/* {{{ proto void imagecrop(resource im, array rect) + Crop an image using the given coordinates and size, x, y, width and height. */ +PHP_FUNCTION(imagecrop) +{ + zval *IM; + gdImagePtr im; + gdImagePtr im_crop; + gdRect rect; + zval *z_rect; + zval **tmp; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &IM, &z_rect) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + if (zend_hash_find(HASH_OF(z_rect), "x", sizeof("x"), (void **)&tmp) != FAILURE) { + rect.x = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "y", sizeof("x"), (void **)&tmp) != FAILURE) { + rect.y = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "width", sizeof("width"), (void **)&tmp) != FAILURE) { + rect.width = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing width"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "height", sizeof("height"), (void **)&tmp) != FAILURE) { + rect.height = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing height"); + RETURN_FALSE; + } + + im_crop = gdImageCrop(im, &rect); + + if (im_crop == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, im_crop, le_gd); + } +} +/* }}} */ + +/* {{{ proto void imagecropauto(resource im [, int mode [, threshold [, color]]]) + Crop an image automatically using one of the available modes. */ +PHP_FUNCTION(imagecropauto) +{ + zval *IM; + long mode = -1; + long color = -1; + double threshold = 0.5f; + gdImagePtr im; + gdImagePtr im_crop; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ldl", &IM, &mode, &threshold, &color) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + switch (mode) { + case -1: + mode = GD_CROP_DEFAULT; + case GD_CROP_DEFAULT: + case GD_CROP_TRANSPARENT: + case GD_CROP_BLACK: + case GD_CROP_WHITE: + case GD_CROP_SIDES: + im_crop = gdImageCropAuto(im, mode); + break; + + case GD_CROP_THRESHOLD: + if (color < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Color argument missing with threshold mode"); + RETURN_FALSE; + } + im_crop = gdImageCropThreshold(im, color, (float) threshold); + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown crop mode"); + RETURN_FALSE; + } + if (im_crop == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, im_crop, le_gd); + } +} +/* }}} */ + +/* {{{ proto resource imagescale(resource im, new_width[, new_height[, method]]) + Scale an image using the given new width and height. */ +PHP_FUNCTION(imagescale) +{ + zval *IM; + gdImagePtr im; + gdImagePtr im_scaled; + int new_width, new_height = -1; + gdInterpolationMethod method = GD_BILINEAR_FIXED; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|ll", &IM, &new_width, &new_height, &method) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + im_scaled = gdImageScale(im, new_width, new_height); + goto finish; + switch (method) { + case GD_NEAREST_NEIGHBOUR: + im_scaled = gdImageScaleNearestNeighbour(im, new_width, new_height); + break; + + case GD_BILINEAR_FIXED: + im_scaled = gdImageScaleBilinear(im, new_width, new_height); + break; + + case GD_BICUBIC: + im_scaled = gdImageScaleBicubicFixed(im, new_width, new_height); + break; + + case GD_BICUBIC_FIXED: + im_scaled = gdImageScaleBicubicFixed(im, new_width, new_height); + break; + + default: + im_scaled = gdImageScaleTwoPass(im, im->sx, im->sy, new_width, new_height); + break; + + } +finish: + if (im_scaled == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, im_scaled, le_gd); + } +} +/* }}} */ + +/* {{{ proto resource imageaffine(resource src, array affine[, array clip]) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffine) +{ + zval *IM; + gdImagePtr src; + gdImagePtr dst; + gdRect rect; + gdRectPtr pRect = NULL; + zval *z_rect = NULL; + zval *z_affine; + zval **tmp; + double affine[6]; + int i, nelems; + zval **zval_affine_elem = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|a", &IM, &z_affine, &z_rect) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(src, gdImagePtr, &IM, -1, "Image", le_gd); + + if ((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_affine))) != 6) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Affine array must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < nelems; i++) { + if (zend_hash_index_find(Z_ARRVAL_P(z_affine), i, (void **) &zval_affine_elem) == SUCCESS) { + switch (Z_TYPE_PP(zval_affine_elem)) { + case IS_LONG: + affine[i] = Z_LVAL_PP(zval_affine_elem); + break; + case IS_DOUBLE: + affine[i] = Z_DVAL_PP(zval_affine_elem); + break; + case IS_STRING: + convert_to_double_ex(zval_affine_elem); + affine[i] = Z_DVAL_PP(zval_affine_elem); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (z_rect != NULL) { + if (zend_hash_find(HASH_OF(z_rect), "x", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.x = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "y", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.y = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "width", sizeof("width"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.width = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing width"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "height", sizeof("height"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.height = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing height"); + RETURN_FALSE; + } + pRect = ▭ + } else { + rect.x = -1; + rect.y = -1; + rect.width = gdImageSX(src); + rect.height = gdImageSY(src); + pRect = NULL; + } + + + //int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]); + if (gdTransformAffineGetImage(&dst, src, pRect, affine) != GD_TRUE) { + RETURN_FALSE; + } + + if (dst == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, dst, le_gd); + } +} +/* }}} */ + +/* {{{ proto array imageaffinematrixget(type[, options]) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffinematrixget) +{ + double affine[6]; + long type; + zval *options; + zval **tmp; + int res = GD_FALSE, i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &type, &options) == FAILURE) { + return; + } + + switch((gdAffineStandardMatrix)type) { + case GD_AFFINE_TRANSLATE: + case GD_AFFINE_SCALE: { + double x, y; + if (Z_TYPE_P(options) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array expected as options"); + } + if (zend_hash_find(HASH_OF(options), "x", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_double_ex(tmp); + x = Z_DVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(options), "y", sizeof("y"), (void **)&tmp) != FAILURE) { + convert_to_double_ex(tmp); + y = Z_DVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (type == GD_AFFINE_TRANSLATE) { + res = gdAffineTranslate(affine, x, y); + } else { + res = gdAffineScale(affine, x, y); + } + break; + } + + case GD_AFFINE_ROTATE: + case GD_AFFINE_SHEAR_HORIZONTAL: + case GD_AFFINE_SHEAR_VERTICAL: { + double angle; + + convert_to_double_ex(&options); + angle = Z_DVAL_P(options); + + if (type == GD_AFFINE_SHEAR_HORIZONTAL) { + res = gdAffineShearHorizontal(affine, angle); + } else if (type == GD_AFFINE_SHEAR_VERTICAL) { + res = gdAffineShearVertical(affine, angle); + } else { + res = gdAffineRotate(affine, angle); + } + break; + } + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %li", type); + RETURN_FALSE; + } + + if (res == GD_FALSE) { + RETURN_FALSE; + } else { + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, affine[i]); + } + } +} + + +/* {{{ proto array imageaffineconcat(array m1, array m2) + Concat two matrices (as in doing many ops in one go) */ +PHP_FUNCTION(imageaffinematrixconcat) +{ + double m1[6]; + double m2[6]; + double mr[6]; + + zval **tmp; + zval *z_m1; + zval *z_m2; + int i, nelems; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &z_m1, &z_m2) == FAILURE) { + return; + } + + if (((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m1))) != 6) || (nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m2))) != 6) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Affine arrays must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < 6; i++) { + if (zend_hash_index_find(Z_ARRVAL_P(z_m1), i, (void **) &tmp) == SUCCESS) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + m1[i] = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + m1[i] = Z_DVAL_PP(tmp); + break; + case IS_STRING: + convert_to_double_ex(tmp); + m1[i] = Z_DVAL_PP(tmp); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + if (zend_hash_index_find(Z_ARRVAL_P(z_m2), i, (void **) &tmp) == SUCCESS) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + m2[i] = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + m2[i] = Z_DVAL_PP(tmp); + break; + case IS_STRING: + convert_to_double_ex(tmp); + m2[i] = Z_DVAL_PP(tmp); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (gdAffineConcat (mr, m1, m2) != GD_TRUE) { + RETURN_FALSE; + } + + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, mr[i]); + } +} + +/* {{{ proto resource imagesetinterpolation(resource im, [, method]]) + Set the default interpolation method, passing -1 or 0 sets it to the libgd default (bilinear). */ +PHP_FUNCTION(imagesetinterpolation) +{ + zval *IM; + gdImagePtr im; + long method = GD_BILINEAR_FIXED; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &IM, &method) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + if (method == -1) { + method = GD_BILINEAR_FIXED; + } + RETURN_BOOL(gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method)); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/gd/gd_compat.c b/ext/gd/gd_compat.c new file mode 100644 index 0000000000..dc6a95ae11 --- /dev/null +++ b/ext/gd/gd_compat.c @@ -0,0 +1,63 @@ +#include "php_config.h" + +#ifdef HAVE_GD_PNG +/* needs to be first */ +# include <png.h> +#endif + +#ifdef HAVE_GD_JPG +# include <jpeglib.h> +#endif + +#include "gd_compat.h" +#include "php.h" + +#ifdef HAVE_GD_JPG +int gdJpegGetVersionInt() +{ + return JPEG_LIB_VERSION; +} + +const char * gdJpegGetVersionString() +{ + switch(JPEG_LIB_VERSION) { + case 62: + return "6b"; + break; + + case 70: + return "7"; + break; + + case 80: + return "8"; + break; + + default: + return "unknown"; + } +} +#endif + +#ifdef HAVE_GD_PNG +const char * gdPngGetVersionString() +{ + return PNG_LIBPNG_VER_STRING; +} +#endif + +int overflow2(int a, int b) +{ + TSRMLS_FETCH(); + + if(a <= 0 || b <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "gd warning: one parameter to a memory allocation multiplication is negative or zero, failing operation gracefully\n"); + return 1; + } + if(a > INT_MAX / b) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "gd warning: product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully\n"); + return 1; + } + return 0; +} + diff --git a/ext/gd/gd_compat.h b/ext/gd/gd_compat.h new file mode 100644 index 0000000000..db757f5988 --- /dev/null +++ b/ext/gd/gd_compat.h @@ -0,0 +1,14 @@ +#ifndef GD_COMPAT_H +#define GD_COMPAT_H 1 + +#ifndef HAVE_GD_BUNDLED +/* from gd_compat.c */ +const char * gdPngGetVersionString(); +const char * gdJpegGetVersionString(); +int gdJpegGetVersionInt(); +#endif + +/* from gd_compat.c of libgd/gd_security.c */ +int overflow2(int a, int b); + +#endif /* GD_COMPAT_H */ diff --git a/ext/gd/gd_ctx.c b/ext/gd/gd_ctx.c index b0e8aa66ab..2a0b500e9e 100644 --- a/ext/gd/gd_ctx.c +++ b/ext/gd/gd_ctx.c @@ -189,11 +189,7 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, break; } -#if HAVE_LIBGD204 ctx->gd_free(ctx); -#else - ctx->free(ctx); -#endif RETURN_TRUE; } diff --git a/ext/gd/gdcache.c b/ext/gd/gdcache.c index 2349e38b93..2af6737f74 100644 --- a/ext/gd/gdcache.c +++ b/ext/gd/gdcache.c @@ -44,7 +44,7 @@ #else #include <php_config.h> #endif -#if HAVE_LIBFREETYPE && !defined(HAVE_GD_CACHE_CREATE) +#if HAVE_LIBFREETYPE && !defined(HAVE_GD_BUNDLED) #include "gdcache.h" @@ -95,6 +95,11 @@ gdCacheGet( gdCache_head_t *head, void *keydata ) void *userdata; elem = head->mru; + if (elem == NULL) { + return NULL; + + } + while(elem) { if ((*(head->gdCacheTest))(elem->userdata, keydata)) { if (i) { /* if not already most-recently-used */ diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index fa75898ddb..7ed6617c57 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -168,6 +168,8 @@ gdImagePtr gdImageCreate (int sx, int sy) im->cy1 = 0; im->cx2 = im->sx - 1; im->cy2 = im->sy - 1; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; return im; } @@ -183,7 +185,7 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy) if (overflow2(sizeof(unsigned char *), sy)) { return NULL; } - + if (overflow2(sizeof(int), sx)) { return NULL; } @@ -221,6 +223,8 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy) im->cy1 = 0; im->cx2 = im->sx - 1; im->cy2 = im->sy - 1; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; return im; } @@ -1954,7 +1958,6 @@ static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc) { int i, l, x1, x2, dy; int oc; /* old pixel value */ - int tiled; int wx2,wy2; /* stack of filled segments */ struct seg *stack; @@ -1966,7 +1969,6 @@ static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc) } wx2=im->sx;wy2=im->sy; - tiled = nc==gdTiled; nc = gdImageTileGet(im,x,y); @@ -2031,7 +2033,6 @@ void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) { int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2; int thick = im->thick; - int half1 = 1; int t; if (x1 == x2 && y1 == y2 && thick == 1) { @@ -2053,7 +2054,7 @@ void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) if (thick > 1) { int cx, cy, x1ul, y1ul, x2lr, y2lr; int half = thick >> 1; - half1 = thick - half; + x1ul = x1 - half; y1ul = y1 - half; @@ -2351,8 +2352,6 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int int colorMap[gdMaxColors]; /* Stretch vectors */ int *stx, *sty; - /* We only need to use floating point to determine the correct stretch vector for one line's worth. */ - double accum; if (overflow2(sizeof(int), srcW)) { return; @@ -2363,7 +2362,6 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int stx = (int *) gdMalloc (sizeof (int) * srcW); sty = (int *) gdMalloc (sizeof (int) * srcH); - accum = 0; /* Fixed by Mao Morimoto 2.0.16 */ for (i = 0; (i < srcW); i++) { @@ -3009,3 +3007,76 @@ void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P) *y2P = im->cy2; } +/* convert a palette image to true color */ +int gdImagePaletteToTrueColor(gdImagePtr src) +{ + unsigned int y; + unsigned char alloc_y = 0; + unsigned int yy; + + if (src == NULL) { + return 0; + } + + if (src->trueColor == 1) { + return 1; + } else { + unsigned int x; + const unsigned int sy = gdImageSY(src); + const unsigned int sx = gdImageSX(src); + + src->tpixels = (int **) gdMalloc(sizeof(int *) * sy); + if (src->tpixels == NULL) { + return 0; + } + + for (y = 0; y < sy; y++) { + const unsigned char *src_row = src->pixels[y]; + int * dst_row; + + /* no need to calloc it, we overwrite all pxl anyway */ + src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int)); + if (src->tpixels[y] == NULL) { + goto clean_on_error; + } + + dst_row = src->tpixels[y]; + for (x = 0; x < sx; x++) { + const unsigned char c = *(src_row + x); + if (c == src->transparent) { + *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127); + } else { + *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); + } + } + } + } + + /* free old palette buffer */ + for (yy = y - 1; yy >= yy - 1; yy--) { + gdFree(src->pixels[yy]); + } + gdFree(src->pixels); + src->trueColor = 1; + src->pixels = NULL; + src->alphaBlendingFlag = 0; + src->saveAlphaFlag = 1; + + if (src->transparent >= 0) { + const unsigned char c = src->transparent; + src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); + } + + return 1; + +clean_on_error: + if (y > 0) { + + for (yy = y; yy >= yy - 1; y--) { + gdFree(src->tpixels[y]); + } + gdFree(src->tpixels); + } + return 0; +} + diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 8aedc2c38c..72515108d6 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -93,6 +93,10 @@ void php_gd_error(const char *format, ...); #define gdEffectNormal 2 #define gdEffectOverlay 3 +#define GD_TRUE 1 +#define GD_FALSE 0 + +#define GD_EPSILON 1e-6 /* This function accepts truecolor pixel values only. The source color is composited with the destination color @@ -101,6 +105,67 @@ void php_gd_error(const char *format, ...); int gdAlphaBlend(int dest, int src); +/** + * Group: Transform + * + * Constants: gdInterpolationMethod + + * GD_BELL - Bell + * GD_BESSEL - Bessel + * GD_BILINEAR_FIXED - fixed point bilinear + * GD_BICUBIC - Bicubic + * GD_BICUBIC_FIXED - fixed point bicubic integer + * GD_BLACKMAN - Blackman + * GD_BOX - Box + * GD_BSPLINE - BSpline + * GD_CATMULLROM - Catmullrom + * GD_GAUSSIAN - Gaussian + * GD_GENERALIZED_CUBIC - Generalized cubic + * GD_HERMITE - Hermite + * GD_HAMMING - Hamming + * GD_HANNING - Hannig + * GD_MITCHELL - Mitchell + * GD_NEAREST_NEIGHBOUR - Nearest neighbour interpolation + * GD_POWER - Power + * GD_QUADRATIC - Quadratic + * GD_SINC - Sinc + * GD_TRIANGLE - Triangle + * GD_WEIGHTED4 - 4 pixels weighted bilinear interpolation + * + * See also: + * <gdSetInterpolationMethod> + **/ +typedef enum { + GD_DEFAULT = 0, + GD_BELL, + GD_BESSEL, + GD_BILINEAR_FIXED, + GD_BICUBIC, + GD_BICUBIC_FIXED, + GD_BLACKMAN, + GD_BOX, + GD_BSPLINE, + GD_CATMULLROM, + GD_GAUSSIAN, + GD_GENERALIZED_CUBIC, + GD_HERMITE, + GD_HAMMING, + GD_HANNING, + GD_MITCHELL, + GD_NEAREST_NEIGHBOUR, + GD_POWER, + GD_QUADRATIC, + GD_SINC, + GD_TRIANGLE, + GD_WEIGHTED4, + GD_METHOD_COUNT = 21 +} gdInterpolationMethod; + +/* define struct with name and func ptr and add it to gdImageStruct gdInterpolationMethod interpolation; */ + +/* Interpolation function ptr */ +typedef double (* interpolation_method )(double); + typedef struct gdImageStruct { /* Palette-based image pixels */ unsigned char ** pixels; @@ -188,10 +253,35 @@ typedef struct gdImageStruct { int cy1; int cx2; int cy2; + gdInterpolationMethod interpolation_id; + interpolation_method interpolation; } gdImage; typedef gdImage * gdImagePtr; +/* Point type for use in polygon drawing. */ + +/** + * Group: Types + * + * typedef: gdPointF + * Defines a point in a 2D coordinate system using floating point + * values. + * x - Floating point position (increase from left to right) + * y - Floating point Row position (increase from top to bottom) + * + * typedef: gdPointFPtr + * Pointer to a <gdPointF> + * + * See also: + * <gdImageCreate>, <gdImageCreateTrueColor>, + **/ +typedef struct +{ + double x, y; +} +gdPointF, *gdPointFPtr; + typedef struct { /* # of characters in font */ int nchars; @@ -209,6 +299,31 @@ typedef struct { /* Text functions take these. */ typedef gdFont *gdFontPtr; + +/** + * Group: Types + * + * typedef: gdRect + * Defines a rectilinear region. + * + * x - left position + * y - right position + * width - Rectangle width + * height - Rectangle height + * + * typedef: gdRectPtr + * Pointer to a <gdRect> + * + * See also: + * <gdSetInterpolationMethod> + **/ +typedef struct +{ + int x, y; + int width, height; +} +gdRect, *gdRectPtr; + /* For backwards compatibility only. Use gdImageSetStyle() for MUCH more flexible line drawing. Also see gdImageSetBrush(). */ @@ -247,8 +362,12 @@ gdImagePtr gdImageCreateFromPng(FILE *fd); gdImagePtr gdImageCreateFromPngCtx(gdIOCtxPtr in); gdImagePtr gdImageCreateFromWBMP(FILE *inFile); gdImagePtr gdImageCreateFromWBMPCtx(gdIOCtx *infile); -gdImagePtr gdImageCreateFromJpeg(FILE *infile, int ignore_warning); -gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile, int ignore_warning); +gdImagePtr gdImageCreateFromJpeg(FILE *infile); +gdImagePtr gdImageCreateFromJpegEx(FILE *infile, int ignore_warning); +gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile); +gdImagePtr gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning); +gdImagePtr gdImageCreateFromJpegPtr (int size, void *data); +gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning); gdImagePtr gdImageCreateFromWebp(FILE *fd); gdImagePtr gdImageCreateFromWebpCtx(gdIOCtxPtr in); gdImagePtr gdImageCreateFromWebpPtr (int size, void *data); @@ -444,7 +563,7 @@ void gdImageColorDeallocate(gdImagePtr im, int color); gdImagePtr gdImageCreatePaletteFromTrueColor (gdImagePtr im, int ditherFlag, int colorsWanted); void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted); - +int gdImagePaletteToTrueColor(gdImagePtr src); /* An attempt at getting the results of gdImageTrueColorToPalette to look a bit more like the original (im1 is the original @@ -575,6 +694,7 @@ gdImagePtr gdImageRotate180(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate270(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate45(gdImagePtr src, double dAngle, int clrBack, int ignoretransparent); gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent); +gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor); void gdImageSetBrush(gdImagePtr im, gdImagePtr brush); void gdImageSetTile(gdImagePtr im, gdImagePtr tile); @@ -682,6 +802,88 @@ int gdImageSmooth(gdImagePtr im, float weight); /* Image comparison definitions */ int gdImageCompare(gdImagePtr im1, gdImagePtr im2); +void gdImageFlipHorizontal(gdImagePtr im); +void gdImageFlipVertical(gdImagePtr im); +void gdImageFlipBoth(gdImagePtr im); + +#define GD_FLIP_HORINZONTAL 1 +#define GD_FLIP_VERTICAL 2 +#define GD_FLIP_BOTH 3 + +/** + * Group: Crop + * + * Constants: gdCropMode + * GD_CROP_DEFAULT - Default crop mode (4 corners or background) + * GD_CROP_TRANSPARENT - Crop using the transparent color + * GD_CROP_BLACK - Crop black borders + * GD_CROP_WHITE - Crop white borders + * GD_CROP_SIDES - Crop using colors of the 4 corners + * + * See also: + * <gdImageAutoCrop> + **/ +enum gdCropMode { + GD_CROP_DEFAULT = 0, + GD_CROP_TRANSPARENT, + GD_CROP_BLACK, + GD_CROP_WHITE, + GD_CROP_SIDES, + GD_CROP_THRESHOLD +}; + +gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop); +gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode); +gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold); + +int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id); + +gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height); +gdImagePtr gdImageScaleBicubic(gdImagePtr src_img, const unsigned int new_width, const unsigned int new_height); +gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height); +gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height); +gdImagePtr gdImageScaleTwoPass(const gdImagePtr pOrigImage, const unsigned int uOrigWidth, const unsigned int uOrigHeight, const unsigned int uNewWidth, const unsigned int uNewHeight); +gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height); + +gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor); + + + +typedef enum { + GD_AFFINE_TRANSLATE = 0, + GD_AFFINE_SCALE, + GD_AFFINE_ROTATE, + GD_AFFINE_SHEAR_HORIZONTAL, + GD_AFFINE_SHEAR_VERTICAL, +} gdAffineStandardMatrix; + +int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, const double affine[6]); +int gdAffineInvert (double dst[6], const double src[6]); +int gdAffineFlip (double dst_affine[6], const double src_affine[6], const int flip_h, const int flip_v); +int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]); + +int gdAffineIdentity (double dst[6]); +int gdAffineScale (double dst[6], const double scale_x, const double scale_y); +int gdAffineRotate (double dst[6], const double angle); +int gdAffineShearHorizontal (double dst[6], const double angle); +int gdAffineShearVertical(double dst[6], const double angle); +int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y); +double gdAffineExpansion (const double src[6]); +int gdAffineRectilinear (const double src[6]); +int gdAffineEqual (const double matrix1[6], const double matrix2[6]); +int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]); +int gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, gdRectPtr src_region, const double affine[6]); +/* +gdTransformAffineCopy(gdImagePtr dst, int x0, int y0, int x1, int y1, + const gdImagePtr src, int src_width, int src_height, + const double affine[6]); +*/ +int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox); + + #define GD_CMP_IMAGE 1 /* Actual image IS different */ #define GD_CMP_NUM_COLORS 2 /* Number of Colours in pallette differ */ #define GD_CMP_COLOR 4 /* Image colours differ */ diff --git a/ext/gd/libgd/gd_compat.c b/ext/gd/libgd/gd_compat.c deleted file mode 100644 index 473ea203e5..0000000000 --- a/ext/gd/libgd/gd_compat.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "php_config.h" -#ifdef HAVE_GD_PNG -/* needs to be first */ -# include <png.h> -#endif - -#ifdef HAVE_GD_JPG -# include <jpeglib.h> -#endif - -#ifdef HAVE_GD_JPG -int gdJpegGetVersionInt() -{ - return JPEG_LIB_VERSION; -} - -const char * gdJpegGetVersionString() -{ - switch(JPEG_LIB_VERSION) { - case 62: - return "6b"; - break; - default: - return "unknown"; - } -} -#endif - -#ifdef HAVE_GD_PNG -const char * gdPngGetVersionString() -{ - return PNG_LIBPNG_VER_STRING; -} -#endif - diff --git a/ext/gd/libgd/gd_compat.h b/ext/gd/libgd/gd_compat.h deleted file mode 100644 index 779e709a63..0000000000 --- a/ext/gd/libgd/gd_compat.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef GD_COMPAT_H -#define GD_COMPAT_H 1 - -#if HAVE_GD_BUNDLED -# include "gd.h" -#else -# include <gd.h> -#endif - -const char * gdPngGetVersionString(); -const char * gdJpegGetVersionString(); -int gdJpegGetVersionInt(); -int overflow2(int a, int b); - -/* filters section - * - * Negate the imag src, white becomes black, - * The red, green, and blue intensities of an image are negated. - * White becomes black, yellow becomes blue, etc. - */ -int gdImageNegate(gdImagePtr src); - -/* Convert the image src to a grayscale image */ -int gdImageGrayScale(gdImagePtr src); - -/* Set the brightness level <brightness> for the image src */ -int gdImageBrightness(gdImagePtr src, int brightness); - -/* Set the contrast level <contrast> for the image <src> */ -int gdImageContrast(gdImagePtr src, double contrast); - -/* Simply adds or substracts respectively red, green or blue to a pixel */ -int gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha); - -#if !defined(HAVE_GD_IMAGE_CONVOLUTION) -/* Image convolution by a 3x3 custom matrix */ -int gdImageConvolution(gdImagePtr src, float ft[3][3], float filter_div, float offset); -int gdImageEdgeDetectQuick(gdImagePtr src); -int gdImageGaussianBlur(gdImagePtr im); -int gdImageSelectiveBlur( gdImagePtr src); -int gdImageEmboss(gdImagePtr im); -int gdImageMeanRemoval(gdImagePtr im); -int gdImageSmooth(gdImagePtr im, float weight); -#endif - -#if !defined(HAVE_GD_IMAGE_PIXELATE) -enum gdPixelateMode { - GD_PIXELATE_UPPERLEFT, - GD_PIXELATE_AVERAGE -}; - -int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode); -#endif - -int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode); - -#if !HAVE_GD_IMAGEELLIPSE -void gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int c); -#endif - -gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent); - -int gdImageColorMatch (gdImagePtr im1, gdImagePtr im2); - -#endif - diff --git a/ext/gd/libgd/gd_crop.c b/ext/gd/libgd/gd_crop.c new file mode 100644 index 0000000000..f0b888a4f1 --- /dev/null +++ b/ext/gd/libgd/gd_crop.c @@ -0,0 +1,356 @@ +/** + * Title: Crop + * + * A couple of functions to crop images, automatically (auto detection of + * the borders color), using a given color (with or without tolerance) + * or using a selection. + * + * The threshold method works relatively well but it can be improved. + * Maybe L*a*b* and Delta-E will give better results (and a better + * granularity). + * + * Example: + * (start code) + * im2 = gdImageAutoCrop(im, GD_CROP_SIDES); + * if (im2) { + + * } + * gdImageDestroy(im2); + * (end code) + **/ + +#include <gd.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color); +static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold); + +/** + * Function: gdImageCrop + * Crops the src image using the area defined by the <crop> rectangle. + * The result is returned as a new image. + * + * + * Parameters: + * src - Source image + * crop - Rectangular region to crop + * + * Returns: + * <gdImagePtr> on success or NULL + */ +gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop) +{ + gdImagePtr dst; + + if (src->trueColor) { + dst = gdImageCreateTrueColor(crop->width, crop->height); + gdImageSaveAlpha(dst, 1); + } else { + dst = gdImageCreate(crop->width, crop->height); + gdImagePaletteCopy(dst, src); + } + dst->transparent = src->transparent; + + if (src->sx < (crop->x + crop->width -1)) { + crop->width = src->sx - crop->x + 1; + } + if (src->sy < (crop->y + crop->height -1)) { + crop->height = src->sy - crop->y + 1; + } +#if 0 +printf("rect->x: %i\nrect->y: %i\nrect->width: %i\nrect->height: %i\n", crop->x, crop->y, crop->width, crop->height); +#endif + if (dst == NULL) { + return NULL; + } else { + int y = crop->y; + if (src->trueColor) { + unsigned int dst_y = 0; + while (y < (crop->y + (crop->height - 1))) { + /* TODO: replace 4 w/byte per channel||pitch once available */ + memcpy(dst->tpixels[dst_y++], src->tpixels[y++] + crop->x, crop->width * 4); + } + } else { + int x; + for (y = crop->y; y < (crop->y + (crop->height - 1)); y++) { + for (x = crop->x; x < (crop->x + (crop->width - 1)); x++) { + dst->pixels[y - crop->y][x - crop->x] = src->pixels[y][x]; + } + } + } + return dst; + } +} + +/** + * Function: gdImageAutoCrop + * Automatic croping of the src image using the given mode + * (see <gdCropMode>) + * + * + * Parameters: + * im - Source image + * mode - crop mode + * + * Returns: + * <gdImagePtr> on success or NULL + * + * See also: + * <gdCropMode> + */ +gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode) +{ + const int width = gdImageSX(im); + const int height = gdImageSY(im); + + int x,y; + int color, corners, match; + gdRect crop; + + crop.x = 0; + crop.y = 0; + crop.width = 0; + crop.height = 0; + + switch (mode) { + case GD_CROP_TRANSPARENT: + color = gdImageGetTransparent(im); + break; + + case GD_CROP_BLACK: + color = gdImageColorClosestAlpha(im, 0, 0, 0, 0); + break; + + case GD_CROP_WHITE: + color = gdImageColorClosestAlpha(im, 255, 255, 255, 0); + break; + + case GD_CROP_SIDES: + corners = gdGuessBackgroundColorFromCorners(im, &color); + break; + + case GD_CROP_DEFAULT: + default: + color = gdImageGetTransparent(im); + if (color == -1) { + corners = gdGuessBackgroundColorFromCorners(im, &color); + } + break; + } + + /* TODO: Add gdImageGetRowPtr and works with ptr at the row level + * for the true color and palette images + * new formats will simply work with ptr + */ + match = 1; + for (y = 0; match && y < height; y++) { + for (x = 0; match && x < width; x++) { + int c2 = gdImageGetPixel(im, x, y); + match = (color == c2); + } + } + + /* Nothing to do > bye + * Duplicate the image? + */ + if (y == height - 1) { + return NULL; + } + + crop.y = y -1; + match = 1; + for (y = height - 1; match && y >= 0; y--) { + for (x = 0; match && x < width; x++) { + match = (color == gdImageGetPixel(im, x,y)); + } + } + + if (y == 0) { + crop.height = height - crop.y + 1; + } else { + crop.height = y - crop.y + 2; + } + + match = 1; + for (x = 0; match && x < width; x++) { + for (y = 0; match && y < crop.y + crop.height - 1; y++) { + match = (color == gdImageGetPixel(im, x,y)); + } + } + crop.x = x - 1; + + match = 1; + for (x = width - 1; match && x >= 0; x--) { + for (y = 0; match && y < crop.y + crop.height - 1; y++) { + match = (color == gdImageGetPixel(im, x,y)); + } + } + crop.width = x - crop.x + 2; + if (crop.x <= 0 || crop.y <= 0 || crop.width <= 0 || crop.height <= 0) { + return NULL; + } + return gdImageCrop(im, &crop); +} +/*TODOs: Implement DeltaE instead, way better perceptual differences */ +/** + * Function: gdImageThresholdCrop + * Crop an image using a given color. The threshold argument defines + * the tolerance to be used while comparing the image color and the + * color to crop. The method used to calculate the color difference + * is based on the color distance in the RGB(a) cube. + * + * + * Parameters: + * im - Source image + * color - color to crop + * threshold - tolerance (0..100) + * + * Returns: + * <gdImagePtr> on success or NULL + * + * See also: + * <gdCropMode>, <gdImageAutoCrop> or <gdImageCrop> + */ +gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold) +{ + const int width = gdImageSX(im); + const int height = gdImageSY(im); + + int x,y; + int match; + gdRect crop; + + crop.x = 0; + crop.y = 0; + crop.width = 0; + crop.height = 0; + + /* Pierre: crop everything sounds bad */ + if (threshold > 1.0) { + return NULL; + } + + /* TODO: Add gdImageGetRowPtr and works with ptr at the row level + * for the true color and palette images + * new formats will simply work with ptr + */ + match = 1; + for (y = 0; match && y < height; y++) { + for (x = 0; match && x < width; x++) { + match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; + } + } + + /* Pierre + * Nothing to do > bye + * Duplicate the image? + */ + if (y == height - 1) { + return NULL; + } + + crop.y = y -1; + match = 1; + for (y = height - 1; match && y >= 0; y--) { + for (x = 0; match && x < width; x++) { + match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0; + } + } + + if (y == 0) { + crop.height = height - crop.y + 1; + } else { + crop.height = y - crop.y + 2; + } + + match = 1; + for (x = 0; match && x < width; x++) { + for (y = 0; match && y < crop.y + crop.height - 1; y++) { + match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; + } + } + crop.x = x - 1; + + match = 1; + for (x = width - 1; match && x >= 0; x--) { + for (y = 0; match && y < crop.y + crop.height - 1; y++) { + match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; + } + } + crop.width = x - crop.x + 2; + + return gdImageCrop(im, &crop); +} + +/* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/) + * Three steps: + * - if 3 corners are equal. + * - if two are equal. + * - Last solution: average the colors + */ +static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color) +{ + const int tl = gdImageGetPixel(im, 0, 0); + const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0); + const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1); + const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1); + + if (tr == bl && tr == br) { + *color = tr; + return 3; + } else if (tl == bl && tl == br) { + *color = tl; + return 3; + } else if (tl == tr && tl == br) { + *color = tl; + return 3; + } else if (tl == tr && tl == bl) { + *color = tl; + return 3; + } else if (tl == tr || tl == bl || tl == br) { + *color = tl; + return 2; + } else if (tr == bl) { + *color = tr; + return 2; + } else if (br == bl) { + *color = bl; + return 2; + } else { + register int r,b,g,a; + + r = (int)(0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4); + g = (int)(0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4); + b = (int)(0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4); + a = (int)(0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4); + *color = gdImageColorClosestAlpha(im, r, g, b, a); + return 0; + } +} + +static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold) +{ + const int dr = gdImageRed(im, col1) - gdImageRed(im, col2); + const int dg = gdImageGreen(im, col1) - gdImageGreen(im, col2); + const int db = gdImageBlue(im, col1) - gdImageBlue(im, col2); + const int da = gdImageAlpha(im, col1) - gdImageAlpha(im, col2); + const double dist = sqrt(dr * dr + dg * dg + db * db + da * da); + const double dist_perc = sqrt(dist / (255^2 + 255^2 + 255^2)); + return (dist_perc <= threshold); + //return (100.0 * dist / 195075) < threshold; +} + +/* + * To be implemented when we have more image formats. + * Buffer like gray8 gray16 or rgb8 will require some tweak + * and can be done in this function (called from the autocrop + * function. (Pierre) + */ +#if 0 +static int colors_equal (const int col1, const in col2) +{ + +} +#endif diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c new file mode 100644 index 0000000000..e3247a78c1 --- /dev/null +++ b/ext/gd/libgd/gd_interpolation.c @@ -0,0 +1,2547 @@ +/* + * Filtered Image Rescaling + * Based on Gems III + * - Schumacher general filtered image rescaling + * (pp. 414-424) + * by Dale Schumacher + * + * Additional changes by Ray Gardener, Daylon Graphics Ltd. + * December 4, 1999 + * + * Ported to libgd by Pierre Joye. Support for multiple channels + * added (argb for now). + * + * Initial sources code is avaibable in the Gems Source Code Packages: + * http://www.acm.org/pubs/tog/GraphicsGems/GGemsIII.tar.gz + */ + +/* + Summary: + + - Horizontal filter contributions are calculated on the fly, + as each column is mapped from src to dst image. This lets + us omit having to allocate a temporary full horizontal stretch + of the src image. + + - If none of the src pixels within a sampling region differ, + then the output pixel is forced to equal (any of) the source pixel. + This ensures that filters do not corrupt areas of constant color. + + - Filter weight contribution results, after summing, are + rounded to the nearest pixel color value instead of + being casted to ILubyte (usually an int or char). Otherwise, + artifacting occurs. + +*/ + +/* + Additional functions are available for simple rotation or up/downscaling. + downscaling using the fixed point implementations are usually much faster + than the existing gdImageCopyResampled while having a similar or better + quality. + + For image rotations, the optimized versions have a lazy antialiasing for + the edges of the images. For a much better antialiased result, the affine + function is recommended. +*/ + +/* +TODO: + - Optimize pixel accesses and loops once we have continuous buffer + - Add scale support for a portion only of an image (equivalent of copyresized/resampled) + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <gd.h> +#include "gdhelpers.h" + +#ifdef _MSC_VER +# pragma optimize("t", on) +# include <emmintrin.h> +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c))) +#ifndef MAX +#define MAX(a,b) ((a)<(b)?(b):(a)) +#endif +#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c))) + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +/* only used here, let do a generic fixed point integers later if required by other + part of GD */ +typedef long gdFixed; +/* Integer to fixed point */ +#define gd_itofx(x) ((x) << 8) + +/* Float to fixed point */ +#define gd_ftofx(x) (long)((x) * 256) + +/* Double to fixed point */ +#define gd_dtofx(x) (long)((x) * 256) + +/* Fixed point to integer */ +#define gd_fxtoi(x) ((x) >> 8) + +/* Fixed point to float */ +# define gd_fxtof(x) ((float)(x) / 256) + +/* Fixed point to double */ +#define gd_fxtod(x) ((double)(x) / 256) + +/* Multiply a fixed by a fixed */ +#define gd_mulfx(x,y) (((x) * (y)) >> 8) + +/* Divide a fixed by a fixed */ +#define gd_divfx(x,y) (((x) << 8) / (y)) + +typedef struct +{ + double *Weights; /* Normalized weights of neighboring pixels */ + int Left,Right; /* Bounds of source pixels window */ +} ContributionType; /* Contirbution information for a single pixel */ + +typedef struct +{ + ContributionType *ContribRow; /* Row (or column) of contribution weights */ + unsigned int WindowSize, /* Filter window size (of affecting source pixels) */ + LineLength; /* Length of line (no. or rows / cols) */ +} LineContribType; + +/* Each core filter has its own radius */ +#define DEFAULT_FILTER_BICUBIC 3.0 +#define DEFAULT_FILTER_BOX 0.5 +#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5 +#define DEFAULT_FILTER_RADIUS 1.0 +#define DEFAULT_LANCZOS8_RADIUS 8.0 +#define DEFAULT_LANCZOS3_RADIUS 3.0 +#define DEFAULT_HERMITE_RADIUS 1.0 +#define DEFAULT_BOX_RADIUS 0.5 +#define DEFAULT_TRIANGLE_RADIUS 1.0 +#define DEFAULT_BELL_RADIUS 1.5 +#define DEFAULT_CUBICSPLINE_RADIUS 2.0 +#define DEFAULT_MITCHELL_RADIUS 2.0 +#define DEFAULT_COSINE_RADIUS 1.0 +#define DEFAULT_CATMULLROM_RADIUS 2.0 +#define DEFAULT_QUADRATIC_RADIUS 1.5 +#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5 +#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0 +#define DEFAULT_GAUSSIAN_RADIUS 1.0 +#define DEFAULT_HANNING_RADIUS 1.0 +#define DEFAULT_HAMMING_RADIUS 1.0 +#define DEFAULT_SINC_RADIUS 1.0 +#define DEFAULT_WELSH_RADIUS 1.0 + +enum GD_RESIZE_FILTER_TYPE{ + FILTER_DEFAULT = 0, + FILTER_BELL, + FILTER_BESSEL, + FILTER_BLACKMAN, + FILTER_BOX, + FILTER_BSPLINE, + FILTER_CATMULLROM, + FILTER_COSINE, + FILTER_CUBICCONVOLUTION, + FILTER_CUBICSPLINE, + FILTER_HERMITE, + FILTER_LANCZOS3, + FILTER_LANCZOS8, + FILTER_MITCHELL, + FILTER_QUADRATIC, + FILTER_QUADRATICBSPLINE, + FILTER_TRIANGLE, + FILTER_GAUSSIAN, + FILTER_HANNING, + FILTER_HAMMING, + FILTER_SINC, + FILTER_WELSH, + + FILTER_CALLBACK = 999 +}; + +typedef enum GD_RESIZE_FILTER_TYPE gdResizeFilterType; + +static double KernelBessel_J1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.581199354001606143928050809e+21, + -0.6672106568924916298020941484e+20, + 0.2316433580634002297931815435e+19, + -0.3588817569910106050743641413e+17, + 0.2908795263834775409737601689e+15, + -0.1322983480332126453125473247e+13, + 0.3413234182301700539091292655e+10, + -0.4695753530642995859767162166e+7, + 0.270112271089232341485679099e+4 + }, + Qone[] = + { + 0.11623987080032122878585294e+22, + 0.1185770712190320999837113348e+20, + 0.6092061398917521746105196863e+17, + 0.2081661221307607351240184229e+15, + 0.5243710262167649715406728642e+12, + 0.1013863514358673989967045588e+10, + 0.1501793594998585505921097578e+7, + 0.1606931573481487801970916749e+4, + 0.1e+1 + }; + + p = Pone[8]; + q = Qone[8]; + for (i=7; i >= 0; i--) + { + p = p*x*x+Pone[i]; + q = q*x*x+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_P1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.352246649133679798341724373e+5, + 0.62758845247161281269005675e+5, + 0.313539631109159574238669888e+5, + 0.49854832060594338434500455e+4, + 0.2111529182853962382105718e+3, + 0.12571716929145341558495e+1 + }, + Qone[] = + { + 0.352246649133679798068390431e+5, + 0.626943469593560511888833731e+5, + 0.312404063819041039923015703e+5, + 0.4930396490181088979386097e+4, + 0.2030775189134759322293574e+3, + 0.1e+1 + }; + + p = Pone[5]; + q = Qone[5]; + for (i=4; i >= 0; i--) + { + p = p*(8.0/x)*(8.0/x)+Pone[i]; + q = q*(8.0/x)*(8.0/x)+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_Q1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.3511751914303552822533318e+3, + 0.7210391804904475039280863e+3, + 0.4259873011654442389886993e+3, + 0.831898957673850827325226e+2, + 0.45681716295512267064405e+1, + 0.3532840052740123642735e-1 + }, + Qone[] = + { + 0.74917374171809127714519505e+4, + 0.154141773392650970499848051e+5, + 0.91522317015169922705904727e+4, + 0.18111867005523513506724158e+4, + 0.1038187585462133728776636e+3, + 0.1e+1 + }; + + p = Pone[5]; + q = Qone[5]; + for (i=4; i >= 0; i--) + { + p = p*(8.0/x)*(8.0/x)+Pone[i]; + q = q*(8.0/x)*(8.0/x)+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_Order1(double x) +{ + double p, q; + + if (x == 0.0) + return (0.0f); + p = x; + if (x < 0.0) + x=(-x); + if (x < 8.0) + return (p*KernelBessel_J1(x)); + q = (double)sqrt(2.0f/(M_PI*x))*(double)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)* + (-1.0f/sqrt(2.0f)*(sin(x)+cos(x)))); + if (p < 0.0f) + q = (-q); + return (q); +} + +static double filter_bessel(const double x) +{ + if (x == 0.0f) + return (double)(M_PI/4.0f); + return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x)); +} + + +static double filter_blackman(const double x) +{ + return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x)); +} + +/** + * Bicubic interpolation kernel (a=-1): + \verbatim + / + | 1-2|t|**2+|t|**3 , if |t| < 1 + h(t) = | 4-8|t|+5|t|**2-|t|**3 , if 1<=|t|<2 + | 0 , otherwise + \ + \endverbatim + * ***bd*** 2.2004 + */ +static double filter_bicubic(const double t) +{ + const double abs_t = (double)fabs(t); + const double abs_t_sq = abs_t * abs_t; + if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t; + if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t; + return 0; +} + +/** + * Generalized cubic kernel (for a=-1 it is the same as BicubicKernel): + \verbatim + / + | (a+2)|t|**3 - (a+3)|t|**2 + 1 , |t| <= 1 + h(t) = | a|t|**3 - 5a|t|**2 + 8a|t| - 4a , 1 < |t| <= 2 + | 0 , otherwise + \ + \endverbatim + * Often used values for a are -1 and -1/2. + */ +static double filter_generalized_cubic(const double t) +{ + const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC; + double abs_t = (double)fabs(t); + double abs_t_sq = abs_t * abs_t; + if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1; + if (abs_t < 2) return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a; + return 0; +} + +/* CubicSpline filter, default radius 2 */ +static double filter_cubic_spline(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 1.0 ) { + const double x2 = x*x; + + return (0.5 * x2 * x - x2 + 2.0 / 3.0); + } + if (x < 2.0) { + return (pow(2.0 - x, 3.0)/6.0); + } + return 0; +} + +/* CubicConvolution filter, default radius 3 */ +static double filter_cubic_convolution(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + const double x2 = x1 * x1; + const double x2_x = x2 * x; + + if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0); + if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5); + if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5); + return 0; +} + +static double filter_box(double x) { + if (x < - DEFAULT_FILTER_BOX) + return 0.0f; + if (x < DEFAULT_FILTER_BOX) + return 1.0f; + return 0.0f; +} + +static double filter_catmullrom(const double x) +{ + if (x < -2.0) + return(0.0f); + if (x < -1.0) + return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x)))); + if (x < 0.0) + return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x))); + if (x < 1.0) + return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x))); + if (x < 2.0) + return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x)))); + return(0.0f); +} + +static double filter_filter(double t) +{ + /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ + if(t < 0.0) t = -t; + if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); + return(0.0); +} + + +/* Lanczos8 filter, default radius 8 */ +static double filter_lanczos8(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; +#define R DEFAULT_LANCZOS8_RADIUS + + if ( x == 0.0) return 1; + + if ( x < R) { + return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI); + } + return 0.0; +#undef R +} + + +/* Lanczos3 filter, default radius 3 */ +static double filter_lanczos3(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; +#define R DEFAULT_LANCZOS3_RADIUS + + if ( x == 0.0) return 1; + + if ( x < R) + { + return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI); + } + return 0.0; +#undef R +} + +/* Hermite filter, default radius 1 */ +static double filter_hermite(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 ); + + return 0.0; +} + +/* Trangle filter, default radius 1 */ +static double filter_triangle(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + if (x < 1.0) return (1.0 - x); + return 0.0; +} + +/* Bell filter, default radius 1.5 */ +static double filter_bell(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 0.5) return (0.75 - x*x); + if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0)); + return 0.0; +} + +/* Mitchell filter, default radius 2.0 */ +static double filter_mitchell(const double x) +{ +#define KM_B (1.0f/3.0f) +#define KM_C (1.0f/3.0f) +#define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f) +#define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f) +#define KM_P3 (( 12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f) +#define KM_Q0 (( 8.0f * KM_B + 24.0f * KM_C) / 6.0f) +#define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f) +#define KM_Q2 (( 6.0f * KM_B + 30.0f * KM_C) / 6.0f) +#define KM_Q3 (( -1.0f * KM_B - 6.0f * KM_C) / 6.0f) + + if (x < -2.0) + return(0.0f); + if (x < -1.0) + return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3))); + if (x < 0.0f) + return(KM_P0+x*x*(KM_P2-x*KM_P3)); + if (x < 1.0f) + return(KM_P0+x*x*(KM_P2+x*KM_P3)); + if (x < 2.0f) + return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3))); + return(0.0f); +} + + + +/* Cosine filter, default radius 1 */ +static double filter_cosine(const double x) +{ + if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0); + + return 0; +} + +/* Quadratic filter, default radius 1.5 */ +static double filter_quadratic(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x <= 0.5) return (- 2.0 * x * x + 1); + if (x <= 1.5) return (x * x - 2.5* x + 1.5); + return 0.0; +} + +static double filter_bspline(const double x) +{ + if (x>2.0f) { + return 0.0f; + } else { + double a, b, c, d; + /* Was calculated anyway cause the "if((x-1.0f) < 0)" */ + const double xm1 = x - 1.0f; + const double xp1 = x + 1.0f; + const double xp2 = x + 2.0f; + + if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2; + if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1; + if (x <= 0) c = 0.0f; else c = x*x*x; + if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1; + + return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d))); + } +} + +/* QuadraticBSpline filter, default radius 1.5 */ +static double filter_quadratic_bspline(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x <= 0.5) return (- x * x + 0.75); + if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125); + return 0.0; +} + +static double filter_gaussian(const double x) +{ + /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */ + return (double)(exp(-2.0f * x * x) * 0.79788456080287f); +} + +static double filter_hanning(const double x) +{ + /* A Cosine windowing function */ + return(0.5 + 0.5 * cos(M_PI * x)); +} + +static double filter_hamming(const double x) +{ + /* should be + (0.54+0.46*cos(M_PI*(double) x)); + but this approximation is sufficient */ + if (x < -1.0f) + return 0.0f; + if (x < 0.0f) + return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f; + if (x < 1.0f) + return 0.92f*(2.0f*x-3.0f)*x*x+1.0f; + return 0.0f; +} + +static double filter_power(const double x) +{ + const double a = 2.0f; + if (fabs(x)>1) return 0.0f; + return (1.0f - (double)fabs(pow(x,a))); +} + +static double filter_sinc(const double x) +{ + /* X-scaled Sinc(x) function. */ + if (x == 0.0) return(1.0); + return (sin(M_PI * (double) x) / (M_PI * (double) x)); +} + +static double filter_welsh(const double x) +{ + /* Welsh parabolic windowing filter */ + if (x < 1.0) + return(1 - x*x); + return(0.0); +} + + +/* Copied from upstream's libgd */ +static inline int _color_blend (const int dst, const int src) +{ + const int src_alpha = gdTrueColorGetAlpha(src); + + if( src_alpha == gdAlphaOpaque ) { + return src; + } else { + const int dst_alpha = gdTrueColorGetAlpha(dst); + + if( src_alpha == gdAlphaTransparent ) return dst; + if( dst_alpha == gdAlphaTransparent ) { + return src; + } else { + register int alpha, red, green, blue; + const int src_weight = gdAlphaTransparent - src_alpha; + const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax; + const int tot_weight = src_weight + dst_weight; + + alpha = src_alpha * dst_alpha / gdAlphaMax; + + red = (gdTrueColorGetRed(src) * src_weight + + gdTrueColorGetRed(dst) * dst_weight) / tot_weight; + green = (gdTrueColorGetGreen(src) * src_weight + + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight; + blue = (gdTrueColorGetBlue(src) * src_weight + + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight; + + return ((alpha << 24) + (red << 16) + (green << 8) + blue); + } + } +} + +static inline int _setEdgePixel(const gdImagePtr src, unsigned int x, unsigned int y, gdFixed coverage, const int bgColor) +{ + const gdFixed f_127 = gd_itofx(127); + register int c = src->tpixels[y][x]; + c = c | (( (int) (gd_fxtof(gd_mulfx(coverage, f_127)) + 50.5f)) << 24); + return _color_blend(bgColor, c); +} + +static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, const int bgColor) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->tpixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return c; + } else { + register int border = 0; + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = im->tpixels[im->cy2][x]; + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = im->tpixels[y][im->cx1]; + goto processborder; + } + + if (x > im->cx2) { + border = im->tpixels[y][im->cx2]; + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127); + } + } +} + +#define colorIndex2RGBA(c) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)]) +#define colorIndex2RGBcustomA(c, a) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)]) +static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, const int bgColor) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->pixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return colorIndex2RGBA(c); + } else { + register int border = 0; + if (y < im->cy1) { + border = gdImageGetPixel(im, im->cx1, 0); + goto processborder; + } + + if (y < im->cy1) { + border = gdImageGetPixel(im, im->cx1, 0); + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = gdImageGetPixel(im, x, im->cy2); + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = gdImageGetPixel(im, im->cx1, y); + goto processborder; + } + + if (x > im->cx2) { + border = gdImageGetPixel(im, im->cx2, y); + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return colorIndex2RGBcustomA(border, 127); + } + } +} + +static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double y, const int bgColor) +{ + /* Closest pixel <= (xf,yf) */ + int sx = (int)(x); + int sy = (int)(y); + const double xf = x - (double)sx; + const double yf = y - (double)sy; + const double nxf = (double) 1.0 - xf; + const double nyf = (double) 1.0 - yf; + const double m1 = xf * yf; + const double m2 = nxf * yf; + const double m3 = xf * nyf; + const double m4 = nxf * nyf; + + /* get color values of neighbouring pixels */ + const int c1 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy, bgColor) : getPixelOverflowPalette(im, sx, sy, bgColor); + const int c2 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy, bgColor) : getPixelOverflowPalette(im, sx - 1, sy, bgColor); + const int c3 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); + const int c4 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); + int r, g, b, a; + + if (x < 0) sx--; + if (y < 0) sy--; + + /* component-wise summing-up of color values */ + if (im->trueColor) { + r = (int)(m1*gdTrueColorGetRed(c1) + m2*gdTrueColorGetRed(c2) + m3*gdTrueColorGetRed(c3) + m4*gdTrueColorGetRed(c4)); + g = (int)(m1*gdTrueColorGetGreen(c1) + m2*gdTrueColorGetGreen(c2) + m3*gdTrueColorGetGreen(c3) + m4*gdTrueColorGetGreen(c4)); + b = (int)(m1*gdTrueColorGetBlue(c1) + m2*gdTrueColorGetBlue(c2) + m3*gdTrueColorGetBlue(c3) + m4*gdTrueColorGetBlue(c4)); + a = (int)(m1*gdTrueColorGetAlpha(c1) + m2*gdTrueColorGetAlpha(c2) + m3*gdTrueColorGetAlpha(c3) + m4*gdTrueColorGetAlpha(c4)); + } else { + r = (int)(m1*im->red[(c1)] + m2*im->red[(c2)] + m3*im->red[(c3)] + m4*im->red[(c4)]); + g = (int)(m1*im->green[(c1)] + m2*im->green[(c2)] + m3*im->green[(c3)] + m4*im->green[(c4)]); + b = (int)(m1*im->blue[(c1)] + m2*im->blue[(c2)] + m3*im->blue[(c3)] + m4*im->blue[(c4)]); + a = (int)(m1*im->alpha[(c1)] + m2*im->alpha[(c2)] + m3*im->alpha[(c3)] + m4*im->alpha[(c4)]); + } + + r = CLAMP(r, 0, 255); + g = CLAMP(g, 0, 255); + b = CLAMP(b, 0, 255); + a = CLAMP(a, 0, gdAlphaMax); + return gdTrueColorAlpha(r, g, b, a); +} + +/** + * Function: getPixelInterpolated + * Returns the interpolated color value using the default interpolation + * method. The returned color is always in the ARGB format (truecolor). + * + * Parameters: + * im - Image to set the default interpolation method + * y - X value of the ideal position + * y - Y value of the ideal position + * method - Interpolation method <gdInterpolationMethod> + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + * + * See also: + * <gdSetInterpolationMethod> + */ +int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor) +{ + const int xi=(int)((x) < 0 ? x - 1: x); + const int yi=(int)((y) < 0 ? y - 1: y); + int yii; + int i; + double kernel, kernel_cache_y; + double kernel_x[12], kernel_y[4]; + double new_r = 0.0f, new_g = 0.0f, new_b = 0.0f, new_a = 0.0f; + + /* These methods use special implementations */ + if (im->interpolation_id == GD_BILINEAR_FIXED || im->interpolation_id == GD_BICUBIC_FIXED || im->interpolation_id == GD_NEAREST_NEIGHBOUR) { + return -1; + } + + /* Default to full alpha */ + if (bgColor == -1) { + } + + if (im->interpolation_id == GD_WEIGHTED4) { + return getPixelInterpolateWeight(im, x, y, bgColor); + } + + if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) { + if (im->trueColor == 1) { + return getPixelOverflowTC(im, xi, yi, bgColor); + } else { + return getPixelOverflowPalette(im, xi, yi, bgColor); + } + } + if (im->interpolation) { + for (i=0; i<4; i++) { + kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x)); + kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y)); + } + } else { + return -1; + } + + /* + * TODO: use the known fast rgba multiplication implementation once + * the new formats are in place + */ + for (yii = yi-1; yii < yi+3; yii++) { + int xii; + kernel_cache_y = kernel_y[yii-(yi-1)]; + if (im->trueColor) { + for (xii=xi-1; xii<xi+3; xii++) { + const int rgbs = getPixelOverflowTC(im, xii, yii, bgColor); + + kernel = kernel_cache_y * kernel_x[xii-(xi-1)]; + new_r += kernel * gdTrueColorGetRed(rgbs); + new_g += kernel * gdTrueColorGetGreen(rgbs); + new_b += kernel * gdTrueColorGetBlue(rgbs); + new_a += kernel * gdTrueColorGetAlpha(rgbs); + } + } else { + for (xii=xi-1; xii<xi+3; xii++) { + const int rgbs = getPixelOverflowPalette(im, xii, yii, bgColor); + + kernel = kernel_cache_y * kernel_x[xii-(xi-1)]; + new_r += kernel * gdTrueColorGetRed(rgbs); + new_g += kernel * gdTrueColorGetGreen(rgbs); + new_b += kernel * gdTrueColorGetBlue(rgbs); + new_a += kernel * gdTrueColorGetAlpha(rgbs); + } + } + } + + new_r = CLAMP(new_r, 0, 255); + new_g = CLAMP(new_g, 0, 255); + new_b = CLAMP(new_b, 0, 255); + new_a = CLAMP(new_a, 0, gdAlphaMax); + + return gdTrueColorAlpha(((int)new_r), ((int)new_g), ((int)new_b), ((int)new_a)); +} + +static inline LineContribType * _gdContributionsAlloc(unsigned int line_length, unsigned int windows_size) +{ + unsigned int u = 0; + LineContribType *res; + + res = (LineContribType *) gdMalloc(sizeof(LineContribType)); + if (!res) { + return NULL; + } + res->WindowSize = windows_size; + res->LineLength = line_length; + res->ContribRow = (ContributionType *) gdMalloc(line_length * sizeof(ContributionType)); + + for (u = 0 ; u < line_length ; u++) { + res->ContribRow[u].Weights = (double *) gdMalloc(windows_size * sizeof(double)); + } + return res; +} + +static inline void _gdContributionsFree(LineContribType * p) +{ + unsigned int u; + for (u = 0; u < p->LineLength; u++) { + gdFree(p->ContribRow[u].Weights); + } + gdFree(p->ContribRow); + gdFree(p); +} + +static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const interpolation_method pFilter) +{ + double width_d; + double scale_f_d = 1.0; + const double filter_width_d = DEFAULT_BOX_RADIUS; + int windows_size; + unsigned int u; + LineContribType *res; + + if (scale_d < 1.0) { + width_d = filter_width_d / scale_d; + scale_f_d = scale_d; + } else { + width_d= filter_width_d; + } + + windows_size = 2 * (int)ceil(width_d) + 1; + res = _gdContributionsAlloc(line_size, windows_size); + + for (u = 0; u < line_size; u++) { + const double dCenter = (double)u / scale_d; + /* get the significant edge points affecting the pixel */ + register int iLeft = MAX(0, (int)floor (dCenter - width_d)); + int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1); + double dTotalWeight = 0.0; + int iSrc; + + res->ContribRow[u].Left = iLeft; + res->ContribRow[u].Right = iRight; + + /* Cut edge points to fit in filter window in case of spill-off */ + if (iRight - iLeft + 1 > windows_size) { + if (iLeft < ((int)src_size - 1 / 2)) { + iLeft++; + } else { + iRight--; + } + } + + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc))); + } + + if (dTotalWeight < 0.0) { + _gdContributionsFree(res); + return NULL; + } + + if (dTotalWeight > 0.0) { + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight; + } + } + } + return res; +} + +static inline void _gdScaleRow(gdImagePtr pSrc, unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib) +{ + int *p_src_row = pSrc->tpixels[row]; + int *p_dst_row = dst->tpixels[row]; + unsigned int x; + + for (x = 0; x < dst_width - 1; x++) { + register unsigned char r = 0, g = 0, b = 0, a = 0; + const int left = contrib->ContribRow[x].Left; + const int right = contrib->ContribRow[x].Right; + int i; + + /* Accumulate each channel */ + for (i = left; i <= right; i++) { + const int left_channel = i - left; + r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i]))); + g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i]))); + b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i]))); + a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i]))); + } + p_dst_row[x] = gdTrueColorAlpha(r, g, b, a); + } +} + +static inline void _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst, unsigned int dst_width, unsigned int dst_height) +{ + unsigned int u; + LineContribType * contrib; + + /* same width, just copy it */ + if (dst_width == src_width) { + unsigned int y; + for (y = 0; y < src_height - 1; ++y) { + memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); + } + } + + contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation); + if (contrib == NULL) { + return; + } + /* Scale each row */ + for (u = 0; u < dst_height - 1; u++) { + _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib); + } + _gdContributionsFree (contrib); +} + +static inline void _gdScaleCol (gdImagePtr pSrc, unsigned int src_width, gdImagePtr pRes, unsigned int dst_width, unsigned int dst_height, unsigned int uCol, LineContribType *contrib) +{ + unsigned int y; + for (y = 0; y < dst_height - 1; y++) { + register unsigned char r = 0, g = 0, b = 0, a = 0; + const int iLeft = contrib->ContribRow[y].Left; + const int iRight = contrib->ContribRow[y].Right; + int i; + int *row = pRes->tpixels[y]; + + /* Accumulate each channel */ + for (i = iLeft; i <= iRight; i++) { + const int pCurSrc = pSrc->tpixels[i][uCol]; + const int i_iLeft = i - iLeft; + r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc))); + g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc))); + b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc))); + a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc))); + } + pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a); + } +} + +static inline void _gdScaleVert (const gdImagePtr pSrc, const unsigned int src_width, const unsigned int src_height, const gdImagePtr pDst, const unsigned int dst_width, const unsigned int dst_height) +{ + unsigned int u; + LineContribType * contrib; + + /* same height, copy it */ + if (src_height == dst_height) { + unsigned int y; + for (y = 0; y < src_height - 1; ++y) { + memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); + } + } + + contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation); + /* scale each column */ + for (u = 0; u < dst_width - 1; u++) { + _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib); + } + _gdContributionsFree(contrib); +} + +gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr tmp_im; + gdImagePtr dst; + + tmp_im = gdImageCreateTrueColor(new_width, src_height); + if (tmp_im == NULL) { + return NULL; + } + _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height); + + dst = gdImageCreateTrueColor(new_width, new_height); + if (dst == NULL) { + gdFree(tmp_im); + return NULL; + } + _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height); + gdFree(tmp_im); + + return dst; +} + +gdImagePtr Scale(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const gdImagePtr dst, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr tmp_im; + + tmp_im = gdImageCreateTrueColor(new_width, src_height); + if (tmp_im == NULL) { + return NULL; + } + _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height); + + _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height); + + gdFree(tmp_im); + return dst; +} + +/* + BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx + http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class + Integer only implementation, good to have for common usages like pre scale very large + images before using another interpolation methods for the last step. +*/ +gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height) +{ + const unsigned long new_width = MAX(1, width); + const unsigned long new_height = MAX(1, height); + const float dx = (float)im->sx / (float)new_width; + const float dy = (float)im->sy / (float)new_height; + const gdFixed f_dx = gd_ftofx(dx); + const gdFixed f_dy = gd_ftofx(dy); + + gdImagePtr dst_img; + unsigned long dst_offset_x; + unsigned long dst_offset_y = 0; + unsigned int i; + + dst_img = gdImageCreateTrueColor(new_width, new_height); + + if (dst_img == NULL) { + return NULL; + } + + for (i=0; i<new_height; i++) { + unsigned int j; + dst_offset_x = 0; + if (im->trueColor) { + for (j=0; j<new_width; j++) { + const gdFixed f_i = gd_itofx(i); + const gdFixed f_j = gd_itofx(j); + const gdFixed f_a = gd_mulfx(f_i, f_dy); + const gdFixed f_b = gd_mulfx(f_j, f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + + dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n]; + } + } else { + for (j=0; j<new_width; j++) { + const gdFixed f_i = gd_itofx(i); + const gdFixed f_j = gd_itofx(j); + const gdFixed f_a = gd_mulfx(f_i, f_dy); + const gdFixed f_b = gd_mulfx(f_j, f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + + dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]); + } + } + dst_offset_y++; + } + return dst_img; +} + +static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->tpixels[y][x]; + if (c == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } + return c; + } else { + register int border = 0; + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = im->tpixels[im->cy2][x]; + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = im->tpixels[y][im->cx1]; + goto processborder; + } + + if (x > im->cx2) { + border = im->tpixels[y][im->cx2]; + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127); + } + } +} + +static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + long _width = MAX(1, new_width); + long _height = MAX(1, new_height); + float dx = (float)gdImageSX(im) / (float)_width; + float dy = (float)gdImageSY(im) / (float)_height; + gdFixed f_dx = gd_ftofx(dx); + gdFixed f_dy = gd_ftofx(dy); + gdFixed f_1 = gd_itofx(1); + + int dst_offset_h; + int dst_offset_v = 0; + long i; + gdImagePtr new_img; + const int transparent = im->transparent; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (new_img == NULL) { + return NULL; + } + new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]); + + for (i=0; i < _height; i++) { + long j; + const gdFixed f_i = gd_itofx(i); + const gdFixed f_a = gd_mulfx(f_i, f_dy); + register long m = gd_fxtoi(f_a); + + dst_offset_h = 0; + + for (j=0; j < _width; j++) { + /* Update bitmap */ + gdFixed f_j = gd_itofx(j); + gdFixed f_b = gd_mulfx(f_j, f_dx); + + const long n = gd_fxtoi(f_b); + gdFixed f_f = f_a - gd_itofx(m); + gdFixed f_g = f_b - gd_itofx(n); + + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + register gdFixed f_r1, f_r2, f_r3, f_r4, + f_g1, f_g2, f_g3, f_g4, + f_b1, f_b2, f_b3, f_b4, + f_a1, f_a2, f_a3, f_a4; + + /* zero for the background color, nothig gets outside anyway */ + pixel1 = getPixelOverflowPalette(im, n, m, 0); + pixel2 = getPixelOverflowPalette(im, n + 1, m, 0); + pixel3 = getPixelOverflowPalette(im, n, m + 1, 0); + pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, 0); + + f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + + { + const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); + const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); + const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); + const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); + + new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; +} + +static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + long dst_w = MAX(1, new_width); + long dst_h = MAX(1, new_height); + float dx = (float)gdImageSX(im) / (float)dst_w; + float dy = (float)gdImageSY(im) / (float)dst_h; + gdFixed f_dx = gd_ftofx(dx); + gdFixed f_dy = gd_ftofx(dy); + gdFixed f_1 = gd_itofx(1); + + int dst_offset_h; + int dst_offset_v = 0; + int dwSrcTotalOffset; + long i; + gdImagePtr new_img; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (!new_img){ + return NULL; + } + + for (i=0; i < dst_h; i++) { + long j; + dst_offset_h = 0; + for (j=0; j < dst_w; j++) { + /* Update bitmap */ + gdFixed f_i = gd_itofx(i); + gdFixed f_j = gd_itofx(j); + gdFixed f_a = gd_mulfx(f_i, f_dy); + gdFixed f_b = gd_mulfx(f_j, f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + gdFixed f_f = f_a - gd_itofx(m); + gdFixed f_g = f_b - gd_itofx(n); + + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + register gdFixed f_r1, f_r2, f_r3, f_r4, + f_g1, f_g2, f_g3, f_g4, + f_b1, f_b2, f_b3, f_b4, + f_a1, f_a2, f_a3, f_a4; + dwSrcTotalOffset = m + n; + /* 0 for bgColor, nothing gets outside anyway */ + pixel1 = getPixelOverflowTC(im, n, m, 0); + pixel2 = getPixelOverflowTC(im, n + 1, m, 0); + pixel3 = getPixelOverflowTC(im, n, m + 1, 0); + pixel4 = getPixelOverflowTC(im, n + 1, m + 1, 0); + + f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + { + const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); + const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); + const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); + const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); + + new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; +} + +gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + if (im->trueColor) { + return gdImageScaleBilinearTC(im, new_width, new_height); + } else { + return gdImageScaleBilinearPalette(im, new_width, new_height); + } +} + +gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height) +{ + const long new_width = MAX(1, width); + const long new_height = MAX(1, height); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width); + const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height); + const gdFixed f_1 = gd_itofx(1); + const gdFixed f_2 = gd_itofx(2); + const gdFixed f_4 = gd_itofx(4); + const gdFixed f_6 = gd_itofx(6); + const gdFixed f_gamma = gd_ftofx(1.04f); + gdImagePtr dst; + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + long i; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + + dst->saveAlphaFlag = 1; + + for (i=0; i < new_height; i++) { + long j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy); + const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + const gdFixed f_f = f_a - gd_itofx(m); + const gdFixed f_g = f_b - gd_itofx(n); + unsigned int src_offset_x[16], src_offset_y[16]; + long k; + register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0; + unsigned char red, green, blue, alpha = 0; + int *dst_row = dst->tpixels[dst_offset_y]; + + if ((m < 1) || (n < 1)) { + src_offset_x[0] = n; + src_offset_y[0] = m; + } else { + src_offset_x[0] = n - 1; + src_offset_y[0] = m; + } + + if (m < 1) { + src_offset_x[1] = n; + src_offset_y[1] = m; + } else { + src_offset_x[1] = n; + src_offset_y[1] = m; + } + + if ((m < 1) || (n >= src_w - 1)) { + src_offset_x[2] = n; + src_offset_y[2] = m; + } else { + src_offset_x[2] = n + 1; + src_offset_y[2] = m; + } + + if ((m < 1) || (n >= src_w - 2)) { + src_offset_x[3] = n; + src_offset_y[3] = m; + } else { + src_offset_x[3] = n + 1 + 1; + src_offset_y[3] = m; + } + + if (n < 1) { + src_offset_x[4] = n; + src_offset_y[4] = m; + } else { + src_offset_x[4] = n - 1; + src_offset_y[4] = m; + } + + src_offset_x[5] = n; + src_offset_y[5] = m; + if (n >= src_w-1) { + src_offset_x[6] = n; + src_offset_y[6] = m; + } else { + src_offset_x[6] = n + 1; + src_offset_y[6] = m; + } + + if (n >= src_w - 2) { + src_offset_x[7] = n; + src_offset_y[7] = m; + } else { + src_offset_x[7] = n + 1 + 1; + src_offset_y[7] = m; + } + + if ((m >= src_h - 1) || (n < 1)) { + src_offset_x[8] = n; + src_offset_y[8] = m; + } else { + src_offset_x[8] = n - 1; + src_offset_y[8] = m; + } + + if (m >= src_h - 1) { + src_offset_x[8] = n; + src_offset_y[8] = m; + } else { + src_offset_x[9] = n; + src_offset_y[9] = m; + } + + if ((m >= src_h-1) || (n >= src_w-1)) { + src_offset_x[10] = n; + src_offset_y[10] = m; + } else { + src_offset_x[10] = n + 1; + src_offset_y[10] = m; + } + + if ((m >= src_h - 1) || (n >= src_w - 2)) { + src_offset_x[11] = n; + src_offset_y[11] = m; + } else { + src_offset_x[11] = n + 1 + 1; + src_offset_y[11] = m; + } + + if ((m >= src_h - 2) || (n < 1)) { + src_offset_x[12] = n; + src_offset_y[12] = m; + } else { + src_offset_x[12] = n - 1; + src_offset_y[12] = m; + } + + if (m >= src_h - 2) { + src_offset_x[13] = n; + src_offset_y[13] = m; + } else { + src_offset_x[13] = n; + src_offset_y[13] = m; + } + + if ((m >= src_h - 2) || (n >= src_w - 1)) { + src_offset_x[14] = n; + src_offset_y[14] = m; + } else { + src_offset_x[14] = n + 1; + src_offset_y[14] = m; + } + + if ((m >= src_h - 2) || (n >= src_w - 2)) { + src_offset_x[15] = n; + src_offset_y[15] = m; + } else { + src_offset_x[15] = n + 1 + 1; + src_offset_y[15] = m; + } + + for (k = -1; k < 3; k++) { + const gdFixed f = gd_itofx(k)-f_f; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0; + register gdFixed f_RY; + int l; + + if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2)); + if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1)); + if (f > 0) f_c = gd_mulfx(f, gd_mulfx(f,f)); + if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1)); + + f_RY = gd_divfx((f_a - gd_mulfx(f_4,f_b) + gd_mulfx(f_6,f_c) - gd_mulfx(f_4,f_d)),f_6); + + for (l = -1; l < 3; l++) { + const gdFixed f = gd_itofx(l) - f_g; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0; + register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba; + register int c; + const int _k = ((k+1)*4) + (l+1); + + if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + + if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + + if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f)); + + if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + + f_RX = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6); + f_R = gd_mulfx(f_RY,f_RX); + + c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)]; + f_rs = gd_itofx(gdTrueColorGetRed(c)); + f_gs = gd_itofx(gdTrueColorGetGreen(c)); + f_bs = gd_itofx(gdTrueColorGetBlue(c)); + f_ba = gd_itofx(gdTrueColorGetAlpha(c)); + + f_red += gd_mulfx(f_rs,f_R); + f_green += gd_mulfx(f_gs,f_R); + f_blue += gd_mulfx(f_bs,f_R); + f_alpha += gd_mulfx(f_ba,f_R); + } + } + + red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gamma)), 0, 255); + green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)), 0, 255); + blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gamma)), 0, 255); + alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gamma)), 0, 127); + + *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha); + + dst_offset_x++; + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr im_scaled = NULL; + + if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) { + return 0; + } + + switch (src->interpolation_id) { + /*Special cases, optimized implementations */ + case GD_NEAREST_NEIGHBOUR: + im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height); + break; + + case GD_BILINEAR_FIXED: + im_scaled = gdImageScaleBilinear(src, new_width, new_height); + break; + + case GD_BICUBIC_FIXED: + im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height); + break; + + /* generic */ + default: + if (src->interpolation == NULL) { + return NULL; + } + im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height); + break; + } + return im_scaled; +} + +gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f); + const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + gdFixed f_j = gd_itofx((int)j - (int)new_width/2); + gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + long m = gd_fxtoi(f_m); + long n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n]; + } + } else { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f); + const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + const gdFixed f_slop_y = f_sin; + const gdFixed f_slop_x = f_cos; + const gdFixed f_slop = f_slop_x > 0 && f_slop_x > 0 ? + f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y) + : 0; + + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2); + gdFixed f_j = gd_itofx((int)j - (int)new_width / 2); + gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + long m = gd_fxtoi(f_m); + long n = gd_fxtoi(f_n); + + if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) { + gdFixed f_127 = gd_itofx(127); + register int c = getPixelInterpolated(src, n, m, bgColor); + c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24); + + dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c); + } else { + dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor); + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = (float)((- degrees / 180.0f) * M_PI); + const unsigned int src_w = gdImageSX(src); + const unsigned int src_h = gdImageSY(src); + unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f)); + unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f)); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + const gdFixed f_1 = gd_itofx(1); + unsigned int i; + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int src_offset_x, src_offset_y; + gdImagePtr dst; + + dst = gdImageCreateTrueColor(new_width, new_height); + if (dst == NULL) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + const gdFixed f_j = gd_itofx((int)j - (int)new_width/2); + const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + const unsigned int m = gd_fxtoi(f_m); + const unsigned int n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) { + const gdFixed f_f = f_m - gd_itofx(m); + const gdFixed f_g = f_n - gd_itofx(n); + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + + if (n < src_w - 1) { + src_offset_x = n + 1; + src_offset_y = m; + } + + if (m < src_h-1) { + src_offset_x = n; + src_offset_y = m + 1; + } + + if (!((n >= src_w-1) || (m >= src_h-1))) { + src_offset_x = n + 1; + src_offset_y = m + 1; + } + { + const int pixel1 = src->tpixels[src_offset_y][src_offset_x]; + register int pixel2, pixel3, pixel4; + + if (src_offset_y + 1 >= src_h) { + pixel2 = bgColor; + pixel3 = bgColor; + pixel4 = bgColor; + } else if (src_offset_x + 1 >= src_w) { + pixel2 = bgColor; + pixel3 = bgColor; + pixel4 = bgColor; + } else { + pixel2 = src->tpixels[src_offset_y][src_offset_x + 1]; + pixel3 = src->tpixels[src_offset_y + 1][src_offset_x]; + pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1]; + } + { + const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + const gdFixed f_red = gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4); + const gdFixed f_green = gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4); + const gdFixed f_blue = gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4); + const gdFixed f_alpha = gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4); + + const unsigned char red = (unsigned char) CLAMP(gd_fxtoi(f_red), 0, 255); + const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255); + const unsigned char blue = (unsigned char) CLAMP(gd_fxtoi(f_blue), 0, 255); + const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127); + + dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha); + } + } + } else { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor) +{ + const float _angle = (float)((- degrees / 180.0f) * M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f)); + const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f)); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + const gdFixed f_1 = gd_itofx(1); + const gdFixed f_2 = gd_itofx(2); + const gdFixed f_4 = gd_itofx(4); + const gdFixed f_6 = gd_itofx(6); + const gdFixed f_gama = gd_ftofx(1.04f); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + dst = gdImageCreateTrueColor(new_width, new_height); + + if (dst == NULL) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i=0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_i = gd_itofx((int)i - (int)new_height/2); + const gdFixed f_j = gd_itofx((int)j - (int)new_width/2); + const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + const int m = gd_fxtoi(f_m); + const int n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) { + const gdFixed f_f = f_m - gd_itofx(m); + const gdFixed f_g = f_n - gd_itofx(n); + unsigned int src_offset_x[16], src_offset_y[16]; + unsigned char red, green, blue, alpha; + gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0; + int k; + + if ((m < 1) || (n < 1)) { + src_offset_x[0] = n; + src_offset_y[0] = m; + } else { + src_offset_x[0] = n - 1; + src_offset_y[0] = m; + } + + if (m < 1) { + src_offset_x[1] = n; + src_offset_y[1] = m; + } else { + src_offset_x[1] = n; + src_offset_y[1] = m ; + } + + if ((m < 1) || (n >= src_w-1)) { + src_offset_x[2] = - 1; + src_offset_y[2] = - 1; + } else { + src_offset_x[2] = n + 1; + src_offset_y[2] = m ; + } + + if ((m < 1) || (n >= src_w-2)) { + src_offset_x[3] = - 1; + src_offset_y[3] = - 1; + } else { + src_offset_x[3] = n + 1 + 1; + src_offset_y[3] = m ; + } + + if (n < 1) { + src_offset_x[4] = - 1; + src_offset_y[4] = - 1; + } else { + src_offset_x[4] = n - 1; + src_offset_y[4] = m; + } + + src_offset_x[5] = n; + src_offset_y[5] = m; + if (n >= src_w-1) { + src_offset_x[6] = - 1; + src_offset_y[6] = - 1; + } else { + src_offset_x[6] = n + 1; + src_offset_y[6] = m; + } + + if (n >= src_w-2) { + src_offset_x[7] = - 1; + src_offset_y[7] = - 1; + } else { + src_offset_x[7] = n + 1 + 1; + src_offset_y[7] = m; + } + + if ((m >= src_h-1) || (n < 1)) { + src_offset_x[8] = - 1; + src_offset_y[8] = - 1; + } else { + src_offset_x[8] = n - 1; + src_offset_y[8] = m; + } + + if (m >= src_h-1) { + src_offset_x[8] = - 1; + src_offset_y[8] = - 1; + } else { + src_offset_x[9] = n; + src_offset_y[9] = m; + } + + if ((m >= src_h-1) || (n >= src_w-1)) { + src_offset_x[10] = - 1; + src_offset_y[10] = - 1; + } else { + src_offset_x[10] = n + 1; + src_offset_y[10] = m; + } + + if ((m >= src_h-1) || (n >= src_w-2)) { + src_offset_x[11] = - 1; + src_offset_y[11] = - 1; + } else { + src_offset_x[11] = n + 1 + 1; + src_offset_y[11] = m; + } + + if ((m >= src_h-2) || (n < 1)) { + src_offset_x[12] = - 1; + src_offset_y[12] = - 1; + } else { + src_offset_x[12] = n - 1; + src_offset_y[12] = m; + } + + if (m >= src_h-2) { + src_offset_x[13] = - 1; + src_offset_y[13] = - 1; + } else { + src_offset_x[13] = n; + src_offset_y[13] = m; + } + + if ((m >= src_h-2) || (n >= src_w - 1)) { + src_offset_x[14] = - 1; + src_offset_y[14] = - 1; + } else { + src_offset_x[14] = n + 1; + src_offset_y[14] = m; + } + + if ((m >= src_h-2) || (n >= src_w-2)) { + src_offset_x[15] = - 1; + src_offset_y[15] = - 1; + } else { + src_offset_x[15] = n + 1 + 1; + src_offset_y[15] = m; + } + + for (k=-1; k<3; k++) { + const gdFixed f = gd_itofx(k)-f_f; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0; + gdFixed f_RY; + int l; + + if (f_fp2 > 0) { + f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + } + + if (f_fp1 > 0) { + f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + } + + if (f > 0) { + f_c = gd_mulfx(f,gd_mulfx(f,f)); + } + + if (f_fm1 > 0) { + f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + } + f_RY = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6); + + for (l=-1; l< 3; l++) { + const gdFixed f = gd_itofx(l) - f_g; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0; + gdFixed f_RX, f_R; + const int _k = ((k + 1) * 4) + (l + 1); + register gdFixed f_rs, f_gs, f_bs, f_as; + register int c; + + if (f_fp2 > 0) { + f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + } + + if (f_fp1 > 0) { + f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + } + + if (f > 0) { + f_c = gd_mulfx(f,gd_mulfx(f,f)); + } + + if (f_fm1 > 0) { + f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + } + + f_RX = gd_divfx((f_a - gd_mulfx(f_4, f_b) + gd_mulfx(f_6, f_c) - gd_mulfx(f_4, f_d)), f_6); + f_R = gd_mulfx(f_RY, f_RX); + + if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) { + c = bgColor; + } else if ((src_offset_x[_k] <= 1) || (src_offset_y[_k] <= 1) || (src_offset_y[_k] >= (int)src_h - 1) || (src_offset_x[_k] >= (int)src_w - 1)) { + gdFixed f_127 = gd_itofx(127); + c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]]; + c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24); + c = _color_blend(bgColor, c); + } else { + c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]]; + } + + f_rs = gd_itofx(gdTrueColorGetRed(c)); + f_gs = gd_itofx(gdTrueColorGetGreen(c)); + f_bs = gd_itofx(gdTrueColorGetBlue(c)); + f_as = gd_itofx(gdTrueColorGetAlpha(c)); + + f_red += gd_mulfx(f_rs, f_R); + f_green += gd_mulfx(f_gs, f_R); + f_blue += gd_mulfx(f_bs, f_R); + f_alpha += gd_mulfx(f_as, f_R); + } + } + + red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)), 0, 255); + green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255); + blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)), 0, 255); + alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127); + + dst->tpixels[dst_offset_y][dst_offset_x] = gdTrueColorAlpha(red, green, blue, alpha); + } else { + dst->tpixels[dst_offset_y][dst_offset_x] = bgColor; + } + dst_offset_x++; + } + + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor) +{ + const int angle_rounded = (int)floor(angle * 100); + + if (bgcolor < 0) { + return NULL; + } + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + if (bgcolor >= 0) { + bgcolor = gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]); + } + gdImagePaletteToTrueColor(src); + } + + /* no interpolation needed here */ + switch (angle_rounded) { + case 9000: + return gdImageRotate90(src, 0); + case 18000: + return gdImageRotate180(src, 0); + case 27000: + return gdImageRotate270(src, 0); + } + + if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) { + return NULL; + } + + switch (src->interpolation_id) { + case GD_NEAREST_NEIGHBOUR: + return gdImageRotateNearestNeighbour(src, angle, bgcolor); + break; + + case GD_BILINEAR_FIXED: + return gdImageRotateBilinear(src, angle, bgcolor); + break; + + case GD_BICUBIC_FIXED: + return gdImageRotateBicubicFixed(src, angle, bgcolor); + break; + + default: + return gdImageRotateGeneric(src, angle, bgcolor); + } + return NULL; +} + +/** + * Title: Affine transformation + **/ + +/** + * Group: Transform + **/ + + static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r) +{ + int c1x, c1y, c2x, c2y; + int x1,y1; + + gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y); + x1 = r->x + r->width - 1; + y1 = r->y + r->height - 1; + r->x = CLAMP(r->x, c1x, c2x); + r->y = CLAMP(r->y, c1y, c2y); + r->width = CLAMP(x1, c1x, c2x) - r->x + 1; + r->height = CLAMP(y1, c1y, c2y) - r->y + 1; +} + +void gdDumpRect(const char *msg, gdRectPtr r) +{ + printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height); +} + +/** + * Function: gdTransformAffineGetImage + * Applies an affine transformation to a region and return an image + * containing the complete transformation. + * + * Parameters: + * dst - Pointer to a gdImagePtr to store the created image, NULL when + * the creation or the transformation failed + * src - Source image + * src_area - rectangle defining the source region to transform + * dstY - Y position in the destination image + * affine - The desired affine transformation + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineGetImage(gdImagePtr *dst, + const gdImagePtr src, + gdRectPtr src_area, + const double affine[6]) +{ + int res; + double m[6]; + gdRect bbox; + gdRect area_full; + + if (src_area == NULL) { + area_full.x = 0; + area_full.y = 0; + area_full.width = gdImageSX(src); + area_full.height = gdImageSY(src); + src_area = &area_full; + } + + gdTransformAffineBoundingBox(src_area, affine, &bbox); + + *dst = gdImageCreateTrueColor(bbox.width, bbox.height); + if (*dst == NULL) { + return GD_FALSE; + } + (*dst)->saveAlphaFlag = 1; + + if (!src->trueColor) { + gdImagePaletteToTrueColor(src); + } + + /* Translate to dst origin (0,0) */ + gdAffineTranslate(m, -bbox.x, -bbox.y); + gdAffineConcat(m, affine, m); + + gdImageAlphaBlending(*dst, 0); + + res = gdTransformAffineCopy(*dst, + 0,0, + src, + src_area, + m); + + if (res != GD_TRUE) { + gdImageDestroy(*dst); + dst = NULL; + return GD_FALSE; + } else { + return GD_TRUE; + } +} + +/** + * Function: gdTransformAffineCopy + * Applies an affine transformation to a region and copy the result + * in a destination to the given position. + * + * Parameters: + * dst - Image to draw the transformed image + * src - Source image + * dstX - X position in the destination image + * dstY - Y position in the destination image + * src_area - Rectangular region to rotate in the src image + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineCopy(gdImagePtr dst, + int dst_x, int dst_y, + const gdImagePtr src, + gdRectPtr src_region, + const double affine[6]) +{ + int c1x,c1y,c2x,c2y; + int backclip = 0; + int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2; + register int x, y, src_offset_x, src_offset_y; + double inv[6]; + int *dst_p; + gdPointF pt, src_pt; + gdRect bbox; + int end_x, end_y; + gdInterpolationMethod interpolation_id_bak = GD_DEFAULT; + interpolation_method interpolation_bak; + + /* These methods use special implementations */ + if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) { + interpolation_id_bak = src->interpolation_id; + interpolation_bak = src->interpolation; + + gdImageSetInterpolationMethod(src, GD_BICUBIC); + } + + + gdImageClipRectangle(src, src_region); + + if (src_region->x > 0 || src_region->y > 0 + || src_region->width < gdImageSX(src) + || src_region->height < gdImageSY(src)) { + backclip = 1; + + gdImageGetClip(src, &backup_clipx1, &backup_clipy1, + &backup_clipx2, &backup_clipy2); + + gdImageSetClip(src, src_region->x, src_region->y, + src_region->x + src_region->width - 1, + src_region->y + src_region->height - 1); + } + + if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) { + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, + backup_clipx2, backup_clipy2); + } + gdImageSetInterpolationMethod(src, interpolation_id_bak); + return GD_FALSE; + } + + gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y); + + end_x = bbox.width + (int) fabs(bbox.x); + end_y = bbox.height + (int) fabs(bbox.y); + + /* Get inverse affine to let us work with destination -> source */ + gdAffineInvert(inv, affine); + + src_offset_x = src_region->x; + src_offset_y = src_region->y; + + if (dst->alphaBlendingFlag) { + for (y = bbox.y; y <= end_y; y++) { + pt.y = y + 0.5; + for (x = 0; x <= end_x; x++) { + pt.x = x + 0.5; + gdAffineApplyToPointF(&src_pt, &pt, inv); + gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0)); + } + } + } else { + for (y = 0; y <= end_y; y++) { + pt.y = y + 0.5 + bbox.y; + if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) { + continue; + } + dst_p = dst->tpixels[dst_y + y] + dst_x; + + for (x = 0; x <= end_x; x++) { + pt.x = x + 0.5 + bbox.x; + gdAffineApplyToPointF(&src_pt, &pt, inv); + + if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) { + break; + } + *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1); + } + } + } + + /* Restore clip if required */ + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, + backup_clipx2, backup_clipy2); + } + + gdImageSetInterpolationMethod(src, interpolation_id_bak); + return GD_TRUE; +} + +/** + * Function: gdTransformAffineBoundingBox + * Returns the bounding box of an affine transformation applied to a + * rectangular area <gdRect> + * + * Parameters: + * src - Rectangular source area for the affine transformation + * affine - the affine transformation + * bbox - the resulting bounding box + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) +{ + gdPointF extent[4], min, max, point; + int i; + + extent[0].x=0.0; + extent[0].y=0.0; + extent[1].x=(double) src->width; + extent[1].y=0.0; + extent[2].x=(double) src->width; + extent[2].y=(double) src->height; + extent[3].x=0.0; + extent[3].y=(double) src->height; + + for (i=0; i < 4; i++) { + point=extent[i]; + if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) { + return GD_FALSE; + } + } + min=extent[0]; + max=extent[0]; + + for (i=1; i < 4; i++) { + if (min.x > extent[i].x) + min.x=extent[i].x; + if (min.y > extent[i].y) + min.y=extent[i].y; + if (max.x < extent[i].x) + max.x=extent[i].x; + if (max.y < extent[i].y) + max.y=extent[i].y; + } + bbox->x = (int) min.x; + bbox->y = (int) min.y; + bbox->width = (int) floor(max.x - min.x) - 1; + bbox->height = (int) floor(max.y - min.y); + return GD_TRUE; +} + +int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id) +{ + if (im == NULL || id < 0 || id > GD_METHOD_COUNT) { + return 0; + } + + switch (id) { + case GD_DEFAULT: + im->interpolation_id = GD_BILINEAR_FIXED; + im->interpolation = NULL; + break; + + /* Optimized versions */ + case GD_BILINEAR_FIXED: + case GD_BICUBIC_FIXED: + case GD_NEAREST_NEIGHBOUR: + case GD_WEIGHTED4: + im->interpolation = NULL; + break; + + /* generic versions*/ + case GD_BELL: + im->interpolation = filter_bell; + break; + case GD_BESSEL: + im->interpolation = filter_bessel; + break; + case GD_BICUBIC: + im->interpolation = filter_bicubic; + break; + case GD_BLACKMAN: + im->interpolation = filter_blackman; + break; + case GD_BOX: + im->interpolation = filter_box; + break; + case GD_BSPLINE: + im->interpolation = filter_bspline; + break; + case GD_CATMULLROM: + im->interpolation = filter_catmullrom; + break; + case GD_GAUSSIAN: + im->interpolation = filter_gaussian; + break; + case GD_GENERALIZED_CUBIC: + im->interpolation = filter_generalized_cubic; + break; + case GD_HERMITE: + im->interpolation = filter_hermite; + break; + case GD_HAMMING: + im->interpolation = filter_hamming; + break; + case GD_HANNING: + im->interpolation = filter_hanning; + break; + case GD_MITCHELL: + im->interpolation = filter_mitchell; + break; + case GD_POWER: + im->interpolation = filter_power; + break; + case GD_QUADRATIC: + im->interpolation = filter_quadratic; + break; + case GD_SINC: + im->interpolation = filter_sinc; + break; + case GD_TRIANGLE: + im->interpolation = filter_triangle; + break; + + default: + return 0; + break; + } + im->interpolation_id = id; + return 1; +} + +#ifdef _MSC_VER +# pragma optimize("", on) +#endif diff --git a/ext/gd/libgd/gd_jpeg.c b/ext/gd/libgd/gd_jpeg.c index 175c5b85fd..a882b28c88 100644 --- a/ext/gd/libgd/gd_jpeg.c +++ b/ext/gd/libgd/gd_jpeg.c @@ -269,21 +269,31 @@ void gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) gdFree (row); } -gdImagePtr gdImageCreateFromJpeg (FILE * inFile, int ignore_warning) +gdImagePtr gdImageCreateFromJpeg (FILE * inFile) +{ + return gdImageCreateFromJpegEx(inFile, 1); +} + +gdImagePtr gdImageCreateFromJpegEx (FILE * inFile, int ignore_warning) { gdImagePtr im; gdIOCtx *in = gdNewFileCtx(inFile); - im = gdImageCreateFromJpegCtx(in, ignore_warning); + im = gdImageCreateFromJpegCtxEx(in, ignore_warning); in->gd_free (in); return im; } -gdImagePtr gdImageCreateFromJpegPtr (int size, void *data, int ignore_warning) +gdImagePtr gdImageCreateFromJpegPtr (int size, void *data) +{ + return gdImageCreateFromJpegPtrEx(size, data, 1); +} + +gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning) { gdImagePtr im; gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromJpegCtx(in, ignore_warning); + im = gdImageCreateFromJpegCtxEx(in, ignore_warning); in->gd_free(in); return im; @@ -298,7 +308,12 @@ static int CMYKToRGB(int c, int m, int y, int k, int inverted); * Create a gd-format image from the JPEG-format INFILE. Returns the * image, or NULL upon error. */ -gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile, int ignore_warning) +gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile) +{ + return gdImageCreateFromJpegCtxEx(infile, 1); +} + +gdImagePtr gdImageCreateFromJpegCtxEx (gdIOCtx * infile, int ignore_warning) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; diff --git a/ext/gd/libgd/gd_matrix.c b/ext/gd/libgd/gd_matrix.c new file mode 100644 index 0000000000..83438bdbe3 --- /dev/null +++ b/ext/gd/libgd/gd_matrix.c @@ -0,0 +1,334 @@ +#include "gd.h" +#include <math.h> + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +/** + * Title: Matrix + * Group: Affine Matrix + */ + +/** + * Function: gdAffineApplyToPointF + * Applies an affine transformation to a point (floating point + * gdPointF) + * + * + * Parameters: + * dst - Where to store the resulting point + * affine - Source Point + * flip_horz - affine matrix + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, + const double affine[6]) +{ + double x = src->x; + double y = src->y; + x = src->x; + y = src->y; + dst->x = x * affine[0] + y * affine[2] + affine[4]; + dst->y = x * affine[1] + y * affine[3] + affine[5]; + return GD_TRUE; +} + +/** + * Function: gdAffineInvert + * Find the inverse of an affine transformation. + * + * All non-degenerate affine transforms are invertible. Applying the + * inverted matrix will restore the original values. Multiplying <src> + * by <dst> (commutative) will return the identity affine (rounding + * error possible). + * + * Parameters: + * dst - Where to store the resulting affine transform + * src_affine - Original affine matrix + * flip_horz - Whether or not to flip horizontally + * flip_vert - Whether or not to flip vertically + * + * See also: + * <gdAffineIdentity> + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineInvert (double dst[6], const double src[6]) +{ + double r_det = (src[0] * src[3] - src[1] * src[2]); + + if (r_det <= 0.0) { + return GD_FALSE; + } + + r_det = 1.0 / r_det; + dst[0] = src[3] * r_det; + dst[1] = -src[1] * r_det; + dst[2] = -src[2] * r_det; + dst[3] = src[0] * r_det; + dst[4] = -src[4] * dst[0] - src[5] * dst[2]; + dst[5] = -src[4] * dst[1] - src[5] * dst[3]; + return GD_TRUE; +} + +/** + * Function: gdAffineFlip + * Flip an affine transformation horizontally or vertically. + * + * Flips the affine transform, giving GD_FALSE for <flip_horz> and + * <flip_vert> will clone the affine matrix. GD_TRUE for both will + * copy a 180° rotation. + * + * Parameters: + * dst - Where to store the resulting affine transform + * src_affine - Original affine matrix + * flip_h - Whether or not to flip horizontally + * flip_v - Whether or not to flip vertically + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineFlip (double dst[6], const double src[6], const int flip_h, const int flip_v) +{ + dst[0] = flip_h ? - src[0] : src[0]; + dst[1] = flip_h ? - src[1] : src[1]; + dst[2] = flip_v ? - src[2] : src[2]; + dst[3] = flip_v ? - src[3] : src[3]; + dst[4] = flip_h ? - src[4] : src[4]; + dst[5] = flip_v ? - src[5] : src[5]; + return GD_TRUE; +} + +/** + * Function: gdAffineConcat + * Concat (Multiply) two affine transformation matrices. + * + * Concats two affine transforms together, i.e. the result + * will be the equivalent of doing first the transformation m1 and then + * m2. All parameters can be the same matrix (safe to call using + * the same array for all three arguments). + * + * Parameters: + * dst - Where to store the resulting affine transform + * m1 - First affine matrix + * m2 - Second affine matrix + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]) +{ + double dst0, dst1, dst2, dst3, dst4, dst5; + + dst0 = m1[0] * m2[0] + m1[1] * m2[2]; + dst1 = m1[0] * m2[1] + m1[1] * m2[3]; + dst2 = m1[2] * m2[0] + m1[3] * m2[2]; + dst3 = m1[2] * m2[1] + m1[3] * m2[3]; + dst4 = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; + dst5 = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; + dst[0] = dst0; + dst[1] = dst1; + dst[2] = dst2; + dst[3] = dst3; + dst[4] = dst4; + dst[5] = dst5; + return GD_TRUE; +} + +/** + * Function: gdAffineIdentity + * Set up the identity matrix. + * + * Parameters: + * dst - Where to store the resulting affine transform + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineIdentity (double dst[6]) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineScale + * Set up a scaling matrix. + * + * Parameters: + * scale_x - X scale factor + * scale_y - Y scale factor + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineScale (double dst[6], const double scale_x, const double scale_y) +{ + dst[0] = scale_x; + dst[1] = 0; + dst[2] = 0; + dst[3] = scale_y; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineRotate + * Set up a rotation affine transform. + * + * Like the other angle in libGD, in which increasing y moves + * downward, this is a counterclockwise rotation. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Rotation angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineRotate (double dst[6], const double angle) +{ + const double sin_t = sin (angle * M_PI / 180.0); + const double cos_t = cos (angle * M_PI / 180.0); + + dst[0] = cos_t; + dst[1] = sin_t; + dst[2] = -sin_t; + dst[3] = cos_t; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineShearHorizontal + * Set up a horizontal shearing matrix || becomes \\. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Shear angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineShearHorizontal(double dst[6], const double angle) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = tan(angle * M_PI / 180.0); + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineShearVertical + * Set up a vertical shearing matrix, columns are untouched. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Shear angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineShearVertical(double dst[6], const double angle) +{ + dst[0] = 1; + dst[1] = tan(angle * M_PI / 180.0);; + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineTranslate + * Set up a translation matrix. + * + * Parameters: + * dst - Where to store the resulting affine transform + * offset_x - Horizontal translation amount + * offset_y - Vertical translation amount + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = offset_x; + dst[5] = offset_y; + return GD_TRUE; +} + +/** + * gdAffineexpansion: Find the affine's expansion factor. + * @src: The affine transformation. + * + * Finds the expansion factor, i.e. the square root of the factor + * by which the affine transform affects area. In an affine transform + * composed of scaling, rotation, shearing, and translation, returns + * the amount of scaling. + * + * GD_SUCCESS on success or GD_FAILURE + **/ +double gdAffineExpansion (const double src[6]) +{ + return sqrt (fabs (src[0] * src[3] - src[1] * src[2])); +} + +/** + * Function: gdAffineRectilinear + * Determines whether the affine transformation is axis aligned. A + * tolerance has been implemented using GD_EPSILON. + * + * Parameters: + * m - The affine transformation + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineRectilinear (const double m[6]) +{ + return ((fabs (m[1]) < GD_EPSILON && fabs (m[2]) < GD_EPSILON) || + (fabs (m[0]) < GD_EPSILON && fabs (m[3]) < GD_EPSILON)); +} + +/** + * Function: gdAffineEqual + * Determines whether two affine transformations are equal. A tolerance + * has been implemented using GD_EPSILON. + * + * Parameters: + * m1 - The first affine transformation + * m2 - The first affine transformation + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineEqual (const double m1[6], const double m2[6]) +{ + return (fabs (m1[0] - m2[0]) < GD_EPSILON && + fabs (m1[1] - m2[1]) < GD_EPSILON && + fabs (m1[2] - m2[2]) < GD_EPSILON && + fabs (m1[3] - m2[3]) < GD_EPSILON && + fabs (m1[4] - m2[4]) < GD_EPSILON && + fabs (m1[5] - m2[5]) < GD_EPSILON); +} + diff --git a/ext/gd/libgd/gd_png.c b/ext/gd/libgd/gd_png.c index bdbb7ee7d3..a012cc63b8 100644 --- a/ext/gd/libgd/gd_png.c +++ b/ext/gd/libgd/gd_png.c @@ -134,6 +134,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile) volatile int transparent = -1; volatile int palette_allocated = FALSE; + /* Make sure the signature can't match by dumb luck -- TBB */ /* GRR: isn't sizeof(infile) equal to the size of the pointer? */ memset (sig, 0, sizeof(sig)); @@ -345,6 +346,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile) open[i] = 1; } } + /* 2.0.12: Slaven Rezic: palette images are not the only images * with a simple transparent color setting. */ diff --git a/ext/gd/libgd/gd_transform.c b/ext/gd/libgd/gd_transform.c new file mode 100644 index 0000000000..9051525eec --- /dev/null +++ b/ext/gd/libgd/gd_transform.c @@ -0,0 +1,73 @@ +#include "gd.h" + +void gdImageFlipVertical(gdImagePtr im) +{ + register int x, y; + + if (im->trueColor) { + for (y = 0; y < im->sy / 2; y++) { + int *row_dst = im->tpixels[y]; + int *row_src = im->tpixels[im->sy - 1 - y]; + for (x = 0; x < im->sx; x++) { + register int p; + p = row_dst[x]; + row_dst[x] = im->tpixels[im->sy - 1 - y][x]; + row_src[x] = p; + } + } + } else { + unsigned char p; + for (y = 0; y < im->sy / 2; y++) { + for (x = 0; x < im->sx; x++) { + p = im->pixels[y][x]; + im->pixels[y][x] = im->pixels[im->sy - 1 - y][x]; + im->pixels[im->sy - 1 - y][x] = p; + } + } + } + return; +} + +void gdImageFlipHorizontal(gdImagePtr im) +{ + + int x, y; + + if (im->trueColor) { + int *px1, *px2, tmp; + + for (y = 0; y < im->sy; y++) { + px1 = im->tpixels[y]; + px2 = im->tpixels[y] + im->sx - 1; + for (x = 0; x < (im->sx >> 1); x++) { + tmp = *px1; + *px1 = *px2; + *px2 = tmp; + px1++; + px2--; + } + } + } else { + unsigned char *px1, *px2, tmp; + + for (y = 0; y < im->sy; y++) { + px1 = im->pixels[y]; + px2 = im->pixels[y] + im->sx - 1; + for (x = 0; x < (im->sx >> 1); x++) { + tmp = *px1; + *px1 = *px2; + *px2 = tmp; + px1++; + px2--; + } + } + } +} + +void gdImageFlipBoth(gdImagePtr im) +{ + gdImageFlipVertical(im); + gdImageFlipHorizontal(im); +} + + diff --git a/ext/gd/libgd/gd_webp.c b/ext/gd/libgd/gd_webp.c index 889f5f10a6..bf9ac9dd0e 100644 --- a/ext/gd/libgd/gd_webp.c +++ b/ext/gd/libgd/gd_webp.c @@ -58,11 +58,13 @@ gdImagePtr gdImageCreateFromWebpPtr (int size, void *data) return im; } +#define GD_WEBP_ALLOC_STEP (4*1024) + gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile) { int width, height, ret; - unsigned char *filedata; - unsigned char dummy[1024]; + unsigned char *filedata = NULL; + unsigned char *read, *temp; unsigned char *Y = NULL; unsigned char *U = NULL; unsigned char *V = NULL; @@ -70,16 +72,25 @@ gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile) gdImagePtr im; do { - n = gdGetBuf(dummy, 1024, infile); - size += n; - } while (n != EOF); + temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP); + if (temp) { + filedata = temp; + read = temp + size; + } else { + if (filedata) { + gdFree(filedata); + } + php_gd_error("WebP decode: realloc failed"); + return NULL; + } + + n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile); + /* differs from upstream where gdGetBuf return 0 instead of EOF */ + if (n>0 && n!=EOF) { + size += n; + } + } while (n>0 && n!=EOF); - filedata = gdMalloc(size); - if (!filedata) { - php_gd_error("WebP decode: alloc failed"); - return NULL; - } - gdGetBuf(filedata, size, infile); ret = WebPDecode(filedata, size, &Y, &U, &V, &width, &height); gdFree(filedata); if (ret != webp_success) { diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h index 45193768ea..269c315e88 100644 --- a/ext/gd/php_gd.h +++ b/ext/gd/php_gd.h @@ -22,15 +22,13 @@ #ifndef PHP_GD_H #define PHP_GD_H -#define HAVE_GDIMAGECREATEFROMPNG 1 - #if HAVE_LIBFREETYPE # ifndef ENABLE_GD_TTF # define ENABLE_GD_TTF # endif #endif -#if HAVE_LIBGD +#if defined(HAVE_LIBGD) || defined(HAVE_GD_BUNDLED) /* open_basedir and safe_mode checks */ #define PHP_GD_CHECK_OPEN_BASEDIR(filename, errormsg) \ @@ -69,10 +67,10 @@ extern zend_module_entry gd_module_entry; /* gd.c functions */ PHP_MINFO_FUNCTION(gd); PHP_MINIT_FUNCTION(gd); -#if HAVE_LIBT1 || HAVE_GD_FONTMUTEX +#if HAVE_LIBT1 PHP_MSHUTDOWN_FUNCTION(gd); #endif -#if HAVE_GD_STRINGFT +#if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_RSHUTDOWN_FUNCTION(gd); #endif @@ -104,6 +102,7 @@ PHP_FUNCTION(imagefttext); PHP_FUNCTION(imagecreatetruecolor); PHP_FUNCTION(imagetruecolortopalette); +PHP_FUNCTION(imagepalettetotruecolor); PHP_FUNCTION(imagesetthickness); PHP_FUNCTION(imagefilledellipse); PHP_FUNCTION(imagefilledarc); @@ -122,10 +121,20 @@ PHP_FUNCTION(imagegrabscreen); PHP_FUNCTION(imagerotate); +PHP_FUNCTION(imageflip); + #ifdef HAVE_GD_BUNDLED PHP_FUNCTION(imageantialias); #endif +PHP_FUNCTION(imagecrop); +PHP_FUNCTION(imagecropauto); +PHP_FUNCTION(imagescale); +PHP_FUNCTION(imageaffine); +PHP_FUNCTION(imageaffinematrixget); +PHP_FUNCTION(imageaffinematrixconcat); +PHP_FUNCTION(imagesetinterpolation); + PHP_FUNCTION(imagesetthickness); PHP_FUNCTION(imagecopymergegray); PHP_FUNCTION(imagesetbrush); @@ -142,7 +151,7 @@ PHP_FUNCTION(imagecreatefromwbmp); PHP_FUNCTION(imagecreatefromgd); PHP_FUNCTION(imagecreatefromgd2); PHP_FUNCTION(imagecreatefromgd2part); -#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +#if defined(HAVE_GD_XPM) PHP_FUNCTION(imagecreatefromxpm); #endif @@ -193,10 +202,8 @@ PHP_FUNCTION(image2wbmp); PHP_FUNCTION(imagecolormatch); -#if HAVE_GD_BUNDLED PHP_FUNCTION(imagelayereffect); PHP_FUNCTION(imagexbm); -#endif PHP_FUNCTION(imagefilter); PHP_FUNCTION(imageconvolution); diff --git a/ext/gd/tests/bug43073.phpt b/ext/gd/tests/bug43073.phpt index 4f448f2b4a..8ddd324421 100644 --- a/ext/gd/tests/bug43073.phpt +++ b/ext/gd/tests/bug43073.phpt @@ -33,19 +33,19 @@ imagepng($g, "$cwd/bug43073.png"); --CLEAN-- <?php @unlink(dirname(__FILE__) . '/bug43073.png'); ?> --EXPECTF-- -(500, 402), (610, 402), (610, 376), (500, 376) +(500, 40%d), (610, 40%d), (610, 376), (500, 376) (492, 363), (591, 322), (580, 295), (480, 336) (470, 331), (548, 254), (527, 233), (449, 310) (439, 309), (483, 202), (461, 193), (416, 299) -(401, 300), (401, 183), (381, 183), (381, 300) +(40%d, 300), (40%d, 183), (38%d, 183), (38%d, 300) (362, 307), (316, 195), (291, 205), (337, 318) (330, 329), (246, 244), (224, 265), (308, 350) (308, 360), (202, 316), (190, 344), (296, 388) -(300, 399), (186, 399), (186, 425), (300, 425) +(300, %d), (18%d, %d), (18%d, 425), (%d, 425) (306, 437), (195, 483), (206, 510), (318, 464) (328, 469), (240, 557), (260, 578), (349, 491) (359, 491), (312, 607), (334, 616), (382, 501) -(398, 500), (398, 618), (418, 618), (418, 500) +(%d, 500), (%d, 618), (41%d, 618), (41%d, 500) (436, 493), (483, 607), (507, 597), (461, 482) (468, 471), (555, 558), (577, 538), (490, 450) (490, 440), (600, 485), (611, 457), (502, 412) diff --git a/ext/gd/tests/bug48801.phpt b/ext/gd/tests/bug48801.phpt index fd25541a52..773564f782 100644 --- a/ext/gd/tests/bug48801.phpt +++ b/ext/gd/tests/bug48801.phpt @@ -20,6 +20,6 @@ echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n"; ?> --EXPECTF-- (-1, 15) -(155, 15) -(155, -48) +(15%d, 15) +(15%d, -48) (-1, -48) diff --git a/ext/gd/tests/imagecrop_auto.phpt b/ext/gd/tests/imagecrop_auto.phpt new file mode 100644 index 0000000000..1c1929d8e8 --- /dev/null +++ b/ext/gd/tests/imagecrop_auto.phpt @@ -0,0 +1,84 @@ +--TEST--
+Testing imagecropauto()
+--SKIPIF--
+<?php
+if ( ! extension_loaded('gd') || !function_exists('imagecrop')) die( 'skip GD imagecropauto not present; skipping test' );
+?>
+--FILE--
+<?php
+
+echo "TC IMG_CROP_DEFAULT\n";
+$im = imagecreatetruecolor(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_DEFAULT);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_DEFAULT\n";
+$im = imagecreate(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_DEFAULT);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "TC IMG_CROP_SIDES\n";
+$im = imagecreatetruecolor(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_SIDES);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_SIDES\n";
+$im = imagecreate(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_SIDES);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "TC IMG_CROP_BLACK\n";
+$im = imagecreatetruecolor(50, 50);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_BLACK);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_BLACK\n";
+$im = imagecreate(50, 50);
+$bgd = imagecolorallocate($im, 0, 0, 0);
+$b = imagecolorallocate($im, 0, 0, 255);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_BLACK);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "IMG_CROP_THRESHOLD\n";
+$im = imagecreatefrompng(__DIR__ . "/logo_noise.png");
+$im_crop = imagecropauto($im, IMG_CROP_THRESHOLD, 0.1, 0x0);
+imagepng($im_crop, __DIR__ . "/crop_threshold.png");
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+@unlink(__DIR__ . "/crop_threshold.png");
+?>
+--EXPECT--
+TC IMG_CROP_DEFAULT
+int(11)
+int(11)
+Palette IMG_CROP_DEFAULT
+int(11)
+int(11)
+TC IMG_CROP_SIDES
+int(11)
+int(11)
+Palette IMG_CROP_SIDES
+int(11)
+int(11)
+TC IMG_CROP_BLACK
+int(11)
+int(11)
+Palette IMG_CROP_BLACK
+int(11)
+int(11)
+IMG_CROP_THRESHOLD
+int(240)
+int(134)
diff --git a/ext/gd/tests/imageflip.phpt b/ext/gd/tests/imageflip.phpt new file mode 100644 index 0000000000..a326e0a2ba --- /dev/null +++ b/ext/gd/tests/imageflip.phpt @@ -0,0 +1,30 @@ +--TEST--
+Testing imageflip() of GD library
+--SKIPIF--
+<?php
+if ( ! extension_loaded('gd') || !function_exists('imageflip')) die( 'skip GD not present; skipping test' );
+?>
+--FILE--
+<?php
+
+$im = imagecreatetruecolor( 99, 99 );
+
+imagesetpixel($im, 0, 0, 0xFF);
+imagesetpixel($im, 0, 98, 0x00FF00);
+imagesetpixel($im, 98, 0, 0xFF0000);
+imagesetpixel($im, 98, 98, 0x0000FF);
+
+imageflip($im, IMG_FLIP_HORIZONTAL);
+imageflip($im, IMG_FLIP_VERTICAL);
+imageflip($im, IMG_FLIP_BOTH);
+
+var_dump(dechex(imagecolorat($im, 0, 0)));
+var_dump(dechex(imagecolorat($im, 0, 98)));
+var_dump(dechex(imagecolorat($im, 98, 0)));
+var_dump(dechex(imagecolorat($im, 98, 98)));
+?>
+--EXPECT--
+string(2) "ff"
+string(4) "ff00"
+string(6) "ff0000"
+string(2) "ff"
\ No newline at end of file diff --git a/ext/gd/tests/imageloadfont_invalid.phpt b/ext/gd/tests/imageloadfont_invalid.phpt index 07bf150ac8..6cf0e336b6 100644 --- a/ext/gd/tests/imageloadfont_invalid.phpt +++ b/ext/gd/tests/imageloadfont_invalid.phpt @@ -3,7 +3,6 @@ imageloadfont() function crashes --SKIPIF-- <?php if (!extension_loaded('gd')) die("skip gd extension not available\n"); - if (!GD_BUNDLED) die('skip external GD libraries always fail'); ?> --FILE-- <?php diff --git a/ext/gd/tests/logo_noise.png b/ext/gd/tests/logo_noise.png Binary files differnew file mode 100644 index 0000000000..c9bde52c30 --- /dev/null +++ b/ext/gd/tests/logo_noise.png |
