libheif-1.19.8/README.md 000664 001750 001750 00000041165 15003473471 015606 0 ustar 00farindk farindk 000000 000000 # libheif
[](https://github.com/strukturag/libheif/actions) [](https://ci.appveyor.com/project/strukturag/libheif) [](https://scan.coverity.com/projects/strukturag-libheif)
libheif is an ISO/IEC 23008-12:2017 HEIF and AVIF (AV1 Image File Format) file format decoder and encoder.
There is partial support for ISO/IEC 23008-12:2022 (2nd Edition) capabilities.
HEIF and AVIF are new image file formats employing HEVC (H.265) or AV1 image coding, respectively, for the
best compression ratios currently possible.
libheif makes use of [libde265](https://github.com/strukturag/libde265) for HEIF image decoding and x265 for encoding.
For AVIF, libaom, dav1d, svt-av1, or rav1e are used as codecs.
## Supported features
libheif has support for:
* HEIC, AVIF, VVC, AVC, JPEG-in-HEIF, JPEG2000, uncompressed (ISO/IEC 23001-17:2024) codecs
* alpha channels, depth maps, thumbnails, auxiliary images
* multiple images in a file
* tiled images with decoding individual tiles and encoding tiled images by adding tiles one after another
* HDR images, correct color transform according to embedded color profiles
* image transformations (crop, mirror, rotate), overlay images
* plugin interface to add alternative codecs
* reading EXIF and XMP metadata
* region annotations and mask images
* decoding of files while downloading (e.g. extract image size before file has been completely downloaded)
Supported codecs:
| Format | Decoders | Encoders |
|:-------------|:-------------------:|:----------------------------:|
| HEIC | libde265, ffmpeg | x265, kvazaar |
| AVIF | AOM, dav1d | AOM, rav1e, svt-av1 |
| VVC | vvdec | vvenc, uvg266 |
| AVC | openh264 | - |
| JPEG | libjpeg(-turbo) | libjpeg(-turbo) |
| JPEG2000 | OpenJPEG | OpenJPEG |
| HTJ2K | OpenJPEG | OpenJPH |
| uncompressed | built-in | built-in |
## API
The library has a C API for easy integration and wide language support.
The decoder automatically supports both HEIF and AVIF (and the other compression formats) through the same API. The same decoding code can be used to decode any of them.
The encoder can be switched between HEIF and AVIF simply by setting `heif_compression_HEVC` or `heif_compression_AV1`
to `heif_context_get_encoder_for_format()`, or using any of the other compression formats.
Loading the primary image in an HEIF file is as easy as this:
```C
heif_context* ctx = heif_context_alloc();
heif_context_read_from_file(ctx, input_filename, nullptr);
// get a handle to the primary image
heif_image_handle* handle;
heif_context_get_primary_image_handle(ctx, &handle);
// decode the image and convert colorspace to RGB, saved as 24bit interleaved
heif_image* img;
heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_RGB, nullptr);
int stride;
const uint8_t* data = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
// ... process data as needed ...
// clean up resources
heif_image_release(img);
heif_image_handle_release(handle);
heif_context_free(ctx);
```
Writing an HEIF file can be done like this:
```C
heif_context* ctx = heif_context_alloc();
// get the default encoder
heif_encoder* encoder;
heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
// set the encoder parameters
heif_encoder_set_lossy_quality(encoder, 50);
// encode the image
heif_image* image; // code to fill in the image omitted in this example
heif_context_encode_image(ctx, image, encoder, nullptr, nullptr);
heif_encoder_release(encoder);
heif_context_write_to_file(ctx, "output.heic");
heif_context_free(ctx);
```
Get the EXIF data from an HEIF file:
```C
heif_item_id exif_id;
int n = heif_image_handle_get_list_of_metadata_block_IDs(image_handle, "Exif", &exif_id, 1);
if (n==1) {
size_t exifSize = heif_image_handle_get_metadata_size(image_handle, exif_id);
uint8_t* exifData = malloc(exifSize);
struct heif_error error = heif_image_handle_get_metadata(image_handle, exif_id, exifData);
}
```
See the header file `heif.h` for the complete C API.
There is also a C++ API which is a header-only wrapper to the C API.
Hence, you can use the C++ API and still be binary compatible.
Code using the C++ API is much less verbose than using the C API directly.
### Reading and Writing Tiled Images
For very large resolution images, it is not always feasible to process the whole image.
In this case, `libheif` can process the image tile by tile.
See [this tutorial](https://github.com/strukturag/libheif/wiki/Reading-and-Writing-Tiled-Images) on how to use the API for this.
## Compiling
This library uses the CMake build system (the earlier autotools build files have been removed in v1.16.0).
For a minimal configuration, we recommend to use the codecs libde265 and x265 for HEIC and AOM for AVIF.
Make sure that you compile and install [libde265](https://github.com/strukturag/libde265)
first, so that the configuration script will find this.
Also install x265 and its development files if you want to use HEIF encoding, but note that x265 is GPL.
An alternative to x265 is kvazaar (BSD).
The basic build steps are as follows (--preset argument needs CMake >= 3.21):
````sh
mkdir build
cd build
cmake --preset=release ..
make
````
There are CMake presets to cover the most frequent use cases.
* `release`: the preferred preset which compiles all codecs as separate plugins.
If you do not want to distribute some of these plugins (e.g. HEIC), you can omit packaging these.
* `release-noplugins`: this is a smaller, self-contained build of libheif without using the plugin system.
A single library is built with support for HEIC and AVIF.
* `testing`: for building and executing the unit tests. Also the internal library symbols are exposed. Do not use for distribution.
* `fuzzing`: all codecs like in release build, but configured into a self-contained library with enabled fuzzers. The library should not distributed.
You can optionally adapt these standard configurations to your needs.
This can be done, for example, by calling `ccmake .` from within the `build` directory.
### CMake configuration variables
Libheif supports many different codecs. In order to reduce the number of dependencies and the library size,
you can choose which of these codecs to include. Each codec can be compiled either as built-in to the library
with a hard dependency, or as a separate plugin file that is loaded dynamically.
For each codec, there are two configuration variables:
* `WITH_{codec}`: enables the codec
* `WITH_{codec}_PLUGIN`: when enabled, the codec is compiled as a separate plugin.
In order to use dynamic plugins, also make sure that `ENABLE_PLUGIN_LOADING` is enabled.
The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `VVDEC`, `VVENC`, `UVG266`.
Further options are:
* `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2024. This is *experimental*
and not available as a dynamic plugin. When enabled, it adds a dependency to `zlib`, and optionally will use `brotli`.
* `WITH_HEADER_COMPRESSION`: enables support for compressed metadata. When enabled, it adds a dependency to `zlib`.
Note that header compression is not widely supported yet.
* `WITH_LIBSHARPYUV`: enables high-quality YCbCr/RGB color space conversion algorithms (requires `libsharpyuv`,
e.g. from the `third-party` directory).
* `ENABLE_EXPERIMENTAL_FEATURES`: enables functions that are currently in development and for which the API is not stable yet.
When this is enabled, a header `heif_experimental.h` will be installed that contains this unstable API.
Distributions that rely on a stable API should not enable this.
* `ENABLE_MULTITHREADING_SUPPORT`: can be used to disable any multithreading support, e.g. for embedded platforms.
* `ENABLE_PARALLEL_TILE_DECODING`: when enabled, libheif will decode tiled images in parallel to speed up compilation.
* `PLUGIN_DIRECTORY`: the directory where libheif will search for dynamic plugins when the environment
variable `LIBHEIF_PLUGIN_PATH` is not set.
* `WITH_REDUCED_VISIBILITY`: only export those symbols into the library that are public API.
Has to be turned off for running some tests.
### macOS
1. Install dependencies with Homebrew
```sh
brew install cmake make pkg-config x265 libde265 libjpeg libtool
```
2. Configure and build project (--preset argument needs CMake >= 3.21):
```sh
mkdir build
cd build
cmake --preset=release ..
./configure
make
```
### Windows
You can build and install libheif using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
```sh
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.bat
./vcpkg integrate install
./vcpkg install libheif
```
The libheif port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
### Adding libaom encoder/decoder for AVIF
* Run the `aom.cmd` script in the `third-party` directory to download libaom and
compile it.
When running `cmake` or `configure`, make sure that the environment variable
`PKG_CONFIG_PATH` includes the absolute path to `third-party/aom/dist/lib/pkgconfig`.
### Adding rav1e encoder for AVIF
* Install `cargo`.
* Install `cargo-c` by executing
```sh
cargo install --force cargo-c
```
* Run the `rav1e.cmd` script in the `third-party` directory to download rav1e
and compile it.
When running `cmake`, make sure that the environment variable
`PKG_CONFIG_PATH` includes the absolute path to `third-party/rav1e/dist/lib/pkgconfig`.
### Adding dav1d decoder for AVIF
* Install [`meson`](https://mesonbuild.com/).
* Run the `dav1d.cmd` script in the `third-party` directory to download dav1d
and compile it.
When running `cmake`, make sure that the environment variable
`PKG_CONFIG_PATH` includes the absolute path to `third-party/dav1d/dist/lib/x86_64-linux-gnu/pkgconfig`.
### Adding SVT-AV1 encoder for AVIF
You can either use the SVT-AV1 encoder libraries installed in the system or use a self-compiled current version.
If you want to compile SVT-AV1 yourself,
* Run the `svt.cmd` script in the `third-party` directory to download SVT-AV1
and compile it.
When running `cmake` or `configure`, make sure that the environment variable
`PKG_CONFIG_PATH` includes the absolute path to `third-party/SVT-AV1/Build/linux/install/lib/pkgconfig`.
You may have to replace `linux` in this path with your system's identifier.
You have to enable SVT-AV1 with CMake.
## Codec plugins
Starting with v1.14.0, each codec backend can be compiled statically into libheif or as a dynamically loaded plugin.
You can choose this individually for each codec backend in the CMake settings.
Compiling a codec backend as dynamic plugin will generate a shared library that is installed in the system together with libheif.
The advantage is that only the required plugins have to be installed and libheif has fewer dependencies.
The plugins are loaded from the colon-separated (semicolon-separated on Windows) list of directories stored in the environment variable `LIBHEIF_PLUGIN_PATH`.
If this variable is empty, they are loaded from a directory specified in the CMake configuration.
You can also add plugin directories programmatically.
### Codec specific notes
* the FFMPEG decoding plugin can make use of h265 hardware decoders. However, it currently (v1.17.0, ffmpeg v4.4.2) does not work
correctly with all streams. Thus, libheif still prefers the libde265 decoder if it is available.
## Encoder benchmark
A current benchmark of the AVIF encoders (as of 14 Oct 2022) can be found on the Wiki page
[AVIF encoding benchmark](https://github.com/strukturag/libheif/wiki/AVIF-Encoder-Benchmark).
## Language bindings
* .NET Platform (C#, F#, and other languages): [libheif-sharp](https://github.com/0xC0000054/libheif-sharp)
* C++: part of libheif
* Go: [libheif-go](https://github.com/strukturag/libheif-go), the wrapper distributed with libheif is deprecated
* JavaScript: by compilation with emscripten (see below)
* NodeJS module: [libheif-js](https://www.npmjs.com/package/libheif-js)
* Python: [pyheif](https://pypi.org/project/pyheif/), [pillow_heif](https://pypi.org/project/pillow-heif/)
* Rust: [libheif-sys](https://github.com/Cykooz/libheif-sys)
* Swift: [libheif-Xcode](https://swiftpackageregistry.com/SDWebImage/libheif-Xcode)
* JavaFX: [LibHeifFx](https://github.com/lanthale/LibHeifFX)
Languages that can directly interface with C libraries (e.g., Swift, C#) should work out of the box.
## Compiling to JavaScript / WASM
libheif can also be compiled to JavaScript using
[emscripten](http://kripken.github.io/emscripten-site/).
It can be built like this (in the libheif directory):
````
mkdir buildjs
cd buildjs
USE_WASM=0 ../build-emscripten.sh ..
````
Set `USE_WASM=1` to build with WASM output.
See the `build-emscripten.sh` script for further options.
## Online demo
Check out this [online demo](https://strukturag.github.io/libheif/).
This is `libheif` running in JavaScript in your browser.
## Example programs
Some example programs are provided in the `examples` directory.
The program `heif-dec` converts all images stored in an HEIF/AVIF file to JPEG or PNG.
`heif-enc` lets you convert JPEG files to HEIF/AVIF.
The program `heif-info` is a simple, minimal decoder that dumps the file structure to the console.
For example convert `example.heic` to JPEGs and one of the JPEGs back to HEIF:
```sh
cd examples/
./heif-dec example.heic example.jpeg
./heif-enc example-1.jpeg -o example.heif
```
In order to convert `example-1.jpeg` to AVIF use:
```sh
./heif-enc example-1.jpeg -A -o example.avif
```
There is also a GIMP plugin using libheif [here](https://github.com/strukturag/heif-gimp-plugin).
## HEIF/AVIF thumbnails for the Gnome desktop
The program `heif-thumbnailer` can be used as an HEIF/AVIF thumbnailer for the Gnome desktop.
The matching Gnome configuration files are in the `gnome` directory.
Place the files `heif.xml` and `avif.xml` into `/usr/share/mime/packages` and `heif.thumbnailer` into `/usr/share/thumbnailers`.
You may have to run `update-mime-database /usr/share/mime` to update the list of known MIME types.
## gdk-pixbuf loader
libheif also includes a gdk-pixbuf loader for HEIF/AVIF images. 'make install' will copy the plugin
into the system directories. However, you will still have to run `gdk-pixbuf-query-loaders --update-cache`
to update the gdk-pixbuf loader database.
## Software using libheif
* [GIMP](https://www.gimp.org/)
* [Krita](https://krita.org)
* [ImageMagick](https://imagemagick.org/)
* [GraphicsMagick](http://www.graphicsmagick.org/)
* [darktable](https://www.darktable.org)
* [digiKam 7.0.0](https://www.digikam.org/)
* [libvips](https://github.com/libvips/libvips)
* [kImageFormats](https://api.kde.org/frameworks/kimageformats/html/index.html)
* [libGD](https://libgd.github.io/)
* [Kodi HEIF image decoder plugin](https://kodi.wiki/view/Add-on:HEIF_image_decoder)
* [bimg](https://github.com/h2non/bimg)
* [GDAL](https://gdal.org/drivers/raster/heif.html)
* [OpenImageIO](https://sites.google.com/site/openimageio/)
* [XnView](https://www.xnview.com)
## Packaging status
[](https://repology.org/project/libheif/versions)
## Sponsors
Since I work as an independent developer, I need your support to be able to allocate time for libheif.
You can [sponsor](https://github.com/sponsors/farindk) the development using the link in the right hand column.
A big thank you goes to these major sponsors for supporting the development of libheif:
* Pinterest
* Shopify
* StrukturAG
## License
The libheif is distributed under the terms of the GNU Lesser General Public License.
The sample applications are distributed under the terms of the MIT License.
See COPYING for more details.
Copyright (c) 2017-2020 Struktur AG
Copyright (c) 2017-2025 Dirk Farin
Contact: Dirk Farin
libheif-1.19.8/CPPLINT.cfg 000664 001750 001750 00000000062 15003473471 016110 0 ustar 00farindk farindk 000000 000000 set noparent
filter=-,+build/include_what_you_use
libheif-1.19.8/COPYING 000664 001750 001750 00000126516 15003473471 015366 0 ustar 00farindk farindk 000000 000000 * The library `libheif` is distributed under the terms of the GNU Lesser General Public License.
* The sample applications and the Go and C++ wrappers are distributed under the terms of the MIT License.
License texts below and in the `COPYING` files of the corresponding subfolders.
----------------------------------------------------------------------
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
----------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
----------------------------------------------------------------------
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
libheif-1.19.8/libheif/ 000775 001750 001750 00000000000 15003473472 015723 5 ustar 00farindk farindk 000000 000000 libheif-1.19.8/libheif/security_limits.h 000664 001750 001750 00000003076 15003473471 021331 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2018 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#ifndef LIBHEIF_SECURITY_LIMITS_H
#define LIBHEIF_SECURITY_LIMITS_H
#include "libheif/heif.h"
#include
#include
#include "error.h"
extern heif_security_limits global_security_limits;
extern heif_security_limits disabled_security_limits;
// Maximum nesting level of boxes in input files.
// We put a limit on this to avoid unlimited stack usage by malicious input files.
static const int MAX_BOX_NESTING_LEVEL = 20;
static const int MAX_BOX_SIZE = 0x7FFFFFFF; // 2 GB
static const int64_t MAX_LARGE_BOX_SIZE = 0x0FFFFFFFFFFFFFFF;
static const int64_t MAX_FILE_POS = 0x007FFFFFFFFFFFFFLL; // maximum file position
static const int MAX_FRACTION_VALUE = 0x10000;
Error check_for_valid_image_size(const heif_security_limits* limits, uint32_t width, uint32_t height);
#endif // LIBHEIF_SECURITY_LIMITS_H
libheif-1.19.8/libheif/nclx.h 000664 001750 001750 00000012321 15003473471 017036 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2020 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#ifndef LIBHEIF_NCLX_H
#define LIBHEIF_NCLX_H
#include "box.h"
#include
#include
#include
#include
struct primaries
{
primaries() = default;
primaries(float gx, float gy, float bx, float by, float rx, float ry, float wx, float wy);
bool defined = false;
float greenX = 0, greenY = 0;
float blueX = 0, blueY = 0;
float redX = 0, redY = 0;
float whiteX = 0, whiteY = 0;
};
primaries get_colour_primaries(uint16_t primaries_idx);
struct Kr_Kb
{
float Kr = 0, Kb = 0;
static Kr_Kb defaults();
};
Kr_Kb get_Kr_Kb(uint16_t matrix_coefficients_idx, uint16_t primaries_idx);
struct YCbCr_to_RGB_coefficients
{
bool defined = false;
float r_cr = 0;
float g_cb = 0;
float g_cr = 0;
float b_cb = 0;
static YCbCr_to_RGB_coefficients defaults();
};
YCbCr_to_RGB_coefficients get_YCbCr_to_RGB_coefficients(uint16_t matrix_coefficients_idx, uint16_t primaries_idx);
struct RGB_to_YCbCr_coefficients
{
bool defined = false;
float c[3][3] = {{0, 0, 0},
{0, 0, 0},
{0, 0, 0}}; // e.g. y = c[0][0]*r + c[0][1]*g + c[0][2]*b
static RGB_to_YCbCr_coefficients defaults();
};
RGB_to_YCbCr_coefficients get_RGB_to_YCbCr_coefficients(uint16_t matrix_coefficients_idx, uint16_t primaries_idx);
// uint16_t get_transfer_characteristics() const {return m_transfer_characteristics;}
// uint16_t get_matrix_coefficients() const {return m_matrix_coefficients;}
// bool get_full_range_flag() const {return m_full_range_flag;}
class color_profile
{
public:
virtual ~color_profile() = default;
virtual uint32_t get_type() const = 0;
virtual std::string dump(Indent&) const = 0;
virtual Error write(StreamWriter& writer) const = 0;
};
class color_profile_raw : public color_profile
{
public:
color_profile_raw(uint32_t type, const std::vector& data)
: m_type(type), m_data(data) {}
uint32_t get_type() const override { return m_type; }
const std::vector& get_data() const { return m_data; }
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
private:
uint32_t m_type;
std::vector m_data;
};
class color_profile_nclx : public color_profile
{
public:
color_profile_nclx() { set_sRGB_defaults(); }
uint32_t get_type() const override { return fourcc("nclx"); }
std::string dump(Indent&) const override;
Error parse(BitstreamRange& range);
Error write(StreamWriter& writer) const override;
uint16_t get_colour_primaries() const { return m_colour_primaries; }
uint16_t get_transfer_characteristics() const { return m_transfer_characteristics; }
uint16_t get_matrix_coefficients() const { return m_matrix_coefficients; }
bool get_full_range_flag() const { return m_full_range_flag; }
void set_colour_primaries(uint16_t primaries) { m_colour_primaries = primaries; }
void set_transfer_characteristics(uint16_t characteristics) { m_transfer_characteristics = characteristics; }
void set_matrix_coefficients(uint16_t coefficients) { m_matrix_coefficients = coefficients; }
void set_full_range_flag(bool full_range) { m_full_range_flag = full_range; }
void set_sRGB_defaults();
void set_undefined();
Error get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const;
static struct heif_color_profile_nclx* alloc_nclx_color_profile();
static void free_nclx_color_profile(struct heif_color_profile_nclx* profile);
void set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx);
void replace_undefined_values_with_sRGB_defaults();
private:
uint16_t m_colour_primaries = heif_color_primaries_unspecified;
uint16_t m_transfer_characteristics = heif_transfer_characteristic_unspecified;
uint16_t m_matrix_coefficients = heif_matrix_coefficients_unspecified;
bool m_full_range_flag = true;
};
class Box_colr : public Box
{
public:
Box_colr()
{
set_short_type(fourcc("colr"));
}
std::string dump(Indent&) const override;
uint32_t get_color_profile_type() const { return m_color_profile->get_type(); }
const std::shared_ptr& get_color_profile() const { return m_color_profile; }
void set_color_profile(const std::shared_ptr& prof) { m_color_profile = prof; }
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
private:
std::shared_ptr m_color_profile;
};
#endif //LIBHEIF_NCLX_H
libheif-1.19.8/libheif/bitstream.h 000664 001750 001750 00000030106 15003473471 020065 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2017 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#ifndef LIBHEIF_BITSTREAM_H
#define LIBHEIF_BITSTREAM_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "error.h"
#include
class StreamReader
{
public:
virtual ~StreamReader() = default;
virtual uint64_t get_position() const = 0;
enum class grow_status : uint8_t
{
size_reached, // requested size has been reached
timeout, // size has not been reached yet, but it may still grow further
size_beyond_eof // size has not been reached and never will. The file has grown to its full size
};
// a StreamReader can maintain a timeout for waiting for new data
virtual grow_status wait_for_file_size(uint64_t target_size) = 0;
// returns 'false' when we read out of the available file size
virtual bool read(void* data, size_t size) = 0;
virtual bool seek(uint64_t position) = 0;
bool seek_cur(uint64_t position_offset)
{
return seek(get_position() + position_offset);
}
// Informs the reader implementation that we will process data in the given range.
// The reader can use this information to retrieve a larger chunk of data instead of individual read() calls.
// Returns the file size that was made available, but you still have to check each read() call.
// Returning a value shorter than the requested range end indicates to libheif that the data is not available.
// Returns 0 on error.
virtual uint64_t request_range(uint64_t start, uint64_t end_pos) {
return std::numeric_limits::max();
}
virtual void release_range(uint64_t start, uint64_t end_pos) { }
virtual void preload_range_hint(uint64_t start, uint64_t end_pos) { }
Error get_error() const {
return m_last_error;
}
void clear_last_error() { m_last_error = {}; }
protected:
Error m_last_error;
};
#include
class StreamReader_istream : public StreamReader
{
public:
StreamReader_istream(std::unique_ptr&& istr);
uint64_t get_position() const override;
grow_status wait_for_file_size(uint64_t target_size) override;
bool read(void* data, size_t size) override;
bool seek(uint64_t position) override;
uint64_t request_range(uint64_t start, uint64_t end_pos) override {
// std::cout << "[istream] request_range " << start << " - " << end_pos << "\n";
return std::min(end_pos, m_length);
}
void release_range(uint64_t start, uint64_t end_pos) override {
// std::cout << "[istream] release_range " << start << " - " << end_pos << "\n";
}
void preload_range_hint(uint64_t start, uint64_t end_pos) override {
// std::cout << "[istream] preload_range_hint " << start << " - " << end_pos << "\n";
}
private:
std::unique_ptr m_istr;
uint64_t m_length;
};
class StreamReader_memory : public StreamReader
{
public:
StreamReader_memory(const uint8_t* data, size_t size, bool copy);
~StreamReader_memory() override;
uint64_t get_position() const override;
grow_status wait_for_file_size(uint64_t target_size) override;
bool read(void* data, size_t size) override;
bool seek(uint64_t position) override;
// end_pos is last byte to read + 1. I.e. like a file size.
uint64_t request_range(uint64_t start, uint64_t end_pos) override {
return m_length;
}
private:
const uint8_t* m_data;
uint64_t m_length;
uint64_t m_position;
// if we made a copy of the data, we store a pointer to the owned memory area here
uint8_t* m_owned_data = nullptr;
};
class StreamReader_CApi : public StreamReader
{
public:
StreamReader_CApi(const heif_reader* func_table, void* userdata);
uint64_t get_position() const override { return m_func_table->get_position(m_userdata); }
StreamReader::grow_status wait_for_file_size(uint64_t target_size) override;
bool read(void* data, size_t size) override { return !m_func_table->read(data, size, m_userdata); }
bool seek(uint64_t position) override { return !m_func_table->seek(position, m_userdata); }
uint64_t request_range(uint64_t start, uint64_t end_pos) override {
if (m_func_table->reader_api_version >= 2) {
heif_reader_range_request_result result = m_func_table->request_range(start, end_pos, m_userdata);
// convert error message string and release input string memory
std::string error_msg;
if (result.reader_error_msg) {
error_msg = std::string{result.reader_error_msg};
if (m_func_table->release_error_msg) {
m_func_table->release_error_msg(result.reader_error_msg);
}
}
switch (result.status) {
case heif_reader_grow_status_size_reached:
return end_pos;
case heif_reader_grow_status_timeout:
return 0; // invalid return value from callback
case heif_reader_grow_status_size_beyond_eof:
m_last_error = {heif_error_Invalid_input, heif_suberror_End_of_data, "Read beyond file size"};
return result.range_end;
case heif_reader_grow_status_error: {
if (result.reader_error_msg) {
std::stringstream sstr;
sstr << "Input error (" << result.reader_error_code << ") : " << error_msg;
m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
}
else {
std::stringstream sstr;
sstr << "Input error (" << result.reader_error_code << ")";
m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
}
return 0; // error occurred
}
default:
m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid input reader return value"};
return 0;
}
}
else {
return std::numeric_limits::max();
}
}
void release_range(uint64_t start, uint64_t end_pos) override {
if (m_func_table->reader_api_version >= 2) {
m_func_table->release_file_range(start, end_pos, m_userdata);
}
}
void preload_range_hint(uint64_t start, uint64_t end_pos) override {
if (m_func_table->reader_api_version >= 2) {
m_func_table->preload_range_hint(start, end_pos, m_userdata);
}
}
private:
const heif_reader* m_func_table;
void* m_userdata;
};
// This class simplifies safely reading part of a file (e.g. a box).
// It makes sure that we do not read past the boundaries of a box.
class BitstreamRange
{
public:
BitstreamRange(std::shared_ptr istr,
size_t length,
BitstreamRange* parent = nullptr);
BitstreamRange(std::shared_ptr istr,
size_t start,
size_t end); // one past end
// This function tries to make sure that the full data of this range is
// available. You should call this before starting reading the range.
// If you don't, you have to make sure that you do not read past the available data.
StreamReader::grow_status wait_until_range_is_available();
uint8_t read8();
uint16_t read16();
int16_t read16s();
/**
* Read 24 bit unsigned integer from the bitstream.
*
* The data is assumed to be in big endian format and is returned as a 32 bit value.
*/
uint32_t read24();
uint32_t read32();
int32_t read32s();
uint64_t read64();
uint64_t read_uint(int len);
/**
* Read 32 bit floating point value from the bitstream.
*
* The file data is assumed to be in big endian format.
*/
float read_float32();
int64_t read64s();
std::string read_string();
bool read(uint8_t* data, size_t n);
bool prepare_read(size_t nBytes);
StreamReader::grow_status wait_for_available_bytes(size_t nBytes);
void skip_to_end_of_file()
{
// we do not actually move the file position here (because the stream may still be incomplete),
// but we set all m_remaining to zero
m_remaining = 0;
if (m_parent_range) {
m_parent_range->skip_to_end_of_file();
}
}
void skip(uint64_t n)
{
size_t actual_skip = std::min(static_cast(n), m_remaining);
if (m_parent_range) {
// also advance position in parent range
m_parent_range->skip_without_advancing_file_pos(actual_skip);
}
assert(actual_skip <= static_cast(std::numeric_limits::max()));
m_istr->seek_cur(static_cast(actual_skip));
m_remaining -= actual_skip;
}
void skip_to_end_of_box()
{
if (m_remaining > 0) {
if (m_parent_range) {
// also advance position in parent range
m_parent_range->skip_without_advancing_file_pos(m_remaining);
}
m_istr->seek_cur(m_remaining);
m_remaining = 0;
}
}
void set_eof_while_reading()
{
m_remaining = 0;
if (m_parent_range) {
m_parent_range->set_eof_while_reading();
}
m_error = true;
}
bool eof() const
{
return m_remaining == 0;
}
bool error() const
{
return m_error;
}
Error get_error() const
{
if (m_error) {
return Error(heif_error_Invalid_input,
heif_suberror_End_of_data);
}
else {
return Error::Ok;
}
}
std::shared_ptr get_istream() { return m_istr; }
int get_nesting_level() const { return m_nesting_level; }
size_t get_remaining_bytes() const { return m_remaining; }
private:
std::shared_ptr m_istr;
BitstreamRange* m_parent_range = nullptr;
int m_nesting_level = 0;
size_t m_remaining;
bool m_error = false;
// Note: 'nBytes' may not be larger than the number of remaining bytes
void skip_without_advancing_file_pos(size_t nBytes);
};
class BitReader
{
public:
BitReader(const uint8_t* buffer, int len);
uint32_t get_bits(int n);
uint8_t get_bits8(int n);
uint16_t get_bits16(int n);
uint32_t get_bits32(int n);
int32_t get_bits32s();
/**
* Get a one-bit flag value.
*
* @returns true if the next bit value is 1, otherwise false
*/
bool get_flag();
std::vector read_bytes(uint32_t n);
int get_bits_fast(int n);
int peek_bits(int n);
void skip_bytes(int nBytes);
void skip_bits(int n);
void skip_bits_fast(int n);
void skip_to_byte_boundary();
bool get_uvlc(int* value);
bool get_svlc(int* value);
int get_current_byte_index() const
{
return data_length - bytes_remaining - nextbits_cnt / 8;
}
int64_t get_bits_remaining() const
{
return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
}
private:
const uint8_t* data;
int data_length;
int bytes_remaining;
uint64_t nextbits; // left-aligned bits
int nextbits_cnt;
void refill(); // refill to at least 56+1 bits
};
class StreamWriter
{
public:
void write8(uint8_t);
void write16(uint16_t);
void write16s(int16_t);
void write24(uint32_t);
void write32(uint32_t);
void write32s(int32_t);
void write64(uint64_t);
void write_float32(float);
void write64s(int64_t);
void write(int size, uint64_t value);
void write(const std::string&);
void write(const std::vector&);
void write(const StreamWriter&);
void skip(int n);
void insert(int nBytes);
size_t data_size() const { return m_data.size(); }
size_t get_position() const { return m_position; }
void set_position(size_t pos) { m_position = pos; }
void set_position_to_end() { m_position = m_data.size(); }
const std::vector get_data() const { return m_data; }
private:
std::vector m_data;
size_t m_position = 0;
};
#endif
libheif-1.19.8/libheif/compression_zlib.cc 000664 001750 001750 00000010142 15003473471 021610 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2022 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#include "compression.h"
#if HAVE_ZLIB
#include
#include
#include
std::vector compress(const uint8_t* input, size_t size, int windowSize)
{
std::vector output;
// initialize compressor
const int outBufferSize = 8192;
uint8_t dst[outBufferSize];
z_stream strm;
memset(&strm, 0, sizeof(z_stream));
strm.avail_in = (uInt)size;
strm.next_in = (Bytef*)input;
strm.avail_out = outBufferSize;
strm.next_out = (Bytef*) dst;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowSize, 8, Z_DEFAULT_STRATEGY);
if (err != Z_OK) {
return {}; // TODO: return error
}
do {
strm.next_out = dst;
strm.avail_out = outBufferSize;
err = deflate(&strm, Z_FINISH);
if (err == Z_BUF_ERROR || err == Z_OK) {
// this is the usual case when we run out of buffer space
// -> do nothing
}
else if (err == Z_STREAM_ERROR) {
return {}; // TODO: return error
}
// append decoded data to output
output.insert(output.end(), dst, dst + outBufferSize - strm.avail_out);
} while (err != Z_STREAM_END);
deflateEnd(&strm);
return output;
}
Error do_inflate(const std::vector& compressed_input, int windowSize, std::vector *output)
{
// decompress data with zlib
const int outBufferSize = 8192;
uint8_t dst[outBufferSize];
z_stream strm;
memset(&strm, 0, sizeof(z_stream));
strm.avail_in = (int)compressed_input.size();
strm.next_in = (Bytef*) compressed_input.data();
strm.avail_out = outBufferSize;
strm.next_out = (Bytef*) dst;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int err = -1;
err = inflateInit2(&strm, windowSize);
if (err != Z_OK) {
std::stringstream sstr;
sstr << "Error initialising zlib inflate: " << (strm.msg ? strm.msg : "NULL") << " (" << err << ")\n";
return Error(heif_error_Memory_allocation_error, heif_suberror_Compression_initialisation_error, sstr.str());
}
do {
strm.next_out = dst;
strm.avail_out = outBufferSize;
err = inflate(&strm, Z_FINISH);
if (err == Z_BUF_ERROR || err == Z_OK) {
// this is the usual case when we run out of buffer space
// -> do nothing
}
else if (err == Z_NEED_DICT || err == Z_DATA_ERROR || err == Z_STREAM_ERROR) {
inflateEnd(&strm);
std::stringstream sstr;
sstr << "Error performing zlib inflate: " << (strm.msg ? strm.msg : "NULL") << " (" << err << ")\n";
return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str());
}
// append decoded data to output
output->insert(output->end(), dst, dst + outBufferSize - strm.avail_out);
} while (err != Z_STREAM_END);
inflateEnd(&strm);
return Error::Ok;
}
std::vector compress_zlib(const uint8_t* input, size_t size)
{
return compress(input, size, 15);
}
std::vector compress_deflate(const uint8_t* input, size_t size)
{
return compress(input, size, -15);
}
Error decompress_zlib(const std::vector& compressed_input, std::vector *output)
{
return do_inflate(compressed_input, 15, output);
}
Error decompress_deflate(const std::vector& compressed_input, std::vector *output)
{
return do_inflate(compressed_input, -15, output);
}
#endif
libheif-1.19.8/libheif/box.h 000664 001750 001750 00000124317 15003473471 016673 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2017 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#ifndef LIBHEIF_BOX_H
#define LIBHEIF_BOX_H
#include
#include "common_utils.h"
#include "libheif/heif.h"
#include "libheif/heif_experimental.h"
#include "libheif/heif_properties.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "error.h"
#include "logging.h"
#include "bitstream.h"
#if !defined(__EMSCRIPTEN__) && !defined(_MSC_VER)
// std::array is not supported on some older compilers.
#define HAS_BOOL_ARRAY 1
#endif
/*
constexpr uint32_t fourcc(const char* string)
{
return ((string[0]<<24) |
(string[1]<<16) |
(string[2]<< 8) |
(string[3]));
}
*/
class Fraction
{
public:
Fraction() = default;
Fraction(int32_t num, int32_t den);
// may only use values up to int32_t maximum
Fraction(uint32_t num, uint32_t den);
// Values will be reduced until they fit into int32_t.
Fraction(int64_t num, int64_t den);
Fraction operator+(const Fraction&) const;
Fraction operator-(const Fraction&) const;
Fraction operator+(int) const;
Fraction operator-(int) const;
Fraction operator/(int) const;
int32_t round_down() const;
int32_t round_up() const;
int32_t round() const;
bool is_valid() const;
double to_double() const {
return numerator / (double)denominator;
}
int32_t numerator = 0;
int32_t denominator = 1;
};
inline std::ostream& operator<<(std::ostream& str, const Fraction& f)
{
str << f.numerator << "/" << f.denominator;
return str;
}
class BoxHeader
{
public:
BoxHeader();
virtual ~BoxHeader() = default;
constexpr static uint64_t size_until_end_of_file = 0;
uint64_t get_box_size() const { return m_size; }
bool has_fixed_box_size() const { return m_size != 0; }
uint32_t get_header_size() const { return m_header_size; }
uint32_t get_short_type() const { return m_type; }
std::vector get_type() const;
std::string get_type_string() const;
void set_short_type(uint32_t type) { m_type = type; }
// should only be called if get_short_type == fourcc("uuid")
std::vector get_uuid_type() const;
void set_uuid_type(const std::vector&);
Error parse_header(BitstreamRange& range);
virtual std::string dump(Indent&) const;
virtual bool is_full_box_header() const { return false; }
private:
uint64_t m_size = 0;
uint32_t m_type = 0;
std::vector m_uuid_type;
protected:
uint32_t m_header_size = 0;
};
enum class BoxStorageMode {
Undefined,
Memory,
Parsed,
File
};
// Consequence of a box parse error
enum class parse_error_fatality {
fatal, // failure to parse this box leads to the associated item being unreable
ignorable, // ignoring this box will lead to unexpected output, but may be better than nothing
optional // the box contains extra information that is not essential for viewing
};
class Box : public BoxHeader
{
public:
Box() = default;
void set_short_header(const BoxHeader& hdr)
{
*(BoxHeader*) this = hdr;
}
// header size without the FullBox fields (if applicable)
int calculate_header_size(bool data64bit) const;
static Error read(BitstreamRange& range, std::shared_ptr* box, const heif_security_limits*);
virtual Error write(StreamWriter& writer) const;
// check, which box version is required and set this in the (full) box header
virtual void derive_box_version() {}
void derive_box_version_recursive();
std::string dump(Indent&) const override;
template [[nodiscard]] std::shared_ptr get_child_box() const
{
// TODO: we could remove the dynamic_cast<> by adding the fourcc type of each Box
// as a "constexpr uint32_t Box::short_type", compare to that and use static_cast<>
for (auto& box : m_children) {
if (auto typed_box = std::dynamic_pointer_cast(box)) {
return typed_box;
}
}
return nullptr;
}
template bool replace_child_box(const std::shared_ptr& box)
{
for (auto & b : m_children) {
if (std::dynamic_pointer_cast(b) != nullptr) {
b = box;
return true;
}
}
append_child_box(box);
return false;
}
template
std::vector> get_child_boxes() const
{
std::vector> result;
for (auto& box : m_children) {
if (auto typed_box = std::dynamic_pointer_cast(box)) {
result.push_back(typed_box);
}
}
return result;
}
const std::vector>& get_all_child_boxes() const { return m_children; }
uint32_t append_child_box(const std::shared_ptr& box)
{
m_children.push_back(box);
return (int) m_children.size() - 1;
}
bool has_child_boxes() const { return !m_children.empty(); }
bool remove_child_box(const std::shared_ptr& box);
virtual bool operator==(const Box& other) const;
static bool equal(const std::shared_ptr& box1, const std::shared_ptr& box2);
BoxStorageMode get_box_storage_mode() const { return m_storage_mode; }
void set_output_position(uint64_t pos) { m_output_position = pos; }
virtual parse_error_fatality get_parse_error_fatality() const { return parse_error_fatality::fatal; }
virtual bool is_essential() const { return m_is_essential; } // only used for properties
void set_is_essential(bool flag) { m_is_essential = flag; }
virtual bool is_transformative_property() const { return false; } // only used for properties
protected:
virtual Error parse(BitstreamRange& range, const heif_security_limits* limits);
std::vector> m_children;
BoxStorageMode m_storage_mode = BoxStorageMode::Undefined;
const static uint64_t INVALID_POSITION = 0xFFFFFFFFFFFFFFFF;
uint64_t m_input_position = INVALID_POSITION;
uint64_t m_output_position = INVALID_POSITION;
std::vector m_box_data; // including header
std::string m_debug_box_type;
bool m_is_essential = false;
const static uint32_t READ_CHILDREN_ALL = 0xFFFFFFFF;
Error read_children(BitstreamRange& range, uint32_t number /* READ_CHILDREN_ALL */, const heif_security_limits* limits);
Error write_children(StreamWriter& writer) const;
std::string dump_children(Indent&, bool with_index = false) const;
// --- writing
virtual size_t reserve_box_header_space(StreamWriter& writer, bool data64bit = false) const;
Error prepend_header(StreamWriter&, size_t box_start, bool data64bit = false) const;
virtual Error write_header(StreamWriter&, size_t total_box_size, bool data64bit = false) const;
};
class FullBox : public Box
{
public:
bool is_full_box_header() const override { return true; }
std::string dump(Indent& indent) const override;
void derive_box_version() override { set_version(0); }
Error parse_full_box_header(BitstreamRange& range);
uint8_t get_version() const { return m_version; }
void set_version(uint8_t version) { m_version = version; }
uint32_t get_flags() const { return m_flags; }
void set_flags(uint32_t flags) { m_flags = flags; }
protected:
// --- writing
size_t reserve_box_header_space(StreamWriter& writer, bool data64bit = false) const override;
Error write_header(StreamWriter&, size_t total_size, bool data64bit = false) const override;
Error unsupported_version_error(const char* box) const;
private:
uint8_t m_version = 0;
uint32_t m_flags = 0;
};
class Box_other : public Box
{
public:
Box_other(uint32_t short_type)
{
set_short_type(short_type);
}
const std::vector& get_raw_data() const { return m_data; }
void set_raw_data(const std::vector& data) { m_data = data; }
Error write(StreamWriter& writer) const override;
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
std::vector m_data;
};
class Box_Error : public Box
{
public:
Box_Error(uint32_t box4cc, Error err, parse_error_fatality fatality)
{
set_short_type(fourcc("ERR "));
m_box_type_with_parse_error = box4cc;
m_error = std::move(err);
m_fatality = fatality;
}
Error write(StreamWriter& writer) const override { return {heif_error_Usage_error, heif_suberror_Unspecified, "Cannot write dummy error box."}; }
std::string dump(Indent&) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override;
[[nodiscard]] Error get_error() const { return m_error; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override { assert(false); return Error::Ok; }
uint32_t m_box_type_with_parse_error;
Error m_error;
parse_error_fatality m_fatality;
};
class Box_ftyp : public Box
{
public:
Box_ftyp()
{
set_short_type(fourcc("ftyp"));
}
std::string dump(Indent&) const override;
bool has_compatible_brand(uint32_t brand) const;
std::vector list_brands() const { return m_compatible_brands; }
uint32_t get_major_brand() const { return m_major_brand; }
void set_major_brand(heif_brand2 major_brand) { m_major_brand = major_brand; }
uint32_t get_minor_version() const { return m_minor_version; }
void set_minor_version(uint32_t minor_version) { m_minor_version = minor_version; }
void clear_compatible_brands() { m_compatible_brands.clear(); }
void add_compatible_brand(heif_brand2 brand);
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
uint32_t m_major_brand = 0;
uint32_t m_minor_version = 0;
std::vector m_compatible_brands;
};
class Box_free : public Box
{
public:
Box_free()
{
set_short_type(fourcc("free"));
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_meta : public FullBox
{
public:
Box_meta()
{
set_short_type(fourcc("meta"));
}
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_hdlr : public FullBox
{
public:
Box_hdlr()
{
set_short_type(fourcc("hdlr"));
}
std::string dump(Indent&) const override;
uint32_t get_handler_type() const { return m_handler_type; }
void set_handler_type(uint32_t handler) { m_handler_type = handler; }
Error write(StreamWriter& writer) const override;
void set_name(std::string name) { m_name = std::move(name); }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
uint32_t m_pre_defined = 0;
uint32_t m_handler_type = fourcc("pict");
uint32_t m_reserved[3] = {0,};
std::string m_name;
};
class Box_pitm : public FullBox
{
public:
Box_pitm()
{
set_short_type(fourcc("pitm"));
}
std::string dump(Indent&) const override;
heif_item_id get_item_ID() const { return m_item_ID; }
void set_item_ID(heif_item_id id) { m_item_ID = id; }
void derive_box_version() override;
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
heif_item_id m_item_ID = 0;
};
class Box_iloc : public FullBox
{
public:
Box_iloc();
~Box_iloc() override;
void set_use_tmp_file(bool flag);
std::string dump(Indent&) const override;
struct Extent
{
uint64_t index = 0;
uint64_t offset = 0;
uint64_t length = 0;
std::vector data; // only used when writing data
};
struct Item
{
heif_item_id item_ID = 0;
uint8_t construction_method = 0; // >= version 1
uint16_t data_reference_index = 0;
uint64_t base_offset = 0;
std::vector extents;
};
const std::vector- & get_items() const { return m_items; }
Error read_data(heif_item_id item,
const std::shared_ptr& istr,
const std::shared_ptr&,
std::vector* dest,
const heif_security_limits* limits) const;
// Note: size==std::numeric_limits::max() reads the data until the end
Error read_data(heif_item_id item,
const std::shared_ptr& istr,
const std::shared_ptr&,
std::vector* dest,
uint64_t offset, uint64_t size,
const heif_security_limits* limits) const;
void set_min_version(uint8_t min_version) { m_user_defined_min_version = min_version; }
// append bitstream data that will be written later (after iloc box)
// TODO: use an enum for the construction method
Error append_data(heif_item_id item_ID,
const std::vector& data,
uint8_t construction_method = 0);
Error replace_data(heif_item_id item_ID,
uint64_t output_offset,
const std::vector& data,
uint8_t construction_method);
// append bitstream data that already has been written (before iloc box)
// Error write_mdat_before_iloc(heif_image_id item_ID,
// std::vector& data)
// reserve data entry that will be written later
// Error reserve_mdat_item(heif_image_id item_ID,
// uint8_t construction_method,
// uint32_t* slot_ID);
// void patch_mdat_slot(uint32_t slot_ID, size_t start, size_t length);
void derive_box_version() override;
Error write(StreamWriter& writer) const override;
Error write_mdat_after_iloc(StreamWriter& writer);
void append_item(Item &item) { m_items.push_back(item); }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
std::vector
- m_items;
mutable size_t m_iloc_box_start = 0;
uint8_t m_user_defined_min_version = 0;
uint8_t m_offset_size = 0;
uint8_t m_length_size = 0;
uint8_t m_base_offset_size = 0;
uint8_t m_index_size = 0;
void patch_iloc_header(StreamWriter& writer) const;
int m_idat_offset = 0; // only for writing: offset of next data array
bool m_use_tmpfile = false;
int m_tmpfile_fd = 0;
char m_tmp_filename[20];
};
class Box_infe : public FullBox
{
public:
Box_infe()
{
set_short_type(fourcc("infe"));
}
std::string dump(Indent&) const override;
bool is_hidden_item() const { return m_hidden_item; }
void set_hidden_item(bool hidden);
heif_item_id get_item_ID() const { return m_item_ID; }
void set_item_ID(heif_item_id id) { m_item_ID = id; }
uint32_t get_item_type_4cc() const { return m_item_type_4cc; }
void set_item_type_4cc(uint32_t type) { m_item_type_4cc = type; }
void set_item_name(const std::string& name) { m_item_name = name; }
const std::string& get_item_name() const { return m_item_name; }
const std::string& get_content_type() const { return m_content_type; }
const std::string& get_content_encoding() const { return m_content_encoding; }
void set_content_type(const std::string& content_type) { m_content_type = content_type; }
void set_content_encoding(const std::string& content_encoding) { m_content_encoding = content_encoding; }
void derive_box_version() override;
Error write(StreamWriter& writer) const override;
const std::string& get_item_uri_type() const { return m_item_uri_type; }
void set_item_uri_type(const std::string& uritype) { m_item_uri_type = uritype; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
heif_item_id m_item_ID = 0;
uint16_t m_item_protection_index = 0;
uint32_t m_item_type_4cc = 0;
std::string m_item_name;
std::string m_content_type;
std::string m_content_encoding;
std::string m_item_uri_type;
// if set, this item should not be part of the presentation (i.e. hidden)
bool m_hidden_item = false;
};
class Box_iinf : public FullBox
{
public:
Box_iinf()
{
set_short_type(fourcc("iinf"));
}
std::string dump(Indent&) const override;
void derive_box_version() override;
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
//std::vector< std::shared_ptr > m_iteminfos;
};
class Box_iprp : public Box
{
public:
Box_iprp()
{
set_short_type(fourcc("iprp"));
}
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_ipco : public Box
{
public:
Box_ipco()
{
set_short_type(fourcc("ipco"));
}
uint32_t find_or_append_child_box(const std::shared_ptr& box);
Error get_properties_for_item_ID(heif_item_id itemID,
const std::shared_ptr&,
std::vector>& out_properties) const;
std::shared_ptr get_property_for_item_ID(heif_item_id itemID,
const std::shared_ptr&,
uint32_t property_box_type) const;
bool is_property_essential_for_item(heif_item_id itemId,
const std::shared_ptr& property,
const std::shared_ptr&) const;
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_ispe : public FullBox
{
public:
Box_ispe()
{
set_short_type(fourcc("ispe"));
}
uint32_t get_width() const { return m_image_width; }
uint32_t get_height() const { return m_image_height; }
void set_size(uint32_t width, uint32_t height)
{
m_image_width = width;
m_image_height = height;
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
bool operator==(const Box& other) const override;
bool is_essential() const override { return false; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
uint32_t m_image_width = 0;
uint32_t m_image_height = 0;
};
class Box_ipma : public FullBox
{
public:
Box_ipma()
{
set_short_type(fourcc("ipma"));
}
std::string dump(Indent&) const override;
struct PropertyAssociation
{
bool essential;
uint16_t property_index;
};
const std::vector* get_properties_for_item_ID(heif_item_id itemID) const;
bool is_property_essential_for_item(heif_item_id itemId, int propertyIndex) const;
void add_property_for_item_ID(heif_item_id itemID,
PropertyAssociation assoc);
void derive_box_version() override;
Error write(StreamWriter& writer) const override;
void insert_entries_from_other_ipma_box(const Box_ipma& b);
// sorts properties such that descriptive properties precede the transformative properties
void sort_properties(const std::shared_ptr&);
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
struct Entry
{
heif_item_id item_ID;
std::vector associations;
};
std::vector m_entries;
};
class Box_auxC : public FullBox
{
public:
Box_auxC()
{
set_short_type(fourcc("auxC"));
}
const std::string& get_aux_type() const { return m_aux_type; }
void set_aux_type(const std::string& type) { m_aux_type = type; }
const std::vector& get_subtypes() const { return m_aux_subtypes; }
bool is_essential() const override { return true; }
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
std::string m_aux_type;
std::vector m_aux_subtypes;
};
class Box_irot : public Box
{
public:
Box_irot()
{
set_short_type(fourcc("irot"));
}
bool is_essential() const override { return true; }
bool is_transformative_property() const override { return true; }
std::string dump(Indent&) const override;
int get_rotation_ccw() const { return m_rotation; }
// Only these multiples of 90 are allowed: 0, 90, 180, 270.
void set_rotation_ccw(int rot) { m_rotation = rot; }
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::ignorable; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
int m_rotation = 0; // in degrees (CCW)
};
class Box_imir : public Box
{
public:
Box_imir()
{
set_short_type(fourcc("imir"));
}
bool is_essential() const override { return true; }
bool is_transformative_property() const override { return true; }
heif_transform_mirror_direction get_mirror_direction() const { return m_axis; }
void set_mirror_direction(heif_transform_mirror_direction dir) { m_axis = dir; }
std::string dump(Indent&) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::ignorable; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
heif_transform_mirror_direction m_axis = heif_transform_mirror_direction_vertical;
};
class Box_clap : public Box
{
public:
Box_clap()
{
set_short_type(fourcc("clap"));
}
bool is_essential() const override { return true; }
bool is_transformative_property() const override { return true; }
std::string dump(Indent&) const override;
int left_rounded(uint32_t image_width) const; // first column
int right_rounded(uint32_t image_width) const; // last column that is part of the cropped image
int top_rounded(uint32_t image_height) const; // first row
int bottom_rounded(uint32_t image_height) const; // last row included in the cropped image
double left(int image_width) const;
double top(int image_height) const;
int get_width_rounded() const;
int get_height_rounded() const;
void set(uint32_t clap_width, uint32_t clap_height,
uint32_t image_width, uint32_t image_height);
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::ignorable; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
Fraction m_clean_aperture_width;
Fraction m_clean_aperture_height;
Fraction m_horizontal_offset;
Fraction m_vertical_offset;
};
class Box_iref : public FullBox
{
public:
Box_iref()
{
set_short_type(fourcc("iref"));
}
struct Reference
{
BoxHeader header;
heif_item_id from_item_ID;
std::vector to_item_ID;
};
std::string dump(Indent&) const override;
bool has_references(heif_item_id itemID) const;
std::vector get_references(heif_item_id itemID, uint32_t ref_type) const;
std::vector get_references_from(heif_item_id itemID) const;
void add_references(heif_item_id from_id, uint32_t type, const std::vector& to_ids);
void overwrite_reference(heif_item_id from_id, uint32_t type, uint32_t reference_idx, heif_item_id to_item);
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
void derive_box_version() override;
Error check_for_double_references() const;
private:
std::vector m_references;
};
class Box_idat : public Box
{
public:
std::string dump(Indent&) const override;
Error read_data(const std::shared_ptr& istr,
uint64_t start, uint64_t length,
std::vector& out_data,
const heif_security_limits* limits) const;
int append_data(const std::vector& data)
{
auto pos = m_data_for_writing.size();
m_data_for_writing.insert(m_data_for_writing.end(),
data.begin(),
data.end());
return (int) pos;
}
Error write(StreamWriter& writer) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
std::streampos m_data_start_pos;
std::vector m_data_for_writing;
};
class Box_grpl : public Box
{
public:
Box_grpl()
{
set_short_type(fourcc("grpl"));
}
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_EntityToGroup : public FullBox
{
public:
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
void set_group_id(heif_entity_group_id id) { group_id = id; }
heif_entity_group_id get_group_id() const { return group_id; }
void set_item_ids(const std::vector& ids) { entity_ids = ids; }
const std::vector& get_item_ids() const { return entity_ids; }
protected:
heif_entity_group_id group_id = 0;
std::vector entity_ids;
Error parse(BitstreamRange& range, const heif_security_limits*) override;
void write_entity_group_ids(StreamWriter& writer) const;
};
class Box_ster : public Box_EntityToGroup
{
public:
Box_ster()
{
set_short_type(fourcc("ster"));
}
std::string dump(Indent&) const override;
heif_item_id get_left_image() const { return entity_ids[0]; }
heif_item_id get_right_image() const { return entity_ids[1]; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_pymd : public Box_EntityToGroup
{
public:
Box_pymd()
{
set_short_type(fourcc("pymd"));
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
struct LayerInfo {
uint16_t layer_binning;
uint16_t tiles_in_layer_row_minus1;
uint16_t tiles_in_layer_column_minus1;
};
void set_layers(uint16_t _tile_size_x,
uint16_t _tile_size_y,
const std::vector& layers,
const std::vector& layer_item_ids) // low to high resolution
{
set_item_ids(layer_item_ids);
m_layer_infos = layers;
tile_size_x = _tile_size_x;
tile_size_y = _tile_size_y;
}
const std::vector& get_layers() const { return m_layer_infos; }
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::ignorable; }
protected:
uint16_t tile_size_x = 0;
uint16_t tile_size_y = 0;
std::vector m_layer_infos;
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_dinf : public Box
{
public:
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_dref : public FullBox
{
public:
std::string dump(Indent&) const override;
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_url : public FullBox
{
public:
std::string dump(Indent&) const override;
bool is_same_file() const { return m_location.empty(); }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
std::string m_location;
};
class Box_pixi : public FullBox
{
public:
Box_pixi()
{
set_short_type(fourcc("pixi"));
}
int get_num_channels() const { return (int) m_bits_per_channel.size(); }
int get_bits_per_channel(int channel) const { return m_bits_per_channel[channel]; }
void add_channel_bits(uint8_t c)
{
m_bits_per_channel.push_back(c);
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
std::vector m_bits_per_channel;
};
class Box_pasp : public Box
{
public:
Box_pasp()
{
set_short_type(fourcc("pasp"));
}
uint32_t hSpacing = 1;
uint32_t vSpacing = 1;
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_lsel : public Box
{
public:
Box_lsel()
{
set_short_type(fourcc("lsel"));
}
uint16_t layer_id = 0;
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_clli : public Box
{
public:
Box_clli()
{
set_short_type(fourcc("clli"));
clli.max_content_light_level = 0;
clli.max_pic_average_light_level = 0;
}
heif_content_light_level clli;
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_mdcv : public Box
{
public:
Box_mdcv();
heif_mastering_display_colour_volume mdcv;
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_amve : public Box
{
public:
Box_amve();
heif_ambient_viewing_environment amve;
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
};
class Box_cclv : public Box
{
public:
Box_cclv();
bool ccv_primaries_are_valid() const { return m_ccv_primaries_valid; }
int32_t get_ccv_primary_x0() const { return m_ccv_primaries_x[0]; }
int32_t get_ccv_primary_y0() const { return m_ccv_primaries_y[0]; }
int32_t get_ccv_primary_x1() const { return m_ccv_primaries_x[1]; }
int32_t get_ccv_primary_y1() const { return m_ccv_primaries_y[1]; }
int32_t get_ccv_primary_x2() const { return m_ccv_primaries_x[2]; }
int32_t get_ccv_primary_y2() const { return m_ccv_primaries_y[2]; }
void set_primaries(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2);
bool min_luminance_is_valid() const { return m_ccv_min_luminance_value.has_value(); }
uint32_t get_min_luminance() const { return *m_ccv_min_luminance_value; }
void set_min_luminance(uint32_t luminance) { m_ccv_min_luminance_value = luminance; }
bool max_luminance_is_valid() const { return m_ccv_max_luminance_value.has_value(); }
uint32_t get_max_luminance() const { return *m_ccv_max_luminance_value; }
void set_max_luminance(uint32_t luminance) { m_ccv_max_luminance_value = luminance; }
bool avg_luminance_is_valid() const { return m_ccv_avg_luminance_value.has_value(); }
uint32_t get_avg_luminance() const { return *m_ccv_avg_luminance_value; }
void set_avg_luminance(uint32_t luminance) { m_ccv_avg_luminance_value = luminance; }
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
bool m_ccv_primaries_valid = false;
int32_t m_ccv_primaries_x[3] {};
int32_t m_ccv_primaries_y[3] {};
std::optional m_ccv_min_luminance_value;
std::optional m_ccv_max_luminance_value;
std::optional m_ccv_avg_luminance_value;
};
class Box_cmin : public FullBox
{
public:
Box_cmin()
{
set_short_type(fourcc("cmin"));
}
struct AbsoluteIntrinsicMatrix;
struct RelativeIntrinsicMatrix
{
double focal_length_x = 0;
double principal_point_x = 0;
double principal_point_y = 0;
bool is_anisotropic = false;
double focal_length_y = 0;
double skew = 0;
void compute_focal_length(int image_width, int image_height,
double& out_focal_length_x, double& out_focal_length_y) const;
void compute_principal_point(int image_width, int image_height,
double& out_principal_point_x, double& out_principal_point_y) const;
struct AbsoluteIntrinsicMatrix to_absolute(int image_width, int image_height) const;
};
struct AbsoluteIntrinsicMatrix
{
double focal_length_x;
double focal_length_y;
double principal_point_x;
double principal_point_y;
double skew = 0;
void apply_clap(const Box_clap* clap, int image_width, int image_height) {
principal_point_x -= clap->left(image_width);
principal_point_y -= clap->top(image_height);
}
void apply_imir(const Box_imir* imir, int image_width, int image_height) {
switch (imir->get_mirror_direction()) {
case heif_transform_mirror_direction_horizontal:
focal_length_x *= -1;
skew *= -1;
principal_point_x = image_width - 1 - principal_point_x;
break;
case heif_transform_mirror_direction_vertical:
focal_length_y *= -1;
principal_point_y = image_height - 1 - principal_point_y;
break;
case heif_transform_mirror_direction_invalid:
break;
}
}
};
std::string dump(Indent&) const override;
RelativeIntrinsicMatrix get_intrinsic_matrix() const { return m_matrix; }
void set_intrinsic_matrix(RelativeIntrinsicMatrix matrix);
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
RelativeIntrinsicMatrix m_matrix;
uint32_t m_denominatorShift = 0;
uint32_t m_skewDenominatorShift = 0;
};
class Box_cmex : public FullBox
{
public:
Box_cmex()
{
set_short_type(fourcc("cmex"));
}
struct ExtrinsicMatrix
{
// in micrometers (um)
int32_t pos_x = 0;
int32_t pos_y = 0;
int32_t pos_z = 0;
bool rotation_as_quaternions = true;
bool orientation_is_32bit = false;
double quaternion_x = 0;
double quaternion_y = 0;
double quaternion_z = 0;
double quaternion_w = 1.0;
// rotation angle in degrees
double rotation_yaw = 0; // [-180 ; 180)
double rotation_pitch = 0; // [-90 ; 90]
double rotation_roll = 0; // [-180 ; 180)
uint32_t world_coordinate_system_id = 0;
// Returns rotation matrix in row-major order.
std::array calculate_rotation_matrix() const;
};
std::string dump(Indent&) const override;
ExtrinsicMatrix get_extrinsic_matrix() const { return m_matrix; }
Error set_extrinsic_matrix(ExtrinsicMatrix matrix);
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
Error write(StreamWriter& writer) const override;
private:
ExtrinsicMatrix m_matrix;
bool m_has_pos_x = false;
bool m_has_pos_y = false;
bool m_has_pos_z = false;
bool m_has_orientation = false;
bool m_has_world_coordinate_system_id = false;
enum Flags
{
pos_x_present = 0x01,
pos_y_present = 0x02,
pos_z_present = 0x04,
orientation_present = 0x08,
rot_large_field_size = 0x10,
id_present = 0x20
};
};
/**
* User Description property.
*
* Permits the association of items or entity groups with a user-defined name, description and tags;
* there may be multiple udes properties, each with a different language code.
*
* See ISO/IEC 23008-12:2022(E) Section 6.5.20.
*/
class Box_udes : public FullBox
{
public:
Box_udes()
{
set_short_type(fourcc("udes"));
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
/**
* Language tag.
*
* An RFC 5646 compliant language identifier for the language of the text contained in the other properties.
* Examples: "en-AU", "de-DE", or "zh-CN“.
* When is empty, the language is unknown or not undefined.
*/
std::string get_lang() const { return m_lang; }
/**
* Set the language tag.
*
* An RFC 5646 compliant language identifier for the language of the text contained in the other properties.
* Examples: "en-AU", "de-DE", or "zh-CN“.
*/
void set_lang(const std::string lang) { m_lang = lang; }
/**
* Name.
*
* Human readable name for the item or group being described.
* May be empty, indicating no name is applicable.
*/
std::string get_name() const { return m_name; }
/**
* Set the name.
*
* Human readable name for the item or group being described.
*/
void set_name(const std::string name) { m_name = name; }
/**
* Description.
*
* Human readable description for the item or group.
* May be empty, indicating no description has been provided.
*/
std::string get_description() const { return m_description; }
/**
* Set the description.
*
* Human readable description for the item or group.
*/
void set_description(const std::string description) { m_description = description; }
/**
* Tags.
*
* Comma separated user defined tags applicable to the item or group.
* May be empty, indicating no tags have been assigned.
*/
std::string get_tags() const { return m_tags; }
/**
* Set the tags.
*
* Comma separated user defined tags applicable to the item or group.
*/
void set_tags(const std::string tags) { m_tags = tags; }
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
std::string m_lang;
std::string m_name;
std::string m_description;
std::string m_tags;
};
#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
class Box_taic : public FullBox
{
public:
Box_taic()
{
set_short_type(fourcc("taic"));
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
/**
* time_uncertainty.
*
* The standard deviation measurement uncertainty in nanoseconds
* for the timestamp generation process.
*/
void set_time_uncertainty(uint64_t time_uncertainty) { m_time_uncertainty = time_uncertainty;}
/**
* clock_resolution.
*
* Specifies the resolution of the receptor clock in nanoseconds.
* For example, a microsecond clock has a clock_resolution of 1000.
*/
void set_clock_resolution(uint32_t clock_resolution) { m_clock_resolution = clock_resolution; }
/**
* clock_drift_rate.
*
* The difference between the synchronized and unsynchronized
* time, over a period of one second.
*/
void set_clock_drift_rate(int32_t clock_drift_rate) { m_clock_drift_rate = clock_drift_rate; }
/**
* clock_type.
*
* 0 = Clock type is unkown
* 1 = The clock does not synchronize to an atomic source of absolute TAI time
* 2 = The clock can synchronize to an atomic source of absolute TAI time
*/
void set_clock_type(uint8_t clock_type) { m_clock_type = clock_type; }
uint64_t get_time_uncertainty() const { return m_time_uncertainty; }
uint32_t get_clock_resolution() const { return m_clock_resolution; }
int32_t get_clock_drift_rate() const { return m_clock_drift_rate; }
uint8_t get_clock_type() const { return m_clock_type; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
uint64_t m_time_uncertainty = heif_tai_clock_info_unknown_time_uncertainty;
uint32_t m_clock_resolution = 0;
int32_t m_clock_drift_rate = heif_tai_clock_info_unknown_drift_rate;
uint8_t m_clock_type = 0;
};
class Box_itai : public FullBox
{
public:
Box_itai()
{
set_short_type(fourcc("itai"));
}
std::string dump(Indent&) const override;
Error write(StreamWriter& writer) const override;
/**
* The number of nanoseconds since the TAI epoch of 1958-01-01T00:00:00.0Z.
*/
void set_tai_timestamp(uint64_t timestamp) { m_tai_timestamp = timestamp; }
/**
* synchronization_state (0=unsynchronized, 1=synchronized)
*/
void set_synchronization_state(bool state) { m_synchronization_state = state; }
/**
* timestamp_generation_failure (0=generated, 1=failed)
*/
void set_timestamp_generation_failure(bool failure) { m_timestamp_generation_failure = failure; }
/**
* timestamp_is_modified (0=original 1=modified)
*/
void set_timestamp_is_modified(bool is_modified) { m_timestamp_is_modified = is_modified; }
uint64_t get_tai_timestamp() const { return m_tai_timestamp; }
bool get_synchronization_state() const { return m_synchronization_state; }
bool get_timestamp_generation_failure() const { return m_timestamp_generation_failure; }
bool get_timestamp_is_modified() const { return m_timestamp_is_modified; }
protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;
private:
uint64_t m_tai_timestamp = 0;
bool m_synchronization_state = false;
bool m_timestamp_generation_failure = false;
bool m_timestamp_is_modified = false;
};
#endif
#endif libheif-1.19.8/libheif/context.cc 000664 001750 001750 00000136747 15003473471 017737 0 ustar 00farindk farindk 000000 000000 /*
* HEIF codec.
* Copyright (c) 2017 Dirk Farin
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
#include "box.h"
#include "error.h"
#include "libheif/heif.h"
#include "region.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "image-items/image_item.h"
#include
#if ENABLE_PARALLEL_TILE_DECODING
#include
#endif
#include "context.h"
#include "file.h"
#include "pixelimage.h"
#include "libheif/api_structs.h"
#include "security_limits.h"
#include "compression.h"
#include "color-conversion/colorconversion.h"
#include "plugin_registry.h"
#include "image-items/hevc.h"
#include "image-items/vvc.h"
#include "image-items/avif.h"
#include "image-items/jpeg.h"
#include "image-items/mask_image.h"
#include "image-items/jpeg2000.h"
#include "image-items/grid.h"
#include "image-items/overlay.h"
#include "image-items/tiled.h"
#if WITH_UNCOMPRESSED_CODEC
#include "image-items/unc_image.h"
#endif
heif_encoder::heif_encoder(const struct heif_encoder_plugin* _plugin)
: plugin(_plugin)
{
}
heif_encoder::~heif_encoder()
{
release();
}
void heif_encoder::release()
{
if (encoder) {
plugin->free_encoder(encoder);
encoder = nullptr;
}
}
struct heif_error heif_encoder::alloc()
{
if (encoder == nullptr) {
struct heif_error error = plugin->new_encoder(&encoder);
// TODO: error handling
return error;
}
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess};
return err;
}
HeifContext::HeifContext()
{
const char* security_limits_variable = getenv("LIBHEIF_SECURITY_LIMITS");
if (security_limits_variable && (strcmp(security_limits_variable, "off") == 0 ||
strcmp(security_limits_variable, "OFF") == 0)) {
m_limits = disabled_security_limits;
}
else {
m_limits = global_security_limits;
}
reset_to_empty_heif();
}
HeifContext::~HeifContext()
{
// Break circular references between Images (when a faulty input image has circular image references)
for (auto& it : m_all_images) {
std::shared_ptr image = it.second;
image->clear();
}
}
static void copy_security_limits(heif_security_limits* dst, const heif_security_limits* src)
{
dst->version = 1;
dst->max_image_size_pixels = src->max_image_size_pixels;
dst->max_number_of_tiles = src->max_number_of_tiles;
dst->max_bayer_pattern_pixels = src->max_bayer_pattern_pixels;
dst->max_items = src->max_items;
dst->max_color_profile_size = src->max_color_profile_size;
dst->max_memory_block_size = src->max_memory_block_size;
dst->max_components = src->max_components;
dst->max_iloc_extents_per_item = src->max_iloc_extents_per_item;
dst->max_size_entity_group = src->max_size_entity_group;
dst->max_children_per_box = src->max_children_per_box;
}
void HeifContext::set_security_limits(const heif_security_limits* limits)
{
copy_security_limits(&m_limits, limits);
}
Error HeifContext::read(const std::shared_ptr& reader)
{
m_heif_file = std::make_shared();
m_heif_file->set_security_limits(&m_limits);
Error err = m_heif_file->read(reader);
if (err) {
return err;
}
return interpret_heif_file();
}
Error HeifContext::read_from_file(const char* input_filename)
{
m_heif_file = std::make_shared();
m_heif_file->set_security_limits(&m_limits);
Error err = m_heif_file->read_from_file(input_filename);
if (err) {
return err;
}
return interpret_heif_file();
}
Error HeifContext::read_from_memory(const void* data, size_t size, bool copy)
{
m_heif_file = std::make_shared();
m_heif_file->set_security_limits(&m_limits);
Error err = m_heif_file->read_from_memory(data, size, copy);
if (err) {
return err;
}
return interpret_heif_file();
}
void HeifContext::reset_to_empty_heif()
{
m_heif_file = std::make_shared();
m_heif_file->set_security_limits(&m_limits);
m_heif_file->new_empty_file();
m_all_images.clear();
m_top_level_images.clear();
m_primary_image.reset();
}
std::vector> HeifContext::get_top_level_images(bool return_error_images)
{
if (return_error_images) {
return m_top_level_images;
}
else {
std::vector> filtered;
for (auto& item : m_top_level_images) {
if (!item->get_item_error()) {
filtered.push_back(item);
}
}
return filtered;
}
}
std::shared_ptr HeifContext::get_image(heif_item_id id, bool return_error_images)
{
auto iter = m_all_images.find(id);
if (iter == m_all_images.end()) {
return nullptr;
}
else {
if (iter->second->get_item_error() && !return_error_images) {
return nullptr;
}
else {
return iter->second;
}
}
}
std::shared_ptr HeifContext::get_primary_image(bool return_error_image)
{
if (!return_error_image && m_primary_image->get_item_error())
return nullptr;
else
return m_primary_image;
}
bool HeifContext::is_image(heif_item_id ID) const
{
return m_all_images.find(ID) != m_all_images.end();
}
std::shared_ptr HeifContext::add_region_item(uint32_t reference_width, uint32_t reference_height)
{
std::shared_ptr box = m_heif_file->add_new_infe_box(fourcc("rgan"));
box->set_hidden_item(true);
auto regionItem = std::make_shared(box->get_item_ID(), reference_width, reference_height);
add_region_item(regionItem);
return regionItem;
}
void HeifContext::add_region_referenced_mask_ref(heif_item_id region_item_id, heif_item_id mask_item_id)
{
m_heif_file->add_iref_reference(region_item_id, fourcc("mask"), {mask_item_id});
}
void HeifContext::write(StreamWriter& writer)
{
// --- serialize regions
for (auto& image : m_all_images) {
for (auto region : image.second->get_region_item_ids()) {
m_heif_file->add_iref_reference(region,
fourcc("cdsc"), {image.first});
}
}
for (auto& region : m_region_items) {
std::vector data_array;
Error err = region->encode(data_array);
// TODO: err
m_heif_file->append_iloc_data(region->item_id, data_array, 0);
}
// --- post-process images
for (auto& img : m_all_images) {
img.second->process_before_write();
}
// --- sort item properties
m_heif_file->get_ipma_box()->sort_properties(m_heif_file->get_ipco_box());
// --- write to file
m_heif_file->write(writer);
}
std::string HeifContext::debug_dump_boxes() const
{
return m_heif_file->debug_dump_boxes();
}
static bool item_type_is_image(uint32_t item_type, const std::string& content_type)
{
return (item_type == fourcc("hvc1") ||
item_type == fourcc("av01") ||
item_type == fourcc("grid") ||
item_type == fourcc("tili") ||
item_type == fourcc("iden") ||
item_type == fourcc("iovl") ||
item_type == fourcc("avc1") ||
item_type == fourcc("unci") ||
item_type == fourcc("vvc1") ||
item_type == fourcc("jpeg") ||
(item_type == fourcc("mime") && content_type == "image/jpeg") ||
item_type == fourcc("j2k1") ||
item_type == fourcc("mski"));
}
void HeifContext::remove_top_level_image(const std::shared_ptr& image)
{
std::vector> new_list;
for (const auto& img : m_top_level_images) {
if (img != image) {
new_list.push_back(img);
}
}
m_top_level_images = std::move(new_list);
}
Error HeifContext::interpret_heif_file()
{
m_all_images.clear();
m_top_level_images.clear();
m_primary_image.reset();
// --- reference all non-hidden images
std::vector image_IDs = m_heif_file->get_item_IDs();
for (heif_item_id id : image_IDs) {
auto infe_box = m_heif_file->get_infe_box(id);
if (!infe_box) {
// TODO(farindk): Should we return an error instead of skipping the invalid id?
continue;
}
auto image = ImageItem::alloc_for_infe_box(this, infe_box);
if (!image) {
// It is no image item, skip it.
continue;
}
m_all_images.insert(std::make_pair(id, image));
if (!infe_box->is_hidden_item()) {
if (id == m_heif_file->get_primary_image_ID()) {
image->set_primary(true);
m_primary_image = image;
}
m_top_level_images.push_back(image);
}
std::vector> properties;
Error err = m_heif_file->get_properties(id, properties);
if (err) {
return err;
}
image->set_properties(properties);
err = image->on_load_file();
if (err) {
return err;
}
}
if (!m_primary_image) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"'pitm' box references an unsupported or non-existing image");
}
// --- process image properties
for (auto& pair : m_all_images) {
auto& image = pair.second;
if (image->get_item_error()) {
continue;
}
std::vector> properties;
Error err = m_heif_file->get_properties(pair.first, properties);
if (err) {
return err;
}
// --- are there any 'essential' properties that we did not parse?
for (const auto& prop : properties) {
if (std::dynamic_pointer_cast(prop) &&
get_heif_file()->get_ipco_box()->is_property_essential_for_item(pair.first, prop, get_heif_file()->get_ipma_box())) {
std::stringstream sstr;
sstr << "could not parse item property '" << prop->get_type_string() << "'";
return {heif_error_Unsupported_feature, heif_suberror_Unsupported_essential_property, sstr.str()};
}
}
// --- Are there any parse errors in optional properties? Attach the errors as warnings to the images.
bool ignore_nonfatal_parse_errors = false; // TODO: this should be a user option. Where should we put this (heif_decoding_options, or while creating the context) ?
for (const auto& prop : properties) {
if (auto errorbox = std::dynamic_pointer_cast(prop)) {
parse_error_fatality fatality = errorbox->get_parse_error_fatality();
if (fatality == parse_error_fatality::optional ||
(fatality == parse_error_fatality::ignorable && ignore_nonfatal_parse_errors)) {
image->add_decoding_warning(errorbox->get_error());
}
else {
return errorbox->get_error();
}
}
}
// --- extract image resolution
bool ispe_read = false;
for (const auto& prop : properties) {
auto ispe = std::dynamic_pointer_cast(prop);
if (ispe) {
uint32_t width = ispe->get_width();
uint32_t height = ispe->get_height();
Error sizeError = check_for_valid_image_size(get_security_limits(), width, height);
if (sizeError) {
return sizeError;
}
image->set_resolution(width, height);
ispe_read = true;
}
}
// Note: usually, we would like to check here if an `ispe` property exists as this is mandatory.
// We want to do this if decoding_options.strict_decoding is set, but we cannot because we have no decoding_options
// when parsing the file structure.
if (!ispe_read) {
image->add_decoding_warning({heif_error_Invalid_input, heif_suberror_No_ispe_property});
}
for (const auto& prop : properties) {
auto colr = std::dynamic_pointer_cast(prop);
if (colr) {
auto profile = colr->get_color_profile();
image->set_color_profile(profile);
continue;
}
auto cmin = std::dynamic_pointer_cast(prop);
if (cmin) {
if (!ispe_read) {
return {heif_error_Invalid_input, heif_suberror_No_ispe_property};
}
image->set_intrinsic_matrix(cmin->get_intrinsic_matrix());
}
auto cmex = std::dynamic_pointer_cast(prop);
if (cmex) {
image->set_extrinsic_matrix(cmex->get_extrinsic_matrix());
}
}
for (const auto& prop : properties) {
auto clap = std::dynamic_pointer_cast(prop);
if (clap) {
image->set_resolution(clap->get_width_rounded(),
clap->get_height_rounded());
if (image->has_intrinsic_matrix()) {
image->get_intrinsic_matrix().apply_clap(clap.get(), image->get_width(), image->get_height());
}
}
auto imir = std::dynamic_pointer_cast(prop);
if (imir) {
if (!ispe_read) {
return {heif_error_Invalid_input, heif_suberror_No_ispe_property};
}
image->get_intrinsic_matrix().apply_imir(imir.get(), image->get_width(), image->get_height());
}
auto irot = std::dynamic_pointer_cast(prop);
if (irot) {
if (irot->get_rotation_ccw() == 90 ||
irot->get_rotation_ccw() == 270) {
if (!ispe_read) {
return {heif_error_Invalid_input, heif_suberror_No_ispe_property};
}
// swap width and height
image->set_resolution(image->get_height(),
image->get_width());
}
// TODO: apply irot to camera extrinsic matrix
}
}
}
// --- remove auxiliary from top-level images and assign to their respective image
auto iref_box = m_heif_file->get_iref_box();
if (iref_box) {
// m_top_level_images.clear();
for (auto& pair : m_all_images) {
auto& image = pair.second;
std::vector references = iref_box->get_references_from(image->get_id());
for (const Box_iref::Reference& ref : references) {
uint32_t type = ref.header.get_short_type();
if (type == fourcc("thmb")) {
// --- this is a thumbnail image, attach to the main image
std::vector refs = ref.to_item_ID;
for (heif_item_id ref: refs) {
image->set_is_thumbnail();
auto master_iter = m_all_images.find(ref);
if (master_iter == m_all_images.end()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Thumbnail references a non-existing image");
}
if (master_iter->second->is_thumbnail()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Thumbnail references another thumbnail");
}
if (image.get() == master_iter->second.get()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Recursive thumbnail image detected");
}
master_iter->second->add_thumbnail(image);
}
remove_top_level_image(image);
}
else if (type == fourcc("auxl")) {
// --- this is an auxiliary image
// check whether it is an alpha channel and attach to the main image if yes
std::shared_ptr auxC_property = image->get_property();
if (!auxC_property) {
std::stringstream sstr;
sstr << "No auxC property for image " << image->get_id();
return Error(heif_error_Invalid_input,
heif_suberror_Auxiliary_image_type_unspecified,
sstr.str());
}
std::vector refs = ref.to_item_ID;
// alpha channel
if (auxC_property->get_aux_type() == "urn:mpeg:avc:2015:auxid:1" || // HEIF (avc)
auxC_property->get_aux_type() == "urn:mpeg:hevc:2015:auxid:1" || // HEIF (h265)
auxC_property->get_aux_type() == "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha") { // MIAF
for (heif_item_id ref: refs) {
auto master_iter = m_all_images.find(ref);
if (master_iter == m_all_images.end()) {
if (!m_heif_file->has_item_with_id(ref)) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Non-existing alpha image referenced");
}
continue;
}
auto master_img = master_iter->second;
if (image.get() == master_img.get()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Recursive alpha image detected");
}
image->set_is_alpha_channel();
master_img->set_alpha_channel(image);
}
}
// depth channel
if (auxC_property->get_aux_type() == "urn:mpeg:hevc:2015:auxid:2" || // HEIF
auxC_property->get_aux_type() == "urn:mpeg:mpegB:cicp:systems:auxiliary:depth") { // AVIF
image->set_is_depth_channel();
for (heif_item_id ref: refs) {
auto master_iter = m_all_images.find(ref);
if (master_iter == m_all_images.end()) {
if (!m_heif_file->has_item_with_id(ref)) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Non-existing depth image referenced");
}
continue;
}
if (image.get() == master_iter->second.get()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Recursive depth image detected");
}
master_iter->second->set_depth_channel(image);
const auto& subtypes = auxC_property->get_subtypes();
if (!subtypes.empty()) {
std::vector> sei_messages;
Error err = decode_hevc_aux_sei_messages(subtypes, sei_messages);
if (err) {
return err;
}
for (auto& msg : sei_messages) {
auto depth_msg = std::dynamic_pointer_cast(msg);
if (depth_msg) {
image->set_depth_representation_info(*depth_msg);
}
}
}
}
}
// --- generic aux image
image->set_is_aux_image(auxC_property->get_aux_type());
for (heif_item_id ref: refs) {
auto master_iter = m_all_images.find(ref);
if (master_iter == m_all_images.end()) {
if (!m_heif_file->has_item_with_id(ref)) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Non-existing aux image referenced");
}
continue;
}
if (image.get() == master_iter->second.get()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Recursive aux image detected");
}
master_iter->second->add_aux_image(image);
remove_top_level_image(image);
}
}
else {
// 'image' is a normal image, keep it as a top-level image
}
}
}
}
// --- check that HEVC images have an hvcC property
for (auto& pair : m_all_images) {
auto& image = pair.second;
if (image->get_item_error()) {
continue;
}
std::shared_ptr infe = m_heif_file->get_infe_box(image->get_id());
if (infe->get_item_type_4cc() == fourcc("hvc1")) {
auto ipma = m_heif_file->get_ipma_box();
auto ipco = m_heif_file->get_ipco_box();
if (!ipco->get_property_for_item_ID(image->get_id(), ipma, fourcc("hvcC"))) {
return Error(heif_error_Invalid_input,
heif_suberror_No_hvcC_box,
"No hvcC property in hvc1 type image");
}
}
if (infe->get_item_type_4cc() == fourcc("vvc1")) {
auto ipma = m_heif_file->get_ipma_box();
auto ipco = m_heif_file->get_ipco_box();
if (!ipco->get_property_for_item_ID(image->get_id(), ipma, fourcc("vvcC"))) {
return Error(heif_error_Invalid_input,
heif_suberror_No_vvcC_box,
"No vvcC property in vvc1 type image");
}
}
}
// --- assign color profile from grid tiles to main image when main image has no profile assigned
for (auto& pair : m_all_images) {
auto& image = pair.second;
auto id = pair.first;
if (image->get_item_error()) {
continue;
}
auto infe_box = m_heif_file->get_infe_box(id);
if (!infe_box) {
continue;
}
if (!iref_box) {
break;
}
if (infe_box->get_item_type_4cc() == fourcc("grid")) {
std::vector image_references = iref_box->get_references(id, fourcc("dimg"));
if (image_references.empty()) {
continue; // TODO: can this every happen?
}
auto tileId = image_references.front();
auto iter = m_all_images.find(tileId);
if (iter == m_all_images.end()) {
continue; // invalid grid entry
}
auto tile_img = iter->second;
if (image->get_color_profile_icc() == nullptr && tile_img->get_color_profile_icc()) {
image->set_color_profile(tile_img->get_color_profile_icc());
}
if (image->get_color_profile_nclx() == nullptr && tile_img->get_color_profile_nclx()) {
image->set_color_profile(tile_img->get_color_profile_nclx());
}
}
}
// --- read metadata and assign to image
for (heif_item_id id : image_IDs) {
uint32_t item_type = m_heif_file->get_item_type_4cc(id);
std::string content_type = m_heif_file->get_content_type(id);
// 'rgan': skip region annotations, handled next
// 'iden': iden images are no metadata
if (item_type_is_image(item_type, content_type) || item_type == fourcc("rgan")) {
continue;
}
std::string item_uri_type = m_heif_file->get_item_uri_type(id);
// we now assign all kinds of metadata to the image, not only 'Exif' and 'XMP'
std::shared_ptr metadata = std::make_shared();
metadata->item_id = id;
metadata->item_type = fourcc_to_string(item_type);
metadata->content_type = content_type;
metadata->item_uri_type = std::move(item_uri_type);
Error err = m_heif_file->get_uncompressed_item_data(id, &(metadata->m_data));
if (err) {
if (item_type == fourcc("Exif") || item_type == fourcc("mime")) {
// these item types should have data
return err;
}
else {
// anything else is probably something that we don't understand yet
continue;
}
}
// --- assign metadata to the image
if (iref_box) {
std::vector references = iref_box->get_references(id, fourcc("cdsc"));
for (heif_item_id exif_image_id : references) {
auto img_iter = m_all_images.find(exif_image_id);
if (img_iter == m_all_images.end()) {
if (!m_heif_file->has_item_with_id(exif_image_id)) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Metadata assigned to non-existing image");
}
continue;
}
img_iter->second->add_metadata(metadata);
}
}
}
// --- set premultiplied alpha flag
for (heif_item_id id : image_IDs) {
if (iref_box) {
std::vector references = iref_box->get_references(id, fourcc("prem"));
for (heif_item_id ref : references) {
(void)ref;
heif_item_id color_image_id = id;
auto img_iter = m_all_images.find(color_image_id);
if (img_iter == m_all_images.end()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"`prem` link assigned to non-existing image");
}
img_iter->second->set_is_premultiplied_alpha(true);
}
}
}
// --- read region item and assign to image(s)
for (heif_item_id id : image_IDs) {
uint32_t item_type = m_heif_file->get_item_type_4cc(id);
if (item_type != fourcc("rgan")) {
continue;
}
std::shared_ptr region_item = std::make_shared();
region_item->item_id = id;
std::vector region_data;
Error err = m_heif_file->get_uncompressed_item_data(id, ®ion_data);
if (err) {
return err;
}
region_item->parse(region_data);
if (iref_box) {
std::vector references = iref_box->get_references_from(id);
for (const auto& ref : references) {
if (ref.header.get_short_type() == fourcc("cdsc")) {
std::vector refs = ref.to_item_ID;
for (uint32_t ref : refs) {
uint32_t image_id = ref;
auto img_iter = m_all_images.find(image_id);
if (img_iter == m_all_images.end()) {
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
"Region item assigned to non-existing image");
}
img_iter->second->add_region_item_id(id);
m_region_items.push_back(region_item);
}
}
/* When the geometry 'mask' of a region is represented by a mask stored in
* another image item the image item containing the mask shall be identified
* by an item reference of type 'mask' from the region item to the image item
* containing the mask. */
if (ref.header.get_short_type() == fourcc("mask")) {
std::vector refs = ref.to_item_ID;
size_t mask_index = 0;
for (int j = 0; j < region_item->get_number_of_regions(); j++) {
if (region_item->get_regions()[j]->getRegionType() == heif_region_type_referenced_mask) {
std::shared_ptr mask_geometry = std::dynamic_pointer_cast(region_item->get_regions()[j]);
if (mask_index >= refs.size()) {
return Error(heif_error_Invalid_input,
heif_suberror_Unspecified,
"Region mask reference with non-existing mask image reference");
}
uint32_t mask_image_id = refs[mask_index];
if (!is_image(mask_image_id)) {
return Error(heif_error_Invalid_input,
heif_suberror_Unspecified,
"Region mask referenced item is not an image");
}
auto mask_image = get_image(mask_image_id, true);
if (auto error = mask_image->get_item_error()) {
return error;
}
mask_geometry->referenced_item = mask_image_id;
if (mask_geometry->width == 0) {
mask_geometry->width = mask_image->get_ispe_width();
}
if (mask_geometry->height == 0) {
mask_geometry->height = mask_image->get_ispe_height();
}
mask_index += 1;
remove_top_level_image(mask_image);
}
}
}
}
}
}
return Error::Ok;
}
bool HeifContext::has_alpha(heif_item_id ID) const
{
auto imgIter = m_all_images.find(ID);
if (imgIter == m_all_images.end()) {
return false;
}
auto img = imgIter->second;
// --- has the image an auxiliary alpha image?
if (img->get_alpha_channel() != nullptr) {
return true;
}
if (img->has_coded_alpha_channel()) {
return true;
}
heif_colorspace colorspace;
heif_chroma chroma;
Error err = img->get_coded_image_colorspace(&colorspace, &chroma);
if (err) {
return false;
}
if (chroma == heif_chroma_interleaved_RGBA ||
chroma == heif_chroma_interleaved_RRGGBBAA_BE ||
chroma == heif_chroma_interleaved_RRGGBBAA_LE) {
return true;
}
// --- if the image is a 'grid', check if there is alpha in any of the tiles
// TODO: move this into ImageItem
uint32_t image_type = m_heif_file->get_item_type_4cc(ID);
if (image_type == fourcc("grid")) {
std::vector grid_data;
Error error = m_heif_file->get_uncompressed_item_data(ID, &grid_data);
if (error) {
return false;
}
ImageGrid grid;
err = grid.parse(grid_data);
if (err) {
return false;
}
auto iref_box = m_heif_file->get_iref_box();
if (!iref_box) {
return false;
}
std::vector image_references = iref_box->get_references(ID, fourcc("dimg"));
if ((int) image_references.size() != grid.get_rows() * grid.get_columns()) {
return false;
}
// --- check that all image IDs are valid images
for (heif_item_id tile_id : image_references) {
if (!is_image(tile_id)) {
return false;
}
}
// --- check whether at least one tile has an alpha channel
bool has_alpha = false;
for (heif_item_id tile_id : image_references) {
auto iter = m_all_images.find(tile_id);
if (iter == m_all_images.end()) {
return false;
}
const std::shared_ptr tileImg = iter->second;
has_alpha |= tileImg->get_alpha_channel() != nullptr;
}
return has_alpha;
}
else {
// TODO: what about overlays ?
return false;
}
}
Error HeifContext::get_id_of_non_virtual_child_image(heif_item_id id, heif_item_id& out) const
{
uint32_t image_type = m_heif_file->get_item_type_4cc(id);
if (image_type == fourcc("grid") ||
image_type == fourcc("iden") ||
image_type == fourcc("iovl")) {
auto iref_box = m_heif_file->get_iref_box();
if (!iref_box) {
return Error(heif_error_Invalid_input,
heif_suberror_No_item_data,
"Derived image does not reference any other image items");
}
std::vector image_references = iref_box->get_references(id, fourcc("dimg"));
// TODO: check whether this really can be recursive (e.g. overlay of grid images)
if (image_references.empty() || image_references[0] == id) {
return Error(heif_error_Invalid_input,
heif_suberror_No_item_data,
"Derived image does not reference any other image items");
}
else {
return get_id_of_non_virtual_child_image(image_references[0], out);
}
}
else {
if (m_all_images.find(id) == m_all_images.end()) {
std::stringstream sstr;
sstr << "Image item " << id << " referenced, but it does not exist\n";
return Error(heif_error_Invalid_input,
heif_suberror_Nonexisting_item_referenced,
sstr.str());
}
else if (dynamic_cast(m_all_images.find(id)->second.get())) {
// Should er return an error here or leave it to the follow-up code to detect that?
}
out = id;
return Error::Ok;
}
}
Result> HeifContext::decode_image(heif_item_id ID,
heif_colorspace out_colorspace,
heif_chroma out_chroma,
const struct heif_decoding_options& options,
bool decode_only_tile, uint32_t tx, uint32_t ty) const
{
std::shared_ptr imgitem;
if (m_all_images.find(ID) != m_all_images.end()) {
imgitem = m_all_images.find(ID)->second;
}
// Note: this may happen, for example when an 'iden' image references a non-existing image item.
if (imgitem == nullptr) {
return Error(heif_error_Invalid_input, heif_suberror_Nonexisting_item_referenced);
}
auto decodingResult = imgitem->decode_image(options, decode_only_tile, tx, ty);
if (decodingResult.error) {
return decodingResult.error;
}
std::shared_ptr img = decodingResult.value;
// --- convert to output chroma format
heif_colorspace target_colorspace = (out_colorspace == heif_colorspace_undefined ?
img->get_colorspace() :
out_colorspace);
heif_chroma target_chroma = (out_chroma == heif_chroma_undefined ?
img->get_chroma_format() : out_chroma);
bool different_chroma = (target_chroma != img->get_chroma_format());
bool different_colorspace = (target_colorspace != img->get_colorspace());
uint8_t img_bpp = img->get_visual_image_bits_per_pixel();
uint8_t converted_output_bpp = (options.convert_hdr_to_8bit && img_bpp > 8) ? 8 : 0 /* keep input depth */;
if (different_chroma ||
different_colorspace ||
converted_output_bpp) {
auto img_result = convert_colorspace(img, target_colorspace, target_chroma, nullptr,
converted_output_bpp, options.color_conversion_options, get_security_limits());
if (img_result.error) {
return img_result.error;
}
else {
img = *img_result;
}
}
img->add_warnings(imgitem->get_decoding_warnings());
return img;
}
static Result>
create_alpha_image_from_image_alpha_channel(const std::shared_ptr& image,
const heif_security_limits* limits)
{
// --- generate alpha image
std::shared_ptr alpha_image = std::make_shared();
alpha_image->create(image->get_width(), image->get_height(),
heif_colorspace_monochrome, heif_chroma_monochrome);
if (image->has_channel(heif_channel_Alpha)) {
alpha_image->copy_new_plane_from(image, heif_channel_Alpha, heif_channel_Y, limits);
}
else if (image->get_chroma_format() == heif_chroma_interleaved_RGBA) {
if (auto err = alpha_image->extract_alpha_from_RGBA(image, limits)) {
return err;
}
}
// TODO: 16 bit
// --- set nclx profile with full-range flag
auto nclx = std::make_shared();
nclx->set_undefined();
nclx->set_full_range_flag(true); // this is the default, but just to be sure in case the defaults change
alpha_image->set_color_profile_nclx(nclx);
return alpha_image;
}
Result> HeifContext::encode_image(const std::shared_ptr& pixel_image,
struct heif_encoder* encoder,
const struct heif_encoding_options& in_options,
enum heif_image_input_class input_class)
{
std::shared_ptr output_image_item = ImageItem::alloc_for_compression_format(this, encoder->plugin->compression_format);
#if 0
// TODO: the hdlr box is not the right place for comments
// m_heif_file->set_hdlr_library_info(encoder->plugin->get_plugin_name());
case heif_compression_mask: {
error = encode_image_as_mask(pixel_image,
encoder,
options,
input_class,
out_image);
}
break;
default:
return Error(heif_error_Encoder_plugin_error, heif_suberror_Unsupported_codec);
}
#endif
// --- check whether we have to convert the image color space
// The reason for doing the color conversion here is that the input might be an RGBA image and the color conversion
// will extract the alpha plane anyway. We can reuse that plane below instead of having to do a new conversion.
heif_encoding_options options = in_options;
if (const auto* nclx = output_image_item->get_forced_output_nclx()) {
options.output_nclx_profile = const_cast(nclx);
}
Result> srcImageResult = output_image_item->convert_colorspace_for_encoding(pixel_image,
encoder,
options);
if (srcImageResult.error) {
return srcImageResult.error;
}
std::shared_ptr colorConvertedImage = srcImageResult.value;
Error err = output_image_item->encode_to_item(this,
colorConvertedImage,
encoder, options, input_class);
if (err) {
return err;
}
insert_image_item(output_image_item->get_id(), output_image_item);
// --- if there is an alpha channel, add it as an additional image
if (options.save_alpha_channel &&
colorConvertedImage->has_alpha() &&
output_image_item->get_auxC_alpha_channel_type() != nullptr) { // does not need a separate alpha aux image
// --- generate alpha image
// TODO: can we directly code a monochrome image instead of the dummy color channels?
std::shared_ptr alpha_image;
auto alpha_image_result = create_alpha_image_from_image_alpha_channel(colorConvertedImage, get_security_limits());
if (!alpha_image_result) {
return alpha_image_result.error;
}
alpha_image = *alpha_image_result;
// --- encode the alpha image
auto alphaEncodingResult = encode_image(alpha_image, encoder, options,
heif_image_input_class_alpha);
if (alphaEncodingResult.error) {
return alphaEncodingResult.error;
}
std::shared_ptr heif_alpha_image = *alphaEncodingResult;
m_heif_file->add_iref_reference(heif_alpha_image->get_id(), fourcc("auxl"), {output_image_item->get_id()});
m_heif_file->set_auxC_property(heif_alpha_image->get_id(), output_image_item->get_auxC_alpha_channel_type());
if (pixel_image->is_premultiplied_alpha()) {
m_heif_file->add_iref_reference(output_image_item->get_id(), fourcc("prem"), {heif_alpha_image->get_id()});
}
}
std::vector> properties;
err = m_heif_file->get_properties(output_image_item->get_id(), properties);
if (err) {
return err;
}
output_image_item->set_properties(properties);
m_heif_file->set_brand(encoder->plugin->compression_format,
output_image_item->is_miaf_compatible());
return output_image_item;
}
void HeifContext::set_primary_image(const std::shared_ptr& image)
{
// update heif context
if (m_primary_image) {
m_primary_image->set_primary(false);
}
image->set_primary(true);
m_primary_image = image;
// update pitm box in HeifFile
m_heif_file->set_primary_item_id(image->get_id());
}
Error HeifContext::assign_thumbnail(const std::shared_ptr& master_image,
const std::shared_ptr& thumbnail_image)
{
m_heif_file->add_iref_reference(thumbnail_image->get_id(),
fourcc("thmb"), {master_image->get_id()});
return Error::Ok;
}
Result> HeifContext::encode_thumbnail(const std::shared_ptr& image,
struct heif_encoder* encoder,
const struct heif_encoding_options& options,
int bbox_size)
{
int orig_width = image->get_width();
int orig_height = image->get_height();
int thumb_width, thumb_height;
if (orig_width <= bbox_size && orig_height <= bbox_size) {
// original image is smaller than thumbnail size -> do not encode any thumbnail
return Error::Ok;
}
else if (orig_width > orig_height) {
thumb_height = orig_height * bbox_size / orig_width;
thumb_width = bbox_size;
}
else {
thumb_width = orig_width * bbox_size / orig_height;
thumb_height = bbox_size;
}
// round size to even width and height
thumb_width &= ~1;
thumb_height &= ~1;
std::shared_ptr thumbnail_image;
Error error = image->scale_nearest_neighbor(thumbnail_image, thumb_width, thumb_height, get_security_limits());
if (error) {
return error;
}
auto encodingResult = encode_image(thumbnail_image,
encoder, options,
heif_image_input_class_thumbnail);
if (encodingResult.error) {
return encodingResult.error;
}
return *encodingResult;
}
Error HeifContext::add_exif_metadata(const std::shared_ptr& master_image, const void* data, int size)
{
// find location of TIFF header
uint32_t offset = 0;
const char* tiffmagic1 = "MM\0*";
const char* tiffmagic2 = "II*\0";
while (offset + 4 < (unsigned int) size) {
if (!memcmp((uint8_t*) data + offset, tiffmagic1, 4)) break;
if (!memcmp((uint8_t*) data + offset, tiffmagic2, 4)) break;
offset++;
}
if (offset >= (unsigned int) size) {
return Error(heif_error_Usage_error,
heif_suberror_Invalid_parameter_value,
"Could not find location of TIFF header in Exif metadata.");
}
std::vector data_array;
data_array.resize(size + 4);
data_array[0] = (uint8_t) ((offset >> 24) & 0xFF);
data_array[1] = (uint8_t) ((offset >> 16) & 0xFF);
data_array[2] = (uint8_t) ((offset >> 8) & 0xFF);
data_array[3] = (uint8_t) ((offset) & 0xFF);
memcpy(data_array.data() + 4, data, size);
return add_generic_metadata(master_image,
data_array.data(), (int) data_array.size(),
fourcc("Exif"), nullptr, nullptr, heif_metadata_compression_off, nullptr);
}
Error HeifContext::add_XMP_metadata(const std::shared_ptr& master_image, const void* data, int size,
heif_metadata_compression compression)
{
return add_generic_metadata(master_image, data, size, fourcc("mime"), "application/rdf+xml", nullptr, compression, nullptr);
}
Error HeifContext::add_generic_metadata(const std::shared_ptr& master_image, const void* data, int size,
uint32_t item_type, const char* content_type, const char* item_uri_type, heif_metadata_compression compression,
heif_item_id* out_item_id)
{
// create an infe box describing what kind of data we are storing (this also creates a new ID)
auto metadata_infe_box = m_heif_file->add_new_infe_box(item_type);
metadata_infe_box->set_hidden_item(true);
if (content_type != nullptr) {
metadata_infe_box->set_content_type(content_type);
}
heif_item_id metadata_id = metadata_infe_box->get_item_ID();
if (out_item_id) {
*out_item_id = metadata_id;
}
// we assign this data to the image
m_heif_file->add_iref_reference(metadata_id,
fourcc("cdsc"), {master_image->get_id()});
// --- metadata compression
if (compression == heif_metadata_compression_auto) {
compression = heif_metadata_compression_off; // currently, we don't use header compression by default
}
// only set metadata compression for MIME type data which has 'content_encoding' field
if (compression != heif_metadata_compression_off &&
item_type != fourcc("mime")) {
// TODO: error, compression not supported
}
std::vector data_array;
if (compression == heif_metadata_compression_zlib) {
#if HAVE_ZLIB
data_array = compress_zlib((const uint8_t*) data, size);
metadata_infe_box->set_content_encoding("compress_zlib");
#else
return Error(heif_error_Unsupported_feature,
heif_suberror_Unsupported_header_compression_method);
#endif
}
else if (compression == heif_metadata_compression_deflate) {
#if HAVE_ZLIB
data_array = compress_zlib((const uint8_t*) data, size);
metadata_infe_box->set_content_encoding("deflate");
#else
return Error(heif_error_Unsupported_feature,
heif_suberror_Unsupported_header_compression_method);
#endif
}
else {
// uncompressed data, plain copy
data_array.resize(size);
memcpy(data_array.data(), data, size);
}
// copy the data into the file, store the pointer to it in an iloc box entry
m_heif_file->append_iloc_data(metadata_id, data_array, 0);
return Error::Ok;
}
heif_property_id HeifContext::add_property(heif_item_id targetItem, std::shared_ptr property, bool essential)
{
heif_property_id id = m_heif_file->add_property(targetItem, property, essential);
return id;
}
Result HeifContext::add_pyramid_group(const std::vector& layer_item_ids)
{
struct pymd_entry
{
std::shared_ptr item;
uint32_t width = 0;
};
// --- sort all images by size
std::vector pymd_entries;
for (auto id : layer_item_ids) {
auto image_item = get_image(id, true);
if (auto error = image_item->get_item_error()) {
return error;
}
pymd_entry entry;
entry.item = image_item;
entry.width = image_item->get_width();
pymd_entries.emplace_back(entry);
}
std::sort(pymd_entries.begin(), pymd_entries.end(), [](const pymd_entry& a, const pymd_entry& b) {
return a.width < b.width;
});
// --- generate pymd box
auto pymd = std::make_shared();
std::vector layers;
std::vector