Skip to content

Commit c10268e

Browse files
committed
Working support for AVIF in imagecreatefromstring()
1 parent 02af0ff commit c10268e

File tree

3 files changed

+93
-61
lines changed

3 files changed

+93
-61
lines changed

ext/gd/gd.c

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char
145145
static gdIOCtx *create_stream_context_from_zval(zval *to_zval);
146146
static gdIOCtx *create_stream_context(php_stream *stream, int close_stream);
147147
static gdIOCtx *create_output_context(void);
148-
static int _php_image_type(char data[12]);
148+
static int _php_image_type (zend_string *data);
149149

150150
/* output streaming (formerly gd_ctx.c) */
151151
static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)());
@@ -1469,14 +1469,15 @@ static int _php_ctx_getmbi(gdIOCtx *ctx)
14691469
}
14701470
/* }}} */
14711471

1472-
/* {{{ _php_image_type */
1472+
/* {{{ _php_image_type
1473+
* Based on ext/standard/image.c
1474+
*/
14731475
static const char php_sig_gd2[3] = {'g', 'd', '2'};
14741476

1475-
static int _php_image_type (char data[12])
1477+
static int _php_image_type (zend_string *data)
14761478
{
1477-
/* Based on ext/standard/image.c */
1478-
1479-
if (data == NULL) {
1479+
if (ZSTR_LEN(data) < 12) {
1480+
/* Handle this the same way as an unknown image type. */
14801481
return -1;
14811482
}
14821483

@@ -1493,18 +1494,24 @@ static int _php_image_type (char data[12])
14931494
} else if(!memcmp(data, php_sig_riff, sizeof(php_sig_riff)) && !memcmp(data + sizeof(php_sig_riff) + sizeof(uint32_t), php_sig_webp, sizeof(php_sig_webp))) {
14941495
return PHP_GDIMG_TYPE_WEBP;
14951496
}
1496-
else {
1497-
gdIOCtx *io_ctx;
1498-
io_ctx = gdNewDynamicCtxEx(8, data, 0);
1499-
if (io_ctx) {
1500-
if (_php_ctx_getmbi(io_ctx) == 0 && _php_ctx_getmbi(io_ctx) >= 0) {
1501-
io_ctx->gd_free(io_ctx);
1502-
return PHP_GDIMG_TYPE_WBM;
1503-
} else {
1504-
io_ctx->gd_free(io_ctx);
1505-
}
1497+
1498+
php_gd_image_reader reader = { .data = data, .data_pos = 0 };
1499+
1500+
if (php_is_image_avif(&reader)) {
1501+
return PHP_GDIMG_TYPE_AVIF;
1502+
}
1503+
1504+
gdIOCtx *io_ctx;
1505+
io_ctx = gdNewDynamicCtxEx(8, data, 0);
1506+
if (io_ctx) {
1507+
if (_php_ctx_getmbi(io_ctx) == 0 && _php_ctx_getmbi(io_ctx) >= 0) {
1508+
io_ctx->gd_free(io_ctx);
1509+
return PHP_GDIMG_TYPE_WBM;
1510+
} else {
1511+
io_ctx->gd_free(io_ctx);
15061512
}
15071513
}
1514+
15081515
return -1;
15091516
}
15101517
/* }}} */
@@ -1540,21 +1547,12 @@ PHP_FUNCTION(imagecreatefromstring)
15401547
zend_string *data;
15411548
gdImagePtr im;
15421549
int imtype;
1543-
char sig[12];
15441550

15451551
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &data) == FAILURE) {
15461552
RETURN_THROWS();
15471553
}
15481554

1549-
if (ZSTR_LEN(data) < sizeof(sig)) {
1550-
/* Handle this the same way as an unknown image type. */
1551-
php_error_docref(NULL, E_WARNING, "Data is not in a recognized format");
1552-
RETURN_FALSE;
1553-
}
1554-
1555-
memcpy(sig, ZSTR_VAL(data), sizeof(sig));
1556-
1557-
imtype = _php_image_type(sig);
1555+
imtype = _php_image_type(data);
15581556

