Skip to content

Commit

Permalink
Implement #69606: Support BMPs (added in GD 2.1.0)
Browse files Browse the repository at this point in the history
We add PHP bindings for libgd's features to read and write BMP files, which
are available as of libgd 2.1.0.

As PHP's bundled libgd doesn't yet include the respective features of the
external libgd, we add these.
  • Loading branch information
cmb69 committed Jan 4, 2017
1 parent d838285 commit 500b496
Show file tree
Hide file tree
Showing 19 changed files with 1,446 additions and 17 deletions.
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ PHP 7.2 UPGRADE NOTES
. Added imagesetclip() and imagegetclip().
. Added imageopenpolygon().
. Added imageresolution().
. Added imagecreatefrombmp() and imagebmp().

- Mbstring:
. Added mb_chr() and mb_ord().
Expand Down Expand Up @@ -191,6 +192,7 @@ PHP 7.2 UPGRADE NOTES

- GD:
. IMG_EFFECT_MULTIPLY
. IMG_BMP

- Standard:
. PASSWORD_ARGON2_DEFAULT_MEMORY_COST
Expand Down
5 changes: 4 additions & 1 deletion ext/gd/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ AC_DEFUN([PHP_GD_CHECK_VERSION],[
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, gdImageCreateFromBmp, [AC_DEFINE(HAVE_GD_BMP, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdImageStringFT, [AC_DEFINE(HAVE_GD_FREETYPE, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
PHP_CHECK_LIBRARY(gd, gdVersionString, [AC_DEFINE(HAVE_GD_LIBVERSION, 1, [ ])], [], [ $GD_SHARED_LIBADD ])
])
Expand Down Expand Up @@ -257,14 +258,16 @@ if test "$PHP_GD" = "yes"; then
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/gd_xbm.c libgd/gd_gif_out.c libgd/gd_security.c \
libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_rotate.c libgd/gd_color_match.c \
libgd/gd_transform.c libgd/gd_crop.c libgd/gd_interpolation.c libgd/gd_matrix.c"
libgd/gd_transform.c libgd/gd_crop.c libgd/gd_interpolation.c libgd/gd_matrix.c \
libgd/gd_bmp.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_GD_BUNDLED, 1, [ ])
AC_DEFINE(HAVE_GD_PNG, 1, [ ])
AC_DEFINE(HAVE_GD_BMP, 1, [ ])
AC_DEFINE(HAVE_GD_CACHE_CREATE, 1, [ ])

dnl Make sure the libgd/ is first in the include path
Expand Down
3 changes: 2 additions & 1 deletion ext/gd/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if (PHP_GD != "no") {
gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \
gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c gd_xbm.c gd_security.c gd_transform.c \
gd_filter.c gd_pixelate.c gd_rotate.c gd_color_match.c gd_webp.c \
gd_crop.c gd_interpolation.c gd_matrix.c", "gd");
gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c", "gd");
AC_DEFINE('HAVE_LIBGD', 1, 'GD support');
ADD_FLAG("CFLAGS_GD", " \
/D HAVE_GD_DYNAMIC_CTX_EX=1 \
Expand All @@ -63,6 +63,7 @@ if (PHP_GD != "no") {
/D HAVE_GD_XBM \
/D HAVE_GD_XPM \
/D HAVE_GD_FREETYPE=1 \
/D HAVE_GD_BMP \
/D HAVE_LIBGD13=1 \
/D HAVE_LIBGD15=1 \
/D HAVE_LIBGD20=1 \
Expand Down
55 changes: 55 additions & 0 deletions ext/gd/gd.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd2part, 0)
ZEND_ARG_INFO(0, height)
ZEND_END_ARG_INFO()

#if defined(HAVE_GD_BMP)
ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefrombmp, 0)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_INFO_EX(arginfo_imagexbm, 0, 0, 2)
ZEND_ARG_INFO(0, im)
ZEND_ARG_INFO(0, filename)
Expand Down Expand Up @@ -402,6 +408,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd2, 0, 0, 1)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

#if defined(HAVE_GD_BMP)
ZEND_BEGIN_ARG_INFO_EX(arginfo_imagebmp, 0, 0, 1)
ZEND_ARG_INFO(0, im)
ZEND_ARG_INFO(0, to)
ZEND_ARG_INFO(0, compressed)
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_INFO(arginfo_imagedestroy, 0)
ZEND_ARG_INFO(0, im)
ZEND_END_ARG_INFO()
Expand Down Expand Up @@ -923,6 +937,9 @@ const zend_function_entry gd_functions[] = {
PHP_FE(imagecreatefromgd, arginfo_imagecreatefromgd)
PHP_FE(imagecreatefromgd2, arginfo_imagecreatefromgd2)
PHP_FE(imagecreatefromgd2part, arginfo_imagecreatefromgd2part)
#ifdef HAVE_GD_BMP
PHP_FE(imagecreatefrombmp, arginfo_imagecreatefrombmp)
#endif
#ifdef HAVE_GD_PNG
PHP_FE(imagepng, arginfo_imagepng)
#endif
Expand All @@ -936,6 +953,9 @@ const zend_function_entry gd_functions[] = {
PHP_FE(imagewbmp, arginfo_imagewbmp)
PHP_FE(imagegd, arginfo_imagegd)
PHP_FE(imagegd2, arginfo_imagegd2)
#ifdef HAVE_GD_BMP
PHP_FE(imagebmp, arginfo_imagebmp)
#endif

PHP_FE(imagedestroy, arginfo_imagedestroy)
PHP_FE(imagegammacorrect, arginfo_imagegammacorrect)
Expand Down Expand Up @@ -1086,6 +1106,7 @@ PHP_MINIT_FUNCTION(gd)
REGISTER_LONG_CONSTANT("IMG_WBMP", 8, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_XPM", 16, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_WEBP", 32, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_BMP", 64, CONST_CS | CONST_PERSISTENT);

/* special colours for gd */
REGISTER_LONG_CONSTANT("IMG_COLOR_TILED", gdTiled, CONST_CS | CONST_PERSISTENT);
Expand Down Expand Up @@ -1341,6 +1362,11 @@ PHP_FUNCTION(gd_info)
#else
add_assoc_bool(return_value, "WebP Support", 0);
#endif
#ifdef HAVE_GD_BMP
add_assoc_bool(return_value, "BMP Support", 1);
#else
add_assoc_bool(return_value, "BMP Support", 0);
#endif
#if defined(USE_GD_JISX0208)
add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 1);
#else
Expand Down Expand Up @@ -2161,6 +2187,9 @@ PHP_FUNCTION(imagetypes)
#ifdef HAVE_GD_WEBP
ret |= 32;
#endif
#ifdef HAVE_GD_BMP
ret |= 64;
#endif

if (zend_parse_parameters_none() == FAILURE) {
return;
Expand Down Expand Up @@ -2211,6 +2240,8 @@ static int _php_image_type (char data[8])
}
} else if (!memcmp(data, php_sig_gif, 3)) {
return PHP_GDIMG_TYPE_GIF;
} else if (!memcmp(data, php_sig_bmp, sizeof(php_sig_bmp))) {
return PHP_GDIMG_TYPE_BMP;
}
else {
gdIOCtx *io_ctx;
Expand Down Expand Up @@ -2308,6 +2339,10 @@ PHP_FUNCTION(imagecreatefromstring)
im = _php_image_create_from_string(data, "GD2", gdImageCreateFromGd2Ctx);
break;

case PHP_GDIMG_TYPE_BMP:
im = _php_image_create_from_string(data, "BMP", gdImageCreateFromBmpCtx);
break;

default:
php_error_docref(NULL, E_WARNING, "Data is not in a recognized format");
RETURN_FALSE;
Expand Down Expand Up @@ -2529,6 +2564,16 @@ PHP_FUNCTION(imagecreatefromgd2part)
}
/* }}} */

#if defined(HAVE_GD_BMP)
/* {{{ proto resource imagecreatefrombmp(string filename)
Create a new image from BMP file or URL */
PHP_FUNCTION(imagecreatefrombmp)
{
_php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP", gdImageCreateFromBmp, gdImageCreateFromBmpCtx);
}
/* }}} */
#endif

/* {{{ _php_image_output
*/
static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)())
Expand Down Expand Up @@ -2750,6 +2795,16 @@ PHP_FUNCTION(imagegd2)
}
/* }}} */

