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.
This commit is contained in:
Christoph M. Becker 2019-06-20 23:40:51 +02:00
parent 2b4fc9eb9c
commit 81fd113506
15 changed files with 512 additions and 2 deletions

3
NEWS
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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);
@ -1316,6 +1326,9 @@ PHP_MINFO_FUNCTION(gd)
#endif
#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)())

View File

@ -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);

360
ext/gd/libgd/gd_tga.c Normal file
View File

@ -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);
}
}

52
ext/gd/libgd/gd_tga.h Normal file
View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -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.

Binary file not shown.

View File

@ -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)