]> granicus.if.org Git - php/commitdiff
Implement #69606: Support BMPs (added in GD 2.1.0)
authorChristoph M. Becker <cmbecker69@gmx.de>
Sun, 24 Jul 2016 12:14:57 +0000 (14:14 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Wed, 4 Jan 2017 13:55:02 +0000 (14:55 +0100)
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.

19 files changed:
UPGRADING
ext/gd/config.m4
ext/gd/config.w32
ext/gd/gd.c
ext/gd/gd_ctx.c
ext/gd/libgd/bmp.h [new file with mode: 0644]
ext/gd/libgd/gd.h
ext/gd/libgd/gd_bmp.c [new file with mode: 0644]
ext/gd/libgd/gd_io.c
ext/gd/libgd/gd_io.h
ext/gd/php_gd.h
ext/gd/tests/gd_info_variation1.phpt
ext/gd/tests/imagebmp_basic.phpt [new file with mode: 0644]
ext/gd/tests/imagecreatefrombmp_basic.bmp [new file with mode: 0644]
ext/gd/tests/imagecreatefrombmp_basic.phpt [new file with mode: 0644]
ext/gd/tests/imagecreatefrombmp_basic.png [new file with mode: 0644]
ext/gd/tests/imagecreatefromstring_bmp.phpt [new file with mode: 0644]
ext/gd/tests/imagecreatefromstring_bmp.png [new file with mode: 0644]
ext/gd/tests/imagetypes_bmp.phpt [new file with mode: 0644]

index fc573565eee7b9350d0d99986018a0128212662c..58df398cb77df389f106a40607162eb3d5cc2567 100644 (file)
--- 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
index 449142841b680474e315770dfe6937008cc2d4ab..f3f736bca891feb01ae463dc424cca808cadf3e1 100644 (file)
@@ -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
index 0248a035e26abe92fa578b35a75ec78e46f0d13c..567cdd52e2a0165051b9203a0c4f8715bdf01b35 100644 (file)
@@ -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  \
index 317ce5e8486159530618230f276c9e207e8ecaba..b0fba30bfdcc5673246497aa9d97da5a31b170a4 100644 (file)
@@ -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)
index dd62c636648ea33d2f401f129f0ccc7a7c5dae6e..80156e90044bb3944fc63ba1a6712921da1f278c 100644 (file)
@@ -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 (file)
index 0000000..cecde03
--- /dev/null
@@ -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
index 613a7016b956bcb6d5f201a7f32c78ea40fc62ec..a72a08ee6ad994c389a5ba7f4bfde77b722d1ae3 100644 (file)
@@ -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 (file)
index 0000000..93a9d61
--- /dev/null
@@ -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;
+}
index f10008649bf7b4795e94c3adaf3881494f332479..9b5cc0c7ddf8e8e83d6ca16d50b170a0dc533eba 100644 (file)
@@ -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..."));
index a4d66bb349ea3b6cfc30233382080c041e1b7281..00f3a88ed6201d61da7cdc4cb6b0a2240822eb75 100644 (file)
@@ -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);
index b136d372fedb0d912cc6c11c852ff05069000544..a99f2e846c941d1e45216dc3c1a6a31057de9114 100644 (file)
@@ -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);
index 7495d26e114c7b4333280d050b52934ad7cce15b..efad42588ac0f37f731559104450f944fd7a6986 100644 (file)
@@ -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 (file)
index 0000000..3493d48
--- /dev/null
@@ -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 (file)
index 0000000..e561247
Binary files /dev/null and b/ext/gd/tests/imagecreatefrombmp_basic.bmp differ
diff --git a/ext/gd/tests/imagecreatefrombmp_basic.phpt b/ext/gd/tests/imagecreatefrombmp_basic.phpt
new file mode 100644 (file)
index 0000000..96c9070
--- /dev/null
@@ -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 (file)
index 0000000..df9d923
Binary files /dev/null and b/ext/gd/tests/imagecreatefrombmp_basic.png differ
diff --git a/ext/gd/tests/imagecreatefromstring_bmp.phpt b/ext/gd/tests/imagecreatefromstring_bmp.phpt
new file mode 100644 (file)
index 0000000..a0c8b3e
--- /dev/null
@@ -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 (file)
index 0000000..04ffc83
Binary files /dev/null and b/ext/gd/tests/imagecreatefromstring_bmp.png differ
diff --git a/ext/gd/tests/imagetypes_bmp.phpt b/ext/gd/tests/imagetypes_bmp.phpt
new file mode 100644 (file)
index 0000000..577dc1f
--- /dev/null
@@ -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)