|
42 | 42 | #include <zlib.h>
|
43 | 43 | #include <set>
|
44 | 44 |
|
| 45 | +#include <folly/ScopeGuard.h> |
45 | 46 | #include <folly/portability/Stdlib.h>
|
46 | 47 | #include <folly/portability/Unistd.h>
|
| 48 | +#include <libheif/heif.h> |
47 | 49 |
|
48 | 50 | /* Section Filters Declarations */
|
49 | 51 | /* IMPORTANT NOTE FOR NEW FILTER
|
@@ -421,6 +423,8 @@ typedef enum {
|
421 | 423 | IMAGE_FILETYPE_XBM,
|
422 | 424 | IMAGE_FILETYPE_ICO,
|
423 | 425 | IMAGE_FILETYPE_WEBP,
|
| 426 | + IMAGE_FILETYPE_AVIF, |
| 427 | + IMAGE_FILETYPE_HEIC, |
424 | 428 |
|
425 | 429 | IMAGE_FILETYPE_COUNT /* Must remain last */
|
426 | 430 | } image_filetype;
|
@@ -1508,6 +1512,111 @@ static struct gfxinfo *php_handle_webp(const req::ptr<File>& stream) {
|
1508 | 1512 | return result;
|
1509 | 1513 | }
|
1510 | 1514 |
|
| 1515 | +static int64_t heif_get_position(void* userdata) { |
| 1516 | + auto stream = static_cast<File*>(userdata); |
| 1517 | + return stream->tell(); |
| 1518 | +} |
| 1519 | + |
| 1520 | +static int heif_read(void* data, size_t size, void* userdata) { |
| 1521 | + auto stream = static_cast<File*>(userdata); |
| 1522 | + |
| 1523 | + // Special handling as read doesn't like 0 passed in for size |
| 1524 | + if (size == 0) { |
| 1525 | + return heif_error_Ok; |
| 1526 | + } |
| 1527 | + // An attempt was made to use readImpl instead to avoid memcpy, but |
| 1528 | + // unfortunately, tell() returns wrong position if readImpl is used. |
| 1529 | + String result = stream->read(size); |
| 1530 | + if (result.length() != size) { |
| 1531 | + return 1; // Failure |
| 1532 | + } |
| 1533 | + memcpy(data, result.data(), size); |
| 1534 | + return heif_error_Ok; |
| 1535 | +} |
| 1536 | + |
| 1537 | +static int heif_seek(int64_t position, void* userdata) { |
| 1538 | + auto stream = static_cast<File*>(userdata); |
| 1539 | + if (!stream->seekable()) { |
| 1540 | + return 1; // Failure |
| 1541 | + } |
| 1542 | + if (stream->seek(position, SEEK_SET)) { |
| 1543 | + return heif_error_Ok; |
| 1544 | + } |
| 1545 | + return 1; // Failure |
| 1546 | +} |
| 1547 | + |
| 1548 | +static heif_reader_grow_status heif_wait_for_file_size( |
| 1549 | + int64_t /*targetSize*/, |
| 1550 | + void* /*userdata*/) { |
| 1551 | + return heif_reader_grow_status_size_reached; |
| 1552 | +} |
| 1553 | + |
| 1554 | +static heif_reader create_heif_reader() { |
| 1555 | + return heif_reader{ |
| 1556 | + .reader_api_version = 1, |
| 1557 | + .get_position = &heif_get_position, |
| 1558 | + .read = &heif_read, |
| 1559 | + .seek = &heif_seek, |
| 1560 | + .wait_for_file_size = &heif_wait_for_file_size}; |
| 1561 | +} |
| 1562 | + |
| 1563 | +static struct gfxinfo *php_handle_heif(const req::ptr<File>& stream) { |
| 1564 | + struct gfxinfo *result = nullptr; |
| 1565 | + |
| 1566 | + if (!stream->rewind()) { |
| 1567 | + return nullptr; |
| 1568 | + } |
| 1569 | + |
| 1570 | + heif_context* ctx = heif_context_alloc(); |
| 1571 | + SCOPE_EXIT { |
| 1572 | + heif_context_free(ctx); |
| 1573 | + }; |
| 1574 | + |
| 1575 | + heif_reader reader = create_heif_reader(); |
| 1576 | + auto err = heif_context_read_from_reader(ctx, &reader, stream.get(), nullptr); |
| 1577 | + if (err.code != 0) { |
| 1578 | + raise_notice("Heif context read failed: %s", err.message); |
| 1579 | + return nullptr; |
| 1580 | + } |
| 1581 | + |
| 1582 | + heif_image_handle* handle; |
| 1583 | + err = heif_context_get_primary_image_handle(ctx, &handle); |
| 1584 | + if (err.code != 0) { |
| 1585 | + // Unable to get image handle |
| 1586 | + raise_notice("Failed to get primary image handle: %s", err.message); |
| 1587 | + return nullptr; |
| 1588 | + } |
| 1589 | + |
| 1590 | + SCOPE_EXIT { |
| 1591 | + heif_image_handle_release(handle); |
| 1592 | + }; |
| 1593 | + |
| 1594 | + result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo)); |
| 1595 | + CHECK_ALLOC_R(result, (sizeof(struct gfxinfo)), nullptr); |
| 1596 | + |
| 1597 | + result->width = heif_image_handle_get_width(handle); |
| 1598 | + result->height = heif_image_handle_get_height(handle); |
| 1599 | + // It is easier to not set bits/channels because it will require actual |
| 1600 | + // decoding of the image bitstream to get the information using libheif. |
| 1601 | + result->bits = 0; |
| 1602 | + result->channels = 0; |
| 1603 | + return result; |
| 1604 | +} |
| 1605 | + |
| 1606 | +static heif_brand2 php_get_heif(const req::ptr<File>& stream) { |
| 1607 | + if (!stream->rewind()) { |
| 1608 | + return heif_unknown_brand; |
| 1609 | + } |
| 1610 | + |
| 1611 | + String fileType = stream->read(12); |
| 1612 | + if (fileType.length() != 12) { |
| 1613 | + return heif_unknown_brand; |
| 1614 | + } |
| 1615 | + |
| 1616 | + return heif_read_main_brand(reinterpret_cast<const uint8_t*>( |
| 1617 | + fileType.c_str()), 12); |
| 1618 | +} |
| 1619 | + |
1511 | 1620 | /* Convert internal image_type to mime type */
|
1512 | 1621 | static char *php_image_type_to_mime_type(int image_type) {
|
1513 | 1622 | switch( image_type) {
|
@@ -1541,6 +1650,10 @@ static char *php_image_type_to_mime_type(int image_type) {
|
1541 | 1650 | return "image/vnd.microsoft.icon";
|
1542 | 1651 | case IMAGE_FILETYPE_WEBP:
|
1543 | 1652 | return "image/webp";
|
| 1653 | + case IMAGE_FILETYPE_AVIF: |
| 1654 | + return "image/avif"; |
| 1655 | + case IMAGE_FILETYPE_HEIC: |
| 1656 | + return "image/heic"; |
1544 | 1657 | default:
|
1545 | 1658 | case IMAGE_FILETYPE_UNKNOWN:
|
1546 | 1659 | return "application/octet-stream"; /* suppose binary format */
|
@@ -1627,6 +1740,12 @@ static int php_getimagetype(const req::ptr<File>& file) {
|
1627 | 1740 | }
|
1628 | 1741 |
|
1629 | 1742 | /* AFTER ALL ABOVE FAILED */
|
| 1743 | + auto heifBrand = php_get_heif(file); |
| 1744 | + if (heifBrand == heif_brand2_heic || heifBrand == heif_brand2_heix) { |
| 1745 | + return IMAGE_FILETYPE_HEIC; |
| 1746 | + } else if (heifBrand == heif_brand2_avif) { |
| 1747 | + return IMAGE_FILETYPE_AVIF; |
| 1748 | + } |
1630 | 1749 | if (php_get_wbmp(file, nullptr, 1)) {
|
1631 | 1750 | return IMAGE_FILETYPE_WBMP;
|
1632 | 1751 | }
|
@@ -1668,6 +1787,10 @@ String HHVM_FUNCTION(image_type_to_mime_type, int64_t imagetype) {
|
1668 | 1787 | return "image/vnd.microsoft.icon";
|
1669 | 1788 | case IMAGE_FILETYPE_WEBP:
|
1670 | 1789 | return "image/webp";
|
| 1790 | + case IMAGE_FILETYPE_AVIF: |
| 1791 | + return "image/avif"; |
| 1792 | + case IMAGE_FILETYPE_HEIC: |
| 1793 | + return "image/heic"; |
1671 | 1794 | default:
|
1672 | 1795 | case IMAGE_FILETYPE_UNKNOWN:
|
1673 | 1796 | return "application/octet-stream"; /* suppose binary format */
|
@@ -1710,6 +1833,10 @@ Variant HHVM_FUNCTION(image_type_to_extension,
|
1710 | 1833 | return include_dot ? String(".ico") : String("ico");
|
1711 | 1834 | case IMAGE_FILETYPE_WEBP:
|
1712 | 1835 | return include_dot ? String(".webp") : String("webp");
|
| 1836 | + case IMAGE_FILETYPE_AVIF: |
| 1837 | + return include_dot ? String(".avif") : String("avif"); |
| 1838 | + case IMAGE_FILETYPE_HEIC: |
| 1839 | + return include_dot ? String(".heic") : String("heic"); |
1713 | 1840 | default:
|
1714 | 1841 | return false;
|
1715 | 1842 | }
|
@@ -1786,6 +1913,12 @@ Variant getImageSize(const req::ptr<File>& stream, Array& imageinfo) {
|
1786 | 1913 | case IMAGE_FILETYPE_WEBP:
|
1787 | 1914 | result = php_handle_webp(stream);
|
1788 | 1915 | break;
|
| 1916 | + case IMAGE_FILETYPE_AVIF: |
| 1917 | + result = php_handle_heif(stream); |
| 1918 | + break; |
| 1919 | + case IMAGE_FILETYPE_HEIC: |
| 1920 | + result = php_handle_heif(stream); |
| 1921 | + break; |
1789 | 1922 | default:
|
1790 | 1923 | case IMAGE_FILETYPE_UNKNOWN:
|
1791 | 1924 | break;
|
@@ -8247,6 +8380,8 @@ struct GdExtension final : Extension {
|
8247 | 8380 | HHVM_RC_INT(IMAGETYPE_XBM, IMAGE_FILETYPE_XBM);
|
8248 | 8381 | HHVM_RC_INT(IMAGETYPE_ICO, IMAGE_FILETYPE_ICO);
|
8249 | 8382 | HHVM_RC_INT(IMAGETYPE_WEBP, IMAGE_FILETYPE_WEBP);
|
| 8383 | + HHVM_RC_INT(IMAGETYPE_AVIF, IMAGE_FILETYPE_AVIF); |
| 8384 | + HHVM_RC_INT(IMAGETYPE_HEIC, IMAGE_FILETYPE_HEIC); |
8250 | 8385 | HHVM_RC_INT(IMAGETYPE_UNKNOWN, IMAGE_FILETYPE_UNKNOWN);
|
8251 | 8386 | HHVM_RC_INT(IMAGETYPE_COUNT, IMAGE_FILETYPE_COUNT);
|
8252 | 8387 | HHVM_RC_INT(IMAGETYPE_SWC, IMAGE_FILETYPE_SWC);
|
|
0 commit comments