#ifdef HAVE_GD_BMP
/* {{{ proto bool imagebmp(resource im [, mixed to [, bool compressed]])
Output BMP image to browser or file */
PHP_FUNCTION(imagebmp)
{
_php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP", gdImageBmpCtx);
}
/* }}} */
#endif

/* {{{ proto bool imagedestroy(resource im)
Destroy an image */
PHP_FUNCTION(imagedestroy)
Expand Down
41 changes: 26 additions & 15 deletions ext/gd/gd_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type,
char *file = NULL;
size_t file_len = 0;
zend_long quality, basefilter;
zend_bool compressed = 1;
gdImagePtr im;
int argc = ZEND_NUM_ARGS();
int q = -1, i;
Expand All @@ -98,27 +99,34 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type,
* The third (quality) parameter for Wbmp and Xbm stands for the foreground color index when called
* from imagey<type>().
*/
if (image_type == PHP_GDIMG_TYPE_XBM) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp!|ll", &imgind, &file, &file_len, &quality, &basefilter) == FAILURE) {
return;
}
} else {
/* PHP_GDIMG_TYPE_GIF
* PHP_GDIMG_TYPE_PNG
* PHP_GDIMG_TYPE_JPG
* PHP_GDIMG_TYPE_WBM
* PHP_GDIMG_TYPE_WEBP
* */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z/!ll", &imgind, &to_zval, &quality, &basefilter) == FAILURE) {
return;
}
switch (image_type) {
case PHP_GDIMG_TYPE_XBM:
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp!|ll", &imgind, &file, &file_len, &quality, &basefilter) == FAILURE) {
return;
}
break;
case PHP_GDIMG_TYPE_BMP:
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z/!b", &imgind, &to_zval, &compressed) == FAILURE) {
return;
}
break;
default:
/* PHP_GDIMG_TYPE_GIF
* PHP_GDIMG_TYPE_PNG
* PHP_GDIMG_TYPE_JPG
* PHP_GDIMG_TYPE_WBM
* PHP_GDIMG_TYPE_WEBP
* */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z/!ll", &imgind, &to_zval, &quality, &basefilter) == FAILURE) {
return;
}
}

