summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph M. Becker <cmbecker69@gmx.de>2019-06-20 23:40:51 +0200
committerChristoph M. Becker <cmbecker69@gmx.de>2019-06-20 23:40:51 +0200
commit81fd113506e4c5833e64998651f232734ebb2cb7 (patch)
treed92370413e673848b7fc774ecd0454fdf0e71966
parent2b4fc9eb9c0e0b607dfb0bf779936507be1ad58b (diff)
downloadphp-git-81fd113506e4c5833e64998651f232734ebb2cb7.tar.gz
Support TGA reading
We add PHP bindings for libgd's features to read TGA 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. Since TGA has no easily recognizable file signature, we don't add TGA support for imagecreatefromstring() or getimagesize() and friends.
-rw-r--r--NEWS3
-rw-r--r--UPGRADING6
-rw-r--r--ext/gd/config.m44
-rw-r--r--ext/gd/config.w323
-rw-r--r--ext/gd/gd.c31
-rw-r--r--ext/gd/libgd/gd.h4
-rw-r--r--ext/gd/libgd/gd_tga.c360
-rw-r--r--ext/gd/libgd/gd_tga.h52
-rw-r--r--ext/gd/php_gd.h5
-rw-r--r--ext/gd/tests/imagecreatefromtga.pngbin0 -> 2559 bytes
-rw-r--r--ext/gd/tests/imagecreatefromtga_basic.phpt17
-rw-r--r--ext/gd/tests/imagecreatefromtga_basic.tgabin0 -> 90444 bytes
-rw-r--r--ext/gd/tests/imagecreatefromtga_variation.phpt17
-rw-r--r--ext/gd/tests/imagecreatefromtga_variation.tgabin0 -> 9987 bytes
-rw-r--r--ext/gd/tests/imagetypes_tga.phpt12
15 files changed, 512 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 50402e8411..ef7507d088 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,9 @@ PHP NEWS
- Date:
. Fixed #69044 (discrepency between time and microtime). (krakjoe)
+- GD:
+ . Added TGA read support. (cmb)
+
- Opcache:
. Fixed bug #78106 (Path resolution fails if opcache disabled during request).
(Nikita)
diff --git a/UPGRADING b/UPGRADING
index 4acfa3e775..be14f0699b 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -354,6 +354,12 @@ PHP 7.4 UPGRADE NOTES
exception that it ignores overloaded array casts, such as used by
ArrayObject.
+- GD:
+ . Added imagecreatefromtga() function, which allows to read images in TGA
+ format. TGA support is now also indicated by gd_info() and imagetypes().
+ Note that TGA images are not recognized by imagecreatefromstring() and
+ getimagesize().
+
- OpenSSL:
. Added openssl_x509_verify(mixed cert, mixed key) function that verifies the
signature of the certificate using a public key. A wrapper around the
diff --git a/ext/gd/config.m4 b/ext/gd/config.m4
index a13313c8e8..742ed412cd 100644
--- a/ext/gd/config.m4
+++ b/ext/gd/config.m4
@@ -126,6 +126,7 @@ AC_DEFUN([PHP_GD_CHECK_VERSION],[
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, gdImageCreateFromTga, [AC_DEFINE(HAVE_GD_TGA, 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 ])
])
@@ -146,7 +147,7 @@ if test "$PHP_GD" != "no"; then
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_bmp.c"
+ libgd/gd_bmp.c libgd/gd_tga.c"
dnl check for fabsf and floorf which are available since C99
AC_CHECK_FUNCS(fabsf floorf)
@@ -155,6 +156,7 @@ 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_TGA, 1, [ ])
dnl Various checks for GD features
PHP_GD_ZLIB
diff --git a/ext/gd/config.w32 b/ext/gd/config.w32
index bd73f572d3..6695976d0e 100644
--- a/ext/gd/config.w32
+++ b/ext/gd/config.w32
@@ -40,7 +40,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_bmp.c", "gd");
+ gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c gd_tga.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_XPM \
/D HAVE_GD_FREETYPE=1 \
/D HAVE_GD_BMP \
+/D HAVE_GD_TGA \
/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 1bdcba71fb..96c0e23edf 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -356,6 +356,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefrombmp, 0)
ZEND_END_ARG_INFO()
#endif
+#if defined(HAVE_GD_TGA)
+ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromtga, 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)
@@ -942,6 +948,9 @@ static const zend_function_entry gd_functions[] = {
#ifdef HAVE_GD_BMP
PHP_FE(imagecreatefrombmp, arginfo_imagecreatefrombmp)
#endif
+#ifdef HAVE_GD_TGA
+ PHP_FE(imagecreatefromtga, arginfo_imagecreatefromtga)
+#endif
#ifdef HAVE_GD_PNG
PHP_FE(imagepng, arginfo_imagepng)
#endif
@@ -1109,6 +1118,7 @@ PHP_MINIT_FUNCTION(gd)
REGISTER_LONG_CONSTANT("IMG_XPM", PHP_IMG_XPM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_WEBP", PHP_IMG_WEBP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_BMP", PHP_IMG_BMP, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_TGA", PHP_IMG_TGA, CONST_CS | CONST_PERSISTENT);
/* special colours for gd */
REGISTER_LONG_CONSTANT("IMG_COLOR_TILED", gdTiled, CONST_CS | CONST_PERSISTENT);
@@ -1317,6 +1327,9 @@ PHP_MINFO_FUNCTION(gd)
#ifdef HAVE_GD_BMP
php_info_print_table_row(2, "BMP Support", "enabled");
#endif
+#ifdef HAVE_GD_TGA
+ php_info_print_table_row(2, "TGA Read Support", "enabled");
+#endif
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
@@ -1373,6 +1386,11 @@ PHP_FUNCTION(gd_info)
#else
add_assoc_bool(return_value, "BMP Support", 0);
#endif
+#ifdef HAVE_GD_TGA
+ add_assoc_bool(return_value, "TGA Read Support", 1);
+#else
+ add_assoc_bool(return_value, "TGA Read Support", 0);
+#endif
#if defined(USE_GD_JISX0208)
add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 1);
#else
@@ -2203,6 +2221,9 @@ PHP_FUNCTION(imagetypes)
#ifdef HAVE_GD_BMP
ret |= PHP_IMG_BMP;
#endif
+#ifdef HAVE_GD_TGA
+ ret |= PHP_IMG_TGA;
+#endif
if (zend_parse_parameters_none() == FAILURE) {
return;
@@ -2599,6 +2620,16 @@ PHP_FUNCTION(imagecreatefrombmp)
/* }}} */
#endif
+#if defined(HAVE_GD_TGA)
+/* {{{ proto resource imagecreatefromtga(string filename)
+ Create a new image from TGA file or URL */
+PHP_FUNCTION(imagecreatefromtga)
+{
+ _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_TGA, "TGA", gdImageCreateFromTga, gdImageCreateFromTgaCtx);
+}
+/* }}} */
+#endif
+
/* {{{ _php_image_output
*/
static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)())
diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h
index 30f59b38f6..461024f0f7 100644
--- a/ext/gd/libgd/gd.h
+++ b/ext/gd/libgd/gd.h
@@ -364,6 +364,10 @@ gdImagePtr gdImageCreateFromWebp(FILE *fd);
gdImagePtr gdImageCreateFromWebpCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromWebpPtr (int size, void *data);
+gdImagePtr gdImageCreateFromTga( FILE * fp );
+gdImagePtr gdImageCreateFromTgaCtx(gdIOCtx* ctx);
+gdImagePtr gdImageCreateFromTgaPtr(int size, void *data);
+
gdImagePtr gdImageCreateFromBmp (FILE * inFile);
gdImagePtr gdImageCreateFromBmpPtr (int size, void *data);
gdImagePtr gdImageCreateFromBmpCtx (gdIOCtxPtr infile);
diff --git a/ext/gd/libgd/gd_tga.c b/ext/gd/libgd/gd_tga.c
new file mode 100644
index 0000000000..67e77f7b98
--- /dev/null
+++ b/ext/gd/libgd/gd_tga.c
@@ -0,0 +1,360 @@
+/**
+ * File: TGA Input
+ *
+ * Read TGA images.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gd_tga.h"
+#include "gd.h"
+#include "gd_errors.h"
+#include "gdhelpers.h"
+
+/*
+ Function: gdImageCreateFromTga
+
+ Creates a gdImage from a TGA file
+
+ Parameters:
+
+ infile - Pointer to TGA binary file
+ */
+gdImagePtr gdImageCreateFromTga(FILE *fp)
+{
+ gdImagePtr image;
+ gdIOCtx* in = gdNewFileCtx(fp);
+ if (in == NULL) return NULL;
+ image = gdImageCreateFromTgaCtx(in);
+ in->gd_free( in );
+ return image;
+}
+
+/*
+ Function: gdImageCreateFromTgaPtr
+*/
+gdImagePtr gdImageCreateFromTgaPtr(int size, void *data)
+{
+ gdImagePtr im;
+ gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
+ if (in == NULL) return NULL;
+ im = gdImageCreateFromTgaCtx(in);
+ in->gd_free(in);
+ return im;
+}
+
+
+/*
+ Function: gdImageCreateFromTgaCtx
+
+ Creates a gdImage from a gdIOCtx referencing a TGA binary file.
+
+ Parameters:
+ ctx - Pointer to a gdIOCtx structure
+ */
+gdImagePtr gdImageCreateFromTgaCtx(gdIOCtx* ctx)
+{
+ int bitmap_caret = 0;
+ oTga *tga = NULL;
+ /* int pixel_block_size = 0;
+ int image_block_size = 0; */
+ volatile gdImagePtr image = NULL;
+ int x = 0;
+ int y = 0;
+
+ tga = (oTga *) gdMalloc(sizeof(oTga));
+ if (!tga) {
+ return NULL;
+ }
+
+ tga->bitmap = NULL;
+ tga->ident = NULL;
+
+ if (read_header_tga(ctx, tga) < 0) {
+ free_tga(tga);
+ return NULL;
+ }
+
+ /*TODO: Will this be used?
+ pixel_block_size = tga->bits / 8;
+ image_block_size = (tga->width * tga->height) * pixel_block_size;
+ */
+
+ if (read_image_tga(ctx, tga) < 0) {
+ free_tga(tga);
+ return NULL;
+ }
+
+ image = gdImageCreateTrueColor((int)tga->width, (int)tga->height );
+
+ if (image == 0) {
+ free_tga( tga );
+ return NULL;
+ }
+
+ /*! \brief Populate GD image object
+ * Copy the pixel data from our tga bitmap buffer into the GD image
+ * Disable blending and save the alpha channel per default
+ */
+ if (tga->alphabits) {
+ gdImageAlphaBlending(image, 0);
+ gdImageSaveAlpha(image, 1);
+ }
+
+ /* TODO: use alphabits as soon as we support 24bit and other alpha bps (ie != 8bits) */
+ for (y = 0; y < tga->height; y++) {
+ register int *tpix = image->tpixels[y];
+ for ( x = 0; x < tga->width; x++, tpix++) {
+ if (tga->bits == TGA_BPP_24) {
+ *tpix = gdTrueColor(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret]);
+ bitmap_caret += 3;
+ } else if (tga->bits == TGA_BPP_32 && tga->alphabits) {
+ register int a = tga->bitmap[bitmap_caret + 3];
+
+ *tpix = gdTrueColorAlpha(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret], gdAlphaMax - (a >> 1));
+ bitmap_caret += 4;
+ }
+ }
+ }
+
+ if (tga->flipv && tga->fliph) {
+ gdImageFlipBoth(image);
+ } else if (tga->flipv) {
+ gdImageFlipVertical(image);
+ } else if (tga->fliph) {
+ gdImageFlipHorizontal(image);
+ }
+
+ free_tga(tga);
+
+ return image;
+}
+
+/*! \brief Reads a TGA header.
+ * Reads the header block from a binary TGA file populating the referenced TGA structure.
+ * \param ctx Pointer to TGA binary file
+ * \param tga Pointer to TGA structure
+ * \return int 1 on sucess, -1 on failure
+ */
+int read_header_tga(gdIOCtx *ctx, oTga *tga)
+{
+
+ unsigned char header[18];
+
+ if (gdGetBuf(header, sizeof(header), ctx) < 18) {
+ gd_error("fail to read header");
+ return -1;
+ }
+
+ tga->identsize = header[0];
+ tga->colormaptype = header[1];
+ tga->imagetype = header[2];
+ tga->colormapstart = header[3] + (header[4] << 8);
+ tga->colormaplength = header[5] + (header[6] << 8);
+ tga->colormapbits = header[7];
+ tga->xstart = header[8] + (header[9] << 8);
+ tga->ystart = header[10] + (header[11] << 8);
+ tga->width = header[12] + (header[13] << 8);
+ tga->height = header[14] + (header[15] << 8);
+ tga->bits = header[16];
+ tga->alphabits = header[17] & 0x0f;
+ tga->fliph = (header[17] & 0x10) ? 1 : 0;
+ tga->flipv = (header[17] & 0x20) ? 0 : 1;
+
+#if DEBUG
+ printf("format bps: %i\n", tga->bits);
+ printf("flip h/v: %i / %i\n", tga->fliph, tga->flipv);
+ printf("alpha: %i\n", tga->alphabits);
+ printf("wxh: %i %i\n", tga->width, tga->height);
+#endif
+
+ if (!((tga->bits == TGA_BPP_24 && tga->alphabits == 0)
+ || (tga->bits == TGA_BPP_32 && tga->alphabits == 8)))
+ {
+ gd_error_ex(GD_WARNING, "gd-tga: %u bits per pixel with %u alpha bits not supported\n",
+ tga->bits, tga->alphabits);
+ return -1;
+ }
+
+ tga->ident = NULL;
+
+ if (tga->identsize > 0) {
+ tga->ident = (char *) gdMalloc(tga->identsize * sizeof(char));
+ if(tga->ident == NULL) {
+ return -1;
+ }
+
+ gdGetBuf(tga->ident, tga->identsize, ctx);
+ }
+
+ return 1;
+}
+
+/*! \brief Reads a TGA image data into buffer.
+ * Reads the image data block from a binary TGA file populating the referenced TGA structure.
+ * \param ctx Pointer to TGA binary file
+ * \param tga Pointer to TGA structure
+ * \return int 0 on sucess, -1 on failure
+ */
+int read_image_tga( gdIOCtx *ctx, oTga *tga )
+{
+ int pixel_block_size = (tga->bits / 8);
+ int image_block_size;
+ int* decompression_buffer = NULL;
+ unsigned char* conversion_buffer = NULL;
+ int buffer_caret = 0;
+ int bitmap_caret = 0;
+ int i = 0;
+ int encoded_pixels;
+ int rle_size;
+
+ if(overflow2(tga->width, tga->height)) {
+ return -1;
+ }
+
+ if(overflow2(tga->width * tga->height, pixel_block_size)) {
+ return -1;
+ }
+
+ image_block_size = (tga->width * tga->height) * pixel_block_size;
+ if(overflow2(image_block_size, sizeof(int))) {
+ return -1;
+ }
+
+ /*! \todo Add more image type support.
+ */
+ if (tga->imagetype != TGA_TYPE_RGB && tga->imagetype != TGA_TYPE_RGB_RLE)
+ return -1;
+
+ /*! \brief Allocate memmory for image block
+ * Allocate a chunk of memory for the image block to be passed into.
+ */
+ tga->bitmap = (int *) gdMalloc(image_block_size * sizeof(int));
+ if (tga->bitmap == NULL)
+ return -1;
+
+ switch (tga->imagetype) {
+ case TGA_TYPE_RGB:
+ /*! \brief Read in uncompressed RGB TGA
+ * Chunk load the pixel data from an uncompressed RGB type TGA.
+ */
+ conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char));
+ if (conversion_buffer == NULL) {
+ return -1;
+ }
+
+ if (gdGetBuf(conversion_buffer, image_block_size, ctx) != image_block_size) {
+ gd_error("gd-tga: premature end of image data\n");
+ gdFree(conversion_buffer);
+ return -1;
+ }
+
+ while (buffer_caret < image_block_size) {
+ tga->bitmap[buffer_caret] = (int) conversion_buffer[buffer_caret];
+ buffer_caret++;
+ }
+
+ gdFree(conversion_buffer);
+ break;
+
+ case TGA_TYPE_RGB_RLE:
+ /*! \brief Read in RLE compressed RGB TGA
+ * Chunk load the pixel data from an RLE compressed RGB type TGA.
+ */
+ decompression_buffer = (int*) gdMalloc(image_block_size * sizeof(int));
+ if (decompression_buffer == NULL) {
+ return -1;
+ }
+ conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char));
+ if (conversion_buffer == NULL) {
+ gd_error("gd-tga: premature end of image data\n");
+ gdFree( decompression_buffer );
+ return -1;
+ }
+
+ rle_size = gdGetBuf(conversion_buffer, image_block_size, ctx);
+ if (rle_size <= 0) {
+ gdFree(conversion_buffer);
+ gdFree(decompression_buffer);
+ return -1;
+ }
+
+ buffer_caret = 0;
+
+ while( buffer_caret < rle_size) {
+ decompression_buffer[buffer_caret] = (int)conversion_buffer[buffer_caret];
+ buffer_caret++;
+ }
+
+ buffer_caret = 0;
+
+ while( bitmap_caret < image_block_size ) {
+
+ if (buffer_caret + pixel_block_size > rle_size) {
+ gdFree( decompression_buffer );
+ gdFree( conversion_buffer );
+ return -1;
+ }
+
+ if ((decompression_buffer[buffer_caret] & TGA_RLE_FLAG) == TGA_RLE_FLAG) {
+ encoded_pixels = ( ( decompression_buffer[ buffer_caret ] & ~TGA_RLE_FLAG ) + 1 );
+ buffer_caret++;
+
+ if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size
+ || buffer_caret + pixel_block_size > rle_size) {
+ gdFree( decompression_buffer );
+ gdFree( conversion_buffer );
+ return -1;
+ }
+
+ for (i = 0; i < encoded_pixels; i++) {
+ memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, pixel_block_size * sizeof(int));
+ bitmap_caret += pixel_block_size;
+ }
+ buffer_caret += pixel_block_size;
+
+ } else {
+ encoded_pixels = decompression_buffer[ buffer_caret ] + 1;
+ buffer_caret++;
+
+ if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size
+ || buffer_caret + (encoded_pixels * pixel_block_size) > rle_size) {
+ gdFree( decompression_buffer );
+ gdFree( conversion_buffer );
+ return -1;
+ }
+
+ memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, encoded_pixels * pixel_block_size * sizeof(int));
+ bitmap_caret += (encoded_pixels * pixel_block_size);
+ buffer_caret += (encoded_pixels * pixel_block_size);
+ }
+ }
+ gdFree( decompression_buffer );
+ gdFree( conversion_buffer );
+ break;
+ }
+
+ return 1;
+}
+
+/*! \brief Cleans up a TGA structure.
+ * Dereferences the bitmap referenced in a TGA structure, then the structure itself
+ * \param tga Pointer to TGA structure
+ */
+void free_tga(oTga * tga)
+{
+ if (tga) {
+ if (tga->ident)
+ gdFree(tga->ident);
+ if (tga->bitmap)
+ gdFree(tga->bitmap);
+ gdFree(tga);
+ }
+}
diff --git a/ext/gd/libgd/gd_tga.h b/ext/gd/libgd/gd_tga.h
new file mode 100644
index 0000000000..297f3dc99d
--- /dev/null
+++ b/ext/gd/libgd/gd_tga.h
@@ -0,0 +1,52 @@
+#ifndef __TGA_H
+#define __TGA_H 1
+
+#include "gd.h"
+#include "gdhelpers.h"
+
+#include "gd_intern.h"
+
+typedef struct oTga_ {
+ uint8_t identsize; // size of ID field that follows 18 uint8_t header (0 usually)
+ uint8_t colormaptype; // type of colour map 0=none, 1=has palette [IGNORED] Adrian requested no support
+ uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed
+
+ int colormapstart; // first colour map entry in palette [IGNORED] Adrian requested no support
+ int colormaplength; // number of colours in palette [IGNORED] Adrian requested no support
+ uint8_t colormapbits; // number of bits per palette entry 15,16,24,32 [IGNORED] Adrian requested no support
+
+ int xstart; // image x origin
+ int ystart; // image y origin
+ int width; // image width in pixels
+ int height; // image height in pixels
+ uint8_t bits; // image bits per pixel 8,16,24,32
+ uint8_t alphabits; // alpha bits (low 4bits of header 17)
+ uint8_t fliph; // horizontal or vertical
+ uint8_t flipv; // flip
+ char *ident; // identifcation tag string
+ int *bitmap; // bitmap data
+
+} oTga;
+
+#define TGA_TYPE_NO_IMAGE 0
+#define TGA_TYPE_INDEXED 1
+#define TGA_TYPE_RGB 2
+#define TGA_TYPE_GREYSCALE 3
+#define TGA_TYPE_INDEXED_RLE 9
+#define TGA_TYPE_RGB_RLE 10
+#define TGA_TYPE_GREYSCALE_RLE 11
+#define TGA_TYPE_INDEXED_HUFFMAN_DELTA_RLE 32
+#define TGA_TYPE_RGB_HUFFMAN_DELTA_QUADTREE_RLE 33
+
+#define TGA_BPP_8 8
+#define TGA_BPP_16 16
+#define TGA_BPP_24 24
+#define TGA_BPP_32 32
+
+#define TGA_RLE_FLAG 128
+
+int read_header_tga(gdIOCtx *ctx, oTga *tga);
+int read_image_tga(gdIOCtx *ctx, oTga *tga);
+void free_tga(oTga *tga);
+
+#endif //__TGA_H
diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h
index 982decd064..e6db0c0ec0 100644
--- a/ext/gd/php_gd.h
+++ b/ext/gd/php_gd.h
@@ -47,6 +47,7 @@
#define PHP_GDIMG_TYPE_GD2PART 10
#define PHP_GDIMG_TYPE_WEBP 11
#define PHP_GDIMG_TYPE_BMP 12
+#define PHP_GDIMG_TYPE_TGA 13
#define PHP_IMG_GIF 1
#define PHP_IMG_JPG 2
@@ -56,6 +57,7 @@
#define PHP_IMG_XPM 16
#define PHP_IMG_WEBP 32
#define PHP_IMG_BMP 64
+#define PHP_IMG_TGA 128
#ifdef PHP_WIN32
# define PHP_GD_API __declspec(dllexport)
@@ -163,6 +165,9 @@ PHP_FUNCTION(imagecreatefromgd2part);
#if defined(HAVE_GD_BMP)
PHP_FUNCTION(imagecreatefrombmp);
#endif
+#if defined(HAVE_GD_TGA)
+PHP_FUNCTION(imagecreatefromtga);
+#endif
#if defined(HAVE_GD_XPM)
PHP_FUNCTION(imagecreatefromxpm);
#endif
diff --git a/ext/gd/tests/imagecreatefromtga.png b/ext/gd/tests/imagecreatefromtga.png
new file mode 100644
index 0000000000..4a3060d974
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromtga.png
Binary files differ
diff --git a/ext/gd/tests/imagecreatefromtga_basic.phpt b/ext/gd/tests/imagecreatefromtga_basic.phpt
new file mode 100644
index 0000000000..3025e522d5
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromtga_basic.phpt
@@ -0,0 +1,17 @@
+--TEST--
+imagecreatefromtga() - basic functionality
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+if (!(imagetypes() & IMG_TGA)) die('skip TGA support required');
+?>
+--FILE--
+<?php
+// create an image from a TGA file
+$im = imagecreatefromtga(__DIR__ . '/imagecreatefromtga_basic.tga');
+
+include_once __DIR__ . '/func.inc';
+test_image_equals_file(__DIR__ . '/imagecreatefromtga.png', $im);
+?>
+--EXPECT--
+The images are equal.
diff --git a/ext/gd/tests/imagecreatefromtga_basic.tga b/ext/gd/tests/imagecreatefromtga_basic.tga
new file mode 100644
index 0000000000..5f11f5a080
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromtga_basic.tga
Binary files differ
diff --git a/ext/gd/tests/imagecreatefromtga_variation.phpt b/ext/gd/tests/imagecreatefromtga_variation.phpt
new file mode 100644
index 0000000000..39dd4b0b59
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromtga_variation.phpt
@@ -0,0 +1,17 @@
+--TEST--
+imagecreatefromtga() - RLE file reading
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+if (!(imagetypes() & IMG_TGA)) die('skip TGA support required');
+?>
+--FILE--
+<?php
+// create an image from a TGA file
+$im = imagecreatefromtga(__DIR__ . '/imagecreatefromtga_variation.tga');
+
+include_once __DIR__ . '/func.inc';
+test_image_equals_file(__DIR__ . '/imagecreatefromtga.png', $im);
+?>
+--EXPECT--
+The images are equal.
diff --git a/ext/gd/tests/imagecreatefromtga_variation.tga b/ext/gd/tests/imagecreatefromtga_variation.tga
new file mode 100644
index 0000000000..ce845ad0c3
--- /dev/null
+++ b/ext/gd/tests/imagecreatefromtga_variation.tga
Binary files differ
diff --git a/ext/gd/tests/imagetypes_tga.phpt b/ext/gd/tests/imagetypes_tga.phpt
new file mode 100644
index 0000000000..56d6b3c2ed
--- /dev/null
+++ b/ext/gd/tests/imagetypes_tga.phpt
@@ -0,0 +1,12 @@
+--TEST--
+imagetypes() - TGA support
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip ext/gd required');
+?>
+--FILE--
+<?php
+var_dump((imagetypes() & IMG_TGA) == function_exists('imagecreatefromtga'));
+?>
+--EXPECT--
+bool(true)