15591557
switch (imtype) {
15601558
case PHP_GDIMG_TYPE_JPG:

ext/gd/php_gd.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#ifndef PHP_GD_H
1919
#define PHP_GD_H
2020

21+
#include "zend_string.h"
22+
#include "php_streams.h"
23+
2124
#if defined(HAVE_LIBGD) || defined(HAVE_GD_BUNDLED)
2225

2326
/* open_basedir and safe_mode checks */
@@ -86,6 +89,15 @@ PHP_RSHUTDOWN_FUNCTION(gd);
8689

8790
PHP_GD_API struct gdImageStruct *php_gd_libgdimageptr_from_zval_p(zval* zp);
8891

92+
/* Struct and function to be used for testing whether an image is AVIF */
93+
typedef struct {
94+
php_stream * stream;
95+
zend_string * data;
96+
size_t data_pos;
97+
} php_gd_image_reader;
98+
99+
int php_is_image_avif(php_gd_image_reader * reader);
100+
89101
#else
90102

91103
#define phpext_gd_ptr NULL

ext/standard/image.c

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <unistd.h>
2727
#endif
2828
#include "php_image.h"
29+
#include "../gd/php_gd.h"
2930

3031
#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
3132
#include "zlib.h"
@@ -1149,9 +1150,9 @@ static struct gfxinfo *php_handle_webp(php_stream * stream)
11491150
}
11501151
/* }}} */
11511152

