|
22 | 22 | #endif
|
23 | 23 | #include "fopen_wrappers.h"
|
24 | 24 | #include "ext/standard/fsock.h"
|
| 25 | +#include "libavifinfo/avifinfo.h" |
25 | 26 | #if HAVE_UNISTD_H
|
26 | 27 | #include <unistd.h>
|
27 | 28 | #endif
|
@@ -1155,95 +1156,76 @@ static struct gfxinfo *php_handle_webp(php_stream * stream)
|
1155 | 1156 | }
|
1156 | 1157 | /* }}} */
|
1157 | 1158 |
|
1158 |
| -/* {{{ php_handle_avif |
1159 |
| - * There's no simple way to get this information - so, for now, this is unsupported. |
1160 |
| - * Simply return 0 for everything. |
1161 |
| - */ |
1162 |
| -static struct gfxinfo *php_handle_avif(php_stream * stream) { |
1163 |
| - return ecalloc(1, sizeof(struct gfxinfo)); |
1164 |
| -} |
1165 |
| -/* }}} */ |
1166 |
| - |
1167 |
| -/* {{{ php_ntohl |
1168 |
| - * Convert a big-endian network uint32 to host order - |
1169 |
| - * which may be either little-endian or big-endian. |
1170 |
| - * Thanks to Rob Pike via Joe Drago: |
1171 |
| - * https://commandcenter.blogspot.nl/2012/04/byte-order-fallacy.html |
1172 |
| - */ |
1173 |
| -static uint32_t php_ntohl(uint32_t val) { |
1174 |
| - uint8_t data[4]; |
1175 |
| - |
1176 |
| - memcpy(&data, &val, sizeof(data)); |
1177 |
| - return ((uint32_t)data[3] << 0) | |
1178 |
| - ((uint32_t)data[2] << 8) | |
1179 |
| - ((uint32_t)data[1] << 16) | |
1180 |
| - ((uint32_t)data[0] << 24); |
1181 |
| -} |
1182 |
| -/* }}} */ |
1183 |
| - |
1184 |
| -/* {{{ php_is_image_avif |
1185 |
| - * detect whether an image is of type AVIF |
1186 |
| - * |
1187 |
| - * An AVIF image will start off a header "box". |
1188 |
| - * This starts with with a four-byte integer containing the number of bytes in the filetype box. |
1189 |
| - * This must be followed by the string "ftyp". |
1190 |
| - * Next comes a four-byte string indicating the "major brand". |
1191 |
| - * If that's "avif" or "avis", this is an AVIF image. |
1192 |
| - * Next, there's a four-byte "minor version" field, which we can ignore. |
1193 |
| - * Next comes an array of four-byte strings containing "compatible brands". |
1194 |
| - * These extend to the end of the box. |
1195 |
| - * If any of the compatible brands is "avif" or "avis", then this is an AVIF image. |
1196 |
| - * Otherwise, well, it's not. |
1197 |
| - * For more, see https://mpeg.chiariglione.org/standards/mpeg-4/iso-base-media-file-format/text-isoiec-14496-12-5th-edition |
1198 |
| - */ |
1199 |
| -bool php_is_image_avif(php_stream * stream) { |
1200 |
| - uint32_t header_size_reversed, header_size, i; |
1201 |
| - char box_type[4], brand[4]; |
| 1159 | +/* {{{ User struct and stream read/skip implementations for libavifinfo API */ |
| 1160 | +struct php_avif_stream { |
| 1161 | + php_stream* stream; |
| 1162 | + uint8_t buffer[AVIFINFO_MAX_NUM_READ_BYTES]; |
| 1163 | +}; |
1202 | 1164 |
|
1203 |
| - ZEND_ASSERT(stream != NULL); |
| 1165 | +static const uint8_t* php_avif_stream_read(void* stream, size_t num_bytes) { |
| 1166 | + struct php_avif_stream* avif_stream = (struct php_avif_stream*)stream; |
1204 | 1167 |
|
1205 |
| - if (php_stream_read(stream, (char *) &header_size_reversed, 4) != 4) { |
1206 |
| - return 0; |
| 1168 | + if (avif_stream == NULL || avif_stream->stream == NULL) { |
| 1169 | + return NULL; |
1207 | 1170 | }
|
1208 |
| - |
1209 |
| - header_size = php_ntohl(header_size_reversed); |
1210 |
| - |
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) { |
1213 |
| - return 0; |
| 1171 | + if (php_stream_read(avif_stream->stream, (char*)avif_stream->buffer, num_bytes) != num_bytes) { |
| 1172 | + avif_stream->stream = NULL; /* fail further calls */ |
| 1173 | + return NULL; |
1214 | 1174 | }
|
| 1175 | + return avif_stream->buffer; |
| 1176 | +} |
1215 | 1177 |
|
1216 |
| - if (memcmp(box_type, "ftyp", 4)) { |
1217 |
| - return 0; |
1218 |
| - } |
1219 |
| - |
1220 |
| - /* If the major brand is "avif" or "avis", it's an AVIF image. */ |
1221 |
| - if (php_stream_read(stream, brand, 4) != 4) { |
1222 |
| - return 0; |
1223 |
| - } |
| 1178 | +static void php_avif_stream_skip(void* stream, size_t num_bytes) { |
| 1179 | + struct php_avif_stream* avif_stream = (struct php_avif_stream*)stream; |
1224 | 1180 |
|
1225 |
| - if (!memcmp(brand, "avif", 4) || !memcmp(brand, "avis", 4)) { |
1226 |
| - return 1; |
| 1181 | + if (avif_stream == NULL || avif_stream->stream == NULL) { |
| 1182 | + return; |
1227 | 1183 | }
|
1228 |
| - |
1229 |
| - /* Skip the next four bytes, which are the "minor version". */ |
1230 |
| - if (php_stream_read(stream, brand, 4) != 4) { |
1231 |
| - return 0; |
| 1184 | + if (php_stream_seek(avif_stream->stream, num_bytes, SEEK_CUR)) { |
| 1185 | + avif_stream->stream = NULL; /* fail further calls */ |
1232 | 1186 | }
|
| 1187 | +} |
| 1188 | +/* }}} */ |
1233 | 1189 |
|
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. */ |
| 1190 | +/* {{{ php_handle_avif |
| 1191 | + * Parse AVIF features |
| 1192 | + * |
| 1193 | + * The stream must be positioned at the beginning of a box, so it does not |
| 1194 | + * matter whether the "ftyp" box was already read by php_is_image_avif() or not. |
| 1195 | + * It will read bytes from the stream until features are found or the file is |
| 1196 | + * declared as invalid. Around 450 bytes are usually enough. |
| 1197 | + * Transforms such as mirror and rotation are not applied on width and height. |
| 1198 | + */ |
| 1199 | +static struct gfxinfo *php_handle_avif(php_stream * stream) { |
| 1200 | + struct gfxinfo* result = NULL; |
| 1201 | + AvifInfoFeatures features; |
| 1202 | + struct php_avif_stream avif_stream; |
| 1203 | + avif_stream.stream = stream; |
| 1204 | + |
| 1205 | + if (AvifInfoGetFeaturesStream(&avif_stream, php_avif_stream_read, php_avif_stream_skip, &features) == kAvifInfoOk) { |
| 1206 | + result = (struct gfxinfo*)ecalloc(1, sizeof(struct gfxinfo)); |
| 1207 | + result->width = features.width; |
| 1208 | + result->height = features.height; |
| 1209 | + result->bits = features.bit_depth; |
| 1210 | + result->channels = features.num_channels; |
| 1211 | + } |
| 1212 | + return result; |
| 1213 | +} |
| 1214 | +/* }}} */ |
1236 | 1215 |
|
1237 |
| - for (i = 16; i < header_size; i += 4) { |
1238 |
| - if (php_stream_read(stream, brand, 4) != 4) { |
1239 |
| - return 0; |
1240 |
| - } |
| 1216 | +/* {{{ php_is_image_avif |
| 1217 | + * Detect whether an image is of type AVIF |
| 1218 | + * |
| 1219 | + * Only the first "ftyp" box is read. |
| 1220 | + * For a valid file, 12 bytes are usually read, but more might be necessary. |
| 1221 | + */ |
| 1222 | +bool php_is_image_avif(php_stream* stream) { |
| 1223 | + struct php_avif_stream avif_stream; |
| 1224 | + avif_stream.stream = stream; |
1241 | 1225 |
|
1242 |
| - if (!memcmp(brand, "avif", 4) || !memcmp(brand, "avis", 4)) { |
1243 |
| - return 1; |
1244 |
| - } |
| 1226 | + if (AvifInfoIdentifyStream(&avif_stream, php_avif_stream_read, php_avif_stream_skip) == kAvifInfoOk) { |
| 1227 | + return 1; |
1245 | 1228 | }
|
1246 |
| - |
1247 | 1229 | return 0;
|
1248 | 1230 | }
|
1249 | 1231 | /* }}} */
|
|
0 commit comments