diff options
-rw-r--r-- | UPGRADING | 2 | ||||
-rw-r--r-- | ext/gd/config.m4 | 5 | ||||
-rw-r--r-- | ext/gd/config.w32 | 3 | ||||
-rw-r--r-- | ext/gd/gd.c | 55 | ||||
-rw-r--r-- | ext/gd/gd_ctx.c | 41 | ||||
-rw-r--r-- | ext/gd/libgd/bmp.h | 112 | ||||
-rw-r--r-- | ext/gd/libgd/gd.h | 9 | ||||
-rw-r--r-- | ext/gd/libgd/gd_bmp.c | 1084 | ||||
-rw-r--r-- | ext/gd/libgd/gd_io.c | 59 | ||||
-rw-r--r-- | ext/gd/libgd/gd_io.h | 2 | ||||
-rw-r--r-- | ext/gd/php_gd.h | 8 | ||||
-rw-r--r-- | ext/gd/tests/gd_info_variation1.phpt | 2 | ||||
-rw-r--r-- | ext/gd/tests/imagebmp_basic.phpt | 22 | ||||
-rw-r--r-- | ext/gd/tests/imagecreatefrombmp_basic.bmp | bin | 0 -> 17154 bytes | |||
-rw-r--r-- | ext/gd/tests/imagecreatefrombmp_basic.phpt | 19 | ||||
-rw-r--r-- | ext/gd/tests/imagecreatefrombmp_basic.png | bin | 0 -> 4000 bytes | |||
-rw-r--r-- | ext/gd/tests/imagecreatefromstring_bmp.phpt | 28 | ||||
-rw-r--r-- | ext/gd/tests/imagecreatefromstring_bmp.png | bin | 0 -> 126 bytes | |||
-rw-r--r-- | ext/gd/tests/imagetypes_bmp.phpt | 12 |
19 files changed, 1446 insertions, 17 deletions
@@ -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(¤t_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(¤t_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 Binary files differnew file mode 100644 index 0000000000..e56124740c --- /dev/null +++ b/ext/gd/tests/imagecreatefrombmp_basic.bmp 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 Binary files differnew file mode 100644 index 0000000000..df9d92372e --- /dev/null +++ b/ext/gd/tests/imagecreatefrombmp_basic.png 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 Binary files differnew file mode 100644 index 0000000000..04ffc83c46 --- /dev/null +++ b/ext/gd/tests/imagecreatefromstring_bmp.png 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) |