1152-
/* {{{ php_handle_avif */
1153-
/* There's no simple way to get this information - so, for now, this is unsupported.
1154-
Simply return 0 for everything.
1153+
/* {{{ php_handle_avif
1154+
* There's no simple way to get this information - so, for now, this is unsupported.
1155+
* Simply return 0 for everything.
11551156
*/
11561157
static struct gfxinfo *php_handle_avif(php_stream * stream) {
11571158
struct gfxinfo * result;
@@ -1178,64 +1179,82 @@ static uint32_t php_ntohl(val) {
11781179
}
11791180
/* }}} */
11801181

1182+
/* {{{ php_image_read_bytes
1183+
* A generalized function that reads bytes from an image_reader struct.
1184+
* It can read either from a php_stream or a zend_string.
1185+
*/
1186+
static size_t php_image_read_bytes(php_gd_image_reader * reader, char * buf, size_t count) {
1187+
if (reader->stream) {
1188+
return php_stream_read(reader->stream, buf, count);
1189+
}
1190+
1191+
if (reader->data) {
1192+
if (ZSTR_LEN(reader->data) < reader->data_pos + count) {
1193+
return 0;
1194+
}
1195+
1196+
memcpy(buf, ZSTR_VAL(reader->data) + reader->data_pos, count);
1197+
reader->data_pos += count;
1198+
return count;
1199+
}
1200+
1201+
return 0;
1202+
}
1203+
/* }}} */
1204+
11811205
/* {{{ php_is_image_avif
1182-
detect whether an image is of type AVIF
1183-
1184-
An AVIF image will start off a header "box".
1185-
This starts with with a four-byte integer containing the number of bytes in the filetype box.
1186-
This must be followed by the string "ftyp".
1187-
Next comes a four-byte string indicating the "major brand".
1188-
If that's "avif" or "avis", this is an AVIF image.
1189-
Next, there's a four-byte "minor version" field, which we can ignore.
1190-
Next comes an array of four-byte strings containing "compatible brands".
1191-
These extend to the end of the box.
1192-
If any of the compatible brands is "avif" or "avis", then this is an AVIF image.
1193-
Otherwise, well, it's not.
1194-
1195-
For more, see https://mpeg.chiariglione.org/standards/mpeg-4/iso-base-media-file-format/text-isoiec-14496-12-5th-edition
1196-
*/
1197-
static int php_is_image_avif(php_stream * stream) {
1206+
* detect whether an image is of type AVIF
1207+
*
1208+
* An AVIF image will start off a header "box".
1209+
* This starts with with a four-byte integer containing the number of bytes in the filetype box.
1210+
* This must be followed by the string "ftyp".
1211+
* Next comes a four-byte string indicating the "major brand".
1212+
* If that's "avif" or "avis", this is an AVIF image.
1213+
* Next, there's a four-byte "minor version" field, which we can ignore.
1214+
* Next comes an array of four-byte strings containing "compatible brands".
1215+
* These extend to the end of the box.
1216+
* If any of the compatible brands is "avif" or "avis", then this is an AVIF image.
1217+
* Otherwise, well, it's not.
1218+
* For more, see https://mpeg.chiariglione.org/standards/mpeg-4/iso-base-media-file-format/text-isoiec-14496-12-5th-edition
1219+
*/
1220+
int php_is_image_avif(php_gd_image_reader * reader) {
11981221
uint32_t header_size_reversed, header_size, i;
11991222
char box_type[4], brand[4];
12001223

1201-
if (php_stream_rewind(stream)) {
1202-
return 0;
1203-
}
1204-
1205-
if (php_stream_read(stream, (char *) &header_size_reversed, 4) != 4) {
1224+
if (php_image_read_bytes(reader, (char *) &header_size_reversed, 4) != 4) {
12061225
return 0;
12071226
}
12081227

12091228
header_size = php_ntohl(header_size_reversed);
12101229

1211-
// If the box type isn't "ftyp", it can't be an AVIF image.
1212-
if (php_stream_read(stream, box_type, 4) != 4) {
1230+
/* If the box type isn't "ftyp", it can't be an AVIF image. */
1231+
if (php_image_read_bytes(reader, box_type, 4) != 4) {
12131232
return 0;
12141233
}
12151234

12161235
if (memcmp(box_type, "ftyp", 4)) {
12171236
return 0;
12181237
}
12191238

1220-
// If the major brand is "avif" or "avis", it's an AVIF image.
1221-
if (php_stream_read(stream, brand, 4) != 4) {
1239+
/* If the major brand is "avif" or "avis", it's an AVIF image. */
1240+
if (php_image_read_bytes(reader, brand, 4) != 4) {
12221241
return 0;
12231242
}
12241243

12251244
if (!memcmp(brand, "avif", 4) || !memcmp(brand, "avis", 4)) {
12261245
return 1;
12271246
}
12281247

1229-
// Skip the next four bytes, which are the "minor version".
1230-
if (php_stream_read(stream, brand, 4) != 4) {
1248+
/* Skip the next four bytes, which are the "minor version". */
1249+
if (php_image_read_bytes(reader, brand, 4) != 4) {
12311250
return 0;
12321251
}
12331252

1234-
// Look for "avif" or "avis" in any member of compatible_brands[], to the end of the header.
1235-
// Note we've already read four groups of four bytes.
1253+
/* Look for "avif" or "avis" in any member of compatible_brands[], to the end of the header.
1254+
Note we've already read four groups of four bytes. */
12361255

12371256
for (i = 16; i < header_size; i += 4) {
1238-
if (php_stream_read(stream, brand, 4) != 4) {
1257+
if (php_image_read_bytes(reader, brand, 4) != 4) {
12391258
return 0;
12401259
}
12411260

@@ -1381,7 +1400,7 @@ PHP_FUNCTION(image_type_to_extension)
13811400

13821401
/* {{{ php_imagetype
13831402
detect filetype from first bytes */
1384-
PHPAPI int php_getimagetype(php_stream * stream, const char *input, char *filetype)
1403+
PHPAPI int php_getimagetype(php_stream * stream, const char * input, char * filetype)
13851404
{
13861405
char tmp[12];
13871406
int twelve_bytes_read;
@@ -1458,7 +1477,10 @@ PHPAPI int php_getimagetype(php_stream * stream, const char *input, char *filety
14581477
return IMAGE_FILETYPE_WBMP;
14591478
}
14601479

1461-
if (php_is_image_avif(stream)) {
1480+
php_gd_image_reader reader;
1481+
reader.stream = stream;
1482+
1483+
if (!php_stream_rewind(stream) && php_is_image_avif(&reader)) {
14621484
return IMAGE_FILETYPE_AVIF;
14631485
}
14641486

0 commit comments

Comments
 (0)