@@ -1149,12 +1149,102 @@ static struct gfxinfo *php_handle_webp(php_stream * stream)
1149
1149
}
1150
1150
/* }}} */
1151
1151
1152
- /* {{{ php_handle_avif }}} */
1153
- /* TODO: This is just a stub */
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.
1155
+ */
1154
1156
static struct gfxinfo * php_handle_avif (php_stream * stream ) {
1155
- return NULL ;
1157
+ struct gfxinfo * result ;
1158
+
1159
+ result = (struct gfxinfo * ) ecalloc (1 , sizeof (struct gfxinfo ));
1160
+ return result ;
1161
+ }
1162
+ /* }}} */
1163
+
1164
+ /* {{{ php_little2bigendian
1165
+ * Reverse the bytes in a little-endian uint32, returning the big-endian equivalent.
1166
+ * Adapted lovingly from libavif's read.c.
1167
+ */
1168
+ static uint32_t php_little2bigendian (val ) {
1169
+ uint8_t data [4 ];
1170
+
1171
+ memcpy (& data , & val , sizeof (data ));
1172
+ return ((uint32_t )data [3 ] << 0 ) |
1173
+ ((uint32_t )data [2 ] << 8 ) |
1174
+ ((uint32_t )data [1 ] << 16 ) |
1175
+ ((uint32_t )data [0 ] << 24 );
1156
1176
}
1177
+ /* }}} */
1178
+
1179
+ /* {{{ php_is_image_avif
1180
+ detect whether an image is of type AVIF
1181
+
1182
+ An AVIF image will start off a header "box".
1183
+ This starts with with a four-byte integer containing the number of bytes in the filetype box.
1184
+ This must be followed by the string "ftyp".
1185
+ Next comes a four-byte string indicating the "major brand".
1186
+ If that's "avif" or "avis", this is an AVIF image.
1187
+ Next, there's a four-byte "minor version" field, which we can ignore.
1188
+ Next comes an array of four-byte strings containing "compatible brands".
1189
+ These extend to the end of the box.
1190
+ If any of the compatible brands is "avif" or "avis", then this is an AVIF image.
1191
+ Otherwise, well, it's not.
1192
+
1193
+ For more, see https://mpeg.chiariglione.org/standards/mpeg-4/iso-base-media-file-format/text-isoiec-14496-12-5th-edition
1194
+ */
1195
+ static int php_is_image_avif (php_stream * stream ) {
1196
+ uint32_t header_size_reversed , header_size , i ;
1197
+ char box_type [4 ], brand [4 ];
1198
+
1199
+ if (php_stream_rewind (stream )) {
1200
+ return 0 ;
1201
+ }
1202
+
1203
+ if (php_stream_read (stream , (char * ) & header_size_reversed , 4 ) != 4 ) {
1204
+ return 0 ;
1205
+ }
1206
+
1207
+ header_size = php_little2bigendian (header_size_reversed );
1208
+
1209
+ // If the box type isn't "ftyp", it can't be an AVIF image.
1210
+ if (php_stream_read (stream , box_type , 4 ) != 4 ) {
1211
+ return 0 ;
1212
+ }
1213
+
1214
+ if (memcmp (box_type , "ftyp" , 4 )) {
1215
+ return 0 ;
1216
+ }
1217
+
1218
+ // If the major brand is "avif" or "avis", it's an AVIF image.
1219
+ if (php_stream_read (stream , brand , 4 ) != 4 ) {
1220
+ return 0 ;
1221
+ }
1222
+
1223
+ if (!memcmp (brand , "avif" , 4 ) || !memcmp (brand , "avis" , 4 )) {
1224
+ return 1 ;
1225
+ }
1226
+
1227
+ // Skip the next four bytes, which are the "minor version".
1228
+ if (php_stream_read (stream , brand , 4 ) != 4 ) {
1229
+ return 0 ;
1230
+ }
1231
+
1232
+ // Look for "avif" or "avis" in any member of compatible_brands[], to the end of the header.
1233
+ // Note we've already read four groups of four bytes.
1234
+
1235
+ for (i = 16 ; i < header_size ; i += 4 ) {
1236
+ if (php_stream_read (stream , brand , 4 ) != 4 ) {
1237
+ return 0 ;
1238
+ }
1157
1239
1240
+ if (!memcmp (brand , "avif" , 4 ) || !memcmp (brand , "avis" , 4 )) {
1241
+ return 1 ;
1242
+ }
1243
+ }
1244
+
1245
+ return 0 ;
1246
+ }
1247
+ /* }}} */
1158
1248
1159
1249
/* {{{ php_image_type_to_mime_type
1160
1250
* Convert internal image_type to mime type */
@@ -1365,13 +1455,20 @@ PHPAPI int php_getimagetype(php_stream * stream, const char *input, char *filety
1365
1455
if (php_get_wbmp (stream , NULL , 1 )) {
1366
1456
return IMAGE_FILETYPE_WBMP ;
1367
1457
}
1368
- if (!twelve_bytes_read ) {
1458
+
1459
+ if (php_is_image_avif (stream )) {
1460
+ return IMAGE_FILETYPE_AVIF ;
1461
+ }
1462
+
1463
+ if (!twelve_bytes_read ) {
1369
1464
php_error_docref (NULL , E_NOTICE , "Error reading from %s!" , input );
1370
1465
return IMAGE_FILETYPE_UNKNOWN ;
1371
- }
1466
+ }
1467
+
1372
1468
if (php_get_xbm (stream , NULL )) {
1373
1469
return IMAGE_FILETYPE_XBM ;
1374
1470
}
1471
+
1375
1472
return IMAGE_FILETYPE_UNKNOWN ;
1376
1473
}
1377
1474
/* }}} */
0 commit comments