diff --git a/.github/workflows/analysis-coverage.yml b/.github/workflows/analysis-coverage.yml index 975e7a33..032f88fb 100644 --- a/.github/workflows/analysis-coverage.yml +++ b/.github/workflows/analysis-coverage.yml @@ -22,7 +22,7 @@ on: jobs: analysis: - runs-on: macos-12 + runs-on: macos-13 name: Analysis steps: @@ -31,6 +31,9 @@ jobs: with: python-version: '3.12' + - name: Install Libheif + run: brew install libheif + - name: Install from source run: python3 -m pip install ".[dev]" wheel setuptools @@ -130,8 +133,8 @@ jobs: verbose: true coverage-macos: - runs-on: macos-12 - name: Coverage(macOS) • 🐍3.10 + runs-on: macos-13 + name: Coverage(macOS-13) • 🐍3.10 env: TEST_DECODE_THREADS: 0 # This test fails on GitHub on macOS. We have such enabled test on Cirrus. @@ -143,6 +146,7 @@ jobs: - name: Install from source run: | + brew install --formula ./libheif/macos/libheif.rb python3 -m pip -v install ".[dev]" - name: LibHeif info @@ -176,6 +180,9 @@ jobs: with: python-version: '3.11' + - name: Workaround for pkg-config + run: brew link --overwrite pkgconf + - name: Install from source run: | brew install --formula ./libheif/macos/libheif.rb @@ -366,11 +373,18 @@ jobs: run: sudo -H PH_LIGHT_ACTION=1 EXP_PH_LIBHEIF_VERSION="" python3 -m pytest coverage-import-error: - runs-on: macos-12 + runs-on: macos-13 name: Coverage(ImportError) steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Libheif + run: brew install libheif + - name: Install from source run: | python3 -m pip -v install . diff --git a/.github/workflows/test-src-build-linux.yml b/.github/workflows/test-src-build-linux.yml index 19ab858d..e80654e3 100644 --- a/.github/workflows/test-src-build-linux.yml +++ b/.github/workflows/test-src-build-linux.yml @@ -37,11 +37,11 @@ jobs: matrix: arch: ["amd64", "arm64"] docker_file: ["Alpine_3_19", "Alpine_3_20", "Almalinux_9", "Debian_12"] - include: - - arch: "amd64" - docker_file: "Archlinux" - - arch: "amd64" - docker_file: "Fedora_39" +# include: +# - arch: "amd64" +# docker_file: "Archlinux" +# - arch: "amd64" +# docker_file: "Fedora_39" steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test-src-build-macos.yml b/.github/workflows/test-src-build-macos.yml index 621d69d4..4f2a7be5 100644 --- a/.github/workflows/test-src-build-macos.yml +++ b/.github/workflows/test-src-build-macos.yml @@ -28,9 +28,9 @@ concurrency: cancel-in-progress: true jobs: - full_macos_12: - name: macOS:12-x86_64 - runs-on: macos-12 + full_macos_13: + name: macOS:13-x86_64 + runs-on: macos-13 env: TEST_DECODE_THREADS: 0 # This test fails on GitHub on macOS. We have such enabled test on Cirrus. PH_FULL_ACTION: 1 @@ -65,6 +65,9 @@ jobs: with: python-version: '3.12' + - name: Workaround for pkg-config + run: brew link --overwrite pkgconf + - name: Install libheif from formula run: | brew uninstall --force --ignore-dependencies imagemagick libheif @@ -79,9 +82,9 @@ jobs: - name: Perform tests run: python3 -m pytest - lite_macos_12: - name: macOS:12-x86_64(Pi-Heif) - runs-on: macos-12 + lite_macos_13: + name: macOS:13-x86_64(Pi-Heif) + runs-on: macos-13 env: TEST_DECODE_THREADS: 0 PH_LIGHT_ACTION: 1 @@ -121,6 +124,9 @@ jobs: with: python-version: '3.12' + - name: Workaround for pkg-config + run: brew link --overwrite pkgconf + - name: Transform to Pi-Heif run: | cp -r -v ./pi-heif/* . diff --git a/.github/workflows/wheels-pi_heif.yml b/.github/workflows/wheels-pi_heif.yml index 649e35a9..12e93c8d 100644 --- a/.github/workflows/wheels-pi_heif.yml +++ b/.github/workflows/wheels-pi_heif.yml @@ -20,6 +20,9 @@ jobs: cp -r -v ./pi-heif/* . python3 .github/transform_to-pi_heif.py + - name: Workaround for pkg-config + run: brew link --overwrite pkgconf + - name: Install libheif from formula run: | brew uninstall --force --ignore-dependencies imagemagick libheif x265 aom diff --git a/.github/workflows/wheels-pillow_heif.yml b/.github/workflows/wheels-pillow_heif.yml index 0e41fc33..71a203cd 100644 --- a/.github/workflows/wheels-pillow_heif.yml +++ b/.github/workflows/wheels-pillow_heif.yml @@ -14,6 +14,9 @@ jobs: with: python-version: '3.12' + - name: Workaround for pkg-config + run: brew link --overwrite pkgconf + - name: Install libheif from formula run: | brew uninstall --force --ignore-dependencies imagemagick libheif diff --git a/CHANGELOG.md b/CHANGELOG.md index f0af6fa0..5fbf948c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ All notable changes to this project will be documented in this file. +## [0.21.0 - 2024-1x-xx] + +### Changed + +- libheif updated from `1.18.2` to `1.19.3` version. + ## [0.20.0 - 2024-10-19] ### Added diff --git a/docker/from_src/Alpine_3_19.Dockerfile b/docker/from_src/Alpine_3_19.Dockerfile index a3b78f3f..70a6a39a 100644 --- a/docker/from_src/Alpine_3_19.Dockerfile +++ b/docker/from_src/Alpine_3_19.Dockerfile @@ -9,7 +9,6 @@ RUN \ nasm \ aom-dev \ x265-dev \ - libde265-dev \ py3-numpy \ py3-pillow diff --git a/docker/from_src/Alpine_3_20.Dockerfile b/docker/from_src/Alpine_3_20.Dockerfile index 3373dd9b..b7c359a6 100644 --- a/docker/from_src/Alpine_3_20.Dockerfile +++ b/docker/from_src/Alpine_3_20.Dockerfile @@ -5,7 +5,11 @@ RUN \ python3-dev \ py3-pip \ alpine-sdk \ - libheif-dev \ + cmake \ + nasm \ + aom-dev \ + x265-dev \ + libde265-dev \ py3-numpy \ py3-pillow @@ -16,6 +20,7 @@ COPY . /pillow_heif RUN \ python3 -m venv --system-site-packages myenv && \ source myenv/bin/activate && \ + python3 pillow_heif/libheif/linux_build_libs.py && \ if [ `getconf LONG_BIT` = 64 ]; then \ python3 -m pip install -v "pillow_heif/.[tests]"; \ else \ diff --git a/libheif/heif.h b/libheif/heif.h index 382740bb..0901e43f 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -1,4 +1,4 @@ -// original: https://github.com/strukturag/libheif/blob/master/libheif/heif.h +// original: https://github.com/strukturag/libheif/blob/master/libheif/api/libheif/heif.h // This file is used for building on Windows. /* @@ -37,7 +37,7 @@ extern "C" { #include //#include -#define LIBHEIF_NUMERIC_VERSION ((1<<24) | (18<<16) | (1<<8) | 0) +#define LIBHEIF_NUMERIC_VERSION ((1<<24) | (19<<16) | (3<<8) | 0) // API versions table // @@ -55,8 +55,10 @@ extern "C" { // 1.14 3 5 1 1 1 1 // 1.15 4 5 1 1 1 1 // 1.16 5 6 1 1 1 1 +// 1.18 5 7 1 1 1 1 +// 1.19 6 7 2 1 1 1 -#if defined(_MSC_VER) && !defined(LIBHEIF_STATIC_BUILD) +#if (defined(_WIN32) || defined __CYGWIN__) && !defined(LIBHEIF_STATIC_BUILD) #ifdef LIBHEIF_EXPORTS #define LIBHEIF_API __declspec(dllexport) #else @@ -136,7 +138,10 @@ enum heif_error_code heif_error_Color_profile_does_not_exist = 10, // Error loading a dynamic plugin - heif_error_Plugin_loading_error = 11 + heif_error_Plugin_loading_error = 11, + + // Operation has been canceled + heif_error_Canceled = 12 }; @@ -247,6 +252,11 @@ enum heif_suberror_code // icbr is only needed in some situations, this error is for those cases heif_suberror_No_icbr_box = 142, + heif_suberror_No_avcC_box = 143, + + // we got a mini box, but could not read it properly + heif_suberror_Invalid_mini_box = 149, + // Decompressing generic compression or header compression data failed (e.g. bitstream corruption) heif_suberror_Decompression_invalid_data = 150, @@ -311,6 +321,8 @@ enum heif_suberror_code // Generically compressed data used an unsupported compression method heif_suberror_Unsupported_generic_compression_method = 3006, + heif_suberror_Unsupported_essential_property = 3007, + // --- Encoder_plugin_error --- heif_suberror_Unsupported_bit_depth = 4000, @@ -401,7 +413,7 @@ enum heif_compression_format */ heif_compression_AV1 = 4, /** - * VVC compression. (Currently unused in libheif.) + * VVC compression. * * The compression format is defined in ISO/IEC 23090-3. This is equivalent to H.266. * @@ -411,7 +423,7 @@ enum heif_compression_format /** * EVC compression. (Currently unused in libheif.) * - * The compression format is defined in ISO/IEC 23094-1. This is equivalent to H.266. + * The compression format is defined in ISO/IEC 23094-1. * * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex M. */ @@ -426,7 +438,7 @@ enum heif_compression_format /** * Uncompressed encoding. * - * This is defined in ISO/IEC 23001-17:2023 (Final Draft International Standard). + * This is defined in ISO/IEC 23001-17:2024. */ heif_compression_uncompressed = 8, /** @@ -485,7 +497,10 @@ enum heif_colorspace heif_colorspace_RGB = 1, // heif_colorspace_monochrome should only be used with heif_chroma = heif_chroma_monochrome - heif_colorspace_monochrome = 2 + heif_colorspace_monochrome = 2, + + // Indicates that this image has no visual channels. + heif_colorspace_nonvisual = 3 }; enum heif_channel @@ -497,9 +512,21 @@ enum heif_channel heif_channel_G = 4, heif_channel_B = 5, heif_channel_Alpha = 6, - heif_channel_interleaved = 10 + heif_channel_interleaved = 10, + heif_channel_filter_array = 11, + heif_channel_depth = 12, + heif_channel_disparity = 13 }; +enum heif_metadata_compression +{ + heif_metadata_compression_off = 0, + heif_metadata_compression_auto = 1, + heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file + heif_metadata_compression_deflate = 3, + heif_metadata_compression_zlib = 4, // do not use for header data + heif_metadata_compression_brotli = 5 +}; // ========================= library initialization ====================== @@ -539,6 +566,10 @@ struct heif_error heif_init(struct heif_init_params*); * You should call heif_init() when you start using libheif and heif_deinit() when you are finished. * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit(). * + * Note: heif_deinit() must not be called after exit(), for example in a global C++ object's destructor. + * If you do, global variables in libheif might have already been released when heif_deinit() is running, + * leading to a crash. + * * \sa heif_init() */ LIBHEIF_API @@ -752,6 +783,13 @@ typedef uint32_t heif_brand2; */ #define heif_brand2_mif2 heif_fourcc('m','i','f','2') +/** + * HEIF image structural brand (`mif3`). + * + * This indicates the low-overhead (ftyp+mini) structure. + */ +#define heif_brand2_mif3 heif_fourcc('m','i','f','3') + /** * HEIF image sequence structural brand (`msf1`). * @@ -853,6 +891,10 @@ typedef uint32_t heif_brand2; LIBHEIF_API heif_brand2 heif_read_main_brand(const uint8_t* data, int len); +// input data should be at least 16 bytes +LIBHEIF_API +heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len); + // 'brand_fourcc' must be 4 character long, but need not be 0-terminated LIBHEIF_API heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc); @@ -915,15 +957,33 @@ LIBHEIF_API void heif_context_free(struct heif_context*); + struct heif_reading_options; enum heif_reader_grow_status { - heif_reader_grow_status_size_reached, // requested size has been reached, we can read until this point - heif_reader_grow_status_timeout, // size has not been reached yet, but it may still grow further - heif_reader_grow_status_size_beyond_eof // size has not been reached and never will. The file has grown to its full size + heif_reader_grow_status_size_reached, // requested size has been reached, we can read until this point + heif_reader_grow_status_timeout, // size has not been reached yet, but it may still grow further (deprecated) + heif_reader_grow_status_size_beyond_eof, // size has not been reached and never will. The file has grown to its full size + heif_reader_grow_status_error // an error has occurred }; +struct heif_reader_range_request_result +{ + enum heif_reader_grow_status status; // should not return 'heif_reader_grow_status_timeout' + + // Indicates up to what position the file has been read. + // If we cannot read the whole file range (status == 'heif_reader_grow_status_size_beyond_eof'), this is the actual end position. + // On the other hand, it may be that the reader was reading more data than requested. In that case, it should indicate the full size here + // and libheif may decide to make use of the additional data (e.g. for filling 'tili' offset tables). + uint64_t range_end; + + // for status == 'heif_reader_grow_status_error' + int reader_error_code; // a reader specific error code + const char* reader_error_msg; // libheif will call heif_reader.release_error_msg on this if it is not NULL +}; + + struct heif_reader { // API version supported by this reader @@ -932,7 +992,7 @@ struct heif_reader // --- version 1 functions --- int64_t (* get_position)(void* userdata); - // The functions read(), and seek() return heif_error_ok on success. + // The functions read(), and seek() return 0 on success. // Generally, libheif will make sure that we do not read past the file size. int (* read)(void* data, size_t size, @@ -950,6 +1010,47 @@ struct heif_reader // detection whether the target_size is above the (fixed) file length // (in this case, return 'size_beyond_eof'). enum heif_reader_grow_status (* wait_for_file_size)(int64_t target_size, void* userdata); + + // --- version 2 functions --- + + // These two functions are for applications that want to stream HEIF files on demand. + // For example, a large HEIF file that is served over HTTPS and we only want to download + // it partially to decode individual tiles. + // If you do not have this use case, you do not have to implement these functions and + // you can set them to NULL. For simple linear loading, you may use the 'wait_for_file_size' + // function above instead. + + // If this function is defined, libheif will often request a file range before accessing it. + // The purpose of this function is that libheif will usually read very small chunks of data with the + // read() callback above. However, it is inefficient to request such a small chunk of data over a network + // and the network delay will significantly increase the decoding time. + // Thus, libheif will call request_range() with a larger block of data that should be preloaded and the + // subsequent read() calls will work within the requested ranges. + // + // Note: `end_pos` is one byte after the last position to be read. + // You should return + // - 'heif_reader_grow_status_size_reached' if the requested range is available, or + // - 'heif_reader_grow_status_size_beyond_eof' if the requested range exceeds the file size + // (the valid part of the range has been read). + struct heif_reader_range_request_result (*request_range)(uint64_t start_pos, uint64_t end_pos, void* userdata); + + // libheif might issue hints when it assumes that a file range might be needed in the future. + // This may happen, for example, when your are doing selective tile accesses and libheif proposes + // to preload offset pointer tables. + // Another difference to request_file_range() is that this call should be non-blocking. + // If you preload any data, do this in a background thread. + void (*preload_range_hint)(uint64_t start_pos, uint64_t end_pos, void* userdata); + + // If libheif does not need access to a file range anymore, it may call this function to + // give a hint to the reader that it may release the range from a cache. + // If you do not maintain a file cache that wants to reduce its size dynamically, you do not + // need to implement this function. + void (*release_file_range)(uint64_t start_pos, uint64_t end_pos, void* userdata); + + // Release an error message that was returned by heif_reader in an earlier call. + // If this function is NULL, the error message string will not be released. + // This is a viable option if you are only returning static strings. + void (*release_error_msg)(const char* msg); }; @@ -1018,6 +1119,9 @@ LIBHEIF_API void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd); +// Set the maximum image size security limit. This function will set the maximum image area (number of pixels) +// to maximum_width ^ 2. Alternatively to using this function, you can also set the maximum image area +// in the security limits structure returned by heif_context_get_security_limits(). LIBHEIF_API void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width); @@ -1030,6 +1134,53 @@ LIBHEIF_API void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads); +// --- security limits + +// If you set a limit to 0, the limit is disabled. +struct heif_security_limits { + uint8_t version; + + // --- version 1 + + // Limit on the maximum image size to avoid allocating too much memory. + // For example, setting this to 32768^2 pixels = 1 Gigapixels results + // in 1.5 GB memory need for YUV-4:2:0 or 4 GB for RGB32. + uint64_t max_image_size_pixels; + uint64_t max_number_of_tiles; + uint32_t max_bayer_pattern_pixels; + uint32_t max_items; + + uint32_t max_color_profile_size; + uint64_t max_memory_block_size; + + uint32_t max_components; + + uint32_t max_iloc_extents_per_item; + uint32_t max_size_entity_group; + + uint32_t max_children_per_box; // for all boxes that are not covered by other limits +}; + +// The global security limits are the default for new heif_contexts. +// These global limits cannot be changed, but you can override the limits for a specific heif_context. +LIBHEIF_API +const struct heif_security_limits* heif_get_global_security_limits(); + +// Returns a set of fully disabled security limits. Use with care and only after user confirmation. +LIBHEIF_API +const struct heif_security_limits* heif_get_disabled_security_limits(); + +// Returns the security limits for a heif_context. +// By default, the limits are set to the global limits, but you can change them in the returned object. +LIBHEIF_API +struct heif_security_limits* heif_context_get_security_limits(const struct heif_context*); + +// Overwrites the security limits of a heif_context. +// This is a convenience function to easily copy limits. +LIBHEIF_API +struct heif_error heif_context_set_security_limits(struct heif_context*, const struct heif_security_limits*); + + // ========================= heif_image_handle ========================= // An heif_image_handle is a handle to a logical image in the HEIF file. @@ -1065,17 +1216,21 @@ LIBHEIF_API int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. +// Only defined for images coded in the YCbCr or monochrome colorspace. LIBHEIF_API int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. +// Only defined for images coded in the YCbCr colorspace. LIBHEIF_API int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle*); // Return the colorspace that libheif proposes to use for decoding. // Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images -// encoded with matrix_coefficients=0. +// encoded with matrix_coefficients=0 or for images coded natively in RGB. // It may also return *_undefined if the file misses relevant information to determine this without decoding. +// These are only proposed values that avoid colorspace conversions as much as possible. +// You can still request the output in your preferred colorspace, but this may involve an internal conversion. LIBHEIF_API struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle, enum heif_colorspace* out_colorspace, @@ -1102,6 +1257,86 @@ LIBHEIF_API struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle); +struct heif_image_tiling +{ + int version; + + // --- version 1 + + uint32_t num_columns; + uint32_t num_rows; + uint32_t tile_width; + uint32_t tile_height; + + uint32_t image_width; + uint32_t image_height; + + // Position of the top left tile. + // Usually, this is (0;0), but if a tiled image is rotated or cropped, it may be that the top left tile should be placed at a negative position. + // The offsets define this negative shift. + uint32_t top_offset; + uint32_t left_offset; + + uint8_t number_of_extra_dimensions; // 0 for normal images, 1 for volumetric (3D), ... + uint32_t extra_dimension_size[8]; // size of extra dimensions (first 8 dimensions) +}; + + +// If 'process_image_transformations' is true, this returns modified sizes. +// If it is false, the top_offset and left_offset will always be (0;0). +LIBHEIF_API +struct heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* out_tiling); + + +// For grid images, return the image item ID of a specific grid tile. +// If 'process_image_transformations' is true, the tile positions are given in the transformed image coordinate system and +// are internally mapped to the original image tile positions. +LIBHEIF_API +struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle, + int process_image_transformations, + uint32_t tile_x, uint32_t tile_y, + heif_item_id* out_tile_item_id); + + +struct heif_decoding_options; + +// The tile position is given in tile indices, not in pixel coordinates. +// If the image transformations are processed (option->ignore_image_transformations==false), the tile position +// is given in the transformed coordinates. +LIBHEIF_API +struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle, + struct heif_image** out_img, + enum heif_colorspace colorspace, + enum heif_chroma chroma, + const struct heif_decoding_options* options, + uint32_t tile_x, uint32_t tile_y); + + +// ------------------------- entity groups ------------------------ + +typedef uint32_t heif_entity_group_id; + +struct heif_entity_group +{ + heif_entity_group_id entity_group_id; + uint32_t entity_group_type; // this is a FourCC constant + heif_item_id* entities; + uint32_t num_entities; +}; + +// Use 0 for `type_filter` or `item_filter` to disable the filter. +// Returns an array of heif_entity_group structs with *out_num_groups entries. +LIBHEIF_API +struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context*, + uint32_t type_filter, + heif_item_id item_filter, + int* out_num_groups); + +// Release an array of entity groups returned by heif_context_get_entity_groups(). +LIBHEIF_API +void heif_entity_groups_release(struct heif_entity_group*, int num_groups); + + // ------------------------- depth images ------------------------- LIBHEIF_API @@ -1397,13 +1632,17 @@ struct heif_color_profile_nclx* heif_nclx_color_profile_alloc(void); LIBHEIF_API void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile); - +// Note: in early versions of HEIF, there could only be one color profile per image. However, this has been changed. +// This function will now return ICC if one is present and NCLX only if there is no ICC. +// You may better avoid this function and simply query for NCLX and ICC directly. LIBHEIF_API enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image); +// Returns the size of the ICC profile if one is assigned to the image. Otherwise, it returns 0. LIBHEIF_API size_t heif_image_get_raw_color_profile_size(const struct heif_image* image); +// Returns the ICC profile if one is assigned to the image. Otherwise, it returns an error. LIBHEIF_API struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image, void* out_data); @@ -1485,8 +1724,10 @@ enum heif_chroma_upsampling_algorithm heif_chroma_upsampling_bilinear = 2 }; + struct heif_color_conversion_options { + // 'version' must be 1. uint8_t version; // --- version 1 options @@ -1501,8 +1742,15 @@ struct heif_color_conversion_options // Set this field to 'true' if you want to make sure that the specified algorithm is used even // at the cost of slightly higher computation times. uint8_t only_use_preferred_chroma_algorithm; + + // --- Note that we cannot extend this struct because it is embedded in + // other structs (heif_decoding_options and heif_encoding_options). }; +// Assumes that it is a version=1 struct. +LIBHEIF_API +void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options*); + struct heif_decoding_options { @@ -1514,6 +1762,7 @@ struct heif_decoding_options // Default: false (do not ignore). uint8_t ignore_transformations; + // Any of the progress functions may be called from background threads. void (* start_progress)(enum heif_progress_step step, int max_progress, void* progress_user_data); void (* on_progress)(enum heif_progress_step step, int progress, void* progress_user_data); @@ -1539,10 +1788,13 @@ struct heif_decoding_options // The priority is defined in the plugin. const char* decoder_id; - // version 5 options struct heif_color_conversion_options color_conversion_options; + + // version 6 options + + int (* cancel_decoding)(void* progress_user_data); }; @@ -1632,7 +1884,6 @@ struct heif_error heif_image_crop(struct heif_image* img, LIBHEIF_API int heif_image_get_bits_per_pixel(const struct heif_image*, enum heif_channel channel); - // Get the number of bits per pixel in the given image channel. This function returns // the number of bits used for representing the pixel value, which might be smaller // than the number of bits used in memory. @@ -1660,6 +1911,7 @@ uint8_t* heif_image_get_plane(struct heif_image*, int* out_stride); + struct heif_scaling_options; // Currently, heif_scaling_options is not defined yet. Pass a NULL pointer. @@ -1669,6 +1921,12 @@ struct heif_error heif_image_scale_image(const struct heif_image* input, int width, int height, const struct heif_scaling_options* options); +// Extends the image size to match the given size by extending the right and bottom borders. +// The border areas are filled with zero. +LIBHEIF_API +struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image, + uint32_t width, uint32_t height); + // The color profile is not attached to the image handle because we might need it // for color space transform and encoding. LIBHEIF_API @@ -1722,6 +1980,10 @@ int heif_image_has_content_light_level(const struct heif_image*); LIBHEIF_API void heif_image_get_content_light_level(const struct heif_image*, struct heif_content_light_level* out); +// Returns whether the image has 'content light level' information. If 0 is returned, the output is not filled. +LIBHEIF_API +int heif_image_handle_get_content_light_level(const struct heif_image_handle*, struct heif_content_light_level* out); + LIBHEIF_API void heif_image_set_content_light_level(const struct heif_image*, const struct heif_content_light_level* in); @@ -1748,15 +2010,27 @@ struct heif_decoded_mastering_display_colour_volume double min_display_mastering_luminance; }; +struct heif_ambient_viewing_environment +{ + uint32_t ambient_illumination; + uint16_t ambient_light_x; + uint16_t ambient_light_y; +}; + LIBHEIF_API int heif_image_has_mastering_display_colour_volume(const struct heif_image*); LIBHEIF_API void heif_image_get_mastering_display_colour_volume(const struct heif_image*, struct heif_mastering_display_colour_volume* out); +// Returns whether the image has 'mastering display colour volume' information. If 0 is returned, the output is not filled. +LIBHEIF_API +int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle*, struct heif_mastering_display_colour_volume* out); + LIBHEIF_API void heif_image_set_mastering_display_colour_volume(const struct heif_image*, const struct heif_mastering_display_colour_volume* in); + // Converts the internal numeric representation of heif_mastering_display_colour_volume to the // normalized values, collected in heif_decoded_mastering_display_colour_volume. // Values that are out-of-range are decoded to 0, indicating an undefined value (as specified in ISO/IEC 23008-2). @@ -1767,6 +2041,10 @@ struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_ LIBHEIF_API void heif_image_get_pixel_aspect_ratio(const struct heif_image*, uint32_t* aspect_h, uint32_t* aspect_v); +// Returns whether the image has 'pixel aspect ratio information' information. If 0 is returned, the output is filled with the 1:1 default. +LIBHEIF_API +int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle*, uint32_t* aspect_h, uint32_t* aspect_v); + LIBHEIF_API void heif_image_set_pixel_aspect_ratio(struct heif_image*, uint32_t aspect_h, uint32_t aspect_v); @@ -1888,7 +2166,7 @@ struct heif_error heif_context_get_encoder(struct heif_context* context, // Quick check whether there is a decoder available for the given format. // Note that the decoder still may not be able to decode all variants of that format. -// You will have to query that further (to-do) or just try to decode and check the returned error. +// You will have to query that further (TO-DO) or just try to decode and check the returned error. LIBHEIF_API int heif_have_decoder_for_format(enum heif_compression_format format); @@ -2106,7 +2384,7 @@ struct heif_encoding_options // version 7 options - // Set this to true to use compressed form of uncC where possible + // Set this to true to use compressed form of uncC where possible. uint8_t prefer_uncC_short_form; }; @@ -2150,6 +2428,34 @@ struct heif_error heif_context_encode_grid(struct heif_context* ctx, const struct heif_encoding_options* input_options, struct heif_image_handle** out_image_handle); +LIBHEIF_API +struct heif_error heif_context_add_grid_image(struct heif_context* ctx, + uint32_t image_width, + uint32_t image_height, + uint32_t tile_columns, + uint32_t tile_rows, + const struct heif_encoding_options* encoding_options, + struct heif_image_handle** out_grid_image_handle); + +LIBHEIF_API +struct heif_error heif_context_add_image_tile(struct heif_context* ctx, + struct heif_image_handle* tiled_image, + uint32_t tile_x, uint32_t tile_y, + const struct heif_image* image, + struct heif_encoder* encoder); + +// offsets[] should either be NULL (all offsets==0) or an array of size 2*nImages with x;y offset pairs. +// If background_rgba is NULL, the background is transparent. +LIBHEIF_API +struct heif_error heif_context_add_overlay_image(struct heif_context* ctx, + uint32_t image_width, + uint32_t image_height, + uint16_t nImages, + const heif_item_id* image_ids, + int32_t* offsets, + const uint16_t background_rgba[4], + struct heif_image_handle** out_iovl_image_handle); + LIBHEIF_API struct heif_error heif_context_set_primary_image(struct heif_context*, struct heif_image_handle* image_handle); @@ -2170,16 +2476,6 @@ struct heif_error heif_context_encode_thumbnail(struct heif_context*, int bbox_size, struct heif_image_handle** out_thumb_image_handle); -enum heif_metadata_compression -{ - heif_metadata_compression_off = 0, - heif_metadata_compression_auto = 1, - heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file - heif_metadata_compression_deflate = 3, - heif_metadata_compression_zlib = 4, // do not use for header data - heif_metadata_compression_brotli = 5 -}; - // Assign 'thumbnail_image' as the thumbnail image of 'master_image'. LIBHEIF_API struct heif_error heif_context_assign_thumbnail(struct heif_context*, diff --git a/libheif/linux_build_libs.py b/libheif/linux_build_libs.py index 45e4905d..653a2d70 100644 --- a/libheif/linux_build_libs.py +++ b/libheif/linux_build_libs.py @@ -6,7 +6,7 @@ from re import IGNORECASE, MULTILINE, match, search from subprocess import DEVNULL, PIPE, STDOUT, CalledProcessError, TimeoutExpired, run -# 1 +# 0 BUILD_DIR = environ.get("BUILD_DIR", "/tmp/ph_build_stuff") INSTALL_DIR_LIBS = environ.get("INSTALL_DIR_LIBS", "/usr") PH_LIGHT_VERSION = sys.maxsize <= 2**32 or getenv("PH_LIGHT_ACTION", "0") != "0" @@ -14,7 +14,7 @@ LIBX265_URL = "https://bitbucket.org/multicoreware/x265_git/get/0b75c44c10e605fe9e9ebed58f04a46271131827.tar.gz" LIBAOM_URL = "https://aomedia.googlesource.com/aom/+archive/v3.6.1.tar.gz" LIBDE265_URL = "https://github.com/strukturag/libde265/releases/download/v1.0.15/libde265-1.0.15.tar.gz" -LIBHEIF_URL = "https://github.com/strukturag/libheif/releases/download/v1.18.2/libheif-1.18.2.tar.gz" +LIBHEIF_URL = "https://github.com/strukturag/libheif/releases/download/v1.19.5/libheif-1.19.5.tar.gz" def download_file(url: str, out_path: str) -> bool: @@ -212,7 +212,8 @@ def build_lib_linux(url: str, name: str): "-DWITH_OpenJPEG_ENCODER=OFF " "-DENABLE_PLUGIN_LOADING=OFF " "-DWITH_LIBSHARPYUV=OFF " - "-DWITH_EXAMPLES=OFF".split() + "-DWITH_EXAMPLES=OFF " + "-DBUILD_TESTING=OFF".split() ) _hide_build_process = False if is_musllinux(): diff --git a/libheif/macos/libheif.rb b/libheif/macos/libheif.rb index 1133cd4b..8e191011 100644 --- a/libheif/macos/libheif.rb +++ b/libheif/macos/libheif.rb @@ -3,19 +3,22 @@ class Libheif < Formula desc "ISO/IEC 23008-12:2017 HEIF file format decoder and encoder" homepage "https://www.libde265.org/" - url "https://github.com/strukturag/libheif/releases/download/v1.18.2/libheif-1.18.2.tar.gz" - sha256 "c4002a622bec9f519f29d84bfdc6024e33fd67953a5fb4dc2c2f11f67d5e45bf" + url "https://github.com/strukturag/libheif/releases/download/v1.19.5/libheif-1.19.5.tar.gz" + sha256 "d3cf0a76076115a070f9bc87cf5259b333a1f05806500045338798486d0afbaf" license "LGPL-3.0-only" # Set current revision from what it was taken plus 10 revision 10 depends_on "cmake" => :build depends_on "pkg-config" => :build + depends_on "aom" depends_on "jpeg-turbo" depends_on "libde265" depends_on "libpng" + depends_on "libtiff" depends_on "shared-mime-info" + depends_on "webp" depends_on "x265" def install @@ -47,8 +50,10 @@ def install -DWITH_OpenJPEG_ENCODER=OFF -DENABLE_PLUGIN_LOADING=OFF -DWITH_LIBSHARPYUV=OFF + -DWITH_GDK_PIXBUF=OFF -DCMAKE_INSTALL_RPATH=#{rpath} ] + system "cmake", "-S", ".", "-B", "build", *args, *std_cmake_args system "cmake", "--build", "build" system "cmake", "--install", "build" diff --git a/libheif/windows/mingw-w64-libheif/PKGBUILD b/libheif/windows/mingw-w64-libheif/PKGBUILD index 59dbea2b..e15191aa 100644 --- a/libheif/windows/mingw-w64-libheif/PKGBUILD +++ b/libheif/windows/mingw-w64-libheif/PKGBUILD @@ -4,11 +4,11 @@ _realname=libheif pkgbase=mingw-w64-${_realname} pkgname=("${MINGW_PACKAGE_PREFIX}-${_realname}") -pkgver=1.18.2 +pkgver=1.19.5 pkgrel=1 pkgdesc="HEIF image decoder/encoder library and tools (mingw-w64)" arch=('any') -mingw_arch=('mingw32' 'mingw64' 'ucrt64' 'clang64' 'clang32' 'clangarm64') +mingw_arch=('mingw32' 'mingw64' 'ucrt64' 'clang64' 'clangarm64') url="https://github.com/strukturag/libheif" license=('spdx:LGPL-3.0' 'spdx:MIT') makedepends=("${MINGW_PACKAGE_PREFIX}-cmake" @@ -20,7 +20,7 @@ depends=("${MINGW_PACKAGE_PREFIX}-gcc-libs" "${MINGW_PACKAGE_PREFIX}-libde265" "${MINGW_PACKAGE_PREFIX}-x265") source=("https://github.com/strukturag/libheif/releases/download/v${pkgver}/${_realname}-${pkgver}.tar.gz") -sha256sums=('c4002a622bec9f519f29d84bfdc6024e33fd67953a5fb4dc2c2f11f67d5e45bf') +sha256sums=('d3cf0a76076115a070f9bc87cf5259b333a1f05806500045338798486d0afbaf') build() { mkdir -p "${srcdir}"/build-${MSYSTEM} && cd "${srcdir}"/build-${MSYSTEM} @@ -63,6 +63,7 @@ build() { -DENABLE_PLUGIN_LOADING=OFF \ -DWITH_LIBSHARPYUV=OFF \ -DWITH_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ -DX265_CFLAGS="-DX265_API_IMPORTS" \ ../${_realname}-${pkgver} diff --git a/pi-heif/libheif/macos/libheif.rb b/pi-heif/libheif/macos/libheif.rb index b462267c..eb74602f 100644 --- a/pi-heif/libheif/macos/libheif.rb +++ b/pi-heif/libheif/macos/libheif.rb @@ -3,14 +3,15 @@ class Libheif < Formula desc "ISO/IEC 23008-12:2017 HEIF file format decoder and encoder" homepage "https://www.libde265.org/" - url "https://github.com/strukturag/libheif/releases/download/v1.18.2/libheif-1.18.2.tar.gz" - sha256 "c4002a622bec9f519f29d84bfdc6024e33fd67953a5fb4dc2c2f11f67d5e45bf" + url "https://github.com/strukturag/libheif/releases/download/v1.19.5/libheif-1.19.5.tar.gz" + sha256 "d3cf0a76076115a070f9bc87cf5259b333a1f05806500045338798486d0afbaf" license "LGPL-3.0-only" # Set current revision from what it was taken plus 10 revision 10 depends_on "cmake" => :build depends_on "pkg-config" => :build + depends_on "libde265" def install @@ -42,9 +43,11 @@ def install -DWITH_OpenJPEG_ENCODER=OFF -DENABLE_PLUGIN_LOADING=OFF -DWITH_LIBSHARPYUV=OFF + -DWITH_GDK_PIXBUF=OFF -DWITH_EXAMPLES=OFF -DCMAKE_INSTALL_RPATH=#{rpath} ] + system "cmake", "-S", ".", "-B", "build", *args, *std_cmake_args system "cmake", "--build", "build" system "cmake", "--install", "build" diff --git a/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD b/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD index 0ed26c59..89bd3ae2 100644 --- a/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD +++ b/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD @@ -4,11 +4,11 @@ _realname=libheif pkgbase=mingw-w64-${_realname} pkgname=("${MINGW_PACKAGE_PREFIX}-${_realname}") -pkgver=1.18.2 +pkgver=1.19.5 pkgrel=1 pkgdesc="HEIF image decoder/encoder library and tools (mingw-w64)" arch=('any') -mingw_arch=('mingw32' 'mingw64' 'ucrt64' 'clang64' 'clang32' 'clangarm64') +mingw_arch=('mingw32' 'mingw64' 'ucrt64' 'clang64' 'clangarm64') url="https://github.com/strukturag/libheif" license=('spdx:LGPL-3.0' 'spdx:MIT') makedepends=("${MINGW_PACKAGE_PREFIX}-cmake" @@ -18,7 +18,7 @@ makedepends=("${MINGW_PACKAGE_PREFIX}-cmake" depends=("${MINGW_PACKAGE_PREFIX}-gcc-libs" "${MINGW_PACKAGE_PREFIX}-libde265") source=("https://github.com/strukturag/libheif/releases/download/v${pkgver}/${_realname}-${pkgver}.tar.gz") -sha256sums=('c4002a622bec9f519f29d84bfdc6024e33fd67953a5fb4dc2c2f11f67d5e45bf') +sha256sums=('d3cf0a76076115a070f9bc87cf5259b333a1f05806500045338798486d0afbaf') build() { mkdir -p "${srcdir}"/build-${MSYSTEM} && cd "${srcdir}"/build-${MSYSTEM} @@ -60,6 +60,7 @@ build() { -DENABLE_PLUGIN_LOADING=OFF \ -DWITH_LIBSHARPYUV=OFF \ -DWITH_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ ../${_realname}-${pkgver} ${MINGW_PREFIX}/bin/cmake --build . diff --git a/tests/basic_test.py b/tests/basic_test.py index cc461366..9c142c77 100644 --- a/tests/basic_test.py +++ b/tests/basic_test.py @@ -17,7 +17,7 @@ def test_libheif_info(): assert key in info version = pillow_heif.libheif_version() - valid_prefixes = ["1.17.", "1.18."] + valid_prefixes = ["1.17.", "1.18.", "1.19."] assert any(version.startswith(prefix) for prefix in valid_prefixes) diff --git a/tests/images/heif/xmp.heif b/tests/images/heif/L_xmp.heif similarity index 100% rename from tests/images/heif/xmp.heif rename to tests/images/heif/L_xmp.heif diff --git a/tests/images/heif_other/xmp_latin1.heic b/tests/images/heif_other/L_xmp_latin1.heic similarity index 100% rename from tests/images/heif_other/xmp_latin1.heic rename to tests/images/heif_other/L_xmp_latin1.heic diff --git a/tests/metadata_xmp_test.py b/tests/metadata_xmp_test.py index 20e5e988..753db9b8 100644 --- a/tests/metadata_xmp_test.py +++ b/tests/metadata_xmp_test.py @@ -110,7 +110,7 @@ def test_heif_xmp_add_remove(): @pytest.mark.skipif(not helpers.hevc_enc(), reason="Requires HEVC encoder.") def test_heif_xmp_latin1_with_zero_byte(): - im = Image.open("images/heif_other/xmp_latin1.heic") + im = Image.open("images/heif_other/L_xmp_latin1.heic") out_heif = BytesIO() im.save(out_heif, format="HEIF") out_im = Image.open(out_heif) diff --git a/tests/write_test.py b/tests/write_test.py index a9c5ab06..407ba172 100644 --- a/tests/write_test.py +++ b/tests/write_test.py @@ -180,7 +180,7 @@ def test_hdr_save(im_path, save_format): heif_file.save(out_buf, quality=-1, format=save_format, chroma=444) heif_file_out = pillow_heif.open_heif(out_buf, convert_hdr_to_8bit=False) helpers.compare_heif_files_fields(heif_file, heif_file_out) - helpers.compare_hashes([im_path, out_buf], hash_size=32) + helpers.compare_hashes([im_path, out_buf], hash_size=16) # was 32 before libheif 1.19 version def test_encoder_parameters(): @@ -188,8 +188,8 @@ def test_encoder_parameters(): out_buf2 = BytesIO() heif_buf = helpers.create_heif() im_heif = pillow_heif.open_heif(heif_buf) - im_heif.save(out_buf1, enc_params={"x265:ctu": "32", "x265:min-cu-size": "16", "x265:rdLevel": "2"}) - im_heif.save(out_buf2, enc_params={"x265:ctu": 64, "x265:min-cu-size": 8, "x265:rdLevel": 6}) + im_heif.save(out_buf1, enc_params={"x265:ctu": "32", "x265:min-cu-size": "16"}) + im_heif.save(out_buf2, enc_params={"x265:ctu": 64, "x265:min-cu-size": 8}) assert out_buf1.seek(0, SEEK_END) != out_buf2.seek(0, SEEK_END) @@ -275,7 +275,7 @@ def test_CMYK_color_mode(): # noqa helpers.compare_hashes([im, im_heif], hash_size=16) -@pytest.mark.parametrize("subsampling, expected_max_difference", (("4:4:4", 0.0004), ("4:2:2", 0.11), ("4:2:0", 1.33))) +@pytest.mark.parametrize("subsampling, expected_max_difference", (("4:4:4", 0.0004), ("4:2:2", 0.11), ("4:2:0", 1.4))) @pytest.mark.parametrize("save_format", ("HEIF", "AVIF")) def test_YCbCr_color_mode( save_format,