if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(imgind), "Image", phpi_get_le_gd())) == NULL) {
RETURN_FALSE;
}

if (argc >= 3) {
if (image_type != PHP_GDIMG_TYPE_BMP && argc >= 3) {
q = quality; /* or colorindex for foreground of BW images (defaults to black) */
if (argc == 4) {
f = basefilter;
Expand Down Expand Up @@ -207,6 +215,9 @@ static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type,
(*func_p)(im, q, ctx);
}
break;
case PHP_GDIMG_TYPE_BMP:
(*func_p)(im, ctx, (int) compressed);
break;
default:
(*func_p)(im, ctx);
break;
Expand Down
112 changes: 112 additions & 0 deletions ext/gd/libgd/bmp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/* $Id$ */
#ifdef __cplusplus
extern "C" {
#endif

/*
gd_bmp.c
Bitmap format support for libgd
* Written 2007, Scott MacVicar
---------------------------------------------------------------------------
Todo:
RLE4, RLE8 and Bitfield encoding
Add full support for Windows v4 and Windows v5 header formats
----------------------------------------------------------------------------
*/

#ifndef BMP_H
#define BMP_H 1

#define BMP_PALETTE_3 1
#define BMP_PALETTE_4 2

#define BMP_WINDOWS_V3 40
#define BMP_OS2_V1 12
#define BMP_OS2_V2 64
#define BMP_WINDOWS_V4 108
#define BMP_WINDOWS_V5 124

#define BMP_BI_RGB 0
#define BMP_BI_RLE8 1
#define BMP_BI_RLE4 2
#define BMP_BI_BITFIELDS 3
#define BMP_BI_JPEG 4
#define BMP_BI_PNG 5

#define BMP_RLE_COMMAND 0
#define BMP_RLE_ENDOFLINE 0
#define BMP_RLE_ENDOFBITMAP 1
#define BMP_RLE_DELTA 2

#define BMP_RLE_TYPE_RAW 0
#define BMP_RLE_TYPE_RLE 1

/* BMP header. */
typedef struct {
/* 16 bit - header identifying the type */
signed short int magic;

/* 32bit - size of the file */
int size;

/* 16bit - these two are in the spec but "reserved" */
signed short int reserved1;
signed short int reserved2;

/* 32 bit - offset of the bitmap header from data in bytes */
signed int off;

} bmp_hdr_t;

/* BMP info. */
typedef struct {
/* 16bit - Type, ie Windows or OS/2 for the palette info */
signed short int type;
/* 32bit - The length of the bitmap information header in bytes. */
signed int len;

/* 32bit - The width of the bitmap in pixels. */
signed int width;

/* 32bit - The height of the bitmap in pixels. */
signed int height;

/* 8 bit - The bitmap data is specified in top-down order. */
signed char topdown;

/* 16 bit - The number of planes. This must be set to a value of one. */
signed short int numplanes;

/* 16 bit - The number of bits per pixel. */
signed short int depth;

/* 32bit - The type of compression used. */
signed int enctype;

/* 32bit - The size of the image in bytes. */
signed int size;

/* 32bit - The horizontal resolution in pixels/metre. */
signed int hres;

/* 32bit - The vertical resolution in pixels/metre. */
signed int vres;

/* 32bit - The number of color indices used by the bitmap. */
signed int numcolors;

/* 32bit - The number of color indices important for displaying the bitmap. */
signed int mincolors;

} bmp_info_t;

#endif

#ifdef __cplusplus
}
#endif
9 changes: 9 additions & 0 deletions ext/gd/libgd/gd.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ gdImagePtr gdImageCreateFromWebp(FILE *fd);
gdImagePtr gdImageCreateFromWebpCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromWebpPtr (int size, void *data);

gdImagePtr gdImageCreateFromBmp (FILE * inFile);
gdImagePtr gdImageCreateFromBmpPtr (int size, void *data);
gdImagePtr gdImageCreateFromBmpCtx (gdIOCtxPtr infile);

int gdJpegGetVersionInt();
const char * gdPngGetVersionString();

Expand Down Expand Up @@ -579,6 +583,11 @@ void gdImagePng(gdImagePtr im, FILE *out);
void gdImagePngCtx(gdImagePtr im, gdIOCtx *out);
void gdImageGif(gdImagePtr im, FILE *out);
void gdImageGifCtx(gdImagePtr im, gdIOCtx *out);

void * gdImageBmpPtr(gdImagePtr im, int *size, int compression);
void gdImageBmp(gdImagePtr im, FILE *outFile, int compression);
void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression);

/* 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all,
* 1 is FASTEST but produces larger files, 9 provides the best
* compression (smallest files) but takes a long time to compress, and
Expand Down
Loading

0 comments on commit 500b496

Please sign in to comment.