summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPGRADING2
-rw-r--r--ext/gd/config.m45
-rw-r--r--ext/gd/config.w323
-rw-r--r--ext/gd/gd.c55
-rw-r--r--ext/gd/gd_ctx.c41
-rw-r--r--ext/gd/libgd/bmp.h112
-rw-r--r--ext/gd/libgd/gd.h9
-rw-r--r--ext/gd/libgd/gd_bmp.c1084
-rw-r--r--ext/gd/libgd/gd_io.c59
-rw-r--r--ext/gd/libgd/gd_io.h2
-rw-r--r--ext/gd/php_gd.h8
-rw-r--r--ext/gd/tests/gd_info_variation1.phpt2
-rw-r--r--ext/gd/tests/imagebmp_basic.phpt22
-rw-r--r--ext/gd/tests/imagecreatefrombmp_basic.bmpbin0 -> 17154 bytes
-rw-r--r--ext/gd/tests/imagecreatefrombmp_basic.phpt19
-rw-r--r--ext/gd/tests/imagecreatefrombmp_basic.pngbin0 -> 4000 bytes
-rw-r--r--ext/gd/tests/imagecreatefromstring_bmp.phpt28
-rw-r--r--ext/gd/tests/imagecreatefromstring_bmp.pngbin0 -> 126 bytes
-rw-r--r--ext/gd/tests/imagetypes_bmp.phpt12
19 files changed, 1446 insertions, 17 deletions
diff --git a/UPGRADING b/UPGRADING
index fc573565ee..58df398cb7 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -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().
@@ -191,6 +192,7 @@ PHP 7.2 UPGRADE NOTES
- GD:
. IMG_EFFECT_MULTIPLY
+ . IMG_BMP
- Standard:
. PASSWORD_ARGON2_DEFAULT_MEMORY_COST
diff --git a/ext/gd/config.m4 b/ext/gd/config.m4
index 449142841b..f3f736bca8 100644
--- a/ext/gd/config.m4
+++ b/ext/gd/config.m4
@@ -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 ])
])
@@ -257,7 +258,8 @@ 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)
@@ -265,6 +267,7 @@ dnl check for fabsf and floorf which are available since C99
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
diff --git a/ext/gd/config.w32 b/ext/gd/config.w32
index 0248a035e2..567cdd52e2 100644
--- a/ext/gd/config.w32
+++ b/ext/gd/config.w32
@@ -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 \
@@ -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 \
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index 317ce5e848..b0fba30bfd 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -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)
@@ -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()
@@ -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
@@ -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)
@@ -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);
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -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)())
@@ -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)
diff --git a/ext/gd/gd_ctx.c b/ext/gd/gd_ctx.c
index dd62c63664..80156e9004 100644
--- a/ext/gd/gd_ctx.c
+++ b/ext/gd/gd_ctx.c
@@ -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;
@@ -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;
@@ -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;
diff --git a/ext/gd/libgd/bmp.h b/ext/gd/libgd/bmp.h
new file mode 100644
index 0000000000..cecde0383f
--- /dev/null
+++ b/ext/gd/libgd/bmp.h
@@ -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
diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h
index 613a7016b9..a72a08ee6a 100644
--- a/ext/gd/libgd/gd.h
+++ b/ext/gd/libgd/gd.h
@@ -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();
@@ -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
diff --git a/ext/gd/libgd/gd_bmp.c b/ext/gd/libgd/gd_bmp.c
new file mode 100644
index 0000000000..93a9d61601
--- /dev/null
+++ b/ext/gd/libgd/gd_bmp.c
@@ -0,0 +1,1084 @@
+/*
+ gd_bmp.c
+
+ Bitmap format support for libgd
+
+ * Written 2007, Scott MacVicar
+ ---------------------------------------------------------------------------
+
+ Todo:
+
+ Bitfield encoding
+
+ ----------------------------------------------------------------------------
+ */
+/* $Id$ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "gd.h"
+#include "gdhelpers.h"
+#include "bmp.h"
+
+static int compress_row(unsigned char *uncompressed_row, int length);
+static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data);
+
+static int bmp_read_header(gdIOCtxPtr infile, bmp_hdr_t *hdr);
+static int bmp_read_info(gdIOCtxPtr infile, bmp_info_t *info);
+static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info);
+static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info);
+static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info);
+
+static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
+static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
+static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
+static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
+static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info);
+
+#define BMP_DEBUG(s)
+
+static int gdBMPPutWord(gdIOCtx *out, int w)
+{
+ /* Byte order is little-endian */
+ gdPutC(w & 0xFF, out);
+ gdPutC((w >> 8) & 0xFF, out);
+ return 0;
+}
+
+static int gdBMPPutInt(gdIOCtx *out, int w)
+{
+ /* Byte order is little-endian */
+ gdPutC(w & 0xFF, out);
+ gdPutC((w >> 8) & 0xFF, out);
+ gdPutC((w >> 16) & 0xFF, out);
+ gdPutC((w >> 24) & 0xFF, out);
+ return 0;
+}
+
+/*
+ Function: gdImageBmpPtr
+*/
+void * gdImageBmpPtr(gdImagePtr im, int *size, int compression)
+{
+ void *rv;
+ gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+ if (out == NULL) return NULL;
+ gdImageBmpCtx(im, out, compression);
+ rv = gdDPExtractData(out, size);
+ out->gd_free(out);
+ return rv;
+}
+
+/*
+ Function: gdImageBmp
+*/
+void gdImageBmp(gdImagePtr im, FILE *outFile, int compression)
+{
+ gdIOCtx *out = gdNewFileCtx(outFile);
+ if (out == NULL) return;
+ gdImageBmpCtx(im, out, compression);
+ out->gd_free(out);
+}
+
+/*
+ Function: gdImageBmpCtx
+*/
+void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
+{
+ int bitmap_size = 0, info_size, total_size, padding;
+ int i, row, xpos, pixel;
+ int error = 0;
+ unsigned char *uncompressed_row = NULL, *uncompressed_row_start = NULL;
+ FILE *tmpfile_for_compression = NULL;
+ gdIOCtxPtr out_original = NULL;
+
+ /* No compression if its true colour or we don't support seek */
+ if (im->trueColor) {
+ compression = 0;
+ }
+
+ if (compression == 1 && !out->seek) {
+ /* Try to create a temp file where we can seek */
+ if ((tmpfile_for_compression = tmpfile()) == NULL) {
+ compression = 0;
+ } else {
+ out_original = out;
+ if ((out = (gdIOCtxPtr)gdNewFileCtx(tmpfile_for_compression)) == NULL) {
+ out = out_original;
+ out_original = NULL;
+ compression = 0;
+ }
+ }
+ }
+
+ bitmap_size = ((im->sx * (im->trueColor ? 24 : 8)) / 8) * im->sy;
+
+ /* 40 byte Windows v3 header */
+ info_size = BMP_WINDOWS_V3;
+
+ /* data for the palette */
+ if (!im->trueColor) {
+ info_size += im->colorsTotal * 4;
+ if (compression) {
+ bitmap_size = 0;
+ }
+ }
+
+ /* bitmap header + info header + data */
+ total_size = 14 + info_size + bitmap_size;
+
+ /* write bmp header info */
+ gdPutBuf("BM", 2, out);
+ gdBMPPutInt(out, total_size);
+ gdBMPPutWord(out, 0);
+ gdBMPPutWord(out, 0);
+ gdBMPPutInt(out, 14 + info_size);
+
+ /* write Windows v3 headers */
+ gdBMPPutInt(out, BMP_WINDOWS_V3); /* header size */
+ gdBMPPutInt(out, im->sx); /* width */
+ gdBMPPutInt(out, im->sy); /* height */
+ gdBMPPutWord(out, 1); /* colour planes */
+ gdBMPPutWord(out, (im->trueColor ? 24 : 8)); /* bit count */
+ gdBMPPutInt(out, (compression ? BMP_BI_RLE8 : BMP_BI_RGB)); /* compression */
+ gdBMPPutInt(out, bitmap_size); /* image size */
+ gdBMPPutInt(out, 0); /* H resolution */
+ gdBMPPutInt(out, 0); /* V ressolution */
+ gdBMPPutInt(out, im->colorsTotal); /* colours used */
+ gdBMPPutInt(out, 0); /* important colours */
+
+ /* The line must be divisible by 4, else its padded with NULLs */
+ padding = ((int)(im->trueColor ? 3 : 1) * im->sx) % 4;
+ if (padding) {
+ padding = 4 - padding;
+ }
+
+ /* 8-bit colours */
+ if (!im->trueColor) {
+ for(i = 0; i< im->colorsTotal; ++i) {
+ Putchar(gdImageBlue(im, i), out);
+ Putchar(gdImageGreen(im, i), out);
+ Putchar(gdImageRed(im, i), out);
+ Putchar(0, out);
+ }
+
+ if (compression) {
+ /* Can potentially change this to X + ((X / 128) * 3) */
+ uncompressed_row = uncompressed_row_start = (unsigned char *) gdCalloc(gdImageSX(im) * 2, sizeof(char));
+ if (!uncompressed_row) {
+ /* malloc failed */
+ goto cleanup;
+ }
+ }
+
+ for (row = (im->sy - 1); row >= 0; row--) {
+ if (compression) {
+ memset (uncompressed_row, 0, gdImageSX(im));
+ }
+
+ for (xpos = 0; xpos < im->sx; xpos++) {
+ if (compression) {
+ *uncompressed_row++ = (unsigned char)gdImageGetPixel(im, xpos, row);
+ } else {
+ Putchar(gdImageGetPixel(im, xpos, row), out);
+ }
+ }
+
+ if (!compression) {
+ /* Add padding to make sure we have n mod 4 == 0 bytes per row */
+ for (xpos = padding; xpos > 0; --xpos) {
+ Putchar('\0', out);
+ }
+ } else {
+ int compressed_size = 0;
+ uncompressed_row = uncompressed_row_start;
+ if ((compressed_size = compress_row(uncompressed_row, gdImageSX(im))) < 0) {
+ error = 1;
+ break;
+ }
+ bitmap_size += compressed_size;
+
+
+ gdPutBuf(uncompressed_row, compressed_size, out);
+ Putchar(BMP_RLE_COMMAND, out);
+ Putchar(BMP_RLE_ENDOFLINE, out);
+ bitmap_size += 2;
+ }
+ }
+
+ if (compression && uncompressed_row) {
+ gdFree(uncompressed_row);
+ if (error != 0) {
+ goto cleanup;
+ }
+ /* Update filesize based on new values and set compression flag */
+ Putchar(BMP_RLE_COMMAND, out);
+ Putchar(BMP_RLE_ENDOFBITMAP, out);
+ bitmap_size += 2;
+
+ /* Write new total bitmap size */
+ gdSeek(out, 2);
+ gdBMPPutInt(out, total_size + bitmap_size);
+
+ /* Total length of image data */
+ gdSeek(out, 34);
+ gdBMPPutInt(out, bitmap_size);
+ }
+
+ } else {
+ for (row = (im->sy - 1); row >= 0; row--) {
+ for (xpos = 0; xpos < im->sx; xpos++) {
+ pixel = gdImageGetPixel(im, xpos, row);
+
+ Putchar(gdTrueColorGetBlue(pixel), out);
+ Putchar(gdTrueColorGetGreen(pixel), out);
+ Putchar(gdTrueColorGetRed(pixel), out);
+ }
+
+ /* Add padding to make sure we have n mod 4 == 0 bytes per row */
+ for (xpos = padding; xpos > 0; --xpos) {
+ Putchar('\0', out);
+ }
+ }
+ }
+
+
+ /* If we needed a tmpfile for compression copy it over to out_original */
+ if (tmpfile_for_compression) {
+ unsigned char* copy_buffer = NULL;
+ int buffer_size = 0;
+
+ gdSeek(out, 0);
+ copy_buffer = (unsigned char *) gdMalloc(1024 * sizeof(unsigned char));
+ if (copy_buffer == NULL) {
+ goto cleanup;
+ }
+
+ while ((buffer_size = gdGetBuf(copy_buffer, 1024, out)) != EOF) {
+ if (buffer_size == 0) {
+ break;
+ }
+ gdPutBuf(copy_buffer , buffer_size, out_original);
+ }
+ gdFree(copy_buffer);
+
+ /* Replace the temp with the original which now has data */
+ out->gd_free(out);
+ out = out_original;
+ out_original = NULL;
+ }
+
+cleanup:
+ if (tmpfile_for_compression) {
+#ifdef _WIN32
+ _rmtmp();
+#else
+ fclose(tmpfile_for_compression);
+#endif
+ tmpfile_for_compression = NULL;
+ }
+
+ if (out_original) {
+ out_original->gd_free(out_original);
+ }
+ return;
+}
+
+static int compress_row(unsigned char *row, int length)
+{
+ int rle_type = 0;
+ int compressed_length = 0;
+ int pixel = 0, compressed_run = 0, rle_compression = 0;
+ unsigned char *uncompressed_row = NULL, *uncompressed_rowp = NULL, *uncompressed_start = NULL;
+
+ uncompressed_row = (unsigned char *) gdMalloc(length);
+ if (!uncompressed_row) {
+ return -1;
+ }
+
+ memcpy(uncompressed_row, row, length);
+ uncompressed_start = uncompressed_rowp = uncompressed_row;
+
+ for (pixel = 0; pixel < length; pixel++) {
+ if (compressed_run == 0) {
+ uncompressed_row = uncompressed_rowp;
+ compressed_run++;
+ uncompressed_rowp++;
+ rle_type = BMP_RLE_TYPE_RAW;
+ continue;
+ }
+
+ if (compressed_run == 1) {
+ /* Compare next byte */
+ if (memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
+ rle_type = BMP_RLE_TYPE_RLE;
+ }
+ }
+
+ if (rle_type == BMP_RLE_TYPE_RLE) {
+ if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) != 0) {
+ /* more than what we can store in a single run or run is over due to non match, force write */
+ rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
+ row += rle_compression;
+ compressed_length += rle_compression;
+ compressed_run = 0;
+ pixel--;
+ } else {
+ compressed_run++;
+ uncompressed_rowp++;
+ }
+ } else {
+ if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
+ /* more than what we can store in a single run or run is over due to match, force write */
+ rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
+ row += rle_compression;
+ compressed_length += rle_compression;
+ compressed_run = 0;
+ pixel--;
+ } else {
+ /* add this pixel to the row */
+ compressed_run++;
+ uncompressed_rowp++;
+ }
+
+ }
+ }
+
+ if (compressed_run) {
+ if (rle_type == BMP_RLE_TYPE_RLE) {
+ compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
+ }
+ }
+
+ gdFree(uncompressed_start);
+
+ return compressed_length;
+}
+
+static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data)
+{
+ int compressed_size = 0;
+ if (length < 1 || length > 128) {
+ return 0;
+ }
+
+ /* Bitmap specific cases is that we can't have uncompressed rows of length 1 or 2 */
+ if (packet_type == BMP_RLE_TYPE_RAW && length < 3) {
+ int i = 0;
+ for (i = 0; i < length; i++) {
+ compressed_size += 2;
+ memset(row, 1, 1);
+ row++;
+
+ memcpy(row, data++, 1);
+ row++;
+ }
+ } else if (packet_type == BMP_RLE_TYPE_RLE) {
+ compressed_size = 2;
+ memset(row, length, 1);
+ row++;
+
+ memcpy(row, data, 1);
+ row++;
+ } else {
+ compressed_size = 2 + length;
+ memset(row, BMP_RLE_COMMAND, 1);
+ row++;
+
+ memset(row, length, 1);
+ row++;
+
+ memcpy(row, data, length);
+ row += length;
+
+ /* Must be an even number for an uncompressed run */
+ if (length % 2) {
+ memset(row, 0, 1);
+ row++;
+ compressed_size++;
+ }
+ }
+ return compressed_size;
+}
+
+/*
+ Function: gdImageCreateFromBmp
+*/
+gdImagePtr gdImageCreateFromBmp(FILE * inFile)
+{
+ gdImagePtr im = 0;
+ gdIOCtx *in = gdNewFileCtx(inFile);
+ if (in == NULL) return NULL;
+ im = gdImageCreateFromBmpCtx(in);
+ in->gd_free(in);
+ return im;
+}
+
+/*
+ Function: gdImageCreateFromBmpPtr
+*/
+gdImagePtr gdImageCreateFromBmpPtr(int size, void *data)
+{
+ gdImagePtr im;
+ gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
+ if (in == NULL) return NULL;
+ im = gdImageCreateFromBmpCtx(in);
+ in->gd_free(in);
+ return im;
+}
+
+/*
+ Function: gdImageCreateFromBmpCtx
+*/
+gdImagePtr gdImageCreateFromBmpCtx(gdIOCtxPtr infile)
+{
+ bmp_hdr_t *hdr;
+ bmp_info_t *info;
+ gdImagePtr im = NULL;
+ int error = 0;
+
+ if (!(hdr= (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) {
+ return NULL;
+ }
+
+ if (bmp_read_header(infile, hdr)) {
+ gdFree(hdr);
+ return NULL;
+ }
+
+ if (hdr->magic != 0x4d42) {
+ gdFree(hdr);
+ return NULL;
+ }
+
+ if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) {
+ gdFree(hdr);
+ return NULL;
+ }
+
+ if (bmp_read_info(infile, info)) {
+ gdFree(hdr);
+ gdFree(info);
+ return NULL;
+ }
+
+ BMP_DEBUG(printf("Numcolours: %d\n", info->numcolors));
+ BMP_DEBUG(printf("Width: %d\n", info->width));
+ BMP_DEBUG(printf("Height: %d\n", info->height));
+ BMP_DEBUG(printf("Planes: %d\n", info->numplanes));
+ BMP_DEBUG(printf("Depth: %d\n", info->depth));
+ BMP_DEBUG(printf("Offset: %d\n", hdr->off));
+
+ if (info->depth >= 16) {
+ im = gdImageCreateTrueColor(info->width, info->height);
+ } else {
+ im = gdImageCreate(info->width, info->height);
+ }
+
+ if (!im) {
+ gdFree(hdr);
+ gdFree(info);
+ return NULL;
+ }
+
+ switch (info->depth) {
+ case 1:
+ BMP_DEBUG(printf("1-bit image\n"));
+ error = bmp_read_1bit(im, infile, info, hdr);
+ break;
+ case 4:
+ BMP_DEBUG(printf("4-bit image\n"));
+ error = bmp_read_4bit(im, infile, info, hdr);
+ break;
+ case 8:
+ BMP_DEBUG(printf("8-bit image\n"));
+ error = bmp_read_8bit(im, infile, info, hdr);
+ break;
+ case 16:
+ case 24:
+ case 32:
+ BMP_DEBUG(printf("Direct BMP image\n"));
+ error = bmp_read_direct(im, infile, info, hdr);
+ break;
+ default:
+ BMP_DEBUG(printf("Unknown bit count\n"));
+ error = 1;
+ }
+
+ gdFree(hdr);
+ gdFree(info);
+
+ if (error) {
+ gdImageDestroy(im);
+ return NULL;
+ }
+
+ return im;
+}
+
+static int bmp_read_header(gdIOCtx *infile, bmp_hdr_t *hdr)
+{
+ if(
+ !gdGetWordLSB(&hdr->magic, infile) ||
+ !gdGetIntLSB(&hdr->size, infile) ||
+ !gdGetWordLSB(&hdr->reserved1, infile) ||
+ !gdGetWordLSB(&hdr->reserved2 , infile) ||
+ !gdGetIntLSB(&hdr->off , infile)
+ ) {
+ return 1;
+ }
+ return 0;
+}
+
+static int bmp_read_info(gdIOCtx *infile, bmp_info_t *info)
+{
+ /* read BMP length so we can work out the version */
+ if (!gdGetIntLSB(&info->len, infile)) {
+ return 1;
+ }
+
+ switch (info->len) {
+ /* For now treat Windows v4 + v5 as v3 */
+ case BMP_WINDOWS_V3:
+ case BMP_WINDOWS_V4:
+ case BMP_WINDOWS_V5:
+ BMP_DEBUG(printf("Reading Windows Header\n"));
+ if (bmp_read_windows_v3_info(infile, info)) {
+ return 1;
+ }
+ break;
+ case BMP_OS2_V1:
+ if (bmp_read_os2_v1_info(infile, info)) {
+ return 1;
+ }
+ break;
+ case BMP_OS2_V2:
+ if (bmp_read_os2_v2_info(infile, info)) {
+ return 1;
+ }
+ break;
+ default:
+ BMP_DEBUG(printf("Unhandled bitmap\n"));
+ return 1;
+ }
+ return 0;
+}
+
+static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info)
+{
+ if (
+ !gdGetIntLSB(&info->width, infile) ||
+ !gdGetIntLSB(&info->height, infile) ||
+ !gdGetWordLSB(&info->numplanes, infile) ||
+ !gdGetWordLSB(&info->depth, infile) ||
+ !gdGetIntLSB(&info->enctype, infile) ||
+ !gdGetIntLSB(&info->size, infile) ||
+ !gdGetIntLSB(&info->hres, infile) ||
+ !gdGetIntLSB(&info->vres, infile) ||
+ !gdGetIntLSB(&info->numcolors, infile) ||
+ !gdGetIntLSB(&info->mincolors, infile)
+ ) {
+ return 1;
+ }
+
+ if (info->height < 0) {
+ info->topdown = 1;
+ info->height = -info->height;
+ } else {
+ info->topdown = 0;
+ }
+
+ info->type = BMP_PALETTE_4;
+
+ if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
+ info->depth <= 0 || info->numcolors < 0 || info->mincolors < 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info)
+{
+ if (
+ !gdGetWordLSB((signed short int *)&info->width, infile) ||
+ !gdGetWordLSB((signed short int *)&info->height, infile) ||
+ !gdGetWordLSB(&info->numplanes, infile) ||
+ !gdGetWordLSB(&info->depth, infile)
+ ) {
+ return 1;
+ }
+
+ /* OS2 v1 doesn't support topdown */
+ info->topdown = 0;
+
+ info->numcolors = 1 << info->depth;
+ info->type = BMP_PALETTE_3;
+
+ if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
+ info->depth <= 0 || info->numcolors < 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info)
+{
+ char useless_bytes[24];
+ if (
+ !gdGetIntLSB(&info->width, infile) ||
+ !gdGetIntLSB(&info->height, infile) ||
+ !gdGetWordLSB(&info->numplanes, infile) ||
+ !gdGetWordLSB(&info->depth, infile) ||
+ !gdGetIntLSB(&info->enctype, infile) ||
+ !gdGetIntLSB(&info->size, infile) ||
+ !gdGetIntLSB(&info->hres, infile) ||
+ !gdGetIntLSB(&info->vres, infile) ||
+ !gdGetIntLSB(&info->numcolors, infile) ||
+ !gdGetIntLSB(&info->mincolors, infile)
+ ) {
+ return 1;
+ }
+
+ /* Lets seek the next 24 pointless bytes, we don't care too much about it */
+ if (!gdGetBuf(useless_bytes, 24, infile)) {
+ return 1;
+ }
+
+ if (info->height < 0) {
+ info->topdown = 1;
+ info->height = -info->height;
+ } else {
+ info->topdown = 0;
+ }
+
+ info->type = BMP_PALETTE_4;
+
+ if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
+ info->depth <= 0 || info->numcolors < 0 || info->mincolors < 0) {
+ return 1;
+ }
+
+
+ return 0;
+}
+
+static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
+{
+ int ypos = 0, xpos = 0, row = 0;
+ int padding = 0, alpha = 0, red = 0, green = 0, blue = 0;
+ signed short int data = 0;
+
+ switch(info->enctype) {
+ case BMP_BI_RGB:
+ /* no-op */
+ break;
+
+ case BMP_BI_BITFIELDS:
+ if (info->depth == 24) {
+ BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
+ return 1;
+ }
+ BMP_DEBUG(printf("Currently no bitfield support\n"));
+ return 1;
+ break;
+
+ case BMP_BI_RLE8:
+ if (info->depth != 8) {
+ BMP_DEBUG(printf("RLE is only valid for 8-bit images\n"));
+ return 1;
+ }
+ break;
+ case BMP_BI_RLE4:
+ if (info->depth != 4) {
+ BMP_DEBUG(printf("RLE is only valid for 4-bit images\n"));
+ return 1;
+ }
+ break;
+ case BMP_BI_JPEG:
+ case BMP_BI_PNG:
+ default:
+ BMP_DEBUG(printf("Unsupported BMP compression format\n"));
+ return 1;
+ }
+
+ /* There is a chance the data isn't until later, would be wierd but it is possible */
+ if (gdTell(infile) != header->off) {
+ /* Should make sure we don't seek past the file size */
+ gdSeek(infile, header->off);
+ }
+
+ /* The line must be divisible by 4, else its padded with NULLs */
+ padding = ((int)(info->depth / 8) * info->width) % 4;
+ if (padding) {
+ padding = 4 - padding;
+ }
+
+
+ for (ypos = 0; ypos < info->height; ++ypos) {
+ if (info->topdown) {
+ row = ypos;
+ } else {
+ row = info->height - ypos - 1;
+ }
+
+ for (xpos = 0; xpos < info->width; xpos++) {
+ if (info->depth == 16) {
+ if (!gdGetWordLSB(&data, infile)) {
+ return 1;
+ }
+ BMP_DEBUG(printf("Data: %X\n", data));
+ red = ((data & 0x7C00) >> 10) << 3;
+ green = ((data & 0x3E0) >> 5) << 3;
+ blue = (data & 0x1F) << 3;
+ BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red, green, blue));
+ } else if (info->depth == 24) {
+ if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile)) {
+ return 1;
+ }
+ } else {
+ if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile) || !gdGetByte(&alpha, infile)) {
+ return 1;
+ }
+ }
+ /*alpha = gdAlphaMax - (alpha >> 1);*/
+ gdImageSetPixel(im, xpos, row, gdTrueColor(red, green, blue));
+ }
+ for (xpos = padding; xpos > 0; --xpos) {
+ if (!gdGetByte(&red, infile)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int bmp_read_palette(gdImagePtr im, gdIOCtxPtr infile, int count, int read_four)
+{
+ int i;
+ int r, g, b, z;
+
+ for (i = 0; i < count; i++) {
+ if (
+ !gdGetByte(&b, infile) ||
+ !gdGetByte(&g, infile) ||
+ !gdGetByte(&r, infile) ||
+ (read_four && !gdGetByte(&z, infile))
+ ) {
+ return 1;
+ }
+ im->red[i] = r;
+ im->green[i] = g;
+ im->blue[i] = b;
+ im->open[i] = 1;
+ }
+ return 0;
+}
+
+static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
+{
+ int ypos = 0, xpos = 0, row = 0, index = 0;
+ int padding = 0, current_byte = 0, bit = 0;
+
+ if (info->enctype != BMP_BI_RGB) {
+ return 1;
+ }
+
+ if (!info->numcolors) {
+ info->numcolors = 2;
+ } else if (info->numcolors < 0 || info->numcolors > 2) {
+ return 1;
+ }
+
+ if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
+ return 1;
+ }
+
+ im->colorsTotal = info->numcolors;
+
+ /* There is a chance the data isn't until later, would be wierd but it is possible */
+ if (gdTell(infile) != header->off) {
+ /* Should make sure we don't seek past the file size */
+ gdSeek(infile, header->off);
+ }
+
+ /* The line must be divisible by 4, else its padded with NULLs */
+ padding = ((int)ceil(0.1 * info->width)) % 4;
+ if (padding) {
+ padding = 4 - padding;
+ }
+
+ for (ypos = 0; ypos < info->height; ++ypos) {
+ if (info->topdown) {
+ row = ypos;
+ } else {
+ row = info->height - ypos - 1;
+ }
+
+ for (xpos = 0; xpos < info->width; xpos += 8) {
+ /* Bitmaps are always aligned in bytes so we'll never overflow */
+ if (!gdGetByte(&current_byte, infile)) {
+ return 1;
+ }
+
+ for (bit = 0; bit < 8; bit++) {
+ index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
+ if (im->open[index]) {
+ im->open[index] = 0;
+ }
+ gdImageSetPixel(im, xpos + bit, row, index);
+ /* No need to read anything extra */
+ if ((xpos + bit) >= info->width) {
+ break;
+ }
+ }
+ }
+
+ for (xpos = padding; xpos > 0; --xpos) {
+ if (!gdGetByte(&index, infile)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
+{
+ int ypos = 0, xpos = 0, row = 0, index = 0;
+ int padding = 0, current_byte = 0;
+
+ if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4) {
+ return 1;
+ }
+
+ if (!info->numcolors) {
+ info->numcolors = 16;
+ } else if (info->numcolors < 0 || info->numcolors > 16) {
+ return 1;
+ }
+
+ if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
+ return 1;
+ }
+
+ im->colorsTotal = info->numcolors;
+
+ /* There is a chance the data isn't until later, would be wierd but it is possible */
+ if (gdTell(infile) != header->off) {
+ /* Should make sure we don't seek past the file size */
+ gdSeek(infile, header->off);
+ }
+
+ /* The line must be divisible by 4, else its padded with NULLs */
+ padding = ((int)ceil(0.5 * info->width)) % 4;
+ if (padding) {
+ padding = 4 - padding;
+ }
+
+ switch (info->enctype) {
+ case BMP_BI_RGB:
+ for (ypos = 0; ypos < info->height; ++ypos) {
+ if (info->topdown) {
+ row = ypos;
+ } else {
+ row = info->height - ypos - 1;
+ }
+
+ for (xpos = 0; xpos < info->width; xpos += 2) {
+ if (!gdGetByte(&current_byte, infile)) {
+ return 1;
+ }
+
+ index = (current_byte >> 4) & 0x0f;
+ if (im->open[index]) {
+ im->open[index] = 0;
+ }
+ gdImageSetPixel(im, xpos, row, index);
+
+ /* This condition may get called often, potential optimsations */
+ if (xpos >= info->width) {
+ break;
+ }
+
+ index = current_byte & 0x0f;
+ if (im->open[index]) {
+ im->open[index] = 0;
+ }
+ gdImageSetPixel(im, xpos + 1, row, index);
+ }
+
+ for (xpos = padding; xpos > 0; --xpos) {
+ if (!gdGetByte(&index, infile)) {
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case BMP_BI_RLE4:
+ if (bmp_read_rle(im, infile, info)) {
+ return 1;
+ }
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
+{
+ int ypos = 0, xpos = 0, row = 0, index = 0;
+ int padding = 0;
+
+ if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) {
+ return 1;
+ }
+
+ if (!info->numcolors) {
+ info->numcolors = 256;
+ } else if (info->numcolors < 0 || info->numcolors > 256) {
+ return 1;
+ }
+
+ if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
+ return 1;
+ }
+
+ im->colorsTotal = info->numcolors;
+
+ /* There is a chance the data isn't until later, would be wierd but it is possible */
+ if (gdTell(infile) != header->off) {
+ /* Should make sure we don't seek past the file size */
+ gdSeek(infile, header->off);
+ }
+
+ /* The line must be divisible by 4, else its padded with NULLs */
+ padding = (1 * info->width) % 4;
+ if (padding) {
+ padding = 4 - padding;
+ }
+
+ switch (info->enctype) {
+ case BMP_BI_RGB:
+ for (ypos = 0; ypos < info->height; ++ypos) {
+ if (info->topdown) {
+ row = ypos;
+ } else {
+ row = info->height - ypos - 1;
+ }
+
+ for (xpos = 0; xpos < info->width; ++xpos) {
+ if (!gdGetByte(&index, infile)) {
+ return 1;
+ }
+
+ if (im->open[index]) {
+ im->open[index] = 0;
+ }
+ gdImageSetPixel(im, xpos, row, index);
+ }
+ /* Could create a new variable, but it isn't really worth it */
+ for (xpos = padding; xpos > 0; --xpos) {
+ if (!gdGetByte(&index, infile)) {
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case BMP_BI_RLE8:
+ if (bmp_read_rle(im, infile, info)) {
+ return 1;
+ }
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info)
+{
+ int ypos = 0, xpos = 0, row = 0, index = 0;
+ int rle_length = 0, rle_data = 0;
+ int padding = 0;
+ int i = 0, j = 0;
+ int pixels_per_byte = 8 / info->depth;
+
+ for (ypos = 0; ypos < info->height && xpos <= info->width;) {
+ if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
+ return 1;
+ }
+ row = info->height - ypos - 1;
+
+ if (rle_length != BMP_RLE_COMMAND) {
+ if (im->open[rle_data]) {
+ im->open[rle_data] = 0;
+ }
+
+ for (i = 0; (i < rle_length) && (xpos < info->width);) {
+ for (j = 1; (j <= pixels_per_byte) && (xpos < info->width) && (i < rle_length); j++, xpos++, i++) {
+ index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> (8 - (j * info->depth));
+ if (im->open[index]) {
+ im->open[index] = 0;
+ }
+ gdImageSetPixel(im, xpos, row, index);
+ }
+ }
+ } else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) {
+ /* Uncompressed RLE needs to be even */
+ padding = 0;
+ for (i = 0; (i < rle_data) && (xpos < info->width); i += pixels_per_byte) {
+ int max_pixels = pixels_per_byte;
+
+ if (!gdGetByte(&index, infile)) {
+ return 1;
+ }
+ padding++;
+
+ if (rle_data - i < max_pixels) {
+ max_pixels = rle_data - i;
+ }
+
+ for (j = 1; (j <= max_pixels) && (xpos < info->width); j++, xpos++) {
+ int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1);
+ if (im->open[temp]) {
+ im->open[temp] = 0;
+ }
+ gdImageSetPixel(im, xpos, row, temp);
+ }
+ }
+
+ /* Make sure the bytes read are even */
+ if (padding % 2 && !gdGetByte(&index, infile)) {
+ return 1;
+ }
+ } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) {
+ /* Next Line */
+ xpos = 0;
+ ypos++;
+ } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA) {
+ /* Delta Record, used for bmp files that contain other data*/
+ if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
+ return 1;
+ }
+ xpos += rle_length;
+ ypos += rle_data;
+ } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) {
+ /* End of bitmap */
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/ext/gd/libgd/gd_io.c b/ext/gd/libgd/gd_io.c
index f10008649b..9b5cc0c7dd 100644
--- a/ext/gd/libgd/gd_io.c
+++ b/ext/gd/libgd/gd_io.c
@@ -95,6 +95,26 @@ int gdGetWord (int *result, gdIOCtx * ctx)
}
+int gdGetWordLSB(signed short int *result, gdIOCtx *ctx)
+{
+ int high = 0, low = 0;
+ low = (ctx->getC) (ctx);
+ if (low == EOF) {
+ return 0;
+ }
+
+ high = (ctx->getC) (ctx);
+ if (high == EOF) {
+ return 0;
+ }
+
+ if (result) {
+ *result = (high << 8) | low;
+ }
+
+ return 1;
+}
+
int gdGetInt (int *result, gdIOCtx * ctx)
{
int r;
@@ -119,6 +139,45 @@ int gdGetInt (int *result, gdIOCtx * ctx)
return 1;
}
+int gdGetIntLSB(signed int *result, gdIOCtx *ctx)
+{
+ int c = 0;
+ unsigned int r = 0;
+
+ c = (ctx->getC) (ctx);
+ if (c == EOF) {
+ return 0;
+ }
+ r |= (c << 24);
+ r >>= 8;
+
+ c = (ctx->getC) (ctx);
+ if (c == EOF) {
+ return 0;
+ }
+ r |= (c << 24);
+ r >>= 8;
+
+ c = (ctx->getC) (ctx);
+ if (c == EOF) {
+ return 0;
+ }
+ r |= (c << 24);
+ r >>= 8;
+
+ c = (ctx->getC) (ctx);
+ if (c == EOF) {
+ return 0;
+ }
+ r |= (c << 24);
+
+ if (result) {
+ *result = (signed int)r;
+ }
+
+ return 1;
+}
+
int gdPutBuf (const void *buf, int size, gdIOCtx * ctx)
{
IO_DBG (gd_error("Putting buf..."));
diff --git a/ext/gd/libgd/gd_io.h b/ext/gd/libgd/gd_io.h
index a4d66bb349..00f3a88ed6 100644
--- a/ext/gd/libgd/gd_io.h
+++ b/ext/gd/libgd/gd_io.h
@@ -36,7 +36,9 @@ int gdGetC(gdIOCtx *ctx);
int gdGetBuf(void *, int, gdIOCtx*);
int gdGetByte(int *result, gdIOCtx *ctx);
int gdGetWord(int *result, gdIOCtx *ctx);
+int gdGetWordLSB(signed short int *result, gdIOCtx *ctx);
int gdGetInt(int *result, gdIOCtx *ctx);
+int gdGetIntLSB(signed int *result, gdIOCtx *ctx);
int gdSeek(gdIOCtx *ctx, const int);
long gdTell(gdIOCtx *ctx);
diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h
index b136d372fe..a99f2e846c 100644
--- a/ext/gd/php_gd.h
+++ b/ext/gd/php_gd.h
@@ -48,6 +48,7 @@
#define PHP_GDIMG_TYPE_GD2 9
#define PHP_GDIMG_TYPE_GD2PART 10
#define PHP_GDIMG_TYPE_WEBP 11
+#define PHP_GDIMG_TYPE_BMP 12
#ifdef PHP_WIN32
# define PHP_GD_API __declspec(dllexport)
@@ -60,6 +61,7 @@
PHPAPI extern const char php_sig_gif[3];
PHPAPI extern const char php_sig_jpg[3];
PHPAPI extern const char php_sig_png[8];
+PHPAPI extern const char php_sig_bmp[2];
extern zend_module_entry gd_module_entry;
#define phpext_gd_ptr &gd_module_entry
@@ -149,6 +151,9 @@ PHP_FUNCTION(imagecreatefromwbmp);
PHP_FUNCTION(imagecreatefromgd);
PHP_FUNCTION(imagecreatefromgd2);
PHP_FUNCTION(imagecreatefromgd2part);
+#if defined(HAVE_GD_BMP)
+PHP_FUNCTION(imagecreatefrombmp);
+#endif
#if defined(HAVE_GD_XPM)
PHP_FUNCTION(imagecreatefromxpm);
#endif
@@ -169,6 +174,9 @@ PHP_FUNCTION(imagewebp);
PHP_FUNCTION(imagewbmp);
PHP_FUNCTION(imagegd);
PHP_FUNCTION(imagegd2);
+#if defined(HAVE_GD_BMP)
+PHP_FUNCTION(imagebmp);
+#endif
PHP_FUNCTION(imageinterlace);
PHP_FUNCTION(imageline);
diff --git a/ext/gd/tests/gd_info_variation1.phpt b/ext/gd/tests/gd_info_variation1.phpt
index 7495d26e11..efad42588a 100644
--- a/ext/gd/tests/gd_info_variation1.phpt
+++ b/ext/gd/tests/gd_info_variation1.phpt
@@ -44,6 +44,8 @@ array(%d) {
bool(%s)
["WebP Support"]=>
bool(%s)
+ ["BMP Support"]=>
+ bool%a
["JIS-mapped Japanese Font Support"]=>
bool(%s)
}
diff --git a/ext/gd/tests/imagebmp_basic.phpt b/ext/gd/tests/imagebmp_basic.phpt
new file mode 100644
index 0000000000..3493d48d60
--- /dev/null
+++ b/ext/gd/tests/imagebmp_basic.phpt
@@ -0,0 +1,22 @@
+--TEST--
+imagebmp() - basic functionality
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+if (!(imagetypes() & IMG_BMP)) die('skip BMP support required');
+?>
+--FILE--
+<?php
+// create an image
+$im = imagecreate(100, 100);
+imagecolorallocate($im, 0, 0, 0);
+$white = imagecolorallocate($im, 255, 255, 255);
+imageline($im, 10,10, 89,89, $white);
+
+// write the md5 hash of its BMP representation
+ob_start();
+imagebmp($im);
+echo md5(ob_get_clean());
+?>
+--EXPECT--
+d49124076771822b09fa72e168c0de56
diff --git a/ext/gd/tests/imagecreatefrombmp_basic.bmp b/ext/gd/tests/imagecreatefrombmp_basic.bmp
new file mode 100644
index 0000000000..e56124740c
--- /dev/null
+++ b/ext/gd/tests/imagecreatefrombmp_basic.bmp
Binary files differ
diff --git a/ext/gd/tests/imagecreatefrombmp_basic.phpt b/ext/gd/tests/imagecreatefrombmp_basic.phpt
new file mode 100644
index 0000000000..96c90700b8
--- /dev/null
+++ b/ext/gd/tests/imagecreatefrombmp_basic.phpt
@@ -0,0 +1,19 @@
+--TEST--
+imagecreatefrombmp() - basic functionality
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+if (!(imagetypes() & IMG_BMP)) die('skip BMP support required');
+?>
+--FILE--
+<?php
+// create an image from a BMP file
+$im = imagecreatefrombmp(__DIR__ . '/imagecreatefrombmp_basic.bmp');
+
+include_once __DIR__ . '/func.inc';
+test_image_equals_file(__DIR__ . '/imagecreatefrombmp_basic.png', $im);
+?>
+===DONE===
+--EXPECT--
+The images are equal.
+===DONE===
diff --git a/ext/gd/tests/imagecreatefrombmp_basic.png b/ext/gd/tests/imagecreatefrombmp_basic.png
new file mode 100644
index 0000000000..df9d92372e
--- /dev/null
+++ b/ext/gd/tests/imagecreatefrombmp_basic.png
Binary files differ
diff --git a/ext/gd/tests/imagecreatefromstring_bmp.phpt b/ext/gd/tests/imagecreatefromstring_bmp.phpt
new file mode 100644
index 0000000000..a0c8b3e57e
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromstring_bmp.phpt
@@ -0,0 +1,28 @@
+--TEST--
+imagecreatefromstring() - BMP format
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+if (!(imagetypes() & IMG_BMP)) die('skip BMP support required');
+?>
+--FILE--
+<?php
+// create an image from a BMP string representation
+$bmp = "\x42\x4D\x3E\x00\x00\x00\x00\x00\x00\x00\x3E\x00\x00\x00\x28\x00"
+ . "\x00\x00\x0A\x00\x00\x00\x0A\x00\x00\x00\x01\x00\x08\x00\x01\x00"
+ . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00"
+ . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\x00\x0A\x00"
+ . "\x00\x00\x0A\x00\x00\x00\x07\x00\x01\x01\x01\x00\x00\x00\x06\x00"
+ . "\x01\x01\x01\x00\x02\x00\x00\x00\x05\x00\x01\x01\x01\x00\x03\x00"
+ . "\x00\x00\x04\x00\x01\x01\x01\x00\x04\x00\x00\x00\x03\x00\x01\x01"
+ . "\x01\x00\x05\x00\x00\x00\x02\x00\x01\x01\x01\x00\x06\x00\x00\x00"
+ . "\x0A\x00\x00\x00\x0A\x00\x00\x00\x00\x01";
+$im = imagecreatefromstring($bmp);
+
+include_once __DIR__ . '/func.inc';
+test_image_equals_file(__DIR__ . '/imagecreatefromstring_bmp.png', $im);
+?>
+===DONE===
+--EXPECT--
+The images are equal.
+===DONE===
diff --git a/ext/gd/tests/imagecreatefromstring_bmp.png b/ext/gd/tests/imagecreatefromstring_bmp.png
new file mode 100644
index 0000000000..04ffc83c46
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromstring_bmp.png
Binary files differ
diff --git a/ext/gd/tests/imagetypes_bmp.phpt b/ext/gd/tests/imagetypes_bmp.phpt
new file mode 100644
index 0000000000..577dc1fc1e
--- /dev/null
+++ b/ext/gd/tests/imagetypes_bmp.phpt
@@ -0,0 +1,12 @@
+--TEST--
+imagetypes() - BMP support
+--SKIP--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+?>
+--FILE--
+<?php
+var_dump((imagetypes() & IMG_BMP) == function_exists('imagebmp'));
+?>
+--EXPECT--
+bool(true)