libheif-1.19.8/README.md000664 001750 001750 00000041165 15003473471 015606 0ustar00farindkfarindk000000 000000 # libheif [![Build Status](https://github.com/strukturag/libheif/workflows/build/badge.svg)](https://github.com/strukturag/libheif/actions) [![Build Status](https://ci.appveyor.com/api/projects/status/github/strukturag/libheif?svg=true)](https://ci.appveyor.com/project/strukturag/libheif) [![Coverity Scan Build Status](https://scan.coverity.com/projects/16641/badge.svg)](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 [![libheif packaging status](https://repology.org/badge/vertical-allrepos/libheif.svg?exclude_unsupported=1&columns=3&exclude_sources=modules,site&header=libheif%20packaging%20status)](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 shopify-logo * 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.cfg000664 001750 001750 00000000062 15003473471 016110 0ustar00farindkfarindk000000 000000 set noparent filter=-,+build/include_what_you_use libheif-1.19.8/COPYING000664 001750 001750 00000126516 15003473471 015366 0ustar00farindkfarindk000000 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 5ustar00farindkfarindk000000 000000 libheif-1.19.8/libheif/security_limits.h000664 001750 001750 00000003076 15003473471 021331 0ustar00farindkfarindk000000 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.h000664 001750 001750 00000012321 15003473471 017036 0ustar00farindkfarindk000000 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.h000664 001750 001750 00000030106 15003473471 020065 0ustar00farindkfarindk000000 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.cc000664 001750 001750 00000010142 15003473471 021610 0ustar00farindkfarindk000000 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.h000664 001750 001750 00000124317 15003473471 016673 0ustar00farindkfarindk000000 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 #endiflibheif-1.19.8/libheif/context.cc000664 001750 001750 00000136747 15003473471 017737 0ustar00farindkfarindk000000 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 ids; auto base_item = pymd_entries.back().item; uint32_t tile_w=0, tile_h=0; base_item->get_tile_size(tile_w, tile_h); uint32_t last_width=0, last_height=0; for (const auto& entry : pymd_entries) { auto layer_item = entry.item; if (false) { // according to pymd definition, we should check that all layers have the same tile size uint32_t item_tile_w = 0, item_tile_h = 0; base_item->get_tile_size(item_tile_w, item_tile_h); if (item_tile_w != tile_w || item_tile_h != tile_h) { // TODO: add warning that tile sizes are not the same } } heif_image_tiling tiling = layer_item->get_heif_image_tiling(); if (tiling.image_width < last_width || tiling.image_height < last_height) { return Error{ heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Multi-resolution pyramid images have to be provided ordered from smallest to largest." }; } last_width = tiling.image_width; last_height = tiling.image_height; Box_pymd::LayerInfo layer{}; layer.layer_binning = (uint16_t)(base_item->get_width() / tiling.image_width); layer.tiles_in_layer_row_minus1 = static_cast(tiling.num_rows - 1); layer.tiles_in_layer_column_minus1 = static_cast(tiling.num_columns - 1); layers.push_back(layer); ids.push_back(layer_item->get_id()); } heif_item_id group_id = m_heif_file->get_unused_item_id(); pymd->set_group_id(group_id); pymd->set_layers((uint16_t)tile_w, (uint16_t)tile_h, layers, ids); m_heif_file->add_entity_group_box(pymd); // add back-references to base image for (size_t i = 0; i < ids.size() - 1; i++) { m_heif_file->add_iref_reference(ids[i], fourcc("base"), {ids.back()}); } return {group_id}; } libheif-1.19.8/libheif/api/000775 001750 001750 00000000000 15003473472 016474 5ustar00farindkfarindk000000 000000 libheif-1.19.8/libheif/api/libheif/000775 001750 001750 00000000000 15003473472 020076 5ustar00farindkfarindk000000 000000 libheif-1.19.8/libheif/api/libheif/heif_regions.h000664 001750 001750 00000115216 15003473471 022715 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 Dirk Farin * Copyright (c) 2023 Brad Hards * * 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_HEIF_REGIONS_H #define LIBHEIF_HEIF_REGIONS_H #include "heif.h" #ifdef __cplusplus extern "C" { #endif // --- region items and annotations // See ISO/IEC 23008-12:2022 Section 6.10 "Region items and region annotations" struct heif_region_item; /** * Region type. * * Each region item will contain zero or more regions, which may have different geometry or * mask representations. */ enum heif_region_type { /** * Point gemetry. * * The region is represented by a single point. */ heif_region_type_point = 0, /** * Rectangle geometry. * * The region is represented by a top left position, and a size defined * by a width and height. All of the interior points and the edge are * part of the region. */ heif_region_type_rectangle = 1, /** * Ellipse geometry. * * The region is represented by a centre point, and radii in the X and * Y directions. All of the interior points and the edge are part of the * region. */ heif_region_type_ellipse = 2, /** * Polygon geometry. * * The region is represented by a sequence of points, which is considered * implicitly closed. All of the interior points and the edge are part * of the region. */ heif_region_type_polygon = 3, /** * Reference mask. * * The region geometry is described by the pixels in another image item, * which has a item reference of type `mask` from the region item to the * image item containing the mask. * * The image item containing the mask is one of: * * - a mask item (see ISO/IEC 23008-12:2022 Section 6.10.2), or a derived * image from a mask item * * - an image item in monochrome format (4:0:0 chroma) * * - an image item in colour format with luma and chroma planes (e.g. 4:2:0) * * If the pixel value is equal to the minimum sample value (e.g. 0 for unsigned * integer), the pixel is not part of the region. If the pixel value is equal * to the maximum sample value (e.g. 255 for 8 bit unsigned integer), the pixel * is part of the region. If the pixel value is between the minimum sample value * and maximum sample value, the pixel value represents an (application defined) * probability that the pixel is part of the region, where higher pixel values * correspond to higher probability values. */ heif_region_type_referenced_mask = 4, /** * Inline mask. * * The region geometry is described by a sequence of bits stored in inline * in the region, one bit per pixel. If the bit value is `1`, the pixel is * part of the region. If the bit value is `0`, the pixel is not part of the * region. */ heif_region_type_inline_mask = 5, /** * Polyline geometry. * * The region is represented by a sequence of points, which are not * considered to form a closed surface. Only the edge is part of the region. */ heif_region_type_polyline = 6 }; struct heif_region; /** * Get the number of region items that are attached to an image. * * @param image_handle the image handle for the image to query. * @return the number of region items, which can be zero. */ LIBHEIF_API int heif_image_handle_get_number_of_region_items(const struct heif_image_handle* image_handle); /** * Get the region item identifiers for the region items attached to an image. * * Possible usage (in C++): * @code * int numRegionItems = heif_image_handle_get_number_of_region_items(handle); * if (numRegionItems > 0) { * std::vector region_item_ids(numRegionItems); * heif_image_handle_get_list_of_region_item_ids(handle, region_item_ids.data(), numRegionItems); * // use region item ids * } * @endcode * * @param image_handle the image handle for the parent image to query * @param region_item_ids_array array to put the item identifiers into * @param max_count the maximum number of region identifiers * @return the number of region item identifiers that were returned. */ LIBHEIF_API int heif_image_handle_get_list_of_region_item_ids(const struct heif_image_handle* image_handle, heif_item_id* region_item_ids_array, int max_count); /** * Get the region item. * * Caller is responsible for release of the output heif_region_item with heif_region_item_release(). * * @param context the context to get the region item from, usually from a file operation * @param region_item_id the identifier for the region item * @param out pointer to pointer to the resulting region item * @return heif_error_ok on success, or an error value indicating the problem */ LIBHEIF_API struct heif_error heif_context_get_region_item(const struct heif_context* context, heif_item_id region_item_id, struct heif_region_item** out); /** * Get the item identifier for a region item. * * @param region_item the region item to query * @return the region item identifier (or -1 if the region_item is null) */ LIBHEIF_API heif_item_id heif_region_item_get_id(struct heif_region_item* region_item); /** * Release a region item. * * This should be called on items from heif_context_get_region_item(). * * @param region_item the item to release. */ LIBHEIF_API void heif_region_item_release(struct heif_region_item* region_item); /** * Get the reference size for a region item. * * The reference size specifies the coordinate space used for the region items. * When the reference size does not match the image size, the regions need to be * scaled to correspond. * * @param out_width the return value for the reference width (before any transformation) * @param out_height the return value for the reference height (before any transformation) */ LIBHEIF_API void heif_region_item_get_reference_size(struct heif_region_item*, uint32_t* out_width, uint32_t* out_height); /** * Get the number of regions within a region item. * * @param region_item the region item to query. * @return the number of regions */ LIBHEIF_API int heif_region_item_get_number_of_regions(const struct heif_region_item* region_item); /** * Get the regions that are part of a region item. * * Caller is responsible for releasing the returned `heif_region` objects, using heif_region_release() * on each region, or heif_region_release_many() on the returned array. * * Possible usage (in C++): * @code * int num_regions = heif_image_handle_get_number_of_regions(region_item); * if (num_regions > 0) { * std::vector regions(num_regions); * int n = heif_region_item_get_list_of_regions(region_item, regions.data(), (int)regions.size()); * // use regions * heif_region_release_many(regions.data(), n); * } * @endcode * * @param region_item the region_item to query * @param out_regions_array array to put the region pointers into * @param max_count the maximum number of regions, which needs to correspond to the size of the out_regions_array * @return the number of regions that were returned. */ LIBHEIF_API int heif_region_item_get_list_of_regions(const struct heif_region_item* region_item, struct heif_region** out_regions_array, int max_count); /** * Release a region. * * This should be called on regions from heif_region_item_get_list_of_regions(). * * @param region the region to release. * * \sa heif_region_release_many() to release the whole list */ LIBHEIF_API void heif_region_release(const struct heif_region* region); /** * Release a list of regions. * * This should be called on the list of regions from heif_region_item_get_list_of_regions(). * * @param regions_array the regions to release. * @param num_items the number of items in the array * * \sa heif_region_release() to release a single region */ LIBHEIF_API void heif_region_release_many(const struct heif_region* const* regions_array, int num_items); /** * Get the region type for a specified region. * * @param region the region to query * @return the corresponding region type as an enumeration value */ LIBHEIF_API enum heif_region_type heif_region_get_type(const struct heif_region* region); // When querying the region geometry, there is a version without and a version with "_transformed" suffix. // The version without returns the coordinates in the reference coordinate space. // The version with "_transformed" suffix give the coordinates in pixels after all transformative properties have been applied. /** * Get the values for a point region. * * This returns the coordinates in the reference coordinate space (from the parent region item). * * @param region the region to query, which must be of type #heif_region_type_point. * @param out_x the X coordinate, where 0 is the left-most column. * @param out_y the Y coordinate, where 0 is the top-most row. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_point_transformed() for a version in pixels after all transformative properties have been applied. */ LIBHEIF_API struct heif_error heif_region_get_point(const struct heif_region* region, int32_t* out_x, int32_t* out_y); /** * Get the transformed values for a point region. * * This returns the coordinates in pixels after all transformative properties have been applied. * * @param region the region to query, which must be of type #heif_region_type_point. * @param image_id the identifier for the image to transform / scale the region to * @param out_x the X coordinate, where 0 is the left-most column. * @param out_y the Y coordinate, where 0 is the top-most row. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_point() for a version that returns the values in the reference coordinate space. */ LIBHEIF_API struct heif_error heif_region_get_point_transformed(const struct heif_region* region, heif_item_id image_id, double* out_x, double* out_y); /** * Get the values for a rectangle region. * * This returns the values in the reference coordinate space (from the parent region item). * The rectangle is represented by a top left corner position, and a size defined * by a width and height. All of the interior points and the edge are * part of the region. * * @param region the region to query, which must be of type #heif_region_type_rectangle. * @param out_x the X coordinate for the top left corner, where 0 is the left-most column. * @param out_y the Y coordinate for the top left corner, where 0 is the top-most row. * @param out_width the width of the rectangle * @param out_height the height of the rectangle * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_rectangle_transformed() for a version in pixels after all transformative properties have been applied. */ LIBHEIF_API struct heif_error heif_region_get_rectangle(const struct heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_width, uint32_t* out_height); /** * Get the transformed values for a rectangle region. * * This returns the coordinates in pixels after all transformative properties have been applied. * The rectangle is represented by a top left corner position, and a size defined * by a width and height. All of the interior points and the edge are * part of the region. * * @param region the region to query, which must be of type #heif_region_type_rectangle. * @param image_id the identifier for the image to transform / scale the region to * @param out_x the X coordinate for the top left corner, where 0 is the left-most column. * @param out_y the Y coordinate for the top left corner, where 0 is the top-most row. * @param out_width the width of the rectangle * @param out_height the height of the rectangle * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_rectangle() for a version that returns the values in the reference coordinate space. */ LIBHEIF_API struct heif_error heif_region_get_rectangle_transformed(const struct heif_region* region, heif_item_id image_id, double* out_x, double* out_y, double* out_width, double* out_height); /** * Get the values for an ellipse region. * * This returns the values in the reference coordinate space (from the parent region item). * The ellipse is represented by a centre position, and a size defined * by radii in the X and Y directions. All of the interior points and the edge are * part of the region. * * @param region the region to query, which must be of type #heif_region_type_ellipse. * @param out_x the X coordinate for the centre point, where 0 is the left-most column. * @param out_y the Y coordinate for the centre point, where 0 is the top-most row. * @param out_radius_x the radius value in the X direction. * @param out_radius_y the radius value in the Y direction * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_ellipse_transformed() for a version in pixels after all transformative properties have been applied. */ LIBHEIF_API struct heif_error heif_region_get_ellipse(const struct heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_radius_x, uint32_t* out_radius_y); /** * Get the transformed values for an ellipse region. * * This returns the coordinates in pixels after all transformative properties have been applied. * The ellipse is represented by a centre position, and a size defined * by radii in the X and Y directions. All of the interior points and the edge are * part of the region. * * @param region the region to query, which must be of type #heif_region_type_ellipse. * @param image_id the identifier for the image to transform / scale the region to * @param out_x the X coordinate for the centre point, where 0 is the left-most column. * @param out_y the Y coordinate for the centre point, where 0 is the top-most row. * @param out_radius_x the radius value in the X direction. * @param out_radius_y the radius value in the Y direction * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_ellipse() for a version that returns the values in the reference coordinate space. */ LIBHEIF_API struct heif_error heif_region_get_ellipse_transformed(const struct heif_region* region, heif_item_id image_id, double* out_x, double* out_y, double* out_radius_x, double* out_radius_y); /** * Get the number of points in a polygon. * * @param region the region to query, which must be of type #heif_region_type_polygon * @return the number of points, or -1 on error. */ LIBHEIF_API int heif_region_get_polygon_num_points(const struct heif_region* region); /** * Get the points in a polygon region. * * This returns the values in the reference coordinate space (from the parent region item). * * A polygon is a sequence of points that form a closed shape. The first point does * not need to be repeated as the last point. All of the interior points and the edge are * part of the region. * The points are returned as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * @param region the region to equery, which must be of type #heif_region_type_polygon * @param out_pts_array the array to return the points in, which must have twice as many entries as there are points * in the polygon. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_polygon_points_transformed() for a version in pixels after all transformative properties have been applied. */ LIBHEIF_API struct heif_error heif_region_get_polygon_points(const struct heif_region* region, int32_t* out_pts_array); /** * Get the transformed points in a polygon region. * * This returns the coordinates in pixels after all transformative properties have been applied. * * A polygon is a sequence of points that form a closed shape. The first point does * not need to be repeated as the last point. All of the interior points and the edge are * part of the region. * The points are returned as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * @param region the region to equery, which must be of type #heif_region_type_polygon * @param image_id the identifier for the image to transform / scale the region to * @param out_pts_array the array to return the points in, which must have twice as many entries as there are points * in the polygon. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_polygon_points() for a version that returns the values in the reference coordinate space. */ LIBHEIF_API struct heif_error heif_region_get_polygon_points_transformed(const struct heif_region* region, heif_item_id image_id, double* out_pts_array); /** * Get the number of points in a polyline. * * @param region the region to query, which must be of type #heif_region_type_polyline * @return the number of points, or -1 on error. */ LIBHEIF_API int heif_region_get_polyline_num_points(const struct heif_region* region); /** * Get the points in a polyline region. * * This returns the values in the reference coordinate space (from the parent region item). * * A polyline is a sequence of points that does not form a closed shape. Even if the * polyline is closed, the only points that are part of the region are those that * intersect (even minimally) a one-pixel line drawn along the polyline. * The points are provided as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * Possible usage (in C++): * @code * int num_polyline_points = heif_region_get_polyline_num_points(region); * if (num_polyline_points > 0) { * std::vector polyline(num_polyline_points * 2); * heif_region_get_polyline_points(region, polyline.data()); * // do something with points ... * } * @endcode * * @param region the region to equery, which must be of type #heif_region_type_polyline * @param out_pts_array the array to return the points in, which must have twice as many entries as there are points * in the polyline. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_polyline_points_transformed() for a version in pixels after all transformative properties have been applied. */ LIBHEIF_API struct heif_error heif_region_get_polyline_points(const struct heif_region* region, int32_t* out_pts_array); /** * Get the transformed points in a polyline region. * * This returns the coordinates in pixels after all transformative properties have been applied. * * A polyline is a sequence of points that does not form a closed shape. Even if the * polyline is closed, the only points that are part of the region are those that * intersect (even minimally) a one-pixel line drawn along the polyline. * The points are provided as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * @param region the region to query, which must be of type #heif_region_type_polyline * @param image_id the identifier for the image to transform / scale the region to * @param out_pts_array the array to return the points in, which must have twice as many entries as there are points * in the polyline. * @return heif_error_ok on success, or an error value indicating the problem on failure * * \sa heif_region_get_polyline_points() for a version that returns the values in the reference coordinate space. */ LIBHEIF_API struct heif_error heif_region_get_polyline_points_transformed(const struct heif_region* region, heif_item_id image_id, double* out_pts_array); /** * Get a referenced item mask region. * * This returns the values in the reference coordinate space (from the parent region item). * The mask location is represented by a top left corner position, and a size defined * by a width and height. The value of each sample in that mask identifies whether the * corresponding pixel is part of the region. * * The mask is provided as an image in another item. The image item containing the mask * is one of: * * - a mask item (see ISO/IEC 23008-12:2022 Section 6.10.2), or a derived * image from a mask item * * - an image item in monochrome format (4:0:0 chroma) * * - an image item in colour format with luma and chroma planes (e.g. 4:2:0) * * If the pixel value is equal to the minimum sample value (e.g. 0 for unsigned * integer), the pixel is not part of the region. If the pixel value is equal * to the maximum sample value (e.g. 255 for 8 bit unsigned integer), the pixel * is part of the region. If the pixel value is between the minimum sample value * and maximum sample value, the pixel value represents an (application defined) * probability that the pixel is part of the region, where higher pixel values * correspond to higher probability values. * * @param region the region to query, which must be of type #heif_region_type_referenced_mask. * @param out_x the X coordinate for the top left corner, where 0 is the left-most column. * @param out_y the Y coordinate for the top left corner, where 0 is the top-most row. * @param out_width the width of the mask region * @param out_height the height of the mask region * @param out_mask_item_id the item identifier for the image that provides the mask. * @return heif_error_ok on success, or an error value indicating the problem on failure */ LIBHEIF_API struct heif_error heif_region_get_referenced_mask_ID(const struct heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_width, uint32_t* out_height, heif_item_id *out_mask_item_id); /** * Get the length of the data in an inline mask region. * * @param region the region to query, which must be of type #heif_region_type_inline_mask. * @return the number of bytes in the mask data, or 0 on error. */ LIBHEIF_API size_t heif_region_get_inline_mask_data_len(const struct heif_region* region); /** * Get data for an inline mask region. * * This returns the values in the reference coordinate space (from the parent region item). * The mask location is represented by a top left corner position, and a size defined * by a width and height. * * The mask is held as inline data on the region, one bit per pixel, most significant * bit first pixel, no padding. If the bit value is `1`, the corresponding pixel is * part of the region. If the bit value is `0`, the corresponding pixel is not part of the * region. * * Possible usage (in C++): * @code * long unsigned int data_len = heif_region_get_inline_mask_data_len(region); * int32_t x, y; * uint32_t width, height; * std::vector mask_data(data_len); * err = heif_region_get_inline_mask(region, &x, &y, &width, &height, mask_data.data()); * @endcode * * @param region the region to query, which must be of type #heif_region_type_inline_mask. * @param out_x the X coordinate for the top left corner, where 0 is the left-most column. * @param out_y the Y coordinate for the top left corner, where 0 is the top-most row. * @param out_width the width of the mask region * @param out_height the height of the mask region * @param out_mask_data the location to return the mask data * @return heif_error_ok on success, or an error value indicating the problem on failure */ LIBHEIF_API struct heif_error heif_region_get_inline_mask_data(const struct heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_width, uint32_t* out_height, uint8_t* out_mask_data); /** * Get a mask region image. * * This returns the values in the reference coordinate space (from the parent region item). * The mask location is represented by a top left corner position, and a size defined * by a width and height. * * This function works when the passed region is either a heif_region_type_referenced_mask or * a heif_region_type_inline_mask. * The returned image is a monochrome image where each pixel represents the (scaled) probability * of the pixel being part of the mask. * * If the region type is an inline mask, which always holds a binary mask, this function * converts the binary inline mask to an 8-bit monochrome image with the values '0' and '255'. * The pixel value is set to `255` where the pixel is part of the region, and `0` where the * pixel is not part of the region. * * @param region the region to query, which must be of type #heif_region_type_inline_mask. * @param out_x the X coordinate for the top left corner, where 0 is the left-most column. * @param out_y the Y coordinate for the top left corner, where 0 is the top-most row. * @param out_width the width of the mask region * @param out_height the height of the mask region * @param out_mask_image the returned mask image * @return heif_error_ok on success, or an error value indicating the problem on failure * * \note the caller is responsible for releasing the mask image */ LIBHEIF_API struct heif_error heif_region_get_mask_image(const struct heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_width, uint32_t* out_height, struct heif_image** out_mask_image); // --- adding region items /** * Add a region item to an image. * * The region item is a collection of regions (point, polyline, polygon, rectangle, ellipse or mask) * along with a reference size (width and height) that forms the coordinate basis for the regions. * * The concept is to add the region item, then add one or more regions to the region item. * * @param image_handle the image to attach the region item to. * @param reference_width the width of the reference size. * @param reference_height the height of the reference size. * @param out_region_item the resulting region item * @return heif_error_ok on success, or an error indicating the problem on failure */ LIBHEIF_API struct heif_error heif_image_handle_add_region_item(struct heif_image_handle* image_handle, uint32_t reference_width, uint32_t reference_height, struct heif_region_item** out_region_item); /** * Add a point region to the region item. * * @param region_item the region item that holds this point region * @param x the x value for the point location * @param y the y value for the point location * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_point(struct heif_region_item* region_item, int32_t x, int32_t y, struct heif_region** out_region); /** * Add a rectangle region to the region item. * * @param region_item the region item that holds this rectangle region * @param x the x value for the top-left corner of this rectangle region * @param y the y value for the top-left corner of this rectangle region * @param width the width of this rectangle region * @param height the height of this rectangle region * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_rectangle(struct heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, struct heif_region** out_region); /** * Add a ellipse region to the region item. * * @param region_item the region item that holds this ellipse region * @param x the x value for the centre of this ellipse region * @param y the y value for the centre of this ellipse region * @param radius_x the radius of the ellipse in the X (horizontal) direction * @param radius_y the radius of the ellipse in the Y (vertical) direction * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_ellipse(struct heif_region_item* region_item, int32_t x, int32_t y, uint32_t radius_x, uint32_t radius_y, struct heif_region** out_region); /** * Add a polygon region to the region item. * * A polygon is a sequence of points that form a closed shape. The first point does * not need to be repeated as the last point. * The points are provided as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * @param region_item the region item that holds this polygon region * @param pts_array the array of points in X,Y order (see above) * @param nPoints the number of points * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note `nPoints` is the number of points, not the number of elements in the array * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_polygon(struct heif_region_item* region_item, const int32_t* pts_array, int nPoints, struct heif_region** out_region); /** * Add a polyline region to the region item. * * A polyline is a sequence of points that does not form a closed shape. Even if the * polyline is closed, the only points that are part of the region are those that * intersect (even minimally) a one-pixel line drawn along the polyline. * The points are provided as pairs of X,Y coordinates, in the order X1, * Y1, X2, Y2, ..., Xn, Yn. * * @param region_item the region item that holds this polyline region * @param pts_array the array of points in X,Y order (see above) * @param nPoints the number of points * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note `nPoints` is the number of points, not the number of elements in the array * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_polyline(struct heif_region_item* region_item, const int32_t* pts_array, int nPoints, struct heif_region** out_region); /** * Add a referenced mask region to the region item. * * The region geometry is described by the pixels in another image item, * which has a item reference of type `mask` from the region item to the * image item containing the mask. * * The image item containing the mask is one of: * * - a mask item (see ISO/IEC 23008-12:2022 Section 6.10.2), or a derived * image from a mask item * * - an image item in monochrome format (4:0:0 chroma) * * - an image item in colour format with luma and chroma planes (e.g. 4:2:0) * * If the pixel value is equal to the minimum sample value (e.g. 0 for unsigned * integer), the pixel is not part of the region. If the pixel value is equal * to the maximum sample value (e.g. 255 for 8 bit unsigned integer), the pixel * is part of the region. If the pixel value is between the minimum sample value * and maximum sample value, the pixel value represents an (application defined) * probability that the pixel is part of the region, where higher pixel values * correspond to higher probability values. * * @param region_item the region item that holds this mask region * @param x the x value for the top-left corner of this mask region * @param y the y value for the top-left corner of this mask region * @param width the width of this mask region * @param height the height of this mask region * @param mask_item_id the item identifier for the mask that is referenced * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error indicating the problem on failure * * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed. */ LIBHEIF_API struct heif_error heif_region_item_add_region_referenced_mask(struct heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, heif_item_id mask_item_id, struct heif_region** out_region); /** * Add an inline mask region to the region item. * * The region geometry is described by a top left corner position, and a size defined * by a width and height. * * The mask is held as inline data on the region, one bit per pixel, most significant * bit first pixel, no padding. If the bit value is `1`, the corresponding pixel is * part of the region. If the bit value is `0`, the corresponding pixel is not part of the * region. * * @param region_item the region item that holds this mask region * @param x the x value for the top-left corner of this mask region * @param y the y value for the top-left corner of this mask region * @param width the width of this mask region * @param height the height of this mask region * @param mask_data the location to return the mask data * @param mask_data_len the length of the mask data, in bytes * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error value indicating the problem on failure */ LIBHEIF_API struct heif_error heif_region_item_add_region_inline_mask_data(struct heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, const uint8_t* mask_data, size_t mask_data_len, struct heif_region** out_region); /** * Add an inline mask region image to the region item. * * The region geometry is described by a top left corner position, and a size defined * by a width and height. * * The mask data is held as inline data on the region, one bit per pixel. The provided * image is converted to inline data, where any pixel with a value >= 0x80 becomes part of the * mask region. If the image width is less that the specified width, it is expanded * to match the width of the region (zero fill on the right). If the image height is * less than the specified height, it is expanded to match the height of the region * (zero fill on the bottom). If the image width or height is greater than the * width or height (correspondingly) of the region, the image is cropped. * * @param region_item the region item that holds this mask region * @param x the x value for the top-left corner of this mask region * @param y the y value for the top-left corner of this mask region * @param width the width of this mask region * @param height the height of this mask region * @param image the image to convert to an inline mask * @param out_region pointer to pointer to the returned region (optional, see below) * @return heif_error_ok on success, or an error value indicating the problem on failure */ LIBHEIF_API struct heif_error heif_region_item_add_region_inline_mask(struct heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, struct heif_image* image, struct heif_region** out_region); #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_properties.h000664 001750 001750 00000022236 15003473471 023442 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2023 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_HEIF_PROPERTIES_H #define LIBHEIF_HEIF_PROPERTIES_H #include "heif.h" #ifdef __cplusplus extern "C" { #endif // ------------------------- item properties ------------------------- enum heif_item_property_type { // heif_item_property_unknown = -1, heif_item_property_type_invalid = 0, heif_item_property_type_user_description = heif_fourcc('u', 'd', 'e', 's'), heif_item_property_type_transform_mirror = heif_fourcc('i', 'm', 'i', 'r'), heif_item_property_type_transform_rotation = heif_fourcc('i', 'r', 'o', 't'), heif_item_property_type_transform_crop = heif_fourcc('c', 'l', 'a', 'p'), heif_item_property_type_image_size = heif_fourcc('i', 's', 'p', 'e'), heif_item_property_type_uuid = heif_fourcc('u', 'u', 'i', 'd'), heif_item_property_type_tai_clock_info = heif_fourcc('t', 'a', 'i', 'c'), heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i') }; // Get the heif_property_id for a heif_item_id. // You may specify which property 'type' you want to receive. // If you specify 'heif_item_property_type_invalid', all properties associated to that item are returned. // The number of properties is returned, which are not more than 'count' if (out_list != nullptr). // By setting out_list==nullptr, you can query the number of properties, 'count' is ignored. LIBHEIF_API int heif_item_get_properties_of_type(const struct heif_context* context, heif_item_id id, enum heif_item_property_type type, heif_property_id* out_list, int count); // Returns all transformative properties in the correct order. // This includes "irot", "imir", "clap". // The number of properties is returned, which are not more than 'count' if (out_list != nullptr). // By setting out_list==nullptr, you can query the number of properties, 'count' is ignored. LIBHEIF_API int heif_item_get_transformation_properties(const struct heif_context* context, heif_item_id id, heif_property_id* out_list, int count); LIBHEIF_API enum heif_item_property_type heif_item_get_property_type(const struct heif_context* context, heif_item_id id, heif_property_id property_id); // The strings are managed by libheif. They will be deleted in heif_property_user_description_release(). struct heif_property_user_description { int version; // version 1 const char* lang; const char* name; const char* description; const char* tags; }; // Get the "udes" user description property content. // Undefined strings are returned as empty strings. LIBHEIF_API struct heif_error heif_item_get_property_user_description(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_user_description** out); // Add a "udes" user description property to the item. // If any string pointers are NULL, an empty string will be used instead. LIBHEIF_API struct heif_error heif_item_add_property_user_description(const struct heif_context* context, heif_item_id itemId, const struct heif_property_user_description* description, heif_property_id* out_propertyId); // Release all strings and the object itself. // Only call for objects that you received from heif_item_get_property_user_description(). LIBHEIF_API void heif_property_user_description_release(struct heif_property_user_description*); enum heif_transform_mirror_direction { heif_transform_mirror_direction_invalid = -1, heif_transform_mirror_direction_vertical = 0, // flip image vertically heif_transform_mirror_direction_horizontal = 1 // flip image horizontally }; // Will return 'heif_transform_mirror_direction_invalid' in case of error. LIBHEIF_API enum heif_transform_mirror_direction heif_item_get_property_transform_mirror(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId); // Returns only 0, 90, 180, or 270 angle values. // Returns -1 in case of error (but it will only return an error in case of wrong usage). LIBHEIF_API int heif_item_get_property_transform_rotation_ccw(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId); // Returns the number of pixels that should be removed from the four edges. // Because of the way this data is stored, you have to pass the image size at the moment of the crop operation // to compute the cropped border sizes. LIBHEIF_API void heif_item_get_property_transform_crop_borders(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, int image_width, int image_height, int* left, int* top, int* right, int* bottom); /** * @param context The heif_context for the file * @param itemId The image item id to which this property belongs. * @param fourcc_type The short four-cc type of the property to add. * @param uuid_type If fourcc_type=='uuid', this should point to a 16-byte UUID type. It is ignored otherwise and can be NULL. * @param data Data to insert for this property (including a full-box header, if required for this box). * @param size Length of data in bytes. * @param is_essential Whether this property is essential (boolean). * @param out_propertyId Outputs the id of the inserted property. Can be NULL. */ LIBHEIF_API struct heif_error heif_item_add_raw_property(const struct heif_context* context, heif_item_id itemId, uint32_t fourcc_type, const uint8_t* uuid_type, const uint8_t* data, size_t size, int is_essential, heif_property_id* out_propertyId); LIBHEIF_API struct heif_error heif_item_get_property_raw_size(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, size_t* out_size); /** * @param out_data User-supplied array to write the property data to. The required size of the output array is given by heif_item_get_property_raw_size(). */ LIBHEIF_API struct heif_error heif_item_get_property_raw_data(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, uint8_t* out_data); /** * Get the extended type for an extended "uuid" box. * * This provides the UUID for the extended box. * * This method should only be called on properties of type `heif_item_property_type_uuid`. * * @param context the heif_context containing the HEIF file * @param itemId the image item id to which this property belongs. * @param propertyId the property index (1-based) to get the extended type for * @param out_extended_type output of the call, must be a pointer to at least 16-bytes. * @return heif_error_success or an error indicating the failure */ LIBHEIF_API struct heif_error heif_item_get_property_uuid_type(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, uint8_t out_extended_type[16]); #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_plugin.cc000664 001750 001750 00000002722 15003473471 022700 0ustar00farindkfarindk000000 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 "heif.h" #include "heif_plugin.h" // needed to avoid 'unresolved symbols' on Visual Studio compiler struct heif_error heif_error_ok = {heif_error_Ok, heif_suberror_Unspecified, "Success"}; struct heif_error heif_error_unsupported_parameter = {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported encoder parameter"}; struct heif_error heif_error_invalid_parameter_value = {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Invalid parameter value"}; libheif-1.19.8/libheif/api/libheif/heif.h000664 001750 001750 00000314050 15003473471 021164 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2023 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_HEIF_H #define LIBHEIF_HEIF_H #ifdef __cplusplus extern "C" { #endif /*! \file heif.h * * Public API for libheif. */ #include #include #include // API versions table // // release dec.options enc.options heif_reader heif_writer depth.rep col.profile // ------------------------------------------------------------------------------------------ // 1.0 1 N/A N/A N/A 1 N/A // 1.1 1 N/A N/A 1 1 N/A // 1.3 1 1 1 1 1 N/A // 1.4 1 1 1 1 1 1 // 1.7 2 1 1 1 1 1 // 1.9.2 2 2 1 1 1 1 // 1.10 2 3 1 1 1 1 // 1.11 2 4 1 1 1 1 // 1.13 3 4 1 1 1 1 // 1.14 3 5 1 1 1 1 // 1.15 4 5 1 1 1 1 // 1.16 5 6 1 1 1 1 // 1.18 5 7 1 1 1 1 // 1.19 6 7 2 1 1 1 #if (defined(_WIN32) || defined __CYGWIN__) && !defined(LIBHEIF_STATIC_BUILD) #ifdef LIBHEIF_EXPORTS #define LIBHEIF_API __declspec(dllexport) #else #define LIBHEIF_API __declspec(dllimport) #endif #elif defined(HAVE_VISIBILITY) && HAVE_VISIBILITY #ifdef LIBHEIF_EXPORTS #define LIBHEIF_API __attribute__((__visibility__("default"))) #else #define LIBHEIF_API #endif #else #define LIBHEIF_API #endif #define heif_fourcc(a, b, c, d) ((uint32_t)((a<<24) | (b<<16) | (c<<8) | d)) /* === version numbers === */ // Version string of linked libheif library. LIBHEIF_API const char* heif_get_version(void); // Numeric version of linked libheif library, encoded as 0xHHMMLL00 = hh.mm.ll, where hh, mm, ll is the decimal representation of HH, MM, LL. // For example: 0x02150300 is version 2.21.3 LIBHEIF_API uint32_t heif_get_version_number(void); // Numeric part "HH" from above. Returned as a decimal number. LIBHEIF_API int heif_get_version_number_major(void); // Numeric part "MM" from above. Returned as a decimal number. LIBHEIF_API int heif_get_version_number_minor(void); // Numeric part "LL" from above. Returned as a decimal number. LIBHEIF_API int heif_get_version_number_maintenance(void); // Helper macros to check for given versions of libheif at compile time. #define LIBHEIF_MAKE_VERSION(h, m, l) ((h) << 24 | (m) << 16 | (l) << 8) #define LIBHEIF_HAVE_VERSION(h, m, l) (LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(h, m, l)) struct heif_context; struct heif_image_handle; struct heif_image; enum heif_error_code { // Everything ok, no error occurred. heif_error_Ok = 0, // Input file does not exist. heif_error_Input_does_not_exist = 1, // Error in input file. Corrupted or invalid content. heif_error_Invalid_input = 2, // Input file type is not supported. heif_error_Unsupported_filetype = 3, // Image requires an unsupported decoder feature. heif_error_Unsupported_feature = 4, // Library API has been used in an invalid way. heif_error_Usage_error = 5, // Could not allocate enough memory. heif_error_Memory_allocation_error = 6, // The decoder plugin generated an error heif_error_Decoder_plugin_error = 7, // The encoder plugin generated an error heif_error_Encoder_plugin_error = 8, // Error during encoding or when writing to the output heif_error_Encoding_error = 9, // Application has asked for a color profile type that does not exist heif_error_Color_profile_does_not_exist = 10, // Error loading a dynamic plugin heif_error_Plugin_loading_error = 11, // Operation has been canceled heif_error_Canceled = 12 }; enum heif_suberror_code { // no further information available heif_suberror_Unspecified = 0, // --- Invalid_input --- // End of data reached unexpectedly. heif_suberror_End_of_data = 100, // Size of box (defined in header) is wrong heif_suberror_Invalid_box_size = 101, // Mandatory 'ftyp' box is missing heif_suberror_No_ftyp_box = 102, heif_suberror_No_idat_box = 103, heif_suberror_No_meta_box = 104, heif_suberror_No_hdlr_box = 105, heif_suberror_No_hvcC_box = 106, heif_suberror_No_pitm_box = 107, heif_suberror_No_ipco_box = 108, heif_suberror_No_ipma_box = 109, heif_suberror_No_iloc_box = 110, heif_suberror_No_iinf_box = 111, heif_suberror_No_iprp_box = 112, heif_suberror_No_iref_box = 113, heif_suberror_No_pict_handler = 114, // An item property referenced in the 'ipma' box is not existing in the 'ipco' container. heif_suberror_Ipma_box_references_nonexisting_property = 115, // No properties have been assigned to an item. heif_suberror_No_properties_assigned_to_item = 116, // Image has no (compressed) data heif_suberror_No_item_data = 117, // Invalid specification of image grid (tiled image) heif_suberror_Invalid_grid_data = 118, // Tile-images in a grid image are missing heif_suberror_Missing_grid_images = 119, heif_suberror_Invalid_clean_aperture = 120, // Invalid specification of overlay image heif_suberror_Invalid_overlay_data = 121, // Overlay image completely outside of visible canvas area heif_suberror_Overlay_image_outside_of_canvas = 122, heif_suberror_Auxiliary_image_type_unspecified = 123, heif_suberror_No_or_invalid_primary_item = 124, heif_suberror_No_infe_box = 125, heif_suberror_Unknown_color_profile_type = 126, heif_suberror_Wrong_tile_image_chroma_format = 127, heif_suberror_Invalid_fractional_number = 128, heif_suberror_Invalid_image_size = 129, heif_suberror_Invalid_pixi_box = 130, heif_suberror_No_av1C_box = 131, heif_suberror_Wrong_tile_image_pixel_depth = 132, heif_suberror_Unknown_NCLX_color_primaries = 133, heif_suberror_Unknown_NCLX_transfer_characteristics = 134, heif_suberror_Unknown_NCLX_matrix_coefficients = 135, // Invalid specification of region item heif_suberror_Invalid_region_data = 136, // Image has no ispe property heif_suberror_No_ispe_property = 137, heif_suberror_Camera_intrinsic_matrix_undefined = 138, heif_suberror_Camera_extrinsic_matrix_undefined = 139, // Invalid JPEG 2000 codestream - usually a missing marker heif_suberror_Invalid_J2K_codestream = 140, heif_suberror_No_vvcC_box = 141, // icbr is only needed in some situations, this error is for those cases heif_suberror_No_icbr_box = 142, heif_suberror_No_avcC_box = 143, // we got a mini box, but could not read it properly heif_suberror_Invalid_mini_box = 149, // Decompressing generic compression or header compression data failed (e.g. bitstream corruption) heif_suberror_Decompression_invalid_data = 150, // --- Memory_allocation_error --- // A security limit preventing unreasonable memory allocations was exceeded by the input file. // Please check whether the file is valid. If it is, contact us so that we could increase the // security limits further. heif_suberror_Security_limit_exceeded = 1000, // There was an error from the underlying compression / decompression library. // One possibility is lack of resources (e.g. memory). heif_suberror_Compression_initialisation_error = 1001, // --- Usage_error --- // An item ID was used that is not present in the file. heif_suberror_Nonexisting_item_referenced = 2000, // also used for Invalid_input // An API argument was given a NULL pointer, which is not allowed for that function. heif_suberror_Null_pointer_argument = 2001, // Image channel referenced that does not exist in the image heif_suberror_Nonexisting_image_channel_referenced = 2002, // The version of the passed plugin is not supported. heif_suberror_Unsupported_plugin_version = 2003, // The version of the passed writer is not supported. heif_suberror_Unsupported_writer_version = 2004, // The given (encoder) parameter name does not exist. heif_suberror_Unsupported_parameter = 2005, // The value for the given parameter is not in the valid range. heif_suberror_Invalid_parameter_value = 2006, // Error in property specification heif_suberror_Invalid_property = 2007, // Image reference cycle found in iref heif_suberror_Item_reference_cycle = 2008, // --- Unsupported_feature --- // Image was coded with an unsupported compression method. heif_suberror_Unsupported_codec = 3000, // Image is specified in an unknown way, e.g. as tiled grid image (which is supported) heif_suberror_Unsupported_image_type = 3001, heif_suberror_Unsupported_data_version = 3002, // The conversion of the source image to the requested chroma / colorspace is not supported. heif_suberror_Unsupported_color_conversion = 3003, heif_suberror_Unsupported_item_construction_method = 3004, heif_suberror_Unsupported_header_compression_method = 3005, // Generically compressed data used an unsupported compression method heif_suberror_Unsupported_generic_compression_method = 3006, heif_suberror_Unsupported_essential_property = 3007, // --- Encoder_plugin_error --- heif_suberror_Unsupported_bit_depth = 4000, // --- Encoding_error --- heif_suberror_Cannot_write_output_data = 5000, heif_suberror_Encoder_initialization = 5001, heif_suberror_Encoder_encoding = 5002, heif_suberror_Encoder_cleanup = 5003, heif_suberror_Too_many_regions = 5004, // --- Plugin loading error --- heif_suberror_Plugin_loading_error = 6000, // a specific plugin file cannot be loaded heif_suberror_Plugin_is_not_loaded = 6001, // trying to remove a plugin that is not loaded heif_suberror_Cannot_read_plugin_directory = 6002, // error while scanning the directory for plugins heif_suberror_No_matching_decoder_installed = 6003 // no decoder found for that compression format }; struct heif_error { // main error category enum heif_error_code code; // more detailed error code enum heif_suberror_code subcode; // textual error message (is always defined, you do not have to check for NULL) const char* message; }; // Default success return value. Intended for use in user-supplied callback functions. LIBHEIF_API extern const struct heif_error heif_error_success; typedef uint32_t heif_item_id; typedef uint32_t heif_property_id; // ========================= enum types ====================== /** * libheif known compression formats. */ enum heif_compression_format { /** * Unspecified / undefined compression format. * * This is used to mean "no match" or "any decoder" for some parts of the * API. It does not indicate a specific compression format. */ heif_compression_undefined = 0, /** * HEVC compression, used for HEIC images. * * This is equivalent to H.265. */ heif_compression_HEVC = 1, /** * AVC compression. (Currently unused in libheif.) * * The compression is defined in ISO/IEC 14496-10. This is equivalent to H.264. * * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex E. */ heif_compression_AVC = 2, /** * JPEG compression. * * The compression format is defined in ISO/IEC 10918-1. The encapsulation * of JPEG is specified in ISO/IEC 23008-12:2022 Annex H. */ heif_compression_JPEG = 3, /** * AV1 compression, used for AVIF images. * * The compression format is provided at https://aomediacodec.github.io/av1-spec/ * * The encapsulation is defined in https://aomediacodec.github.io/av1-avif/ */ heif_compression_AV1 = 4, /** * VVC compression. * * The compression format is defined in ISO/IEC 23090-3. This is equivalent to H.266. * * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex L. */ heif_compression_VVC = 5, /** * EVC compression. (Currently unused in libheif.) * * The compression format is defined in ISO/IEC 23094-1. * * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex M. */ heif_compression_EVC = 6, /** * JPEG 2000 compression. * * The encapsulation of JPEG 2000 is specified in ISO/IEC 15444-16:2021. * The core encoding is defined in ISO/IEC 15444-1, or ITU-T T.800. */ heif_compression_JPEG2000 = 7, /** * Uncompressed encoding. * * This is defined in ISO/IEC 23001-17:2024. */ heif_compression_uncompressed = 8, /** * Mask image encoding. * * See ISO/IEC 23008-12:2022 Section 6.10.2 */ heif_compression_mask = 9, /** * High Throughput JPEG 2000 (HT-J2K) compression. * * The encapsulation of HT-J2K is specified in ISO/IEC 15444-16:2021. * The core encoding is defined in ISO/IEC 15444-15, or ITU-T T.814. */ heif_compression_HTJ2K = 10 }; enum heif_chroma { heif_chroma_undefined = 99, heif_chroma_monochrome = 0, heif_chroma_420 = 1, heif_chroma_422 = 2, heif_chroma_444 = 3, heif_chroma_interleaved_RGB = 10, heif_chroma_interleaved_RGBA = 11, heif_chroma_interleaved_RRGGBB_BE = 12, // HDR, big endian. heif_chroma_interleaved_RRGGBBAA_BE = 13, // HDR, big endian. heif_chroma_interleaved_RRGGBB_LE = 14, // HDR, little endian. heif_chroma_interleaved_RRGGBBAA_LE = 15 // HDR, little endian. }; // DEPRECATED ENUM NAMES #define heif_chroma_interleaved_24bit heif_chroma_interleaved_RGB #define heif_chroma_interleaved_32bit heif_chroma_interleaved_RGBA enum heif_colorspace { heif_colorspace_undefined = 99, // heif_colorspace_YCbCr should be used with one of these heif_chroma values: // * heif_chroma_444 // * heif_chroma_422 // * heif_chroma_420 heif_colorspace_YCbCr = 0, // heif_colorspace_RGB should be used with one of these heif_chroma values: // * heif_chroma_444 (for planar RGB) // * heif_chroma_interleaved_RGB // * heif_chroma_interleaved_RGBA // * heif_chroma_interleaved_RRGGBB_BE // * heif_chroma_interleaved_RRGGBBAA_BE // * heif_chroma_interleaved_RRGGBB_LE // * heif_chroma_interleaved_RRGGBBAA_LE heif_colorspace_RGB = 1, // heif_colorspace_monochrome should only be used with heif_chroma = heif_chroma_monochrome heif_colorspace_monochrome = 2, // Indicates that this image has no visual channels. heif_colorspace_nonvisual = 3 }; enum heif_channel { heif_channel_Y = 0, heif_channel_Cb = 1, heif_channel_Cr = 2, heif_channel_R = 3, heif_channel_G = 4, heif_channel_B = 5, heif_channel_Alpha = 6, heif_channel_interleaved = 10, heif_channel_filter_array = 11, heif_channel_depth = 12, heif_channel_disparity = 13 }; enum heif_metadata_compression { heif_metadata_compression_off = 0, heif_metadata_compression_auto = 1, heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file heif_metadata_compression_deflate = 3, heif_metadata_compression_zlib = 4, // do not use for header data heif_metadata_compression_brotli = 5 }; // ========================= library initialization ====================== struct heif_init_params { int version; // currently no parameters }; /** * Initialise library. * * You should call heif_init() when you start using libheif and heif_deinit() when you are finished. * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit(). * * For backwards compatibility, it is not really necessary to call heif_init(), but some library memory objects * will never be freed if you do not call heif_init()/heif_deinit(). * * heif_init() will load the external modules installed in the default plugin path. Thus, you need it when you * want to load external plugins from the default path. * Codec plugins that are compiled into the library directly (selected by the compile-time parameters of libheif) * will be available even without heif_init(). * * Make sure that you do not have one part of your program use heif_init()/heif_deinit() and another part that does * not use it as the latter may try to use an uninitialized library. If in doubt, enclose everything with init/deinit. * * You may pass nullptr to get default parameters. Currently, no parameters are supported. */ LIBHEIF_API struct heif_error heif_init(struct heif_init_params*); /** * Deinitialise and clean up library. * * You should call heif_init() when you start using libheif and heif_deinit() when you are finished. * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit(). * * Note: heif_deinit() must not be called after exit(), for example in a global C++ object's destructor. * If you do, global variables in libheif might have already been released when heif_deinit() is running, * leading to a crash. * * \sa heif_init() */ LIBHEIF_API void heif_deinit(void); // --- Plugins are currently only supported on Unix platforms. enum heif_plugin_type { heif_plugin_type_encoder, heif_plugin_type_decoder }; struct heif_plugin_info { int version; // version of this info struct enum heif_plugin_type type; const void* plugin; void* internal_handle; // for internal use only }; LIBHEIF_API struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin); LIBHEIF_API struct heif_error heif_load_plugins(const char* directory, const struct heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size); LIBHEIF_API struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin); // Get a NULL terminated array of the plugin directories that are searched by libheif. // This includes the paths specified in the environment variable LIBHEIF_PLUGIN_PATHS and the built-in path // (if not overridden by the environment variable). LIBHEIF_API const char*const* heif_get_plugin_directories(void); LIBHEIF_API void heif_free_plugin_directories(const char*const*); // ========================= file type check ====================== enum heif_filetype_result { heif_filetype_no, heif_filetype_yes_supported, // it is heif and can be read by libheif heif_filetype_yes_unsupported, // it is heif, but cannot be read by libheif heif_filetype_maybe // not sure whether it is an heif, try detection with more input data }; // input data should be at least 12 bytes LIBHEIF_API enum heif_filetype_result heif_check_filetype(const uint8_t* data, int len); /** * Check the filetype box content for a supported file type. * *

The data is assumed to start from the start of the `ftyp` box. * *

This function checks the compatible brands. * * @returns heif_error_ok if a supported brand is found, or other error if not. */ LIBHEIF_API struct heif_error heif_has_compatible_filetype(const uint8_t* data, int len); LIBHEIF_API int heif_check_jpeg_filetype(const uint8_t* data, int len); // DEPRECATED, use heif_brand2 and the heif_brand2_* constants below instead enum heif_brand { heif_unknown_brand, heif_heic, // HEIF image with h265 heif_heix, // 10bit images, or anything that uses h265 with range extension heif_hevc, heif_hevx, // brands for image sequences heif_heim, // multiview heif_heis, // scalable heif_hevm, // multiview sequence heif_hevs, // scalable sequence heif_mif1, // image, any coding algorithm heif_msf1, // sequence, any coding algorithm heif_avif, // HEIF image with AV1 heif_avis, heif_vvic, // VVC image heif_vvis, // VVC sequence heif_evbi, // EVC image heif_evbs, // EVC sequence heif_j2ki, // JPEG2000 image heif_j2is, // JPEG2000 image sequence }; // input data should be at least 12 bytes // DEPRECATED, use heif_read_main_brand() instead LIBHEIF_API enum heif_brand heif_main_brand(const uint8_t* data, int len); typedef uint32_t heif_brand2; /** * HEVC image (`heic`) brand. * * Image conforms to HEVC (H.265) Main or Main Still profile. * * See ISO/IEC 23008-12:2022 Section B.4.1. */ #define heif_brand2_heic heif_fourcc('h','e','i','c') /** * HEVC image (`heix`) brand. * * Image conforms to HEVC (H.265) Main 10 profile. * * See ISO/IEC 23008-12:2022 Section B.4.1. */ #define heif_brand2_heix heif_fourcc('h','e','i','x') /** * HEVC image sequence (`hevc`) brand. * * Image sequence conforms to HEVC (H.265) Main profile. * * See ISO/IEC 23008-12:2022 Section B.4.2. */ #define heif_brand2_hevc heif_fourcc('h','e','v','c') /** * HEVC image sequence (`hevx`) brand. * * Image sequence conforms to HEVC (H.265) Main 10 profile. * * See ISO/IEC 23008-12:2022 Section B.4.2. */ #define heif_brand2_hevx heif_fourcc('h','e','v','x') /** * HEVC layered image (`heim`) brand. * * Image layers conform to HEVC (H.265) Main or Multiview Main profile. * * See ISO/IEC 23008-12:2022 Section B.4.3. */ #define heif_brand2_heim heif_fourcc('h','e','i','m') /** * HEVC layered image (`heis`) brand. * * Image layers conform to HEVC (H.265) Main, Main 10, Scalable Main * or Scalable Main 10 profile. * * See ISO/IEC 23008-12:2022 Section B.4.3. */ #define heif_brand2_heis heif_fourcc('h','e','i','s') /** * HEVC layered image sequence (`hevm`) brand. * * Image sequence layers conform to HEVC (H.265) Main or Multiview Main profile. * * See ISO/IEC 23008-12:2022 Section B.4.4. */ #define heif_brand2_hevm heif_fourcc('h','e','v','m') /** * HEVC layered image sequence (`hevs`) brand. * * Image sequence layers conform to HEVC (H.265) Main, Main 10, Scalable Main * or Scalable Main 10 profile. * * See ISO/IEC 23008-12:2022 Section B.4.4. */ #define heif_brand2_hevs heif_fourcc('h','e','v','s') /** * AV1 image (`avif`) brand. * * See https://aomediacodec.github.io/av1-avif/#image-and-image-collection-brand */ #define heif_brand2_avif heif_fourcc('a','v','i','f') /** * AV1 image sequence (`avis`) brand. * * See https://aomediacodec.github.io/av1-avif/#image-sequence-brand */ #define heif_brand2_avis heif_fourcc('a','v','i','s') // AVIF sequence /** * HEIF image structural brand (`mif1`). * * This does not imply a specific coding algorithm. * * See ISO/IEC 23008-12:2022 Section 10.2.2. */ #define heif_brand2_mif1 heif_fourcc('m','i','f','1') /** * HEIF image structural brand (`mif2`). * * This does not imply a specific coding algorithm. `mif2` extends * the requirements of `mif1` to include the `rref` and `iscl` item * properties. * * See ISO/IEC 23008-12:2022 Section 10.2.3. */ #define heif_brand2_mif2 heif_fourcc('m','i','f','2') /** * HEIF image structural brand (`mif3`). * * This indicates the low-overhead (ftyp+mini) structure. */ #define heif_brand2_mif3 heif_fourcc('m','i','f','3') /** * HEIF image sequence structural brand (`msf1`). * * This does not imply a specific coding algorithm. * * See ISO/IEC 23008-12:2022 Section 10.3.1. */ #define heif_brand2_msf1 heif_fourcc('m','s','f','1') /** * VVC image (`vvic`) brand. * * See ISO/IEC 23008-12:2022 Section L.4.1. */ #define heif_brand2_vvic heif_fourcc('v','v','i','c') /** * VVC image sequence (`vvis`) brand. * * See ISO/IEC 23008-12:2022 Section L.4.2. */ #define heif_brand2_vvis heif_fourcc('v','v','i','s') /** * EVC baseline image (`evbi`) brand. * * See ISO/IEC 23008-12:2022 Section M.4.1. */ #define heif_brand2_evbi heif_fourcc('e','v','b','i') /** * EVC main profile image (`evmi`) brand. * * See ISO/IEC 23008-12:2022 Section M.4.2. */ #define heif_brand2_evmi heif_fourcc('e','v','m','i') /** * EVC baseline image sequence (`evbs`) brand. * * See ISO/IEC 23008-12:2022 Section M.4.3. */ #define heif_brand2_evbs heif_fourcc('e','v','b','s') /** * EVC main profile image sequence (`evms`) brand. * * See ISO/IEC 23008-12:2022 Section M.4.4. */ #define heif_brand2_evms heif_fourcc('e','v','m','s') /** * JPEG image (`jpeg`) brand. * * See ISO/IEC 23008-12:2022 Annex H.4 */ #define heif_brand2_jpeg heif_fourcc('j','p','e','g') /** * JPEG image sequence (`jpgs`) brand. * * See ISO/IEC 23008-12:2022 Annex H.5 */ #define heif_brand2_jpgs heif_fourcc('j','p','g','s') /** * JPEG 2000 image (`j2ki`) brand. * * See ISO/IEC 15444-16:2021 Section 6.5 */ #define heif_brand2_j2ki heif_fourcc('j','2','k','i') /** * JPEG 2000 image sequence (`j2is`) brand. * * See ISO/IEC 15444-16:2021 Section 7.6 */ #define heif_brand2_j2is heif_fourcc('j','2','i','s') /** * Multi-image application format (MIAF) brand. * * This is HEIF with additional constraints for interoperability. * * See ISO/IEC 23000-22. */ #define heif_brand2_miaf heif_fourcc('m','i','a','f') /** * Single picture file brand. * * This is a compatible brand indicating the file contains a single intra-coded picture. * * See ISO/IEC 23008-12:2022 Section 10.2.5. */ #define heif_brand2_1pic heif_fourcc('1','p','i','c') // input data should be at least 12 bytes LIBHEIF_API heif_brand2 heif_read_main_brand(const uint8_t* data, int len); // input data should be at least 16 bytes LIBHEIF_API heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len); // 'brand_fourcc' must be 4 character long, but need not be 0-terminated LIBHEIF_API heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc); // the output buffer must be at least 4 bytes long LIBHEIF_API void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc); // 'brand_fourcc' must be 4 character long, but need not be 0-terminated // returns 1 if file includes the brand, and 0 if it does not // returns -1 if the provided data is not sufficient // (you should input at least as many bytes as indicated in the first 4 bytes of the file, usually ~50 bytes will do) // returns -2 on other errors LIBHEIF_API int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc); // Returns an array of compatible brands. The array is allocated by this function and has to be freed with 'heif_free_list_of_compatible_brands()'. // The number of entries is returned in out_size. LIBHEIF_API struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size); LIBHEIF_API void heif_free_list_of_compatible_brands(heif_brand2* brands_list); // Returns one of these MIME types: // - image/heic HEIF file using h265 compression // - image/heif HEIF file using any other compression // - image/heic-sequence HEIF image sequence using h265 compression // - image/heif-sequence HEIF image sequence using any other compression // - image/avif AVIF image // - image/avif-sequence AVIF sequence // - image/jpeg JPEG image // - image/png PNG image // If the format could not be detected, an empty string is returned. // // Provide at least 12 bytes of input. With less input, its format might not // be detected. You may also provide more input to increase detection accuracy. // // Note that JPEG and PNG images cannot be decoded by libheif even though the // formats are detected by this function. LIBHEIF_API const char* heif_get_file_mime_type(const uint8_t* data, int len); // ========================= heif_context ========================= // A heif_context represents a HEIF file that has been read. // In the future, you will also be able to add pictures to a heif_context // and write it into a file again. // Allocate a new context for reading HEIF files. // Has to be freed again with heif_context_free(). LIBHEIF_API struct heif_context* heif_context_alloc(void); // Free a previously allocated HEIF context. You should not free a context twice. LIBHEIF_API void heif_context_free(struct heif_context*); struct heif_reading_options; enum heif_reader_grow_status { heif_reader_grow_status_size_reached, // requested size has been reached, we can read until this point heif_reader_grow_status_timeout, // size has not been reached yet, but it may still grow further (deprecated) heif_reader_grow_status_size_beyond_eof, // size has not been reached and never will. The file has grown to its full size heif_reader_grow_status_error // an error has occurred }; struct heif_reader_range_request_result { enum heif_reader_grow_status status; // should not return 'heif_reader_grow_status_timeout' // Indicates up to what position the file has been read. // If we cannot read the whole file range (status == 'heif_reader_grow_status_size_beyond_eof'), this is the actual end position. // On the other hand, it may be that the reader was reading more data than requested. In that case, it should indicate the full size here // and libheif may decide to make use of the additional data (e.g. for filling 'tili' offset tables). uint64_t range_end; // for status == 'heif_reader_grow_status_error' int reader_error_code; // a reader specific error code const char* reader_error_msg; // libheif will call heif_reader.release_error_msg on this if it is not NULL }; struct heif_reader { // API version supported by this reader int reader_api_version; // --- version 1 functions --- int64_t (* get_position)(void* userdata); // The functions read(), and seek() return 0 on success. // Generally, libheif will make sure that we do not read past the file size. int (* read)(void* data, size_t size, void* userdata); int (* seek)(int64_t position, void* userdata); // When calling this function, libheif wants to make sure that it can read the file // up to 'target_size'. This is useful when the file is currently downloaded and may // grow with time. You may, for example, extract the image sizes even before the actual // compressed image data has been completely downloaded. // // Even if your input files will not grow, you will have to implement at least // detection whether the target_size is above the (fixed) file length // (in this case, return 'size_beyond_eof'). enum heif_reader_grow_status (* wait_for_file_size)(int64_t target_size, void* userdata); // --- version 2 functions --- // These two functions are for applications that want to stream HEIF files on demand. // For example, a large HEIF file that is served over HTTPS and we only want to download // it partially to decode individual tiles. // If you do not have this use case, you do not have to implement these functions and // you can set them to NULL. For simple linear loading, you may use the 'wait_for_file_size' // function above instead. // If this function is defined, libheif will often request a file range before accessing it. // The purpose of this function is that libheif will usually read very small chunks of data with the // read() callback above. However, it is inefficient to request such a small chunk of data over a network // and the network delay will significantly increase the decoding time. // Thus, libheif will call request_range() with a larger block of data that should be preloaded and the // subsequent read() calls will work within the requested ranges. // // Note: `end_pos` is one byte after the last position to be read. // You should return // - 'heif_reader_grow_status_size_reached' if the requested range is available, or // - 'heif_reader_grow_status_size_beyond_eof' if the requested range exceeds the file size // (the valid part of the range has been read). struct heif_reader_range_request_result (*request_range)(uint64_t start_pos, uint64_t end_pos, void* userdata); // libheif might issue hints when it assumes that a file range might be needed in the future. // This may happen, for example, when your are doing selective tile accesses and libheif proposes // to preload offset pointer tables. // Another difference to request_file_range() is that this call should be non-blocking. // If you preload any data, do this in a background thread. void (*preload_range_hint)(uint64_t start_pos, uint64_t end_pos, void* userdata); // If libheif does not need access to a file range anymore, it may call this function to // give a hint to the reader that it may release the range from a cache. // If you do not maintain a file cache that wants to reduce its size dynamically, you do not // need to implement this function. void (*release_file_range)(uint64_t start_pos, uint64_t end_pos, void* userdata); // Release an error message that was returned by heif_reader in an earlier call. // If this function is NULL, the error message string will not be released. // This is a viable option if you are only returning static strings. void (*release_error_msg)(const char* msg); }; // Read a HEIF file from a named disk file. // The heif_reading_options should currently be set to NULL. LIBHEIF_API struct heif_error heif_context_read_from_file(struct heif_context*, const char* filename, const struct heif_reading_options*); // Read a HEIF file stored completely in memory. // The heif_reading_options should currently be set to NULL. // DEPRECATED: use heif_context_read_from_memory_without_copy() instead. LIBHEIF_API struct heif_error heif_context_read_from_memory(struct heif_context*, const void* mem, size_t size, const struct heif_reading_options*); // Same as heif_context_read_from_memory() except that the provided memory is not copied. // That means, you will have to keep the memory area alive as long as you use the heif_context. LIBHEIF_API struct heif_error heif_context_read_from_memory_without_copy(struct heif_context*, const void* mem, size_t size, const struct heif_reading_options*); LIBHEIF_API struct heif_error heif_context_read_from_reader(struct heif_context*, const struct heif_reader* reader, void* userdata, const struct heif_reading_options*); // Number of top-level images in the HEIF file. This does not include the thumbnails or the // tile images that are composed to an image grid. You can get access to the thumbnails via // the main image handle. LIBHEIF_API int heif_context_get_number_of_top_level_images(struct heif_context* ctx); LIBHEIF_API int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id); // Fills in image IDs into the user-supplied int-array 'ID_array', preallocated with 'count' entries. // Function returns the total number of IDs filled into the array. LIBHEIF_API int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx, heif_item_id* ID_array, int count); LIBHEIF_API struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id); // Get a handle to the primary image of the HEIF file. // This is the image that should be displayed primarily when there are several images in the file. LIBHEIF_API struct heif_error heif_context_get_primary_image_handle(struct heif_context* ctx, struct heif_image_handle**); // Get the image handle for a known image ID. LIBHEIF_API struct heif_error heif_context_get_image_handle(struct heif_context* ctx, heif_item_id id, struct heif_image_handle**); // Print information about the boxes of a HEIF file to file descriptor. // This is for debugging and informational purposes only. You should not rely on // the output having a specific format. At best, you should not use this at all. LIBHEIF_API void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd); // Set the maximum image size security limit. This function will set the maximum image area (number of pixels) // to maximum_width ^ 2. Alternatively to using this function, you can also set the maximum image area // in the security limits structure returned by heif_context_get_security_limits(). LIBHEIF_API void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width); // If the maximum threads number is set to 0, the image tiles are decoded in the main thread. // This is different from setting it to 1, which will generate a single background thread to decode the tiles. // Note that this setting only affects libheif itself. The codecs itself may still use multi-threaded decoding. // You can use it, for example, in cases where you are decoding several images in parallel anyway you thus want // to minimize parallelism in each decoder. LIBHEIF_API void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads); // --- security limits // If you set a limit to 0, the limit is disabled. struct heif_security_limits { uint8_t version; // --- version 1 // Limit on the maximum image size to avoid allocating too much memory. // For example, setting this to 32768^2 pixels = 1 Gigapixels results // in 1.5 GB memory need for YUV-4:2:0 or 4 GB for RGB32. uint64_t max_image_size_pixels; uint64_t max_number_of_tiles; uint32_t max_bayer_pattern_pixels; uint32_t max_items; uint32_t max_color_profile_size; uint64_t max_memory_block_size; uint32_t max_components; uint32_t max_iloc_extents_per_item; uint32_t max_size_entity_group; uint32_t max_children_per_box; // for all boxes that are not covered by other limits }; // The global security limits are the default for new heif_contexts. // These global limits cannot be changed, but you can override the limits for a specific heif_context. LIBHEIF_API const struct heif_security_limits* heif_get_global_security_limits(); // Returns a set of fully disabled security limits. Use with care and only after user confirmation. LIBHEIF_API const struct heif_security_limits* heif_get_disabled_security_limits(); // Returns the security limits for a heif_context. // By default, the limits are set to the global limits, but you can change them in the returned object. LIBHEIF_API struct heif_security_limits* heif_context_get_security_limits(const struct heif_context*); // Overwrites the security limits of a heif_context. // This is a convenience function to easily copy limits. LIBHEIF_API struct heif_error heif_context_set_security_limits(struct heif_context*, const struct heif_security_limits*); // ========================= heif_image_handle ========================= // An heif_image_handle is a handle to a logical image in the HEIF file. // To get the actual pixel data, you have to decode the handle to an heif_image. // An heif_image_handle also gives you access to the thumbnails and Exif data // associated with an image. // Once you obtained an heif_image_handle, you can already release the heif_context, // since it is internally ref-counted. // Release image handle. LIBHEIF_API void heif_image_handle_release(const struct heif_image_handle*); // Check whether the given image_handle is the primary image of the file. LIBHEIF_API int heif_image_handle_is_primary_image(const struct heif_image_handle* handle); LIBHEIF_API heif_item_id heif_image_handle_get_item_id(const struct heif_image_handle* handle); // Get the resolution of an image. LIBHEIF_API int heif_image_handle_get_width(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_height(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_has_alpha_channel(const struct heif_image_handle*); LIBHEIF_API int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. // Only defined for images coded in the YCbCr or monochrome colorspace. LIBHEIF_API int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. // Only defined for images coded in the YCbCr colorspace. LIBHEIF_API int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle*); // Return the colorspace that libheif proposes to use for decoding. // Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images // encoded with matrix_coefficients=0 or for images coded natively in RGB. // It may also return *_undefined if the file misses relevant information to determine this without decoding. // These are only proposed values that avoid colorspace conversions as much as possible. // You can still request the output in your preferred colorspace, but this may involve an internal conversion. LIBHEIF_API struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle, enum heif_colorspace* out_colorspace, enum heif_chroma* out_chroma); // Get the image width from the 'ispe' box. This is the original image size without // any transformations applied to it. Do not use this unless you know exactly what // you are doing. LIBHEIF_API int heif_image_handle_get_ispe_width(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_ispe_height(const struct heif_image_handle* handle); // This gets the context associated with the image handle. // Note that you have to release the returned context with heif_context_free() in any case. // // This means: when you have several image-handles that originate from the same file and you get the // context of each of them, the returned pointer may be different even though it refers to the same // logical context. You have to call heif_context_free() on all those context pointers. // After you freed a context pointer, you can still use the context through a different pointer that you // might have acquired from elsewhere. LIBHEIF_API struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle); struct heif_image_tiling { int version; // --- version 1 uint32_t num_columns; uint32_t num_rows; uint32_t tile_width; uint32_t tile_height; uint32_t image_width; uint32_t image_height; // Position of the top left tile. // Usually, this is (0;0), but if a tiled image is rotated or cropped, it may be that the top left tile should be placed at a negative position. // The offsets define this negative shift. uint32_t top_offset; uint32_t left_offset; uint8_t number_of_extra_dimensions; // 0 for normal images, 1 for volumetric (3D), ... uint32_t extra_dimension_size[8]; // size of extra dimensions (first 8 dimensions) }; // If 'process_image_transformations' is true, this returns modified sizes. // If it is false, the top_offset and left_offset will always be (0;0). LIBHEIF_API struct heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* out_tiling); // For grid images, return the image item ID of a specific grid tile. // If 'process_image_transformations' is true, the tile positions are given in the transformed image coordinate system and // are internally mapped to the original image tile positions. LIBHEIF_API struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle, int process_image_transformations, uint32_t tile_x, uint32_t tile_y, heif_item_id* out_tile_item_id); struct heif_decoding_options; // The tile position is given in tile indices, not in pixel coordinates. // If the image transformations are processed (option->ignore_image_transformations==false), the tile position // is given in the transformed coordinates. LIBHEIF_API struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle, struct heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const struct heif_decoding_options* options, uint32_t tile_x, uint32_t tile_y); // ------------------------- entity groups ------------------------ typedef uint32_t heif_entity_group_id; struct heif_entity_group { heif_entity_group_id entity_group_id; uint32_t entity_group_type; // this is a FourCC constant heif_item_id* entities; uint32_t num_entities; }; // Use 0 for `type_filter` or `item_filter` to disable the filter. // Returns an array of heif_entity_group structs with *out_num_groups entries. LIBHEIF_API struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context*, uint32_t type_filter, heif_item_id item_filter, int* out_num_groups); // Release an array of entity groups returned by heif_context_get_entity_groups(). LIBHEIF_API void heif_entity_groups_release(struct heif_entity_group*, int num_groups); // ------------------------- depth images ------------------------- LIBHEIF_API int heif_image_handle_has_depth_image(const struct heif_image_handle*); LIBHEIF_API int heif_image_handle_get_number_of_depth_images(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_list_of_depth_image_IDs(const struct heif_image_handle* handle, heif_item_id* ids, int count); LIBHEIF_API struct heif_error heif_image_handle_get_depth_image_handle(const struct heif_image_handle* handle, heif_item_id depth_image_id, struct heif_image_handle** out_depth_handle); enum heif_depth_representation_type { heif_depth_representation_type_uniform_inverse_Z = 0, heif_depth_representation_type_uniform_disparity = 1, heif_depth_representation_type_uniform_Z = 2, heif_depth_representation_type_nonuniform_disparity = 3 }; struct heif_depth_representation_info { uint8_t version; // version 1 fields uint8_t has_z_near; uint8_t has_z_far; uint8_t has_d_min; uint8_t has_d_max; double z_near; double z_far; double d_min; double d_max; enum heif_depth_representation_type depth_representation_type; uint32_t disparity_reference_view; uint32_t depth_nonlinear_representation_model_size; uint8_t* depth_nonlinear_representation_model; // version 2 fields below }; LIBHEIF_API void heif_depth_representation_info_free(const struct heif_depth_representation_info* info); // Returns true when there is depth_representation_info available // Note 1: depth_image_id is currently unused because we support only one depth channel per image, but // you should still provide the correct ID for future compatibility. // Note 2: Because of an API bug before v1.11.0, the function also works when 'handle' is the handle of the depth image. // However, you should pass the handle of the main image. Please adapt your code if needed. LIBHEIF_API int heif_image_handle_get_depth_image_representation_info(const struct heif_image_handle* handle, heif_item_id depth_image_id, const struct heif_depth_representation_info** out); // ------------------------- thumbnails ------------------------- // List the number of thumbnails assigned to this image handle. Usually 0 or 1. LIBHEIF_API int heif_image_handle_get_number_of_thumbnails(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_list_of_thumbnail_IDs(const struct heif_image_handle* handle, heif_item_id* ids, int count); // Get the image handle of a thumbnail image. LIBHEIF_API struct heif_error heif_image_handle_get_thumbnail(const struct heif_image_handle* main_image_handle, heif_item_id thumbnail_id, struct heif_image_handle** out_thumbnail_handle); // ------------------------- auxiliary images ------------------------- #define LIBHEIF_AUX_IMAGE_FILTER_OMIT_ALPHA (1UL<<1) #define LIBHEIF_AUX_IMAGE_FILTER_OMIT_DEPTH (2UL<<1) // List the number of auxiliary images assigned to this image handle. LIBHEIF_API int heif_image_handle_get_number_of_auxiliary_images(const struct heif_image_handle* handle, int aux_filter); LIBHEIF_API int heif_image_handle_get_list_of_auxiliary_image_IDs(const struct heif_image_handle* handle, int aux_filter, heif_item_id* ids, int count); // You are responsible to deallocate the returned buffer with heif_image_handle_release_auxiliary_type(). LIBHEIF_API struct heif_error heif_image_handle_get_auxiliary_type(const struct heif_image_handle* handle, const char** out_type); LIBHEIF_API void heif_image_handle_release_auxiliary_type(const struct heif_image_handle* handle, const char** out_type); // DEPRECATED (because typo in function name). Use heif_image_handle_release_auxiliary_type() instead. LIBHEIF_API void heif_image_handle_free_auxiliary_types(const struct heif_image_handle* handle, const char** out_type); // Get the image handle of an auxiliary image. LIBHEIF_API struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif_image_handle* main_image_handle, heif_item_id auxiliary_id, struct heif_image_handle** out_auxiliary_handle); // ------------------------- metadata (Exif / XMP) ------------------------- // How many metadata blocks are attached to an image. If you only want to get EXIF data, // set the type_filter to "Exif". Otherwise, set the type_filter to NULL. LIBHEIF_API int heif_image_handle_get_number_of_metadata_blocks(const struct heif_image_handle* handle, const char* type_filter); // 'type_filter' can be used to get only metadata of specific types, like "Exif". // If 'type_filter' is NULL, it will return all types of metadata IDs. LIBHEIF_API int heif_image_handle_get_list_of_metadata_block_IDs(const struct heif_image_handle* handle, const char* type_filter, heif_item_id* ids, int count); // Return a string indicating the type of the metadata, as specified in the HEIF file. // Exif data will have the type string "Exif". // This string will be valid until the next call to a libheif function. // You do not have to free this string. LIBHEIF_API const char* heif_image_handle_get_metadata_type(const struct heif_image_handle* handle, heif_item_id metadata_id); // For EXIF, the content type is empty. // For XMP, the content type is "application/rdf+xml". LIBHEIF_API const char* heif_image_handle_get_metadata_content_type(const struct heif_image_handle* handle, heif_item_id metadata_id); // Get the size of the raw metadata, as stored in the HEIF file. LIBHEIF_API size_t heif_image_handle_get_metadata_size(const struct heif_image_handle* handle, heif_item_id metadata_id); // 'out_data' must point to a memory area of the size reported by heif_image_handle_get_metadata_size(). // The data is returned exactly as stored in the HEIF file. // For Exif data, you probably have to skip the first four bytes of the data, since they // indicate the offset to the start of the TIFF header of the Exif data. LIBHEIF_API struct heif_error heif_image_handle_get_metadata(const struct heif_image_handle* handle, heif_item_id metadata_id, void* out_data); // Only valid for item type == "uri ", an absolute URI LIBHEIF_API const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle, heif_item_id metadata_id); // ------------------------- color profiles ------------------------- enum heif_color_profile_type { heif_color_profile_type_not_present = 0, heif_color_profile_type_nclx = heif_fourcc('n', 'c', 'l', 'x'), heif_color_profile_type_rICC = heif_fourcc('r', 'I', 'C', 'C'), heif_color_profile_type_prof = heif_fourcc('p', 'r', 'o', 'f') }; // Returns 'heif_color_profile_type_not_present' if there is no color profile. // If there is an ICC profile and an NCLX profile, the ICC profile is returned. // TODO: we need a new API for this function as images can contain both NCLX and ICC at the same time. // However, you can still use heif_image_handle_get_raw_color_profile() and // heif_image_handle_get_nclx_color_profile() to access both profiles. LIBHEIF_API enum heif_color_profile_type heif_image_handle_get_color_profile_type(const struct heif_image_handle* handle); LIBHEIF_API size_t heif_image_handle_get_raw_color_profile_size(const struct heif_image_handle* handle); // Returns 'heif_error_Color_profile_does_not_exist' when there is no ICC profile. LIBHEIF_API struct heif_error heif_image_handle_get_raw_color_profile(const struct heif_image_handle* handle, void* out_data); enum heif_color_primaries { heif_color_primaries_ITU_R_BT_709_5 = 1, // g=0.3;0.6, b=0.15;0.06, r=0.64;0.33, w=0.3127,0.3290 heif_color_primaries_unspecified = 2, heif_color_primaries_ITU_R_BT_470_6_System_M = 4, heif_color_primaries_ITU_R_BT_470_6_System_B_G = 5, heif_color_primaries_ITU_R_BT_601_6 = 6, heif_color_primaries_SMPTE_240M = 7, heif_color_primaries_generic_film = 8, heif_color_primaries_ITU_R_BT_2020_2_and_2100_0 = 9, heif_color_primaries_SMPTE_ST_428_1 = 10, heif_color_primaries_SMPTE_RP_431_2 = 11, heif_color_primaries_SMPTE_EG_432_1 = 12, heif_color_primaries_EBU_Tech_3213_E = 22 }; enum heif_transfer_characteristics { heif_transfer_characteristic_ITU_R_BT_709_5 = 1, heif_transfer_characteristic_unspecified = 2, heif_transfer_characteristic_ITU_R_BT_470_6_System_M = 4, heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G = 5, heif_transfer_characteristic_ITU_R_BT_601_6 = 6, heif_transfer_characteristic_SMPTE_240M = 7, heif_transfer_characteristic_linear = 8, heif_transfer_characteristic_logarithmic_100 = 9, heif_transfer_characteristic_logarithmic_100_sqrt10 = 10, heif_transfer_characteristic_IEC_61966_2_4 = 11, heif_transfer_characteristic_ITU_R_BT_1361 = 12, heif_transfer_characteristic_IEC_61966_2_1 = 13, heif_transfer_characteristic_ITU_R_BT_2020_2_10bit = 14, heif_transfer_characteristic_ITU_R_BT_2020_2_12bit = 15, heif_transfer_characteristic_ITU_R_BT_2100_0_PQ = 16, heif_transfer_characteristic_SMPTE_ST_428_1 = 17, heif_transfer_characteristic_ITU_R_BT_2100_0_HLG = 18 }; enum heif_matrix_coefficients { heif_matrix_coefficients_RGB_GBR = 0, heif_matrix_coefficients_ITU_R_BT_709_5 = 1, // TODO: or 709-6 according to h.273 heif_matrix_coefficients_unspecified = 2, heif_matrix_coefficients_US_FCC_T47 = 4, heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G = 5, heif_matrix_coefficients_ITU_R_BT_601_6 = 6, // TODO: or 601-7 according to h.273 heif_matrix_coefficients_SMPTE_240M = 7, heif_matrix_coefficients_YCgCo = 8, heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance = 9, heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance = 10, heif_matrix_coefficients_SMPTE_ST_2085 = 11, heif_matrix_coefficients_chromaticity_derived_non_constant_luminance = 12, heif_matrix_coefficients_chromaticity_derived_constant_luminance = 13, heif_matrix_coefficients_ICtCp = 14 }; struct heif_color_profile_nclx { // === version 1 fields uint8_t version; enum heif_color_primaries color_primaries; enum heif_transfer_characteristics transfer_characteristics; enum heif_matrix_coefficients matrix_coefficients; uint8_t full_range_flag; // --- decoded values (not used when saving nclx) float color_primary_red_x, color_primary_red_y; float color_primary_green_x, color_primary_green_y; float color_primary_blue_x, color_primary_blue_y; float color_primary_white_x, color_primary_white_y; }; LIBHEIF_API struct heif_error heif_nclx_color_profile_set_color_primaries(struct heif_color_profile_nclx* nclx, uint16_t cp); LIBHEIF_API struct heif_error heif_nclx_color_profile_set_transfer_characteristics(struct heif_color_profile_nclx* nclx, uint16_t transfer_characteristics); LIBHEIF_API struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t matrix_coefficients); // Returns 'heif_error_Color_profile_does_not_exist' when there is no NCLX profile. // TODO: This function does currently not return an NCLX profile if it is stored in the image bitstream. // Only NCLX profiles stored as colr boxes are returned. This may change in the future. LIBHEIF_API struct heif_error heif_image_handle_get_nclx_color_profile(const struct heif_image_handle* handle, struct heif_color_profile_nclx** out_data); // Returned color profile has 'version' field set to the maximum allowed. // Do not fill values for higher versions as these might be outside the allocated structure size. // May return NULL. LIBHEIF_API struct heif_color_profile_nclx* heif_nclx_color_profile_alloc(void); LIBHEIF_API void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile); // Note: in early versions of HEIF, there could only be one color profile per image. However, this has been changed. // This function will now return ICC if one is present and NCLX only if there is no ICC. // You may better avoid this function and simply query for NCLX and ICC directly. LIBHEIF_API enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image); // Returns the size of the ICC profile if one is assigned to the image. Otherwise, it returns 0. LIBHEIF_API size_t heif_image_get_raw_color_profile_size(const struct heif_image* image); // Returns the ICC profile if one is assigned to the image. Otherwise, it returns an error. LIBHEIF_API struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image, void* out_data); LIBHEIF_API struct heif_error heif_image_get_nclx_color_profile(const struct heif_image* image, struct heif_color_profile_nclx** out_data); // ------------------------- intrinsic and extrinsic matrices ------------------------- struct heif_camera_intrinsic_matrix { double focal_length_x; double focal_length_y; double principal_point_x; double principal_point_y; double skew; }; LIBHEIF_API int heif_image_handle_has_camera_intrinsic_matrix(const struct heif_image_handle* handle); LIBHEIF_API struct heif_error heif_image_handle_get_camera_intrinsic_matrix(const struct heif_image_handle* handle, struct heif_camera_intrinsic_matrix* out_matrix); struct heif_camera_extrinsic_matrix; LIBHEIF_API int heif_image_handle_has_camera_extrinsic_matrix(const struct heif_image_handle* handle); LIBHEIF_API struct heif_error heif_image_handle_get_camera_extrinsic_matrix(const struct heif_image_handle* handle, struct heif_camera_extrinsic_matrix** out_matrix); LIBHEIF_API void heif_camera_extrinsic_matrix_release(struct heif_camera_extrinsic_matrix*); LIBHEIF_API struct heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_camera_extrinsic_matrix*, double* out_matrix_row_major); // ========================= heif_image ========================= // An heif_image contains a decoded pixel image in various colorspaces, chroma formats, // and bit depths. // Note: when converting images to an interleaved chroma format, the resulting // image contains only a single channel of type channel_interleaved with, e.g., 3 bytes per pixel, // containing the interleaved R,G,B values. // Planar RGB images are specified as heif_colorspace_RGB / heif_chroma_444. enum heif_progress_step { heif_progress_step_total = 0, heif_progress_step_load_tile = 1 }; enum heif_chroma_downsampling_algorithm { heif_chroma_downsampling_nearest_neighbor = 1, heif_chroma_downsampling_average = 2, // Combine with 'heif_chroma_upsampling_bilinear' for best quality. // Makes edges look sharper when using YUV 420 with bilinear chroma upsampling. heif_chroma_downsampling_sharp_yuv = 3 }; enum heif_chroma_upsampling_algorithm { heif_chroma_upsampling_nearest_neighbor = 1, heif_chroma_upsampling_bilinear = 2 }; struct heif_color_conversion_options { // 'version' must be 1. uint8_t version; // --- version 1 options enum heif_chroma_downsampling_algorithm preferred_chroma_downsampling_algorithm; enum heif_chroma_upsampling_algorithm preferred_chroma_upsampling_algorithm; // When set to 'false' libheif may also use a different algorithm if the preferred one is not available // or using a different algorithm is computationally less complex. Note that currently (v1.17.0) this // means that for RGB input it will usually choose nearest-neighbor sampling because this is computationally // the simplest. // Set this field to 'true' if you want to make sure that the specified algorithm is used even // at the cost of slightly higher computation times. uint8_t only_use_preferred_chroma_algorithm; // --- Note that we cannot extend this struct because it is embedded in // other structs (heif_decoding_options and heif_encoding_options). }; // Assumes that it is a version=1 struct. LIBHEIF_API void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options*); struct heif_decoding_options { uint8_t version; // version 1 options // Ignore geometric transformations like cropping, rotation, mirroring. // Default: false (do not ignore). uint8_t ignore_transformations; // Any of the progress functions may be called from background threads. void (* start_progress)(enum heif_progress_step step, int max_progress, void* progress_user_data); void (* on_progress)(enum heif_progress_step step, int progress, void* progress_user_data); void (* end_progress)(enum heif_progress_step step, void* progress_user_data); void* progress_user_data; // version 2 options uint8_t convert_hdr_to_8bit; // version 3 options // When enabled, an error is returned for invalid input. Otherwise, it will try its best and // add decoding warnings to the decoded heif_image. Default is non-strict. uint8_t strict_decoding; // version 4 options // name_id of the decoder to use for the decoding. // If set to NULL (default), the highest priority decoder is chosen. // The priority is defined in the plugin. const char* decoder_id; // version 5 options struct heif_color_conversion_options color_conversion_options; // version 6 options int (* cancel_decoding)(void* progress_user_data); }; // Allocate decoding options and fill with default values. // Note: you should always get the decoding options through this function since the // option structure may grow in size in future versions. LIBHEIF_API struct heif_decoding_options* heif_decoding_options_alloc(void); LIBHEIF_API void heif_decoding_options_free(struct heif_decoding_options*); // Decode an heif_image_handle into the actual pixel image and also carry out // all geometric transformations specified in the HEIF file (rotation, cropping, mirroring). // // If colorspace or chroma is set to heif_colorspace_undefined or heif_chroma_undefined, // respectively, the original colorspace is taken. // Decoding options may be NULL. If you want to supply options, always use // heif_decoding_options_alloc() to get the structure. LIBHEIF_API struct heif_error heif_decode_image(const struct heif_image_handle* in_handle, struct heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const struct heif_decoding_options* options); // Get the colorspace format of the image. LIBHEIF_API enum heif_colorspace heif_image_get_colorspace(const struct heif_image*); // Get the chroma format of the image. LIBHEIF_API enum heif_chroma heif_image_get_chroma_format(const struct heif_image*); /** * Get the width of a specified image channel. * * @param img the image to get the width for * @param channel the channel to select * @return the width of the channel in pixels, or -1 the channel does not exist in the image */ LIBHEIF_API int heif_image_get_width(const struct heif_image* img, enum heif_channel channel); /** * Get the height of a specified image channel. * * @param img the image to get the height for * @param channel the channel to select * @return the height of the channel in pixels, or -1 the channel does not exist in the image */ LIBHEIF_API int heif_image_get_height(const struct heif_image* img, enum heif_channel channel); /** * Get the width of the main channel. * * This is the Y channel in YCbCr or mono, or any in RGB. * * @param img the image to get the primary width for * @return the width in pixels */ LIBHEIF_API int heif_image_get_primary_width(const struct heif_image* img); /** * Get the height of the main channel. * * This is the Y channel in YCbCr or mono, or any in RGB. * * @param img the image to get the primary height for * @return the height in pixels */ LIBHEIF_API int heif_image_get_primary_height(const struct heif_image* img); LIBHEIF_API struct heif_error heif_image_crop(struct heif_image* img, int left, int right, int top, int bottom); // Get the number of bits per pixel in the given image channel. Returns -1 if // a non-existing channel was given. // Note that the number of bits per pixel may be different for each color channel. // This function returns the number of bits used for storage of each pixel. // Especially for HDR images, this is probably not what you want. Have a look at // heif_image_get_bits_per_pixel_range() instead. LIBHEIF_API int heif_image_get_bits_per_pixel(const struct heif_image*, enum heif_channel channel); // Get the number of bits per pixel in the given image channel. This function returns // the number of bits used for representing the pixel value, which might be smaller // than the number of bits used in memory. // For example, in 12bit HDR images, this function returns '12', while still 16 bits // are reserved for storage. For interleaved RGBA with 12 bit, this function also returns // '12', not '48' or '64' (heif_image_get_bits_per_pixel returns 64 in this case). LIBHEIF_API int heif_image_get_bits_per_pixel_range(const struct heif_image*, enum heif_channel channel); LIBHEIF_API int heif_image_has_channel(const struct heif_image*, enum heif_channel channel); // Get a pointer to the actual pixel data. // The 'out_stride' is returned as "bytes per line". // When out_stride is NULL, no value will be written. // Returns NULL if a non-existing channel was given. // TODO: it would be better if the 'stride' parameter would be size_t to prevent integer overflows when this value is multiplicated with large y coordinates. LIBHEIF_API const uint8_t* heif_image_get_plane_readonly(const struct heif_image*, enum heif_channel channel, int* out_stride); LIBHEIF_API uint8_t* heif_image_get_plane(struct heif_image*, enum heif_channel channel, int* out_stride); struct heif_scaling_options; // Currently, heif_scaling_options is not defined yet. Pass a NULL pointer. LIBHEIF_API struct heif_error heif_image_scale_image(const struct heif_image* input, struct heif_image** output, int width, int height, const struct heif_scaling_options* options); // Extends the image size to match the given size by extending the right and bottom borders. // The border areas are filled with zero. LIBHEIF_API struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image, uint32_t width, uint32_t height); // The color profile is not attached to the image handle because we might need it // for color space transform and encoding. LIBHEIF_API struct heif_error heif_image_set_raw_color_profile(struct heif_image* image, const char* profile_type_fourcc_string, const void* profile_data, const size_t profile_size); LIBHEIF_API struct heif_error heif_image_set_nclx_color_profile(struct heif_image* image, const struct heif_color_profile_nclx* color_profile); // TODO: this function does not make any sense yet, since we currently cannot modify existing HEIF files. //LIBHEIF_API //void heif_image_remove_color_profile(struct heif_image* image); // Fills the image decoding warnings into the provided 'out_warnings' array. // The size of the array has to be provided in max_output_buffer_entries. // If max_output_buffer_entries==0, the number of decoder warnings is returned. // The function fills the warnings into the provided buffer, starting with 'first_warning_idx'. // It returns the number of warnings filled into the buffer. // Note: you can iterate through all warnings by using 'max_output_buffer_entries=1' and iterate 'first_warning_idx'. LIBHEIF_API int heif_image_get_decoding_warnings(struct heif_image* image, int first_warning_idx, struct heif_error* out_warnings, int max_output_buffer_entries); // This function is only for decoder plugin implementors. LIBHEIF_API void heif_image_add_decoding_warning(struct heif_image* image, struct heif_error err); // Release heif_image. LIBHEIF_API void heif_image_release(const struct heif_image*); // Note: a value of 0 for any of these values indicates that the value is undefined. // The unit of these values is Candelas per square meter. struct heif_content_light_level { uint16_t max_content_light_level; uint16_t max_pic_average_light_level; }; LIBHEIF_API int heif_image_has_content_light_level(const struct heif_image*); LIBHEIF_API void heif_image_get_content_light_level(const struct heif_image*, struct heif_content_light_level* out); // Returns whether the image has 'content light level' information. If 0 is returned, the output is not filled. LIBHEIF_API int heif_image_handle_get_content_light_level(const struct heif_image_handle*, struct heif_content_light_level* out); LIBHEIF_API void heif_image_set_content_light_level(const struct heif_image*, const struct heif_content_light_level* in); // Note: color coordinates are defined according to the CIE 1931 definition of x as specified in ISO 11664-1 (see also ISO 11664-3 and CIE 15). struct heif_mastering_display_colour_volume { uint16_t display_primaries_x[3]; uint16_t display_primaries_y[3]; uint16_t white_point_x; uint16_t white_point_y; uint32_t max_display_mastering_luminance; uint32_t min_display_mastering_luminance; }; // The units for max_display_mastering_luminance and min_display_mastering_luminance is Candelas per square meter. struct heif_decoded_mastering_display_colour_volume { float display_primaries_x[3]; float display_primaries_y[3]; float white_point_x; float white_point_y; double max_display_mastering_luminance; double min_display_mastering_luminance; }; struct heif_ambient_viewing_environment { uint32_t ambient_illumination; uint16_t ambient_light_x; uint16_t ambient_light_y; }; LIBHEIF_API int heif_image_has_mastering_display_colour_volume(const struct heif_image*); LIBHEIF_API void heif_image_get_mastering_display_colour_volume(const struct heif_image*, struct heif_mastering_display_colour_volume* out); // Returns whether the image has 'mastering display colour volume' information. If 0 is returned, the output is not filled. LIBHEIF_API int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle*, struct heif_mastering_display_colour_volume* out); LIBHEIF_API void heif_image_set_mastering_display_colour_volume(const struct heif_image*, const struct heif_mastering_display_colour_volume* in); // Converts the internal numeric representation of heif_mastering_display_colour_volume to the // normalized values, collected in heif_decoded_mastering_display_colour_volume. // Values that are out-of-range are decoded to 0, indicating an undefined value (as specified in ISO/IEC 23008-2). LIBHEIF_API struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_mastering_display_colour_volume* in, struct heif_decoded_mastering_display_colour_volume* out); LIBHEIF_API void heif_image_get_pixel_aspect_ratio(const struct heif_image*, uint32_t* aspect_h, uint32_t* aspect_v); // Returns whether the image has 'pixel aspect ratio information' information. If 0 is returned, the output is filled with the 1:1 default. LIBHEIF_API int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle*, uint32_t* aspect_h, uint32_t* aspect_v); LIBHEIF_API void heif_image_set_pixel_aspect_ratio(struct heif_image*, uint32_t aspect_h, uint32_t aspect_v); // ==================================================================================================== // Encoding API LIBHEIF_API struct heif_error heif_context_write_to_file(struct heif_context*, const char* filename); struct heif_writer { // API version supported by this writer int writer_api_version; // --- version 1 functions --- // On success, the returned heif_error may have a NULL message. It will automatically be replaced with a "Success" string. struct heif_error (* write)(struct heif_context* ctx, // TODO: why do we need this parameter? const void* data, size_t size, void* userdata); }; LIBHEIF_API struct heif_error heif_context_write(struct heif_context*, struct heif_writer* writer, void* userdata); // Add a compatible brand that is now added automatically by libheif when encoding images (e.g. some application brands like 'geo1'). LIBHEIF_API void heif_context_add_compatible_brand(struct heif_context* ctx, heif_brand2 compatible_brand); // ----- encoder ----- // The encoder used for actually encoding an image. struct heif_encoder; // A description of the encoder's capabilities and name. struct heif_encoder_descriptor; // A configuration parameter of the encoder. Each encoder implementation may have a different // set of parameters. For the most common settings (e.q. quality), special functions to set // the parameters are provided. struct heif_encoder_parameter; struct heif_decoder_descriptor; // Get a list of available decoders. You can filter the encoders by compression format. // Use format_filter==heif_compression_undefined to get all available decoders. // The returned list of decoders is sorted by their priority (which is a plugin property). // The number of decoders is returned, which are not more than 'count' if (out_decoders != nullptr). // By setting out_decoders==nullptr, you can query the number of decoders, 'count' is ignored. LIBHEIF_API int heif_get_decoder_descriptors(enum heif_compression_format format_filter, const struct heif_decoder_descriptor** out_decoders, int count); // Return a long, descriptive name of the decoder (including version information). LIBHEIF_API const char* heif_decoder_descriptor_get_name(const struct heif_decoder_descriptor*); // Return a short, symbolic name for identifying the decoder. // This name should stay constant over different decoder versions. // Note: the returned ID may be NULL for old plugins that don't support this yet. LIBHEIF_API const char* heif_decoder_descriptor_get_id_name(const struct heif_decoder_descriptor*); // DEPRECATED: use heif_get_encoder_descriptors() instead. // Get a list of available encoders. You can filter the encoders by compression format and name. // Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards. // The returned list of encoders is sorted by their priority (which is a plugin property). // The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr). // By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored. // Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder(). LIBHEIF_API int heif_context_get_encoder_descriptors(struct heif_context*, // TODO: why do we need this parameter? enum heif_compression_format format_filter, const char* name_filter, const struct heif_encoder_descriptor** out_encoders, int count); // Get a list of available encoders. You can filter the encoders by compression format and name. // Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards. // The returned list of encoders is sorted by their priority (which is a plugin property). // The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr). // By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored. // Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder(). LIBHEIF_API int heif_get_encoder_descriptors(enum heif_compression_format format_filter, const char* name_filter, const struct heif_encoder_descriptor** out_encoders, int count); // Return a long, descriptive name of the encoder (including version information). LIBHEIF_API const char* heif_encoder_descriptor_get_name(const struct heif_encoder_descriptor*); // Return a short, symbolic name for identifying the encoder. // This name should stay constant over different encoder versions. LIBHEIF_API const char* heif_encoder_descriptor_get_id_name(const struct heif_encoder_descriptor*); LIBHEIF_API enum heif_compression_format heif_encoder_descriptor_get_compression_format(const struct heif_encoder_descriptor*); LIBHEIF_API int heif_encoder_descriptor_supports_lossy_compression(const struct heif_encoder_descriptor*); LIBHEIF_API int heif_encoder_descriptor_supports_lossless_compression(const struct heif_encoder_descriptor*); // Get an encoder instance that can be used to actually encode images from a descriptor. LIBHEIF_API struct heif_error heif_context_get_encoder(struct heif_context* context, const struct heif_encoder_descriptor*, struct heif_encoder** out_encoder); // Quick check whether there is a decoder available for the given format. // Note that the decoder still may not be able to decode all variants of that format. // You will have to query that further (todo) or just try to decode and check the returned error. LIBHEIF_API int heif_have_decoder_for_format(enum heif_compression_format format); // Quick check whether there is an enoder available for the given format. // Note that the encoder may be limited to a certain subset of features (e.g. only 8 bit, only lossy). // You will have to query the specific capabilities further. LIBHEIF_API int heif_have_encoder_for_format(enum heif_compression_format format); // Get an encoder for the given compression format. If there are several encoder plugins // for this format, the encoder with the highest plugin priority will be returned. LIBHEIF_API struct heif_error heif_context_get_encoder_for_format(struct heif_context* context, enum heif_compression_format format, struct heif_encoder**); // You have to release the encoder after use. LIBHEIF_API void heif_encoder_release(struct heif_encoder*); // Get the encoder name from the encoder itself. LIBHEIF_API const char* heif_encoder_get_name(const struct heif_encoder*); // --- Encoder Parameters --- // Libheif supports settings parameters through specialized functions and through // generic functions by parameter name. Sometimes, the same parameter can be set // in both ways. // We consider it best practice to use the generic parameter functions only in // dynamically generated user interfaces, as no guarantees are made that some specific // parameter names are supported by all plugins. // Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is // encoder dependent. LIBHEIF_API struct heif_error heif_encoder_set_lossy_quality(struct heif_encoder*, int quality); LIBHEIF_API struct heif_error heif_encoder_set_lossless(struct heif_encoder*, int enable); // level should be between 0 (= none) to 4 (= full) LIBHEIF_API struct heif_error heif_encoder_set_logging_level(struct heif_encoder*, int level); // Get a generic list of encoder parameters. // Each encoder may define its own, additional set of parameters. // You do not have to free the returned list. LIBHEIF_API const struct heif_encoder_parameter* const* heif_encoder_list_parameters(struct heif_encoder*); // Return the parameter name. LIBHEIF_API const char* heif_encoder_parameter_get_name(const struct heif_encoder_parameter*); enum heif_encoder_parameter_type { heif_encoder_parameter_type_integer = 1, heif_encoder_parameter_type_boolean = 2, heif_encoder_parameter_type_string = 3 }; // Return the parameter type. LIBHEIF_API enum heif_encoder_parameter_type heif_encoder_parameter_get_type(const struct heif_encoder_parameter*); // DEPRECATED. Use heif_encoder_parameter_get_valid_integer_values() instead. LIBHEIF_API struct heif_error heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_parameter*, int* have_minimum_maximum, int* minimum, int* maximum); // If integer is limited by a range, have_minimum and/or have_maximum will be != 0 and *minimum, *maximum is set. // If integer is limited by a fixed set of values, *num_valid_values will be >0 and *out_integer_array is set. LIBHEIF_API struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter*, int* have_minimum, int* have_maximum, int* minimum, int* maximum, int* num_valid_values, const int** out_integer_array); LIBHEIF_API struct heif_error heif_encoder_parameter_get_valid_string_values(const struct heif_encoder_parameter*, const char* const** out_stringarray); LIBHEIF_API struct heif_error heif_encoder_set_parameter_integer(struct heif_encoder*, const char* parameter_name, int value); LIBHEIF_API struct heif_error heif_encoder_get_parameter_integer(struct heif_encoder*, const char* parameter_name, int* value); // TODO: name should be changed to heif_encoder_get_valid_integer_parameter_range LIBHEIF_API // DEPRECATED. struct heif_error heif_encoder_parameter_integer_valid_range(struct heif_encoder*, const char* parameter_name, int* have_minimum_maximum, int* minimum, int* maximum); LIBHEIF_API struct heif_error heif_encoder_set_parameter_boolean(struct heif_encoder*, const char* parameter_name, int value); LIBHEIF_API struct heif_error heif_encoder_get_parameter_boolean(struct heif_encoder*, const char* parameter_name, int* value); LIBHEIF_API struct heif_error heif_encoder_set_parameter_string(struct heif_encoder*, const char* parameter_name, const char* value); LIBHEIF_API struct heif_error heif_encoder_get_parameter_string(struct heif_encoder*, const char* parameter_name, char* value, int value_size); // returns a NULL-terminated list of valid strings or NULL if all values are allowed LIBHEIF_API struct heif_error heif_encoder_parameter_string_valid_values(struct heif_encoder*, const char* parameter_name, const char* const** out_stringarray); LIBHEIF_API struct heif_error heif_encoder_parameter_integer_valid_values(struct heif_encoder*, const char* parameter_name, int* have_minimum, int* have_maximum, int* minimum, int* maximum, int* num_valid_values, const int** out_integer_array); // Set a parameter of any type to the string value. // Integer values are parsed from the string. // Boolean values can be "true"/"false"/"1"/"0" // // x265 encoder specific note: // When using the x265 encoder, you may pass any of its parameters by // prefixing the parameter name with 'x265:'. Hence, to set the 'ctu' parameter, // you will have to set 'x265:ctu' in libheif. // Note that there is no checking for valid parameters when using the prefix. LIBHEIF_API struct heif_error heif_encoder_set_parameter(struct heif_encoder*, const char* parameter_name, const char* value); // Get the current value of a parameter of any type as a human readable string. // The returned string is compatible with heif_encoder_set_parameter(). LIBHEIF_API struct heif_error heif_encoder_get_parameter(struct heif_encoder*, const char* parameter_name, char* value_ptr, int value_size); // Query whether a specific parameter has a default value. LIBHEIF_API int heif_encoder_has_default(struct heif_encoder*, const char* parameter_name); // The orientation values are defined equal to the EXIF Orientation tag. enum heif_orientation { heif_orientation_normal = 1, heif_orientation_flip_horizontally = 2, heif_orientation_rotate_180 = 3, heif_orientation_flip_vertically = 4, heif_orientation_rotate_90_cw_then_flip_horizontally = 5, heif_orientation_rotate_90_cw = 6, heif_orientation_rotate_90_cw_then_flip_vertically = 7, heif_orientation_rotate_270_cw = 8 }; struct heif_encoding_options { uint8_t version; // version 1 options uint8_t save_alpha_channel; // default: true // version 2 options // DEPRECATED. This option is not required anymore. Its value will be ignored. uint8_t macOS_compatibility_workaround; // version 3 options uint8_t save_two_colr_boxes_when_ICC_and_nclx_available; // default: false // version 4 options // Set this to the NCLX parameters to be used in the output image or set to NULL // when the same parameters as in the input image should be used. struct heif_color_profile_nclx* output_nclx_profile; uint8_t macOS_compatibility_workaround_no_nclx_profile; // version 5 options // libheif will generate irot/imir boxes to match these orientations enum heif_orientation image_orientation; // version 6 options struct heif_color_conversion_options color_conversion_options; // version 7 options // Set this to true to use compressed form of uncC where possible. uint8_t prefer_uncC_short_form; // TODO: we should add a flag to force MIAF compatible outputs. E.g. this will put restrictions on grid tile sizes and // might add a clap box when the grid output size does not match the color subsampling factors. // Since some of these constraints have to be known before actually encoding the image, "forcing MIAF compatibility" // could also be a flag in the heif_context. }; LIBHEIF_API struct heif_encoding_options* heif_encoding_options_alloc(void); LIBHEIF_API void heif_encoding_options_free(struct heif_encoding_options*); // Compress the input image. // Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL. // 'options' should be NULL for now. // The first image added to the context is also automatically set the primary image, but // you can change the primary image later with heif_context_set_primary_image(). LIBHEIF_API struct heif_error heif_context_encode_image(struct heif_context*, const struct heif_image* image, struct heif_encoder* encoder, const struct heif_encoding_options* options, struct heif_image_handle** out_image_handle); /** * @brief Encodes an array of images into a grid. * * @param ctx The file context * @param tiles User allocated array of images that will form the grid. * @param rows The number of rows in the grid. * @param columns The number of columns in the grid. * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format() * @param input_options Optional, may be nullptr. * @param out_image_handle Returns a handle to the grid. The caller is responsible for freeing it. * @return Returns an error if ctx, tiles, or encoder is nullptr. If rows or columns is 0. */ LIBHEIF_API struct heif_error heif_context_encode_grid(struct heif_context* ctx, struct heif_image** tiles, uint16_t rows, uint16_t columns, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, struct heif_image_handle** out_image_handle); LIBHEIF_API struct heif_error heif_context_add_grid_image(struct heif_context* ctx, uint32_t image_width, uint32_t image_height, uint32_t tile_columns, uint32_t tile_rows, const struct heif_encoding_options* encoding_options, struct heif_image_handle** out_grid_image_handle); LIBHEIF_API struct heif_error heif_context_add_image_tile(struct heif_context* ctx, struct heif_image_handle* tiled_image, uint32_t tile_x, uint32_t tile_y, const struct heif_image* image, struct heif_encoder* encoder); // offsets[] should either be NULL (all offsets==0) or an array of size 2*nImages with x;y offset pairs. // If background_rgba is NULL, the background is transparent. LIBHEIF_API struct heif_error heif_context_add_overlay_image(struct heif_context* ctx, uint32_t image_width, uint32_t image_height, uint16_t nImages, const heif_item_id* image_ids, int32_t* offsets, const uint16_t background_rgba[4], struct heif_image_handle** out_iovl_image_handle); LIBHEIF_API struct heif_error heif_context_set_primary_image(struct heif_context*, struct heif_image_handle* image_handle); // Encode the 'image' as a scaled down thumbnail image. // The image is scaled down to fit into a square area of width 'bbox_size'. // If the input image is already so small that it fits into this bounding box, no thumbnail // image is encoded and NULL is returned in 'out_thumb_image_handle'. // No error is returned in this case. // The encoded thumbnail is automatically assigned to the 'master_image_handle'. Hence, you // do not have to call heif_context_assign_thumbnail(). LIBHEIF_API struct heif_error heif_context_encode_thumbnail(struct heif_context*, const struct heif_image* image, const struct heif_image_handle* master_image_handle, struct heif_encoder* encoder, const struct heif_encoding_options* options, int bbox_size, struct heif_image_handle** out_thumb_image_handle); // Assign 'thumbnail_image' as the thumbnail image of 'master_image'. LIBHEIF_API struct heif_error heif_context_assign_thumbnail(struct heif_context*, const struct heif_image_handle* master_image, const struct heif_image_handle* thumbnail_image); // Add EXIF metadata to an image. LIBHEIF_API struct heif_error heif_context_add_exif_metadata(struct heif_context*, const struct heif_image_handle* image_handle, const void* data, int size); // Add XMP metadata to an image. LIBHEIF_API struct heif_error heif_context_add_XMP_metadata(struct heif_context*, const struct heif_image_handle* image_handle, const void* data, int size); // New version of heif_context_add_XMP_metadata() with data compression (experimental). LIBHEIF_API struct heif_error heif_context_add_XMP_metadata2(struct heif_context*, const struct heif_image_handle* image_handle, const void* data, int size, enum heif_metadata_compression compression); // Add generic, proprietary metadata to an image. You have to specify an 'item_type' that will // identify your metadata. 'content_type' can be an additional type, or it can be NULL. // For example, this function can be used to add IPTC metadata (IIM stream, not XMP) to an image. // Although not standard, we propose to store IPTC data with item type="iptc", content_type=NULL. LIBHEIF_API struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size, const char* item_type, const char* content_type); // Add generic metadata with item_type "uri ". Items with this type do not have a content_type, but // an item_uri_type and they have no content_encoding (they are always stored uncompressed). LIBHEIF_API struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size, const char* item_uri_type, heif_item_id* out_item_id); // --- heif_image allocation /** * Create a new image of the specified resolution and colorspace. * *

This does not allocate memory for the image data. Use {@link heif_image_add_plane} to * add the corresponding planes to match the specified {@code colorspace} and {@code chroma}. * * @param width the width of the image in pixels * @param height the height of the image in pixels * @param colorspace the colorspace of the image * @param chroma the chroma of the image * @param out_image pointer to pointer of the resulting image * @return whether the creation succeeded or there was an error */ LIBHEIF_API struct heif_error heif_image_create(int width, int height, enum heif_colorspace colorspace, enum heif_chroma chroma, struct heif_image** out_image); /** * Add an image plane to the image. * *

The image plane needs to match the colorspace and chroma of the image. Note * that this does not need to be a single "planar" format - interleaved pixel channels * can also be used if the chroma is interleaved. * *

The indicated bit_depth corresponds to the bit depth per channel. For example, * with an interleaved format like RRGGBB where each color is represented by 10 bits, * the {@code bit_depth} would be {@code 10} rather than {@code 30}. * *

For backward compatibility, one can also specify 24bits for RGB and 32bits for RGBA, * instead of the preferred 8 bits. However, this use is deprecated. * * @param image the parent image to add the channel plane to * @param channel the channel of the plane to add * @param width the width of the plane * @param height the height of the plane * @param bit_depth the bit depth per color channel * @return whether the addition succeeded or there was an error * * @note The width and height are usually the same as the parent image, but can be * less for subsampling. * * @note The specified width can differ from the row stride of the resulting image plane. * Always use the result of {@link heif_image_get_plane} or {@link heif_image_get_plane_readonly} * to determine row stride. */ LIBHEIF_API struct heif_error heif_image_add_plane(struct heif_image* image, enum heif_channel channel, int width, int height, int bit_depth); // Signal that the image is premultiplied by the alpha pixel values. LIBHEIF_API void heif_image_set_premultiplied_alpha(struct heif_image* image, int is_premultiplied_alpha); LIBHEIF_API int heif_image_is_premultiplied_alpha(struct heif_image* image); // This function extends the padding of the image so that it has at least the given physical size. // The padding border is filled with the pixels along the right/bottom border. // This function may be useful if you want to process the image, but have some external padding requirements. // The image size will not be modified if it is already larger/equal than the given physical size. // I.e. you cannot assume that after calling this function, the stride will be equal to min_physical_width. LIBHEIF_API struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height); // --- register plugins struct heif_decoder_plugin; struct heif_encoder_plugin; // DEPRECATED. Use heif_register_decoder_plugin(const struct heif_decoder_plugin*) instead. LIBHEIF_API struct heif_error heif_register_decoder(struct heif_context* heif, const struct heif_decoder_plugin*); LIBHEIF_API struct heif_error heif_register_decoder_plugin(const struct heif_decoder_plugin*); LIBHEIF_API struct heif_error heif_register_encoder_plugin(const struct heif_encoder_plugin*); // DEPRECATED, typo in function name LIBHEIF_API int heif_encoder_descriptor_supportes_lossy_compression(const struct heif_encoder_descriptor*); // DEPRECATED, typo in function name LIBHEIF_API int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor*); #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_regions.cc000664 001750 001750 00000057450 15003473471 023060 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 Dirk Farin * Copyright (c) 2023 Brad Hards * * 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 "heif_plugin.h" #include "region.h" #include "heif_regions.h" #include "file.h" #include "api_structs.h" #include "context.h" #include #include #include #include #include int heif_image_handle_get_number_of_region_items(const struct heif_image_handle* handle) { return (int) handle->image->get_region_item_ids().size(); } int heif_image_handle_get_list_of_region_item_ids(const struct heif_image_handle* handle, heif_item_id* item_ids, int max_count) { auto region_item_ids = handle->image->get_region_item_ids(); int num = std::min((int) region_item_ids.size(), max_count); memcpy(item_ids, region_item_ids.data(), num * sizeof(heif_item_id)); return num; } struct heif_error heif_context_get_region_item(const struct heif_context* context, heif_item_id region_item_id, struct heif_region_item** out) { if (out==nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument"}; } auto r = context->context->get_region_item(region_item_id); if (r==nullptr) { return {heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced, "Region item does not exist"}; } heif_region_item* item = new heif_region_item(); item->context = context->context; item->region_item = std::move(r); *out = item; return heif_error_success; } heif_item_id heif_region_item_get_id(struct heif_region_item* region_item) { if (region_item == nullptr) { return -1; } return region_item->region_item->item_id; } void heif_region_item_release(struct heif_region_item* region_item) { delete region_item; } void heif_region_item_get_reference_size(struct heif_region_item* region_item, uint32_t* width, uint32_t* height) { auto r = region_item->context->get_region_item(region_item->region_item->item_id); if (width) *width = r->reference_width; if (height) *height = r->reference_height; } int heif_region_item_get_number_of_regions(const struct heif_region_item* region_item) { return region_item->region_item->get_number_of_regions(); } int heif_region_item_get_list_of_regions(const struct heif_region_item* region_item, struct heif_region** out_regions, int max_count) { auto regions = region_item->region_item->get_regions(); int num = std::min(max_count, (int) regions.size()); for (int i = 0; i < num; i++) { auto region = new heif_region(); region->context = region_item->context; //region->parent_region_item_id = region_item->region_item->item_id; region->region_item = region_item->region_item; region->region = regions[i]; out_regions[i] = region; } return num; } struct heif_error heif_image_handle_add_region_item(struct heif_image_handle* image_handle, uint32_t reference_width, uint32_t reference_height, struct heif_region_item** out_region_item) { std::shared_ptr regionItem = image_handle->context->add_region_item(reference_width, reference_height); image_handle->image->add_region_item_id(regionItem->item_id); if (out_region_item) { heif_region_item* item = new heif_region_item(); item->context = image_handle->context; item->region_item = std::move(regionItem); *out_region_item = item; } return heif_error_success; } static struct heif_region* create_region(const std::shared_ptr& r, heif_region_item* item) { auto region = new heif_region(); region->region = r; region->region_item = item->region_item; region->context = item->context; return region; } struct heif_error heif_region_item_add_region_point(struct heif_region_item* item, int32_t x, int32_t y, struct heif_region** out_region) { auto region = std::make_shared(); region->x = x; region->y = y; item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_rectangle(struct heif_region_item* item, int32_t x, int32_t y, uint32_t width, uint32_t height, struct heif_region** out_region) { auto region = std::make_shared(); region->x = x; region->y = y; region->width = width; region->height = height; item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_ellipse(struct heif_region_item* item, int32_t x, int32_t y, uint32_t radius_x, uint32_t radius_y, struct heif_region** out_region) { auto region = std::make_shared(); region->x = x; region->y = y; region->radius_x = radius_x; region->radius_y = radius_y; item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_polygon(struct heif_region_item* item, const int32_t* pts, int nPoints, struct heif_region** out_region) { auto region = std::make_shared(); region->points.resize(nPoints); for (int i=0;ipoints[i].x = pts[2*i+0]; region->points[i].y = pts[2*i+1]; } region->closed = true; item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_polyline(struct heif_region_item* item, const int32_t* pts, int nPoints, struct heif_region** out_region) { auto region = std::make_shared(); region->points.resize(nPoints); for (int i=0;ipoints[i].x = pts[2*i+0]; region->points[i].y = pts[2*i+1]; } region->closed = false; item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_referenced_mask(struct heif_region_item* item, int32_t x, int32_t y, uint32_t width, uint32_t height, heif_item_id mask_item_id, struct heif_region** out_region) { auto region = std::make_shared(); region->x = x; region->y = y; region->width = width; region->height = height; region->referenced_item = mask_item_id; item->region_item->add_region(region); if (out_region) { *out_region = create_region(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. */ std::shared_ptr ctx = item->context; ctx->add_region_referenced_mask_ref(item->region_item->item_id, mask_item_id); return heif_error_success; } struct heif_error heif_region_item_add_region_inline_mask_data(struct heif_region_item* item, int32_t x, int32_t y, uint32_t width, uint32_t height, const uint8_t* mask_data, size_t mask_data_len, struct heif_region** out_region) { auto region = std::make_shared(); region->x = x; region->y = y; region->width = width; region->height = height; region->mask_data.resize(mask_data_len); std::memcpy(region->mask_data.data(), mask_data, region->mask_data.size()); item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } struct heif_error heif_region_item_add_region_inline_mask(struct heif_region_item* item, int32_t x0, int32_t y0, uint32_t width, uint32_t height, heif_image* mask_image, struct heif_region** out_region) { if (! heif_image_has_channel(mask_image, heif_channel_Y)) { return {heif_error_Usage_error, heif_suberror_Nonexisting_image_channel_referenced, "Inline mask image must have a Y channel"}; } auto region = std::make_shared(); region->x = x0; region->y = y0; region->width = width; region->height = height; region->mask_data.resize((width * height + 7) / 8); memset(region->mask_data.data(), 0, region->mask_data.size()); uint32_t mask_height = mask_image->image->get_height(); uint32_t mask_width = mask_image->image->get_width(); int stride; uint8_t* p = heif_image_get_plane(mask_image, heif_channel_Y, &stride); uint64_t pixel_index = 0; for (uint32_t y = 0; y < mask_height; y++) { for (uint32_t x = 0; x < mask_width; x++) { uint8_t mask_bit = p[y * stride + x] & 0x80; // use high-order bit of the 8-bit mask value as binary mask value region->mask_data.data()[pixel_index/8] |= uint8_t(mask_bit >> (pixel_index % 8)); pixel_index++; } } item->region_item->add_region(region); if (out_region) { *out_region = create_region(region, item); } return heif_error_success; } void heif_region_release(const struct heif_region* region) { delete region; } void heif_region_release_many(const struct heif_region* const* regions, int num) { for (int i = 0; i < num; i++) { delete regions[i]; } } enum heif_region_type heif_region_get_type(const struct heif_region* region) { return region->region->getRegionType(); } struct heif_error heif_region_get_point(const struct heif_region* region, int32_t* x, int32_t* y) { if (!x || !y) { return heif_error_invalid_parameter_value; } const std::shared_ptr point = std::dynamic_pointer_cast(region->region); if (point) { *x = point->x; *y = point->y; return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_point_transformed(const struct heif_region* region, heif_item_id image_id, double* x, double* y) { if (!x || !y) { return heif_error_invalid_parameter_value; } const std::shared_ptr point = std::dynamic_pointer_cast(region->region); if (point) { auto t = RegionCoordinateTransform::create(region->context->get_heif_file(), image_id, region->region_item->reference_width, region->region_item->reference_height); RegionCoordinateTransform::Point p = t.transform_point({(double) point->x, (double) point->y}); *x = p.x; *y = p.y; return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_rectangle(const struct heif_region* region, int32_t* x, int32_t* y, uint32_t* width, uint32_t* height) { const std::shared_ptr rect = std::dynamic_pointer_cast(region->region); if (rect) { *x = rect->x; *y = rect->y; *width = rect->width; *height = rect->height; return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_rectangle_transformed(const struct heif_region* region, heif_item_id image_id, double* x, double* y, double* width, double* height) { const std::shared_ptr rect = std::dynamic_pointer_cast(region->region); if (rect) { auto t = RegionCoordinateTransform::create(region->context->get_heif_file(), image_id, region->region_item->reference_width, region->region_item->reference_height); RegionCoordinateTransform::Point p = t.transform_point({(double) rect->x, (double) rect->y}); RegionCoordinateTransform::Extent e = t.transform_extent({(double) rect->width, (double) rect->height}); *x = p.x; *y = p.y; *width = e.x; *height = e.y; return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_ellipse(const struct heif_region* region, int32_t* x, int32_t* y, uint32_t* radius_x, uint32_t* radius_y) { const std::shared_ptr ellipse = std::dynamic_pointer_cast(region->region); if (ellipse) { *x = ellipse->x; *y = ellipse->y; *radius_x = ellipse->radius_x; *radius_y = ellipse->radius_y; return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_ellipse_transformed(const struct heif_region* region, heif_item_id image_id, double* x, double* y, double* radius_x, double* radius_y) { const std::shared_ptr ellipse = std::dynamic_pointer_cast(region->region); if (ellipse) { auto t = RegionCoordinateTransform::create(region->context->get_heif_file(), image_id, region->region_item->reference_width, region->region_item->reference_height); RegionCoordinateTransform::Point p = t.transform_point({(double) ellipse->x, (double) ellipse->y}); RegionCoordinateTransform::Extent e = t.transform_extent({(double) ellipse->radius_x, (double) ellipse->radius_y}); *x = p.x; *y = p.y; *radius_x = e.x; *radius_y = e.y; return heif_error_success; } return heif_error_invalid_parameter_value; } static int heif_region_get_poly_num_points(const struct heif_region* region) { const std::shared_ptr polygon = std::dynamic_pointer_cast(region->region); if (polygon) { return (int) polygon->points.size(); } return 0; } int heif_region_get_polygon_num_points(const struct heif_region* region) { return heif_region_get_poly_num_points(region); } int heif_region_get_polyline_num_points(const struct heif_region* region) { return heif_region_get_poly_num_points(region); } static struct heif_error heif_region_get_poly_points(const struct heif_region* region, int32_t* pts) { if (pts == nullptr) { return heif_error_invalid_parameter_value; } const std::shared_ptr poly = std::dynamic_pointer_cast(region->region); if (poly) { for (int i = 0; i < (int) poly->points.size(); i++) { pts[2 * i + 0] = poly->points[i].x; pts[2 * i + 1] = poly->points[i].y; } return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_polygon_points(const struct heif_region* region, int32_t* pts) { return heif_region_get_poly_points(region, pts); } struct heif_error heif_region_get_polyline_points(const struct heif_region* region, int32_t* pts) { return heif_region_get_poly_points(region, pts); } static struct heif_error heif_region_get_poly_points_scaled(const struct heif_region* region, double* pts, heif_item_id image_id) { if (pts == nullptr) { return heif_error_invalid_parameter_value; } const std::shared_ptr poly = std::dynamic_pointer_cast(region->region); if (poly) { auto t = RegionCoordinateTransform::create(region->context->get_heif_file(), image_id, region->region_item->reference_width, region->region_item->reference_height); for (int i = 0; i < (int) poly->points.size(); i++) { RegionCoordinateTransform::Point p = t.transform_point({(double) poly->points[i].x, (double) poly->points[i].y}); pts[2 * i + 0] = p.x; pts[2 * i + 1] = p.y; } return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_polygon_points_transformed(const struct heif_region* region, heif_item_id image_id, double* pts) { return heif_region_get_poly_points_scaled(region, pts, image_id); } struct heif_error heif_region_get_polyline_points_transformed(const struct heif_region* region, heif_item_id image_id, double* pts) { return heif_region_get_poly_points_scaled(region, pts, image_id); } struct heif_error heif_region_get_referenced_mask_ID(const struct heif_region* region, int32_t* x, int32_t* y, uint32_t* width, uint32_t* height, heif_item_id *mask_item_id) { if ((x == nullptr) || (y == nullptr) || (width == nullptr) || (height == nullptr) || (mask_item_id == nullptr)) { return heif_error_invalid_parameter_value; } const std::shared_ptr mask = std::dynamic_pointer_cast(region->region); if (mask) { *x = mask->x; *y = mask->y; *width = mask->width; *height = mask->height; *mask_item_id = mask->referenced_item; return heif_error_success; } return heif_error_invalid_parameter_value; } size_t heif_region_get_inline_mask_data_len(const struct heif_region* region) { const std::shared_ptr mask = std::dynamic_pointer_cast(region->region); if (mask) { return mask->mask_data.size(); } return 0; } struct heif_error heif_region_get_inline_mask_data(const struct heif_region* region, int32_t* x, int32_t* y, uint32_t* width, uint32_t* height, uint8_t* data) { if ((x == nullptr) || (y == nullptr) || (width == nullptr) || (height == nullptr)) { return heif_error_invalid_parameter_value; } const std::shared_ptr mask = std::dynamic_pointer_cast(region->region); if (mask) { *x = mask->x; *y = mask->y; *width = mask->width; *height = mask->height; memcpy(data, mask->mask_data.data(), mask->mask_data.size()); return heif_error_success; } return heif_error_invalid_parameter_value; } static struct heif_error heif_region_get_inline_mask_image(const struct heif_region* region, int32_t* out_x0, int32_t* out_y0, uint32_t* out_width, uint32_t* out_height, heif_image** out_mask_image) { if ((out_x0 == nullptr) || (out_y0 == nullptr) || (out_width == nullptr) || (out_height == nullptr)) { return heif_error_invalid_parameter_value; } const std::shared_ptr mask = std::dynamic_pointer_cast(region->region); if (mask) { *out_x0 = mask->x; *out_y0 = mask->y; uint32_t width = *out_width = mask->width; uint32_t height= *out_height = mask->height; uint8_t* mask_data = mask->mask_data.data(); heif_error err = heif_image_create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome, out_mask_image); if (err.code) { return err; } err = heif_image_add_plane(*out_mask_image, heif_channel_Y, width, height, 8); if (err.code) { heif_image_release(*out_mask_image); return err; } int stride; uint8_t* p = heif_image_get_plane(*out_mask_image, heif_channel_Y, &stride); uint64_t pixel_index = 0; for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { uint64_t mask_byte = pixel_index / 8; uint8_t pixel_bit = uint8_t(0x80U >> (pixel_index % 8)); p[y * stride + x] = (mask_data[mask_byte] & pixel_bit) ? 255 : 0; pixel_index++; } } return heif_error_success; } return heif_error_invalid_parameter_value; } struct heif_error heif_region_get_mask_image(const struct heif_region* region, int32_t* x, int32_t* y, uint32_t* width, uint32_t* height, struct heif_image** mask_image) { if (region->region->getRegionType() == heif_region_type_inline_mask) { return heif_region_get_inline_mask_image(region,x,y,width,height,mask_image); } else if (region->region->getRegionType() == heif_region_type_referenced_mask) { heif_item_id referenced_item_id = 0; heif_error err = heif_region_get_referenced_mask_ID(region, x, y, width, height, &referenced_item_id); if (err.code != heif_error_Ok) { return err; } heif_context ctx; ctx.context = region->context; heif_image_handle* mski_handle_in; err = heif_context_get_image_handle(&ctx, referenced_item_id, &mski_handle_in); if (err.code != heif_error_Ok) { assert(mski_handle_in == nullptr); return err; } err = heif_decode_image(mski_handle_in, mask_image, heif_colorspace_monochrome, heif_chroma_monochrome, NULL); heif_image_handle_release(mski_handle_in); return err; } return heif_error_invalid_parameter_value; } libheif-1.19.8/libheif/api/libheif/api_structs.h000664 001750 001750 00000003556 15003473471 022617 0ustar00farindkfarindk000000 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 HEIF_API_STRUCTS_H #define HEIF_API_STRUCTS_H #include "pixelimage.h" #include "context.h" #include #include "image-items/image_item.h" struct heif_image_handle { std::shared_ptr image; // store reference to keep the context alive while we are using the handle (issue #147) std::shared_ptr context; }; struct heif_image { std::shared_ptr image; }; struct heif_context { std::shared_ptr context; }; struct heif_encoder { heif_encoder(const struct heif_encoder_plugin* plugin); ~heif_encoder(); struct heif_error alloc(); void release(); const struct heif_encoder_plugin* plugin; void* encoder = nullptr; }; struct heif_region_item { std::shared_ptr context; std::shared_ptr region_item; }; struct heif_region { std::shared_ptr context; // we need this to perform coordinate transformation //heif_item_id parent_region_item_id; // we need this to perform coordinate transformation std::shared_ptr region_item; std::shared_ptr region; }; #endif libheif-1.19.8/libheif/api/libheif/heif.cc000664 001750 001750 00000353200 15003473471 021322 0ustar00farindkfarindk000000 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 "heif_plugin.h" #include "security_limits.h" #include "region.h" #include "common_utils.h" #include #include "heif.h" #include "file.h" #include "pixelimage.h" #include "api_structs.h" #include "context.h" #include "plugin_registry.h" #include "error.h" #include "bitstream.h" #include "init.h" #include "image-items/grid.h" #include "image-items/overlay.h" #include "image-items/tiled.h" #include #include #if WITH_UNCOMPRESSED_CODEC #include "image-items/unc_image.h" #endif #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__) #include "heif_emscripten.h" #endif #include #include #include #include #include #include #include #include #include #ifdef _WIN32 // for _write #include #else #include #endif #include const struct heif_error heif_error_success = {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; static struct heif_error error_unsupported_parameter = {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported encoder parameter"}; static struct heif_error error_invalid_parameter_value = {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Invalid parameter value"}; static struct heif_error error_unsupported_plugin_version = {heif_error_Usage_error, heif_suberror_Unsupported_plugin_version, "Unsupported plugin version"}; static struct heif_error error_null_parameter = {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; const char* heif_get_version(void) { return (LIBHEIF_VERSION); } uint32_t heif_get_version_number(void) { return (LIBHEIF_NUMERIC_VERSION); } int heif_get_version_number_major(void) { return ((LIBHEIF_NUMERIC_VERSION) >> 24) & 0xFF; } int heif_get_version_number_minor(void) { return ((LIBHEIF_NUMERIC_VERSION) >> 16) & 0xFF; } int heif_get_version_number_maintenance(void) { return ((LIBHEIF_NUMERIC_VERSION) >> 8) & 0xFF; } heif_filetype_result heif_check_filetype(const uint8_t* data, int len) { if (len < 8) { return heif_filetype_maybe; } if (data[4] != 'f' || data[5] != 't' || data[6] != 'y' || data[7] != 'p') { return heif_filetype_no; } if (len >= 12) { heif_brand2 brand = heif_read_main_brand(data, len); if (brand == heif_brand2_heic) { return heif_filetype_yes_supported; } else if (brand == heif_brand2_heix) { return heif_filetype_yes_supported; } else if (brand == heif_brand2_avif) { return heif_filetype_yes_supported; } else if (brand == heif_brand2_jpeg) { return heif_filetype_yes_supported; } else if (brand == heif_brand2_j2ki) { return heif_filetype_yes_supported; } else if (brand == heif_brand2_mif1) { return heif_filetype_maybe; } else if (brand == heif_brand2_mif2) { return heif_filetype_maybe; } else { return heif_filetype_yes_unsupported; } } return heif_filetype_maybe; } heif_error heif_has_compatible_filetype(const uint8_t* data, int len) { // Get compatible brands first, because that does validity checks heif_brand2* compatible_brands = nullptr; int nBrands = 0; struct heif_error err = heif_list_compatible_brands(data, len, &compatible_brands, &nBrands); if (err.code) { return err; } heif_brand2 main_brand = heif_read_main_brand(data, len); std::set supported_brands{ heif_brand2_avif, heif_brand2_heic, heif_brand2_heix, heif_brand2_j2ki, heif_brand2_jpeg, heif_brand2_miaf, heif_brand2_mif1, heif_brand2_mif2 #if ENABLE_EXPERIMENTAL_MINI_FORMAT , heif_brand2_mif3 #endif }; auto it = supported_brands.find(main_brand); if (it != supported_brands.end()) { heif_free_list_of_compatible_brands(compatible_brands); return heif_error_ok; } for (int i = 0; i < nBrands; i++) { heif_brand2 compatible_brand = compatible_brands[i]; it = supported_brands.find(compatible_brand); if (it != supported_brands.end()) { heif_free_list_of_compatible_brands(compatible_brands); return heif_error_ok; } } heif_free_list_of_compatible_brands(compatible_brands); return {heif_error_Invalid_input, heif_suberror_Unsupported_image_type, "No supported brands found."};; } int heif_check_jpeg_filetype(const uint8_t* data, int len) { if (len < 4 || data == nullptr) { return -1; } return (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && (data[3] & 0xF0) == 0xE0); } heif_brand heif_fourcc_to_brand_enum(const char* fourcc) { if (fourcc == nullptr || !fourcc[0] || !fourcc[1] || !fourcc[2] || !fourcc[3]) { return heif_unknown_brand; } char brand[5]; brand[0] = fourcc[0]; brand[1] = fourcc[1]; brand[2] = fourcc[2]; brand[3] = fourcc[3]; brand[4] = 0; if (strcmp(brand, "heic") == 0) { return heif_heic; } else if (strcmp(brand, "heix") == 0) { return heif_heix; } else if (strcmp(brand, "hevc") == 0) { return heif_hevc; } else if (strcmp(brand, "hevx") == 0) { return heif_hevx; } else if (strcmp(brand, "heim") == 0) { return heif_heim; } else if (strcmp(brand, "heis") == 0) { return heif_heis; } else if (strcmp(brand, "hevm") == 0) { return heif_hevm; } else if (strcmp(brand, "hevs") == 0) { return heif_hevs; } else if (strcmp(brand, "mif1") == 0) { return heif_mif1; } else if (strcmp(brand, "msf1") == 0) { return heif_msf1; } else if (strcmp(brand, "avif") == 0) { return heif_avif; } else if (strcmp(brand, "avis") == 0) { return heif_avis; } else if (strcmp(brand, "vvic") == 0) { return heif_vvic; } else if (strcmp(brand, "j2ki") == 0) { return heif_j2ki; } else if (strcmp(brand, "j2is") == 0) { return heif_j2is; } else { return heif_unknown_brand; } } enum heif_brand heif_main_brand(const uint8_t* data, int len) { if (len < 12) { return heif_unknown_brand; } return heif_fourcc_to_brand_enum((char*) (data + 8)); } heif_brand2 heif_read_main_brand(const uint8_t* data, int len) { if (len < 12) { return heif_unknown_brand; } return heif_fourcc_to_brand((char*) (data + 8)); } heif_brand2 heif_fourcc_to_brand(const char* fourcc_string) { if (fourcc_string == nullptr || !fourcc_string[0] || !fourcc_string[1] || !fourcc_string[2] || !fourcc_string[3]) { return 0; } return fourcc(fourcc_string); } heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len) { if (len < 16) { return heif_unknown_brand; } return heif_fourcc_to_brand((char*) (data + 12)); } void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc) { if (out_fourcc) { out_fourcc[0] = (char) ((brand >> 24) & 0xFF); out_fourcc[1] = (char) ((brand >> 16) & 0xFF); out_fourcc[2] = (char) ((brand >> 8) & 0xFF); out_fourcc[3] = (char) ((brand >> 0) & 0xFF); } } int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc) { if (data == nullptr || len <= 0 || brand_fourcc == nullptr || !brand_fourcc[0] || !brand_fourcc[1] || !brand_fourcc[2] || !brand_fourcc[3]) { return -1; } auto stream = std::make_shared(data, len, false); BitstreamRange range(stream, len); std::shared_ptr box; Error err = Box::read(range, &box, heif_get_global_security_limits()); if (err) { if (err.sub_error_code == heif_suberror_End_of_data) { return -1; } return -2; } auto ftyp = std::dynamic_pointer_cast(box); if (!ftyp) { return -2; } return ftyp->has_compatible_brand(fourcc(brand_fourcc)) ? 1 : 0; } struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size) { if (data == nullptr || out_brands == nullptr || out_size == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument"}; } if (len <= 0) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "data length must be positive"}; } auto stream = std::make_shared(data, len, false); BitstreamRange range(stream, len); std::shared_ptr box; Error err = Box::read(range, &box, heif_get_global_security_limits()); if (err) { if (err.sub_error_code == heif_suberror_End_of_data) { return {err.error_code, err.sub_error_code, "insufficient input data"}; } return {err.error_code, err.sub_error_code, "error reading ftyp box"}; } auto ftyp = std::dynamic_pointer_cast(box); if (!ftyp) { return {heif_error_Invalid_input, heif_suberror_No_ftyp_box, "input is not a ftyp box"}; } auto brands = ftyp->list_brands(); size_t nBrands = brands.size(); *out_brands = (heif_brand2*) malloc(sizeof(heif_brand2) * nBrands); *out_size = (int)nBrands; for (size_t i = 0; i < nBrands; i++) { (*out_brands)[i] = brands[i]; } return heif_error_success; } void heif_free_list_of_compatible_brands(heif_brand2* brands_list) { if (brands_list) { free(brands_list); } } enum class TriBool { No, Yes, Unknown }; TriBool is_jpeg(const uint8_t* data, int len) { if (len < 12) { return TriBool::Unknown; } if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE0 && data[4] == 0x00 && data[5] == 0x10 && data[6] == 0x4A && data[7] == 0x46 && data[8] == 0x49 && data[9] == 0x46 && data[10] == 0x00 && data[11] == 0x01) { return TriBool::Yes; } if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE1 && data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66 && data[10] == 0x00 && data[11] == 0x00) { return TriBool::Yes; } else { return TriBool::No; } } TriBool is_png(const uint8_t* data, int len) { if (len < 8) { return TriBool::Unknown; } if (data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 && data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A) { return TriBool::Yes; } else { return TriBool::No; } } const char* heif_get_file_mime_type(const uint8_t* data, int len) { heif_brand mainBrand = heif_main_brand(data, len); if (mainBrand == heif_heic || mainBrand == heif_heix || mainBrand == heif_heim || mainBrand == heif_heis) { return "image/heic"; } else if (mainBrand == heif_mif1) { return "image/heif"; } else if (mainBrand == heif_hevc || mainBrand == heif_hevx || mainBrand == heif_hevm || mainBrand == heif_hevs) { return "image/heic-sequence"; } else if (mainBrand == heif_msf1) { return "image/heif-sequence"; } else if (mainBrand == heif_avif) { return "image/avif"; } else if (mainBrand == heif_avis) { return "image/avif-sequence"; } #if ENABLE_EXPERIMENTAL_MINI_FORMAT else if (mainBrand == heif_brand2_mif3) { heif_brand2 minorBrand = heif_read_minor_version_brand(data, len); if (minorBrand == heif_brand2_avif) { return "image/avif"; } if (minorBrand == heif_brand2_heic || minorBrand == heif_brand2_heix || minorBrand == heif_brand2_heim || minorBrand == heif_brand2_heis) { return "image/heic"; } // There could be other options in here, like VVC or J2K return "image/heif"; } #endif else if (mainBrand == heif_j2ki) { return "image/hej2k"; } else if (mainBrand == heif_j2is) { return "image/j2is"; } else if (is_jpeg(data, len) == TriBool::Yes) { return "image/jpeg"; } else if (is_png(data, len) == TriBool::Yes) { return "image/png"; } else { return ""; } } const struct heif_security_limits* heif_get_global_security_limits() { return &global_security_limits; } const struct heif_security_limits* heif_get_disabled_security_limits() { return &disabled_security_limits; } struct heif_security_limits* heif_context_get_security_limits(const struct heif_context* ctx) { if (!ctx) { return nullptr; } return ctx->context->get_security_limits(); } struct heif_error heif_context_set_security_limits(struct heif_context* ctx, const struct heif_security_limits* limits) { if (ctx==nullptr || limits==nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument}; } ctx->context->set_security_limits(limits); return heif_error_ok; } heif_context* heif_context_alloc() { load_plugins_if_not_initialized_yet(); struct heif_context* ctx = new heif_context; ctx->context = std::make_shared(); return ctx; } void heif_context_free(heif_context* ctx) { delete ctx; } heif_error heif_context_read_from_file(heif_context* ctx, const char* filename, const struct heif_reading_options*) { Error err = ctx->context->read_from_file(filename); return err.error_struct(ctx->context.get()); } heif_error heif_context_read_from_memory(heif_context* ctx, const void* mem, size_t size, const struct heif_reading_options*) { Error err = ctx->context->read_from_memory(mem, size, true); return err.error_struct(ctx->context.get()); } heif_error heif_context_read_from_memory_without_copy(heif_context* ctx, const void* mem, size_t size, const struct heif_reading_options*) { Error err = ctx->context->read_from_memory(mem, size, false); return err.error_struct(ctx->context.get()); } heif_error heif_context_read_from_reader(struct heif_context* ctx, const struct heif_reader* reader_func_table, void* userdata, const struct heif_reading_options*) { auto reader = std::make_shared(reader_func_table, userdata); Error err = ctx->context->read(reader); return err.error_struct(ctx->context.get()); } // TODO: heif_error heif_context_read_from_file_descriptor(heif_context*, int fd); void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd) { if (!ctx) { return; } std::string dump = ctx->context->debug_dump_boxes(); // TODO(fancycode): Should we return an error if writing fails? #ifdef _WIN32 auto written = _write(fd, dump.c_str(), static_cast(dump.size())); #else auto written = write(fd, dump.c_str(), dump.size()); #endif (void) written; } heif_error heif_context_get_primary_image_handle(heif_context* ctx, heif_image_handle** img) { if (!img) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(ctx->context.get()); } std::shared_ptr primary_image = ctx->context->get_primary_image(true); // It is a requirement of an HEIF file there is always a primary image. // If there is none, an error is generated when loading the file. if (!primary_image) { Error err(heif_error_Invalid_input, heif_suberror_No_or_invalid_primary_item); return err.error_struct(ctx->context.get()); } if (auto errImage = std::dynamic_pointer_cast(primary_image)) { Error error = errImage->get_item_error(); return error.error_struct(ctx->context.get()); } *img = new heif_image_handle(); (*img)->image = std::move(primary_image); (*img)->context = ctx->context; return Error::Ok.error_struct(ctx->context.get()); } struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id) { if (!id) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } std::shared_ptr primary = ctx->context->get_primary_image(true); if (!primary) { return Error(heif_error_Invalid_input, heif_suberror_No_or_invalid_primary_item).error_struct(ctx->context.get()); } *id = primary->get_id(); return Error::Ok.error_struct(ctx->context.get()); } int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id) { const std::vector> images = ctx->context->get_top_level_images(true); for (const auto& img : images) { if (img->get_id() == id) { return true; } } return false; } int heif_context_get_number_of_top_level_images(heif_context* ctx) { return (int) ctx->context->get_top_level_images(true).size(); } int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx, heif_item_id* ID_array, int count) { if (ID_array == nullptr || count == 0 || ctx == nullptr) { return 0; } // fill in ID values into output array const std::vector> imgs = ctx->context->get_top_level_images(true); int n = (int) std::min(count, (int) imgs.size()); for (int i = 0; i < n; i++) { ID_array[i] = imgs[i]->get_id(); } return n; } struct heif_error heif_context_get_image_handle(struct heif_context* ctx, heif_item_id id, struct heif_image_handle** imgHdl) { if (!imgHdl) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, ""}; } auto image = ctx->context->get_image(id, true); if (auto errImage = std::dynamic_pointer_cast(image)) { Error error = errImage->get_item_error(); return error.error_struct(ctx->context.get()); } if (!image) { *imgHdl = nullptr; return {heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced, ""}; } *imgHdl = new heif_image_handle(); (*imgHdl)->image = std::move(image); (*imgHdl)->context = ctx->context; return heif_error_success; } int heif_image_handle_is_primary_image(const struct heif_image_handle* handle) { return handle->image->is_primary(); } heif_item_id heif_image_handle_get_item_id(const struct heif_image_handle* handle) { return handle->image->get_id(); } int heif_image_handle_get_number_of_thumbnails(const struct heif_image_handle* handle) { return (int) handle->image->get_thumbnails().size(); } int heif_image_handle_get_list_of_thumbnail_IDs(const struct heif_image_handle* handle, heif_item_id* ids, int count) { if (ids == nullptr) { return 0; } auto thumbnails = handle->image->get_thumbnails(); int n = (int) std::min(count, (int) thumbnails.size()); for (int i = 0; i < n; i++) { ids[i] = thumbnails[i]->get_id(); } return n; } heif_error heif_image_handle_get_thumbnail(const struct heif_image_handle* handle, heif_item_id thumbnail_id, struct heif_image_handle** out_thumbnail_handle) { if (!out_thumbnail_handle) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(handle->image.get()); } auto thumbnails = handle->image->get_thumbnails(); for (const auto& thumb : thumbnails) { if (thumb->get_id() == thumbnail_id) { *out_thumbnail_handle = new heif_image_handle(); (*out_thumbnail_handle)->image = thumb; (*out_thumbnail_handle)->context = handle->context; return Error::Ok.error_struct(handle->image.get()); } } Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); return err.error_struct(handle->image.get()); } int heif_image_handle_get_number_of_auxiliary_images(const struct heif_image_handle* handle, int include_alpha_image) { return (int) handle->image->get_aux_images(include_alpha_image).size(); } int heif_image_handle_get_list_of_auxiliary_image_IDs(const struct heif_image_handle* handle, int include_alpha_image, heif_item_id* ids, int count) { if (ids == nullptr) { return 0; } auto auxImages = handle->image->get_aux_images(include_alpha_image); int n = (int) std::min(count, (int) auxImages.size()); for (int i = 0; i < n; i++) { ids[i] = auxImages[i]->get_id(); } return n; } struct heif_error heif_image_handle_get_auxiliary_type(const struct heif_image_handle* handle, const char** out_type) { if (out_type == nullptr) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(handle->image.get()); } *out_type = nullptr; const auto& auxType = handle->image->get_aux_type(); char* buf = (char*) malloc(auxType.length() + 1); if (buf == nullptr) { return Error(heif_error_Memory_allocation_error, heif_suberror_Unspecified, "Failed to allocate memory for the type string").error_struct(handle->image.get()); } strcpy(buf, auxType.c_str()); *out_type = buf; return heif_error_success; } void heif_image_handle_release_auxiliary_type(const struct heif_image_handle* handle, const char** out_type) { if (out_type && *out_type) { free((void*) *out_type); *out_type = nullptr; } } // DEPRECATED (typo) void heif_image_handle_free_auxiliary_types(const struct heif_image_handle* handle, const char** out_type) { heif_image_handle_release_auxiliary_type(handle, out_type); } struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif_image_handle* main_image_handle, heif_item_id auxiliary_id, struct heif_image_handle** out_auxiliary_handle) { if (!out_auxiliary_handle) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(main_image_handle->image.get()); } *out_auxiliary_handle = nullptr; auto auxImages = main_image_handle->image->get_aux_images(); for (const auto& aux : auxImages) { if (aux->get_id() == auxiliary_id) { *out_auxiliary_handle = new heif_image_handle(); (*out_auxiliary_handle)->image = aux; (*out_auxiliary_handle)->context = main_image_handle->context; return Error::Ok.error_struct(main_image_handle->image.get()); } } Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); return err.error_struct(main_image_handle->image.get()); } int heif_image_handle_get_width(const struct heif_image_handle* handle) { if (handle && handle->image) { return handle->image->get_width(); } else { return 0; } } int heif_image_handle_get_height(const struct heif_image_handle* handle) { if (handle && handle->image) { return handle->image->get_height(); } else { return 0; } } int heif_image_handle_get_ispe_width(const struct heif_image_handle* handle) { if (handle && handle->image) { return handle->image->get_ispe_width(); } else { return 0; } } int heif_image_handle_get_ispe_height(const struct heif_image_handle* handle) { if (handle && handle->image) { return handle->image->get_ispe_height(); } else { return 0; } } struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle) { auto ctx = new heif_context(); ctx->context = handle->context; return ctx; } heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* tiling) { if (!handle || !tiling) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed to heif_image_handle_get_image_tiling()"}; } *tiling = handle->image->get_heif_image_tiling(); if (process_image_transformations) { Error error = handle->image->process_image_transformations_on_tiling(*tiling); if (error) { return error.error_struct(handle->context.get()); } } return heif_error_ok; } struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle, int process_image_transformations, uint32_t tile_x, uint32_t tile_y, heif_item_id* tile_item_id) { if (!handle || !tile_item_id) { return { heif_error_Usage_error, heif_suberror_Null_pointer_argument }; } std::shared_ptr gridItem = std::dynamic_pointer_cast(handle->image); if (!gridItem) { return { heif_error_Usage_error, heif_suberror_Unspecified, "Image is no grid image" }; } const ImageGrid& gridspec = gridItem->get_grid_spec(); if (tile_x >= gridspec.get_columns() || tile_y >= gridspec.get_rows()) { return { heif_error_Usage_error, heif_suberror_Unspecified, "Grid tile index out of range" }; } if (process_image_transformations) { gridItem->transform_requested_tile_position_to_original_tile_position(tile_x, tile_y); } *tile_item_id = gridItem->get_grid_tiles()[tile_y * gridspec.get_columns() + tile_x]; return heif_error_ok; } #if 0 // TODO: do we need this ? This does not handle rotations. We can use heif_image_handle_get_image_tiling() to get the same information. struct heif_error heif_image_handle_get_tile_size(const struct heif_image_handle* handle, uint32_t* tile_width, uint32_t* tile_height) { if (!handle) { return error_null_parameter; } uint32_t w,h; handle->image->get_tile_size(w,h); if (tile_width) { *tile_width = w; } if (tile_height) { *tile_height = h; } return heif_error_success; } #endif struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context* ctx, uint32_t type_filter, heif_item_id item_filter, int* out_num_groups) { std::shared_ptr grplBox = ctx->context->get_heif_file()->get_grpl_box(); if (!grplBox) { *out_num_groups = 0; return nullptr; } std::vector> all_entity_group_boxes = grplBox->get_all_child_boxes(); if (all_entity_group_boxes.empty()) { *out_num_groups = 0; return nullptr; } // --- filter groups std::vector> entity_group_boxes; for (auto& group : all_entity_group_boxes) { if (type_filter != 0 && group->get_short_type() != type_filter) { continue; } auto groupBox = std::dynamic_pointer_cast(group); const std::vector& items = groupBox->get_item_ids(); if (item_filter != 0 && std::all_of(items.begin(), items.end(), [item_filter](heif_item_id item) { return item != item_filter; })) { continue; } entity_group_boxes.emplace_back(groupBox); } // --- convert to C structs auto* groups = new heif_entity_group[entity_group_boxes.size()]; for (size_t i = 0; i < entity_group_boxes.size(); i++) { const auto& groupBox = entity_group_boxes[i]; const std::vector& items = groupBox->get_item_ids(); groups[i].entity_group_id = groupBox->get_group_id(); groups[i].entity_group_type = groupBox->get_short_type(); groups[i].entities = (items.empty() ? nullptr : new heif_item_id[items.size()]); groups[i].num_entities = static_cast(items.size()); if (groups[i].entities) { // avoid clang static analyzer false positive for (size_t k = 0; k < items.size(); k++) { groups[i].entities[k] = items[k]; } } } *out_num_groups = static_cast(entity_group_boxes.size()); return groups; } void heif_entity_groups_release(struct heif_entity_group* grp, int num_groups) { for (int i=0;i layers(num_layers); for (size_t i = 0; i < num_layers; i++) { layers[i] = layer_item_ids[i]; } Result result = ctx->context->add_pyramid_group(layers); if (result) { if (out_group_id) { *out_group_id = result.value; } return heif_error_success; } else { return result.error.error_struct(ctx->context.get()); } } struct heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(struct heif_context* ctx, heif_entity_group_id id, int* out_num_layers) { if (!out_num_layers) { return nullptr; } std::shared_ptr groupBox = ctx->context->get_heif_file()->get_entity_group(id); if (!groupBox) { return nullptr; } const auto pymdBox = std::dynamic_pointer_cast(groupBox); if (!pymdBox) { return nullptr; } const std::vector pymd_layers = pymdBox->get_layers(); if (pymd_layers.empty()) { return nullptr; } auto items = pymdBox->get_item_ids(); assert(items.size() == pymd_layers.size()); auto* layerInfo = new heif_pyramid_layer_info[pymd_layers.size()]; for (size_t i=0; i(pymd_layers.size()); return layerInfo; } void heif_pyramid_layer_info_release(struct heif_pyramid_layer_info* infos) { delete[] infos; } struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle, enum heif_colorspace* out_colorspace, enum heif_chroma* out_chroma) { Error err = image_handle->image->get_coded_image_colorspace(out_colorspace, out_chroma); if (err) { return err.error_struct(image_handle->image.get()); } return heif_error_success; } int heif_image_handle_has_alpha_channel(const struct heif_image_handle* handle) { // TODO: for now, also scan the grid tiles for alpha information (issue #708), but depending about // how the discussion about this structure goes forward, we might remove this again. return handle->context->has_alpha(handle->image->get_id()); // handle case in issue #708 //return handle->image->get_alpha_channel() != nullptr; // old alpha check that fails on alpha in grid tiles } int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle* handle) { // TODO: what about images that have the alpha in the grid tiles (issue #708) ? return handle->image->is_premultiplied_alpha(); } int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle* handle) { return handle->image->get_luma_bits_per_pixel(); } int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle* handle) { return handle->image->get_chroma_bits_per_pixel(); } int heif_image_handle_has_depth_image(const struct heif_image_handle* handle) { return handle->image->get_depth_channel() != nullptr; } void heif_depth_representation_info_free(const struct heif_depth_representation_info* info) { delete info; } int heif_image_handle_get_depth_image_representation_info(const struct heif_image_handle* handle, heif_item_id depth_image_id, const struct heif_depth_representation_info** out) { std::shared_ptr depth_image; if (out) { if (handle->image->is_depth_channel()) { // Because of an API bug before v1.11.0, the input handle may be the depth image (#422). depth_image = handle->image; } else { depth_image = handle->image->get_depth_channel(); } if (depth_image->has_depth_representation_info()) { auto info = new heif_depth_representation_info; *info = depth_image->get_depth_representation_info(); *out = info; return true; } else { *out = nullptr; } } return false; } int heif_image_handle_get_number_of_depth_images(const struct heif_image_handle* handle) { auto depth_image = handle->image->get_depth_channel(); if (depth_image) { return 1; } else { return 0; } } int heif_image_handle_get_list_of_depth_image_IDs(const struct heif_image_handle* handle, heif_item_id* ids, int count) { auto depth_image = handle->image->get_depth_channel(); if (count == 0) { return 0; } if (depth_image) { ids[0] = depth_image->get_id(); return 1; } else { return 0; } } struct heif_error heif_image_handle_get_depth_image_handle(const struct heif_image_handle* handle, heif_item_id depth_id, struct heif_image_handle** out_depth_handle) { if (out_depth_handle == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL out_depth_handle passed to heif_image_handle_get_depth_image_handle()"}; } auto depth_image = handle->image->get_depth_channel(); if (depth_image->get_id() != depth_id) { *out_depth_handle = nullptr; Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); return err.error_struct(handle->image.get()); } *out_depth_handle = new heif_image_handle(); (*out_depth_handle)->image = depth_image; (*out_depth_handle)->context = handle->context; return Error::Ok.error_struct(handle->image.get()); } void fill_default_decoding_options(heif_decoding_options& options) { options.version = 6; options.ignore_transformations = false; options.start_progress = nullptr; options.on_progress = nullptr; options.end_progress = nullptr; options.progress_user_data = nullptr; // version 2 options.convert_hdr_to_8bit = false; // version 3 options.strict_decoding = false; // version 4 options.decoder_id = nullptr; // version 5 options.color_conversion_options.version = 1; options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average; options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear; options.color_conversion_options.only_use_preferred_chroma_algorithm = false; // version 6 options.cancel_decoding = nullptr; } // overwrite the (possibly lower version) input options over the default options static heif_decoding_options normalize_options(const heif_decoding_options* input_options) { heif_decoding_options options{}; fill_default_decoding_options(options); if (input_options) { switch (input_options->version) { case 6: options.cancel_decoding = input_options->cancel_decoding; // fallthrough case 5: options.color_conversion_options = input_options->color_conversion_options; // fallthrough case 4: options.decoder_id = input_options->decoder_id; // fallthrough case 3: options.strict_decoding = input_options->strict_decoding; // fallthrough case 2: options.convert_hdr_to_8bit = input_options->convert_hdr_to_8bit; // fallthrough case 1: options.ignore_transformations = input_options->ignore_transformations; options.start_progress = input_options->start_progress; options.on_progress = input_options->on_progress; options.end_progress = input_options->end_progress; options.progress_user_data = input_options->progress_user_data; } } return options; } void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options* options) { options->version = 1; #if HAVE_LIBSHARPYUV options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_sharp_yuv; #else options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average; #endif options->preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear; options->only_use_preferred_chroma_algorithm = true; } heif_decoding_options* heif_decoding_options_alloc() { auto options = new heif_decoding_options; fill_default_decoding_options(*options); return options; } void heif_decoding_options_free(heif_decoding_options* options) { delete options; } struct heif_error heif_decode_image(const struct heif_image_handle* in_handle, struct heif_image** out_img, heif_colorspace colorspace, heif_chroma chroma, const struct heif_decoding_options* input_options) { if (out_img == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL out_img passed to heif_decode_image()"}; } *out_img = nullptr; heif_item_id id = in_handle->image->get_id(); heif_decoding_options dec_options = normalize_options(input_options); Result> decodingResult = in_handle->context->decode_image(id, colorspace, chroma, dec_options, false, 0,0); if (decodingResult.error.error_code != heif_error_Ok) { return decodingResult.error.error_struct(in_handle->image.get()); } std::shared_ptr img = decodingResult.value; *out_img = new heif_image(); (*out_img)->image = std::move(img); return Error::Ok.error_struct(in_handle->image.get()); } struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle, struct heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const struct heif_decoding_options* input_options, uint32_t x0, uint32_t y0) { if (!in_handle) { return error_null_parameter; } heif_item_id id = in_handle->image->get_id(); heif_decoding_options dec_options = normalize_options(input_options); Result> decodingResult = in_handle->context->decode_image(id, colorspace, chroma, dec_options, true, x0,y0); if (decodingResult.error.error_code != heif_error_Ok) { return decodingResult.error.error_struct(in_handle->image.get()); } std::shared_ptr img = decodingResult.value; *out_img = new heif_image(); (*out_img)->image = std::move(img); return Error::Ok.error_struct(in_handle->image.get()); } struct heif_error heif_image_create(int width, int height, heif_colorspace colorspace, heif_chroma chroma, struct heif_image** image) { if (image == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_image_create: NULL passed as image pointer."}; } // auto-correct colorspace_YCbCr + chroma_monochrome to colorspace_monochrome // TODO: this should return an error in a later version (see below) if (chroma == heif_chroma_monochrome && colorspace == heif_colorspace_YCbCr) { colorspace = heif_colorspace_monochrome; std::cerr << "libheif warning: heif_image_create() used with an illegal colorspace/chroma combination. This will return an error in a future version.\n"; } // return error if invalid colorspace + chroma combination is used auto validChroma = get_valid_chroma_values_for_colorspace(colorspace); if (std::find(validChroma.begin(), validChroma.end(), chroma) == validChroma.end()) { *image = nullptr; return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Invalid colorspace/chroma combination."}; } struct heif_image* img = new heif_image; img->image = std::make_shared(); img->image->create(width, height, colorspace, chroma); *image = img; return heif_error_success; } int heif_image_get_decoding_warnings(struct heif_image* image, int first_warning_idx, struct heif_error* out_warnings, int max_output_buffer_entries) { if (max_output_buffer_entries == 0) { return (int) image->image->get_warnings().size(); } else { const auto& warnings = image->image->get_warnings(); int n; for (n = 0; n + first_warning_idx < (int) warnings.size(); n++) { out_warnings[n] = warnings[n + first_warning_idx].error_struct(image->image.get()); } return n; } } void heif_image_add_decoding_warning(struct heif_image* image, struct heif_error err) { image->image->add_warning(Error(err.code, err.subcode)); } int heif_image_has_content_light_level(const struct heif_image* image) { return image->image->has_clli(); } void heif_image_get_content_light_level(const struct heif_image* image, struct heif_content_light_level* out) { if (out) { *out = image->image->get_clli(); } } int heif_image_handle_get_content_light_level(const struct heif_image_handle* handle, struct heif_content_light_level* out) { auto clli = handle->image->get_property(); if (out && clli) { *out = clli->clli; } return clli ? 1 : 0; } void heif_image_set_content_light_level(const struct heif_image* image, const struct heif_content_light_level* in) { if (in == nullptr) { return; } image->image->set_clli(*in); } int heif_image_has_mastering_display_colour_volume(const struct heif_image* image) { return image->image->has_mdcv(); } void heif_image_get_mastering_display_colour_volume(const struct heif_image* image, struct heif_mastering_display_colour_volume* out) { *out = image->image->get_mdcv(); } int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle* handle, struct heif_mastering_display_colour_volume* out) { auto mdcv = handle->image->get_property(); if (out && mdcv) { *out = mdcv->mdcv; } return mdcv ? 1 : 0; } void heif_image_set_mastering_display_colour_volume(const struct heif_image* image, const struct heif_mastering_display_colour_volume* in) { if (in == nullptr) { return; } image->image->set_mdcv(*in); } float mdcv_coord_decode_x(uint16_t coord) { // check for unspecified value if (coord < 5 || coord > 37000) { return 0.0f; } return (float) (coord * 0.00002); } float mdcv_coord_decode_y(uint16_t coord) { // check for unspecified value if (coord < 5 || coord > 42000) { return 0.0f; } return (float) (coord * 0.00002); } struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_mastering_display_colour_volume* in, struct heif_decoded_mastering_display_colour_volume* out) { if (in == nullptr || out == nullptr) { return error_null_parameter; } for (int c = 0; c < 3; c++) { out->display_primaries_x[c] = mdcv_coord_decode_x(in->display_primaries_x[c]); out->display_primaries_y[c] = mdcv_coord_decode_y(in->display_primaries_y[c]); } out->white_point_x = mdcv_coord_decode_x(in->white_point_x); out->white_point_y = mdcv_coord_decode_y(in->white_point_y); if (in->max_display_mastering_luminance < 50000 || in->max_display_mastering_luminance > 100000000) { out->max_display_mastering_luminance = 0; } else { out->max_display_mastering_luminance = in->max_display_mastering_luminance * 0.0001; } if (in->min_display_mastering_luminance < 1 || in->min_display_mastering_luminance > 50000) { out->min_display_mastering_luminance = 0; } else { out->min_display_mastering_luminance = in->min_display_mastering_luminance * 0.0001; } return heif_error_success; } void heif_image_get_pixel_aspect_ratio(const struct heif_image* image, uint32_t* aspect_h, uint32_t* aspect_v) { image->image->get_pixel_ratio(aspect_h, aspect_v); } int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle* handle, uint32_t* aspect_h, uint32_t* aspect_v) { auto pasp = handle->image->get_property(); if (pasp) { *aspect_h = pasp->hSpacing; *aspect_v = pasp->vSpacing; return 1; } else { *aspect_h = 1; *aspect_v = 1; return 0; } } void heif_image_set_pixel_aspect_ratio(struct heif_image* image, uint32_t aspect_h, uint32_t aspect_v) { image->image->set_pixel_ratio(aspect_h, aspect_v); } void heif_image_release(const struct heif_image* img) { delete img; } void heif_image_handle_release(const struct heif_image_handle* handle) { delete handle; } heif_colorspace heif_image_get_colorspace(const struct heif_image* img) { return img->image->get_colorspace(); } enum heif_chroma heif_image_get_chroma_format(const struct heif_image* img) { return img->image->get_chroma_format(); } static int uint32_to_int(uint32_t v) { if (v == 0 || v > static_cast(std::numeric_limits::max())) { return -1; } else { return static_cast(v); } } int heif_image_get_width(const struct heif_image* img, enum heif_channel channel) { return uint32_to_int(img->image->get_width(channel)); } int heif_image_get_height(const struct heif_image* img, enum heif_channel channel) { return uint32_to_int(img->image->get_height(channel)); } int heif_image_get_primary_width(const struct heif_image* img) { if (img->image->get_colorspace() == heif_colorspace_RGB) { if (img->image->get_chroma_format() == heif_chroma_444) { return uint32_to_int(img->image->get_width(heif_channel_G)); } else { return uint32_to_int(img->image->get_width(heif_channel_interleaved)); } } else { return uint32_to_int(img->image->get_width(heif_channel_Y)); } } int heif_image_get_primary_height(const struct heif_image* img) { if (img->image->get_colorspace() == heif_colorspace_RGB) { if (img->image->get_chroma_format() == heif_chroma_444) { return uint32_to_int(img->image->get_height(heif_channel_G)); } else { return uint32_to_int(img->image->get_height(heif_channel_interleaved)); } } else { return uint32_to_int(img->image->get_height(heif_channel_Y)); } } heif_error heif_image_crop(struct heif_image* img, int left, int right, int top, int bottom) { uint32_t w = img->image->get_width(); uint32_t h = img->image->get_height(); if (w==0 || w>0x7FFFFFFF || h==0 || h>0x7FFFFFFF) { return heif_error{heif_error_Usage_error, heif_suberror_Invalid_image_size, "Image size exceeds maximum supported size"}; } auto cropResult = img->image->crop(left, static_cast(w) - 1 - right, top, static_cast(h) - 1 - bottom, nullptr); if (cropResult.error) { return cropResult.error.error_struct(img->image.get()); } img->image = cropResult.value; return heif_error_success; } int heif_image_get_bits_per_pixel(const struct heif_image* img, enum heif_channel channel) { return img->image->get_storage_bits_per_pixel(channel); } int heif_image_get_bits_per_pixel_range(const struct heif_image* img, enum heif_channel channel) { return img->image->get_bits_per_pixel(channel); } int heif_image_has_channel(const struct heif_image* img, enum heif_channel channel) { return img->image->has_channel(channel); } struct heif_error heif_image_add_plane(struct heif_image* image, heif_channel channel, int width, int height, int bit_depth) { // Note: no security limit, because this is explicitly requested by the user. if (auto err = image->image->add_plane(channel, width, height, bit_depth, nullptr)) { return err.error_struct(image->image.get()); } else { return heif_error_success; } } struct heif_error heif_image_add_channel(struct heif_image* image, enum heif_channel channel, int width, int height, heif_channel_datatype datatype, int bit_depth) { if (auto err = image->image->add_channel(channel, width, height, datatype, bit_depth, nullptr)) { return err.error_struct(image->image.get()); } else { return heif_error_success; } } const uint8_t* heif_image_get_plane_readonly(const struct heif_image* image, enum heif_channel channel, int* out_stride) { if (!out_stride) { return nullptr; } if (!image || !image->image) { *out_stride = 0; return nullptr; } size_t stride; const auto* p = image->image->get_plane(channel, &stride); // TODO: use C++20 std::cmp_greater() if (stride > static_cast(std::numeric_limits::max())) { return nullptr; } *out_stride = static_cast(stride); return p; } uint8_t* heif_image_get_plane(struct heif_image* image, enum heif_channel channel, int* out_stride) { if (!out_stride) { return nullptr; } if (!image || !image->image) { *out_stride = 0; return nullptr; } size_t stride; uint8_t* p = image->image->get_plane(channel, &stride); // TODO: use C++20 std::cmp_greater() if (stride > static_cast(std::numeric_limits::max())) { return nullptr; } *out_stride = static_cast(stride); return p; } enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* image, enum heif_channel channel) { if (image == nullptr) { return heif_channel_datatype_undefined; } return image->image->get_datatype(channel); } int heif_image_list_channels(struct heif_image* image, enum heif_channel** out_channels) { if (!image || !out_channels) { return 0; } auto channels = image->image->get_channel_set(); *out_channels = new heif_channel[channels.size()]; heif_channel* p = *out_channels; for (heif_channel c : channels) { *p++ = c; } assert(channels.size() < static_cast(std::numeric_limits::max())); return static_cast(channels.size()); } void heif_channel_release_list(enum heif_channel** channels) { delete[] channels; } #define heif_image_get_channel_X(name, type, datatype, bits) \ const type* heif_image_get_channel_ ## name ## _readonly(const struct heif_image* image, \ enum heif_channel channel, \ size_t* out_stride) \ { \ if (!image || !image->image) { \ *out_stride = 0; \ return nullptr; \ } \ \ if (image->image->get_datatype(channel) != datatype) { \ return nullptr; \ } \ if (image->image->get_storage_bits_per_pixel(channel) != bits) { \ return nullptr; \ } \ return image->image->get_channel(channel, out_stride); \ } \ \ type* heif_image_get_channel_ ## name (struct heif_image* image, \ enum heif_channel channel, \ size_t* out_stride) \ { \ if (!image || !image->image) { \ *out_stride = 0; \ return nullptr; \ } \ \ if (image->image->get_datatype(channel) != datatype) { \ return nullptr; \ } \ if (image->image->get_storage_bits_per_pixel(channel) != bits) { \ return nullptr; \ } \ return image->image->get_channel(channel, out_stride); \ } heif_image_get_channel_X(uint16, uint16_t, heif_channel_datatype_unsigned_integer, 16) heif_image_get_channel_X(uint32, uint32_t, heif_channel_datatype_unsigned_integer, 32) heif_image_get_channel_X(uint64, uint64_t, heif_channel_datatype_unsigned_integer, 64) heif_image_get_channel_X(int16, int16_t, heif_channel_datatype_signed_integer, 16) heif_image_get_channel_X(int32, int32_t, heif_channel_datatype_signed_integer, 32) heif_image_get_channel_X(int64, int64_t, heif_channel_datatype_signed_integer, 64) heif_image_get_channel_X(float32, float, heif_channel_datatype_floating_point, 32) heif_image_get_channel_X(float64, double, heif_channel_datatype_floating_point, 64) heif_image_get_channel_X(complex32, heif_complex32, heif_channel_datatype_complex_number, 64) heif_image_get_channel_X(complex64, heif_complex64, heif_channel_datatype_complex_number, 64) void heif_image_set_premultiplied_alpha(struct heif_image* image, int is_premultiplied_alpha) { if (image == nullptr) { return; } image->image->set_premultiplied_alpha(is_premultiplied_alpha); } int heif_image_is_premultiplied_alpha(struct heif_image* image) { if (image == nullptr) { return 0; } return image->image->is_premultiplied_alpha(); } struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height) { Error err = image->image->extend_padding_to_size(min_physical_width, min_physical_height, false, nullptr); if (err) { return err.error_struct(image->image.get()); } else { return heif_error_success; } } struct heif_error heif_image_scale_image(const struct heif_image* input, struct heif_image** output, int width, int height, const struct heif_scaling_options* options) { std::shared_ptr out_img; Error err = input->image->scale_nearest_neighbor(out_img, width, height, nullptr); if (err) { return err.error_struct(input->image.get()); } *output = new heif_image; (*output)->image = std::move(out_img); return Error::Ok.error_struct(input->image.get()); } struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image, uint32_t width, uint32_t height) { Error err = image->image->extend_to_size_with_zero(width, height, nullptr); if (err) { return err.error_struct(image->image.get()); } return heif_error_ok; } struct heif_error heif_image_set_raw_color_profile(struct heif_image* image, const char* color_profile_type_fourcc, const void* profile_data, const size_t profile_size) { if (strlen(color_profile_type_fourcc) != 4) { heif_error err = {heif_error_Usage_error, heif_suberror_Unspecified, "Invalid color_profile_type (must be 4 characters)"}; return err; } uint32_t color_profile_type = fourcc(color_profile_type_fourcc); std::vector data; data.insert(data.end(), (const uint8_t*) profile_data, (const uint8_t*) profile_data + profile_size); auto color_profile = std::make_shared(color_profile_type, data); image->image->set_color_profile_icc(color_profile); return heif_error_success; } struct heif_error heif_image_set_nclx_color_profile(struct heif_image* image, const struct heif_color_profile_nclx* color_profile) { auto nclx = std::make_shared(); nclx->set_colour_primaries(color_profile->color_primaries); nclx->set_transfer_characteristics(color_profile->transfer_characteristics); nclx->set_matrix_coefficients(color_profile->matrix_coefficients); nclx->set_full_range_flag(color_profile->full_range_flag); image->image->set_color_profile_nclx(nclx); return heif_error_success; } /* void heif_image_remove_color_profile(struct heif_image* image) { image->image->set_color_profile(nullptr); } */ int heif_image_handle_get_number_of_metadata_blocks(const struct heif_image_handle* handle, const char* type_filter) { int cnt = 0; for (const auto& metadata : handle->image->get_metadata()) { if (type_filter == nullptr || metadata->item_type == type_filter) { cnt++; } } return cnt; } int heif_image_handle_get_list_of_metadata_block_IDs(const struct heif_image_handle* handle, const char* type_filter, heif_item_id* ids, int count) { int cnt = 0; for (const auto& metadata : handle->image->get_metadata()) { if (type_filter == nullptr || metadata->item_type == type_filter) { if (cnt < count) { ids[cnt] = metadata->item_id; cnt++; } else { break; } } } return cnt; } const char* heif_image_handle_get_metadata_type(const struct heif_image_handle* handle, heif_item_id metadata_id) { for (auto& metadata : handle->image->get_metadata()) { if (metadata->item_id == metadata_id) { return metadata->item_type.c_str(); } } return nullptr; } const char* heif_image_handle_get_metadata_content_type(const struct heif_image_handle* handle, heif_item_id metadata_id) { for (auto& metadata : handle->image->get_metadata()) { if (metadata->item_id == metadata_id) { return metadata->content_type.c_str(); } } return nullptr; } const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle, heif_item_id metadata_id) { for (auto& metadata : handle->image->get_metadata()) { if (metadata->item_id == metadata_id) { return metadata->item_uri_type.c_str(); } } return nullptr; } size_t heif_image_handle_get_metadata_size(const struct heif_image_handle* handle, heif_item_id metadata_id) { for (auto& metadata : handle->image->get_metadata()) { if (metadata->item_id == metadata_id) { return metadata->m_data.size(); } } return 0; } struct heif_error heif_image_handle_get_metadata(const struct heif_image_handle* handle, heif_item_id metadata_id, void* out_data) { for (auto& metadata : handle->image->get_metadata()) { if (metadata->item_id == metadata_id) { if (!metadata->m_data.empty()) { if (out_data == nullptr) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(handle->image.get()); } memcpy(out_data, metadata->m_data.data(), metadata->m_data.size()); } return Error::Ok.error_struct(handle->image.get()); } } Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); return err.error_struct(handle->image.get()); } heif_color_profile_type heif_image_handle_get_color_profile_type(const struct heif_image_handle* handle) { auto profile_icc = handle->image->get_color_profile_icc(); if (profile_icc) { return (heif_color_profile_type) profile_icc->get_type(); } auto profile_nclx = handle->image->get_color_profile_nclx(); if (profile_nclx) { return (heif_color_profile_type) profile_nclx->get_type(); } else { return heif_color_profile_type_not_present; } } size_t heif_image_handle_get_raw_color_profile_size(const struct heif_image_handle* handle) { auto profile_icc = handle->image->get_color_profile_icc(); if (profile_icc) { return profile_icc->get_data().size(); } else { return 0; } } static const std::set::type> known_color_primaries{ heif_color_primaries_ITU_R_BT_709_5, heif_color_primaries_unspecified, heif_color_primaries_ITU_R_BT_470_6_System_M, heif_color_primaries_ITU_R_BT_470_6_System_B_G, heif_color_primaries_ITU_R_BT_601_6, heif_color_primaries_SMPTE_240M, heif_color_primaries_generic_film, heif_color_primaries_ITU_R_BT_2020_2_and_2100_0, heif_color_primaries_SMPTE_ST_428_1, heif_color_primaries_SMPTE_RP_431_2, heif_color_primaries_SMPTE_EG_432_1, heif_color_primaries_EBU_Tech_3213_E, }; struct heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp) { if (static_cast::type>(cp) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr); } auto n = static_cast::type>(cp); if (known_color_primaries.find(n) != known_color_primaries.end()) { nclx->color_primaries = static_cast(n); } else { nclx->color_primaries = heif_color_primaries_unspecified; return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr); } return Error::Ok.error_struct(nullptr); } static const std::set::type> known_transfer_characteristics{ heif_transfer_characteristic_ITU_R_BT_709_5, heif_transfer_characteristic_unspecified, heif_transfer_characteristic_ITU_R_BT_470_6_System_M, heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G, heif_transfer_characteristic_ITU_R_BT_601_6, heif_transfer_characteristic_SMPTE_240M, heif_transfer_characteristic_linear, heif_transfer_characteristic_logarithmic_100, heif_transfer_characteristic_logarithmic_100_sqrt10, heif_transfer_characteristic_IEC_61966_2_4, heif_transfer_characteristic_ITU_R_BT_1361, heif_transfer_characteristic_IEC_61966_2_1, heif_transfer_characteristic_ITU_R_BT_2020_2_10bit, heif_transfer_characteristic_ITU_R_BT_2020_2_12bit, heif_transfer_characteristic_ITU_R_BT_2100_0_PQ, heif_transfer_characteristic_SMPTE_ST_428_1, heif_transfer_characteristic_ITU_R_BT_2100_0_HLG }; struct heif_error heif_nclx_color_profile_set_transfer_characteristics(struct heif_color_profile_nclx* nclx, uint16_t tc) { if (static_cast::type>(tc) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr); } auto n = static_cast::type>(tc); if (known_transfer_characteristics.find(n) != known_transfer_characteristics.end()) { nclx->transfer_characteristics = static_cast(n); } else { nclx->transfer_characteristics = heif_transfer_characteristic_unspecified; return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr); } return Error::Ok.error_struct(nullptr); } static const std::set::type> known_matrix_coefficients{ heif_matrix_coefficients_RGB_GBR, heif_matrix_coefficients_ITU_R_BT_709_5, heif_matrix_coefficients_unspecified, heif_matrix_coefficients_US_FCC_T47, heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G, heif_matrix_coefficients_ITU_R_BT_601_6, heif_matrix_coefficients_SMPTE_240M, heif_matrix_coefficients_YCgCo, heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance, heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance, heif_matrix_coefficients_SMPTE_ST_2085, heif_matrix_coefficients_chromaticity_derived_non_constant_luminance, heif_matrix_coefficients_chromaticity_derived_constant_luminance, heif_matrix_coefficients_ICtCp }; struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t mc) { if (static_cast::type>(mc) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr); } auto n = static_cast::type>(mc); if (known_matrix_coefficients.find(n) != known_matrix_coefficients.end()) { nclx->matrix_coefficients = static_cast(n);; } else { nclx->matrix_coefficients = heif_matrix_coefficients_unspecified; return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr); } return Error::Ok.error_struct(nullptr); } struct heif_error heif_image_handle_get_nclx_color_profile(const struct heif_image_handle* handle, struct heif_color_profile_nclx** out_data) { if (!out_data) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(handle->image.get()); } auto nclx_profile = handle->image->get_color_profile_nclx(); if (!nclx_profile) { Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); return err.error_struct(handle->image.get()); } Error err = nclx_profile->get_nclx_color_profile(out_data); return err.error_struct(handle->image.get()); } struct heif_error heif_image_handle_get_raw_color_profile(const struct heif_image_handle* handle, void* out_data) { if (out_data == nullptr) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(handle->image.get()); } auto raw_profile = handle->image->get_color_profile_icc(); if (raw_profile) { memcpy(out_data, raw_profile->get_data().data(), raw_profile->get_data().size()); } else { Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); return err.error_struct(handle->image.get()); } return Error::Ok.error_struct(handle->image.get()); } enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image) { std::shared_ptr profile; profile = image->image->get_color_profile_icc(); if (!profile) { profile = image->image->get_color_profile_nclx(); } if (!profile) { return heif_color_profile_type_not_present; } else { return (heif_color_profile_type) profile->get_type(); } } size_t heif_image_get_raw_color_profile_size(const struct heif_image* image) { auto raw_profile = image->image->get_color_profile_icc(); if (raw_profile) { return raw_profile->get_data().size(); } else { return 0; } } struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image, void* out_data) { if (out_data == nullptr) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(image->image.get()); } auto raw_profile = image->image->get_color_profile_icc(); if (raw_profile) { memcpy(out_data, raw_profile->get_data().data(), raw_profile->get_data().size()); } else { Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); return err.error_struct(image->image.get()); } return Error::Ok.error_struct(image->image.get()); } struct heif_error heif_image_get_nclx_color_profile(const struct heif_image* image, struct heif_color_profile_nclx** out_data) { if (!out_data) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(image->image.get()); } auto nclx_profile = image->image->get_color_profile_nclx(); if (!nclx_profile) { Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); return err.error_struct(image->image.get()); } Error err = nclx_profile->get_nclx_color_profile(out_data); return err.error_struct(image->image.get()); } struct heif_color_profile_nclx* heif_nclx_color_profile_alloc() { return color_profile_nclx::alloc_nclx_color_profile(); } void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile) { color_profile_nclx::free_nclx_color_profile(nclx_profile); } int heif_image_handle_has_camera_intrinsic_matrix(const struct heif_image_handle* handle) { if (!handle) { return false; } return handle->image->has_intrinsic_matrix(); } struct heif_error heif_image_handle_get_camera_intrinsic_matrix(const struct heif_image_handle* handle, struct heif_camera_intrinsic_matrix* out_matrix) { if (handle == nullptr || out_matrix == nullptr) { return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument}; } if (!handle->image->has_intrinsic_matrix()) { Error err(heif_error_Usage_error, heif_suberror_Camera_intrinsic_matrix_undefined); return err.error_struct(handle->image.get()); } const auto& m = handle->image->get_intrinsic_matrix(); out_matrix->focal_length_x = m.focal_length_x; out_matrix->focal_length_y = m.focal_length_y; out_matrix->principal_point_x = m.principal_point_x; out_matrix->principal_point_y = m.principal_point_y; out_matrix->skew = m.skew; return heif_error_success; } int heif_image_handle_has_camera_extrinsic_matrix(const struct heif_image_handle* handle) { if (!handle) { return false; } return handle->image->has_extrinsic_matrix(); } struct heif_camera_extrinsic_matrix { Box_cmex::ExtrinsicMatrix matrix; }; struct heif_error heif_image_handle_get_camera_extrinsic_matrix(const struct heif_image_handle* handle, struct heif_camera_extrinsic_matrix** out_matrix) { if (handle == nullptr || out_matrix == nullptr) { return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument}; } if (!handle->image->has_extrinsic_matrix()) { Error err(heif_error_Usage_error, heif_suberror_Camera_extrinsic_matrix_undefined); return err.error_struct(handle->image.get()); } *out_matrix = new heif_camera_extrinsic_matrix; (*out_matrix)->matrix = handle->image->get_extrinsic_matrix(); return heif_error_success; } void heif_camera_extrinsic_matrix_release(struct heif_camera_extrinsic_matrix* matrix) { delete matrix; } struct heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_camera_extrinsic_matrix* matrix, double* out_matrix_row_major) { if (matrix == nullptr || out_matrix_row_major == nullptr) { return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument}; } auto m3x3 = matrix->matrix.calculate_rotation_matrix(); for (int i=0;i<9;i++) { out_matrix_row_major[i] = m3x3[i]; } return heif_error_success; } // DEPRECATED struct heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin* decoder_plugin) { return heif_register_decoder_plugin(decoder_plugin); } struct heif_error heif_register_decoder_plugin(const heif_decoder_plugin* decoder_plugin) { if (!decoder_plugin) { return error_null_parameter; } else if (decoder_plugin->plugin_api_version > 3) { return error_unsupported_plugin_version; } register_decoder(decoder_plugin); return heif_error_success; } struct heif_error heif_register_encoder_plugin(const heif_encoder_plugin* encoder_plugin) { if (!encoder_plugin) { return error_null_parameter; } else if (encoder_plugin->plugin_api_version > 3) { return error_unsupported_plugin_version; } register_encoder(encoder_plugin); return heif_error_success; } /* int heif_image_get_number_of_data_chunks(heif_image* img); void heif_image_get_data_chunk(heif_image* img, int chunk_index, uint8_t const*const* dataptr, int const* data_size); void heif_image_free_data_chunk(heif_image* img, int chunk_index); */ /* void heif_context_reset(struct heif_context* ctx) { ctx->context->reset_to_empty_heif(); } */ static struct heif_error heif_file_writer_write(struct heif_context* ctx, const void* data, size_t size, void* userdata) { const char* filename = static_cast(userdata); #if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) std::ofstream ostr(HeifFile::convert_utf8_path_to_utf16(filename).c_str(), std::ios_base::binary); #else std::ofstream ostr(filename, std::ios_base::binary); #endif ostr.write(static_cast(data), size); // TODO: handle write errors return Error::Ok.error_struct(ctx->context.get()); } struct heif_error heif_context_write_to_file(struct heif_context* ctx, const char* filename) { heif_writer writer; writer.writer_api_version = 1; writer.write = heif_file_writer_write; return heif_context_write(ctx, &writer, (void*) filename); } struct heif_error heif_context_write(struct heif_context* ctx, struct heif_writer* writer, void* userdata) { if (!writer) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } else if (writer->writer_api_version != 1) { Error err(heif_error_Usage_error, heif_suberror_Unsupported_writer_version); return err.error_struct(ctx->context.get()); } StreamWriter swriter; ctx->context->write(swriter); const auto& data = swriter.get_data(); heif_error writer_error = writer->write(ctx, data.data(), data.size(), userdata); if (!writer_error.message) { // It is now allowed to return a NULL error message on success. It will be replaced by "Success". An error message is still required when there is an error. if (writer_error.code == heif_error_Ok) { writer_error.message = Error::kSuccess; return writer_error; } else { return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_writer callback returned a null error text"}; } } else { return writer_error; } } void heif_context_add_compatible_brand(struct heif_context* ctx, heif_brand2 compatible_brand) { ctx->context->get_heif_file()->get_ftyp_box()->add_compatible_brand(compatible_brand); } int heif_context_get_encoder_descriptors(struct heif_context* ctx, enum heif_compression_format format, const char* name, const struct heif_encoder_descriptor** out_encoder_descriptors, int count) { return heif_get_encoder_descriptors(format, name, out_encoder_descriptors, count); } int heif_get_encoder_descriptors(enum heif_compression_format format, const char* name, const struct heif_encoder_descriptor** out_encoder_descriptors, int count) { if (out_encoder_descriptors != nullptr && count <= 0) { return 0; } std::vector descriptors; descriptors = get_filtered_encoder_descriptors(format, name); if (out_encoder_descriptors == nullptr) { return static_cast(descriptors.size()); } int i; for (i = 0; i < count && static_cast(i) < descriptors.size(); i++) { out_encoder_descriptors[i] = descriptors[i]; } return i; } const char* heif_encoder_descriptor_get_name(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->get_plugin_name(); } const char* heif_encoder_descriptor_get_id_name(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->id_name; } int heif_get_decoder_descriptors(enum heif_compression_format format_filter, const struct heif_decoder_descriptor** out_decoders, int count) { struct decoder_with_priority { const heif_decoder_plugin* plugin; int priority; }; std::vector plugins; std::vector formats; if (format_filter == heif_compression_undefined) { formats = {heif_compression_HEVC, heif_compression_AV1, heif_compression_JPEG, heif_compression_JPEG2000, heif_compression_HTJ2K, heif_compression_VVC}; } else { formats.emplace_back(format_filter); } for (const auto* plugin : get_decoder_plugins()) { for (auto& format : formats) { int priority = plugin->does_support_format(format); if (priority) { plugins.push_back({plugin, priority}); break; } } } if (out_decoders == nullptr) { return (int) plugins.size(); } std::sort(plugins.begin(), plugins.end(), [](const decoder_with_priority& a, const decoder_with_priority& b) { return a.priority > b.priority; }); int nDecodersReturned = std::min(count, (int) plugins.size()); for (int i = 0; i < nDecodersReturned; i++) { out_decoders[i] = (heif_decoder_descriptor*) (plugins[i].plugin); } return nDecodersReturned; } const char* heif_decoder_descriptor_get_name(const struct heif_decoder_descriptor* descriptor) { auto decoder = (heif_decoder_plugin*) descriptor; return decoder->get_plugin_name(); } const char* heif_decoder_descriptor_get_id_name(const struct heif_decoder_descriptor* descriptor) { auto decoder = (heif_decoder_plugin*) descriptor; if (decoder->plugin_api_version < 3) { return nullptr; } else { return decoder->id_name; } } enum heif_compression_format heif_encoder_descriptor_get_compression_format(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->compression_format; } int heif_encoder_descriptor_supports_lossy_compression(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->supports_lossy_compression; } int heif_encoder_descriptor_supports_lossless_compression(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->supports_lossless_compression; } // DEPRECATED: typo in function name int heif_encoder_descriptor_supportes_lossy_compression(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->supports_lossy_compression; } // DEPRECATED: typo in function name int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor* descriptor) { return descriptor->plugin->supports_lossless_compression; } const char* heif_encoder_get_name(const struct heif_encoder* encoder) { return encoder->plugin->get_plugin_name(); } struct heif_error heif_context_get_encoder(struct heif_context* context, const struct heif_encoder_descriptor* descriptor, struct heif_encoder** encoder) { // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation. if (!descriptor || !encoder) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(context ? context->context.get() : nullptr); } *encoder = new struct heif_encoder(descriptor->plugin); return (*encoder)->alloc(); } int heif_have_decoder_for_format(enum heif_compression_format format) { auto plugin = get_decoder(format, nullptr); return plugin != nullptr; } int heif_have_encoder_for_format(enum heif_compression_format format) { auto plugin = get_encoder(format); return plugin != nullptr; } struct heif_error heif_context_get_encoder_for_format(struct heif_context* context, enum heif_compression_format format, struct heif_encoder** encoder) { // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation. if (!encoder) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); return err.error_struct(context ? context->context.get() : nullptr); } std::vector descriptors; descriptors = get_filtered_encoder_descriptors(format, nullptr); if (descriptors.size() > 0) { *encoder = new struct heif_encoder(descriptors[0]->plugin); return (*encoder)->alloc(); } else { *encoder = nullptr; Error err(heif_error_Unsupported_filetype, // TODO: is this the right error code? heif_suberror_Unspecified); return err.error_struct(context ? context->context.get() : nullptr); } } void heif_encoder_release(struct heif_encoder* encoder) { if (encoder) { delete encoder; } } //struct heif_encoder_param* heif_encoder_get_param(struct heif_encoder* encoder) //{ // return nullptr; //} //void heif_encoder_release_param(struct heif_encoder_param* param) //{ //} // Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is // encoder dependent. struct heif_error heif_encoder_set_lossy_quality(struct heif_encoder* encoder, int quality) { if (!encoder) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(nullptr); } return encoder->plugin->set_parameter_quality(encoder->encoder, quality); } struct heif_error heif_encoder_set_lossless(struct heif_encoder* encoder, int enable) { if (!encoder) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(nullptr); } return encoder->plugin->set_parameter_lossless(encoder->encoder, enable); } struct heif_error heif_encoder_set_logging_level(struct heif_encoder* encoder, int level) { if (!encoder) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(nullptr); } if (encoder->plugin->set_parameter_logging_level) { return encoder->plugin->set_parameter_logging_level(encoder->encoder, level); } return heif_error_success; } const struct heif_encoder_parameter* const* heif_encoder_list_parameters(struct heif_encoder* encoder) { return encoder->plugin->list_parameters(encoder->encoder); } const char* heif_encoder_parameter_get_name(const struct heif_encoder_parameter* param) { return param->name; } enum heif_encoder_parameter_type heif_encoder_parameter_get_type(const struct heif_encoder_parameter* param) { return param->type; } struct heif_error heif_encoder_set_parameter_integer(struct heif_encoder* encoder, const char* parameter_name, int value) { // --- check if parameter is valid for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { int have_minimum = 0, have_maximum = 0, minimum = 0, maximum = 0, num_valid_values = 0; const int* valid_values = nullptr; heif_error err = heif_encoder_parameter_get_valid_integer_values((*params), &have_minimum, &have_maximum, &minimum, &maximum, &num_valid_values, &valid_values); if (err.code) { return err; } if ((have_minimum && value < minimum) || (have_maximum && value > maximum)) { return error_invalid_parameter_value; } if (num_valid_values > 0) { bool found = false; for (int i = 0; i < num_valid_values; i++) { if (valid_values[i] == value) { found = true; break; } } if (!found) { return error_invalid_parameter_value; } } } } // --- parameter is ok, pass it to the encoder plugin return encoder->plugin->set_parameter_integer(encoder->encoder, parameter_name, value); } struct heif_error heif_encoder_get_parameter_integer(struct heif_encoder* encoder, const char* parameter_name, int* value_ptr) { return encoder->plugin->get_parameter_integer(encoder->encoder, parameter_name, value_ptr); } struct heif_error heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_parameter* param, int* have_minimum_maximum, int* minimum, int* maximum) { if (param->type != heif_encoder_parameter_type_integer) { return error_unsupported_parameter; // TODO: correct error ? } if (param->integer.have_minimum_maximum) { if (minimum) { *minimum = param->integer.minimum; } if (maximum) { *maximum = param->integer.maximum; } } if (have_minimum_maximum) { *have_minimum_maximum = param->integer.have_minimum_maximum; } return heif_error_success; } struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter* param, int* have_minimum, int* have_maximum, int* minimum, int* maximum, int* num_valid_values, const int** out_integer_array) { if (param->type != heif_encoder_parameter_type_integer) { return error_unsupported_parameter; // TODO: correct error ? } // --- range of values if (param->integer.have_minimum_maximum) { if (minimum) { *minimum = param->integer.minimum; } if (maximum) { *maximum = param->integer.maximum; } } if (have_minimum) { *have_minimum = param->integer.have_minimum_maximum; } if (have_maximum) { *have_maximum = param->integer.have_minimum_maximum; } // --- set of valid values if (param->integer.num_valid_values > 0) { if (out_integer_array) { *out_integer_array = param->integer.valid_values; } } if (num_valid_values) { *num_valid_values = param->integer.num_valid_values; } return heif_error_success; } struct heif_error heif_encoder_parameter_get_valid_string_values(const struct heif_encoder_parameter* param, const char* const** out_stringarray) { if (param->type != heif_encoder_parameter_type_string) { return error_unsupported_parameter; // TODO: correct error ? } if (out_stringarray) { *out_stringarray = param->string.valid_values; } return heif_error_success; } struct heif_error heif_encoder_parameter_integer_valid_range(struct heif_encoder* encoder, const char* parameter_name, int* have_minimum_maximum, int* minimum, int* maximum) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { return heif_encoder_parameter_get_valid_integer_range(*params, have_minimum_maximum, minimum, maximum); } } return error_unsupported_parameter; } struct heif_error heif_encoder_set_parameter_boolean(struct heif_encoder* encoder, const char* parameter_name, int value) { return encoder->plugin->set_parameter_boolean(encoder->encoder, parameter_name, value); } struct heif_error heif_encoder_get_parameter_boolean(struct heif_encoder* encoder, const char* parameter_name, int* value_ptr) { return encoder->plugin->get_parameter_boolean(encoder->encoder, parameter_name, value_ptr); } struct heif_error heif_encoder_set_parameter_string(struct heif_encoder* encoder, const char* parameter_name, const char* value) { return encoder->plugin->set_parameter_string(encoder->encoder, parameter_name, value); } struct heif_error heif_encoder_get_parameter_string(struct heif_encoder* encoder, const char* parameter_name, char* value_ptr, int value_size) { return encoder->plugin->get_parameter_string(encoder->encoder, parameter_name, value_ptr, value_size); } struct heif_error heif_encoder_parameter_string_valid_values(struct heif_encoder* encoder, const char* parameter_name, const char* const** out_stringarray) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { return heif_encoder_parameter_get_valid_string_values(*params, out_stringarray); } } return error_unsupported_parameter; } struct heif_error heif_encoder_parameter_integer_valid_values(struct heif_encoder* encoder, const char* parameter_name, int* have_minimum, int* have_maximum, int* minimum, int* maximum, int* num_valid_values, const int** out_integer_array) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { return heif_encoder_parameter_get_valid_integer_values(*params, have_minimum, have_maximum, minimum, maximum, num_valid_values, out_integer_array); } } return error_unsupported_parameter; } static bool parse_boolean(const char* value) { if (strcmp(value, "true") == 0) { return true; } else if (strcmp(value, "false") == 0) { return false; } else if (strcmp(value, "1") == 0) { return true; } else if (strcmp(value, "0") == 0) { return false; } return false; } struct heif_error heif_encoder_set_parameter(struct heif_encoder* encoder, const char* parameter_name, const char* value) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { switch ((*params)->type) { case heif_encoder_parameter_type_integer: return heif_encoder_set_parameter_integer(encoder, parameter_name, atoi(value)); case heif_encoder_parameter_type_boolean: return heif_encoder_set_parameter_boolean(encoder, parameter_name, parse_boolean(value)); case heif_encoder_parameter_type_string: return heif_encoder_set_parameter_string(encoder, parameter_name, value); break; } return heif_error_success; } } return heif_encoder_set_parameter_string(encoder, parameter_name, value); //return error_unsupported_parameter; } struct heif_error heif_encoder_get_parameter(struct heif_encoder* encoder, const char* parameter_name, char* value_ptr, int value_size) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { switch ((*params)->type) { case heif_encoder_parameter_type_integer: { int value; struct heif_error error = heif_encoder_get_parameter_integer(encoder, parameter_name, &value); if (error.code) { return error; } else { snprintf(value_ptr, value_size, "%d", value); } } break; case heif_encoder_parameter_type_boolean: { int value; struct heif_error error = heif_encoder_get_parameter_boolean(encoder, parameter_name, &value); if (error.code) { return error; } else { snprintf(value_ptr, value_size, "%d", value); } } break; case heif_encoder_parameter_type_string: { struct heif_error error = heif_encoder_get_parameter_string(encoder, parameter_name, value_ptr, value_size); if (error.code) { return error; } } break; } return heif_error_success; } } return error_unsupported_parameter; } int heif_encoder_has_default(struct heif_encoder* encoder, const char* parameter_name) { for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder); *params; params++) { if (strcmp((*params)->name, parameter_name) == 0) { if ((*params)->version >= 2) { return (*params)->has_default; } else { return true; } } } return false; } void set_default_encoding_options(heif_encoding_options& options) { options.version = 7; options.save_alpha_channel = true; options.macOS_compatibility_workaround = false; options.save_two_colr_boxes_when_ICC_and_nclx_available = false; options.output_nclx_profile = nullptr; options.macOS_compatibility_workaround_no_nclx_profile = false; options.image_orientation = heif_orientation_normal; options.color_conversion_options.version = 1; options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average; options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear; options.color_conversion_options.only_use_preferred_chroma_algorithm = false; options.prefer_uncC_short_form = true; } static void copy_options(heif_encoding_options& options, const heif_encoding_options& input_options) { switch (input_options.version) { case 7: options.prefer_uncC_short_form = input_options.prefer_uncC_short_form; // fallthrough case 6: options.color_conversion_options = input_options.color_conversion_options; // fallthrough case 5: options.image_orientation = input_options.image_orientation; // fallthrough case 4: options.output_nclx_profile = input_options.output_nclx_profile; options.macOS_compatibility_workaround_no_nclx_profile = input_options.macOS_compatibility_workaround_no_nclx_profile; // fallthrough case 3: options.save_two_colr_boxes_when_ICC_and_nclx_available = input_options.save_two_colr_boxes_when_ICC_and_nclx_available; // fallthrough case 2: options.macOS_compatibility_workaround = input_options.macOS_compatibility_workaround; // fallthrough case 1: options.save_alpha_channel = input_options.save_alpha_channel; } } heif_encoding_options* heif_encoding_options_alloc() { auto options = new heif_encoding_options; set_default_encoding_options(*options); return options; } void heif_encoding_options_free(heif_encoding_options* options) { delete options; } struct heif_error heif_context_encode_image(struct heif_context* ctx, const struct heif_image* input_image, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, struct heif_image_handle** out_image_handle) { if (!encoder) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } if (out_image_handle) { *out_image_handle = nullptr; } heif_encoding_options options; heif_color_profile_nclx nclx; set_default_encoding_options(options); if (input_options) { copy_options(options, *input_options); if (options.output_nclx_profile == nullptr) { auto input_nclx = input_image->image->get_color_profile_nclx(); if (input_nclx) { options.output_nclx_profile = &nclx; nclx.version = 1; nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries(); nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics(); nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients(); nclx.full_range_flag = input_nclx->get_full_range_flag(); } } } auto encodingResult = ctx->context->encode_image(input_image->image, encoder, options, heif_image_input_class_normal); if (encodingResult.error != Error::Ok) { return encodingResult.error.error_struct(ctx->context.get()); } std::shared_ptr image = *encodingResult; // mark the new image as primary image if (ctx->context->is_primary_image_set() == false) { ctx->context->set_primary_image(image); } if (out_image_handle) { *out_image_handle = new heif_image_handle; (*out_image_handle)->image = std::move(image); (*out_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_encode_grid(struct heif_context* ctx, struct heif_image** tiles, uint16_t columns, uint16_t rows, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, struct heif_image_handle** out_image_handle) { if (!encoder || !tiles) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } else if (rows == 0 || columns == 0) { return Error(heif_error_Usage_error, heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get()); } // TODO: Don't repeat this code from heif_context_encode_image() heif_encoding_options options; heif_color_profile_nclx nclx; set_default_encoding_options(options); if (input_options) { copy_options(options, *input_options); if (options.output_nclx_profile == nullptr) { auto input_nclx = tiles[0]->image->get_color_profile_nclx(); if (input_nclx) { options.output_nclx_profile = &nclx; nclx.version = 1; nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries(); nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics(); nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients(); nclx.full_range_flag = input_nclx->get_full_range_flag(); } } } // Convert heif_images to a vector of HeifPixelImages std::vector> pixel_tiles; for (int i=0; iimage); } // Encode Grid std::shared_ptr out_grid; auto addGridResult = ImageItem_Grid::add_and_encode_full_grid(ctx->context.get(), pixel_tiles, rows, columns, encoder, options); if (addGridResult.error) { return addGridResult.error.error_struct(ctx->context.get()); } out_grid = addGridResult.value; // Mark as primary image if (ctx->context->is_primary_image_set() == false) { ctx->context->set_primary_image(out_grid); } if (out_image_handle) { *out_image_handle = new heif_image_handle; (*out_image_handle)->image = std::move(out_grid); (*out_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_add_grid_image(struct heif_context* ctx, uint32_t image_width, uint32_t image_height, uint32_t tile_columns, uint32_t tile_rows, const struct heif_encoding_options* encoding_options, struct heif_image_handle** out_grid_image_handle) { if (tile_rows == 0 || tile_columns == 0) { return Error(heif_error_Usage_error, heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get()); } else if (tile_rows > 0xFFFF || tile_columns > 0xFFFF) { return heif_error{heif_error_Usage_error, heif_suberror_Invalid_image_size, "Number of tile rows/columns may not exceed 65535"}; } auto generateGridItemResult = ImageItem_Grid::add_new_grid_item(ctx->context.get(), image_width, image_height, static_cast(tile_rows), static_cast(tile_columns), encoding_options); if (generateGridItemResult.error) { return generateGridItemResult.error.error_struct(ctx->context.get()); } if (out_grid_image_handle) { *out_grid_image_handle = new heif_image_handle; (*out_grid_image_handle)->image = generateGridItemResult.value; (*out_grid_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_add_overlay_image(struct heif_context* ctx, uint32_t image_width, uint32_t image_height, uint16_t nImages, const heif_item_id* image_ids, int32_t* offsets, const uint16_t background_rgba[4], struct heif_image_handle** out_iovl_image_handle) { if (!image_ids) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } else if (nImages == 0) { return Error(heif_error_Usage_error, heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get()); } std::vector refs; refs.insert(refs.end(), image_ids, image_ids + nImages); ImageOverlay overlay; overlay.set_canvas_size(image_width, image_height); if (background_rgba) { overlay.set_background_color(background_rgba); } for (uint16_t i=0;i> addImageResult = ImageItem_Overlay::add_new_overlay_item(ctx->context.get(), overlay); if (addImageResult.error != Error::Ok) { return addImageResult.error.error_struct(ctx->context.get()); } std::shared_ptr iovlimage = addImageResult.value; if (out_iovl_image_handle) { *out_iovl_image_handle = new heif_image_handle; (*out_iovl_image_handle)->image = std::move(iovlimage); (*out_iovl_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_add_tiled_image(struct heif_context* ctx, const struct heif_tiled_image_parameters* parameters, const struct heif_encoding_options* options, // TODO: do we need this? const struct heif_encoder* encoder, struct heif_image_handle** out_grid_image_handle) { if (out_grid_image_handle) { *out_grid_image_handle = nullptr; } Result> gridImageResult; gridImageResult = ImageItem_Tiled::add_new_tiled_item(ctx->context.get(), parameters, encoder); if (gridImageResult.error != Error::Ok) { return gridImageResult.error.error_struct(ctx->context.get()); } if (out_grid_image_handle) { *out_grid_image_handle = new heif_image_handle; (*out_grid_image_handle)->image = gridImageResult.value; (*out_grid_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_add_image_tile(struct heif_context* ctx, struct heif_image_handle* tiled_image, uint32_t tile_x, uint32_t tile_y, const struct heif_image* image, struct heif_encoder* encoder) { if (auto tili_image = std::dynamic_pointer_cast(tiled_image->image)) { Error err = tili_image->add_image_tile(tile_x, tile_y, image->image, encoder); return err.error_struct(ctx->context.get()); } #if WITH_UNCOMPRESSED_CODEC else if (auto unci = std::dynamic_pointer_cast(tiled_image->image)) { Error err = unci->add_image_tile(tile_x, tile_y, image->image); return err.error_struct(ctx->context.get()); } #endif else if (auto grid_item = std::dynamic_pointer_cast(tiled_image->image)) { Error err = grid_item->add_image_tile(tile_x, tile_y, image->image, encoder); return err.error_struct(ctx->context.get()); } else { return { heif_error_Usage_error, heif_suberror_Unspecified, "Cannot add tile to a non-tiled image" }; } } struct heif_error heif_context_add_unci_image(struct heif_context* ctx, const struct heif_unci_image_parameters* parameters, const struct heif_encoding_options* encoding_options, const heif_image* prototype, struct heif_image_handle** out_unci_image_handle) { #if WITH_UNCOMPRESSED_CODEC Result> unciImageResult; unciImageResult = ImageItem_uncompressed::add_unci_item(ctx->context.get(), parameters, encoding_options, prototype->image); if (unciImageResult.error != Error::Ok) { return unciImageResult.error.error_struct(ctx->context.get()); } if (out_unci_image_handle) { *out_unci_image_handle = new heif_image_handle; (*out_unci_image_handle)->image = unciImageResult.value; (*out_unci_image_handle)->context = ctx->context; } return heif_error_success; #else return {heif_error_Unsupported_feature, heif_suberror_Unspecified, "support for uncompressed images (ISO23001-17) has been disabled."}; #endif } struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx, const struct heif_image_handle* master_image, const struct heif_image_handle* thumbnail_image) { Error error = ctx->context->assign_thumbnail(thumbnail_image->image, master_image->image); return error.error_struct(ctx->context.get()); } struct heif_error heif_context_encode_thumbnail(struct heif_context* ctx, const struct heif_image* image, const struct heif_image_handle* image_handle, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, int bbox_size, struct heif_image_handle** out_image_handle) { heif_encoding_options options; set_default_encoding_options(options); if (input_options != nullptr) { copy_options(options, *input_options); } auto encodingResult = ctx->context->encode_thumbnail(image->image, encoder, options, bbox_size); if (encodingResult.error != Error::Ok) { return encodingResult.error.error_struct(ctx->context.get()); } std::shared_ptr thumbnail_image = *encodingResult; if (!thumbnail_image) { Error err(heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Thumbnail images must be smaller than the original image."); return err.error_struct(ctx->context.get()); } Error error = ctx->context->assign_thumbnail(image_handle->image, thumbnail_image); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } if (out_image_handle) { *out_image_handle = new heif_image_handle; (*out_image_handle)->image = thumbnail_image; (*out_image_handle)->context = ctx->context; } return heif_error_success; } struct heif_error heif_context_set_primary_image(struct heif_context* ctx, struct heif_image_handle* image_handle) { ctx->context->set_primary_image(image_handle->image); return heif_error_success; } struct heif_error heif_context_add_exif_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size) { Error error = ctx->context->add_exif_metadata(image_handle->image, data, size); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } else { return heif_error_success; } } struct heif_error heif_context_add_XMP_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size) { return heif_context_add_XMP_metadata2(ctx, image_handle, data, size, heif_metadata_compression_off); } struct heif_error heif_context_add_XMP_metadata2(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size, heif_metadata_compression compression) { Error error = ctx->context->add_XMP_metadata(image_handle->image, data, size, compression); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } else { return heif_error_success; } } struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size, const char* item_type, const char* content_type) { if (item_type == nullptr || strlen(item_type) != 4) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "called heif_context_add_generic_metadata() with invalid 'item_type'."}; } Error error = ctx->context->add_generic_metadata(image_handle->image, data, size, fourcc(item_type), content_type, nullptr, heif_metadata_compression_off, nullptr); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } else { return heif_error_success; } } struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx, const struct heif_image_handle* image_handle, const void* data, int size, const char* item_uri_type, heif_item_id* out_item_id) { Error error = ctx->context->add_generic_metadata(image_handle->image, data, size, fourcc("uri "), nullptr, item_uri_type, heif_metadata_compression_off, out_item_id); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } else { return heif_error_success; } } void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width) { ctx->context->get_security_limits()->max_image_size_pixels = static_cast(maximum_width) * maximum_width; } void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads) { ctx->context->set_max_decoding_threads(max_threads); } libheif-1.19.8/libheif/api/libheif/heif_experimental.h000664 001750 001750 00000043265 15003473471 023750 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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_HEIF_EXPERIMENTAL_H #define LIBHEIF_HEIF_EXPERIMENTAL_H #include "libheif/heif.h" #ifdef __cplusplus extern "C" { #endif #if HEIF_ENABLE_EXPERIMENTAL_FEATURES /* =================================================================================== * This file contains candidate APIs that did not make it into the public API yet. * =================================================================================== */ /* heif_item_property_type_camera_intrinsic_matrix = heif_fourcc('c', 'm', 'i', 'n'), heif_item_property_type_camera_extrinsic_matrix = heif_fourcc('c', 'm', 'e', 'x') */ struct heif_property_camera_intrinsic_matrix; struct heif_property_camera_extrinsic_matrix; //LIBHEIF_API struct heif_error heif_item_get_property_camera_intrinsic_matrix(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_camera_intrinsic_matrix** out_matrix); //LIBHEIF_API void heif_property_camera_intrinsic_matrix_release(struct heif_property_camera_intrinsic_matrix* matrix); //LIBHEIF_API struct heif_error heif_property_camera_intrinsic_matrix_get_focal_length(const struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_focal_length_x, double* out_focal_length_y); //LIBHEIF_API struct heif_error heif_property_camera_intrinsic_matrix_get_principal_point(const struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_principal_point_x, double* out_principal_point_y); //LIBHEIF_API struct heif_error heif_property_camera_intrinsic_matrix_get_skew(const struct heif_property_camera_intrinsic_matrix* matrix, double* out_skew); //LIBHEIF_API struct heif_property_camera_intrinsic_matrix* heif_property_camera_intrinsic_matrix_alloc(); //LIBHEIF_API void heif_property_camera_intrinsic_matrix_set_simple(struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double focal_length, double principal_point_x, double principal_point_y); //LIBHEIF_API void heif_property_camera_intrinsic_matrix_set_full(struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double focal_length_x, double focal_length_y, double principal_point_x, double principal_point_y, double skew); //LIBHEIF_API struct heif_error heif_item_add_property_camera_intrinsic_matrix(const struct heif_context* context, heif_item_id itemId, const struct heif_property_camera_intrinsic_matrix* matrix, heif_property_id* out_propertyId); //LIBHEIF_API struct heif_error heif_item_get_property_camera_extrinsic_matrix(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_camera_extrinsic_matrix** out_matrix); //LIBHEIF_API void heif_property_camera_extrinsic_matrix_release(struct heif_property_camera_extrinsic_matrix* matrix); // `out_matrix` must point to a 9-element matrix, which will be filled in row-major order. //LIBHEIF_API struct heif_error heif_property_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_property_camera_extrinsic_matrix* matrix, double* out_matrix); // `out_vector` must point to a 3-element vector, which will be filled with the (X,Y,Z) coordinates (in micrometers). //LIBHEIF_API struct heif_error heif_property_camera_extrinsic_matrix_get_position_vector(const struct heif_property_camera_extrinsic_matrix* matrix, int32_t* out_vector); //LIBHEIF_API struct heif_error heif_property_camera_extrinsic_matrix_get_world_coordinate_system_id(const struct heif_property_camera_extrinsic_matrix* matrix, uint32_t* out_wcs_id); #endif // --- Tiled images struct heif_tiled_image_parameters { int version; // --- version 1 uint32_t image_width; uint32_t image_height; uint32_t tile_width; uint32_t tile_height; uint32_t compression_format_fourcc; // will be set automatically when calling heif_context_add_tiled_image() uint8_t offset_field_length; // one of: 32, 40, 48, 64 uint8_t size_field_length; // one of: 0, 24, 32, 64 uint8_t number_of_extra_dimensions; // 0 for normal images, 1 for volumetric (3D), ... uint32_t extra_dimensions[8]; // size of extra dimensions (first 8 dimensions) // boolean flags uint8_t tiles_are_sequential; // TODO: can we derive this automatically }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_context_add_tiled_image(struct heif_context* ctx, const struct heif_tiled_image_parameters* parameters, const struct heif_encoding_options* options, // TODO: do we need this? const struct heif_encoder* encoder, struct heif_image_handle** out_tiled_image_handle); #endif // --- 'unci' images // This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just // contain the allowed values. enum heif_unci_compression { heif_unci_compression_off = 0, //heif_unci_compression_auto = 1, //heif_unci_compression_unknown = 2, // only used when reading unknown method from input file heif_unci_compression_deflate = 3, heif_unci_compression_zlib = 4, heif_unci_compression_brotli = 5 }; struct heif_unci_image_parameters { int version; // --- version 1 uint32_t image_width; uint32_t image_height; uint32_t tile_width; uint32_t tile_height; enum heif_unci_compression compression; // TODO // TODO: interleave type, padding }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_context_add_unci_image(struct heif_context* ctx, const struct heif_unci_image_parameters* parameters, const struct heif_encoding_options* encoding_options, const struct heif_image* prototype, struct heif_image_handle** out_unci_image_handle); #endif // --- 'pymd' entity group (pyramid layers) struct heif_pyramid_layer_info { heif_item_id layer_image_id; uint16_t layer_binning; uint32_t tile_rows_in_layer; uint32_t tile_columns_in_layer; }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES // The input images are automatically sorted according to resolution. You can provide them in any order. LIBHEIF_API struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx, const heif_item_id* layer_item_ids, size_t num_layers, heif_item_id* out_group_id); LIBHEIF_API struct heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(struct heif_context*, heif_entity_group_id id, int* out_num_layers); LIBHEIF_API void heif_pyramid_layer_info_release(struct heif_pyramid_layer_info*); #endif // --- other pixel datatype support enum heif_channel_datatype { heif_channel_datatype_undefined = 0, heif_channel_datatype_unsigned_integer = 1, heif_channel_datatype_signed_integer = 2, heif_channel_datatype_floating_point = 3, heif_channel_datatype_complex_number = 4 }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_image_add_channel(struct heif_image* image, enum heif_channel channel, int width, int height, enum heif_channel_datatype datatype, int bit_depth); LIBHEIF_API int heif_image_list_channels(struct heif_image*, enum heif_channel** out_channels); LIBHEIF_API void heif_channel_release_list(enum heif_channel** channels); #endif struct heif_complex32 { float real, imaginary; }; struct heif_complex64 { double real, imaginary; }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* img, enum heif_channel channel); // The 'stride' in all of these functions are in units of the underlying datatype. LIBHEIF_API const uint16_t* heif_image_get_channel_uint16_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const uint32_t* heif_image_get_channel_uint32_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const uint64_t* heif_image_get_channel_uint64_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int16_t* heif_image_get_channel_int16_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int32_t* heif_image_get_channel_int32_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int64_t* heif_image_get_channel_int64_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const float* heif_image_get_channel_float32_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const double* heif_image_get_channel_float64_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const struct heif_complex32* heif_image_get_channel_complex32_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const struct heif_complex64* heif_image_get_channel_complex64_readonly(const struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint16_t* heif_image_get_channel_uint16(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint32_t* heif_image_get_channel_uint32(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint64_t* heif_image_get_channel_uint64(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int16_t* heif_image_get_channel_int16(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int32_t* heif_image_get_channel_int32(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int64_t* heif_image_get_channel_int64(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API float* heif_image_get_channel_float32(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API double* heif_image_get_channel_float64(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API struct heif_complex32* heif_image_get_channel_complex32(struct heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API struct heif_complex64* heif_image_get_channel_complex64(struct heif_image*, enum heif_channel channel, size_t* out_stride); // ========================= Timestamps ========================= LIBHEIF_API extern const uint64_t heif_tai_clock_info_unknown_time_uncertainty; LIBHEIF_API extern const int32_t heif_tai_clock_info_unknown_drift_rate; LIBHEIF_API extern const uint64_t heif_unknown_tai_timestamp; #endif struct heif_tai_clock_info { uint8_t version; // version 1 uint64_t time_uncertainty; uint32_t clock_resolution; int32_t clock_drift_rate; uint8_t clock_type; }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES int heif_is_tai_clock_info_drift_rate_undefined(int32_t drift_rate); // Creates a new clock info property if it doesn't already exist. LIBHEIF_API struct heif_error heif_property_set_clock_info(struct heif_context* ctx, heif_item_id itemId, const struct heif_tai_clock_info* clock, heif_property_id* out_propertyId); // The `out_clock` struct passed in needs to have the `version` field set so that this // function knows which fields it is safe to fill. // When the read property is a lower version, the version variable of out_clock will be reduced. LIBHEIF_API struct heif_error heif_property_get_clock_info(const struct heif_context* ctx, heif_item_id itemId, struct heif_tai_clock_info* out_clock); #endif struct heif_tai_timestamp_packet { uint8_t version; // version 1 uint64_t tai_timestamp; uint8_t synchronization_state; // bool uint8_t timestamp_generation_failure; // bool uint8_t timestamp_is_modified; // bool }; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES // Creates a new TAI timestamp property if one doesn't already exist for itemId. // Creates a new clock info property if one doesn't already exist for itemId. LIBHEIF_API struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx, heif_item_id itemId, struct heif_tai_timestamp_packet* timestamp, heif_property_id* out_propertyId); LIBHEIF_API struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx, heif_item_id itemId, struct heif_tai_timestamp_packet* out_timestamp); LIBHEIF_API heif_error heif_image_extract_area(const heif_image*, uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, const heif_security_limits* limits, struct heif_image** out_image); #endif #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_items.cc000664 001750 001750 00000023375 15003473471 022532 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "heif_items.h" #include "context.h" #include "api_structs.h" #include "file.h" #include #include #include #include // ------------------------- reading ------------------------- int heif_context_get_number_of_items(const struct heif_context* ctx) { return (int) ctx->context->get_heif_file()->get_number_of_items(); } int heif_context_get_list_of_item_IDs(const struct heif_context* ctx, heif_item_id* ID_array, int count) { if (!ID_array) { return 0; } auto ids = ctx->context->get_heif_file()->get_item_IDs(); for (int i = 0; i < (int) ids.size(); i++) { if (i == count) { return count; } ID_array[i] = ids[i]; } return (int) ids.size(); } uint32_t heif_item_get_item_type(const struct heif_context* ctx, heif_item_id item_id) { return ctx->context->get_heif_file()->get_item_type_4cc(item_id); } int heif_item_is_item_hidden(const struct heif_context* ctx, heif_item_id item_id) { auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (infe == nullptr) { return true; } else { return infe->is_hidden_item(); } } const char* heif_item_get_mime_item_content_type(const struct heif_context* ctx, heif_item_id item_id) { auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } if (infe->get_item_type_4cc() != fourcc("mime")) { return nullptr; } return infe->get_content_type().c_str(); } const char* heif_item_get_mime_item_content_encoding(const struct heif_context* ctx, heif_item_id item_id) { auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } if (infe->get_item_type_4cc() != fourcc("mime")) { return nullptr; } return infe->get_content_encoding().c_str(); } const char* heif_item_get_uri_item_uri_type(const struct heif_context* ctx, heif_item_id item_id) { auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } if (infe->get_item_type_4cc() != fourcc("uri ")) { return nullptr; } return infe->get_item_uri_type().c_str(); } const char* heif_item_get_item_name(const struct heif_context* ctx, heif_item_id item_id) { auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } return infe->get_item_name().c_str(); } struct heif_error heif_item_get_item_data(const struct heif_context* ctx, heif_item_id item_id, heif_metadata_compression* out_compression_format, uint8_t** out_data, size_t* out_data_size) { if (out_data && !out_data_size) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "cannot return data with out_data_size==NULL"}; } std::vector data; Error err = ctx->context->get_heif_file()->get_item_data(item_id, &data, out_compression_format); if (err) { *out_data_size = 0; if (out_data) { *out_data = 0; } return err.error_struct(ctx->context.get()); } if (out_data_size) { *out_data_size = data.size(); } if (out_data) { *out_data = new uint8_t[data.size()]; memcpy(*out_data, data.data(), data.size()); } return heif_error_success; } void heif_release_item_data(const struct heif_context* ctx, uint8_t** item_data) { (void) ctx; if (item_data) { delete[] *item_data; *item_data = nullptr; } } size_t heif_context_get_item_references(const struct heif_context* ctx, heif_item_id from_item_id, int index, uint32_t* out_reference_type_4cc, heif_item_id** out_references_to) { if (index < 0) { return 0; } auto iref = ctx->context->get_heif_file()->get_iref_box(); if (!iref) { return 0; } auto refs = iref->get_references_from(from_item_id); if (index >= (int) refs.size()) { return 0; } const auto& ref = refs[index]; if (out_reference_type_4cc) { *out_reference_type_4cc = ref.header.get_short_type(); } if (out_references_to) { *out_references_to = new heif_item_id[ref.to_item_ID.size()]; for (size_t i = 0; i < ref.to_item_ID.size(); i++) { (*out_references_to)[i] = ref.to_item_ID[i]; } } return ref.to_item_ID.size(); } void heif_release_item_references(const struct heif_context* ctx, heif_item_id** references) { (void) ctx; if (references) { delete[] *references; *references = nullptr; } } // ------------------------- writing ------------------------- struct heif_error heif_context_add_item(struct heif_context* ctx, const char* item_type, const void* data, int size, heif_item_id* out_item_id) { if (item_type == nullptr || strlen(item_type) != 4) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "called heif_context_add_item() with invalid 'item_type'."}; } Result result = ctx->context->get_heif_file()->add_infe(fourcc(item_type), (const uint8_t*) data, size); if (result && out_item_id) { *out_item_id = result.value; return heif_error_success; } else { return result.error.error_struct(ctx->context.get()); } } struct heif_error heif_context_add_mime_item(struct heif_context* ctx, const char* content_type, heif_metadata_compression content_encoding, const void* data, int size, heif_item_id* out_item_id) { Result result = ctx->context->get_heif_file()->add_infe_mime(content_type, content_encoding, (const uint8_t*) data, size); if (result && out_item_id) { *out_item_id = result.value; return heif_error_success; } else { return result.error.error_struct(ctx->context.get()); } } struct heif_error heif_context_add_precompressed_mime_item(struct heif_context* ctx, const char* content_type, const char* content_encoding, const void* data, int size, heif_item_id* out_item_id) { Result result = ctx->context->get_heif_file()->add_precompressed_infe_mime(content_type, content_encoding, (const uint8_t*) data, size); if (result && out_item_id) { *out_item_id = result.value; return heif_error_success; } else { return result.error.error_struct(ctx->context.get()); } } struct heif_error heif_context_add_uri_item(struct heif_context* ctx, const char* item_uri_type, const void* data, int size, heif_item_id* out_item_id) { Result result = ctx->context->get_heif_file()->add_infe_uri(item_uri_type, (const uint8_t*) data, size); if (result && out_item_id) { *out_item_id = result.value; return heif_error_success; } else { return result.error.error_struct(ctx->context.get()); } } struct heif_error heif_context_add_item_reference(struct heif_context* ctx, uint32_t reference_type, heif_item_id from_item, heif_item_id to_item) { ctx->context->get_heif_file()->add_iref_reference(from_item, reference_type, {to_item}); return heif_error_success; } struct heif_error heif_context_add_item_references(struct heif_context* ctx, uint32_t reference_type, heif_item_id from_item, const heif_item_id* to_item, int num_to_items) { std::vector to_refs(to_item, to_item + num_to_items); ctx->context->get_heif_file()->add_iref_reference(from_item, reference_type, to_refs); return heif_error_success; } struct heif_error heif_item_set_item_name(struct heif_context* ctx, heif_item_id item, const char* item_name) { auto infe = ctx->context->get_heif_file()->get_infe_box(item); if (!infe) { return heif_error{heif_error_Input_does_not_exist, heif_suberror_Nonexisting_item_referenced, "Item does not exist"}; } infe->set_item_name(item_name); return heif_error_success; } libheif-1.19.8/libheif/api/libheif/heif_items.h000664 001750 001750 00000021753 15003473471 022372 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_HEIF_ITEMS_H #define LIBHEIF_HEIF_ITEMS_H #include "libheif/heif.h" #ifdef __cplusplus extern "C" { #endif /** * Gets the number of items. * * This is not the same as the number of images, since there can be other types of items, * such as metadata. * * @param ctx the file context * @return the number of items */ LIBHEIF_API int heif_context_get_number_of_items(const struct heif_context* ctx); /** * Get the item identifiers. * * Fills in the item IDs into the user-supplied array {@code ID_array}, preallocated with {@code count} entries. * * @param ctx the file context * @param ID_array the output array. * @param count the number of items allocated within {@code ID_array}. * @return the total number of IDs filled into the array, which may be less than {@code count}. */ LIBHEIF_API int heif_context_get_list_of_item_IDs(const struct heif_context* ctx, heif_item_id* ID_array, int count); /** * Gets the item type. * * Usually, this is a four character code (e.g. `mime` or `uri `), but it can theoretically be * any 4-byte number. Thus, the type is returned as an integer. You can use {@link heif_fourcc} to map * between the two representations. * * @param ctx the file context * @param item_id the item identifier for the item * @return the item type */ LIBHEIF_API uint32_t heif_item_get_item_type(const struct heif_context* ctx, heif_item_id item_id); #define heif_item_type_mime heif_fourcc('m','i','m','e') #define heif_item_type_uri heif_fourcc('u','r','i',' ') LIBHEIF_API int heif_item_is_item_hidden(const struct heif_context* ctx, heif_item_id item_id); /** * Gets the MIME content_type for an item. * * Only valid if the item type is `mime`. * If the item does not exist, or if it is not a `mime` item, NULL is returned. * * @param ctx the file context * @param item_id the item identifier for the item * @return the item content_type */ LIBHEIF_API const char* heif_item_get_mime_item_content_type(const struct heif_context* ctx, heif_item_id item_id); /** * Gets the content_encoding for a MIME item. * * Only valid if the item type is `mime`. * If the item does not exist, or if it is not a `mime` item, NULL is returned. * * If the item is not encoded, the returned value will be an empty string (not null). * * @param ctx the file context * @param item_id the item identifier for the item * @return the item content_type */ LIBHEIF_API const char* heif_item_get_mime_item_content_encoding(const struct heif_context* ctx, heif_item_id item_id); /** * Gets the item_uri_type for an item. * * Only valid if the item type is `uri `. * If the item does not exist, or if it is not a `uri ` item, NULL is returned. * * @param ctx the file context * @param item_id the item identifier for the item * @return the item item_uri_type */ LIBHEIF_API const char* heif_item_get_uri_item_uri_type(const struct heif_context* ctx, heif_item_id item_id); LIBHEIF_API const char* heif_item_get_item_name(const struct heif_context* ctx, heif_item_id item_id); LIBHEIF_API struct heif_error heif_item_set_item_name(struct heif_context* ctx, heif_item_id item, const char* item_name); /** * Gets the raw metadata, as stored in the HEIF file. * * Data in a "mime" item with "content_encoding" can be compressed. * When `out_compression_format` is NULL, the decompressed data will be returned. * Otherwise, the compressed data is returned and `out_compression_format` will be filled with the * compression format. * If the compression method is not supported, an error will be returned. * * It is valid to set `out_data` to NULL. In that case, only the `out_data_size` is filled. * Note that it is inefficient to use `out_data=NULL` just to get the size of compressed data. * In general, this should be avoided. * * If there is no data assigned to the item or there is an error, `out_data_size` is set to zero. * * @param ctx the file context * @param item_id the item identifier for the item * @param out_compression_format how the data is compressed. If the pointer is NULL, the decompressed data will be returned. * @param out_data the corresponding raw metadata * @param out_data_size the size of the metadata in bytes * @return whether the call succeeded, or there was an error */ LIBHEIF_API struct heif_error heif_item_get_item_data(const struct heif_context* ctx, heif_item_id item_id, enum heif_metadata_compression* out_compression_format, uint8_t** out_data, size_t* out_data_size); /** * Free the item data. * * This is used to free memory associated with the data returned by * {@link heif_item_get_item_data} in 'out_data' and set the pointer to NULL. * * @param ctx the file context * @param item_data the data to free */ LIBHEIF_API void heif_release_item_data(const struct heif_context* ctx, uint8_t** item_data); // ------------------------- item references ------------------------- /** * Get the item ids that reference the given item. * * @param ctx the file context. * @param from_item_id the item identifier for the item. * @param index the index of the reference to get. * @param out_reference_type_4cc The 4cc of the reference. (e.g dimg, thmb, cdsc, or auxl) * @param out_references_to the item references. Use {@link heif_release_item_references} to free the memory. * @return the number of items that reference the given item. Returns 0 if the index exceeds the number of references. */ LIBHEIF_API size_t heif_context_get_item_references(const struct heif_context* ctx, heif_item_id from_item_id, int index, uint32_t* out_reference_type_4cc, heif_item_id** out_references_to); LIBHEIF_API void heif_release_item_references(const struct heif_context* ctx, heif_item_id** references); LIBHEIF_API struct heif_error heif_context_add_item_reference(struct heif_context* ctx, uint32_t reference_type, heif_item_id from_item, heif_item_id to_item); LIBHEIF_API struct heif_error heif_context_add_item_references(struct heif_context* ctx, uint32_t reference_type, heif_item_id from_item, const heif_item_id* to_item, int num_to_items); // ------------------------- adding new items ------------------------- LIBHEIF_API struct heif_error heif_context_add_item(struct heif_context* ctx, const char* item_type, const void* data, int size, heif_item_id* out_item_id); LIBHEIF_API struct heif_error heif_context_add_mime_item(struct heif_context* ctx, const char* content_type, enum heif_metadata_compression content_encoding, const void* data, int size, heif_item_id* out_item_id); LIBHEIF_API struct heif_error heif_context_add_precompressed_mime_item(struct heif_context* ctx, const char* content_type, const char* content_encoding, const void* data, int size, heif_item_id* out_item_id); LIBHEIF_API struct heif_error heif_context_add_uri_item(struct heif_context* ctx, const char* item_uri_type, const void* data, int size, heif_item_id* out_item_id); #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_plugin.h000664 001750 001750 00000024070 15003473471 022542 0ustar00farindkfarindk000000 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_HEIF_PLUGIN_H #define LIBHEIF_HEIF_PLUGIN_H #ifdef __cplusplus extern "C" { #endif #include // ==================================================================================================== // This file is for codec plugin developers only. // ==================================================================================================== // API versions table // // release decoder encoder enc.params // ----------------------------------------- // 1.0 1 N/A N/A // 1.1 1 1 1 // 1.4 1 1 2 // 1.8 1 2 2 // 1.13 2 3 2 // 1.15 3 3 2 // ==================================================================================================== // Decoder plugin API // In order to decode images in other formats than HEVC, additional compression codecs can be // added as plugins. A plugin has to implement the functions specified in heif_decoder_plugin // and the plugin has to be registered to the libheif library using heif_register_decoder(). struct heif_decoder_plugin { // API version supported by this plugin (see table above for supported versions) int plugin_api_version; // --- version 1 functions --- // Human-readable name of the plugin const char* (* get_plugin_name)(); // Global plugin initialization (may be NULL) void (* init_plugin)(); // Global plugin deinitialization (may be NULL) void (* deinit_plugin)(); // Query whether the plugin supports decoding of the given format // Result is a priority value. The plugin with the largest value wins. // Default priority is 100. Returning 0 indicates that the plugin cannot decode this format. int (* does_support_format)(enum heif_compression_format format); // Create a new decoder context for decoding an image struct heif_error (* new_decoder)(void** decoder); // Free the decoder context (heif_image can still be used after destruction) void (* free_decoder)(void* decoder); // Push more data into the decoder. This can be called multiple times. // This may not be called after any decode_*() function has been called. struct heif_error (* push_data)(void* decoder, const void* data, size_t size); // --- After pushing the data into the decoder, the decode functions may be called only once. struct heif_error (* decode_image)(void* decoder, struct heif_image** out_img); // --- version 2 functions will follow below ... --- void (*set_strict_decoding)(void* decoder, int flag); // If not NULL, this can provide a specialized function to convert YCbCr to sRGB, because // only the codec itself knows how to interpret the chroma samples and their locations. /* struct heif_error (*convert_YCbCr_to_sRGB)(void* decoder, struct heif_image* in_YCbCr_img, struct heif_image** out_sRGB_img); */ // Reset decoder, such that we can feed in new data for another image. // void (*reset_image)(void* decoder); // --- version 3 functions will follow below ... --- const char* id_name; // --- version 4 functions will follow below ... --- }; enum heif_encoded_data_type { heif_encoded_data_type_HEVC_header = 1, heif_encoded_data_type_HEVC_image = 2, heif_encoded_data_type_HEVC_depth_SEI = 3 }; // Specifies the class of the input image content. // The encoder may want to encode different classes with different parameters // (e.g. always encode alpha lossless) enum heif_image_input_class { heif_image_input_class_normal = 1, heif_image_input_class_alpha = 2, heif_image_input_class_depth = 3, heif_image_input_class_thumbnail = 4 }; struct heif_encoder_plugin { // API version supported by this plugin (see table above for supported versions) int plugin_api_version; // --- version 1 functions --- // The compression format generated by this plugin. enum heif_compression_format compression_format; // Short name of the encoder that can be used as command line parameter when selecting an encoder. // Hence, it should stay stable and not contain any version numbers that will change. const char* id_name; // Default priority is 100. int priority; // Feature support int supports_lossy_compression; int supports_lossless_compression; // Human-readable name of the plugin const char* (* get_plugin_name)(); // Global plugin initialization (may be NULL) void (* init_plugin)(); // Global plugin cleanup (may be NULL). // Free data that was allocated in init_plugin() void (* cleanup_plugin)(); // Create a new decoder context for decoding an image struct heif_error (* new_encoder)(void** encoder); // Free the decoder context (heif_image can still be used after destruction) void (* free_encoder)(void* encoder); struct heif_error (* set_parameter_quality)(void* encoder, int quality); struct heif_error (* get_parameter_quality)(void* encoder, int* quality); struct heif_error (* set_parameter_lossless)(void* encoder, int lossless); struct heif_error (* get_parameter_lossless)(void* encoder, int* lossless); struct heif_error (* set_parameter_logging_level)(void* encoder, int logging); struct heif_error (* get_parameter_logging_level)(void* encoder, int* logging); const struct heif_encoder_parameter** (* list_parameters)(void* encoder); struct heif_error (* set_parameter_integer)(void* encoder, const char* name, int value); struct heif_error (* get_parameter_integer)(void* encoder, const char* name, int* value); struct heif_error (* set_parameter_boolean)(void* encoder, const char* name, int value); struct heif_error (* get_parameter_boolean)(void* encoder, const char* name, int* value); struct heif_error (* set_parameter_string)(void* encoder, const char* name, const char* value); struct heif_error (* get_parameter_string)(void* encoder, const char* name, char* value, int value_size); // Replace the input colorspace/chroma with the one that is supported by the encoder and that // comes as close to the input colorspace/chroma as possible. void (* query_input_colorspace)(enum heif_colorspace* inout_colorspace, enum heif_chroma* inout_chroma); // Encode an image. // After pushing an image into the encoder, you should call get_compressed_data() to // get compressed data until it returns a NULL data pointer. struct heif_error (* encode_image)(void* encoder, const struct heif_image* image, enum heif_image_input_class image_class); // Get a packet of decoded data. The data format depends on the codec. // For HEVC, each packet shall contain exactly one NAL, starting with the NAL header without startcode. struct heif_error (* get_compressed_data)(void* encoder, uint8_t** data, int* size, enum heif_encoded_data_type* type); // --- version 2 --- void (* query_input_colorspace2)(void* encoder, enum heif_colorspace* inout_colorspace, enum heif_chroma* inout_chroma); // --- version 3 --- // The encoded image size may be different from the input frame size, e.g. because // of required rounding, or a required minimum size. Use this function to return // the encoded size for a given input image size. // You may set this to NULL if no padding is required for any image size. void (* query_encoded_size)(void* encoder, uint32_t input_width, uint32_t input_height, uint32_t* encoded_width, uint32_t* encoded_height); // --- version 4 functions will follow below ... --- }; // Names for standard parameters. These should only be used by the encoder plugins. #define heif_encoder_parameter_name_quality "quality" #define heif_encoder_parameter_name_lossless "lossless" // For use only by the encoder plugins. // Application programs should use the access functions. // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) struct heif_encoder_parameter { int version; // current version: 2 // --- version 1 fields --- const char* name; enum heif_encoder_parameter_type type; union { struct { int default_value; uint8_t have_minimum_maximum; // bool int minimum; int maximum; int* valid_values; int num_valid_values; } integer; struct { const char* default_value; const char* const* valid_values; } string; // NOLINT struct { int default_value; } boolean; }; // --- version 2 fields int has_default; }; extern struct heif_error heif_error_ok; extern struct heif_error heif_error_unsupported_parameter; extern struct heif_error heif_error_invalid_parameter_value; #define HEIF_WARN_OR_FAIL(strict, image, cmd, cleanupBlock) \ { struct heif_error e = cmd; \ if (e.code != heif_error_Ok) { \ if (strict) { \ cleanupBlock \ return e; \ } \ else { \ heif_image_add_decoding_warning(image, e); \ } \ } \ } #ifdef __cplusplus } #endif #endif libheif-1.19.8/libheif/api/libheif/heif_emscripten.h000664 001750 001750 00000054562 15003473471 023426 0ustar00farindkfarindk000000 000000 #ifndef LIBHEIF_BOX_EMSCRIPTEN_H #define LIBHEIF_BOX_EMSCRIPTEN_H #include #include #include #include #include #include #include #include #include "heif.h" static std::string _heif_get_version() { return heif_get_version(); } static struct heif_error _heif_context_read_from_memory( struct heif_context* context, const std::string& data) { return heif_context_read_from_memory(context, data.data(), data.size(), nullptr); } static heif_filetype_result heif_js_check_filetype(const std::string& data) { return heif_check_filetype((const uint8_t*) data.data(), data.size()); } static emscripten::val heif_js_context_get_image_handle( struct heif_context* context, heif_item_id id) { emscripten::val result = emscripten::val::object(); if (!context) { return result; } struct heif_image_handle* handle; struct heif_error err = heif_context_get_image_handle(context, id, &handle); if (err.code != heif_error_Ok) { return emscripten::val(err); } return emscripten::val(handle); } static emscripten::val heif_js_context_get_primary_image_handle( struct heif_context* context) { emscripten::val result = emscripten::val::object(); if (!context) { return result; } heif_image_handle* handle; struct heif_error err = heif_context_get_primary_image_handle(context, &handle); if (err.code != heif_error_Ok) { return emscripten::val(err); } return emscripten::val(handle); } static emscripten::val heif_js_context_get_list_of_top_level_image_IDs( struct heif_context* context) { emscripten::val result = emscripten::val::array(); if (!context) { return result; } int count = heif_context_get_number_of_top_level_images(context); if (count <= 0) { return result; } heif_item_id* ids = (heif_item_id*) malloc(count * sizeof(heif_item_id)); if (!ids) { struct heif_error err; err.code = heif_error_Memory_allocation_error; err.subcode = heif_suberror_Security_limit_exceeded; return emscripten::val(err); } int received = heif_context_get_list_of_top_level_image_IDs(context, ids, count); if (!received) { free(ids); return result; } for (int i = 0; i < received; i++) { result.set(i, ids[i]); } free(ids); return result; } #if 0 static void strided_copy(void* dest, const void* src, int width, int height, int stride) { if (width == stride) { memcpy(dest, src, width * height); } else { const uint8_t* _src = static_cast(src); uint8_t* _dest = static_cast(dest); for (int y = 0; y < height; y++, _dest += width, _src += stride) { memcpy(_dest, _src, width); } } } static int round_odd(int v) { return (int) ((v / 2.0) + 0.5); } static emscripten::val heif_js_decode_image(struct heif_image_handle* handle, enum heif_colorspace colorspace, enum heif_chroma chroma) { emscripten::val result = emscripten::val::object(); if (!handle) { return result; } struct heif_image* image; struct heif_error err = heif_decode_image(handle, &image, colorspace, chroma, nullptr); if (err.code != heif_error_Ok) { return emscripten::val(err); } result.set("is_primary", heif_image_handle_is_primary_image(handle)); result.set("thumbnails", heif_image_handle_get_number_of_thumbnails(handle)); int width = heif_image_handle_get_width(handle); result.set("width", width); int height = heif_image_handle_get_height(handle); result.set("height", height); std::basic_string data; result.set("chroma", heif_image_get_chroma_format(image)); result.set("colorspace", heif_image_get_colorspace(image)); switch (heif_image_get_colorspace(image)) { case heif_colorspace_YCbCr: { int stride_y; const uint8_t* plane_y = heif_image_get_plane_readonly(image, heif_channel_Y, &stride_y); int stride_u; const uint8_t* plane_u = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride_u); int stride_v; const uint8_t* plane_v = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride_v); data.resize((width * height) + (2 * round_odd(width) * round_odd(height))); unsigned char* dest = const_cast(data.data()); strided_copy(dest, plane_y, width, height, stride_y); strided_copy(dest + (width * height), plane_u, round_odd(width), round_odd(height), stride_u); strided_copy(dest + (width * height) + (round_odd(width) * round_odd(height)), plane_v, round_odd(width), round_odd(height), stride_v); } break; case heif_colorspace_RGB: { if(heif_image_get_chroma_format(image) == heif_chroma_interleaved_RGB) { int stride_rgb; const uint8_t* plane_rgb = heif_image_get_plane_readonly(image, heif_channel_interleaved, &stride_rgb); data.resize(width * height * 3); unsigned char* dest = const_cast(data.data()); strided_copy(dest, plane_rgb, width * 3, height, stride_rgb); } else if (heif_image_get_chroma_format(image) == heif_chroma_interleaved_RGBA) { int stride_rgba; const uint8_t* plane_rgba = heif_image_get_plane_readonly(image, heif_channel_interleaved, &stride_rgba); data.resize(width * height * 4); unsigned char* dest = const_cast(data.data()); strided_copy(dest, plane_rgba, width * 4, height, stride_rgba); } else { assert(false); } } break; case heif_colorspace_monochrome: { assert(heif_image_get_chroma_format(image) == heif_chroma_monochrome); int stride_grey; const uint8_t* plane_grey = heif_image_get_plane_readonly(image, heif_channel_Y, &stride_grey); data.resize(width * height); unsigned char* dest = const_cast(data.data()); strided_copy(dest, plane_grey, width, height, stride_grey); } break; default: // Should never reach here. break; } result.set("data", std::move(data)); if (heif_image_has_channel(image, heif_channel_Alpha)) { std::basic_string alpha; int stride_alpha; const uint8_t* plane_alpha = heif_image_get_plane_readonly(image, heif_channel_Alpha, &stride_alpha); alpha.resize(width * height); unsigned char* dest = const_cast(alpha.data()); strided_copy(dest, plane_alpha, width, height, stride_alpha); result.set("alpha", std::move(alpha)); } heif_image_release(image); return result; } #endif /* * The returned object includes a pointer to an heif_image in the property "image". * This image has to be released after the image data has been read (copied) with heif_image_release(). */ static emscripten::val heif_js_decode_image2(struct heif_image_handle* handle, enum heif_colorspace colorspace, enum heif_chroma chroma) { emscripten::val result = emscripten::val::object(); if (!handle) { return result; } struct heif_image* image; struct heif_error err = heif_decode_image(handle, &image, colorspace, chroma, nullptr); if (err.code != heif_error_Ok) { return emscripten::val(err); } result.set("image", image); int width = heif_image_handle_get_width(handle); result.set("width", width); int height = heif_image_handle_get_height(handle); result.set("height", height); std::vector data; result.set("chroma", heif_image_get_chroma_format(image)); result.set("colorspace", heif_image_get_colorspace(image)); std::vector channels { heif_channel_Y, heif_channel_Cb, heif_channel_Cr, heif_channel_R, heif_channel_G, heif_channel_B, heif_channel_Alpha, heif_channel_interleaved }; emscripten::val val_channels = emscripten::val::array(); for (auto channel : channels) { if (heif_image_has_channel(image, channel)) { emscripten::val val_channel_info = emscripten::val::object(); val_channel_info.set("id", channel); int stride; const uint8_t* plane = heif_image_get_plane_readonly(image, channel, &stride); val_channel_info.set("stride", stride); val_channel_info.set("data", emscripten::val(emscripten::typed_memory_view(stride * height, plane))); val_channel_info.set("width", heif_image_get_width(image, channel)); val_channel_info.set("height", heif_image_get_height(image, channel)); val_channel_info.set("bits_per_pixel", heif_image_get_bits_per_pixel_range(image, channel)); val_channels.call("push", val_channel_info); } } result.set("channels", val_channels); return result; } #define EXPORT_HEIF_FUNCTION(name) \ emscripten::function(#name, &name, emscripten::allow_raw_pointers()) EMSCRIPTEN_BINDINGS(libheif) { emscripten::function("heif_get_version", &_heif_get_version, emscripten::allow_raw_pointers()); EXPORT_HEIF_FUNCTION(heif_get_version_number); EXPORT_HEIF_FUNCTION(heif_context_alloc); EXPORT_HEIF_FUNCTION(heif_context_free); emscripten::function("heif_context_read_from_memory", &_heif_context_read_from_memory, emscripten::allow_raw_pointers()); emscripten::function("heif_js_check_filetype", &heif_js_check_filetype, emscripten::allow_raw_pointers()); EXPORT_HEIF_FUNCTION(heif_context_get_number_of_top_level_images); emscripten::function("heif_js_context_get_list_of_top_level_image_IDs", &heif_js_context_get_list_of_top_level_image_IDs, emscripten::allow_raw_pointers()); emscripten::function("heif_js_context_get_image_handle", &heif_js_context_get_image_handle, emscripten::allow_raw_pointers()); emscripten::function("heif_js_context_get_primary_image_handle", &heif_js_context_get_primary_image_handle, emscripten::allow_raw_pointers()); //emscripten::function("heif_js_decode_image", //&heif_js_decode_image, emscripten::allow_raw_pointers()); emscripten::function("heif_js_decode_image2", &heif_js_decode_image2, emscripten::allow_raw_pointers()); EXPORT_HEIF_FUNCTION(heif_image_handle_release); EXPORT_HEIF_FUNCTION(heif_image_handle_get_width); EXPORT_HEIF_FUNCTION(heif_image_handle_get_height); EXPORT_HEIF_FUNCTION(heif_image_handle_is_primary_image); EXPORT_HEIF_FUNCTION(heif_image_release); emscripten::enum_("heif_error_code") .value("heif_error_Ok", heif_error_Ok) .value("heif_error_Input_does_not_exist", heif_error_Input_does_not_exist) .value("heif_error_Invalid_input", heif_error_Invalid_input) .value("heif_error_Plugin_loading_error", heif_error_Plugin_loading_error) .value("heif_error_Unsupported_filetype", heif_error_Unsupported_filetype) .value("heif_error_Unsupported_feature", heif_error_Unsupported_feature) .value("heif_error_Usage_error", heif_error_Usage_error) .value("heif_error_Memory_allocation_error", heif_error_Memory_allocation_error) .value("heif_error_Decoder_plugin_error", heif_error_Decoder_plugin_error) .value("heif_error_Encoder_plugin_error", heif_error_Encoder_plugin_error) .value("heif_error_Encoding_error", heif_error_Encoding_error) .value("heif_error_Color_profile_does_not_exist", heif_error_Color_profile_does_not_exist) .value("heif_error_Canceled", heif_error_Canceled); emscripten::enum_("heif_suberror_code") .value("heif_suberror_Unspecified", heif_suberror_Unspecified) .value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data) .value("heif_suberror_Compression_initialisation_error", heif_suberror_Compression_initialisation_error) .value("heif_suberror_Decompression_invalid_data", heif_suberror_Decompression_invalid_data) .value("heif_suberror_Encoder_initialization", heif_suberror_Encoder_initialization) .value("heif_suberror_Encoder_encoding", heif_suberror_Encoder_encoding) .value("heif_suberror_Encoder_cleanup", heif_suberror_Encoder_cleanup) .value("heif_suberror_Too_many_regions", heif_suberror_Too_many_regions) .value("heif_suberror_End_of_data", heif_suberror_End_of_data) .value("heif_suberror_Invalid_box_size", heif_suberror_Invalid_box_size) .value("heif_suberror_No_ftyp_box", heif_suberror_No_ftyp_box) .value("heif_suberror_No_idat_box", heif_suberror_No_idat_box) .value("heif_suberror_No_meta_box", heif_suberror_No_meta_box) .value("heif_suberror_No_hdlr_box", heif_suberror_No_hdlr_box) .value("heif_suberror_No_hvcC_box", heif_suberror_No_hvcC_box) .value("heif_suberror_No_vvcC_box", heif_suberror_No_vvcC_box) .value("heif_suberror_No_pitm_box", heif_suberror_No_pitm_box) .value("heif_suberror_No_ipco_box", heif_suberror_No_ipco_box) .value("heif_suberror_No_ipma_box", heif_suberror_No_ipma_box) .value("heif_suberror_No_iloc_box", heif_suberror_No_iloc_box) .value("heif_suberror_No_iinf_box", heif_suberror_No_iinf_box) .value("heif_suberror_No_iprp_box", heif_suberror_No_iprp_box) .value("heif_suberror_No_iref_box", heif_suberror_No_iref_box) .value("heif_suberror_No_pict_handler", heif_suberror_No_pict_handler) .value("heif_suberror_Ipma_box_references_nonexisting_property", heif_suberror_Ipma_box_references_nonexisting_property) .value("heif_suberror_No_properties_assigned_to_item", heif_suberror_No_properties_assigned_to_item) .value("heif_suberror_No_item_data", heif_suberror_No_item_data) .value("heif_suberror_Invalid_grid_data", heif_suberror_Invalid_grid_data) .value("heif_suberror_Missing_grid_images", heif_suberror_Missing_grid_images) .value("heif_suberror_No_av1C_box", heif_suberror_No_av1C_box) .value("heif_suberror_No_avcC_box", heif_suberror_No_avcC_box) .value("heif_suberror_Invalid_mini_box", heif_suberror_Invalid_mini_box) .value("heif_suberror_Invalid_clean_aperture", heif_suberror_Invalid_clean_aperture) .value("heif_suberror_Invalid_overlay_data", heif_suberror_Invalid_overlay_data) .value("heif_suberror_Overlay_image_outside_of_canvas", heif_suberror_Overlay_image_outside_of_canvas) .value("heif_suberror_Plugin_is_not_loaded", heif_suberror_Plugin_is_not_loaded) .value("heif_suberror_Plugin_loading_error", heif_suberror_Plugin_loading_error) .value("heif_suberror_Auxiliary_image_type_unspecified", heif_suberror_Auxiliary_image_type_unspecified) .value("heif_suberror_Cannot_read_plugin_directory", heif_suberror_Cannot_read_plugin_directory) .value("heif_suberror_No_matching_decoder_installed", heif_suberror_No_matching_decoder_installed) .value("heif_suberror_No_or_invalid_primary_item", heif_suberror_No_or_invalid_primary_item) .value("heif_suberror_No_infe_box", heif_suberror_No_infe_box) .value("heif_suberror_Security_limit_exceeded", heif_suberror_Security_limit_exceeded) .value("heif_suberror_Unknown_color_profile_type", heif_suberror_Unknown_color_profile_type) .value("heif_suberror_Wrong_tile_image_chroma_format", heif_suberror_Wrong_tile_image_chroma_format) .value("heif_suberror_Invalid_fractional_number", heif_suberror_Invalid_fractional_number) .value("heif_suberror_Invalid_image_size", heif_suberror_Invalid_image_size) .value("heif_suberror_Nonexisting_item_referenced", heif_suberror_Nonexisting_item_referenced) .value("heif_suberror_Null_pointer_argument", heif_suberror_Null_pointer_argument) .value("heif_suberror_Nonexisting_image_channel_referenced", heif_suberror_Nonexisting_image_channel_referenced) .value("heif_suberror_Unsupported_plugin_version", heif_suberror_Unsupported_plugin_version) .value("heif_suberror_Unsupported_writer_version", heif_suberror_Unsupported_writer_version) .value("heif_suberror_Unsupported_parameter", heif_suberror_Unsupported_parameter) .value("heif_suberror_Invalid_parameter_value", heif_suberror_Invalid_parameter_value) .value("heif_suberror_Invalid_property", heif_suberror_Invalid_property) .value("heif_suberror_Item_reference_cycle", heif_suberror_Item_reference_cycle) .value("heif_suberror_Invalid_pixi_box", heif_suberror_Invalid_pixi_box) .value("heif_suberror_Invalid_region_data", heif_suberror_Invalid_region_data) .value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec) .value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type) .value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version) .value("heif_suberror_Unsupported_generic_compression_method", heif_suberror_Unsupported_generic_compression_method) .value("heif_suberror_Unsupported_essential_property", heif_suberror_Unsupported_essential_property) .value("heif_suberror_Unsupported_color_conversion", heif_suberror_Unsupported_color_conversion) .value("heif_suberror_Unsupported_item_construction_method", heif_suberror_Unsupported_item_construction_method) .value("heif_suberror_Unsupported_header_compression_method", heif_suberror_Unsupported_header_compression_method) .value("heif_suberror_Unsupported_bit_depth", heif_suberror_Unsupported_bit_depth) .value("heif_suberror_Wrong_tile_image_pixel_depth", heif_suberror_Wrong_tile_image_pixel_depth) .value("heif_suberror_Unknown_NCLX_color_primaries", heif_suberror_Unknown_NCLX_color_primaries) .value("heif_suberror_Unknown_NCLX_transfer_characteristics", heif_suberror_Unknown_NCLX_transfer_characteristics) .value("heif_suberror_Unknown_NCLX_matrix_coefficients", heif_suberror_Unknown_NCLX_matrix_coefficients) .value("heif_suberror_No_ispe_property", heif_suberror_No_ispe_property) .value("heif_suberror_Camera_intrinsic_matrix_undefined", heif_suberror_Camera_intrinsic_matrix_undefined) .value("heif_suberror_Camera_extrinsic_matrix_undefined", heif_suberror_Camera_extrinsic_matrix_undefined) .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream) .value("heif_suberror_No_icbr_box", heif_suberror_No_icbr_box); emscripten::enum_("heif_compression_format") .value("heif_compression_undefined", heif_compression_undefined) .value("heif_compression_HEVC", heif_compression_HEVC) .value("heif_compression_AVC", heif_compression_AVC) .value("heif_compression_JPEG", heif_compression_JPEG) .value("heif_compression_AV1", heif_compression_AV1) .value("heif_compression_VVC", heif_compression_VVC) .value("heif_compression_EVC", heif_compression_EVC) .value("heif_compression_JPEG2000", heif_compression_JPEG2000) .value("heif_compression_uncompressed", heif_compression_uncompressed) .value("heif_compression_mask", heif_compression_mask) .value("heif_compression_HTJ2K", heif_compression_HTJ2K); emscripten::enum_("heif_chroma") .value("heif_chroma_undefined", heif_chroma_undefined) .value("heif_chroma_monochrome", heif_chroma_monochrome) .value("heif_chroma_420", heif_chroma_420) .value("heif_chroma_422", heif_chroma_422) .value("heif_chroma_444", heif_chroma_444) .value("heif_chroma_interleaved_RGB", heif_chroma_interleaved_RGB) .value("heif_chroma_interleaved_RGBA", heif_chroma_interleaved_RGBA) .value("heif_chroma_interleaved_RRGGBB_BE", heif_chroma_interleaved_RRGGBB_BE) .value("heif_chroma_interleaved_RRGGBBAA_BE", heif_chroma_interleaved_RRGGBBAA_BE) .value("heif_chroma_interleaved_RRGGBB_LE", heif_chroma_interleaved_RRGGBB_LE) .value("heif_chroma_interleaved_RRGGBBAA_LE", heif_chroma_interleaved_RRGGBBAA_LE) // Aliases .value("heif_chroma_interleaved_24bit", heif_chroma_interleaved_24bit) .value("heif_chroma_interleaved_32bit", heif_chroma_interleaved_32bit); emscripten::enum_("heif_chroma_downsampling_algorithm") .value("heif_chroma_downsampling_average", heif_chroma_downsampling_average) .value("heif_chroma_downsampling_nearest_neighbor", heif_chroma_downsampling_nearest_neighbor) .value("heif_chroma_downsampling_sharp_yuv", heif_chroma_downsampling_sharp_yuv); emscripten::enum_("heif_chroma_upsampling_algorithm") .value("heif_chroma_upsampling_bilinear", heif_chroma_upsampling_bilinear) .value("heif_chroma_upsampling_nearest_neighbor", heif_chroma_upsampling_nearest_neighbor); emscripten::enum_("heif_colorspace") .value("heif_colorspace_undefined", heif_colorspace_undefined) .value("heif_colorspace_YCbCr", heif_colorspace_YCbCr) .value("heif_colorspace_RGB", heif_colorspace_RGB) .value("heif_colorspace_monochrome", heif_colorspace_monochrome) .value("heif_colorspace_nonvisual", heif_colorspace_nonvisual); emscripten::enum_("heif_channel") .value("heif_channel_Y", heif_channel_Y) .value("heif_channel_Cr", heif_channel_Cr) .value("heif_channel_Cb", heif_channel_Cb) .value("heif_channel_R", heif_channel_R) .value("heif_channel_G", heif_channel_G) .value("heif_channel_B", heif_channel_B) .value("heif_channel_Alpha", heif_channel_Alpha) .value("heif_channel_interleaved", heif_channel_interleaved) .value("heif_channel_filter_array", heif_channel_filter_array) .value("heif_channel_depth", heif_channel_depth) .value("heif_channel_disparity", heif_channel_disparity); emscripten::enum_("heif_filetype_result") .value("heif_filetype_no", heif_filetype_no) .value("heif_filetype_yes_supported", heif_filetype_yes_supported) .value("heif_filetype_yes_unsupported", heif_filetype_yes_unsupported) .value("heif_filetype_maybe", heif_filetype_maybe); emscripten::class_("heif_context"); emscripten::class_("heif_image_handle"); emscripten::class_("heif_image"); emscripten::value_object("heif_error") .field("code", &heif_error::code) .field("subcode", &heif_error::subcode) .field("message", emscripten::optional_override([](const struct heif_error& err) { return std::string(err.message); }), emscripten::optional_override([](struct heif_error& err, const std::string& value) { err.message = value.c_str(); })); } #endif // LIBHEIF_BOX_EMSCRIPTEN_H libheif-1.19.8/libheif/api/libheif/heif_version.h.in000664 001750 001750 00000002471 15003473471 023337 0ustar00farindkfarindk000000 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 . */ /* heif_version.h * * This file was automatically generated when libheif was built. * * DO NOT EDIT THIS FILE. */ #ifndef LIBHEIF_HEIF_VERSION_H #define LIBHEIF_HEIF_VERSION_H /* Numeric representation of the version */ #define LIBHEIF_NUMERIC_VERSION ((@PROJECT_VERSION_MAJOR@<<24) | (@PROJECT_VERSION_MINOR@<<16) | (@PROJECT_VERSION_PATCH@<<8) | 0) /* Version string */ #define LIBHEIF_VERSION "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" #define LIBHEIF_PLUGIN_DIRECTORY "@PLUGIN_DIRECTORY@" #endif // LIBHEIF_HEIF_VERSION_H libheif-1.19.8/libheif/api/libheif/heif_cxx.h000664 001750 001750 00000116626 15003473471 022057 0ustar00farindkfarindk000000 000000 /* * C++ interface to libheif * * MIT License * * Copyright (c) 2018 Dirk Farin * * 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. */ #ifndef LIBHEIF_HEIF_CXX_H #define LIBHEIF_HEIF_CXX_H #include #include #include #include extern "C" { #include } namespace heif { class Error { public: Error() { m_code = heif_error_Ok; m_subcode = heif_suberror_Unspecified; m_message = "Ok"; } Error(const heif_error& err) { assert(err.message); m_code = err.code; m_subcode = err.subcode; m_message = err.message; } Error(heif_error_code code, heif_suberror_code subcode, const std::string& msg) { m_code = code; m_subcode = subcode; m_message = msg; } const std::string& get_message() const { return m_message; } heif_error_code get_code() const { return m_code; } heif_suberror_code get_subcode() const { return m_subcode; } operator bool() const { return m_code != heif_error_Ok; } private: heif_error_code m_code; heif_suberror_code m_subcode; std::string m_message; }; class ImageHandle; class Image; class Encoder; class EncoderParameter; class EncoderDescriptor; class Context { public: Context(); class ReadingOptions { }; // throws Error void read_from_file(const std::string& filename, const ReadingOptions& opts = ReadingOptions()); // DEPRECATED. Use read_from_memory_without_copy() instead. // throws Error void read_from_memory(const void* mem, size_t size, const ReadingOptions& opts = ReadingOptions()); // throws Error void read_from_memory_without_copy(const void* mem, size_t size, const ReadingOptions& opts = ReadingOptions()); class Reader { public: virtual ~Reader() = default; virtual int64_t get_position() const = 0; virtual int read(void* data, size_t size) = 0; virtual int seek(int64_t position) = 0; virtual heif_reader_grow_status wait_for_file_size(int64_t target_size) = 0; }; // throws Error void read_from_reader(Reader&, const ReadingOptions& opts = ReadingOptions()); int get_number_of_top_level_images() const noexcept; bool is_top_level_image_ID(heif_item_id id) const noexcept; std::vector get_list_of_top_level_image_IDs() const noexcept; // throws Error heif_item_id get_primary_image_ID() const; // throws Error ImageHandle get_primary_image_handle() const; ImageHandle get_image_handle(heif_item_id id) const; class EncodingOptions : public heif_encoding_options { public: EncodingOptions(); }; // throws Error ImageHandle encode_image(const Image& img, Encoder& encoder, const EncodingOptions& options = EncodingOptions()); // throws Error void set_primary_image(ImageHandle& new_primary_image_handle); // throws Error ImageHandle encode_thumbnail(const Image& image, const ImageHandle& master_image, Encoder& encoder, const EncodingOptions&, int bbox_size); // throws Error void assign_thumbnail(const ImageHandle& thumbnail_image, const ImageHandle& master_image); // throws Error void add_exif_metadata(const ImageHandle& master_image, const void* data, int size); // throws Error void add_XMP_metadata(const ImageHandle& master_image, const void* data, int size); class Writer { public: virtual ~Writer() = default; virtual heif_error write(const void* data, size_t size) = 0; }; // throws Error void write(Writer&); // throws Error void write_to_file(const std::string& filename) const; private: std::shared_ptr m_context; friend struct ::heif_error heif_writer_trampoline_write(struct heif_context* ctx, const void* data, size_t size, void* userdata); //static Context wrap_without_releasing(heif_context*); // internal use in friend function only }; class ImageHandle { public: ImageHandle() = default; ImageHandle(heif_image_handle* handle); bool empty() const noexcept { return !m_image_handle; } bool is_primary_image() const noexcept; int get_width() const noexcept; int get_height() const noexcept; bool has_alpha_channel() const noexcept; bool is_premultiplied_alpha() const noexcept; int get_luma_bits_per_pixel() const noexcept; int get_chroma_bits_per_pixel() const noexcept; int get_ispe_width() const noexcept; int get_ispe_height() const noexcept; // ------------------------- depth images ------------------------- // TODO // ------------------------- thumbnails ------------------------- int get_number_of_thumbnails() const noexcept; std::vector get_list_of_thumbnail_IDs() const noexcept; // throws Error ImageHandle get_thumbnail(heif_item_id id); // ------------------------- metadata (Exif / XMP) ------------------------- // Can optionally be filtered by type ("Exif" / "XMP") std::vector get_list_of_metadata_block_IDs(const char* type_filter = nullptr) const noexcept; std::string get_metadata_type(heif_item_id metadata_id) const noexcept; std::string get_metadata_content_type(heif_item_id metadata_id) const noexcept; // throws error std::vector get_metadata(heif_item_id) const; class DecodingOptions { }; // throws Error Image decode_image(heif_colorspace colorspace, heif_chroma chroma, const DecodingOptions& options = DecodingOptions()); heif_image_handle* get_raw_image_handle() noexcept { return m_image_handle.get(); } const heif_image_handle* get_raw_image_handle() const noexcept { return m_image_handle.get(); } private: std::shared_ptr m_image_handle; }; class ColorProfile_nclx { public: ColorProfile_nclx(); ~ColorProfile_nclx(); heif_color_primaries get_color_primaries() const; heif_transfer_characteristics get_transfer_characteristics() const; heif_matrix_coefficients get_matrix_coefficients() const; bool is_full_range() const; void set_color_primaries(heif_color_primaries cp); // DEPRECATED: typo in function name. Use set_color_primaries() instead. void set_color_primaties(heif_color_primaries cp); void set_transfer_characteristics(heif_transfer_characteristics tc); void set_matrix_coefficients(heif_matrix_coefficients mc); void set_full_range_flag(bool is_full_range); private: ColorProfile_nclx(heif_color_profile_nclx* nclx) { mProfile = nclx; } heif_color_profile_nclx* mProfile; friend class Image; }; class Image { public: Image() = default; Image(heif_image* image); // throws Error void create(int width, int height, enum heif_colorspace colorspace, enum heif_chroma chroma); // throws Error void add_plane(enum heif_channel channel, int width, int height, int bit_depth); heif_colorspace get_colorspace() const noexcept; heif_chroma get_chroma_format() const noexcept; int get_width(enum heif_channel channel) const noexcept; int get_height(enum heif_channel channel) const noexcept; int get_bits_per_pixel(enum heif_channel channel) const noexcept; int get_bits_per_pixel_range(enum heif_channel channel) const noexcept; bool has_channel(enum heif_channel channel) const noexcept; const uint8_t* get_plane(enum heif_channel channel, int* out_stride) const noexcept; uint8_t* get_plane(enum heif_channel channel, int* out_stride) noexcept; // throws Error void set_nclx_color_profile(const ColorProfile_nclx&); // throws Error ColorProfile_nclx get_nclx_color_profile() const; heif_color_profile_type get_color_profile_type() const; // throws Error std::vector get_raw_color_profile() const; void set_raw_color_profile(heif_color_profile_type type, const std::vector& data); bool is_premultiplied_alpha() const noexcept; void set_premultiplied_alpha(bool is_premultiplied_alpha) noexcept; class ScalingOptions { }; // throws Error Image scale_image(int width, int height, const ScalingOptions& options = ScalingOptions()) const; private: std::shared_ptr m_image; friend class Context; }; class EncoderDescriptor { public: static std::vector get_encoder_descriptors(enum heif_compression_format format_filter, const char* name_filter) noexcept; std::string get_name() const noexcept; std::string get_id_name() const noexcept; enum heif_compression_format get_compression_format() const noexcept; // DEPRECATED: typo in function name bool supportes_lossy_compression() const noexcept; // DEPRECATED: typo in function name bool supportes_lossless_compression() const noexcept; // throws Error Encoder get_encoder() const; bool supports_lossy_compression() const noexcept; bool supports_lossless_compression() const noexcept; private: EncoderDescriptor(const struct heif_encoder_descriptor* descr) : m_descriptor(descr) {} const struct heif_encoder_descriptor* m_descriptor = nullptr; }; class EncoderParameter { public: std::string get_name() const noexcept; enum heif_encoder_parameter_type get_type() const noexcept; bool is_integer() const noexcept; // Returns 'true' if the integer range is limited. bool get_valid_integer_range(int& out_minimum, int& out_maximum); bool is_boolean() const noexcept; bool is_string() const noexcept; std::vector get_valid_string_values() const; private: EncoderParameter(const heif_encoder_parameter*); const struct heif_encoder_parameter* m_parameter; friend class Encoder; }; class Encoder { public: // throws Error Encoder(enum heif_compression_format format); // throws Error void set_lossy_quality(int quality); // throws Error void set_lossless(bool enable_lossless); std::vector list_parameters() const noexcept; void set_integer_parameter(const std::string& parameter_name, int value); int get_integer_parameter(const std::string& parameter_name) const; void set_boolean_parameter(const std::string& parameter_name, bool value); bool get_boolean_parameter(const std::string& parameter_name) const; void set_string_parameter(const std::string& parameter_name, const std::string& value); std::string get_string_parameter(const std::string& parameter_name) const; void set_parameter(const std::string& parameter_name, const std::string& parameter_value); std::string get_parameter(const std::string& parameter_name) const; private: Encoder(struct heif_encoder*) noexcept; std::shared_ptr m_encoder; friend class EncoderDescriptor; friend class Context; }; // ========================================================================================== // IMPLEMENTATION // ========================================================================================== inline Context::Context() { heif_context* ctx = heif_context_alloc(); m_context = std::shared_ptr(ctx, [](heif_context* c) { heif_context_free(c); }); } inline void Context::read_from_file(const std::string& filename, const ReadingOptions& /*opts*/) { Error err = Error(heif_context_read_from_file(m_context.get(), filename.c_str(), NULL)); if (err) { throw err; } } inline void Context::read_from_memory(const void* mem, size_t size, const ReadingOptions& /*opts*/) { Error err = Error(heif_context_read_from_memory(m_context.get(), mem, size, NULL)); if (err) { throw err; } } inline void Context::read_from_memory_without_copy(const void* mem, size_t size, const ReadingOptions& /*opts*/) { Error err = Error(heif_context_read_from_memory_without_copy(m_context.get(), mem, size, NULL)); if (err) { throw err; } } inline int64_t heif_reader_trampoline_get_position(void* userdata) { Context::Reader* reader = (Context::Reader*) userdata; return reader->get_position(); } inline int heif_reader_trampoline_read(void* data, size_t size, void* userdata) { Context::Reader* reader = (Context::Reader*) userdata; return reader->read(data, size); } inline int heif_reader_trampoline_seek(int64_t position, void* userdata) { Context::Reader* reader = (Context::Reader*) userdata; return reader->seek(position); } inline heif_reader_grow_status heif_reader_trampoline_wait_for_file_size(int64_t target_size, void* userdata) { Context::Reader* reader = (Context::Reader*) userdata; return reader->wait_for_file_size(target_size); } static struct heif_reader heif_reader_trampoline = { 1, heif_reader_trampoline_get_position, heif_reader_trampoline_read, heif_reader_trampoline_seek, heif_reader_trampoline_wait_for_file_size }; inline void Context::read_from_reader(Reader& reader, const ReadingOptions& /*opts*/) { Error err = Error(heif_context_read_from_reader(m_context.get(), &heif_reader_trampoline, &reader, NULL)); if (err) { throw err; } } inline int Context::get_number_of_top_level_images() const noexcept { return heif_context_get_number_of_top_level_images(m_context.get()); } inline bool Context::is_top_level_image_ID(heif_item_id id) const noexcept { return heif_context_is_top_level_image_ID(m_context.get(), id); } inline std::vector Context::get_list_of_top_level_image_IDs() const noexcept { int num = get_number_of_top_level_images(); std::vector IDs(num); heif_context_get_list_of_top_level_image_IDs(m_context.get(), IDs.data(), num); return IDs; } inline heif_item_id Context::get_primary_image_ID() const { heif_item_id id; Error err = Error(heif_context_get_primary_image_ID(m_context.get(), &id)); if (err) { throw err; } return id; } inline ImageHandle Context::get_primary_image_handle() const { heif_image_handle* handle; Error err = Error(heif_context_get_primary_image_handle(m_context.get(), &handle)); if (err) { throw err; } return ImageHandle(handle); } inline ImageHandle Context::get_image_handle(heif_item_id id) const { struct heif_image_handle* handle; Error err = Error(heif_context_get_image_handle(m_context.get(), id, &handle)); if (err) { throw err; } return ImageHandle(handle); } #if 0 inline Context Context::wrap_without_releasing(heif_context* ctx) { Context context; context.m_context = std::shared_ptr(ctx, [] (heif_context*) { /* NOP */ }); return context; } #endif inline struct ::heif_error heif_writer_trampoline_write(struct heif_context* ctx, const void* data, size_t size, void* userdata) { Context::Writer* writer = (Context::Writer*) userdata; (void) ctx; //Context context = Context::wrap_without_releasing(ctx); //return writer->write(context, data, size); return writer->write(data, size); } static struct heif_writer heif_writer_trampoline = { 1, &heif_writer_trampoline_write }; inline void Context::write(Writer& writer) { Error err = Error(heif_context_write(m_context.get(), &heif_writer_trampoline, &writer)); if (err) { throw err; } } inline void Context::write_to_file(const std::string& filename) const { Error err = Error(heif_context_write_to_file(m_context.get(), filename.c_str())); if (err) { throw err; } } inline ImageHandle::ImageHandle(heif_image_handle* handle) { if (handle != nullptr) { m_image_handle = std::shared_ptr(handle, [](heif_image_handle* h) { heif_image_handle_release(h); }); } } inline bool ImageHandle::is_primary_image() const noexcept { return heif_image_handle_is_primary_image(m_image_handle.get()) != 0; } inline int ImageHandle::get_width() const noexcept { return heif_image_handle_get_width(m_image_handle.get()); } inline int ImageHandle::get_height() const noexcept { return heif_image_handle_get_height(m_image_handle.get()); } inline bool ImageHandle::has_alpha_channel() const noexcept { return heif_image_handle_has_alpha_channel(m_image_handle.get()) != 0; } inline bool ImageHandle::is_premultiplied_alpha() const noexcept { return heif_image_handle_is_premultiplied_alpha(m_image_handle.get()) != 0; } inline int ImageHandle::get_luma_bits_per_pixel() const noexcept { return heif_image_handle_get_luma_bits_per_pixel(m_image_handle.get()); } inline int ImageHandle::get_chroma_bits_per_pixel() const noexcept { return heif_image_handle_get_chroma_bits_per_pixel(m_image_handle.get()); } inline int ImageHandle::get_ispe_width() const noexcept { return heif_image_handle_get_ispe_width(m_image_handle.get()); } inline int ImageHandle::get_ispe_height() const noexcept { return heif_image_handle_get_ispe_height(m_image_handle.get()); } // ------------------------- depth images ------------------------- // TODO // ------------------------- thumbnails ------------------------- inline int ImageHandle::get_number_of_thumbnails() const noexcept { return heif_image_handle_get_number_of_thumbnails(m_image_handle.get()); } inline std::vector ImageHandle::get_list_of_thumbnail_IDs() const noexcept { int num = get_number_of_thumbnails(); std::vector IDs(num); heif_image_handle_get_list_of_thumbnail_IDs(m_image_handle.get(), IDs.data(), num); return IDs; } inline ImageHandle ImageHandle::get_thumbnail(heif_item_id id) { heif_image_handle* handle; Error err = Error(heif_image_handle_get_thumbnail(m_image_handle.get(), id, &handle)); if (err) { throw err; } return ImageHandle(handle); } inline Image ImageHandle::decode_image(heif_colorspace colorspace, heif_chroma chroma, const DecodingOptions& /*options*/) { heif_image* out_img; Error err = Error(heif_decode_image(m_image_handle.get(), &out_img, colorspace, chroma, nullptr)); //const struct heif_decoding_options* options); if (err) { throw err; } return Image(out_img); } inline std::vector ImageHandle::get_list_of_metadata_block_IDs(const char* type_filter) const noexcept { int nBlocks = heif_image_handle_get_number_of_metadata_blocks(m_image_handle.get(), type_filter); std::vector ids(nBlocks); int n = heif_image_handle_get_list_of_metadata_block_IDs(m_image_handle.get(), type_filter, ids.data(), nBlocks); (void) n; //assert(n==nBlocks); return ids; } inline std::string ImageHandle::get_metadata_type(heif_item_id metadata_id) const noexcept { return heif_image_handle_get_metadata_type(m_image_handle.get(), metadata_id); } inline std::string ImageHandle::get_metadata_content_type(heif_item_id metadata_id) const noexcept { return heif_image_handle_get_metadata_content_type(m_image_handle.get(), metadata_id); } inline std::vector ImageHandle::get_metadata(heif_item_id metadata_id) const { size_t data_size = heif_image_handle_get_metadata_size(m_image_handle.get(), metadata_id); std::vector data(data_size); Error err = Error(heif_image_handle_get_metadata(m_image_handle.get(), metadata_id, data.data())); if (err) { throw err; } return data; } inline ColorProfile_nclx::ColorProfile_nclx() { mProfile = heif_nclx_color_profile_alloc(); } inline ColorProfile_nclx::~ColorProfile_nclx() { heif_nclx_color_profile_free(mProfile); } inline heif_color_primaries ColorProfile_nclx::get_color_primaries() const { return mProfile->color_primaries; } inline heif_transfer_characteristics ColorProfile_nclx::get_transfer_characteristics() const { return mProfile->transfer_characteristics; } inline heif_matrix_coefficients ColorProfile_nclx::get_matrix_coefficients() const { return mProfile->matrix_coefficients; } inline bool ColorProfile_nclx::is_full_range() const { return mProfile->full_range_flag; } inline void ColorProfile_nclx::set_color_primaries(heif_color_primaries cp) { mProfile->color_primaries = cp; } inline void ColorProfile_nclx::set_color_primaties(heif_color_primaries cp) { set_color_primaries(cp); } inline void ColorProfile_nclx::set_transfer_characteristics(heif_transfer_characteristics tc) { mProfile->transfer_characteristics = tc; } inline void ColorProfile_nclx::set_matrix_coefficients(heif_matrix_coefficients mc) { mProfile->matrix_coefficients = mc; } inline void ColorProfile_nclx::set_full_range_flag(bool is_full_range) { mProfile->full_range_flag = is_full_range; } inline Image::Image(heif_image* image) { m_image = std::shared_ptr(image, [](heif_image* h) { heif_image_release(h); }); } inline void Image::create(int width, int height, enum heif_colorspace colorspace, enum heif_chroma chroma) { heif_image* image; Error err = Error(heif_image_create(width, height, colorspace, chroma, &image)); if (err) { m_image.reset(); throw err; } else { m_image = std::shared_ptr(image, [](heif_image* h) { heif_image_release(h); }); } } inline void Image::add_plane(enum heif_channel channel, int width, int height, int bit_depth) { Error err = Error(heif_image_add_plane(m_image.get(), channel, width, height, bit_depth)); if (err) { throw err; } } inline heif_colorspace Image::get_colorspace() const noexcept { return heif_image_get_colorspace(m_image.get()); } inline heif_chroma Image::get_chroma_format() const noexcept { return heif_image_get_chroma_format(m_image.get()); } inline int Image::get_width(enum heif_channel channel) const noexcept { return heif_image_get_width(m_image.get(), channel); } inline int Image::get_height(enum heif_channel channel) const noexcept { return heif_image_get_height(m_image.get(), channel); } inline int Image::get_bits_per_pixel(enum heif_channel channel) const noexcept { return heif_image_get_bits_per_pixel(m_image.get(), channel); } inline int Image::get_bits_per_pixel_range(enum heif_channel channel) const noexcept { return heif_image_get_bits_per_pixel_range(m_image.get(), channel); } inline bool Image::has_channel(enum heif_channel channel) const noexcept { return heif_image_has_channel(m_image.get(), channel); } inline const uint8_t* Image::get_plane(enum heif_channel channel, int* out_stride) const noexcept { return heif_image_get_plane_readonly(m_image.get(), channel, out_stride); } inline uint8_t* Image::get_plane(enum heif_channel channel, int* out_stride) noexcept { return heif_image_get_plane(m_image.get(), channel, out_stride); } inline void Image::set_nclx_color_profile(const ColorProfile_nclx& nclx) { Error err = Error(heif_image_set_nclx_color_profile(m_image.get(), nclx.mProfile)); if (err) { throw err; } } // throws Error inline ColorProfile_nclx Image::get_nclx_color_profile() const { heif_color_profile_nclx* nclx = nullptr; Error err = Error(heif_image_get_nclx_color_profile(m_image.get(), &nclx)); if (err) { throw err; } return ColorProfile_nclx(nclx); } inline heif_color_profile_type Image::get_color_profile_type() const { return heif_image_get_color_profile_type(m_image.get()); } // throws Error inline std::vector Image::get_raw_color_profile() const { auto size = heif_image_get_raw_color_profile_size(m_image.get()); std::vector profile(size); heif_image_get_raw_color_profile(m_image.get(), profile.data()); return profile; } inline void Image::set_raw_color_profile(heif_color_profile_type type, const std::vector& data) { const char* profile_type = nullptr; switch (type) { case heif_color_profile_type_prof: profile_type = "prof"; break; case heif_color_profile_type_rICC: profile_type = "rICC"; break; default: throw Error(heif_error_code::heif_error_Usage_error, heif_suberror_Unspecified, "invalid raw color profile type"); break; } Error err = Error(heif_image_set_raw_color_profile(m_image.get(), profile_type, data.data(), data.size())); if (err) { throw err; } } inline bool Image::is_premultiplied_alpha() const noexcept { return heif_image_is_premultiplied_alpha(m_image.get()) != 0; } inline void Image::set_premultiplied_alpha(bool is_premultiplied_alpha) noexcept { heif_image_set_premultiplied_alpha(m_image.get(), is_premultiplied_alpha); } inline Image Image::scale_image(int width, int height, const ScalingOptions&) const { heif_image* img; Error err = Error(heif_image_scale_image(m_image.get(), &img, width, height, nullptr)); // TODO: scaling options not defined yet if (err) { throw err; } return Image(img); } inline std::vector EncoderDescriptor::get_encoder_descriptors(enum heif_compression_format format_filter, const char* name_filter) noexcept { int maxDescriptors = 10; int nDescriptors; for (;;) { const struct heif_encoder_descriptor** descriptors; descriptors = new const heif_encoder_descriptor* [maxDescriptors]; nDescriptors = heif_context_get_encoder_descriptors(nullptr, format_filter, name_filter, descriptors, maxDescriptors); if (nDescriptors < maxDescriptors) { std::vector outDescriptors; outDescriptors.reserve(nDescriptors); for (int i = 0; i < nDescriptors; i++) { outDescriptors.push_back(EncoderDescriptor(descriptors[i])); } delete[] descriptors; return outDescriptors; } else { delete[] descriptors; maxDescriptors *= 2; } } } inline std::string EncoderDescriptor::get_name() const noexcept { return heif_encoder_descriptor_get_name(m_descriptor); } inline std::string EncoderDescriptor::get_id_name() const noexcept { return heif_encoder_descriptor_get_id_name(m_descriptor); } inline enum heif_compression_format EncoderDescriptor::get_compression_format() const noexcept { return heif_encoder_descriptor_get_compression_format(m_descriptor); } inline bool EncoderDescriptor::supportes_lossy_compression() const noexcept { return heif_encoder_descriptor_supports_lossy_compression(m_descriptor); } inline bool EncoderDescriptor::supports_lossy_compression() const noexcept { return heif_encoder_descriptor_supports_lossy_compression(m_descriptor); } inline bool EncoderDescriptor::supportes_lossless_compression() const noexcept { return heif_encoder_descriptor_supports_lossless_compression(m_descriptor); } inline bool EncoderDescriptor::supports_lossless_compression() const noexcept { return heif_encoder_descriptor_supports_lossless_compression(m_descriptor); } inline Encoder EncoderDescriptor::get_encoder() const { heif_encoder* encoder; Error err = Error(heif_context_get_encoder(nullptr, m_descriptor, &encoder)); if (err) { throw err; } return Encoder(encoder); } inline Encoder::Encoder(enum heif_compression_format format) { heif_encoder* encoder; Error err = Error(heif_context_get_encoder_for_format(nullptr, format, &encoder)); if (err) { throw err; } m_encoder = std::shared_ptr(encoder, [](heif_encoder* e) { heif_encoder_release(e); }); } inline Encoder::Encoder(struct heif_encoder* encoder) noexcept { m_encoder = std::shared_ptr(encoder, [](heif_encoder* e) { heif_encoder_release(e); }); } inline EncoderParameter::EncoderParameter(const heif_encoder_parameter* param) : m_parameter(param) { } inline std::string EncoderParameter::get_name() const noexcept { return heif_encoder_parameter_get_name(m_parameter); } inline enum heif_encoder_parameter_type EncoderParameter::get_type() const noexcept { return heif_encoder_parameter_get_type(m_parameter); } inline bool EncoderParameter::is_integer() const noexcept { return get_type() == heif_encoder_parameter_type_integer; } inline bool EncoderParameter::get_valid_integer_range(int& out_minimum, int& out_maximum) { int have_minimum_maximum; Error err = Error(heif_encoder_parameter_get_valid_integer_range(m_parameter, &have_minimum_maximum, &out_minimum, &out_maximum)); if (err) { throw err; } return have_minimum_maximum; } inline bool EncoderParameter::is_boolean() const noexcept { return get_type() == heif_encoder_parameter_type_boolean; } inline bool EncoderParameter::is_string() const noexcept { return get_type() == heif_encoder_parameter_type_string; } inline std::vector EncoderParameter::get_valid_string_values() const { const char* const* stringarray; Error err = Error(heif_encoder_parameter_get_valid_string_values(m_parameter, &stringarray)); if (err) { throw err; } std::vector values; for (int i = 0; stringarray[i]; i++) { values.push_back(stringarray[i]); } return values; } inline std::vector Encoder::list_parameters() const noexcept { std::vector parameters; for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(m_encoder.get()); *params; params++) { parameters.push_back(EncoderParameter(*params)); } return parameters; } inline void Encoder::set_lossy_quality(int quality) { Error err = Error(heif_encoder_set_lossy_quality(m_encoder.get(), quality)); if (err) { throw err; } } inline void Encoder::set_lossless(bool enable_lossless) { Error err = Error(heif_encoder_set_lossless(m_encoder.get(), enable_lossless)); if (err) { throw err; } } inline void Encoder::set_integer_parameter(const std::string& parameter_name, int value) { Error err = Error(heif_encoder_set_parameter_integer(m_encoder.get(), parameter_name.c_str(), value)); if (err) { throw err; } } inline int Encoder::get_integer_parameter(const std::string& parameter_name) const { int value; Error err = Error(heif_encoder_get_parameter_integer(m_encoder.get(), parameter_name.c_str(), &value)); if (err) { throw err; } return value; } inline void Encoder::set_boolean_parameter(const std::string& parameter_name, bool value) { Error err = Error(heif_encoder_set_parameter_boolean(m_encoder.get(), parameter_name.c_str(), value)); if (err) { throw err; } } inline bool Encoder::get_boolean_parameter(const std::string& parameter_name) const { int value; Error err = Error(heif_encoder_get_parameter_boolean(m_encoder.get(), parameter_name.c_str(), &value)); if (err) { throw err; } return value; } inline void Encoder::set_string_parameter(const std::string& parameter_name, const std::string& value) { Error err = Error(heif_encoder_set_parameter_string(m_encoder.get(), parameter_name.c_str(), value.c_str())); if (err) { throw err; } } inline std::string Encoder::get_string_parameter(const std::string& parameter_name) const { const int max_size = 250; char value[max_size]; Error err = Error(heif_encoder_get_parameter_string(m_encoder.get(), parameter_name.c_str(), value, max_size)); if (err) { throw err; } return value; } inline void Encoder::set_parameter(const std::string& parameter_name, const std::string& parameter_value) { Error err = Error(heif_encoder_set_parameter(m_encoder.get(), parameter_name.c_str(), parameter_value.c_str())); if (err) { throw err; } } inline std::string Encoder::get_parameter(const std::string& parameter_name) const { const int max_size = 250; char value[max_size]; Error err = Error(heif_encoder_get_parameter(m_encoder.get(), parameter_name.c_str(), value, max_size)); if (err) { throw err; } return value; } inline void Context::set_primary_image(ImageHandle& new_primary_image_handle) { Error err = Error(heif_context_set_primary_image(m_context.get(), new_primary_image_handle.get_raw_image_handle())); if (err) { throw err; } } inline Context::EncodingOptions::EncodingOptions() { // TODO: this is a bit hacky. It would be better to have an API function to set // the options to default values. But I do not see any reason for that apart from // this use-case. struct heif_encoding_options* default_options = heif_encoding_options_alloc(); *static_cast(this) = *default_options; // copy over all options heif_encoding_options_free(default_options); } inline ImageHandle Context::encode_image(const Image& img, Encoder& encoder, const EncodingOptions& options) { struct heif_image_handle* image_handle; Error err = Error(heif_context_encode_image(m_context.get(), img.m_image.get(), encoder.m_encoder.get(), &options, &image_handle)); if (err) { throw err; } return ImageHandle(image_handle); } inline ImageHandle Context::encode_thumbnail(const Image& image, const ImageHandle& master_image_handle, Encoder& encoder, const EncodingOptions& options, int bbox_size) { struct heif_image_handle* thumb_image_handle; Error err = Error(heif_context_encode_thumbnail(m_context.get(), image.m_image.get(), master_image_handle.get_raw_image_handle(), encoder.m_encoder.get(), &options, bbox_size, &thumb_image_handle)); if (err) { throw err; } return ImageHandle(thumb_image_handle); } inline void Context::assign_thumbnail(const ImageHandle& thumbnail_image, const ImageHandle& master_image) { Error err = Error(heif_context_assign_thumbnail(m_context.get(), thumbnail_image.get_raw_image_handle(), master_image.get_raw_image_handle())); if (err) { throw err; } } inline void Context::add_exif_metadata(const ImageHandle& master_image, const void* data, int size) { Error err = Error(heif_context_add_exif_metadata(m_context.get(), master_image.get_raw_image_handle(), data, size)); if (err) { throw err; } } inline void Context::add_XMP_metadata(const ImageHandle& master_image, const void* data, int size) { Error err = Error(heif_context_add_XMP_metadata(m_context.get(), master_image.get_raw_image_handle(), data, size)); if (err) { throw err; } } } #endif libheif-1.19.8/libheif/api/libheif/heif_properties.cc000664 001750 001750 00000047022 15003473471 023600 0ustar00farindkfarindk000000 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 "libheif/heif_properties.h" #include "context.h" #include "api_structs.h" #include "file.h" #include #include #include #include #include #include int heif_item_get_properties_of_type(const struct heif_context* context, heif_item_id id, heif_item_property_type type, heif_property_id* out_list, int count) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(id, properties); if (err) { // We do not pass the error, because a missing ipco should have been detected already when reading the file. return 0; } int out_idx = 0; int property_id = 1; for (const auto& property : properties) { bool match; if (type == heif_item_property_type_invalid) { match = true; } else { match = (property->get_short_type() == type); } if (match) { if (out_list && out_idx < count) { out_list[out_idx] = property_id; out_idx++; } else if (!out_list) { out_idx++; } } property_id++; } return out_idx; } int heif_item_get_transformation_properties(const struct heif_context* context, heif_item_id id, heif_property_id* out_list, int count) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(id, properties); if (err) { // We do not pass the error, because a missing ipco should have been detected already when reading the file. return 0; } int out_idx = 0; int property_id = 1; for (const auto& property : properties) { bool match = (property->get_short_type() == fourcc("imir") || property->get_short_type() == fourcc("irot") || property->get_short_type() == fourcc("clap")); if (match) { if (out_list && out_idx < count) { out_list[out_idx] = property_id; out_idx++; } else if (!out_list) { out_idx++; } } property_id++; } return out_idx; } enum heif_item_property_type heif_item_get_property_type(const struct heif_context* context, heif_item_id id, heif_property_id propertyId) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(id, properties); if (err) { // We do not pass the error, because a missing ipco should have been detected already when reading the file. return heif_item_property_type_invalid; } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return heif_item_property_type_invalid; } auto property = properties[propertyId - 1]; return (enum heif_item_property_type) property->get_short_type(); } static char* create_c_string_copy(const std::string s) { char* copy = new char[s.length() + 1]; strcpy(copy, s.data()); return copy; } struct heif_error heif_item_get_property_user_description(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_user_description** out) { if (!out || !context) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return err.error_struct(context->context.get()); } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "property index out of range"}; } auto udes = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!udes) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "wrong property type"}; } auto* udes_c = new heif_property_user_description(); udes_c->version = 1; udes_c->lang = create_c_string_copy(udes->get_lang()); udes_c->name = create_c_string_copy(udes->get_name()); udes_c->description = create_c_string_copy(udes->get_description()); udes_c->tags = create_c_string_copy(udes->get_tags()); *out = udes_c; return heif_error_success; } struct heif_error heif_item_add_property_user_description(const struct heif_context* context, heif_item_id itemId, const struct heif_property_user_description* description, heif_property_id* out_propertyId) { if (!context || !description) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; } auto udes = std::make_shared(); udes->set_lang(description->lang ? description->lang : ""); udes->set_name(description->name ? description->name : ""); udes->set_description(description->description ? description->description : ""); udes->set_tags(description->tags ? description->tags : ""); heif_property_id id = context->context->add_property(itemId, udes, false); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } enum heif_transform_mirror_direction heif_item_get_property_transform_mirror(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return heif_transform_mirror_direction_invalid; } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return heif_transform_mirror_direction_invalid; } auto imir = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!imir) { return heif_transform_mirror_direction_invalid; } return imir->get_mirror_direction(); } int heif_item_get_property_transform_rotation_ccw(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return -1; } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return -1; } auto irot = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!irot) { return -1; } return irot->get_rotation_ccw(); } void heif_item_get_property_transform_crop_borders(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, int image_width, int image_height, int* left, int* top, int* right, int* bottom) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return; } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return; } auto clap = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!clap) { return; } if (left) *left = clap->left_rounded(image_width); if (right) *right = image_width - 1 - clap->right_rounded(image_width); if (top) *top = clap->top_rounded(image_height); if (bottom) *bottom = image_height - 1 - clap->bottom_rounded(image_height); } void heif_property_user_description_release(struct heif_property_user_description* udes) { if (udes == nullptr) { return; } delete[] udes->lang; delete[] udes->name; delete[] udes->description; delete[] udes->tags; delete udes; } struct heif_error heif_item_add_raw_property(const struct heif_context* context, heif_item_id itemId, uint32_t short_type, const uint8_t* uuid_type, const uint8_t* data, size_t size, int is_essential, heif_property_id* out_propertyId) { if (!context || !data || (short_type == fourcc("uuid") && uuid_type==nullptr)) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; } auto raw_box = std::make_shared(short_type); if (short_type == fourcc("uuid")) { std::vector uuid_type_vector(uuid_type, uuid_type + 16); raw_box->set_uuid_type(uuid_type_vector); } std::vector data_vector(data, data + size); raw_box->set_raw_data(data_vector); heif_property_id id = context->context->add_property(itemId, raw_box, is_essential != 0); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } template struct heif_error find_property(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, std::shared_ptr* box_casted) { auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return err.error_struct(context->context.get()); } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "property index out of range"}; } auto box = properties[propertyId - 1]; *box_casted = std::dynamic_pointer_cast(box); return heif_error_success; } #if HEIF_ENABLE_EXPERIMENTAL_FEATURES const uint64_t heif_tai_clock_info_unknown_time_uncertainty = 0xFFFFFFFFFFFFFFFF; const uint64_t heif_unknown_tai_timestamp = 0xFFFFFFFFFFFFFFFF; const int32_t heif_tai_clock_info_unknown_drift_rate = 0x7FFFFFFF; int heif_is_tai_clock_info_drift_rate_undefined(int32_t drift_rate) { if (drift_rate == heif_tai_clock_info_unknown_drift_rate) { return 1; } return 0; } struct heif_error heif_property_set_clock_info(struct heif_context* ctx, heif_item_id itemId, const heif_tai_clock_info* clock, heif_property_id* out_propertyId) { if (!ctx || !clock) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; } // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->image_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"}; } // Create new taic if one doesn't exist for the itemId. auto taic = ctx->context->get_heif_file()->get_property_for_item(itemId); if (!taic) { taic = std::make_shared(); } taic->set_time_uncertainty(clock->time_uncertainty); taic->set_clock_resolution(clock->clock_resolution); taic->set_clock_drift_rate(clock->clock_drift_rate); taic->set_clock_type(clock->clock_type); bool essential = false; heif_property_id id = ctx->context->add_property(itemId, taic, essential); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } struct heif_error heif_property_get_clock_info(const struct heif_context* ctx, heif_item_id itemId, heif_tai_clock_info* out_clock) { if (!ctx) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL heif_context passed in"}; } else if (!out_clock) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_clock_info passed in"}; } // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->image_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"}; } // Check if taic exists for itemId auto taic = file->get_property_for_item(itemId); if (!taic) { out_clock = nullptr; return {heif_error_Usage_error, heif_suberror_Invalid_property, "TAI Clock property not found for itemId"}; } if (out_clock->version >= 1) { out_clock->version = 1; out_clock->time_uncertainty = taic->get_time_uncertainty(); out_clock->clock_resolution = taic->get_clock_resolution(); out_clock->clock_drift_rate = taic->get_clock_drift_rate(); out_clock->clock_type = taic->get_clock_type(); } return heif_error_success; } struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx, heif_item_id itemId, heif_tai_timestamp_packet* timestamp, heif_property_id* out_propertyId) { if (!ctx) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; } // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->image_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"}; } // Create new itai if one doesn't exist for the itemId. auto itai = file->get_property_for_item(itemId); if (!itai) { itai = std::make_shared(); } // Set timestamp values itai->set_tai_timestamp(timestamp->tai_timestamp); itai->set_synchronization_state(timestamp->synchronization_state); itai->set_timestamp_generation_failure(timestamp->timestamp_generation_failure); itai->set_timestamp_is_modified(timestamp->timestamp_is_modified); heif_property_id id = ctx->context->add_property(itemId, itai, false); // Create new taic if one doesn't exist for the itemId. auto taic = file->get_property_for_item(itemId); if (!taic) { taic = std::make_shared(); ctx->context->add_property(itemId, taic, false); // Should we output taic_id? } if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx, heif_item_id itemId, heif_tai_timestamp_packet* out_timestamp) { if (!ctx) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->image_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"}; } //Check if itai exists for itemId auto itai = file->get_property_for_item(itemId); if (!itai) { out_timestamp = nullptr; return {heif_error_Usage_error, heif_suberror_Invalid_property, "Timestamp property not found for itemId"}; } if (out_timestamp) { out_timestamp->version = 1; out_timestamp->tai_timestamp = itai->get_tai_timestamp(); out_timestamp->synchronization_state = itai->get_synchronization_state(); out_timestamp->timestamp_generation_failure = itai->get_timestamp_generation_failure(); out_timestamp->timestamp_is_modified = itai->get_timestamp_is_modified(); } return heif_error_success; } #endif struct heif_error heif_item_get_property_raw_size(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, size_t* size_out) { if (!context || !size_out) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; } std::shared_ptr box_other; struct heif_error err = find_property(context, itemId, propertyId, &box_other); if (err.code) { return err; } // TODO: every Box (not just Box_other) should have a get_raw_data() method. if (box_other == nullptr) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "this property is not read as a raw box"}; } const auto& data = box_other->get_raw_data(); *size_out = data.size(); return heif_error_success; } struct heif_error heif_item_get_property_raw_data(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, uint8_t* data_out) { if (!context || !data_out) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; } std::shared_ptr box_other; struct heif_error err = find_property(context, itemId, propertyId, &box_other); if (err.code) { return err; } // TODO: every Box (not just Box_other) should have a get_raw_data() method. if (box_other == nullptr) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "this property is not read as a raw box"}; } auto data = box_other->get_raw_data(); std::copy(data.begin(), data.end(), data_out); return heif_error_success; } struct heif_error heif_item_get_property_uuid_type(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, uint8_t extended_type[16]) { if (!context || !extended_type) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; } std::shared_ptr box_other; struct heif_error err = find_property(context, itemId, propertyId, &box_other); if (err.code) { return err; } if (box_other == nullptr) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "this property is not read as a raw box"}; } auto uuid = box_other->get_uuid_type(); std::copy(uuid.begin(), uuid.end(), extended_type); return heif_error_success; } libheif-1.19.8/libheif/api/libheif/heif_experimental.cc000664 001750 001750 00000024567 15003473471 024112 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "heif_experimental.h" #include "context.h" #include "api_structs.h" #include "file.h" #include #include #include #include #include struct heif_property_camera_intrinsic_matrix { Box_cmin::RelativeIntrinsicMatrix matrix; }; struct heif_error heif_item_get_property_camera_intrinsic_matrix(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_camera_intrinsic_matrix** out_matrix) { if (!out_matrix || !context) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return err.error_struct(context->context.get()); } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "property index out of range"}; } auto cmin = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!cmin) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "wrong property type"}; } *out_matrix = new heif_property_camera_intrinsic_matrix; (*out_matrix)->matrix = cmin->get_intrinsic_matrix(); return heif_error_success; } void heif_property_camera_intrinsic_matrix_release(struct heif_property_camera_intrinsic_matrix* matrix) { delete matrix; } struct heif_error heif_property_camera_intrinsic_matrix_get_focal_length(const struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_focal_length_x, double* out_focal_length_y) { if (!matrix) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed as matrix"}; } double fx, fy; matrix->matrix.compute_focal_length(image_width, image_height, fx, fy); if (out_focal_length_x) *out_focal_length_x = fx; if (out_focal_length_y) *out_focal_length_y = fy; return heif_error_success; } struct heif_error heif_property_camera_intrinsic_matrix_get_principal_point(const struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_principal_point_x, double* out_principal_point_y) { if (!matrix) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed as matrix"}; } double px, py; matrix->matrix.compute_principal_point(image_width, image_height, px, py); if (out_principal_point_x) *out_principal_point_x = px; if (out_principal_point_y) *out_principal_point_y = py; return heif_error_success; } struct heif_error heif_property_camera_intrinsic_matrix_get_skew(const struct heif_property_camera_intrinsic_matrix* matrix, double* out_skew) { if (!matrix || !out_skew) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } *out_skew = matrix->matrix.skew; return heif_error_success; } struct heif_property_camera_intrinsic_matrix* heif_property_camera_intrinsic_matrix_alloc() { return new heif_property_camera_intrinsic_matrix; } void heif_property_camera_intrinsic_matrix_set_simple(struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double focal_length, double principal_point_x, double principal_point_y) { if (!matrix) { return; } matrix->matrix.is_anisotropic = false; matrix->matrix.focal_length_x = focal_length / image_width; matrix->matrix.principal_point_x = principal_point_x / image_width; matrix->matrix.principal_point_y = principal_point_y / image_height; } void heif_property_camera_intrinsic_matrix_set_full(struct heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double focal_length_x, double focal_length_y, double principal_point_x, double principal_point_y, double skew) { if (!matrix) { return; } if (focal_length_x == focal_length_y && skew == 0) { heif_property_camera_intrinsic_matrix_set_simple(matrix, image_width, image_height, focal_length_x, principal_point_x, principal_point_y); return; } matrix->matrix.is_anisotropic = true; matrix->matrix.focal_length_x = focal_length_x / image_width; matrix->matrix.focal_length_y = focal_length_y / image_width; matrix->matrix.principal_point_x = principal_point_x / image_width; matrix->matrix.principal_point_y = principal_point_y / image_height; matrix->matrix.skew = skew; } struct heif_error heif_item_add_property_camera_intrinsic_matrix(const struct heif_context* context, heif_item_id itemId, const struct heif_property_camera_intrinsic_matrix* matrix, heif_property_id* out_propertyId) { if (!context || !matrix) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; } auto cmin = std::make_shared(); cmin->set_intrinsic_matrix(matrix->matrix); heif_property_id id = context->context->add_property(itemId, cmin, false); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } struct heif_property_camera_extrinsic_matrix { Box_cmex::ExtrinsicMatrix matrix; }; struct heif_error heif_item_get_property_camera_extrinsic_matrix(const struct heif_context* context, heif_item_id itemId, heif_property_id propertyId, struct heif_property_camera_extrinsic_matrix** out_matrix) { if (!out_matrix || !context) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } auto file = context->context->get_heif_file(); std::vector> properties; Error err = file->get_properties(itemId, properties); if (err) { return err.error_struct(context->context.get()); } if (propertyId < 1 || propertyId - 1 >= properties.size()) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "property index out of range"}; } auto cmex = std::dynamic_pointer_cast(properties[propertyId - 1]); if (!cmex) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "wrong property type"}; } *out_matrix = new heif_property_camera_extrinsic_matrix; (*out_matrix)->matrix = cmex->get_extrinsic_matrix(); return heif_error_success; } void heif_property_camera_extrinsic_matrix_release(struct heif_property_camera_extrinsic_matrix* matrix) { delete matrix; } struct heif_error heif_property_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_property_camera_extrinsic_matrix* matrix, double* out_matrix) { if (!matrix || !out_matrix) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } auto rot_matrix = matrix->matrix.calculate_rotation_matrix(); for (int i = 0; i < 9; i++) { out_matrix[i] = rot_matrix[i]; } return heif_error_success; } struct heif_error heif_property_camera_extrinsic_matrix_get_position_vector(const struct heif_property_camera_extrinsic_matrix* matrix, int32_t* out_vector) { if (!matrix || !out_vector) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } out_vector[0] = matrix->matrix.pos_x; out_vector[1] = matrix->matrix.pos_y; out_vector[2] = matrix->matrix.pos_z; return heif_error_success; } struct heif_error heif_property_camera_extrinsic_matrix_get_world_coordinate_system_id(const struct heif_property_camera_extrinsic_matrix* matrix, uint32_t* out_wcs_id) { if (!matrix || !out_wcs_id) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } *out_wcs_id = matrix->matrix.world_coordinate_system_id; return heif_error_success; } struct heif_error heif_image_extract_area(const heif_image* srcimg, uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, const heif_security_limits* limits, struct heif_image** out_image) { auto extractResult = srcimg->image->extract_image_area(x0,y0,w,h, limits); if (extractResult.error) { return extractResult.error.error_struct(srcimg->image.get()); } heif_image* area = new heif_image; area->image = extractResult.value; *out_image = area; return heif_error_success; } libheif-1.19.8/libheif/init.cc000664 001750 001750 00000023740 15003473471 017202 0ustar00farindkfarindk000000 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 "init.h" #include "libheif/heif.h" #include "error.h" #include "plugin_registry.h" #include "common_utils.h" #include "color-conversion/colorconversion.h" #if ENABLE_MULTITHREADING_SUPPORT #include #endif #if defined(_WIN32) #include "plugins_windows.h" #else #include "plugins_unix.h" #endif void heif_unload_all_plugins(); #if ENABLE_PLUGIN_LOADING void heif_unregister_encoder_plugin(const heif_encoder_plugin* plugin); std::vector get_plugin_paths() { std::vector plugin_paths; #if defined(_WIN32) plugin_paths = get_plugin_directories_from_environment_variable_windows(); #else plugin_paths = get_plugin_directories_from_environment_variable_unix(); #endif if (plugin_paths.empty()) { plugin_paths.push_back(LIBHEIF_PLUGIN_DIRECTORY); } return plugin_paths; } std::vector list_all_potential_plugins_in_directory(const char* directory) { #if defined(_WIN32) return list_all_potential_plugins_in_directory_windows(directory); #else return list_all_potential_plugins_in_directory_unix(directory); #endif } #else std::vector get_plugin_paths() { return {}; } #endif static int heif_library_initialization_count = 0; static bool default_plugins_registered = true; // because they are implicitly registered at startup #if ENABLE_MULTITHREADING_SUPPORT static std::recursive_mutex& heif_init_mutex() { static std::recursive_mutex init_mutex; return init_mutex; } #endif void load_plugins_if_not_initialized_yet() { if (heif_library_initialization_count == 0) { heif_init(nullptr); } } struct heif_error heif_init(struct heif_init_params*) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard lock(heif_init_mutex()); #endif if (heif_library_initialization_count == 0) { ColorConversionPipeline::init_ops(); // --- initialize builtin plugins if (!default_plugins_registered) { register_default_plugins(); } #if ENABLE_PLUGIN_LOADING struct heif_error err{}; std::vector plugin_paths = get_plugin_paths(); for (const auto& dir : plugin_paths) { err = heif_load_plugins(dir.c_str(), nullptr, nullptr, 0); if (err.code != 0) { return err; } } #endif } // Note: it is important that we increase the counter AFTER initialization such that // 'load_plugins_if_not_initialized_yet()' can check this without having to lock the mutex. heif_library_initialization_count++; return {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; } void heif_deinit() { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard lock(heif_init_mutex()); #endif if (heif_library_initialization_count == 0) { // This case should never happen (heif_deinit() is called more often than heif_init()). return; } if (heif_library_initialization_count == 1) { heif_unregister_decoder_plugins(); heif_unregister_encoder_plugins(); default_plugins_registered = false; heif_unload_all_plugins(); ColorConversionPipeline::release_ops(); } // Note: contrary to heif_init() I think it does not matter whether we decrease the counter before or after deinitialization. // If the client application calls heif_deinit() in parallel to some other libheif function, it is really broken. heif_library_initialization_count--; } // This could be inside ENABLE_PLUGIN_LOADING, but the "include-what-you-use" checker cannot process this. #include #include #include #if ENABLE_PLUGIN_LOADING #if defined(_WIN32) typedef PluginLibrary_Windows PluginLibrary_SysDep; #else typedef PluginLibrary_Unix PluginLibrary_SysDep; #endif struct loaded_plugin { PluginLibrary_SysDep plugin_library_handle; struct heif_plugin_info* info = nullptr; int openCnt = 0; }; static std::vector sLoadedPlugins; MAYBE_UNUSED heif_error error_dlopen{heif_error_Plugin_loading_error, heif_suberror_Plugin_loading_error, "Cannot open plugin (dlopen)."}; MAYBE_UNUSED heif_error error_plugin_not_loaded{heif_error_Plugin_loading_error, heif_suberror_Plugin_is_not_loaded, "Trying to remove a plugin that is not loaded."}; MAYBE_UNUSED heif_error error_cannot_read_plugin_directory{heif_error_Plugin_loading_error, heif_suberror_Cannot_read_plugin_directory, "Cannot read plugin directory."}; MAYBE_UNUSED static void unregister_plugin(const heif_plugin_info* info) { switch (info->type) { case heif_plugin_type_encoder: { auto* encoder_plugin = static_cast(info->plugin); heif_unregister_encoder_plugin(encoder_plugin); break; } case heif_plugin_type_decoder: { // TODO } } } struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard lock(heif_init_mutex()); #endif PluginLibrary_SysDep plugin; auto err = plugin.load_from_file(filename); if (err.code) { return err; } heif_plugin_info* plugin_info = plugin.get_plugin_info(); // --- check whether the plugin is already loaded // If yes, return pointer to existing plugin. for (auto& p : sLoadedPlugins) { if (p.plugin_library_handle == plugin) { if (out_plugin) { *out_plugin = p.info; p.openCnt++; return heif_error_ok; } } } loaded_plugin loadedPlugin; loadedPlugin.plugin_library_handle = plugin; loadedPlugin.openCnt = 1; loadedPlugin.info = plugin_info; sLoadedPlugins.push_back(loadedPlugin); *out_plugin = plugin_info; switch (loadedPlugin.info->type) { case heif_plugin_type_encoder: { auto* encoder_plugin = static_cast(plugin_info->plugin); struct heif_error err = heif_register_encoder_plugin(encoder_plugin); if (err.code) { return err; } break; } case heif_plugin_type_decoder: { auto* decoder_plugin = static_cast(plugin_info->plugin); struct heif_error err = heif_register_decoder_plugin(decoder_plugin); if (err.code) { return err; } break; } } return heif_error_ok; } struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard lock(heif_init_mutex()); #endif for (size_t i = 0; i < sLoadedPlugins.size(); i++) { auto& p = sLoadedPlugins[i]; if (p.info == plugin) { p.plugin_library_handle.release(); p.openCnt--; if (p.openCnt == 0) { unregister_plugin(plugin); sLoadedPlugins[i] = sLoadedPlugins.back(); sLoadedPlugins.pop_back(); } return heif_error_ok; } } return error_plugin_not_loaded; } void heif_unload_all_plugins() { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard lock(heif_init_mutex()); #endif for (auto& p : sLoadedPlugins) { unregister_plugin(p.info); for (int i = 0; i < p.openCnt; i++) { p.plugin_library_handle.release(); } } sLoadedPlugins.clear(); } struct heif_error heif_load_plugins(const char* directory, const struct heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size) { auto libraryFiles = list_all_potential_plugins_in_directory(directory); int nPlugins = 0; for (const auto& filename : libraryFiles) { const struct heif_plugin_info* info = nullptr; auto err = heif_load_plugin(filename.c_str(), &info); if (err.code == 0) { if (out_plugins) { if (nPlugins == output_array_size) { break; } out_plugins[nPlugins] = info; } nPlugins++; } } if (nPlugins < output_array_size && out_plugins) { out_plugins[nPlugins] = nullptr; } if (out_nPluginsLoaded) { *out_nPluginsLoaded = nPlugins; } return heif_error_ok; } #else static heif_error heif_error_plugins_unsupported{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Plugins are not supported"}; struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) { return heif_error_plugins_unsupported; } struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) { return heif_error_plugins_unsupported; } void heif_unload_all_plugins() {} struct heif_error heif_load_plugins(const char* directory, const struct heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size) { if (out_nPluginsLoaded) { *out_nPluginsLoaded = 0; } return heif_error_plugins_unsupported; } #endif const char* const* heif_get_plugin_directories() { auto plugin_paths = get_plugin_paths(); size_t n = plugin_paths.size(); auto out_paths = new char* [n + 1]; for (size_t i = 0; i < n; i++) { out_paths[i] = new char[plugin_paths[i].size() + 1]; strcpy(out_paths[i], plugin_paths[i].c_str()); } out_paths[n] = nullptr; return out_paths; } void heif_free_plugin_directories(const char* const* paths) { for (int i = 0; paths[i]; i++) { delete[] paths[i]; } delete[] paths; } libheif-1.19.8/libheif/plugins_windows.h000664 001750 001750 00000003047 15003473471 021332 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_PLUGINS_WINDOWS_H #define LIBHEIF_PLUGINS_WINDOWS_H #include #include #include "init.h" #include std::vector get_plugin_directories_from_environment_variable_windows(); std::vector list_all_potential_plugins_in_directory_windows(const char*); class PluginLibrary_Windows : public PluginLibrary { public: heif_error load_from_file(const char* filename) override; void release() override; heif_plugin_info* get_plugin_info() override { return m_plugin_info; } bool operator==(const PluginLibrary_Windows& b) const { return m_filename == b.m_filename; } private: std::string m_filename; HMODULE m_library_handle = nullptr; heif_plugin_info* m_plugin_info = nullptr; }; #endif //LIBHEIF_PLUGINS_WINDOWS_H libheif-1.19.8/libheif/error.h000664 001750 001750 00000005607 15003473471 017234 0ustar00farindkfarindk000000 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_ERROR_H #define LIBHEIF_ERROR_H #include #include #include #include #include #include #include #include #include #include "libheif/heif.h" #include class ErrorBuffer { public: ErrorBuffer() = default; void set_success() { m_error_message = c_success; } void set_error(const std::string& err) { m_buffer = err; m_error_message = m_buffer.c_str(); } const char* get_error() const { return m_error_message; } private: constexpr static const char* c_success = "Success"; std::string m_buffer; const char* m_error_message = c_success; }; class Error { public: enum heif_error_code error_code = heif_error_Ok; enum heif_suberror_code sub_error_code = heif_suberror_Unspecified; std::string message; Error(); Error(heif_error_code c, heif_suberror_code sc = heif_suberror_Unspecified, const std::string& msg = ""); static const Error Ok; static const Error InternalError; static const char kSuccess[]; bool operator==(const Error& other) const { return error_code == other.error_code; } bool operator!=(const Error& other) const { return !(*this == other); } Error operator||(const Error& other) const { if (error_code != heif_error_Ok) { return *this; } else { return other; } } operator bool() const { return error_code != heif_error_Ok; } static const char* get_error_string(heif_error_code err); static const char* get_error_string(heif_suberror_code err); heif_error error_struct(ErrorBuffer* error_buffer) const; }; inline std::ostream& operator<<(std::ostream& ostr, const Error& err) { ostr << err.error_code << "/" << err.sub_error_code; return ostr; } template class Result { public: Result() = default; Result(const T& v) : value(v), error(Error::Ok) {} Result(const Error& e) : error(e) {} operator bool() const { return error.error_code == heif_error_Ok; } T& operator*() { assert(error.error_code == heif_error_Ok); return value; } T value{}; Error error; }; #endif libheif-1.19.8/libheif/logging.h000664 001750 001750 00000003065 15003473471 017525 0ustar00farindkfarindk000000 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_LOGGING_H #define LIBHEIF_LOGGING_H #include #include #include #include #include #include #include class Indent { public: Indent() = default; int get_indent() const { return m_indent; } void operator++(int) { m_indent++; } void operator--(int) { m_indent--; if (m_indent < 0) m_indent = 0; } std::string get_string() const; private: int m_indent = 0; }; inline std::ostream& operator<<(std::ostream& ostr, const Indent& indent) { ostr << indent.get_string(); return ostr; } std::string write_raw_data_as_hex(const uint8_t* data, size_t len, const std::string& firstLineIndent, const std::string& remainingLinesIndent); #endif libheif-1.19.8/libheif/plugins_windows.cc000664 001750 001750 00000005503 15003473471 021467 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "plugins_windows.h" #include "libheif/heif_plugin.h" #include std::vector get_plugin_directories_from_environment_variable_windows() { char* path_variable = getenv("LIBHEIF_PLUGIN_PATH"); if (path_variable == nullptr) { return {}; } // --- split LIBHEIF_PLUGIN_PATH value at ';' into separate directories std::vector plugin_paths; std::istringstream paths(path_variable); std::string dir; while (getline(paths, dir, ';')) { plugin_paths.push_back(dir); } return plugin_paths; } std::vector list_all_potential_plugins_in_directory_windows(const char* directory) { std::vector result; HANDLE hFind; WIN32_FIND_DATA FindFileData; std::string findPattern{directory}; findPattern += "\\*.dll"; #if 0 DIR* dir = opendir(directory); if (dir == nullptr) { return error_cannot_read_plugin_directory; } #endif if ((hFind = FindFirstFile(findPattern.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE) { do { std::string filename = directory; filename += '/'; filename += FindFileData.cFileName; result.push_back(filename); } while (FindNextFile(hFind, &FindFileData)); FindClose(hFind); } return result; } heif_error PluginLibrary_Windows::load_from_file(const char* filename) { m_library_handle = LoadLibraryA(filename); if (!m_library_handle) { fprintf(stderr, "LoadLibraryA error: %lu\n", GetLastError()); return error_dlopen; } m_plugin_info = (heif_plugin_info*) GetProcAddress(m_library_handle, "plugin_info"); if (!m_plugin_info) { fprintf(stderr, "GetProcAddress error for dll '%s': %lu\n", filename, GetLastError()); return error_dlopen; } // Remember filename for comparison whether the plugin was already loaded. // We need this since LoadLibraryA() returns a separate instance if we load the same DLL twice. m_filename = filename; return heif_error_ok; } void PluginLibrary_Windows::release() { if (m_library_handle) { FreeLibrary(m_library_handle); m_library_handle = nullptr; } } libheif-1.19.8/libheif/nclx.cc000664 001750 001750 00000031603 15003473471 017200 0ustar00farindkfarindk000000 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 . */ #include "nclx.h" #include "libheif/heif_experimental.h" #include #include #include #include primaries::primaries(float gx, float gy, float bx, float by, float rx, float ry, float wx, float wy) { defined = true; redX = rx; redY = ry; greenX = gx; greenY = gy; blueX = bx; blueY = by; whiteX = wx; whiteY = wy; } primaries get_colour_primaries(uint16_t primaries_idx) { switch (primaries_idx) { case 1: return {0.300f, 0.600f, 0.150f, 0.060f, 0.640f, 0.330f, 0.3127f, 0.3290f}; case 4: return {0.21f, 0.71f, 0.14f, 0.08f, 0.67f, 0.33f, 0.310f, 0.316f}; case 5: return {0.29f, 0.60f, 0.15f, 0.06f, 0.64f, 0.33f, 0.3127f, 0.3290f}; case 6: case 7: return {0.310f, 0.595f, 0.155f, 0.070f, 0.630f, 0.340f, 0.3127f, 0.3290f}; case 8: return {0.243f, 0.692f, 0.145f, 0.049f, 0.681f, 0.319f, 0.310f, 0.316f}; case 9: return {0.170f, 0.797f, 0.131f, 0.046f, 0.708f, 0.292f, 0.3127f, 0.3290f}; case 10: return {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.333333f, 0.33333f}; case 11: return {0.265f, 0.690f, 0.150f, 0.060f, 0.680f, 0.320f, 0.314f, 0.351f}; case 12: return {0.265f, 0.690f, 0.150f, 0.060f, 0.680f, 0.320f, 0.3127f, 0.3290f}; case 22: return {0.295f, 0.605f, 0.155f, 0.077f, 0.630f, 0.340f, 0.3127f, 0.3290f}; default: return {}; } } Kr_Kb Kr_Kb::defaults() { Kr_Kb kr_kb; // Rec 601. kr_kb.Kr = 0.2990f; kr_kb.Kb = 0.1140f; return kr_kb; } Kr_Kb get_Kr_Kb(uint16_t matrix_coefficients_idx, uint16_t primaries_idx) { Kr_Kb result; if (matrix_coefficients_idx == 12 || matrix_coefficients_idx == 13) { primaries p = get_colour_primaries(primaries_idx); float zr = 1 - (p.redX + p.redY); float zg = 1 - (p.greenX + p.greenY); float zb = 1 - (p.blueX + p.blueY); float zw = 1 - (p.whiteX + p.whiteY); float denom = p.whiteY * (p.redX * (p.greenY * zb - p.blueY * zg) + p.greenX * (p.blueY * zr - p.redY * zb) + p.blueX * (p.redY * zg - p.greenY * zr)); if (denom == 0.0f) { return result; } result.Kr = (p.redY * (p.whiteX * (p.greenY * zb - p.blueY * zg) + p.whiteY * (p.blueX * zg - p.greenX * zb) + zw * (p.greenX * p.blueY - p.blueX * p.greenY))) / denom; result.Kb = (p.blueY * (p.whiteX * (p.redY * zg - p.greenY * zr) + p.whiteY * (p.greenX * zr - p.redX * zg) + zw * (p.redX * p.greenY - p.greenX * p.redY))) / denom; } else switch (matrix_coefficients_idx) { case 1: result.Kr = 0.2126f; result.Kb = 0.0722f; break; case 4: result.Kr = 0.30f; result.Kb = 0.11f; break; case 5: case 6: result.Kr = 0.299f; result.Kb = 0.114f; break; case 7: result.Kr = 0.212f; result.Kb = 0.087f; break; case 9: case 10: result.Kr = 0.2627f; result.Kb = 0.0593f; break; default:; } return result; } YCbCr_to_RGB_coefficients YCbCr_to_RGB_coefficients::defaults() { YCbCr_to_RGB_coefficients coeffs; coeffs.defined = true; coeffs.r_cr = 1.402f; coeffs.g_cb = -0.344136f; coeffs.g_cr = -0.714136f; coeffs.b_cb = 1.772f; return coeffs; } YCbCr_to_RGB_coefficients get_YCbCr_to_RGB_coefficients(uint16_t matrix_coefficients_idx, uint16_t primaries_idx) { YCbCr_to_RGB_coefficients coeffs; Kr_Kb k = get_Kr_Kb(matrix_coefficients_idx, primaries_idx); if (k.Kb != 0 || k.Kr != 0) { // both will be != 0 when valid coeffs.defined = true; coeffs.r_cr = 2 * (-k.Kr + 1); coeffs.g_cb = 2 * k.Kb * (-k.Kb + 1) / (k.Kb + k.Kr - 1); coeffs.g_cr = 2 * k.Kr * (-k.Kr + 1) / (k.Kb + k.Kr - 1); coeffs.b_cb = 2 * (-k.Kb + 1); } else { coeffs = YCbCr_to_RGB_coefficients::defaults(); } return coeffs; } RGB_to_YCbCr_coefficients get_RGB_to_YCbCr_coefficients(uint16_t matrix_coefficients_idx, uint16_t primaries_idx) { RGB_to_YCbCr_coefficients coeffs; Kr_Kb k = get_Kr_Kb(matrix_coefficients_idx, primaries_idx); if (k.Kb != 0 || k.Kr != 0) { // both will be != 0 when valid coeffs.defined = true; coeffs.c[0][0] = k.Kr; coeffs.c[0][1] = 1 - k.Kr - k.Kb; coeffs.c[0][2] = k.Kb; coeffs.c[1][0] = -k.Kr / (1 - k.Kb) / 2; coeffs.c[1][1] = -(1 - k.Kr - k.Kb) / (1 - k.Kb) / 2; coeffs.c[1][2] = 0.5f; coeffs.c[2][0] = 0.5f; coeffs.c[2][1] = -(1 - k.Kr - k.Kb) / (1 - k.Kr) / 2; coeffs.c[2][2] = -k.Kb / (1 - k.Kr) / 2; } else { coeffs = RGB_to_YCbCr_coefficients::defaults(); } return coeffs; } RGB_to_YCbCr_coefficients RGB_to_YCbCr_coefficients::defaults() { RGB_to_YCbCr_coefficients coeffs; coeffs.defined = true; // Rec 601 full. Kr=0.2990f Kb=0.1140f. coeffs.c[0][0] = 0.299f; coeffs.c[0][1] = 0.587f; coeffs.c[0][2] = 0.114f; coeffs.c[1][0] = -0.168735f; coeffs.c[1][1] = -0.331264f; coeffs.c[1][2] = 0.5f; coeffs.c[2][0] = 0.5f; coeffs.c[2][1] = -0.418688f; coeffs.c[2][2] = -0.081312f; return coeffs; } Error color_profile_nclx::parse(BitstreamRange& range) { StreamReader::grow_status status; status = range.wait_for_available_bytes(7); if (status != StreamReader::grow_status::size_reached) { // TODO: return recoverable error at timeout return Error(heif_error_Invalid_input, heif_suberror_End_of_data); } m_colour_primaries = range.read16(); m_transfer_characteristics = range.read16(); m_matrix_coefficients = range.read16(); m_full_range_flag = (range.read8() & 0x80 ? true : false); return Error::Ok; } Error color_profile_nclx::get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const { *out_data = nullptr; struct heif_color_profile_nclx* nclx = alloc_nclx_color_profile(); if (nclx == nullptr) { return Error(heif_error_Memory_allocation_error, heif_suberror_Unspecified); } struct heif_error err; nclx->version = 1; err = heif_nclx_color_profile_set_color_primaries(nclx, get_colour_primaries()); if (err.code) { free_nclx_color_profile(nclx); return {err.code, err.subcode}; } err = heif_nclx_color_profile_set_transfer_characteristics(nclx, get_transfer_characteristics()); if (err.code) { free_nclx_color_profile(nclx); return {err.code, err.subcode}; } err = heif_nclx_color_profile_set_matrix_coefficients(nclx, get_matrix_coefficients()); if (err.code) { free_nclx_color_profile(nclx); return {err.code, err.subcode}; } nclx->full_range_flag = get_full_range_flag(); // fill color primaries auto primaries = ::get_colour_primaries(nclx->color_primaries); nclx->color_primary_red_x = primaries.redX; nclx->color_primary_red_y = primaries.redY; nclx->color_primary_green_x = primaries.greenX; nclx->color_primary_green_y = primaries.greenY; nclx->color_primary_blue_x = primaries.blueX; nclx->color_primary_blue_y = primaries.blueY; nclx->color_primary_white_x = primaries.whiteX; nclx->color_primary_white_y = primaries.whiteY; *out_data = nclx; return Error::Ok; } struct heif_color_profile_nclx* color_profile_nclx::alloc_nclx_color_profile() { auto profile = (heif_color_profile_nclx*) malloc(sizeof(struct heif_color_profile_nclx)); if (profile) { profile->version = 1; // sRGB defaults profile->color_primaries = heif_color_primaries_ITU_R_BT_709_5; // 1 profile->transfer_characteristics = heif_transfer_characteristic_IEC_61966_2_1; // 13 profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; // 6 profile->full_range_flag = true; } return profile; } void color_profile_nclx::free_nclx_color_profile(struct heif_color_profile_nclx* profile) { free(profile); } void color_profile_nclx::set_sRGB_defaults() { // sRGB defaults m_colour_primaries = 1; m_transfer_characteristics = 13; m_matrix_coefficients = 6; m_full_range_flag = true; } void color_profile_nclx::set_undefined() { m_colour_primaries = 2; m_transfer_characteristics = 2; m_matrix_coefficients = 2; m_full_range_flag = true; } void color_profile_nclx::set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx) { if (nclx) { m_colour_primaries = nclx->color_primaries; m_transfer_characteristics = nclx->transfer_characteristics; m_matrix_coefficients = nclx->matrix_coefficients; m_full_range_flag = nclx->full_range_flag; } } void color_profile_nclx::replace_undefined_values_with_sRGB_defaults() { if (m_matrix_coefficients == heif_matrix_coefficients_unspecified) { m_matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; } if (m_colour_primaries == heif_color_primaries_unspecified) { m_colour_primaries = heif_color_primaries_ITU_R_BT_709_5; } if (m_transfer_characteristics == heif_transfer_characteristic_unspecified) { m_transfer_characteristics = heif_transfer_characteristic_IEC_61966_2_1; } } Error Box_colr::parse(BitstreamRange& range, const heif_security_limits* limits) { StreamReader::grow_status status; uint32_t colour_type = range.read32(); if (colour_type == fourcc("nclx")) { auto color_profile = std::make_shared(); m_color_profile = color_profile; Error err = color_profile->parse(range); if (err) { return err; } } else if (colour_type == fourcc("prof") || colour_type == fourcc("rICC")) { if (!has_fixed_box_size()) { return Error(heif_error_Unsupported_feature, heif_suberror_Unspecified, "colr boxes with undefined box size are not supported"); } uint64_t profile_size_64 = get_box_size() - get_header_size() - 4; if (limits->max_color_profile_size && profile_size_64 > limits->max_color_profile_size) { return Error(heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, "Color profile exceeds maximum supported size"); } size_t profile_size = static_cast(profile_size_64); status = range.wait_for_available_bytes(profile_size); if (status != StreamReader::grow_status::size_reached) { // TODO: return recoverable error at timeout return Error(heif_error_Invalid_input, heif_suberror_End_of_data); } std::vector rawData(profile_size); for (size_t i = 0; i < profile_size; i++) { rawData[i] = range.read8(); } m_color_profile = std::make_shared(colour_type, rawData); } else { return Error(heif_error_Invalid_input, heif_suberror_Unknown_color_profile_type); } return range.get_error(); } std::string Box_colr::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); if (m_color_profile) { sstr << indent << "colour_type: " << fourcc_to_string(get_color_profile_type()) << "\n"; sstr << m_color_profile->dump(indent); } else { sstr << indent << "colour_type: ---\n"; sstr << "no color profile\n"; } return sstr.str(); } std::string color_profile_raw::dump(Indent& indent) const { std::ostringstream sstr; sstr << indent << "profile size: " << m_data.size() << "\n"; return sstr.str(); } std::string color_profile_nclx::dump(Indent& indent) const { std::ostringstream sstr; sstr << indent << "colour_primaries: " << m_colour_primaries << "\n" << indent << "transfer_characteristics: " << m_transfer_characteristics << "\n" << indent << "matrix_coefficients: " << m_matrix_coefficients << "\n" << indent << "full_range_flag: " << m_full_range_flag << "\n"; return sstr.str(); } Error color_profile_nclx::write(StreamWriter& writer) const { writer.write16(m_colour_primaries); writer.write16(m_transfer_characteristics); writer.write16(m_matrix_coefficients); writer.write8(m_full_range_flag ? 0x80 : 0x00); return Error::Ok; } Error color_profile_raw::write(StreamWriter& writer) const { writer.write(m_data); return Error::Ok; } Error Box_colr::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); assert(m_color_profile); writer.write32(m_color_profile->get_type()); Error err = m_color_profile->write(writer); if (err) { return err; } prepend_header(writer, box_start); return Error::Ok; } libheif-1.19.8/libheif/CMakeLists.txt000664 001750 001750 00000021764 15003473471 020474 0ustar00farindkfarindk000000 000000 include(CMakePackageConfigHelpers) configure_file(api/libheif/heif_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h) set(libheif_headers api/libheif/heif.h api/libheif/heif_cxx.h api/libheif/heif_plugin.h api/libheif/heif_properties.h api/libheif/heif_regions.h api/libheif/heif_items.h ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h) set(libheif_sources bitstream.cc bitstream.h box.cc box.h error.cc error.h context.cc context.h file.cc file.h file_layout.h file_layout.cc pixelimage.cc pixelimage.h plugin_registry.cc nclx.cc nclx.h plugin_registry.h security_limits.cc security_limits.h init.cc init.h logging.h logging.cc compression.h compression_brotli.cc compression_zlib.cc common_utils.cc common_utils.h region.cc region.h api/libheif/api_structs.h api/libheif/heif.cc api/libheif/heif_regions.cc api/libheif/heif_plugin.cc api/libheif/heif_properties.cc api/libheif/heif_items.cc codecs/decoder.h codecs/decoder.cc image-items/hevc.cc image-items/hevc.h codecs/hevc_boxes.cc codecs/hevc_boxes.h codecs/hevc_dec.cc codecs/hevc_dec.h image-items/avif.cc image-items/avif.h codecs/avif_dec.cc codecs/avif_dec.h codecs/avif_boxes.cc codecs/avif_boxes.h image-items/jpeg.h image-items/jpeg.cc codecs/jpeg_boxes.h codecs/jpeg_boxes.cc codecs/jpeg_dec.h codecs/jpeg_dec.cc image-items/jpeg2000.h image-items/jpeg2000.cc codecs/jpeg2000_dec.h codecs/jpeg2000_dec.cc codecs/jpeg2000_boxes.h codecs/jpeg2000_boxes.cc image-items/vvc.h image-items/vvc.cc codecs/vvc_dec.h codecs/vvc_dec.cc codecs/vvc_boxes.h codecs/vvc_boxes.cc image-items/avc.h image-items/avc.cc codecs/avc_boxes.h codecs/avc_boxes.cc codecs/avc_dec.h codecs/avc_dec.cc image-items/mask_image.h image-items/mask_image.cc image-items/image_item.h image-items/image_item.cc image-items/grid.h image-items/grid.cc image-items/overlay.h image-items/overlay.cc image-items/iden.h image-items/iden.cc image-items/tiled.h image-items/tiled.cc color-conversion/colorconversion.cc color-conversion/colorconversion.h color-conversion/rgb2yuv.cc color-conversion/rgb2yuv.h color-conversion/rgb2yuv_sharp.cc color-conversion/rgb2yuv_sharp.h color-conversion/yuv2rgb.cc color-conversion/yuv2rgb.h color-conversion/rgb2rgb.cc color-conversion/rgb2rgb.h color-conversion/monochrome.cc color-conversion/monochrome.h color-conversion/hdr_sdr.cc color-conversion/hdr_sdr.h color-conversion/alpha.cc color-conversion/alpha.h color-conversion/chroma_sampling.cc color-conversion/chroma_sampling.h ${libheif_headers}) add_library(heif ${libheif_sources}) if (ENABLE_PLUGIN_LOADING) if (WIN32) target_sources(heif PRIVATE plugins_windows.cc plugins_windows.h) else () target_sources(heif PRIVATE plugins_unix.cc plugins_unix.h) endif () endif () option(ENABLE_EXPERIMENTAL_FEATURES "Compile experimental features and install headers with unstable API" OFF) if (ENABLE_EXPERIMENTAL_FEATURES) target_sources(heif PRIVATE api/libheif/heif_experimental.h api/libheif/heif_experimental.cc) list(APPEND libheif_headers api/libheif/heif_experimental.h) target_compile_definitions(heif PUBLIC HEIF_ENABLE_EXPERIMENTAL_FEATURES) endif() # Needed to find libheif/heif_version.h while compiling the library target_include_directories(heif PRIVATE ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif ${libheif_SOURCE_DIR}/libheif/api) # Propagate include/libheif to consume the headers from other projects target_include_directories(heif PUBLIC $ $ $) set_target_properties(heif PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) if (APPLE) set_target_properties(heif PROPERTIES LINK_FLAGS "-Wl,-compatibility_version,${MACOS_COMPATIBLE_VERSION}") endif () if (BUILD_FRAMEWORK) set_target_properties(heif PROPERTIES FRAMEWORK TRUE FRAMEWORK_VERSION "${PACKAGE_VERSION}" PRODUCT_BUNDLE_IDENTIFIER "github.com/strukturag/libheif" XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" # OUTPUT_NAME "heif" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO" PUBLIC_HEADER "${libheif_headers}" MACOSX_FRAMEWORK_IDENTIFIER "github.com/strukturag/libheif" MACOSX_FRAMEWORK_BUNDLE_VERSION "${PACKAGE_VERSION}" MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" MACOSX_RPATH TRUE) endif() target_compile_definitions(heif PUBLIC LIBHEIF_EXPORTS HAVE_VISIBILITY) if (PLUGIN_LOADING_SUPPORTED_AND_ENABLED) target_compile_definitions(heif PRIVATE ENABLE_PLUGIN_LOADING=1) target_link_libraries(heif PRIVATE ${CMAKE_DL_LIBS}) endif () add_subdirectory(plugins) if (LIBSHARPYUV_FOUND) message("Compiling in 'libsharpyuv'") target_compile_definitions(heif PUBLIC HAVE_LIBSHARPYUV=1) target_include_directories(heif PRIVATE ${LIBSHARPYUV_INCLUDE_DIRS}) target_link_libraries(heif PRIVATE ${LIBSHARPYUV_LIBRARIES}) else () message("Not compiling 'libsharpyuv'") endif () if (ZLIB_FOUND) target_compile_definitions(heif PRIVATE HAVE_ZLIB=1) target_link_libraries(heif PRIVATE ZLIB::ZLIB) endif () if (Brotli_FOUND) target_compile_definitions(heif PUBLIC HAVE_BROTLI=1) target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS}) target_link_libraries(heif PRIVATE ${BROTLI_LIBS}) endif() if (ENABLE_MULTITHREADING_SUPPORT) find_package(Threads) target_link_libraries(heif PRIVATE ${CMAKE_THREAD_LIBS_INIT}) target_compile_definitions(heif PRIVATE ENABLE_MULTITHREADING_SUPPORT=1) if (ENABLE_PARALLEL_TILE_DECODING) target_compile_definitions(heif PRIVATE ENABLE_PARALLEL_TILE_DECODING=1) endif () endif () if (WITH_UNCOMPRESSED_CODEC) target_compile_definitions(heif PUBLIC WITH_UNCOMPRESSED_CODEC=1) target_sources(heif PRIVATE codecs/uncompressed/unc_boxes.h codecs/uncompressed/unc_boxes.cc image-items/unc_image.h image-items/unc_image.cc codecs/uncompressed/unc_codec.h codecs/uncompressed/unc_codec.cc codecs/uncompressed/unc_dec.h codecs/uncompressed/unc_dec.cc codecs/uncompressed/decoder_abstract.h codecs/uncompressed/decoder_abstract.cc codecs/uncompressed/decoder_component_interleave.h codecs/uncompressed/decoder_component_interleave.cc codecs/uncompressed/decoder_pixel_interleave.h codecs/uncompressed/decoder_pixel_interleave.cc codecs/uncompressed/decoder_mixed_interleave.h codecs/uncompressed/decoder_mixed_interleave.cc codecs/uncompressed/decoder_row_interleave.h codecs/uncompressed/decoder_row_interleave.cc codecs/uncompressed/decoder_tile_component_interleave.h codecs/uncompressed/decoder_tile_component_interleave.cc) endif () if (ENABLE_EXPERIMENTAL_MINI_FORMAT) target_compile_definitions(heif PUBLIC ENABLE_EXPERIMENTAL_MINI_FORMAT=1) target_sources(heif PRIVATE mini.h mini.cc) endif () write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) install(TARGETS heif EXPORT ${PROJECT_NAME}-config RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION Library/Frameworks COMPONENT runtime OPTIONAL ) install(FILES ${libheif_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) install(EXPORT ${PROJECT_NAME}-config DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") # --- on Windows, copy the DLL into the executable directory for easier development if (WIN32 AND BUILD_SHARED_LIBS) add_custom_command(TARGET heif POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $/../examples ) endif () libheif-1.19.8/libheif/compression.h000664 001750 001750 00000005765 15003473471 020451 0ustar00farindkfarindk000000 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 . */ #ifndef LIBHEIF_COMPRESSION_H #define LIBHEIF_COMPRESSION_H #include #include #include #include #if HAVE_ZLIB /** * Compress data using zlib method. * * This is the RFC 1950 format. * * @param input pointer to the data to be compressed * @param size the length of the input array in bytes * @return the corresponding compressed data */ std::vector compress_zlib(const uint8_t* input, size_t size); /** * Compress data using deflate method. * * This is the RFC 1951 format. * * @param input pointer to the data to be compressed * @param size the length of the input array in bytes * @return the corresponding compressed data */ std::vector compress_deflate(const uint8_t* input, size_t size); /** * Decompress zlib compressed data. * * This is assumed to be in RFC 1950 format, which is the normal zlib format. * * @param compressed_input the compressed data to be decompressed * @param output pointer to the resulting vector of decompressed data * @return success (Ok) or an error on failure (usually corrupt data) * * @sa decompress_deflate * @sa compress_zlib */ Error decompress_zlib(const std::vector& compressed_input, std::vector* output); /** * Decompress "deflate" compressed data. * * This is assumed to be in RFC 1951 format, which is the deflate format. * * @param compressed_input the compressed data to be decompressed * @param output pointer to the resulting vector of decompressed data * @return success (Ok) or an error on failure (usually corrupt data) * * @sa decompress_zlib * @sa compress_deflate */ Error decompress_deflate(const std::vector& compressed_input, std::vector* output); #endif #if HAVE_BROTLI /** * Decompress Brotli compressed data. * * Brotli is described at https://brotli.org/ * * @param compressed_input the compressed data to be decompressed * @param output pointer to the resulting vector of decompressed data * @return success (Ok) or an error on failure (usually corrupt data) */ Error decompress_brotli(const std::vector& compressed_input, std::vector* output); std::vector compress_brotli(const uint8_t* input, size_t size); #endif #endif //LIBHEIF_COMPRESSION_H libheif-1.19.8/libheif/mini.cc000664 001750 001750 00000113334 15003473471 017172 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 Brad Hards * * 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 "mini.h" #include "file.h" #include "codecs/avif_boxes.h" #include "codecs/hevc_boxes.h" #include #include #include #include #include #include Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits) { uint64_t start_offset = range.get_istream()->get_position(); std::size_t length = range.get_remaining_bytes(); std::vector mini_data(length); range.read(mini_data.data(), mini_data.size()); BitReader bits(mini_data.data(), (int)(mini_data.size())); m_version = bits.get_bits8(2); m_explicit_codec_types_flag = bits.get_flag(); m_float_flag = bits.get_flag(); m_full_range_flag = bits.get_flag(); m_alpha_flag = bits.get_flag(); m_explicit_cicp_flag = bits.get_flag(); m_hdr_flag = bits.get_flag(); m_icc_flag = bits.get_flag(); m_exif_flag = bits.get_flag(); m_xmp_flag = bits.get_flag(); m_chroma_subsampling = bits.get_bits8(2); m_orientation = bits.get_bits8(3) + 1; bool small_dimensions_flag = bits.get_flag(); if (small_dimensions_flag) { m_width = 1 + bits.get_bits32(7); m_height = 1 + bits.get_bits32(7); } else { m_width = 1 + bits.get_bits32(15); m_height = 1 + bits.get_bits32(15); } if ((m_chroma_subsampling == 1) || (m_chroma_subsampling == 2)) { m_chroma_is_horizontally_centred = bits.get_flag(); } if (m_chroma_subsampling == 1) { m_chroma_is_vertically_centred = bits.get_flag(); } bool high_bit_depth_flag = false; if (m_float_flag) { uint8_t bit_depth_log2_minus4 = bits.get_bits8(2); m_bit_depth = (uint8_t)powl(2, (bit_depth_log2_minus4 + 4)); } else { high_bit_depth_flag = bits.get_flag(); if (high_bit_depth_flag) { m_bit_depth = 9 + bits.get_bits8(3); } } if (m_alpha_flag) { m_alpha_is_premultiplied = bits.get_flag(); } if (m_explicit_cicp_flag) { m_colour_primaries = bits.get_bits8(8); m_transfer_characteristics = bits.get_bits8(8); if (m_chroma_subsampling != 0) { m_matrix_coefficients = bits.get_bits8(8); } else { m_matrix_coefficients = 2; } } else { m_colour_primaries = m_icc_flag ? 2 : 1; m_transfer_characteristics = m_icc_flag ? 2 : 13; m_matrix_coefficients = (m_chroma_subsampling == 0) ? 2 : 6; } if (m_explicit_codec_types_flag) { m_infe_type = bits.get_bits32(32); m_codec_config_type = bits.get_bits32(32); } if (m_hdr_flag) { m_gainmap_flag = bits.get_flag(); if (m_gainmap_flag) { uint32_t gainmap_width_minus1 = bits.get_bits32(small_dimensions_flag ? 7 : 15); m_gainmap_width = gainmap_width_minus1 + 1; uint32_t gainmap_height_minus1 = bits.get_bits32(small_dimensions_flag ? 7 : 15); m_gainmap_height = gainmap_height_minus1 + 1; m_gainmap_matrix_coefficients = bits.get_bits8(8); m_gainmap_full_range_flag = bits.get_flag(); m_gainmap_chroma_subsampling = bits.get_bits8(2); if ((m_gainmap_chroma_subsampling == 1) || (m_gainmap_chroma_subsampling == 2)) { m_gainmap_chroma_is_horizontally_centred = bits.get_flag(); } if (m_gainmap_chroma_subsampling == 1) { m_gainmap_chroma_is_vertically_centred = bits.get_flag(); } m_gainmap_float_flag = bits.get_flag(); bool gainmap_high_bit_depth_flag = false; if (m_gainmap_float_flag) { uint8_t bit_depth_log2_minus4 = bits.get_bits8(2); m_gainmap_bit_depth = (uint8_t)powl(2, (bit_depth_log2_minus4 + 4)); } else { gainmap_high_bit_depth_flag = bits.get_flag(); if (gainmap_high_bit_depth_flag) { m_gainmap_bit_depth = 9 + bits.get_bits8(3); } } m_tmap_icc_flag = bits.get_flag(); m_tmap_explicit_cicp_flag = bits.get_flag(); if (m_tmap_explicit_cicp_flag) { m_tmap_colour_primaries = bits.get_bits8(8); m_tmap_transfer_characteristics = bits.get_bits8(8); m_tmap_matrix_coefficients = bits.get_bits8(8); m_tmap_full_range_flag = bits.get_flag(); } else { m_tmap_colour_primaries = 1; m_tmap_transfer_characteristics = 13; m_tmap_matrix_coefficients = 6; m_tmap_full_range_flag = true; } } bool clli_flag = bits.get_flag(); bool mdcv_flag = bits.get_flag(); bool cclv_flag = bits.get_flag(); bool amve_flag = bits.get_flag(); m_reve_flag = bits.get_flag(); m_ndwt_flag = bits.get_flag(); if (clli_flag) { m_clli = std::make_shared(); m_clli->clli.max_content_light_level = bits.get_bits16(16); m_clli->clli.max_pic_average_light_level = bits.get_bits16(16); } if (mdcv_flag) { m_mdcv = std::make_shared(); for (int c = 0; c < 3; c++) { m_mdcv->mdcv.display_primaries_x[c] = bits.get_bits16(16); m_mdcv->mdcv.display_primaries_y[c] = bits.get_bits16(16); } m_mdcv->mdcv.white_point_x = bits.get_bits16(16); m_mdcv->mdcv.white_point_y = bits.get_bits16(16); m_mdcv->mdcv.max_display_mastering_luminance = bits.get_bits32(32); m_mdcv->mdcv.min_display_mastering_luminance = bits.get_bits32(32); } if (cclv_flag) { m_cclv = std::make_shared(); bits.skip_bits(2); bool ccv_primaries_present_flag = bits.get_flag(); bool ccv_min_luminance_value_present_flag = bits.get_flag(); bool ccv_max_luminance_value_present_flag = bits.get_flag(); bool ccv_avg_luminance_value_present_flag = bits.get_flag(); bits.skip_bits(2); if (ccv_primaries_present_flag) { int32_t x0 = bits.get_bits32s(); int32_t y0 = bits.get_bits32s(); int32_t x1 = bits.get_bits32s(); int32_t y1 = bits.get_bits32s(); int32_t x2 = bits.get_bits32s(); int32_t y2 = bits.get_bits32s(); m_cclv->set_primaries(x0, y0, x1, y1, x2, y2); } if (ccv_min_luminance_value_present_flag) { m_cclv->set_min_luminance(bits.get_bits32(32)); } if (ccv_max_luminance_value_present_flag) { m_cclv->set_max_luminance(bits.get_bits32(32)); } if (ccv_avg_luminance_value_present_flag) { m_cclv->set_avg_luminance(bits.get_bits32(32)); } } if (amve_flag) { m_amve = std::make_shared(); m_amve->amve.ambient_illumination = bits.get_bits32(32); m_amve->amve.ambient_light_x = bits.get_bits16(16); m_amve->amve.ambient_light_y = bits.get_bits16(16); } if (m_reve_flag) { // TODO: ReferenceViewingEnvironment isn't published yet bits.skip_bits(32); bits.skip_bits(16); bits.skip_bits(16); bits.skip_bits(32); bits.skip_bits(16); bits.skip_bits(16); } if (m_ndwt_flag) { // TODO: NominalDiffuseWhite isn't published yet bits.skip_bits(32); } if (m_gainmap_flag) { bool tmap_clli_flag = bits.get_flag(); bool tmap_mdcv_flag = bits.get_flag(); bool tmap_cclv_flag = bits.get_flag(); bool tmap_amve_flag = bits.get_flag(); m_tmap_reve_flag = bits.get_flag(); m_tmap_ndwt_flag = bits.get_flag(); if (tmap_clli_flag) { m_tmap_clli = std::make_shared(); m_tmap_clli->clli.max_content_light_level = (uint16_t)bits.get_bits32(16); m_tmap_clli->clli.max_pic_average_light_level = (uint16_t)bits.get_bits32(16); } if (tmap_mdcv_flag) { m_tmap_mdcv = std::make_shared(); for (int c = 0; c < 3; c++) { m_tmap_mdcv->mdcv.display_primaries_x[c] = bits.get_bits16(16); m_tmap_mdcv->mdcv.display_primaries_y[c] = bits.get_bits16(16); } m_tmap_mdcv->mdcv.white_point_x = bits.get_bits16(16); m_tmap_mdcv->mdcv.white_point_y = bits.get_bits16(16); m_tmap_mdcv->mdcv.max_display_mastering_luminance = bits.get_bits32(32); m_tmap_mdcv->mdcv.min_display_mastering_luminance = bits.get_bits32(32); } if (tmap_cclv_flag) { m_tmap_cclv = std::make_shared(); bits.skip_bits(2); // skip ccv_cancel_flag and ccv_persistence_flag bool ccv_primaries_present_flag = bits.get_flag(); bool ccv_min_luminance_value_present_flag = bits.get_flag(); bool ccv_max_luminance_value_present_flag = bits.get_flag(); bool ccv_avg_luminance_value_present_flag = bits.get_flag(); bits.skip_bits(2); if (ccv_primaries_present_flag) { int32_t x0 = bits.get_bits32s(); int32_t y0 = bits.get_bits32s(); int32_t x1 = bits.get_bits32s(); int32_t y1 = bits.get_bits32s(); int32_t x2 = bits.get_bits32s(); int32_t y2 = bits.get_bits32s(); m_tmap_cclv->set_primaries(x0, y0, x1, y1, x2, y2); } if (ccv_min_luminance_value_present_flag) { m_tmap_cclv->set_min_luminance(bits.get_bits32(32)); } if (ccv_max_luminance_value_present_flag) { m_tmap_cclv->set_max_luminance(bits.get_bits32(32)); } if (ccv_avg_luminance_value_present_flag) { m_tmap_cclv->set_avg_luminance(bits.get_bits32(32)); } } if (tmap_amve_flag) { m_tmap_amve = std::make_shared(); m_tmap_amve->amve.ambient_illumination = bits.get_bits32(32); m_tmap_amve->amve.ambient_light_x = bits.get_bits16(16); m_tmap_amve->amve.ambient_light_y = bits.get_bits16(16); } if (m_tmap_reve_flag) { // TODO: ReferenceViewingEnvironment isn't published yet bits.skip_bits(32); bits.skip_bits(16); bits.skip_bits(16); bits.skip_bits(32); bits.skip_bits(16); bits.skip_bits(16); } if (m_tmap_ndwt_flag) { // TODO: NominalDiffuseWhite isn't published yet bits.skip_bits(32); } } } // Chunk sizes bool few_metadata_bytes_flag = false; if (m_icc_flag || m_exif_flag || m_xmp_flag || (m_hdr_flag && m_gainmap_flag)) { few_metadata_bytes_flag = bits.get_flag(); } bool few_codec_config_bytes_flag = bits.get_flag(); bool few_item_data_bytes_flag = bits.get_flag(); uint32_t icc_data_size = 0; if (m_icc_flag) { icc_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1; } uint32_t tmap_icc_data_size = 0; if (m_hdr_flag && m_gainmap_flag && m_tmap_icc_flag) { tmap_icc_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1; } uint32_t gainmap_metadata_size = 0; if (m_hdr_flag && m_gainmap_flag) { gainmap_metadata_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20); } if (m_hdr_flag && m_gainmap_flag) { m_gainmap_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28); } uint32_t gainmap_item_codec_config_size = 0; if (m_hdr_flag && m_gainmap_flag && (m_gainmap_item_data_size > 0)) { gainmap_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12); } uint32_t main_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12); m_main_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28) + 1; if (m_alpha_flag) { m_alpha_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28); } uint32_t alpha_item_codec_config_size = 0; if (m_alpha_flag && (m_alpha_item_data_size > 0)) { alpha_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12); } if (m_exif_flag) { m_exif_item_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1; } if (m_xmp_flag) { m_xmp_item_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1; } bits.skip_to_byte_boundary(); // Chunks if (m_alpha_flag && (m_alpha_item_data_size > 0) && (alpha_item_codec_config_size > 0)) { m_alpha_item_codec_config = bits.read_bytes(alpha_item_codec_config_size); } if (m_hdr_flag && m_gainmap_flag && (gainmap_item_codec_config_size > 0)) { m_gainmap_item_codec_config = bits.read_bytes(gainmap_item_codec_config_size); } if (main_item_codec_config_size > 0) { m_main_item_codec_config = bits.read_bytes(main_item_codec_config_size); } if (m_icc_flag) { m_icc_data = bits.read_bytes(icc_data_size); } if (m_hdr_flag && m_gainmap_flag && m_tmap_icc_flag) { m_tmap_icc_data = bits.read_bytes(tmap_icc_data_size); } if (m_hdr_flag && m_gainmap_flag && (gainmap_metadata_size > 0)) { m_gainmap_metadata = bits.read_bytes(gainmap_metadata_size); } if (m_alpha_flag && (m_alpha_item_data_size > 0)) { m_alpha_item_data_offset = bits.get_current_byte_index() + start_offset; bits.skip_bytes(m_alpha_item_data_size); } if (m_alpha_flag && m_gainmap_flag && (m_gainmap_item_data_size > 0)) { m_gainmap_item_data_offset = bits.get_current_byte_index() + start_offset; bits.skip_bits(m_gainmap_item_data_size); } m_main_item_data_offset = bits.get_current_byte_index() + start_offset; bits.skip_bytes(m_main_item_data_size); if (m_exif_flag) { m_exif_item_data_offset = bits.get_current_byte_index() + start_offset; bits.skip_bytes(m_exif_item_data_size); } if (m_xmp_flag) { m_xmp_item_data_offset = bits.get_current_byte_index() + start_offset; bits.skip_bytes(m_xmp_item_data_size); } return range.get_error(); } std::string Box_mini::dump(Indent &indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "version: " << (int)m_version << "\n"; sstr << indent << "explicit_codec_types_flag: " << m_explicit_codec_types_flag << "\n"; sstr << indent << "float_flag: " << m_float_flag << "\n"; sstr << indent << "full_range_flag: " << m_full_range_flag << "\n"; sstr << indent << "alpha_flag: " << m_alpha_flag << "\n"; sstr << indent << "explicit_cicp_flag: " << m_explicit_cicp_flag << "\n"; sstr << indent << "hdr_flag: " << m_hdr_flag << "\n"; sstr << indent << "icc_flag: " << m_icc_flag << "\n"; sstr << indent << "exif_flag: " << m_exif_flag << "\n"; sstr << indent << "xmp_flag: " << m_xmp_flag << "\n"; sstr << indent << "chroma_subsampling: " << (int)m_chroma_subsampling << "\n"; sstr << indent << "orientation: " << (int)m_orientation << "\n"; sstr << indent << "width: " << m_width << "\n"; sstr << indent << "height: " << m_height << "\n"; if ((m_chroma_subsampling == 1) || (m_chroma_subsampling == 2)) { sstr << indent << "chroma_is_horizontally_centered: " << m_chroma_is_horizontally_centred << "\n"; } if (m_chroma_subsampling == 1) { sstr << indent << "chroma_is_vertically_centered: " << m_chroma_is_vertically_centred << "\n"; } sstr << "bit_depth: " << (int)m_bit_depth << "\n"; if (m_alpha_flag) { sstr << "alpha_is_premultiplied: " << m_alpha_is_premultiplied << "\n"; } sstr << "colour_primaries: " << (int)m_colour_primaries << "\n"; sstr << "transfer_characteristics: " << (int)m_transfer_characteristics << "\n"; sstr << "matrix_coefficients: " << (int)m_matrix_coefficients << "\n"; if (m_explicit_codec_types_flag) { sstr << "infe_type: " << fourcc_to_string(m_infe_type) << " (" << m_infe_type << ")" << "\n"; sstr << "codec_config_type: " << fourcc_to_string(m_codec_config_type) << " (" << m_codec_config_type << ")" << "\n"; } if (m_hdr_flag) { sstr << indent << "gainmap_flag: " << m_gainmap_flag << "\n"; if (m_gainmap_flag) { sstr << indent << "gainmap_width: " << m_gainmap_width << "\n"; sstr << indent << "gainmap_height: " << m_gainmap_height << "\n"; sstr << indent << "gainmap_matrix_coefficients: " << (int)m_gainmap_matrix_coefficients << "\n"; sstr << indent << "gainmap_full_range_flag: " << m_gainmap_full_range_flag << "\n"; sstr << indent << "gainmap_chroma_subsampling: " << (int)m_gainmap_chroma_subsampling << "\n"; if ((m_gainmap_chroma_subsampling == 1) || (m_gainmap_chroma_subsampling == 2)) { sstr << indent << "gainmap_chroma_is_horizontally_centred: " << m_gainmap_chroma_is_horizontally_centred << "\n"; } if (m_gainmap_chroma_subsampling == 1) { sstr << indent << "gainmap_chroma_is_vertically_centred: " << m_gainmap_chroma_is_vertically_centred << "\n"; } sstr << indent << "gainmap_float_flag: " << m_gainmap_float_flag << "\n"; sstr << "gainmap_bit_depth: " << (int)m_gainmap_bit_depth << "\n"; sstr << indent << "tmap_icc_flag: " << m_tmap_icc_flag << "\n"; sstr << indent << "tmap_explicit_cicp_flag: " << m_tmap_explicit_cicp_flag << "\n"; if (m_tmap_explicit_cicp_flag) { sstr << "tmap_colour_primaries: " << (int)m_tmap_colour_primaries << "\n"; sstr << "tmap_transfer_characteristics: " << (int)m_tmap_transfer_characteristics << "\n"; sstr << "tmap_matrix_coefficients: " << (int)m_tmap_matrix_coefficients << "\n"; sstr << "tmap_full_range_flag: " << m_tmap_full_range_flag << "\n"; } } if (m_clli) { sstr << indent << "ccli.max_content_light_level: " << m_clli->clli.max_content_light_level << "\n"; sstr << indent << "ccli.max_pic_average_light_level: " << m_clli->clli.max_pic_average_light_level << "\n"; } else { sstr << indent << "clli: ---\n"; } if (m_mdcv) { sstr << indent << "mdcv.display_primaries (x,y): "; sstr << "(" << m_mdcv->mdcv.display_primaries_x[0] << ";" << m_mdcv->mdcv.display_primaries_y[0] << "), "; sstr << "(" << m_mdcv->mdcv.display_primaries_x[1] << ";" << m_mdcv->mdcv.display_primaries_y[1] << "), "; sstr << "(" << m_mdcv->mdcv.display_primaries_x[2] << ";" << m_mdcv->mdcv.display_primaries_y[2] << ")\n"; sstr << indent << "mdcv.white point (x,y): (" << m_mdcv->mdcv.white_point_x << ";" << m_mdcv->mdcv.white_point_y << ")\n"; sstr << indent << "mdcv.max display mastering luminance: " << m_mdcv->mdcv.max_display_mastering_luminance << "\n"; sstr << indent << "mdcv.min display mastering luminance: " << m_mdcv->mdcv.min_display_mastering_luminance << "\n"; } else { sstr << indent << "mdcv: ---\n"; } if (m_cclv) { sstr << indent << "cclv.ccv_primaries_present_flag: " << m_cclv->ccv_primaries_are_valid() << "\n"; sstr << indent << "cclv.ccv_min_luminance_value_present_flag: " << m_cclv->min_luminance_is_valid() << "\n"; sstr << indent << "cclv.ccv_max_luminance_value_present_flag: " << m_cclv->max_luminance_is_valid() << "\n"; sstr << indent << "cclv.ccv_avg_luminance_value_present_flag: " << m_cclv->avg_luminance_is_valid() << "\n"; if (m_cclv->ccv_primaries_are_valid()) { sstr << indent << "cclv.ccv_primaries (x,y): "; sstr << "(" << m_cclv->get_ccv_primary_x0() << ";" << m_cclv->get_ccv_primary_y0() << "), "; sstr << "(" << m_cclv->get_ccv_primary_x1() << ";" << m_cclv->get_ccv_primary_y1() << "), "; sstr << "(" << m_cclv->get_ccv_primary_x2() << ";" << m_cclv->get_ccv_primary_y2() << ")\n"; } if (m_cclv->min_luminance_is_valid()) { sstr << indent << "cclv.ccv_min_luminance_value: " << m_cclv->get_min_luminance() << "\n"; } if (m_cclv->max_luminance_is_valid()) { sstr << indent << "cclv.ccv_max_luminance_value: " << m_cclv->get_max_luminance() << "\n"; } if (m_cclv->avg_luminance_is_valid()) { sstr << indent << "cclv.ccv_avg_luminance_value: " << m_cclv->get_avg_luminance() << "\n"; } } else { sstr << indent << "cclv: ---\n"; } if (m_amve) { sstr << indent << "amve.ambient_illumination: " << m_amve->amve.ambient_illumination << "\n"; sstr << indent << "amve.ambient_light_x: " << m_amve->amve.ambient_light_x << "\n"; sstr << indent << "amve.ambient_light_y: " << m_amve->amve.ambient_light_y << "\n"; } else { sstr << indent << "amve: ---\n"; } sstr << indent << "reve_flag: " << m_reve_flag << "\n"; sstr << indent << "ndwt_flag: " << m_ndwt_flag << "\n"; if (m_reve_flag) { // TODO - this isn't published yet } if (m_ndwt_flag) { // TODO - this isn't published yet } if (m_gainmap_flag) { if (m_tmap_clli) { sstr << indent << "tmap_clli.max_content_light_level: " << m_tmap_clli->clli.max_content_light_level << "\n"; sstr << indent << "tmap_clli.max_pic_average_light_level: " << m_tmap_clli->clli.max_pic_average_light_level << "\n"; } else { sstr << indent << "tmap_clli: ---\n"; } if (m_tmap_mdcv) { sstr << indent << "tmap_mdcv.display_primaries (x,y): "; sstr << "(" << m_tmap_mdcv->mdcv.display_primaries_x[0] << ";" << m_tmap_mdcv->mdcv.display_primaries_y[0] << "), "; sstr << "(" << m_tmap_mdcv->mdcv.display_primaries_x[1] << ";" << m_tmap_mdcv->mdcv.display_primaries_y[1] << "), "; sstr << "(" << m_tmap_mdcv->mdcv.display_primaries_x[2] << ";" << m_tmap_mdcv->mdcv.display_primaries_y[2] << ")\n"; sstr << indent << "tmap_mdcv.white point (x,y): (" << m_tmap_mdcv->mdcv.white_point_x << ";" << m_tmap_mdcv->mdcv.white_point_y << ")\n"; sstr << indent << "tmap_mdcv.max display mastering luminance: " << m_tmap_mdcv->mdcv.max_display_mastering_luminance << "\n"; sstr << indent << "tmap_mdcv.min display mastering luminance: " << m_tmap_mdcv->mdcv.min_display_mastering_luminance << "\n"; } else { sstr << indent << "tmap_mdcv: ---\n"; } if (m_tmap_cclv) { sstr << indent << "tmap_cclv.ccv_primaries_present_flag: " << m_tmap_cclv->ccv_primaries_are_valid() << "\n"; sstr << indent << "tmap_cclv.ccv_min_luminance_value_present_flag: " << m_tmap_cclv->min_luminance_is_valid() << "\n"; sstr << indent << "tmap_cclv.ccv_max_luminance_value_present_flag: " << m_tmap_cclv->max_luminance_is_valid() << "\n"; sstr << indent << "tmap_cclv.ccv_avg_luminance_value_present_flag: " << m_tmap_cclv->avg_luminance_is_valid() << "\n"; if (m_tmap_cclv->ccv_primaries_are_valid()) { sstr << indent << "tmap_cclv.ccv_primaries (x,y): "; sstr << "(" << m_tmap_cclv->get_ccv_primary_x0() << ";" << m_tmap_cclv->get_ccv_primary_y0() << "), "; sstr << "(" << m_tmap_cclv->get_ccv_primary_x1() << ";" << m_tmap_cclv->get_ccv_primary_y1() << "), "; sstr << "(" << m_tmap_cclv->get_ccv_primary_x2() << ";" << m_tmap_cclv->get_ccv_primary_y2() << ")\n"; } if (m_tmap_cclv->min_luminance_is_valid()) { sstr << indent << "tmap_cclv.ccv_min_luminance_value: " << m_tmap_cclv->get_min_luminance() << "\n"; } if (m_tmap_cclv->max_luminance_is_valid()) { sstr << indent << "tmap_cclv.ccv_max_luminance_value: " << m_tmap_cclv->get_max_luminance() << "\n"; } if (m_tmap_cclv->avg_luminance_is_valid()) { sstr << indent << "tmap_cclv.ccv_avg_luminance_value: " << m_tmap_cclv->get_avg_luminance() << "\n"; } } else { sstr << indent << "tmap_cclv: ---\n"; } if (m_tmap_amve) { sstr << indent << "tmap_amve.ambient_illumination: " << m_tmap_amve->amve.ambient_illumination << "\n"; sstr << indent << "tmap_amve.ambient_light_x: " << m_tmap_amve->amve.ambient_light_x << "\n"; sstr << indent << "tmap_amve.ambient_light_y: " << m_tmap_amve->amve.ambient_light_y << "\n"; } else { sstr << indent << "tmap_amve: ---\n"; } sstr << indent << "tmap_reve_flag: " << m_tmap_reve_flag << "\n"; sstr << indent << "tmap_ndwt_flag: " << m_tmap_ndwt_flag << "\n"; if (m_tmap_reve_flag) { // TODO - this isn't published yet } if (m_tmap_ndwt_flag) { // TODO - this isn't published yet } } } if (m_alpha_flag && (m_alpha_item_data_size > 0) && (m_alpha_item_codec_config.size() > 0)) { sstr << "alpha_item_code_config size: " << m_alpha_item_codec_config.size() << "\n"; } if (m_hdr_flag && m_gainmap_flag && m_gainmap_item_codec_config.size() > 0) { sstr << "gainmap_item_codec_config size: " << m_gainmap_item_codec_config.size() << "\n"; } if (m_main_item_codec_config.size() > 0) { sstr << "main_item_code_config size: " << m_main_item_codec_config.size() << "\n"; } if (m_icc_flag) { sstr << "icc_data size: " << m_icc_data.size() << "\n"; } if (m_hdr_flag && m_gainmap_flag && m_tmap_icc_flag) { sstr << "tmap_icc_data size: " << m_tmap_icc_data.size() << "\n"; } if (m_hdr_flag && m_gainmap_flag && m_gainmap_metadata.size() > 0) { sstr << "gainmap_metadata size: " << m_gainmap_metadata.size() << "\n"; } if (m_alpha_flag && (m_alpha_item_data_size > 0)) { sstr << "alpha_item_data offset: " << m_alpha_item_data_offset << ", size: " << m_alpha_item_data_size << "\n"; } if (m_hdr_flag && m_gainmap_flag && (m_gainmap_item_data_size > 0)) { sstr << "gainmap_item_data offset: " << m_gainmap_item_data_offset << ", size: " << m_gainmap_item_data_size << "\n"; } sstr << "main_item_data offset: " << m_main_item_data_offset << ", size: " << m_main_item_data_size << "\n"; if (m_exif_flag) { sstr << "exif_data offset: " << m_exif_item_data_offset << ", size: " << m_exif_item_data_size << "\n"; } if (m_xmp_flag) { sstr << "xmp_data offset: " << m_xmp_item_data_offset << ", size: " << m_xmp_item_data_size << "\n"; } return sstr.str(); } static uint32_t get_item_type_for_brand(const heif_brand2 brand) { switch(brand) { case heif_brand2_avif: return fourcc("av01"); case heif_brand2_heic: return fourcc("hvc1"); default: return 0; } } Error Box_mini::create_expanded_boxes(class HeifFile* file) { file->init_meta_box(); auto hdlr_box = std::make_shared(); hdlr_box->set_handler_type(fourcc("pict")); file->set_hdlr_box(hdlr_box); file->set_primary_item_id(1); std::shared_ptr primary_infe_box = std::make_shared(); primary_infe_box->set_version(2); primary_infe_box->set_item_ID(1); // TODO: check explicit codec flag uint32_t minor_version = file->get_ftyp_box()->get_minor_version(); heif_brand2 mini_brand = minor_version; uint32_t infe_type = get_item_type_for_brand(mini_brand); if (infe_type == 0) { // not found std::stringstream sstr; sstr << "Minimised file requires brand " << fourcc_to_string(mini_brand) << " but this is not yet supported."; return Error(heif_error_Unsupported_filetype, heif_suberror_Unspecified, sstr.str()); } primary_infe_box->set_item_type_4cc(infe_type); file->add_infe_box(1, primary_infe_box); if (get_alpha_item_data_size() != 0) { std::shared_ptr alpha_infe_box = std::make_shared(); alpha_infe_box->set_version(2); alpha_infe_box->set_flags(1); alpha_infe_box->set_item_ID(2); alpha_infe_box->set_item_type_4cc(infe_type); file->add_infe_box(2, alpha_infe_box); } if (get_exif_flag()) { std::shared_ptr exif_infe_box = std::make_shared(); exif_infe_box->set_version(2); exif_infe_box->set_flags(1); exif_infe_box->set_item_ID(6); exif_infe_box->set_item_type_4cc(fourcc("Exif")); file->add_infe_box(6, exif_infe_box); } if (get_xmp_flag()) { std::shared_ptr xmp_infe_box = std::make_shared(); xmp_infe_box->set_version(2); xmp_infe_box->set_flags(1); xmp_infe_box->set_item_ID(7); xmp_infe_box->set_item_type_4cc(fourcc("mime")); xmp_infe_box->set_content_type("application/rdf+xml"); file->add_infe_box(7, xmp_infe_box); } auto ipco_box = std::make_shared(); file->set_ipco_box(ipco_box); if (get_main_item_codec_config().size() != 0) { std::shared_ptr istr = std::make_shared( get_main_item_codec_config().data(), get_main_item_codec_config().size(), false ); BitstreamRange codec_range(istr, get_main_item_codec_config().size(), nullptr); std::shared_ptr main_item_codec_prop; if (infe_type == fourcc("av01")) { std::shared_ptr codec_prop = std::make_shared(); codec_prop->parse(codec_range, heif_get_global_security_limits()); main_item_codec_prop = std::move(codec_prop); } else if (infe_type == fourcc("hvc1")) { std::shared_ptr codec_prop = std::make_shared(); codec_prop->parse(codec_range, heif_get_global_security_limits()); main_item_codec_prop = std::move(codec_prop); } else { // not found std::stringstream sstr; sstr << "Minimised file requires infe support for " << fourcc_to_string(infe_type) << " but this is not yet supported."; return Error(heif_error_Unsupported_filetype, heif_suberror_Unspecified, sstr.str()); } ipco_box->append_child_box(main_item_codec_prop); // entry 1 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 1 } std::shared_ptr ispe = std::make_shared(); ispe->set_size(get_width(), get_height()); ipco_box->append_child_box(ispe); // entry 2 std::shared_ptr pixi = std::make_shared(); pixi->set_version(0); // pixi->set_version(1); // TODO: when we support version 1 // TODO: there is more when we do version 1, and anything other than RGB pixi->add_channel_bits(get_bit_depth()); pixi->add_channel_bits(get_bit_depth()); pixi->add_channel_bits(get_bit_depth()); ipco_box->append_child_box(pixi); // entry 3 std::shared_ptr colr = std::make_shared(); std::shared_ptr nclx = std::make_shared(); nclx->set_colour_primaries(get_colour_primaries()); nclx->set_transfer_characteristics(get_transfer_characteristics()); nclx->set_matrix_coefficients(get_matrix_coefficients()); nclx->set_full_range_flag(get_full_range_flag()); colr->set_color_profile(nclx); ipco_box->append_child_box(colr); // entry 4 if (get_icc_flag()) { std::shared_ptr colr_icc = std::make_shared(); std::shared_ptr icc = std::make_shared(fourcc("prof"), get_icc_data()); colr_icc->set_color_profile(icc); ipco_box->append_child_box(colr_icc); // entry 5 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 5 } if (get_alpha_item_codec_config().size() != 0) { std::shared_ptr istr = std::make_shared( get_alpha_item_codec_config().data(), get_alpha_item_codec_config().size(), false ); BitstreamRange alpha_codec_range(istr, get_alpha_item_codec_config().size(), nullptr); std::shared_ptr alpha_item_codec_prop; if (infe_type == fourcc("av01")) { std::shared_ptr codec_prop = std::make_shared(); codec_prop->parse(alpha_codec_range, heif_get_global_security_limits()); alpha_item_codec_prop = std::move(codec_prop); } else if (infe_type == fourcc("hvc1")) { std::shared_ptr codec_prop = std::make_shared(); codec_prop->parse(alpha_codec_range, heif_get_global_security_limits()); alpha_item_codec_prop = std::move(codec_prop); } else { // not found std::stringstream sstr; sstr << "Minimised file requires infe support for " << fourcc_to_string(infe_type) << " but this is not yet supported."; return Error(heif_error_Unsupported_filetype, heif_suberror_Unspecified, sstr.str()); } ipco_box->append_child_box(alpha_item_codec_prop); // entry 6 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 6 } if (get_alpha_item_data_size() != 0) { std::shared_ptr aux_type = std::make_shared(); aux_type->set_aux_type("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); ipco_box->append_child_box(aux_type); // entry 7 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 7 } // TODO: replace this placeholder with pixi box version 1 once that is supported ipco_box->append_child_box(std::make_shared()); // placeholder for entry 8 if (get_orientation() == 2) { std::shared_ptr irot = std::make_shared(); irot->set_rotation_ccw(2 * 90); ipco_box->append_child_box(irot); // entry 9 } else if ((get_orientation() == 4) || (get_orientation() == 6) || (get_orientation() == 7)) { std::shared_ptr irot = std::make_shared(); irot->set_rotation_ccw(1 * 90); ipco_box->append_child_box(irot); // entry 9 } else if (get_orientation() == 5) { std::shared_ptr irot = std::make_shared(); irot->set_rotation_ccw(3 * 90); ipco_box->append_child_box(irot); // entry 9 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 9 } if ((get_orientation() == 1) || (get_orientation() == 6)) { std::shared_ptr imir = std::make_shared(); imir->set_mirror_direction(heif_transform_mirror_direction_horizontal); ipco_box->append_child_box(imir); // entry 10 } else if ((get_orientation() == 3) || (get_orientation() == 4)) { std::shared_ptr imir = std::make_shared(); imir->set_mirror_direction(heif_transform_mirror_direction_vertical); ipco_box->append_child_box(imir); // entry 10 } else { ipco_box->append_child_box(std::make_shared()); // placeholder for entry 10 } auto ipma_box = std::make_shared(); file->set_ipma_box(ipma_box); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(1)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(2)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(3)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(4)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(5)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(9)}); ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(10)}); if (get_alpha_item_data_size() != 0) { ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(6)}); ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(2)}); ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(7)}); ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(8)}); ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(9)}); ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(10)}); } // TODO: will need more once we support HDR / gainmap representation auto iloc_box = std::make_shared(); file->set_iloc_box(iloc_box); Box_iloc::Item main_item; main_item.item_ID = 1; main_item.construction_method = 0; main_item.base_offset = 0; main_item.data_reference_index = 0; Box_iloc::Extent main_item_extent; main_item_extent.offset = get_main_item_data_offset(); main_item_extent.length = get_main_item_data_size(); main_item.extents.push_back(main_item_extent); iloc_box->append_item(main_item); if (get_alpha_item_data_size() != 0) { Box_iloc::Item alpha_item; alpha_item.item_ID = 2; alpha_item.base_offset = 0; alpha_item.data_reference_index = 0; Box_iloc::Extent alpha_item_extent; alpha_item_extent.offset = get_alpha_item_data_offset(); alpha_item_extent.length = get_alpha_item_data_size(); alpha_item.extents.push_back(alpha_item_extent); iloc_box->append_item(alpha_item); } if (get_exif_flag()) { Box_iloc::Item exif_item; exif_item.item_ID = 6; exif_item.base_offset = 0; exif_item.data_reference_index = 0; Box_iloc::Extent exif_item_extent; exif_item_extent.offset = get_exif_item_data_offset(); exif_item_extent.length = get_exif_item_data_size(); exif_item.extents.push_back(exif_item_extent); iloc_box->append_item(exif_item); } if (get_xmp_flag()) { Box_iloc::Item xmp_item; xmp_item.item_ID = 7; xmp_item.base_offset = 0; xmp_item.data_reference_index = 0; Box_iloc::Extent xmp_item_extent; xmp_item_extent.offset = get_xmp_item_data_offset(); xmp_item_extent.length = get_xmp_item_data_size(); xmp_item.extents.push_back(xmp_item_extent); iloc_box->append_item(xmp_item); } auto iref_box = std::make_shared(); file->set_iref_box(iref_box); std::vector to_items = {1}; if (get_alpha_item_data_size() != 0) { iref_box->add_references(2, fourcc("auxl"), to_items); } // TODO: if alpha prem // TODO: if gainmap flag && item 4 // TODO: if gainmap flag && !item 4 if (get_exif_flag()) { iref_box->add_references(6, fourcc("cdsc"), to_items); } if (get_xmp_flag()) { iref_box->add_references(7, fourcc("cdsc"), to_items); } return Error::Ok; } libheif-1.19.8/libheif/codecs/000775 001750 001750 00000000000 15003473472 017163 5ustar00farindkfarindk000000 000000 libheif-1.19.8/libheif/codecs/uncompressed/000775 001750 001750 00000000000 15003473472 021672 5ustar00farindkfarindk000000 000000 libheif-1.19.8/libheif/codecs/uncompressed/decoder_tile_component_interleave.h000664 001750 001750 00000003154 15003473471 030767 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H #define UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H #include "decoder_abstract.h" #include #include class TileComponentInterleaveDecoder : public AbstractDecoder { public: TileComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC) : AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {} Error decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_column, uint32_t tile_row) override; }; #endif // UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H libheif-1.19.8/libheif/codecs/uncompressed/unc_codec.cc000664 001750 001750 00000105144 15003473471 024127 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "unc_codec.h" #include "common_utils.h" #include "context.h" #include "error.h" #include "libheif/heif.h" #include "unc_types.h" #include "unc_boxes.h" #include "decoder_abstract.h" #include "decoder_component_interleave.h" #include "decoder_pixel_interleave.h" #include "decoder_mixed_interleave.h" #include "decoder_row_interleave.h" #include "decoder_tile_component_interleave.h" #include #include #include #include #include "security_limits.h" bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC) { return ((uncC != nullptr) && (uncC->get_version() == 1) && ((uncC->get_profile() == fourcc("rgb3")) || (uncC->get_profile() == fourcc("rgba")) || (uncC->get_profile() == fourcc("abgr")))); } static Error uncompressed_image_type_is_supported(const std::shared_ptr& uncC, const std::shared_ptr& cmpd) { if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) { return Error::Ok; } if (!cmpd) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Missing required cmpd box (no match in uncC box) for uncompressed codec"); } for (Box_uncC::Component component : uncC->get_components()) { uint16_t component_index = component.component_index; uint16_t component_type = cmpd->get_components()[component_index].component_type; if ((component_type > 7) && (component_type != component_type_padded) && (component_type != component_type_filter_array)) { std::stringstream sstr; sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } if ((component.component_bit_depth > 16)) { std::stringstream sstr; sstr << "Uncompressed image with component_bit_depth " << ((int) component.component_bit_depth) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } if (component.component_format != component_format_unsigned) { std::stringstream sstr; sstr << "Uncompressed image with component_format " << ((int) component.component_format) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } if (component.component_align_size > 2) { std::stringstream sstr; sstr << "Uncompressed image with component_align_size " << ((int) component.component_align_size) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } } if ((uncC->get_sampling_type() != sampling_mode_no_subsampling) && (uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_420) ) { std::stringstream sstr; sstr << "Uncompressed sampling_type of " << ((int) uncC->get_sampling_type()) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } if ((uncC->get_interleave_type() != interleave_mode_component) && (uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_mixed) && (uncC->get_interleave_type() != interleave_mode_row) && (uncC->get_interleave_type() != interleave_mode_tile_component) ) { std::stringstream sstr; sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.3 if (uncC->get_sampling_type() == sampling_mode_422) { // We check Y Cb and Cr appear in the chroma test // TODO: error for tile width not multiple of 2 if ((uncC->get_interleave_type() != interleave_mode_component) && (uncC->get_interleave_type() != interleave_mode_mixed) && (uncC->get_interleave_type() != interleave_mode_multi_y)) { std::stringstream sstr; sstr << "YCbCr 4:2:2 subsampling is only valid with component, mixed or multi-Y interleave mode (ISO/IEC 23001-17 5.2.1.5.3)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { if (uncC->get_row_align_size() % 2 != 0) { std::stringstream sstr; sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } } if (uncC->get_tile_align_size() != 0) { if (uncC->get_tile_align_size() % 2 != 0) { std::stringstream sstr; sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } } } // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.4 if (uncC->get_sampling_type() == sampling_mode_422) { // We check Y Cb and Cr appear in the chroma test // TODO: error for tile width not multiple of 2 if ((uncC->get_interleave_type() != interleave_mode_component) && (uncC->get_interleave_type() != interleave_mode_mixed)) { std::stringstream sstr; sstr << "YCbCr 4:2:0 subsampling is only valid with component or mixed interleave mode (ISO/IEC 23001-17 5.2.1.5.4)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { if (uncC->get_row_align_size() % 2 != 0) { std::stringstream sstr; sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.4)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } } if (uncC->get_tile_align_size() != 0) { if (uncC->get_tile_align_size() % 4 != 0) { std::stringstream sstr; sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 4 (ISO/IEC 23001-17 5.2.1.5.3)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } } } if ((uncC->get_interleave_type() == interleave_mode_mixed) && (uncC->get_sampling_type() == sampling_mode_no_subsampling)) { std::stringstream sstr; sstr << "Interleave interleave mode is not valid with subsampling mode (ISO/IEC 23001-17 5.2.1.6.4)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } if ((uncC->get_interleave_type() == interleave_mode_multi_y) && ((uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_411))) { std::stringstream sstr; sstr << "Multi-Y interleave mode is only valid with 4:2:2 and 4:1:1 subsampling modes (ISO/IEC 23001-17 5.2.1.6.7)."; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } // TODO: throw error if mixed and Cb and Cr are not adjacent. if (uncC->get_block_size() != 0) { std::stringstream sstr; sstr << "Uncompressed block_size of " << ((int) uncC->get_block_size()) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } if (uncC->is_components_little_endian()) { const auto& comps = uncC->get_components(); bool all_8_bit = std::all_of(comps.begin(), comps.end(), [](const Box_uncC::Component& c) { return c.component_bit_depth==8; }); if (!all_8_bit) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Uncompressed components_little_endian == 1 is not implemented yet"); } } if (uncC->is_block_pad_lsb()) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Uncompressed block_pad_lsb == 1 is not implemented yet"); } if (uncC->is_block_little_endian()) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Uncompressed block_little_endian == 1 is not implemented yet"); } if (uncC->is_block_reversed()) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Uncompressed block_reversed == 1 is not implemented yet"); } if ((uncC->get_pixel_size() != 0) && ((uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_multi_y))) { std::stringstream sstr; sstr << "Uncompressed pixel_size of " << ((int) uncC->get_pixel_size()) << " is only valid with interleave_type 1 or 5 (ISO/IEC 23001-17 5.2.1.7)"; return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()); } return Error::Ok; } Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr& uncC, const std::shared_ptr& cmpd, heif_chroma* out_chroma, heif_colorspace* out_colourspace, bool* out_has_alpha) { bool dummy_has_alpha; if (out_has_alpha == nullptr) { out_has_alpha = &dummy_has_alpha; } *out_chroma = heif_chroma_undefined; *out_colourspace = heif_colorspace_undefined; *out_has_alpha = false; Error error = check_header_validity(std::nullopt, cmpd, uncC); if (error) { return error; } if (uncC != nullptr && uncC->get_version() == 1) { switch (uncC->get_profile()) { case fourcc("rgb3"): *out_chroma = heif_chroma_444; *out_colourspace = heif_colorspace_RGB; *out_has_alpha = false; return Error::Ok; case fourcc("abgr"): case fourcc("rgba"): *out_chroma = heif_chroma_444; *out_colourspace = heif_colorspace_RGB; *out_has_alpha = true; return Error::Ok; default: return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_image_type, "unci image has unsupported profile"); } } // each 1-bit represents an existing component in the image uint16_t componentSet = 0; for (Box_uncC::Component component : uncC->get_components()) { uint16_t component_index = component.component_index; uint16_t component_type = cmpd->get_components()[component_index].component_type; if (component_type > component_type_max_valid) { std::stringstream sstr; sstr << "a component_type > " << component_type_max_valid << " is not supported"; return {heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()}; } if (component_type == component_type_padded) { // not relevant for determining chroma continue; } componentSet |= (1 << component_type); } *out_has_alpha = (componentSet & (1 << component_type_alpha)) != 0; if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) || componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) { *out_chroma = heif_chroma_444; *out_colourspace = heif_colorspace_RGB; } if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) { switch (uncC->get_sampling_type()) { case sampling_mode_no_subsampling: *out_chroma = heif_chroma_444; break; case sampling_mode_422: *out_chroma = heif_chroma_422; break; case sampling_mode_420: *out_chroma = heif_chroma_420; break; } *out_colourspace = heif_colorspace_YCbCr; } if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) { // mono or mono + alpha input, mono output. *out_chroma = heif_chroma_monochrome; *out_colourspace = heif_colorspace_monochrome; } if (componentSet == (1 << component_type_filter_array)) { // TODO - we should look up the components *out_chroma = heif_chroma_monochrome; *out_colourspace = heif_colorspace_monochrome; } // TODO: more combinations if (*out_chroma == heif_chroma_undefined) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Could not determine chroma"); } else if (*out_colourspace == heif_colorspace_undefined) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Could not determine colourspace"); } else { return Error::Ok; } } bool map_uncompressed_component_to_channel(const std::shared_ptr& cmpd, const std::shared_ptr& uncC, Box_uncC::Component component, heif_channel* channel) { uint16_t component_index = component.component_index; if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) { if (uncC->get_profile() == fourcc("rgb3")) { switch (component_index) { case 0: *channel = heif_channel_R; return true; case 1: *channel = heif_channel_G; return true; case 2: *channel = heif_channel_B; return true; } } else if (uncC->get_profile() == fourcc("rgba")) { switch (component_index) { case 0: *channel = heif_channel_R; return true; case 1: *channel = heif_channel_G; return true; case 2: *channel = heif_channel_B; return true; case 3: *channel = heif_channel_Alpha; return true; } } else if (uncC->get_profile() == fourcc("abgr")) { switch (component_index) { case 0: *channel = heif_channel_Alpha; return true; case 1: *channel = heif_channel_B; return true; case 2: *channel = heif_channel_G; return true; case 3: *channel = heif_channel_R; return true; } } } uint16_t component_type = cmpd->get_components()[component_index].component_type; switch (component_type) { case component_type_monochrome: *channel = heif_channel_Y; return true; case component_type_Y: *channel = heif_channel_Y; return true; case component_type_Cb: *channel = heif_channel_Cb; return true; case component_type_Cr: *channel = heif_channel_Cr; return true; case component_type_red: *channel = heif_channel_R; return true; case component_type_green: *channel = heif_channel_G; return true; case component_type_blue: *channel = heif_channel_B; return true; case component_type_alpha: *channel = heif_channel_Alpha; return true; case component_type_filter_array: // TODO: this is just a temporary hack *channel = heif_channel_Y; return true; case component_type_padded: return false; default: return false; } } static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr& cmpd, const std::shared_ptr& uncC) { switch (uncC->get_interleave_type()) { case interleave_mode_component: return new ComponentInterleaveDecoder(width, height, cmpd, uncC); case interleave_mode_pixel: return new PixelInterleaveDecoder(width, height, cmpd, uncC); case interleave_mode_mixed: return new MixedInterleaveDecoder(width, height, cmpd, uncC); case interleave_mode_row: return new RowInterleaveDecoder(width, height, cmpd, uncC); case interleave_mode_tile_component: return new TileComponentInterleaveDecoder(width, height, cmpd, uncC); default: return nullptr; } } Result> UncompressedImageCodec::create_image(const std::shared_ptr cmpd, const std::shared_ptr uncC, uint32_t width, uint32_t height, const heif_security_limits* limits) { auto img = std::make_shared(); heif_chroma chroma = heif_chroma_undefined; heif_colorspace colourspace = heif_colorspace_undefined; Error error = get_heif_chroma_uncompressed(uncC, cmpd, &chroma, &colourspace, nullptr); if (error) { return error; } img->create(width, height, colourspace, chroma); for (Box_uncC::Component component : uncC->get_components()) { heif_channel channel; if (map_uncompressed_component_to_channel(cmpd, uncC, component, &channel)) { if (img->has_channel(channel)) { return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Cannot generate image with several similar heif_channels."}; } if ((channel == heif_channel_Cb) || (channel == heif_channel_Cr)) { if (auto err = img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth, limits)) { return err; } } else { if (auto err = img->add_plane(channel, width, height, component.component_bit_depth, limits)) { return err; } } } } return img; } Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, uint32_t tile_x0, uint32_t tile_y0) { auto file = context->get_heif_file(); auto image = context->get_image(ID, false); if (!image) { return {heif_error_Invalid_input, heif_suberror_Nonexisting_item_referenced}; } std::shared_ptr ispe = image->get_property(); std::shared_ptr cmpd = image->get_property(); std::shared_ptr uncC = image->get_property(); Error error = check_header_validity(ispe, cmpd, uncC); if (error) { return error; } uint32_t tile_width = ispe->get_width() / uncC->get_number_of_tile_columns(); uint32_t tile_height = ispe->get_height() / uncC->get_number_of_tile_rows(); Result> createImgResult = create_image(cmpd, uncC, tile_width, tile_height, context->get_security_limits()); if (createImgResult.error) { return createImgResult.error; } img = createImgResult.value; AbstractDecoder* decoder = makeDecoder(ispe->get_width(), ispe->get_height(), cmpd, uncC); if (decoder == nullptr) { std::stringstream sstr; sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } decoder->buildChannelList(img); Error result = decoder->decode_tile(context, ID, img, 0, 0, ispe->get_width(), ispe->get_height(), tile_x0, tile_y0); delete decoder; return result; } Error UncompressedImageCodec::check_header_validity(std::optional> ispe, const std::shared_ptr& cmpd, const std::shared_ptr& uncC) { // if we miss a required box, show error if (!uncC) { return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Missing required uncC box for uncompressed codec"}; } if (!cmpd && (uncC->get_version() != 1)) { return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Missing required cmpd or uncC version 1 box for uncompressed codec"}; } if (cmpd) { for (const auto& comp : uncC->get_components()) { if (comp.component_index > cmpd->get_components().size()) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid component index in uncC box"}; } } } if (ispe) { if (!*ispe) { return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Missing required ispe box for uncompressed codec"}; } if (uncC->get_number_of_tile_rows() > (*ispe)->get_height() || uncC->get_number_of_tile_columns() > (*ispe)->get_width()) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "More tiles than pixels in uncC box"}; } if ((*ispe)->get_height() % uncC->get_number_of_tile_rows() != 0 || (*ispe)->get_width() % uncC->get_number_of_tile_columns() != 0) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid tile size (image size not a multiple of the tile size)"}; } } return Error::Ok; } Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img) { // Get the properties for this item // We need: ispe, cmpd, uncC std::vector> item_properties; Error error = context->get_heif_file()->get_properties(ID, item_properties); if (error) { return error; } auto image = context->get_image(ID, false); if (!image) { return {heif_error_Invalid_input, heif_suberror_Nonexisting_item_referenced}; } std::shared_ptr ispe = image->get_property(); std::shared_ptr cmpd = image->get_property(); std::shared_ptr uncC = image->get_property(); error = check_header_validity(ispe, cmpd, uncC); if (error) { return error; } // check if we support the type of image error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO if (error) { return error; } assert(ispe); uint32_t width = ispe->get_width(); uint32_t height = ispe->get_height(); error = check_for_valid_image_size(context->get_security_limits(), width, height); if (error) { return error; } Result> createImgResult = create_image(cmpd, uncC, width, height, context->get_security_limits()); if (createImgResult.error) { return createImgResult.error; } else { img = *createImgResult; } AbstractDecoder* decoder = makeDecoder(width, height, cmpd, uncC); if (decoder == nullptr) { std::stringstream sstr; sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, sstr.str()); } decoder->buildChannelList(img); uint32_t tile_width = width / uncC->get_number_of_tile_columns(); uint32_t tile_height = height / uncC->get_number_of_tile_rows(); for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height) for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) { error = decoder->decode_tile(context, ID, img, tile_x0, tile_y0, width, height, tile_x0 / tile_width, tile_y0 / tile_height); if (error) { delete decoder; return error; } } //Error result = decoder->decode(source_data, img); delete decoder; return Error::Ok; } Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptr& uncC, const std::shared_ptr& image, const heif_unci_image_parameters* parameters) { uint32_t nTileColumns = parameters->image_width / parameters->tile_width; uint32_t nTileRows = parameters->image_height / parameters->tile_height; const heif_colorspace colourspace = image->get_colorspace(); if (colourspace == heif_colorspace_YCbCr) { if (!(image->has_channel(heif_channel_Y) && image->has_channel(heif_channel_Cb) && image->has_channel(heif_channel_Cr))) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Invalid colourspace / channel combination - YCbCr"); } Box_cmpd::Component yComponent = {component_type_Y}; cmpd->add_component(yComponent); Box_cmpd::Component cbComponent = {component_type_Cb}; cmpd->add_component(cbComponent); Box_cmpd::Component crComponent = {component_type_Cr}; cmpd->add_component(crComponent); uint8_t bpp_y = image->get_bits_per_pixel(heif_channel_Y); Box_uncC::Component component0 = {0, bpp_y, component_format_unsigned, 0}; uncC->add_component(component0); uint8_t bpp_cb = image->get_bits_per_pixel(heif_channel_Cb); Box_uncC::Component component1 = {1, bpp_cb, component_format_unsigned, 0}; uncC->add_component(component1); uint8_t bpp_cr = image->get_bits_per_pixel(heif_channel_Cr); Box_uncC::Component component2 = {2, bpp_cr, component_format_unsigned, 0}; uncC->add_component(component2); if (image->get_chroma_format() == heif_chroma_444) { uncC->set_sampling_type(sampling_mode_no_subsampling); } else if (image->get_chroma_format() == heif_chroma_422) { uncC->set_sampling_type(sampling_mode_422); } else if (image->get_chroma_format() == heif_chroma_420) { uncC->set_sampling_type(sampling_mode_420); } else { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Unsupported YCbCr sub-sampling type"); } uncC->set_interleave_type(interleave_mode_component); uncC->set_block_size(0); uncC->set_components_little_endian(false); uncC->set_block_pad_lsb(false); uncC->set_block_little_endian(false); uncC->set_block_reversed(false); uncC->set_pad_unknown(false); uncC->set_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); uncC->set_number_of_tile_columns(nTileColumns); uncC->set_number_of_tile_rows(nTileRows); } else if (colourspace == heif_colorspace_RGB) { if (!((image->get_chroma_format() == heif_chroma_444) || (image->get_chroma_format() == heif_chroma_interleaved_RGB) || (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE))) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Unsupported colourspace / chroma combination - RGB"); } Box_cmpd::Component rComponent = {component_type_red}; cmpd->add_component(rComponent); Box_cmpd::Component gComponent = {component_type_green}; cmpd->add_component(gComponent); Box_cmpd::Component bComponent = {component_type_blue}; cmpd->add_component(bComponent); if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE) || (image->has_channel(heif_channel_Alpha))) { Box_cmpd::Component alphaComponent = {component_type_alpha}; cmpd->add_component(alphaComponent); } if ((image->get_chroma_format() == heif_chroma_interleaved_RGB) || (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) { uncC->set_interleave_type(interleave_mode_pixel); int bpp = image->get_bits_per_pixel(heif_channel_interleaved); uint8_t component_align = 1; if (bpp == 8) { component_align = 0; } else if (bpp > 8) { component_align = 2; } Box_uncC::Component component0 = {0, (uint8_t) (bpp), component_format_unsigned, component_align}; uncC->add_component(component0); Box_uncC::Component component1 = {1, (uint8_t) (bpp), component_format_unsigned, component_align}; uncC->add_component(component1); Box_uncC::Component component2 = {2, (uint8_t) (bpp), component_format_unsigned, component_align}; uncC->add_component(component2); if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) { Box_uncC::Component component3 = { 3, (uint8_t) (bpp), component_format_unsigned, component_align}; uncC->add_component(component3); } } else { uncC->set_interleave_type(interleave_mode_component); int bpp_red = image->get_bits_per_pixel(heif_channel_R); Box_uncC::Component component0 = {0, (uint8_t) (bpp_red), component_format_unsigned, 0}; uncC->add_component(component0); int bpp_green = image->get_bits_per_pixel(heif_channel_G); Box_uncC::Component component1 = {1, (uint8_t) (bpp_green), component_format_unsigned, 0}; uncC->add_component(component1); int bpp_blue = image->get_bits_per_pixel(heif_channel_B); Box_uncC::Component component2 = {2, (uint8_t) (bpp_blue), component_format_unsigned, 0}; uncC->add_component(component2); if (image->has_channel(heif_channel_Alpha)) { int bpp_alpha = image->get_bits_per_pixel(heif_channel_Alpha); Box_uncC::Component component3 = {3, (uint8_t) (bpp_alpha), component_format_unsigned, 0}; uncC->add_component(component3); } } uncC->set_sampling_type(sampling_mode_no_subsampling); uncC->set_block_size(0); if ((image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) { uncC->set_components_little_endian(true); } else { uncC->set_components_little_endian(false); } uncC->set_block_pad_lsb(false); uncC->set_block_little_endian(false); uncC->set_block_reversed(false); uncC->set_pad_unknown(false); uncC->set_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); uncC->set_number_of_tile_columns(nTileColumns); uncC->set_number_of_tile_rows(nTileRows); } else if (colourspace == heif_colorspace_monochrome) { Box_cmpd::Component monoComponent = {component_type_monochrome}; cmpd->add_component(monoComponent); if (image->has_channel(heif_channel_Alpha)) { Box_cmpd::Component alphaComponent = {component_type_alpha}; cmpd->add_component(alphaComponent); } int bpp = image->get_bits_per_pixel(heif_channel_Y); Box_uncC::Component component0 = {0, (uint8_t) (bpp), component_format_unsigned, 0}; uncC->add_component(component0); if (image->has_channel(heif_channel_Alpha)) { bpp = image->get_bits_per_pixel(heif_channel_Alpha); Box_uncC::Component component1 = {1, (uint8_t) (bpp), component_format_unsigned, 0}; uncC->add_component(component1); } uncC->set_sampling_type(sampling_mode_no_subsampling); uncC->set_interleave_type(interleave_mode_component); uncC->set_block_size(0); uncC->set_components_little_endian(false); uncC->set_block_pad_lsb(false); uncC->set_block_little_endian(false); uncC->set_block_reversed(false); uncC->set_pad_unknown(false); uncC->set_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); uncC->set_number_of_tile_columns(nTileColumns); uncC->set_number_of_tile_rows(nTileRows); } else { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Unsupported colourspace"); } return Error::Ok; } libheif-1.19.8/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc000664 001750 001750 00000011505 15003473471 031124 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "decoder_tile_component_interleave.h" #include "context.h" #include "error.h" #include #include #include Error TileComponentInterleaveDecoder::decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_column, uint32_t tile_row) { if (m_tile_width == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_width=0"}; } if (m_tile_height == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_height=0"}; } // --- compute which file range we need to read for the tile std::map channel_tile_size; //uint64_t total_tile_size = 0; for (ChannelListEntry& entry : channelList) { uint32_t bits_per_pixel = entry.bits_per_component_sample; if (entry.component_alignment > 0) { // start at byte boundary //bits_per_row = (bits_per_row + 7) & ~7U; uint32_t bytes_per_component = (bits_per_pixel + 7) / 8; skip_to_alignment(bytes_per_component, entry.component_alignment); bits_per_pixel = bytes_per_component * 8; } uint32_t bytes_per_row; if (m_uncC->get_pixel_size() != 0) { // TODO: does pixel_size apply here? uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; skip_to_alignment(bytes_per_pixel, m_uncC->get_pixel_size()); bytes_per_row = bytes_per_pixel * m_tile_width; } else { bytes_per_row = (bits_per_pixel * m_tile_width + 7) / 8; } skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); uint64_t component_tile_size = bytes_per_row * static_cast(m_tile_height); if (m_uncC->get_tile_align_size() != 0) { skip_to_alignment(component_tile_size, m_uncC->get_tile_align_size()); } channel_tile_size[entry.channel] = component_tile_size; //total_tile_size += component_tile_size; } uint64_t component_start_offset = 0; assert(m_tile_width > 0); assert(m_tile_height > 0); for (ChannelListEntry& entry : channelList) { //processTile(srcBits, tile_y, tile_x, out_x0, out_y0); if (!entry.use_channel) { //uint64_t bytes_per_component = entry.get_bytes_per_tile() * m_uncC->get_number_of_tile_columns() * m_uncC->get_number_of_tile_rows(); //srcBits.skip_bytes((int)bytes_per_component); component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); continue; } // --- read required file range uint32_t tileIdx = tile_column + tile_row * (image_width / m_tile_width); uint64_t tile_start_offset = component_start_offset + channel_tile_size[entry.channel] * tileIdx; std::vector src_data; Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, channel_tile_size[entry.channel], tileIdx, nullptr); //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, channel_tile_size[entry.channel]); if (err) { return err; } UncompressedBitReader srcBits(src_data); srcBits.markTileStart(); for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { srcBits.markRowStart(); uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); srcBits.handleRowAlignment(m_uncC->get_row_align_size()); } srcBits.handleTileAlignment(m_uncC->get_tile_align_size()); component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); } return Error::Ok; } libheif-1.19.8/libheif/codecs/uncompressed/decoder_abstract.cc000664 001750 001750 00000027317 15003473471 025502 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 #include #include #include #include #if ((defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__PGI)) && __GNUC__ < 9) || (defined(__clang__) && __clang_major__ < 10) #include #else #include #endif #include "common_utils.h" #include "context.h" #include "compression.h" #include "error.h" #include "libheif/heif.h" #include "unc_types.h" #include "unc_boxes.h" #include "unc_codec.h" #include "decoder_abstract.h" AbstractDecoder::AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr cmpd, const std::shared_ptr uncC) : m_width(width), m_height(height), m_cmpd(std::move(cmpd)), m_uncC(std::move(uncC)) { m_tile_height = m_height / m_uncC->get_number_of_tile_rows(); m_tile_width = m_width / m_uncC->get_number_of_tile_columns(); assert(m_tile_width > 0); assert(m_tile_height > 0); } void AbstractDecoder::buildChannelList(std::shared_ptr& img) { for (Box_uncC::Component component : m_uncC->get_components()) { ChannelListEntry entry = buildChannelListEntry(component, img); channelList.push_back(entry); } } void AbstractDecoder::memcpy_to_native_endian(uint8_t* dst, uint32_t value, uint32_t bytes_per_sample) { // TODO: this assumes that the file endianness is always big-endian. The endianness flags in the uncC header are not taken into account yet. if (bytes_per_sample==1) { *dst = static_cast(value); return; } else if (std::endian::native == std::endian::big) { for (uint32_t i = 0; i < bytes_per_sample; i++) { dst[bytes_per_sample - 1 - i] = static_cast((value >> (i * 8)) & 0xFF); } } else { for (uint32_t i = 0; i < bytes_per_sample; i++) { dst[i] = static_cast((value >> (i * 8)) & 0xFF); } } } void AbstractDecoder::processComponentSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x) { uint64_t dst_col_number = static_cast(tile_column) * entry.tile_width + tile_x; uint64_t dst_column_offset = dst_col_number * entry.bytes_per_component_sample; int val = srcBits.get_bits(entry.bits_per_component_sample); // get_bits() reads input in big-endian order memcpy_to_native_endian(entry.dst_plane + dst_row_offset + dst_column_offset, val, entry.bytes_per_component_sample); } // Handles the case where a row consists of a single component type // Not valid for Pixel interleave // Not valid for the Cb/Cr channels in Mixed Interleave // Not valid for multi-Y pixel interleave void AbstractDecoder::processComponentRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_row_offset, uint32_t tile_column) { for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { if (entry.component_alignment != 0) { srcBits.skip_to_byte_boundary(); int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; srcBits.skip_bits(numPadBits); } processComponentSample(srcBits, entry, dst_row_offset, tile_column, tile_x); } srcBits.skip_to_byte_boundary(); } void AbstractDecoder::processComponentTileSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_offset, uint32_t tile_x) { uint64_t dst_sample_offset = uint64_t{tile_x} * entry.bytes_per_component_sample; int val = srcBits.get_bits(entry.bits_per_component_sample); memcpy_to_native_endian(entry.dst_plane + dst_offset + dst_sample_offset, val, entry.bytes_per_component_sample); } // Handles the case where a row consists of a single component type // Not valid for Pixel interleave // Not valid for the Cb/Cr channels in Mixed Interleave // Not valid for multi-Y pixel interleave void AbstractDecoder::processComponentTileRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_offset) { for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { if (entry.component_alignment != 0) { srcBits.skip_to_byte_boundary(); int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; srcBits.skip_bits(numPadBits); } processComponentTileSample(srcBits, entry, dst_offset, tile_x); } srcBits.skip_to_byte_boundary(); } AbstractDecoder::ChannelListEntry AbstractDecoder::buildChannelListEntry(Box_uncC::Component component, std::shared_ptr& img) { ChannelListEntry entry; entry.use_channel = map_uncompressed_component_to_channel(m_cmpd, m_uncC, component, &(entry.channel)); entry.dst_plane = img->get_plane(entry.channel, &(entry.dst_plane_stride)); entry.tile_width = m_tile_width; entry.tile_height = m_tile_height; entry.other_chroma_dst_plane_stride = 0; // will be overwritten below if used if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { if (m_uncC->get_sampling_type() == sampling_mode_422) { entry.tile_width /= 2; } else if (m_uncC->get_sampling_type() == sampling_mode_420) { entry.tile_width /= 2; entry.tile_height /= 2; } if (entry.channel == heif_channel_Cb) { entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cr, &(entry.other_chroma_dst_plane_stride)); } else if (entry.channel == heif_channel_Cr) { entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cb, &(entry.other_chroma_dst_plane_stride)); } } entry.bits_per_component_sample = component.component_bit_depth; entry.component_alignment = component.component_align_size; entry.bytes_per_component_sample = (component.component_bit_depth + 7) / 8; entry.bytes_per_tile_row_src = entry.tile_width * entry.bytes_per_component_sample; return entry; } // generic compression and uncompressed, per 23001-17 const Error AbstractDecoder::get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID, std::vector* data, uint64_t range_start_offset, uint64_t range_size, uint32_t tile_idx, const Box_iloc::Item* item) const { auto image = context->get_image(ID, false); if (!image) { return {heif_error_Invalid_input, heif_suberror_Nonexisting_item_referenced}; } // --- get codec configuration std::shared_ptr cmpC_box = image->get_property(); std::shared_ptr icef_box = image->get_property(); if (!cmpC_box) { // assume no generic compression return context->get_heif_file()->append_data_from_iloc(ID, *data, range_start_offset, range_size); } if (icef_box && cmpC_box->get_compressed_unit_type() == heif_cmpC_compressed_unit_type_image_tile) { const auto& units = icef_box->get_units(); if (tile_idx >= units.size()) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "no icef-box entry for tile index"}; } const auto unit = units[tile_idx]; // get all data and decode all std::vector compressed_bytes; Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes, unit.unit_offset, unit.unit_size); if (err) { return err; } // decompress only the unit err = do_decompress_data(cmpC_box, compressed_bytes, data); if (err) { return err; } } else if (icef_box) { // get all data and decode all std::vector compressed_bytes; Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); if (err) { return err; } for (Box_icef::CompressedUnitInfo unit_info : icef_box->get_units()) { auto unit_start = compressed_bytes.begin() + unit_info.unit_offset; auto unit_end = unit_start + unit_info.unit_size; std::vector compressed_unit_data = std::vector(unit_start, unit_end); std::vector uncompressed_unit_data; err = do_decompress_data(cmpC_box, std::move(compressed_unit_data), &uncompressed_unit_data); if (err) { return err; } data->insert(data->end(), uncompressed_unit_data.data(), uncompressed_unit_data.data() + uncompressed_unit_data.size()); } // cut out the range that we actually need memcpy(data->data(), data->data() + range_start_offset, range_size); data->resize(range_size); } else { // get all data and decode all std::vector compressed_bytes; Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); if (err) { return err; } // Decode as a single blob err = do_decompress_data(cmpC_box, compressed_bytes, data); if (err) { return err; } // cut out the range that we actually need memcpy(data->data(), data->data() + range_start_offset, range_size); data->resize(range_size); } return Error::Ok; } const Error AbstractDecoder::do_decompress_data(std::shared_ptr& cmpC_box, std::vector compressed_data, std::vector* data) const { if (cmpC_box->get_compression_type() == fourcc("brot")) { #if HAVE_BROTLI return decompress_brotli(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with brotli compression - not enabled" << std::endl; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_generic_compression_method, sstr.str()); #endif } else if (cmpC_box->get_compression_type() == fourcc("zlib")) { #if HAVE_ZLIB return decompress_zlib(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with zlib compression - not enabled" << std::endl; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_generic_compression_method, sstr.str()); #endif } else if (cmpC_box->get_compression_type() == fourcc("defl")) { #if HAVE_ZLIB return decompress_deflate(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with deflate compression - not enabled" << std::endl; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_generic_compression_method, sstr.str()); #endif } else { std::stringstream sstr; sstr << "cannot decode unci item with unsupported compression type: " << cmpC_box->get_compression_type() << std::endl; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_generic_compression_method, sstr.str()); } } libheif-1.19.8/libheif/codecs/uncompressed/unc_boxes.h000664 001750 001750 00000021415 15003473471 024032 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_UNC_BOXES_H #define LIBHEIF_UNC_BOXES_H #include "box.h" #include "bitstream.h" #include "unc_types.h" #include #include #include #include /** * Component definition (cmpd) box. */ class Box_cmpd : public Box { public: Box_cmpd() { set_short_type(fourcc("cmpd")); } std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; struct Component { uint16_t component_type; std::string component_type_uri; std::string get_component_type_name() const { return get_component_type_name(component_type); } static std::string get_component_type_name(uint16_t type); }; const std::vector& get_components() const { return m_components; } void add_component(const Component& component) { m_components.push_back(component); } protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; std::vector m_components; }; /** * Uncompressed Frame Configuration Box */ class Box_uncC : public FullBox { public: Box_uncC() { set_short_type(fourcc("uncC")); } bool is_essential() const override { return true; } void derive_box_version() override {}; std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; struct Component { uint16_t component_index; uint16_t component_bit_depth; // range [1..256] uint8_t component_format; uint8_t component_align_size; }; const std::vector& get_components() const { return m_components; } void add_component(Component component) { m_components.push_back(component); } uint32_t get_profile() const { return m_profile; } void set_profile(const uint32_t profile) { m_profile = profile; } uint8_t get_sampling_type() const { return m_sampling_type; } void set_sampling_type(const uint8_t sampling_type) { m_sampling_type = sampling_type; } uint8_t get_interleave_type() const { return m_interleave_type; } void set_interleave_type(const uint8_t interleave_type) { m_interleave_type = interleave_type; } uint8_t get_block_size() const { return m_block_size; } void set_block_size(const uint8_t block_size) { m_block_size = block_size; } bool is_components_little_endian() const { return m_components_little_endian; } void set_components_little_endian (const bool components_little_endian) { m_components_little_endian = components_little_endian; } bool is_block_pad_lsb() const { return m_block_pad_lsb; } void set_block_pad_lsb(const bool block_pad_lsb) { m_block_pad_lsb = block_pad_lsb; } bool is_block_little_endian() const { return m_block_little_endian; } void set_block_little_endian(const bool block_little_endian) { m_block_little_endian = block_little_endian; } bool is_block_reversed() const { return m_block_reversed; } void set_block_reversed(const bool block_reversed) { m_block_reversed = block_reversed; } bool is_pad_unknown() const { return m_pad_unknown; } void set_pad_unknown(const bool pad_unknown) { m_pad_unknown = pad_unknown; } uint32_t get_pixel_size() const { return m_pixel_size; } void set_pixel_size(const uint32_t pixel_size) { m_pixel_size = pixel_size; } uint32_t get_row_align_size() const { return m_row_align_size; } void set_row_align_size(const uint32_t row_align_size) { m_row_align_size = row_align_size; } uint32_t get_tile_align_size() const { return m_tile_align_size; } void set_tile_align_size(const uint32_t tile_align_size) { m_tile_align_size = tile_align_size; } uint32_t get_number_of_tile_columns() const { return m_num_tile_cols; } void set_number_of_tile_columns(const uint32_t num_tile_cols) { m_num_tile_cols = num_tile_cols; } uint32_t get_number_of_tile_rows() const { return m_num_tile_rows; } void set_number_of_tile_rows(const uint32_t num_tile_rows) { m_num_tile_rows = num_tile_rows; } uint32_t get_number_of_tiles() const { return m_num_tile_rows * m_num_tile_rows; } uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; uint32_t m_profile = 0; // 0 = not compliant to any profile std::vector m_components; uint8_t m_sampling_type = sampling_mode_no_subsampling; // no subsampling uint8_t m_interleave_type = interleave_mode_pixel; // component interleaving uint8_t m_block_size = 0; bool m_components_little_endian = false; bool m_block_pad_lsb = false; bool m_block_little_endian = false; bool m_block_reversed = false; bool m_pad_unknown = false; uint32_t m_pixel_size = 0; uint32_t m_row_align_size = 0; uint32_t m_tile_align_size = 0; uint32_t m_num_tile_cols = 1; uint32_t m_num_tile_rows = 1; }; enum heif_cmpC_compressed_unit_type { heif_cmpC_compressed_unit_type_full_item = 0, heif_cmpC_compressed_unit_type_image = 1, heif_cmpC_compressed_unit_type_image_tile = 2, heif_cmpC_compressed_unit_type_image_row = 3, heif_cmpC_compressed_unit_type_image_pixel = 4 }; /** * Generic compression configuration box (cmpC). * * This is from ISO/IEC 23001-17 Amd 2. */ class Box_cmpC : public FullBox { public: Box_cmpC() { set_short_type(fourcc("cmpC")); } std::string dump(Indent&) const override; uint32_t get_compression_type() const { return m_compression_type; } heif_cmpC_compressed_unit_type get_compressed_unit_type() const { return m_compressed_unit_type; } void set_compression_type(uint32_t type) { m_compression_type = type; } void set_compressed_unit_type(heif_cmpC_compressed_unit_type type) { m_compressed_unit_type = type; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; uint32_t m_compression_type = 0; heif_cmpC_compressed_unit_type m_compressed_unit_type = heif_cmpC_compressed_unit_type_full_item; }; /** * Generically compressed units item info (icef). * * This describes the units of compressed data for an item. * * The box is from ISO/IEC 23001-17 Amd 2. */ class Box_icef : public FullBox { public: Box_icef() { set_short_type(fourcc("icef")); } struct CompressedUnitInfo { uint64_t unit_offset = 0; uint64_t unit_size = 0; }; const std::vector& get_units() const { return m_unit_infos; } void add_component(const CompressedUnitInfo& unit_info) { m_unit_infos.push_back(unit_info); } void set_component(uint32_t tile_idx, const CompressedUnitInfo& unit_info) { if (tile_idx >= m_unit_infos.size()) { m_unit_infos.resize(tile_idx+1); } m_unit_infos[tile_idx] = unit_info; } std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; std::vector m_unit_infos; private: const uint8_t get_required_offset_code(uint64_t offset) const; const uint8_t get_required_size_code(uint64_t size) const; }; /** * Component pattern definition box (cpat). * * The component pattern is used when representing filter array * data, such as Bayer. It defines the filter mask in the raw * data. * * This is from ISO/IEC 23001-17 Section 6.1.3. */ class Box_cpat : public FullBox { public: Box_cpat() { set_short_type(fourcc("cpat")); } struct PatternComponent { uint32_t component_index; float component_gain; }; uint16_t get_pattern_width() const { return m_pattern_width; } uint16_t get_pattern_height() const { return m_pattern_height; } std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; uint16_t m_pattern_width = 0; uint16_t m_pattern_height = 0; std::vector m_components; }; #endif //LIBHEIF_UNC_BOXES_H libheif-1.19.8/libheif/codecs/uncompressed/decoder_mixed_interleave.h000664 001750 001750 00000003254 15003473471 027057 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_MIXED_INTERLEAVE_H #define UNCI_DECODER_MIXED_INTERLEAVE_H #include "decoder_abstract.h" #include #include class MixedInterleaveDecoder : public AbstractDecoder { public: MixedInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC) : AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {} Error decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) override; void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0); }; #endif // UNCI_DECODER_MIXED_INTERLEAVE_H libheif-1.19.8/libheif/codecs/uncompressed/decoder_row_interleave.h000664 001750 001750 00000003302 15003473471 026552 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_ROW_INTERLEAVE_H #define UNCI_DECODER_ROW_INTERLEAVE_H #include "decoder_abstract.h" #include #include class RowInterleaveDecoder : public AbstractDecoder { public: RowInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC) : AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {} Error decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) override; private: void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0); }; #endif // UNCI_DECODER_ROW_INTERLEAVE_H libheif-1.19.8/libheif/codecs/uncompressed/unc_types.h000664 001750 001750 00000013344 15003473471 024060 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_UNC_TYPES_H #define LIBHEIF_UNC_TYPES_H #include /** * Component type. * * See ISO/IEC 23001-17 Table 1. */ enum heif_uncompressed_component_type { /** * Monochrome component. */ component_type_monochrome = 0, /** * Luma component (Y). */ component_type_Y = 1, /** * Chroma component (Cb / U). */ component_type_Cb = 2, /** * Chroma component (Cr / V). */ component_type_Cr = 3, /** * Red component (R). */ component_type_red = 4, /** * Green component (G). */ component_type_green = 5, /** * Blue component (B). */ component_type_blue = 6, /** * Alpha / transparency component (A). */ component_type_alpha = 7, /** * Depth component (D). */ component_type_depth = 8, /** * Disparity component (Disp). */ component_type_disparity = 9, /** * Palette component (P). * * The {@code component_format} value for this component shall be 0. */ component_type_palette = 10, /** * Filter Array (FA) component such as Bayer, RGBW, etc. */ component_type_filter_array = 11, /** * Padded component (unused bits/bytes). */ component_type_padded = 12, /** * Cyan component (C). */ component_type_cyan = 13, /** * Magenta component (M). */ component_type_magenta = 14, /** * Yellow component (Y). */ component_type_yellow = 15, /** * Key (black) component (K). */ component_type_key_black = 16, /** * Maximum valid component type value. */ component_type_max_valid = component_type_key_black }; /** * HEIF uncompressed component format. * * The binary representation of a component is determined by the * {@code component_bit_depth} and the component format. * * See ISO/IEC 23001-17 Table 2. */ enum heif_uncompressed_component_format { /** * Unsigned integer. * * The component value is an unsigned integer. */ component_format_unsigned = 0, /** * Floating point value. * * The component value is an IEEE 754 binary float. * Valid bit depths for this format are: *

    *
  • 16 (half precision) *
  • 32 (single precision) *
  • 64 (double precision) *
  • 128 (quadruple precision) *
  • 256 (octuple precision) *
*/ component_format_float = 1, /** * Complex value. * * The component value is two IEEE 754 binary float numbers * where the first part is the real part of the value and * the second part is the imaginary part of the value. * * Each part has the same number of bits, which is half * the component bit depth value. Valid bit depths for this * format are: *
    *
  • 32 - each part is 16 bits (half precision) *
  • 64 - each part is 32 bits (single precision) *
  • 128 - each part is 64 bits (double precision) *
  • 256 - each part is 128 bits (quadruple precision) *
*/ component_format_complex = 2, /** * Maximum valid component format identifier. */ component_format_max_valid = component_format_complex }; /** * HEIF uncompressed sampling mode. * * All components in a frame use the same dimensions, or use pre-defined * sampling modes. This is only valid for YCbCr formats. * * See ISO/IEC 23001-17 Table 3. */ enum heif_uncompressed_sampling_mode { /** * No subsampling. */ sampling_mode_no_subsampling = 0, /** * YCbCr 4:2:2 subsampling. * * Y dimensions are the same as the dimensions of the frame. * Cb (U) and Cr (V) have the same height as the frame, but only have * half the width. */ sampling_mode_422 = 1, /** * YCbCr 4:2:0 subsampling. * * Y dimensions are the same as the dimensions of the frame. * Cb (U) and Cr (V) have the half the height and half the width of * the frame. */ sampling_mode_420 = 2, /** * YCbCr 4:1:1 subsampling. * * Y dimensions are the same as the dimensions of the frame. * Cb (U) and Cr (V) have the same height as the frame, but only have * one quarter the width. */ sampling_mode_411 = 3, /** * Maximum valid sampling mode identifier. */ sampling_mode_max_valid = sampling_mode_411 }; /** * HEIF uncompressed interleaving mode. * * See ISO/IEC 23001-17 Table 4. */ enum heif_uncompressed_interleave_mode { /** * Component interleaving. */ interleave_mode_component = 0, /** * Pixel interleaving. */ interleave_mode_pixel = 1, /** * Mixed interleaving. * * This is associated with YCbCr images, with subsampling * and "semi-planar" interleave. */ interleave_mode_mixed = 2, /** * Row interleaving. */ interleave_mode_row = 3, /** * Tile-component interleaving. */ interleave_mode_tile_component = 4, /** * Multi-Y pixel interleaving. * * This is only valid with 4:2:2 and 4:1:1 subsampling. */ interleave_mode_multi_y = 5, /** * Maximum valid interleave mode identifier. */ interleave_mode_max_valid = interleave_mode_multi_y }; #endif //LIBHEIF_UNC_TYPES_H libheif-1.19.8/libheif/codecs/uncompressed/unc_dec.h000664 001750 001750 00000003326 15003473471 023446 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_UNC_DEC_H #define HEIF_UNC_DEC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include #include #include "codecs/decoder.h" class Box_uncC; class Box_cmpd; class Decoder_uncompressed : public Decoder { public: explicit Decoder_uncompressed(const std::shared_ptr& uncC, const std::shared_ptr& cmpd) : m_uncC(uncC), m_cmpd(cmpd) {} heif_compression_format get_compression_format() const override { return heif_compression_uncompressed; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; bool has_alpha_component() const; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_uncC; const std::shared_ptr m_cmpd; }; #endif libheif-1.19.8/libheif/codecs/uncompressed/decoder_row_interleave.cc000664 001750 001750 00000007776 15003473471 026733 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "decoder_row_interleave.h" #include "context.h" #include "error.h" #include #include Error RowInterleaveDecoder::decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) { if (m_tile_width == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: RowInterleaveDecoder tile_width=0"}; } // --- compute which file range we need to read for the tile uint32_t bits_per_row = 0; for (ChannelListEntry& entry : channelList) { uint32_t bits_per_component = entry.bits_per_component_sample; if (entry.component_alignment > 0) { // start at byte boundary bits_per_row = (bits_per_row + 7) & ~7U; uint32_t bytes_per_component = (bits_per_component + 7) / 8; skip_to_alignment(bytes_per_component, entry.component_alignment); bits_per_component = bytes_per_component * 8; } if (m_uncC->get_row_align_size() != 0) { uint32_t bytes_this_row = (bits_per_component * m_tile_width + 7) / 8; skip_to_alignment(bytes_this_row, m_uncC->get_row_align_size()); bits_per_row += bytes_this_row * 8; } else { bits_per_row += bits_per_component * m_tile_width; } bits_per_row = (bits_per_row + 7) & ~7U; } uint32_t bytes_per_row = (bits_per_row + 7) / 8; if (m_uncC->get_row_align_size()) { skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); } uint64_t total_tile_size = 0; total_tile_size += bytes_per_row * static_cast(m_tile_height); if (m_uncC->get_tile_align_size() != 0) { skip_to_alignment(total_tile_size, m_uncC->get_tile_align_size()); } assert(m_tile_width > 0); uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); uint64_t tile_start_offset = total_tile_size * tileIdx; // --- read required file range std::vector src_data; Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); if (err) { return err; } UncompressedBitReader srcBits(src_data); processTile(srcBits, tile_y, tile_x, out_x0, out_y0); return Error::Ok; } void RowInterleaveDecoder::processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { for (ChannelListEntry& entry : channelList) { srcBits.markRowStart(); if (entry.use_channel) { uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); } else { srcBits.skip_bytes(entry.bytes_per_tile_row_src); } srcBits.handleRowAlignment(m_uncC->get_row_align_size()); } } } libheif-1.19.8/libheif/codecs/uncompressed/unc_codec.h000664 001750 001750 00000006442 15003473471 023772 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_UNC_CODEC_H #define LIBHEIF_UNC_CODEC_H #include "pixelimage.h" #include "file.h" #include "context.h" #include #include #include #include #include class HeifContext; bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC); Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptr& uncC, const std::shared_ptr& image, const heif_unci_image_parameters* parameters); bool map_uncompressed_component_to_channel(const std::shared_ptr &cmpd, const std::shared_ptr &uncC, Box_uncC::Component component, heif_channel *channel); class UncompressedImageCodec { public: static Error decode_uncompressed_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img); static Error decode_uncompressed_image_tile(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, uint32_t tile_x0, uint32_t tile_y0); static Error get_heif_chroma_uncompressed(const std::shared_ptr& uncC, const std::shared_ptr& cmpd, heif_chroma* out_chroma, heif_colorspace* out_colourspace, bool* out_has_alpha); static Result> create_image(std::shared_ptr, std::shared_ptr, uint32_t width, uint32_t height, const heif_security_limits* limits); static Error check_header_validity(std::optional>, const std::shared_ptr&, const std::shared_ptr&); }; #endif //LIBHEIF_UNC_CODEC_H libheif-1.19.8/libheif/codecs/uncompressed/decoder_pixel_interleave.cc000664 001750 001750 00000010612 15003473471 027224 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "decoder_pixel_interleave.h" #include "context.h" #include "error.h" #include #include Error PixelInterleaveDecoder::decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) { if (m_tile_width == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: PixelInterleaveDecoder tile_width=0"}; } // --- compute which file range we need to read for the tile uint32_t bits_per_row = 0; for (uint32_t x = 0; x < m_tile_width; x++) { uint32_t bits_per_pixel = 0; for (ChannelListEntry& entry : channelList) { uint32_t bits_per_component = entry.bits_per_component_sample; if (entry.component_alignment > 0) { // start at byte boundary bits_per_row = (bits_per_row + 7) & ~7U; uint32_t bytes_per_component = (bits_per_component + 7) / 8; skip_to_alignment(bytes_per_component, entry.component_alignment); bits_per_component = bytes_per_component * 8; } bits_per_pixel += bits_per_component; } if (m_uncC->get_pixel_size() != 0) { uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; skip_to_alignment(bytes_per_pixel, m_uncC->get_pixel_size()); bits_per_pixel = bytes_per_pixel * 8; } bits_per_row += bits_per_pixel; } uint32_t bytes_per_row = (bits_per_row + 7) / 8; skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); uint64_t total_tile_size = bytes_per_row * static_cast(m_tile_height); if (m_uncC->get_tile_align_size() != 0) { skip_to_alignment(total_tile_size, m_uncC->get_tile_align_size()); } assert(m_tile_width > 0); uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); uint64_t tile_start_offset = total_tile_size * tileIdx; // --- read required file range std::vector src_data; Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); if (err) { return err; } UncompressedBitReader srcBits(src_data); processTile(srcBits, tile_y, tile_x, out_x0, out_y0); return Error::Ok; } void PixelInterleaveDecoder::processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { srcBits.markRowStart(); for (uint32_t tile_x = 0; tile_x < m_tile_width; tile_x++) { srcBits.markPixelStart(); for (ChannelListEntry& entry : channelList) { if (entry.use_channel) { uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); if (entry.component_alignment != 0) { srcBits.skip_to_byte_boundary(); int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; srcBits.skip_bits(numPadBits); } processComponentSample(srcBits, entry, dst_row_offset, 0, out_x0 + tile_x); } else { srcBits.skip_bytes(entry.bytes_per_component_sample); } } srcBits.handlePixelAlignment(m_uncC->get_pixel_size()); } srcBits.handleRowAlignment(m_uncC->get_row_align_size()); } } libheif-1.19.8/libheif/codecs/uncompressed/decoder_pixel_interleave.h000664 001750 001750 00000004053 15003473471 027070 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_PIXEL_INTERLEAVE_H #define UNCI_DECODER_PIXEL_INTERLEAVE_H #include #include #include #include #include #include #include #include "common_utils.h" #include "context.h" #include "compression.h" #include "error.h" #include "libheif/heif.h" #include "unc_types.h" #include "unc_boxes.h" #include "unc_codec.h" #include "unc_dec.h" #include "decoder_abstract.h" #include "decoder_component_interleave.h" #include #include class PixelInterleaveDecoder : public AbstractDecoder { public: PixelInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC) : AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {} Error decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) override; void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0); }; #endif // UNCI_DECODER_PIXEL_INTERLEAVE_H libheif-1.19.8/libheif/codecs/uncompressed/unc_boxes.cc000664 001750 001750 00000057451 15003473471 024201 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 #include #include #include #include #include #include #include "libheif/heif.h" #include "libheif/heif_experimental.h" #include "unc_types.h" #include "unc_boxes.h" /** * Check for valid component format. * * @param format the format value to check * @return true if the format is a valid value, or false otherwise */ bool is_valid_component_format(uint8_t format) { return format <= component_format_max_valid; } static std::map sNames_uncompressed_component_format{ {component_format_unsigned, "unsigned"}, {component_format_float, "float"}, {component_format_complex, "complex"} }; /** * Check for valid interleave mode. * * @param interleave the interleave value to check * @return true if the interleave mode is valid, or false otherwise */ bool is_valid_interleave_mode(uint8_t interleave) { return interleave <= interleave_mode_max_valid; } static std::map sNames_uncompressed_interleave_mode{ {interleave_mode_component, "component"}, {interleave_mode_pixel, "pixel"}, {interleave_mode_mixed, "mixed"}, {interleave_mode_row, "row"}, {interleave_mode_tile_component, "tile-component"}, {interleave_mode_multi_y, "multi-y"} }; /** * Check for valid sampling mode. * * @param sampling the sampling value to check * @return true if the sampling mode is valid, or false otherwise */ bool is_valid_sampling_mode(uint8_t sampling) { return sampling <= sampling_mode_max_valid; } static std::map sNames_uncompressed_sampling_mode{ {sampling_mode_no_subsampling, "no subsampling"}, {sampling_mode_422, "4:2:2"}, {sampling_mode_420, "4:2:0"}, {sampling_mode_411, "4:1:1"} }; bool is_predefined_component_type(uint16_t type) { // check whether the component type can be mapped to heif_uncompressed_component_type and we have a name defined for // it in sNames_uncompressed_component_type. return type <= component_type_max_valid; } static std::map sNames_uncompressed_component_type{ {component_type_monochrome, "monochrome"}, {component_type_Y, "Y"}, {component_type_Cb, "Cb"}, {component_type_Cr, "Cr"}, {component_type_red, "red"}, {component_type_green, "green"}, {component_type_blue, "blue"}, {component_type_alpha, "alpha"}, {component_type_depth, "depth"}, {component_type_disparity, "disparity"}, {component_type_palette, "palette"}, {component_type_filter_array, "filter-array"}, {component_type_padded, "padded"}, {component_type_cyan, "cyan"}, {component_type_magenta, "magenta"}, {component_type_yellow, "yellow"}, {component_type_key_black, "key (black)"} }; template const char* get_name(T val, const std::map& table) { auto iter = table.find(val); if (iter == table.end()) { return "unknown"; } else { return iter->second; } } Error Box_cmpd::parse(BitstreamRange& range, const heif_security_limits* limits) { uint32_t component_count = range.read32(); if (limits->max_components && component_count > limits->max_components) { std::stringstream sstr; sstr << "cmpd box should countain " << component_count << " components, but security limit is set to " << limits->max_components << " components"; return {heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, sstr.str() }; } for (unsigned int i = 0; i < component_count ; i++) { if (range.eof()) { std::stringstream sstr; sstr << "cmpd box should countain " << component_count << " components, but box only contained " << i << " components"; return {heif_error_Invalid_input, heif_suberror_End_of_data, sstr.str() }; } Component component; component.component_type = range.read16(); if (component.component_type >= 0x8000) { component.component_type_uri = range.read_string(); } else { component.component_type_uri = std::string(); } m_components.push_back(component); } return range.get_error(); } std::string Box_cmpd::Component::get_component_type_name(uint16_t component_type) { std::stringstream sstr; if (is_predefined_component_type(component_type)) { sstr << get_name(heif_uncompressed_component_type(component_type), sNames_uncompressed_component_type) << "\n"; } else { sstr << "0x" << std::hex << component_type << std::dec << "\n"; } return sstr.str(); } std::string Box_cmpd::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); for (const auto& component : m_components) { sstr << indent << "component_type: " << component.get_component_type_name(); if (component.component_type >= 0x8000) { sstr << indent << "| component_type_uri: " << component.component_type_uri << "\n"; } } return sstr.str(); } Error Box_cmpd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32((uint32_t) m_components.size()); for (const auto& component : m_components) { writer.write16(component.component_type); if (component.component_type >= 0x8000) { writer.write(component.component_type_uri); } } prepend_header(writer, box_start); return Error::Ok; } Error Box_uncC::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); m_profile = range.read32(); if (get_version() == 1) { if (m_profile == fourcc("rgb3")) { Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; add_component(component0); Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; add_component(component1); Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; add_component(component2); } else if ((m_profile == fourcc("rgba")) || (m_profile == fourcc("abgr"))) { Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; add_component(component0); Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; add_component(component1); Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; add_component(component2); Box_uncC::Component component3 = {3, 8, component_format_unsigned, 0}; add_component(component3); } else { return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; } } else if (get_version() == 0) { uint32_t component_count = range.read32(); if (limits->max_components && component_count > limits->max_components) { std::stringstream sstr; sstr << "Number of image components (" << component_count << ") exceeds security limit (" << limits->max_components << ")"; return {heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, sstr.str()}; } for (uint32_t i = 0; i < component_count && !range.error() && !range.eof(); i++) { Component component; component.component_index = range.read16(); component.component_bit_depth = uint16_t(range.read8() + 1); component.component_format = range.read8(); component.component_align_size = range.read8(); m_components.push_back(component); if (!is_valid_component_format(component.component_format)) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; } } m_sampling_type = range.read8(); if (!is_valid_sampling_mode(m_sampling_type)) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"}; } m_interleave_type = range.read8(); if (!is_valid_interleave_mode(m_interleave_type)) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"}; } m_block_size = range.read8(); uint8_t flags = range.read8(); m_components_little_endian = !!(flags & 0x80); m_block_pad_lsb = !!(flags & 0x40); m_block_little_endian = !!(flags & 0x20); m_block_reversed = !!(flags & 0x10); m_pad_unknown = !!(flags & 0x08); m_pixel_size = range.read32(); m_row_align_size = range.read32(); m_tile_align_size = range.read32(); uint32_t num_tile_cols_minus_one = range.read32(); uint32_t num_tile_rows_minus_one = range.read32(); if (limits->max_number_of_tiles && static_cast(num_tile_cols_minus_one) + 1 > limits->max_number_of_tiles / (static_cast(num_tile_rows_minus_one) + 1)) { std::stringstream sstr; sstr << "Tiling size " << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1) << " exceeds the maximum allowed number " << limits->max_number_of_tiles << " set as security limit"; return {heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded, sstr.str()}; } m_num_tile_cols = num_tile_cols_minus_one + 1; m_num_tile_rows = num_tile_rows_minus_one + 1; } return range.get_error(); } std::string Box_uncC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "profile: " << m_profile; if (m_profile != 0) { sstr << " (" << fourcc_to_string(m_profile) << ")"; } sstr << "\n"; if (get_version() == 0) { for (const auto& component : m_components) { sstr << indent << "component_index: " << component.component_index << "\n"; indent++; sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n"; sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n"; sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n"; indent--; } sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n"; sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n"; sstr << indent << "block_size: " << (int) m_block_size << "\n"; sstr << indent << "components_little_endian: " << m_components_little_endian << "\n"; sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n"; sstr << indent << "block_little_endian: " << m_block_little_endian << "\n"; sstr << indent << "block_reversed: " << m_block_reversed << "\n"; sstr << indent << "pad_unknown: " << m_pad_unknown << "\n"; sstr << indent << "pixel_size: " << m_pixel_size << "\n"; sstr << indent << "row_align_size: " << m_row_align_size << "\n"; sstr << indent << "tile_align_size: " << m_tile_align_size << "\n"; sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n"; sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n"; } return sstr.str(); } Error Box_uncC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(m_profile); if (get_version() == 1) { } else if (get_version() == 0) { writer.write32((uint32_t)m_components.size()); for (const auto &component : m_components) { if (component.component_bit_depth < 1 || component.component_bit_depth > 256) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component bit-depth out of range [1..256]"}; } writer.write16(component.component_index); writer.write8(uint8_t(component.component_bit_depth - 1)); writer.write8(component.component_format); writer.write8(component.component_align_size); } writer.write8(m_sampling_type); writer.write8(m_interleave_type); writer.write8(m_block_size); uint8_t flags = 0; flags |= (m_components_little_endian ? 0x80 : 0); flags |= (m_block_pad_lsb ? 0x40 : 0); flags |= (m_block_little_endian ? 0x20 : 0); flags |= (m_block_reversed ? 0x10 : 0); flags |= (m_pad_unknown ? 0x08 : 0); writer.write8(flags); writer.write32(m_pixel_size); writer.write32(m_row_align_size); writer.write32(m_tile_align_size); writer.write32(m_num_tile_cols - 1); writer.write32(m_num_tile_rows - 1); } prepend_header(writer, box_start); return Error::Ok; } uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const { if (m_profile != 0) { switch (m_profile) { case fourcc("rgba"): return 4 * uint64_t{tile_width} * tile_height; case fourcc("rgb3"): return 3 * uint64_t{tile_width} * tile_height; default: assert(false); return 0; } } switch (m_interleave_type) { case interleave_mode_component: case interleave_mode_pixel: { uint32_t bytes_per_pixel = 0; for (const auto& comp : m_components) { assert(comp.component_bit_depth % 8 == 0); // TODO: component sizes that are no multiples of bytes bytes_per_pixel += comp.component_bit_depth / 8; } return bytes_per_pixel * uint64_t{tile_width} * tile_height; } default: assert(false); return 0; } } Error Box_cmpC::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() != 0) { return unsupported_version_error("cmpC"); } m_compression_type = range.read32(); uint8_t unit_type = range.read8(); if (unit_type > heif_cmpC_compressed_unit_type_image_pixel) { return {heif_error_Invalid_input, heif_suberror_Unsupported_parameter, "Unsupported cmpC compressed unit type"}; }; m_compressed_unit_type = static_cast(unit_type); return range.get_error(); } std::string Box_cmpC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "compression_type: " << fourcc_to_string(m_compression_type) << "\n"; sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; return sstr.str(); } Error Box_cmpC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(m_compression_type); writer.write8(m_compressed_unit_type); prepend_header(writer, box_start); return Error::Ok; } static uint8_t unit_offset_bits_table[] = {0, 16, 24, 32, 64 }; static uint8_t unit_size_bits_table[] = {8, 16, 24, 32, 64 }; Error Box_icef::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() != 0) { return unsupported_version_error("icef"); } uint8_t codes = range.read8(); uint8_t unit_offset_code = (codes & 0b11100000) >> 5; uint8_t unit_size_code = (codes & 0b00011100) >> 2; uint32_t num_compressed_units = range.read32(); uint64_t implied_offset = 0; if (unit_offset_code > 4) { return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit offset code"}; } if (unit_size_code > 4) { return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit size code"}; } // --- precompute fields lengths uint8_t unit_offset_bits = unit_offset_bits_table[unit_offset_code]; uint8_t unit_size_bits = unit_size_bits_table[unit_size_code]; // --- check if box is large enough for all the data uint64_t data_size_bytes = static_cast(num_compressed_units) * (unit_offset_bits + unit_size_bits) / 8; if (data_size_bytes > range.get_remaining_bytes()) { uint64_t contained_units = range.get_remaining_bytes() / ((unit_offset_bits + unit_size_bits) * 8); std::stringstream sstr; sstr << "icef box declares " << num_compressed_units << " units, but only " << contained_units << " were contained in the file"; return {heif_error_Invalid_input, heif_suberror_End_of_data, sstr.str()}; } // TODO: should we impose some security limit? // --- read box content m_unit_infos.resize(num_compressed_units); for (uint32_t r = 0; r < num_compressed_units; r++) { struct CompressedUnitInfo unitInfo; if (unit_offset_code == 0) { unitInfo.unit_offset = implied_offset; } else { unitInfo.unit_offset = range.read_uint(unit_offset_bits); } unitInfo.unit_size = range.read_uint(unit_size_bits); if (unitInfo.unit_size >= UINT64_MAX - implied_offset) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "cumulative offsets too large for 64 bit file size"}; } implied_offset += unitInfo.unit_size; if (range.get_error() != Error::Ok) { return range.get_error(); } m_unit_infos[r] = unitInfo; } return range.get_error(); } std::string Box_icef::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "num_compressed_units: " << m_unit_infos.size() << "\n"; for (CompressedUnitInfo unit_info: m_unit_infos) { sstr << indent << "unit_offset: " << unit_info.unit_offset << ", unit_size: " << unit_info.unit_size << "\n"; } return sstr.str(); } Error Box_icef::write(StreamWriter& writer) const { // check that all units have a non-zero size for (const CompressedUnitInfo& unit_info: m_unit_infos) { if (unit_info.unit_size == 0) { return { heif_error_Usage_error, heif_suberror_Unspecified, "tiled 'unci' image has an undefined tile." }; } } size_t box_start = reserve_box_header_space(writer); uint8_t unit_offset_code = 1; uint8_t unit_size_code = 0; uint64_t implied_offset = 0; bool can_use_implied_offsets = true; for (const CompressedUnitInfo& unit_info: m_unit_infos) { if (unit_info.unit_offset != implied_offset) { can_use_implied_offsets = false; } if (unit_info.unit_size > (std::numeric_limits::max() - implied_offset)) { can_use_implied_offsets = false; } else { implied_offset += unit_info.unit_size; } uint8_t required_offset_code = get_required_offset_code(unit_info.unit_offset); if (required_offset_code > unit_offset_code) { unit_offset_code = required_offset_code; } uint8_t required_size_code = get_required_size_code(unit_info.unit_size); if (required_size_code > unit_size_code) { unit_size_code = required_size_code; } } if (can_use_implied_offsets) { unit_offset_code = 0; } uint8_t code_bits = (uint8_t)((unit_offset_code << 5) | (unit_size_code << 2)); writer.write8(code_bits); writer.write32((uint32_t)m_unit_infos.size()); for (CompressedUnitInfo unit_info: m_unit_infos) { if (unit_offset_code == 0) { // nothing } else if (unit_offset_code == 1) { writer.write16((uint16_t)unit_info.unit_offset); } else if (unit_offset_code == 2) { writer.write24((uint32_t)unit_info.unit_offset); } else if (unit_offset_code == 3) { writer.write32((uint32_t)unit_info.unit_offset); } else { writer.write64(unit_info.unit_offset); } if (unit_size_code == 0) { writer.write8((uint8_t)unit_info.unit_size); } else if (unit_size_code == 1) { writer.write16((uint16_t)unit_info.unit_size); } else if (unit_size_code == 2) { writer.write24((uint32_t)unit_info.unit_size); } else if (unit_size_code == 3) { writer.write32((uint32_t)unit_info.unit_size); } else { writer.write64(unit_info.unit_size); } } prepend_header(writer, box_start); return Error::Ok; } static uint64_t MAX_OFFSET_UNIT_CODE_1 = std::numeric_limits::max(); static uint64_t MAX_OFFSET_UNIT_CODE_2 = (1ULL << 24) - 1; static uint64_t MAX_OFFSET_UNIT_CODE_3 = std::numeric_limits::max(); const uint8_t Box_icef::get_required_offset_code(uint64_t offset) const { if (offset <= MAX_OFFSET_UNIT_CODE_1) { return 1; } if (offset <= MAX_OFFSET_UNIT_CODE_2) { return 2; } if (offset <= MAX_OFFSET_UNIT_CODE_3) { return 3; } return 4; } static uint64_t MAX_SIZE_UNIT_CODE_0 = std::numeric_limits::max(); static uint64_t MAX_SIZE_UNIT_CODE_1 = std::numeric_limits::max(); static uint64_t MAX_SIZE_UNIT_CODE_2 = (1ULL << 24) - 1; static uint64_t MAX_SIZE_UNIT_CODE_3 = std::numeric_limits::max(); const uint8_t Box_icef::get_required_size_code(uint64_t size) const { if (size <= MAX_SIZE_UNIT_CODE_0) { return 0; } if (size <= MAX_SIZE_UNIT_CODE_1) { return 1; } if (size <= MAX_SIZE_UNIT_CODE_2) { return 2; } if (size <= MAX_SIZE_UNIT_CODE_3) { return 3; } return 4; } Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() != 0) { return unsupported_version_error("cpat"); } m_pattern_width = range.read16(); m_pattern_height = range.read16(); if (m_pattern_width == 0 || m_pattern_height == 0) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Zero Bayer pattern size."}; } auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels; if (max_bayer_pattern_size && m_pattern_height > max_bayer_pattern_size / m_pattern_width) { return {heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, "Maximum Bayer pattern size exceeded."}; } m_components.resize(size_t{m_pattern_width} * m_pattern_height); for (uint16_t i = 0; i < m_pattern_height; i++) { for (uint16_t j = 0; j < m_pattern_width; j++) { PatternComponent component{}; component.component_index = range.read32(); component.component_gain = range.read_float32(); m_components[i] = component; } } return range.get_error(); } std::string Box_cpat::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "pattern_width: " << get_pattern_width() << "\n"; sstr << indent << "pattern_height: " << get_pattern_height() << "\n"; for (const auto& component : m_components) { sstr << indent << "component index: " << component.component_index << ", gain: " << component.component_gain << "\n"; } return sstr.str(); } Error Box_cpat::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (m_pattern_width * size_t{m_pattern_height} != m_components.size()) { // needs to be rectangular return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "incorrect number of pattern components"}; } writer.write16(m_pattern_width); writer.write16(m_pattern_height); for (const auto& component : m_components) { writer.write32(component.component_index); writer.write_float32(component.component_gain); } prepend_header(writer, box_start); return Error::Ok; } libheif-1.19.8/libheif/codecs/uncompressed/decoder_component_interleave.h000664 001750 001750 00000003147 15003473471 027754 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_COMPONENT_INTERLEAVE_H #define UNCI_DECODER_COMPONENT_INTERLEAVE_H #include "decoder_abstract.h" #include #include class ComponentInterleaveDecoder : public AbstractDecoder { public: ComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC) : AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {} Error decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) override; }; #endif // UNCI_DECODER_COMPONENT_INTERLEAVE_H libheif-1.19.8/libheif/codecs/uncompressed/decoder_component_interleave.cc000664 001750 001750 00000006671 15003473471 030117 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "decoder_component_interleave.h" #include "context.h" #include "error.h" #include #include Error ComponentInterleaveDecoder::decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) { if (m_tile_width == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: ComponentInterleaveDecoder tile_width=0"}; } // --- compute which file range we need to read for the tile uint64_t total_tile_size = 0; for (ChannelListEntry& entry : channelList) { uint32_t bits_per_component = entry.bits_per_component_sample; if (entry.component_alignment > 0) { uint32_t bytes_per_component = (bits_per_component + 7) / 8; skip_to_alignment(bytes_per_component, entry.component_alignment); bits_per_component = bytes_per_component * 8; } uint32_t bytes_per_tile_row = (bits_per_component * entry.tile_width + 7) / 8; skip_to_alignment(bytes_per_tile_row, m_uncC->get_row_align_size()); uint64_t bytes_per_tile = uint64_t{bytes_per_tile_row} * entry.tile_height; total_tile_size += bytes_per_tile; } if (m_uncC->get_tile_align_size() != 0) { skip_to_alignment(total_tile_size, m_uncC->get_tile_align_size()); } assert(m_tile_width > 0); uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); uint64_t tile_start_offset = total_tile_size * tileIdx; // --- read required file range std::vector src_data; //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); if (err) { return err; } UncompressedBitReader srcBits(src_data); // --- decode tile for (ChannelListEntry& entry : channelList) { for (uint32_t y = 0; y < entry.tile_height; y++) { srcBits.markRowStart(); if (entry.use_channel) { uint64_t dst_row_offset = uint64_t{(out_y0 + y)} * entry.dst_plane_stride; processComponentTileRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample); } else { srcBits.skip_bytes(entry.bytes_per_tile_row_src); } srcBits.handleRowAlignment(m_uncC->get_row_align_size()); } } return Error::Ok; } libheif-1.19.8/libheif/codecs/uncompressed/decoder_abstract.h000664 001750 001750 00000014627 15003473471 025344 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 UNCI_DECODER_ABSTRACT_H #define UNCI_DECODER_ABSTRACT_H #include #include #include #include #include #include #include #include #include #include "common_utils.h" #include "context.h" #include "compression.h" #include "error.h" #include "libheif/heif.h" #include "unc_types.h" #include "unc_boxes.h" #include "unc_codec.h" class UncompressedBitReader : public BitReader { public: UncompressedBitReader(const std::vector& data) : BitReader(data.data(), (int) data.size()) {} void markPixelStart() { m_pixelStartOffset = get_current_byte_index(); } void markRowStart() { m_rowStartOffset = get_current_byte_index(); } void markTileStart() { m_tileStartOffset = get_current_byte_index(); } inline void handlePixelAlignment(uint32_t pixel_size) { if (pixel_size != 0) { uint32_t bytes_in_pixel = get_current_byte_index() - m_pixelStartOffset; uint32_t padding = pixel_size - bytes_in_pixel; skip_bytes(padding); } } void handleRowAlignment(uint32_t alignment) { skip_to_byte_boundary(); if (alignment != 0) { uint32_t bytes_in_row = get_current_byte_index() - m_rowStartOffset; uint32_t residual = bytes_in_row % alignment; if (residual != 0) { uint32_t padding = alignment - residual; skip_bytes(padding); } } } void handleTileAlignment(uint32_t alignment) { if (alignment != 0) { uint32_t bytes_in_tile = get_current_byte_index() - m_tileStartOffset; uint32_t residual = bytes_in_tile % alignment; if (residual != 0) { uint32_t tile_padding = alignment - residual; skip_bytes(tile_padding); } } } private: int m_pixelStartOffset = 0; int m_rowStartOffset = 0; int m_tileStartOffset = 0; }; template void skip_to_alignment(T& position, uint32_t alignment) { if (alignment == 0) { return; } T residual = position % alignment; if (residual == 0) { return; } position += alignment - residual; } class AbstractDecoder { public: virtual ~AbstractDecoder() = default; virtual Error decode_tile(const HeifContext* context, heif_item_id item_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) = 0; void buildChannelList(std::shared_ptr& img); protected: AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr cmpd, const std::shared_ptr uncC); const uint32_t m_width; const uint32_t m_height; const std::shared_ptr m_cmpd; const std::shared_ptr m_uncC; // TODO: see if we can make this const uint32_t m_tile_height; uint32_t m_tile_width; class ChannelListEntry { public: uint32_t get_bytes_per_tile() const { return bytes_per_tile_row_src * tile_height; } inline uint64_t getDestinationRowOffset(uint32_t tile_row, uint32_t tile_y) const { uint64_t dst_row_number = uint64_t{tile_row} * tile_height + tile_y; return dst_row_number * dst_plane_stride; } heif_channel channel = heif_channel_Y; uint8_t* dst_plane = nullptr; uint8_t* other_chroma_dst_plane = nullptr; size_t dst_plane_stride; size_t other_chroma_dst_plane_stride; uint32_t tile_width; uint32_t tile_height; uint32_t bytes_per_component_sample; uint16_t bits_per_component_sample; uint8_t component_alignment; uint32_t bytes_per_tile_row_src; bool use_channel; }; std::vector channelList; void processComponentSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x); // Handles the case where a row consists of a single component type // Not valid for Pixel interleave // Not valid for the Cb/Cr channels in Mixed Interleave // Not valid for multi-Y pixel interleave void processComponentRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_row_offset, uint32_t tile_column); void processComponentTileSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_offset, uint32_t tile_x); // Handles the case where a row consists of a single component type // Not valid for Pixel interleave // Not valid for the Cb/Cr channels in Mixed Interleave // Not valid for multi-Y pixel interleave void processComponentTileRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_offset); // generic compression and uncompressed, per 23001-17 const Error get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID, std::vector* data, uint64_t range_start_offset, uint64_t range_size, uint32_t tile_idx, const Box_iloc::Item* item) const; const Error do_decompress_data(std::shared_ptr& cmpC_box, std::vector compressed_data, std::vector* data) const; protected: void memcpy_to_native_endian(uint8_t* dst, uint32_t value, uint32_t bytes_per_sample); private: ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr& img); }; #endif libheif-1.19.8/libheif/codecs/uncompressed/unc_dec.cc000664 001750 001750 00000011266 15003473471 023606 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "codecs/uncompressed/unc_dec.h" #include "codecs/uncompressed/unc_codec.h" #include "error.h" #include "context.h" #include #include Result> Decoder_uncompressed::read_bitstream_configuration_data() const { return std::vector{}; } int Decoder_uncompressed::get_luma_bits_per_pixel() const { assert(m_uncC); if (!m_cmpd) { if (isKnownUncompressedFrameConfigurationBoxProfile(m_uncC)) { return 8; } else { return -1; } } int luma_bits = 0; int alternate_channel_bits = 0; for (Box_uncC::Component component : m_uncC->get_components()) { uint16_t component_index = component.component_index; if (component_index >= m_cmpd->get_components().size()) { return -1; } auto component_type = m_cmpd->get_components()[component_index].component_type; switch (component_type) { case component_type_monochrome: case component_type_red: case component_type_green: case component_type_blue: case component_type_filter_array: alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth); break; case component_type_Y: luma_bits = std::max(luma_bits, (int) component.component_bit_depth); break; // TODO: there are other things we'll need to handle eventually, like palette. } } if (luma_bits > 0) { return luma_bits; } else if (alternate_channel_bits > 0) { return alternate_channel_bits; } else { return 8; } } int Decoder_uncompressed::get_chroma_bits_per_pixel() const { if (m_uncC && m_uncC->get_version() == 1) { // All of the version 1 cases are 8 bit return 8; } if (!m_uncC || !m_cmpd) { return -1; } int chroma_bits = 0; int alternate_channel_bits = 0; for (Box_uncC::Component component : m_uncC->get_components()) { uint16_t component_index = component.component_index; if (component_index >= m_cmpd->get_components().size()) { return -1; } auto component_type = m_cmpd->get_components()[component_index].component_type; switch (component_type) { case component_type_monochrome: case component_type_red: case component_type_green: case component_type_blue: case component_type_filter_array: alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth); break; case component_type_Cb: case component_type_Cr: chroma_bits = std::max(chroma_bits, (int) component.component_bit_depth); break; // TODO: there are other things we'll need to handle eventually, like palette. } } if (chroma_bits > 0) { return chroma_bits; } else if (alternate_channel_bits > 0) { return alternate_channel_bits; } else { return 8; } } Error Decoder_uncompressed::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { if (m_uncC->get_version() == 1) { // This is the shortform case, no cmpd box, and always some kind of RGB *out_colorspace = heif_colorspace_RGB; if (m_uncC->get_profile() == fourcc("rgb3")) { *out_chroma = heif_chroma_interleaved_RGB; } else if ((m_uncC->get_profile() == fourcc("rgba")) || (m_uncC->get_profile() == fourcc("abgr"))) { *out_chroma = heif_chroma_interleaved_RGBA; } return Error::Ok; } else if (m_cmpd) { UncompressedImageCodec::get_heif_chroma_uncompressed(m_uncC, m_cmpd, out_chroma, out_colorspace, nullptr); return Error::Ok; } else { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Missing 'cmpd' box."}; } } bool Decoder_uncompressed::has_alpha_component() const { heif_colorspace dummy_colorspace; heif_chroma dummy_chroma; bool has_alpha; UncompressedImageCodec::get_heif_chroma_uncompressed(m_uncC, m_cmpd, &dummy_chroma, &dummy_colorspace, &has_alpha); return has_alpha; } libheif-1.19.8/libheif/codecs/uncompressed/decoder_mixed_interleave.cc000664 001750 001750 00000011734 15003473471 027217 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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 "decoder_mixed_interleave.h" #include "context.h" #include "error.h" #include #include #include Error MixedInterleaveDecoder::decode_tile(const HeifContext* context, heif_item_id image_id, std::shared_ptr& img, uint32_t out_x0, uint32_t out_y0, uint32_t image_width, uint32_t image_height, uint32_t tile_x, uint32_t tile_y) { if (m_tile_width == 0) { return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: MixedInterleaveDecoder tile_width=0"}; } // --- compute which file range we need to read for the tile uint64_t tile_size = 0; for (ChannelListEntry& entry : channelList) { if (entry.channel == heif_channel_Cb || entry.channel == heif_channel_Cr) { uint32_t bits_per_row = entry.bits_per_component_sample * entry.tile_width; bits_per_row = (bits_per_row + 7) & ~7U; // align to byte boundary tile_size += uint64_t{bits_per_row} / 8 * entry.tile_height; } else { uint32_t bits_per_component = entry.bits_per_component_sample; if (entry.component_alignment > 0) { uint32_t bytes_per_component = (bits_per_component + 7) / 8; skip_to_alignment(bytes_per_component, entry.component_alignment); bits_per_component = bytes_per_component * 8; } uint32_t bits_per_row = bits_per_component * entry.tile_width; bits_per_row = (bits_per_row + 7) & ~7U; // align to byte boundary tile_size += uint64_t{bits_per_row} / 8 * entry.tile_height; } } if (m_uncC->get_tile_align_size() != 0) { skip_to_alignment(tile_size, m_uncC->get_tile_align_size()); } assert(m_tile_width > 0); uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); uint64_t tile_start_offset = tile_size * tileIdx; // --- read required file range std::vector src_data; Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, tile_size, tileIdx, nullptr); //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, tile_size); if (err) { return err; } UncompressedBitReader srcBits(src_data); processTile(srcBits, tile_y, tile_x, out_x0, out_y0); return Error::Ok; } void MixedInterleaveDecoder::processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { bool haveProcessedChromaForThisTile = false; for (ChannelListEntry& entry : channelList) { if (entry.use_channel) { if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { if (!haveProcessedChromaForThisTile) { for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { // TODO: row padding uint64_t dst_row_number = tile_y + out_y0; uint64_t dst_row_offset = dst_row_number * entry.dst_plane_stride; for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { uint64_t dst_column_number = out_x0 + tile_x; uint64_t dst_column_offset = dst_column_number * entry.bytes_per_component_sample; int val = srcBits.get_bits(entry.bytes_per_component_sample * 8); memcpy_to_native_endian(entry.dst_plane + dst_row_offset + dst_column_offset, val, entry.bytes_per_component_sample); val = srcBits.get_bits(entry.bytes_per_component_sample * 8); memcpy_to_native_endian(entry.other_chroma_dst_plane + dst_row_offset + dst_column_offset, val, entry.bytes_per_component_sample); } haveProcessedChromaForThisTile = true; } } } else { for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { uint64_t dst_row_offset = entry.getDestinationRowOffset(tile_row, tile_y); processComponentRow(entry, srcBits, dst_row_offset, tile_column); } } } else { // skip over the data we are not using srcBits.skip_bytes(entry.get_bytes_per_tile()); continue; } } } libheif-1.19.8/libheif/codecs/jpeg_dec.cc000664 001750 001750 00000010172 15003473471 021232 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "jpeg_dec.h" #include "jpeg_boxes.h" #include "error.h" #include "context.h" #include #include Result> Decoder_JPEG::read_bitstream_configuration_data() const { if (m_jpgC) { return m_jpgC->get_data(); } else { return std::vector{}; } } // This checks whether a start code FFCx with nibble 'x' is a SOF marker. // E.g. FFC0-FFC3 are, while FFC4 is not. static bool isSOF[16] = {true, true, true, true, false, true, true, true, false, true, true, true, false, true, true, true}; Error Decoder_JPEG::parse_SOF() { if (m_config) { return Error::Ok; } // image data, usually from 'mdat' auto dataResult = get_compressed_data(); if (dataResult.error) { return dataResult.error; } const std::vector& data = dataResult.value; const Error error_invalidSOF{heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid JPEG SOF header"}; for (size_t i = 0; i + 1 < data.size(); i++) { if (data[i] == 0xFF && (data[i + 1] & 0xF0) == 0xC0 && isSOF[data[i + 1] & 0x0F]) { if (i + 9 >= data.size()) { return error_invalidSOF; } ConfigInfo info; info.sample_precision = data[i + 4]; info.nComponents = data[i + 9]; if (i + 11 + 3 * info.nComponents >= data.size()) { return error_invalidSOF; } for (int c = 0; c < std::min(info.nComponents, uint8_t(3)); c++) { int ss = data[i + 11 + 3 * c]; info.h_sampling[c] = (ss >> 4) & 0xF; info.v_sampling[c] = ss & 0xF; } if (info.nComponents == 1) { info.chroma = heif_chroma_monochrome; } else if (info.nComponents != 3) { return error_invalidSOF; } else { if (info.h_sampling[1] != info.h_sampling[2] || info.v_sampling[1] != info.v_sampling[2]) { return error_invalidSOF; } if (info.h_sampling[0] == 2 && info.v_sampling[0] == 2 && info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { info.chroma = heif_chroma_420; } else if (info.h_sampling[0] == 2 && info.v_sampling[0] == 1 && info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { info.chroma = heif_chroma_422; } else if (info.h_sampling[0] == 1 && info.v_sampling[0] == 1 && info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { info.chroma = heif_chroma_444; } else { return error_invalidSOF; } } m_config = info; return Error::Ok; } } return error_invalidSOF; } int Decoder_JPEG::get_luma_bits_per_pixel() const { Error err = const_cast(this)->parse_SOF(); if (err) { return -1; } else { return m_config->sample_precision; } } int Decoder_JPEG::get_chroma_bits_per_pixel() const { return get_luma_bits_per_pixel(); } Error Decoder_JPEG::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { Error err = const_cast(this)->parse_SOF(); if (err) { return err; } *out_chroma = m_config->chroma; if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } return Error::Ok; } libheif-1.19.8/libheif/codecs/decoder.h000664 001750 001750 00000005070 15003473471 020742 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_DECODER_H #define HEIF_DECODER_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "image-items/hevc.h" // Specifies the input data for decoding. // For images, this points to the iloc extents. // For sequences, this points to the track data. struct DataExtent { std::shared_ptr m_file; enum class Source : uint8_t { Raw, Image, Sequence } m_source = Source::Raw; // --- raw data mutable std::vector m_raw; // also for cached data // --- image heif_item_id m_item_id = 0; // --- sequence // TODO void set_from_image_item(std::shared_ptr file, heif_item_id item); Result*> read_data() const; Result> read_data(uint64_t offset, uint64_t size) const; }; class Decoder { public: static std::shared_ptr alloc_for_infe_type(const ImageItem* item); virtual ~Decoder() = default; virtual heif_compression_format get_compression_format() const = 0; void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); } // --- information about the image format [[nodiscard]] virtual int get_luma_bits_per_pixel() const = 0; [[nodiscard]] virtual int get_chroma_bits_per_pixel() const = 0; [[nodiscard]] virtual Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const = 0; // --- raw data access [[nodiscard]] virtual Result> read_bitstream_configuration_data() const = 0; Result> get_compressed_data() const; // --- decoding virtual Result> decode_single_frame_from_compressed_data(const struct heif_decoding_options& options); private: DataExtent m_data_extent; }; #endif libheif-1.19.8/libheif/codecs/vvc_boxes.h000664 001750 001750 00000006236 15003473471 021340 0ustar00farindkfarindk000000 000000 /* * HEIF VVC codec. * Copyright (c) 2023 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_VVC_BOXES_H #define LIBHEIF_VVC_BOXES_H #include "box.h" #include #include #include "image-items/image_item.h" #include class Box_vvcC : public FullBox { public: Box_vvcC() { set_short_type(fourcc("vvcC")); } bool is_essential() const override { return true; } struct VvcPTLRecord { uint8_t num_bytes_constraint_info; // 6 bits uint8_t general_profile_idc; // 7 bits uint8_t general_tier_flag; // 1 bit uint8_t general_level_idc; // 8 bits uint8_t ptl_frame_only_constraint_flag; // 1 bit uint8_t ptl_multi_layer_enabled_flag; // 1 bit std::vector general_constraint_info; std::vector ptl_sublayer_level_present_flag; // TODO: should we save this here or can we simply derive it on the fly? std::vector sublayer_level_idc; std::vector general_sub_profile_idc; }; struct configuration { uint8_t LengthSizeMinusOne = 3; // 0,1,3 default: 4 bytes for NAL unit lengths bool ptl_present_flag = true; // only of PTL present uint16_t ols_idx; // 9 bits uint8_t num_sublayers; // 3 bits uint8_t constant_frame_rate; // 2 bits uint8_t chroma_format_idc; // 2 bits uint8_t bit_depth_minus8; // 3 bits struct VvcPTLRecord native_ptl; uint16_t max_picture_width; uint16_t max_picture_height; uint16_t avg_frame_rate; }; std::string dump(Indent&) const override; bool get_headers(std::vector* dest) const; void set_configuration(const configuration& config) { m_configuration = config; } const configuration& get_configuration() const { return m_configuration; } void append_nal_data(const std::vector& nal); void append_nal_data(const uint8_t* data, size_t size); Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: struct NalArray { bool m_array_completeness; uint8_t m_NAL_unit_type; std::vector > m_nal_units; // only one NAL item for DCI and OPI }; configuration m_configuration; std::vector m_nal_array; }; Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, Box_vvcC::configuration* inout_config, int* width, int* height); #endif // LIBHEIF_VVC_BOXES_H libheif-1.19.8/libheif/codecs/vvc_dec.h000664 001750 001750 00000002773 15003473471 020755 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_VVC_DEC_H #define HEIF_VVC_DEC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include #include #include class Box_vvcC; class Decoder_VVC : public Decoder { public: explicit Decoder_VVC(const std::shared_ptr& vvcC) : m_vvcC(vvcC) {} heif_compression_format get_compression_format() const override { return heif_compression_VVC; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_vvcC; }; #endif libheif-1.19.8/libheif/codecs/jpeg_dec.h000664 001750 001750 00000003472 15003473471 021101 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_JPEG_DEC_H #define HEIF_JPEG_DEC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "codecs/decoder.h" #include #include #include class Box_jpgC; class Decoder_JPEG : public Decoder { public: explicit Decoder_JPEG(const std::shared_ptr& jpgC) : m_jpgC(jpgC) {} heif_compression_format get_compression_format() const override { return heif_compression_JPEG; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_jpgC; // Optional jpgC box. May be NULL. struct ConfigInfo { uint8_t sample_precision = 0; heif_chroma chroma = heif_chroma_undefined; uint8_t nComponents = 0; uint8_t h_sampling[3]{}; uint8_t v_sampling[3]{}; }; std::optional m_config; Error parse_SOF(); }; #endif libheif-1.19.8/libheif/codecs/avc_boxes.h000664 001750 001750 00000004755 15003473471 021317 0ustar00farindkfarindk000000 000000 /* * HEIF AVC codec. * Copyright (c) 2023 Brad Hards * * 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 HEIF_AVC_BOXES_H #define HEIF_AVC_BOXES_H #include "box.h" #include "error.h" #include #include #include #include #include "image-items/image_item.h" class Box_avcC : public Box { public: Box_avcC() { set_short_type(fourcc("avcC")); } bool is_essential() const override { return true; } struct configuration { uint8_t configuration_version = 0; uint8_t AVCProfileIndication = 0; // profile_idc uint8_t profile_compatibility = 0; // constraint set flags uint8_t AVCLevelIndication = 0; // level_idc uint8_t lengthSize = 0; heif_chroma chroma_format = heif_chroma_420; // Note: avcC integer value can be cast to heif_chroma enum uint8_t bit_depth_luma = 8; uint8_t bit_depth_chroma = 8; }; void set_configuration(const configuration& config) { m_configuration = config; } const configuration& get_configuration() const { return m_configuration; } const std::vector< std::vector > getSequenceParameterSets() const { return m_sps; } const std::vector< std::vector > getPictureParameterSets() const { return m_pps; } const std::vector< std::vector > getSequenceParameterSetExt() const { return m_sps_ext; } void get_header_nals(std::vector& data) const; std::string dump(Indent &) const override; Error write(StreamWriter &writer) const override; protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; std::string profileIndicationAsText() const; private: configuration m_configuration; std::vector< std::vector > m_sps; std::vector< std::vector > m_pps; std::vector< std::vector > m_sps_ext; }; #endif libheif-1.19.8/libheif/codecs/vvc_dec.cc000664 001750 001750 00000003556 15003473471 021113 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "vvc_dec.h" #include "vvc_boxes.h" #include "error.h" #include "context.h" #include Result> Decoder_VVC::read_bitstream_configuration_data() const { std::vector data; if (!m_vvcC->get_headers(&data)) { return Error{heif_error_Invalid_input, heif_suberror_No_item_data}; } return data; } int Decoder_VVC::get_luma_bits_per_pixel() const { const Box_vvcC::configuration& config = m_vvcC->get_configuration(); if (config.ptl_present_flag) { return config.bit_depth_minus8 + 8; } else { return 8; // TODO: what shall we do if the bit-depth is unknown? Use PIXI? } } int Decoder_VVC::get_chroma_bits_per_pixel() const { return get_luma_bits_per_pixel(); } Error Decoder_VVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { *out_chroma = (heif_chroma) (m_vvcC->get_configuration().chroma_format_idc); if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } return Error::Ok; } libheif-1.19.8/libheif/codecs/decoder.cc000664 001750 001750 00000014071 15003473471 021101 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "codecs/decoder.h" #include #include "error.h" #include "context.h" #include "plugin_registry.h" #include "libheif/api_structs.h" #include "codecs/hevc_dec.h" #include "codecs/avif_dec.h" #include "codecs/avc_dec.h" #include "codecs/vvc_dec.h" #include "codecs/jpeg_dec.h" #include "codecs/jpeg2000_dec.h" #include "avc_boxes.h" #include "avif_boxes.h" #include "hevc_boxes.h" #include "vvc_boxes.h" #include "jpeg_boxes.h" #include "jpeg2000_boxes.h" #include "codecs/uncompressed/unc_dec.h" void DataExtent::set_from_image_item(std::shared_ptr file, heif_item_id item) { m_file = std::move(file); m_item_id = item; m_source = Source::Image; } Result*> DataExtent::read_data() const { if (!m_raw.empty()) { return &m_raw; } else if (m_source == Source::Image) { assert(m_file); // image Error err = m_file->append_data_from_iloc(m_item_id, m_raw); if (err) { return err; } } else { // sequence assert(false); // TODO } return &m_raw; } Result> DataExtent::read_data(uint64_t offset, uint64_t size) const { std::vector data; if (!m_raw.empty()) { data.insert(data.begin(), m_raw.begin() + offset, m_raw.begin() + offset + size); return data; } else if (m_source == Source::Image) { // TODO: cache data // image Error err = m_file->append_data_from_iloc(m_item_id, m_raw, 0, size); if (err) { return err; } return data; } else { // sequence assert(false); // TODO return Error::Ok; } } std::shared_ptr Decoder::alloc_for_infe_type(const ImageItem* item) { uint32_t format_4cc = item->get_infe_type(); switch (format_4cc) { case fourcc("hvc1"): { auto hvcC = item->get_property(); return std::make_shared(hvcC); } case fourcc("av01"): { auto av1C = item->get_property(); return std::make_shared(av1C); } case fourcc("avc1"): { auto avcC = item->get_property(); return std::make_shared(avcC); } case fourcc("j2k1"): { auto j2kH = item->get_property(); return std::make_shared(j2kH); } case fourcc("vvc1"): { auto vvcC = item->get_property(); return std::make_shared(vvcC); } case fourcc("jpeg"): { auto jpgC = item->get_property(); return std::make_shared(jpgC); } #if WITH_UNCOMPRESSED_CODEC case fourcc("unci"): { auto uncC = item->get_property(); auto cmpd = item->get_property(); return std::make_shared(uncC,cmpd); } #endif case fourcc("mski"): { return nullptr; // do we need a decoder for this? } default: return nullptr; } } Result> Decoder::get_compressed_data() const { // --- get the compressed image data // data from configuration blocks Result> confData = read_bitstream_configuration_data(); if (confData.error) { return confData.error; } std::vector data = confData.value; // append image data Result dataResult = m_data_extent.read_data(); if (dataResult.error) { return dataResult.error; } data.insert(data.end(), dataResult.value->begin(), dataResult.value->end()); return data; } Result> Decoder::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options) { const struct heif_decoder_plugin* decoder_plugin = get_decoder(get_compression_format(), options.decoder_id); if (!decoder_plugin) { return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed); } // --- decode image with the plugin if (decoder_plugin->new_decoder == nullptr) { return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed, "Cannot decode with a dummy decoder plugins."); } void* decoder; struct heif_error err = decoder_plugin->new_decoder(&decoder); if (err.code != heif_error_Ok) { return Error(err.code, err.subcode, err.message); } // automatically delete decoder plugin when we leave the scope std::unique_ptr decoderSmartPtr(decoder, decoder_plugin->free_decoder); if (decoder_plugin->plugin_api_version >= 2) { if (decoder_plugin->set_strict_decoding) { decoder_plugin->set_strict_decoding(decoder, options.strict_decoding); } } auto dataResult = get_compressed_data(); if (dataResult.error) { return dataResult.error; } err = decoder_plugin->push_data(decoder, dataResult.value.data(), dataResult.value.size()); if (err.code != heif_error_Ok) { return Error(err.code, err.subcode, err.message); } heif_image* decoded_img = nullptr; err = decoder_plugin->decode_image(decoder, &decoded_img); if (err.code != heif_error_Ok) { return Error(err.code, err.subcode, err.message); } if (!decoded_img) { // TODO(farindk): The plugin should return an error in this case. return Error(heif_error_Decoder_plugin_error, heif_suberror_Unspecified); } // -- cleanup std::shared_ptr img = std::move(decoded_img->image); heif_image_release(decoded_img); return img; } libheif-1.19.8/libheif/codecs/hevc_dec.cc000664 001750 001750 00000003310 15003473471 021226 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "hevc_dec.h" #include "hevc_boxes.h" #include "error.h" #include "context.h" #include Result> Decoder_HEVC::read_bitstream_configuration_data() const { std::vector data; if (!m_hvcC->get_headers(&data)) { return Error{heif_error_Invalid_input, heif_suberror_No_item_data}; } return data; } int Decoder_HEVC::get_luma_bits_per_pixel() const { return m_hvcC->get_configuration().bit_depth_luma; } int Decoder_HEVC::get_chroma_bits_per_pixel() const { return m_hvcC->get_configuration().bit_depth_chroma; } Error Decoder_HEVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { *out_chroma = (heif_chroma) (m_hvcC->get_configuration().chroma_format); if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } return Error::Ok; } libheif-1.19.8/libheif/codecs/hevc_dec.h000664 001750 001750 00000003000 15003473471 021064 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_HEVC_DEC_H #define HEIF_HEVC_DEC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include #include #include class Box_hvcC; class Decoder_HEVC : public Decoder { public: explicit Decoder_HEVC(const std::shared_ptr& hvcC) : m_hvcC(hvcC) {} heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_hvcC; }; #endif libheif-1.19.8/libheif/codecs/jpeg2000_boxes.cc000664 001750 001750 00000034600 15003473471 022123 0ustar00farindkfarindk000000 000000 /* * HEIF JPEG 2000 codec. * Copyright (c) 2023 Brad Hards * * 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 "jpeg2000_boxes.h" #include "libheif/api_structs.h" #include #include #include static const uint16_t JPEG2000_CAP_MARKER = 0xFF50; static const uint16_t JPEG2000_SIZ_MARKER = 0xFF51; static const uint16_t JPEG2000_SOC_MARKER = 0xFF4F; Error Box_cdef::parse(BitstreamRange& range, const heif_security_limits* limits) { uint16_t channel_count = range.read16(); if (limits->max_components && channel_count > limits->max_components) { std::stringstream sstr; sstr << "cdef box wants to define " << channel_count << " JPEG-2000 channels, but the security limit is set to " << limits->max_components << " components"; return {heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, sstr.str()}; } if (channel_count > range.get_remaining_bytes() / 6) { std::stringstream sstr; sstr << "cdef box wants to define " << channel_count << " JPEG-2000 channels, but file only contains " << range.get_remaining_bytes() / 6 << " components"; return {heif_error_Invalid_input, heif_suberror_End_of_data, sstr.str()}; } m_channels.resize(channel_count); for (uint16_t i = 0; i < channel_count && !range.error() && !range.eof(); i++) { Channel channel; channel.channel_index = range.read16(); channel.channel_type = range.read16(); channel.channel_association = range.read16(); m_channels[i] = channel; } return range.get_error(); } std::string Box_cdef::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); for (const auto& channel : m_channels) { sstr << indent << "channel_index: " << channel.channel_index << ", channel_type: " << channel.channel_type << ", channel_association: " << channel.channel_association << "\n"; } return sstr.str(); } Error Box_cdef::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write16((uint16_t) m_channels.size()); for (const auto& channel : m_channels) { writer.write16(channel.channel_index); writer.write16(channel.channel_type); writer.write16(channel.channel_association); } prepend_header(writer, box_start); return Error::Ok; } void Box_cdef::set_channels(heif_colorspace colorspace) { // TODO - Check for the presence of a cmap box which specifies channel indices. const uint16_t TYPE_COLOR = 0; const uint16_t ASOC_GREY = 1; const uint16_t ASOC_RED = 1; const uint16_t ASOC_GREEN = 2; const uint16_t ASOC_BLUE = 3; const uint16_t ASOC_Y = 1; const uint16_t ASOC_Cb = 2; const uint16_t ASOC_Cr = 3; switch (colorspace) { case heif_colorspace_RGB: m_channels.push_back({0, TYPE_COLOR, ASOC_RED}); m_channels.push_back({1, TYPE_COLOR, ASOC_GREEN}); m_channels.push_back({2, TYPE_COLOR, ASOC_BLUE}); break; case heif_colorspace_YCbCr: m_channels.push_back({0, TYPE_COLOR, ASOC_Y}); m_channels.push_back({1, TYPE_COLOR, ASOC_Cb}); m_channels.push_back({2, TYPE_COLOR, ASOC_Cr}); break; case heif_colorspace_monochrome: m_channels.push_back({0, TYPE_COLOR, ASOC_GREY}); break; default: //TODO - Handle remaining cases. break; } } Error Box_cmap::parse(BitstreamRange& range, const heif_security_limits* limits) { while (!range.eof() && !range.error()) { Component component; component.component_index = range.read16(); component.mapping_type = range.read8(); component.palette_colour = range.read8(); m_components.push_back(component); } return range.get_error(); } std::string Box_cmap::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); for (const auto& component : m_components) { sstr << indent << "component_index: " << component.component_index << ", mapping_type: " << (int) (component.mapping_type) << ", palette_colour: " << (int) (component.palette_colour) << "\n"; } return sstr.str(); } Error Box_cmap::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); for (const auto& component : m_components) { writer.write16(component.component_index); writer.write8(component.mapping_type); writer.write8(component.palette_colour); } prepend_header(writer, box_start); return Error::Ok; } Error Box_pclr::parse(BitstreamRange& range, const heif_security_limits* limits) { uint16_t num_entries = range.read16(); uint8_t num_palette_columns = range.read8(); for (uint8_t i = 0; i < num_palette_columns; i++) { uint8_t bit_depth = range.read8(); if (bit_depth & 0x80) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "pclr with signed data is not supported"); } if (bit_depth > 16) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "pclr more than 16 bits per channel is not supported"); } m_bitDepths.push_back(bit_depth); } for (uint16_t j = 0; j < num_entries; j++) { PaletteEntry entry; for (unsigned long int i = 0; i < entry.columns.size(); i++) { if (m_bitDepths[i] <= 8) { entry.columns.push_back(range.read8()); } else { entry.columns.push_back(range.read16()); } } m_entries.push_back(entry); } return range.get_error(); } std::string Box_pclr::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "NE: " << m_entries.size(); sstr << ", NPC: " << (int) get_num_columns(); sstr << ", B: "; for (uint8_t b : m_bitDepths) { sstr << (int) b << ", "; } // TODO: maybe dump entries too? sstr << "\n"; return sstr.str(); } Error Box_pclr::write(StreamWriter& writer) const { if (get_num_columns() == 0) { // skip return Error::Ok; } size_t box_start = reserve_box_header_space(writer); writer.write16(get_num_entries()); writer.write8(get_num_columns()); for (uint8_t b : m_bitDepths) { writer.write8(b); } for (PaletteEntry entry : m_entries) { for (unsigned long int i = 0; i < entry.columns.size(); i++) { if (m_bitDepths[i] <= 8) { writer.write8((uint8_t) (entry.columns[i])); } else { writer.write16(entry.columns[i]); } } } prepend_header(writer, box_start); return Error::Ok; } void Box_pclr::set_columns(uint8_t num_columns, uint8_t bit_depth) { m_bitDepths.clear(); m_entries.clear(); for (int i = 0; i < num_columns; i++) { m_bitDepths.push_back(bit_depth); } } Error Box_j2kL::parse(BitstreamRange& range, const heif_security_limits* limits) { uint16_t layer_count = range.read16(); if (layer_count > range.get_remaining_bytes() / (2+1+2)) { std::stringstream sstr; sstr << "j2kL box wants to define " << layer_count << "JPEG-2000 layers, but the box only contains " << range.get_remaining_bytes() / (2 + 1 + 2) << " layers entries"; return {heif_error_Invalid_input, heif_suberror_End_of_data, sstr.str()}; } m_layers.resize(layer_count); for (int i = 0; i < layer_count && !range.error() && !range.eof(); i++) { Layer layer; layer.layer_id = range.read16(); layer.discard_levels = range.read8(); layer.decode_layers = range.read16(); m_layers[i] = layer; } if (range.get_error()) { m_layers.clear(); } return range.get_error(); } std::string Box_j2kL::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); for (const auto& layer : m_layers) { sstr << indent << "layer_id: " << layer.layer_id << ", discard_levels: " << (int) (layer.discard_levels) << ", decode_layers: " << layer.decode_layers << "\n"; } return sstr.str(); } Error Box_j2kL::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write16((uint16_t) m_layers.size()); for (const auto& layer : m_layers) { writer.write16(layer.layer_id); writer.write8(layer.discard_levels); writer.write16(layer.decode_layers); } prepend_header(writer, box_start); return Error::Ok; } Error Box_j2kH::parse(BitstreamRange& range, const heif_security_limits* limits) { return read_children(range, READ_CHILDREN_ALL, limits); } std::string Box_j2kH::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << dump_children(indent); return sstr.str(); } Error JPEG2000MainHeader::parseHeader(const std::vector& compressedImageData) { // TODO: it is very inefficient to store the whole image data when we only need the header headerData = compressedImageData; return doParse(); } Error JPEG2000MainHeader::doParse() { cursor = 0; Error err = parse_SOC_segment(); if (err) { return err; } err = parse_SIZ_segment(); if (err) { return err; } if (cursor < headerData.size() - MARKER_LEN) { uint16_t marker = read16(); if (marker == JPEG2000_CAP_MARKER) { return parse_CAP_segment_body(); } return Error::Ok; } // we should have at least COD and QCD, so this is probably broken. return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Missing required header marker(s)")); } Error JPEG2000MainHeader::parse_SOC_segment() { const size_t REQUIRED_BYTES = MARKER_LEN; if ((headerData.size() < REQUIRED_BYTES) || (cursor > (headerData.size() - REQUIRED_BYTES))) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream); } uint16_t marker = read16(); if (marker == JPEG2000_SOC_MARKER) { return Error::Ok; } return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Missing required SOC Marker")); } Error JPEG2000MainHeader::parse_SIZ_segment() { size_t REQUIRED_BYTES = MARKER_LEN + 38 + 3 * 1; if ((headerData.size() < REQUIRED_BYTES) || (cursor > (headerData.size() - REQUIRED_BYTES))) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream); } uint16_t marker = read16(); if (marker != JPEG2000_SIZ_MARKER) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Missing required SIZ Marker")); } uint16_t lsiz = read16(); if ((lsiz < 41) || (lsiz > 49190)) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Out of range Lsiz value")); } siz.decoder_capabilities = read16(); siz.reference_grid_width = read32(); siz.reference_grid_height = read32(); siz.image_horizontal_offset = read32(); siz.image_vertical_offset = read32(); siz.tile_width = read32(); siz.tile_height = read32(); siz.tile_offset_x = read32(); siz.tile_offset_y = read32(); uint16_t csiz = read16(); if ((csiz < 1) || (csiz > 16384)) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Out of range Csiz value")); } if (cursor > headerData.size() - (3 * csiz)) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream); } // TODO: consider checking for Lsiz consistent with Csiz for (uint16_t c = 0; c < csiz; c++) { JPEG2000_SIZ_segment::component comp; uint8_t ssiz = read8(); comp.is_signed = (ssiz & 0x80); comp.precision = uint8_t((ssiz & 0x7F) + 1); comp.h_separation = read8(); comp.v_separation = read8(); siz.components.push_back(comp); } return Error::Ok; } Error JPEG2000MainHeader::parse_CAP_segment_body() { if (cursor > headerData.size() - 8) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream); } uint16_t lcap = read16(); if ((lcap < 8) || (lcap > 70)) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_J2K_codestream, std::string("Out of range Lcap value")); } uint32_t pcap = read32(); for (uint8_t i = 2; i <= 32; i++) { if (pcap & (1 << (32 - i))) { switch (i) { case JPEG2000_Extension_Capability_HT::IDENT: parse_Ccap15(); break; default: std::cout << "unhandled extended capabilities value: " << (int)i << std::endl; read16(); } } } return Error::Ok; } void JPEG2000MainHeader::parse_Ccap15() { uint16_t val = read16(); JPEG2000_Extension_Capability_HT ccap; // We could parse more here, but we don't need that yet. ccap.setValue(val); cap.push_back(ccap); } heif_chroma JPEG2000MainHeader::get_chroma_format() const { // Y-plane must be full resolution if (siz.components[0].h_separation != 1 || siz.components[0].v_separation != 1) { return heif_chroma_undefined; } if (siz.components.size() == 1) { return heif_chroma_monochrome; } else if (siz.components.size() == 3) { // TODO: we should map channels through `cdef` ? // both chroma components must have the same sampling if (siz.components[1].h_separation != siz.components[2].h_separation || siz.components[1].v_separation != siz.components[2].v_separation) { return heif_chroma_undefined; } if (siz.components[1].h_separation == 2 && siz.components[1].v_separation==2) { return heif_chroma_420; } if (siz.components[1].h_separation == 2 && siz.components[1].v_separation==1) { return heif_chroma_422; } if (siz.components[1].h_separation == 1 && siz.components[1].v_separation==1) { return heif_chroma_444; } } return heif_chroma_undefined; } libheif-1.19.8/libheif/codecs/avc_boxes.cc000664 001750 001750 00000020703 15003473471 021444 0ustar00farindkfarindk000000 000000 /* * HEIF AVC codec. * Copyright (c) 2023 Brad Hards * * 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 "avc_boxes.h" #include #include #include #include #include "file.h" #include "context.h" #include "avc_dec.h" Error Box_avcC::parse(BitstreamRange& range, const heif_security_limits* limits) { m_configuration.configuration_version = range.read8(); m_configuration.AVCProfileIndication = range.read8(); m_configuration.profile_compatibility = range.read8(); m_configuration.AVCLevelIndication = range.read8(); uint8_t lengthSizeMinusOneWithReserved = range.read8(); m_configuration.lengthSize = (lengthSizeMinusOneWithReserved & 0b00000011) + 1; uint8_t numOfSequenceParameterSets = (range.read8() & 0b00011111); for (int i = 0; i < numOfSequenceParameterSets; i++) { uint16_t sequenceParameterSetLength = range.read16(); std::vector sps(sequenceParameterSetLength); range.read(sps.data(), sps.size()); m_sps.push_back(sps); } uint8_t numOfPictureParameterSets = range.read8(); for (int i = 0; i < numOfPictureParameterSets; i++) { uint16_t pictureParameterSetLength = range.read16(); std::vector pps(pictureParameterSetLength); range.read(pps.data(), pps.size()); m_pps.push_back(pps); } // See ISO/IEC 14496-15 2017 Section 5.3.3.1.2 if ((m_configuration.AVCProfileIndication != 66) && (m_configuration.AVCProfileIndication != 77) && (m_configuration.AVCProfileIndication != 88)) { m_configuration.chroma_format = (heif_chroma) (range.read8() & 0b00000011); m_configuration.bit_depth_luma = 8 + (range.read8() & 0b00000111); m_configuration.bit_depth_chroma = 8 + (range.read8() & 0b00000111); uint8_t numOfSequenceParameterSetExt = range.read8(); for (int i = 0; i < numOfSequenceParameterSetExt; i++) { uint16_t sequenceParameterSetExtLength = range.read16(); std::vector sps_ext(sequenceParameterSetExtLength); range.read(sps_ext.data(), sps_ext.size()); m_sps_ext.push_back(sps_ext); } } return range.get_error(); } Error Box_avcC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write8(m_configuration.configuration_version); writer.write8(m_configuration.AVCProfileIndication); writer.write8(m_configuration.profile_compatibility); writer.write8(m_configuration.AVCLevelIndication); uint8_t lengthSizeMinusOneWithReserved = 0b11111100 | ((m_configuration.lengthSize - 1) & 0b11); writer.write8(lengthSizeMinusOneWithReserved); if (m_sps.size() > 0b00011111) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write more than 31 PPS into avcC box."}; } uint8_t numSpsWithReserved = 0b11100000 | (m_sps.size() & 0b00011111); writer.write8(numSpsWithReserved); for (const auto& sps : m_sps) { if (sps.size() > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write SPS larger than 65535 bytes into avcC box."}; } writer.write16((uint16_t) sps.size()); writer.write(sps); } if (m_pps.size() > 0xFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write more than 255 PPS into avcC box."}; } writer.write8(m_pps.size() & 0xFF); for (const auto& pps : m_pps) { if (pps.size() > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write PPS larger than 65535 bytes into avcC box."}; } writer.write16((uint16_t) pps.size()); writer.write(pps); } if ((m_configuration.AVCProfileIndication != 66) && (m_configuration.AVCProfileIndication != 77) && (m_configuration.AVCProfileIndication != 88)) { writer.write8(m_configuration.chroma_format); writer.write8(m_configuration.bit_depth_luma - 8); writer.write8(m_configuration.bit_depth_chroma - 8); if (m_sps_ext.size() > 0xFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write more than 255 SPS-Ext into avcC box."}; } writer.write8(m_sps_ext.size() & 0xFF); for (const auto& spsext : m_sps_ext) { if (spsext.size() > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Cannot write SPS-Ext larger than 65535 bytes into avcC box."}; } writer.write16((uint16_t) spsext.size()); writer.write(spsext); } } prepend_header(writer, box_start); return Error::Ok; } std::string Box_avcC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "configuration_version: " << ((int) m_configuration.configuration_version) << "\n" << indent << "AVCProfileIndication: " << ((int) m_configuration.AVCProfileIndication) << " (" << profileIndicationAsText() << ")\n" << indent << "profile_compatibility: " << ((int) m_configuration.profile_compatibility) << "\n" << indent << "AVCLevelIndication: " << ((int) m_configuration.AVCLevelIndication) << "\n" << indent << "Chroma format: "; switch (m_configuration.chroma_format) { case heif_chroma_monochrome: sstr << "4:0:0\n"; break; case heif_chroma_420: sstr << "4:2:0\n"; break; case heif_chroma_422: sstr << "4:2:2\n"; break; case heif_chroma_444: sstr << "4:4:4\n"; break; default: sstr << "unsupported\n"; break; } sstr << indent << "Bit depth luma: " << ((int) m_configuration.bit_depth_luma) << "\n" << indent << "Bit depth chroma: " << ((int) m_configuration.bit_depth_chroma) << "\n"; for (const auto& sps : m_sps) { sstr << indent << "SPS: "; for (uint8_t b : sps) { sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; } sstr << "\n"; sstr << std::dec; } for (const auto& spsext : m_sps_ext) { sstr << indent << "SPS-EXT: "; for (uint8_t b : spsext) { sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; } sstr << "\n"; sstr << std::dec; } for (const auto& pps : m_pps) { sstr << indent << "PPS: "; for (uint8_t b : pps) { sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; } sstr << "\n"; sstr << std::dec; } return sstr.str(); } std::string Box_avcC::profileIndicationAsText() const { // See ISO/IEC 14496-10:2022 Annex A switch (m_configuration.AVCProfileIndication) { case 44: return "CALVC 4:4:4"; case 66: return "Constrained Baseline"; case 77: return "Main"; case 88: return "Extended"; case 100: return "High variant"; case 110: return "High 10"; case 122: return "High 4:2:2"; case 244: return "High 4:4:4"; default: return "Unknown"; } } void Box_avcC::get_header_nals(std::vector& data) const { for (const auto& sps : m_sps) { data.push_back((sps.size() >> 24) & 0xFF); data.push_back((sps.size() >> 16) & 0xFF); data.push_back((sps.size() >> 8) & 0xFF); data.push_back((sps.size() >> 0) & 0xFF); data.insert(data.end(), sps.begin(), sps.end()); } for (const auto& spsext : m_sps_ext) { data.push_back((spsext.size() >> 24) & 0xFF); data.push_back((spsext.size() >> 16) & 0xFF); data.push_back((spsext.size() >> 8) & 0xFF); data.push_back((spsext.size() >> 0) & 0xFF); data.insert(data.end(), spsext.begin(), spsext.end()); } for (const auto& pps : m_pps) { data.push_back((pps.size() >> 24) & 0xFF); data.push_back((pps.size() >> 16) & 0xFF); data.push_back((pps.size() >> 8) & 0xFF); data.push_back((pps.size() >> 0) & 0xFF); data.insert(data.end(), pps.begin(), pps.end()); } } libheif-1.19.8/libheif/codecs/avif_dec.h000664 001750 001750 00000002756 15003473471 021105 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_AVIF_DEC_H #define HEIF_AVIF_DEC_H #include "libheif/heif.h" #include "error.h" #include #include #include class Box_av1C; class Decoder_AVIF : public Decoder { public: explicit Decoder_AVIF(const std::shared_ptr& av1C) : m_av1C(av1C) {} heif_compression_format get_compression_format() const override { return heif_compression_AV1; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_av1C; }; #endif libheif-1.19.8/libheif/codecs/vvc_boxes.cc000664 001750 001750 00000037035 15003473471 021477 0ustar00farindkfarindk000000 000000 /* * HEIF VVC codec. * Copyright (c) 2023 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 "vvc_boxes.h" #include "file.h" #include #include #include #include #include #include Error Box_vvcC::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); uint8_t byte; auto& c = m_configuration; // abbreviation byte = range.read8(); c.LengthSizeMinusOne = (byte >> 1) & 3; c.ptl_present_flag = !!(byte & 1); if (c.ptl_present_flag) { uint16_t word = range.read16(); c.ols_idx = (word >> 7) & 0x1FF; c.num_sublayers = (word >> 4) & 0x07; c.constant_frame_rate = (word >> 2) & 0x03; c.chroma_format_idc = word & 0x03; byte = range.read8(); c.bit_depth_minus8 = (byte >> 5) & 0x07; // VvcPTLRecord auto& ptl = c.native_ptl; // abbreviation byte = range.read8(); ptl.num_bytes_constraint_info = byte & 0x3f; if (ptl.num_bytes_constraint_info == 0) { return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "vvcC with num_bytes_constraint_info==0 is not allowed."}; } byte = range.read8(); ptl.general_profile_idc = (byte >> 1) & 0x7f; ptl.general_tier_flag = (byte & 1); ptl.general_level_idc = range.read8(); for (int i = 0; i < ptl.num_bytes_constraint_info; i++) { byte = range.read8(); if (i == 0) { ptl.ptl_frame_only_constraint_flag = (byte >> 7) & 1; ptl.ptl_multi_layer_enabled_flag = (byte >> 6) & 1; byte &= 0x3f; } ptl.general_constraint_info.push_back(byte); } if (c.num_sublayers > 1) { ptl.ptl_sublayer_level_present_flag.resize(c.num_sublayers - 1); byte = range.read8(); uint8_t mask = 0x80; for (int i = c.num_sublayers - 2; i >= 0; i--) { ptl.ptl_sublayer_level_present_flag[i] = !!(byte & mask); mask >>= 1; } } ptl.sublayer_level_idc.resize(c.num_sublayers); if (c.num_sublayers > 0) { ptl.sublayer_level_idc[c.num_sublayers - 1] = ptl.general_level_idc; for (int i = c.num_sublayers - 2; i >= 0; i--) { if (ptl.ptl_sublayer_level_present_flag[i]) { ptl.sublayer_level_idc[i] = range.read8(); } else { ptl.sublayer_level_idc[i] = ptl.sublayer_level_idc[i + 1]; } } } uint8_t ptl_num_sub_profiles = range.read8(); for (int j=0; j < ptl_num_sub_profiles; j++) { ptl.general_sub_profile_idc.push_back(range.read32()); } // remaining fields c.max_picture_width = range.read16(); c.max_picture_height = range.read16(); c.avg_frame_rate = range.read16(); } // read NAL arrays int nArrays = range.read8(); for (int i = 0; i < nArrays && !range.error(); i++) { byte = range.read8(); NalArray array; array.m_array_completeness = (byte >> 7) & 1; array.m_NAL_unit_type = (byte & 0x3F); int nUnits = range.read16(); for (int u = 0; u < nUnits && !range.error(); u++) { std::vector nal_unit; int size = range.read16(); if (!size) { // Ignore empty NAL units. continue; } if (range.prepare_read(size)) { nal_unit.resize(size); bool success = range.get_istream()->read((char*) nal_unit.data(), size); if (!success) { return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while reading hvcC box"}; } } array.m_nal_units.push_back(std::move(nal_unit)); } m_nal_array.push_back(std::move(array)); } return range.get_error(); } bool Box_vvcC::get_headers(std::vector* dest) const { for (const auto& nal_array : m_nal_array) { for (const auto& nal : nal_array.m_nal_units) { assert(nal.size() <= 0xFFFF); auto size = static_cast(nal.size()); dest->push_back(0); dest->push_back(0); dest->push_back(static_cast(size >> 8)); dest->push_back(static_cast(size & 0xFF)); dest->insert(dest->end(), nal.begin(), nal.end()); } } return true; } void Box_vvcC::append_nal_data(const std::vector& nal) { assert(nal.size()>=2); uint8_t nal_type = (nal[1] >> 3) & 0x1F; // insert into existing array if it exists for (auto& nalarray : m_nal_array) { if (nalarray.m_NAL_unit_type == nal_type) { nalarray.m_nal_units.push_back(nal); return; } } // generate new NAL array NalArray array; array.m_array_completeness = true; array.m_NAL_unit_type = uint8_t((nal[1] >> 3) & 0x1F); array.m_nal_units.push_back(nal); m_nal_array.push_back(array); } void Box_vvcC::append_nal_data(const uint8_t* data, size_t size) { std::vector nal; nal.resize(size); memcpy(nal.data(), data, size); append_nal_data(nal); } Error Box_vvcC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); const auto& c = m_configuration; uint8_t byte; byte = uint8_t(0xF8 | (c.LengthSizeMinusOne<<1) | (c.ptl_present_flag ? 1 : 0)); writer.write8(byte); if (c.ptl_present_flag) { assert(c.ols_idx <= 0x1FF); assert(c.num_sublayers <= 7); assert(c.constant_frame_rate <= 3); assert(c.chroma_format_idc <= 3); assert(c.bit_depth_minus8 <= 7); auto word = uint16_t((c.ols_idx << 7) | (c.num_sublayers << 4) | (c.constant_frame_rate << 2) | (c.chroma_format_idc)); writer.write16(word); writer.write8(uint8_t((c.bit_depth_minus8<<5) | 0x1F)); const auto& ptl = c.native_ptl; assert(ptl.general_profile_idc <= 0x7F); writer.write8(ptl.num_bytes_constraint_info & 0x3f); writer.write8(static_cast((ptl.general_profile_idc<<1) | ptl.general_tier_flag)); writer.write8(ptl.general_level_idc); for (int i=0;i((ptl.ptl_frame_only_constraint_flag << 7) | (ptl.ptl_multi_layer_enabled_flag << 6) | ptl.general_constraint_info[0]); } else { byte = ptl.general_constraint_info[i]; } writer.write8(byte); } byte = 0; if (c.num_sublayers > 1) { uint8_t mask=0x80; for (int i = c.num_sublayers - 2; i >= 0; i--) { if (ptl.ptl_sublayer_level_present_flag[i]) { byte |= mask; } mask >>= 1; } } writer.write8(byte); for (int i=c.num_sublayers-2; i >= 0; i--) { if (ptl.ptl_sublayer_level_present_flag[i]) { writer.write8(ptl.sublayer_level_idc[i]); } } assert(ptl.general_sub_profile_idc.size() <= 0xFF); byte = static_cast(ptl.general_sub_profile_idc.size()); writer.write8(byte); for (int j=0; j < byte; j++) { writer.write32(ptl.general_sub_profile_idc[j]); } writer.write16(c.max_picture_width); writer.write16(c.max_picture_height); writer.write16(c.avg_frame_rate); } // --- write configuration NALs if (m_nal_array.size() > 255) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL arrays."}; } writer.write8((uint8_t)m_nal_array.size()); for (const NalArray& nal_array : m_nal_array) { uint8_t v2 = (nal_array.m_array_completeness ? 0x80 : 0); v2 |= nal_array.m_NAL_unit_type; writer.write8(v2); if (nal_array.m_nal_units.size() > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL units."}; } writer.write16((uint16_t)nal_array.m_nal_units.size()); for (const auto& nal : nal_array.m_nal_units) { if (nal.size() > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "VVC NAL too large."}; } writer.write16((uint16_t)nal.size()); writer.write(nal); } } prepend_header(writer, box_start); return Error::Ok; } static const char* vvc_chroma_names[4] = {"mono", "4:2:0", "4:2:2", "4:4:4"}; const char* NAL_name(uint8_t nal_type) { switch (nal_type) { case 12: return "OPI"; case 13: return "DCI"; case 14: return "VPS"; case 15: return "SPS"; case 16: return "PPS"; case 17: return "PREFIX_APS"; case 18: return "SUFFIX_APS"; case 19: return "PH"; default: return "?"; } } std::string Box_vvcC::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); const auto& c = m_configuration; // abbreviation sstr << indent << "NAL length size: " << ((int) c.LengthSizeMinusOne + 1) << "\n"; if (c.ptl_present_flag) { const auto& ptl = c.native_ptl; sstr << indent << "ols-index: " << c.ols_idx << "\n" << indent << "num sublayers: " << ((int) c.num_sublayers) << "\n" << indent << "constant frame rate: " << (c.constant_frame_rate == 1 ? "constant" : (c.constant_frame_rate == 2 ? "multi-layer" : "unknown")) << "\n" << indent << "chroma-format: " << vvc_chroma_names[c.chroma_format_idc] << "\n" << indent << "bit-depth: " << ((int) c.bit_depth_minus8 + 8) << "\n" << indent << "max picture width: " << c.max_picture_width << "\n" << indent << "max picture height: " << c.max_picture_height << "\n"; sstr << indent << "general profile: " << ((int)ptl.general_profile_idc) << "\n" << indent << "tier flag: " << ((int)ptl.general_tier_flag) << "\n" << indent << "general level:" << ((int)ptl.general_level_idc) << "\n" << indent << "ptl frame only constraint flag: " << ((int)ptl.ptl_frame_only_constraint_flag) << "\n" << indent << "ptl multi layer enabled flag: " << ((int)ptl.ptl_multi_layer_enabled_flag) << "\n"; } sstr << indent << "num of arrays: " << m_nal_array.size() << "\n"; sstr << indent << "config NALs:\n"; for (const auto& nal_array : m_nal_array) { indent++; sstr << indent << "NAL type: " << ((int)nal_array.m_NAL_unit_type) << " (" << NAL_name(nal_array.m_NAL_unit_type) << ")\n"; sstr << indent << "array completeness: " << ((int)nal_array.m_array_completeness) << "\n"; for (const auto& nal : nal_array.m_nal_units) { indent++; std::string ind = indent.get_string(); sstr << write_raw_data_as_hex(nal.data(), nal.size(), ind, ind); indent--; } indent--; } return sstr.str(); } static std::vector remove_start_code_emulation(const uint8_t* sps, size_t size) { std::vector out_data; for (size_t i = 0; i < size; i++) { if (i + 2 < size && sps[i] == 0 && sps[i + 1] == 0 && sps[i + 2] == 3) { out_data.push_back(0); out_data.push_back(0); i += 2; } else { out_data.push_back(sps[i]); } } return out_data; } Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, Box_vvcC::configuration* config, int* width, int* height) { // remove start-code emulation bytes from SPS header stream std::vector sps_no_emul = remove_start_code_emulation(sps, size); sps = sps_no_emul.data(); size = sps_no_emul.size(); BitReader reader(sps, (int) size); // skip NAL header reader.skip_bits(2 * 8); // skip SPS ID reader.skip_bits(4); // skip VPS ID reader.skip_bits(4); config->ols_idx = 0; config->num_sublayers = reader.get_bits8(3) + 1; config->chroma_format_idc = reader.get_bits8(2); reader.skip_bits(2); bool sps_ptl_dpb_hrd_params_present_flag = reader.get_bits(1); if (sps_ptl_dpb_hrd_params_present_flag) { // profile_tier_level( 1, sps_max_sublayers_minus1 ) auto& ptl = config->native_ptl; if (true /*profileTierPresentFlag*/) { ptl.general_profile_idc = reader.get_bits8(7); ptl.general_tier_flag = reader.get_bits8(1); } ptl.general_level_idc = reader.get_bits8(8); ptl.ptl_frame_only_constraint_flag = reader.get_bits8(1); ptl.ptl_multi_layer_enabled_flag = reader.get_bits8(1); if (true /* profileTierPresentFlag*/ ) { // general_constraints_info() bool gci_present_flag = reader.get_bits(1); if (gci_present_flag) { assert(false); } else { ptl.num_bytes_constraint_info = 1; ptl.general_constraint_info.push_back(0); } reader.skip_to_byte_boundary(); } ptl.ptl_sublayer_level_present_flag.resize(config->num_sublayers); for (int i = config->num_sublayers-2; i >= 0; i--) { ptl.ptl_sublayer_level_present_flag[i] = reader.get_bits(1); } reader.skip_to_byte_boundary(); ptl.sublayer_level_idc.resize(config->num_sublayers); for (int i = config->num_sublayers-2; i >= 0; i--) { if (ptl.ptl_sublayer_level_present_flag[i]) { ptl.sublayer_level_idc[i] = reader.get_bits8(8); } } if (true /*profileTierPresentFlag*/) { int ptl_num_sub_profiles = reader.get_bits(8); ptl.general_sub_profile_idc.resize(ptl_num_sub_profiles); for (int i = 0; i < ptl_num_sub_profiles; i++) { ptl.general_sub_profile_idc[i] = reader.get_bits(32); } } } reader.skip_bits(1); // sps_gdr_enabled_flag bool sps_ref_pic_resampling_enabled_flag = reader.get_bits(1); if (sps_ref_pic_resampling_enabled_flag) { reader.skip_bits(1); // sps_res_change_in_clvs_allowed_flag } int sps_pic_width_max_in_luma_samples; int sps_pic_height_max_in_luma_samples; bool success; success = reader.get_uvlc(&sps_pic_width_max_in_luma_samples); (void)success; success = reader.get_uvlc(&sps_pic_height_max_in_luma_samples); (void)success; *width = sps_pic_width_max_in_luma_samples; *height = sps_pic_height_max_in_luma_samples; if (sps_pic_width_max_in_luma_samples > 0xFFFF || sps_pic_height_max_in_luma_samples > 0xFFFF) { return {heif_error_Encoding_error, heif_suberror_Invalid_parameter_value, "SPS max picture width or height exceeds maximum (65535)"}; } config->max_picture_width = static_cast(sps_pic_width_max_in_luma_samples); config->max_picture_height = static_cast(sps_pic_height_max_in_luma_samples); int sps_conformance_window_flag = reader.get_bits(1); if (sps_conformance_window_flag) { int left,right,top,bottom; reader.get_uvlc(&left); reader.get_uvlc(&right); reader.get_uvlc(&top); reader.get_uvlc(&bottom); } bool sps_subpic_info_present_flag = reader.get_bits(1); if (sps_subpic_info_present_flag) { assert(false); // TODO } int bitDepth_minus8; success = reader.get_uvlc(&bitDepth_minus8); (void)success; if (bitDepth_minus8 > 0xFF - 8) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "VCC bit depth out of range."}; } config->bit_depth_minus8 = static_cast(bitDepth_minus8); config->constant_frame_rate = 1; // is constant (TODO: where do we get this from) return Error::Ok; } libheif-1.19.8/libheif/codecs/avif_boxes.h000664 001750 001750 00000007517 15003473471 021472 0ustar00farindkfarindk000000 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 HEIF_AVIF_BOXES_H #define HEIF_AVIF_BOXES_H #include #include #include #include #include #include "libheif/heif.h" #include "box.h" #include "error.h" #include "image-items/image_item.h" class Box_av1C : public Box { // allow access to protected parse() method friend class Box_mini; public: Box_av1C() { set_short_type(fourcc("av1C")); } bool is_essential() const override { return true; } struct configuration { //unsigned int (1) marker = 1; uint8_t version = 1; uint8_t seq_profile = 0; uint8_t seq_level_idx_0 = 0; uint8_t seq_tier_0 = 0; uint8_t high_bitdepth = 0; uint8_t twelve_bit = 0; uint8_t monochrome = 0; uint8_t chroma_subsampling_x = 0; // (minus 1) that is: either 0 or 1 uint8_t chroma_subsampling_y = 0; // (minus 1) uint8_t chroma_sample_position = 0; //uint8_t reserved = 0; uint8_t initial_presentation_delay_present = 0; uint8_t initial_presentation_delay_minus_one = 0; //unsigned int (8)[] configOBUs; heif_chroma get_heif_chroma() const { if (monochrome) { return heif_chroma_monochrome; } else if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { return heif_chroma_420; } else if (chroma_subsampling_x==1 && chroma_subsampling_y==0) { return heif_chroma_422; } else if (chroma_subsampling_x==0 && chroma_subsampling_y==0) { return heif_chroma_444; } else { return heif_chroma_undefined; } } }; std::string dump(Indent&) const override; bool get_headers(std::vector* dest) const { *dest = m_config_OBUs; return true; } void set_configuration(const configuration& config) { m_configuration = config; } const configuration& get_configuration() const { return m_configuration; } //void append_nal_data(const std::vector& nal); //void append_nal_data(const uint8_t* data, size_t size); Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: configuration m_configuration; std::vector m_config_OBUs; }; class Box_a1op : public Box { public: Box_a1op() { set_short_type(fourcc("a1op")); } uint8_t op_index = 0; std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; }; class Box_a1lx : public Box { public: Box_a1lx() { set_short_type(fourcc("a1lx")); } uint32_t layer_size[3]{}; std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; }; class HeifPixelImage; Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std::shared_ptr& image); bool fill_av1C_configuration_from_stream(Box_av1C::configuration* out_config, const uint8_t* data, int dataSize); #endif libheif-1.19.8/libheif/codecs/jpeg2000_dec.cc000664 001750 001750 00000004134 15003473471 021535 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "jpeg2000_dec.h" #include "jpeg2000_boxes.h" #include "error.h" #include "context.h" #include Result> Decoder_JPEG2000::read_bitstream_configuration_data() const { return std::vector{}; } int Decoder_JPEG2000::get_luma_bits_per_pixel() const { Result> imageDataResult = get_compressed_data(); if (imageDataResult.error) { return -1; } JPEG2000MainHeader header; Error err = header.parseHeader(*imageDataResult); if (err) { return -1; } return header.get_precision(0); } int Decoder_JPEG2000::get_chroma_bits_per_pixel() const { Result> imageDataResult = get_compressed_data(); if (imageDataResult.error) { return -1; } JPEG2000MainHeader header; Error err = header.parseHeader(*imageDataResult); if (err) { return -1; } return header.get_precision(1); } Error Decoder_JPEG2000::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { #if 0 *out_chroma = (heif_chroma) (m_hvcC->get_configuration().chroma_format); if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } #endif *out_colorspace = heif_colorspace_YCbCr; *out_chroma = heif_chroma_444; return Error::Ok; } libheif-1.19.8/libheif/codecs/avif_dec.cc000664 001750 001750 00000003504 15003473471 021233 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "avif_dec.h" #include "avif_boxes.h" #include "error.h" #include "context.h" #include Result> Decoder_AVIF::read_bitstream_configuration_data() const { std::vector data; if (!m_av1C->get_headers(&data)) { return Error{heif_error_Invalid_input, heif_suberror_No_item_data}; } return data; } int Decoder_AVIF::get_luma_bits_per_pixel() const { Box_av1C::configuration config = m_av1C->get_configuration(); if (!config.high_bitdepth) { return 8; } else if (config.twelve_bit) { return 12; } else { return 10; } } int Decoder_AVIF::get_chroma_bits_per_pixel() const { return get_luma_bits_per_pixel(); } Error Decoder_AVIF::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { *out_chroma = (heif_chroma) (m_av1C->get_configuration().get_heif_chroma()); if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } return Error::Ok; } libheif-1.19.8/libheif/codecs/jpeg_boxes.cc000664 001750 001750 00000004151 15003473471 021617 0ustar00farindkfarindk000000 000000 /* * HEIF JPEG codec. * Copyright (c) 2023 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 "jpeg_boxes.h" #include #include "security_limits.h" #include #include "libheif/heif_experimental.h" // returns 0 if the marker_type was not found size_t find_jpeg_marker_start(const std::vector& data, uint8_t marker_type) { for (size_t i = 0; i < data.size() - 1; i++) { if (data[i] == 0xFF && data[i + 1] == marker_type) { return i; } } return 0; } std::string Box_jpgC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "num bytes: " << m_data.size() << "\n"; return sstr.str(); } Error Box_jpgC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write(m_data); prepend_header(writer, box_start); return Error::Ok; } Error Box_jpgC::parse(BitstreamRange& range, const heif_security_limits* limits) { if (!has_fixed_box_size()) { return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, "jpgC with unspecified size are not supported"}; } size_t nBytes = range.get_remaining_bytes(); if (limits->max_memory_block_size && nBytes > limits->max_memory_block_size) { return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "jpgC block exceeds maximum size"}; } m_data.resize(nBytes); range.read(m_data.data(), nBytes); return range.get_error(); } libheif-1.19.8/libheif/codecs/hevc_boxes.cc000664 001750 001750 00000042714 15003473471 021626 0ustar00farindkfarindk000000 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 "hevc_boxes.h" #include "bitstream.h" #include "error.h" #include "file.h" #include "hevc_dec.h" #include #include #include #include #include #include #include Error Box_hvcC::parse(BitstreamRange& range, const heif_security_limits* limits) { //parse_full_box_header(range); uint8_t byte; auto& c = m_configuration; // abbreviation c.configuration_version = range.read8(); byte = range.read8(); c.general_profile_space = (byte >> 6) & 3; c.general_tier_flag = (byte >> 5) & 1; c.general_profile_idc = (byte & 0x1F); c.general_profile_compatibility_flags = range.read32(); for (int i = 0; i < 6; i++) { byte = range.read8(); for (int b = 0; b < 8; b++) { c.general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1; } } c.general_level_idc = range.read8(); c.min_spatial_segmentation_idc = range.read16() & 0x0FFF; c.parallelism_type = range.read8() & 0x03; c.chroma_format = range.read8() & 0x03; c.bit_depth_luma = static_cast((range.read8() & 0x07) + 8); c.bit_depth_chroma = static_cast((range.read8() & 0x07) + 8); c.avg_frame_rate = range.read16(); byte = range.read8(); c.constant_frame_rate = (byte >> 6) & 0x03; c.num_temporal_layers = (byte >> 3) & 0x07; c.temporal_id_nested = (byte >> 2) & 1; m_length_size = static_cast((byte & 0x03) + 1); int nArrays = range.read8(); for (int i = 0; i < nArrays && !range.error(); i++) { byte = range.read8(); NalArray array; array.m_array_completeness = (byte >> 6) & 1; array.m_NAL_unit_type = (byte & 0x3F); int nUnits = range.read16(); for (int u = 0; u < nUnits && !range.error(); u++) { std::vector nal_unit; int size = range.read16(); if (!size) { // Ignore empty NAL units. continue; } if (range.prepare_read(size)) { nal_unit.resize(size); bool success = range.get_istream()->read((char*) nal_unit.data(), size); if (!success) { return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while reading hvcC box"}; } } array.m_nal_units.push_back(std::move(nal_unit)); } m_nal_array.push_back(std::move(array)); } range.skip_to_end_of_box(); return range.get_error(); } std::string Box_hvcC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); const auto& c = m_configuration; // abbreviation sstr << indent << "configuration_version: " << ((int) c.configuration_version) << "\n" << indent << "general_profile_space: " << ((int) c.general_profile_space) << "\n" << indent << "general_tier_flag: " << c.general_tier_flag << "\n" << indent << "general_profile_idc: " << ((int) c.general_profile_idc) << "\n"; sstr << indent << "general_profile_compatibility_flags: "; for (int i = 0; i < 32; i++) { sstr << ((c.general_profile_compatibility_flags >> (31 - i)) & 1); if ((i % 8) == 7) sstr << ' '; else if ((i % 4) == 3) sstr << '.'; } sstr << "\n"; sstr << indent << "general_constraint_indicator_flags: "; int cnt = 0; for (int i = 0; i < configuration::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) { bool b = c.general_constraint_indicator_flags[i]; sstr << (b ? 1 : 0); cnt++; if ((cnt % 8) == 0) sstr << ' '; } sstr << "\n"; sstr << indent << "general_level_idc: " << ((int) c.general_level_idc) << "\n" << indent << "min_spatial_segmentation_idc: " << c.min_spatial_segmentation_idc << "\n" << indent << "parallelism_type: " << ((int) c.parallelism_type) << "\n" << indent << "chroma_format: "; switch (c.chroma_format) { case 1: sstr << "4:2:0"; break; case 2: sstr << "4:2:2"; break; case 3: sstr << "4:4:4"; break; default: sstr << ((int) c.chroma_format); break; } sstr << "\n" << indent << "bit_depth_luma: " << ((int) c.bit_depth_luma) << "\n" << indent << "bit_depth_chroma: " << ((int) c.bit_depth_chroma) << "\n" << indent << "avg_frame_rate: " << c.avg_frame_rate << "\n" << indent << "constant_frame_rate: " << ((int) c.constant_frame_rate) << "\n" << indent << "num_temporal_layers: " << ((int) c.num_temporal_layers) << "\n" << indent << "temporal_id_nested: " << ((int) c.temporal_id_nested) << "\n" << indent << "length_size: " << ((int) m_length_size) << "\n"; for (const auto& array : m_nal_array) { sstr << indent << "\n"; indent++; sstr << indent << "array_completeness: " << ((int) array.m_array_completeness) << "\n" << indent << "NAL_unit_type: " << ((int) array.m_NAL_unit_type) << "\n"; for (const auto& unit : array.m_nal_units) { //sstr << " unit with " << unit.size() << " bytes of data\n"; sstr << indent; for (uint8_t b : unit) { sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; } sstr << "\n"; sstr << std::dec; } indent--; } return sstr.str(); } bool Box_hvcC::get_headers(std::vector* dest) const { for (const auto& array : m_nal_array) { for (const auto& unit : array.m_nal_units) { dest->push_back((unit.size() >> 24) & 0xFF); dest->push_back((unit.size() >> 16) & 0xFF); dest->push_back((unit.size() >> 8) & 0xFF); dest->push_back((unit.size() >> 0) & 0xFF); /* dest->push_back(0); dest->push_back(0); dest->push_back(1); */ dest->insert(dest->end(), unit.begin(), unit.end()); } } return true; } void Box_hvcC::append_nal_data(const std::vector& nal) { NalArray array; array.m_array_completeness = 0; array.m_NAL_unit_type = uint8_t(nal[0] >> 1); array.m_nal_units.push_back(nal); m_nal_array.push_back(array); } void Box_hvcC::append_nal_data(const uint8_t* data, size_t size) { std::vector nal; nal.resize(size); memcpy(nal.data(), data, size); NalArray array; array.m_array_completeness = 0; array.m_NAL_unit_type = uint8_t(nal[0] >> 1); array.m_nal_units.push_back(std::move(nal)); m_nal_array.push_back(array); } Error Box_hvcC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); const auto& c = m_configuration; // abbreviation writer.write8(c.configuration_version); writer.write8((uint8_t) (((c.general_profile_space & 3) << 6) | ((c.general_tier_flag & 1) << 5) | (c.general_profile_idc & 0x1F))); writer.write32(c.general_profile_compatibility_flags); for (int i = 0; i < 6; i++) { uint8_t byte = 0; for (int b = 0; b < 8; b++) { if (c.general_constraint_indicator_flags[i * 8 + b]) { byte |= 1; } byte = (uint8_t) (byte << 1); } writer.write8(byte); } writer.write8(c.general_level_idc); writer.write16((c.min_spatial_segmentation_idc & 0x0FFF) | 0xF000); writer.write8(c.parallelism_type | 0xFC); writer.write8(c.chroma_format | 0xFC); writer.write8((uint8_t) ((c.bit_depth_luma - 8) | 0xF8)); writer.write8((uint8_t) ((c.bit_depth_chroma - 8) | 0xF8)); writer.write16(c.avg_frame_rate); writer.write8((uint8_t) (((c.constant_frame_rate & 0x03) << 6) | ((c.num_temporal_layers & 0x07) << 3) | ((c.temporal_id_nested & 1) << 2) | ((m_length_size - 1) & 0x03))); size_t nArrays = m_nal_array.size(); if (nArrays > 0xFF) { // TODO: error: too many NAL units } writer.write8((uint8_t) nArrays); for (const NalArray& array : m_nal_array) { writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) | (array.m_NAL_unit_type & 0x3F))); size_t nUnits = array.m_nal_units.size(); if (nUnits > 0xFFFF) { // TODO: error: too many NAL units } writer.write16((uint16_t) nUnits); for (const std::vector& nal_unit : array.m_nal_units) { writer.write16((uint16_t) nal_unit.size()); writer.write(nal_unit); } } prepend_header(writer, box_start); return Error::Ok; } static double read_depth_rep_info_element(BitReader& reader) { uint8_t sign_flag = reader.get_bits8(1); int exponent = reader.get_bits(7); auto mantissa_len = static_cast(reader.get_bits8(5) + 1); if (mantissa_len < 1 || mantissa_len > 32) { // TODO err } if (exponent == 127) { // TODO value unspecified } uint32_t mantissa = reader.get_bits32(mantissa_len); double value; //printf("sign:%d exponent:%d mantissa_len:%d mantissa:%d\n",sign_flag,exponent,mantissa_len,mantissa); // TODO: this seems to be wrong. 'exponent' is never negative. How to read it correctly? if (exponent > 0) { value = pow(2.0, exponent - 31) * (1.0 + mantissa / pow(2.0, mantissa_len)); } else { value = pow(2.0, -(30 + mantissa_len)) * mantissa; } if (sign_flag) { value = -value; } return value; } static Result> read_depth_representation_info(BitReader& reader) { Result> result; auto msg = std::make_shared(); // default values msg->version = 1; msg->disparity_reference_view = 0; msg->depth_nonlinear_representation_model_size = 0; msg->depth_nonlinear_representation_model = nullptr; // read header msg->has_z_near = (uint8_t) reader.get_bits(1); msg->has_z_far = (uint8_t) reader.get_bits(1); msg->has_d_min = (uint8_t) reader.get_bits(1); msg->has_d_max = (uint8_t) reader.get_bits(1); int rep_type; if (!reader.get_uvlc(&rep_type)) { result.error = {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "invalid depth representation type in input"}; return result; } if (rep_type < 0 || rep_type > 3) { result.error = {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "input depth representation type out of range"}; return result; } msg->depth_representation_type = (enum heif_depth_representation_type) rep_type; //printf("flags: %d %d %d %d\n",msg->has_z_near,msg->has_z_far,msg->has_d_min,msg->has_d_max); //printf("type: %d\n",rep_type); if (msg->has_d_min || msg->has_d_max) { int ref_view; if (!reader.get_uvlc(&ref_view)) { result.error = {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "invalid disparity_reference_view in input"}; return result; } msg->disparity_reference_view = ref_view; //printf("ref_view: %d\n",msg->disparity_reference_view); } if (msg->has_z_near) msg->z_near = read_depth_rep_info_element(reader); if (msg->has_z_far) msg->z_far = read_depth_rep_info_element(reader); if (msg->has_d_min) msg->d_min = read_depth_rep_info_element(reader); if (msg->has_d_max) msg->d_max = read_depth_rep_info_element(reader); /* printf("z_near: %f\n",msg->z_near); printf("z_far: %f\n",msg->z_far); printf("dmin: %f\n",msg->d_min); printf("dmax: %f\n",msg->d_max); */ if (msg->depth_representation_type == heif_depth_representation_type_nonuniform_disparity) { // TODO: load non-uniform response curve } result.value = msg; return result; } // aux subtypes: 00 00 00 11 / 00 00 00 0d / 4e 01 / b1 09 / 35 1e 78 c8 01 03 c5 d0 20 Error decode_hevc_aux_sei_messages(const std::vector& data, std::vector>& msgs) { // TODO: we probably do not need a full BitReader just for the array size. // Read this and the NAL size directly on the array data. BitReader reader(data.data(), (int) data.size()); if (reader.get_bits_remaining() < 32) { return {heif_error_Invalid_input, heif_suberror_End_of_data, "HEVC SEI NAL too short"}; } uint32_t len = reader.get_bits32(32); if (len > data.size() - 4) { // ERROR: read past end of data } while (reader.get_current_byte_index() < (int) len) { int currPos = reader.get_current_byte_index(); BitReader sei_reader(data.data() + currPos, (int) data.size() - currPos); if (sei_reader.get_bits_remaining() < 32+8) { return {heif_error_Invalid_input, heif_suberror_End_of_data, "HEVC SEI NAL too short"}; } uint32_t nal_size = sei_reader.get_bits32(32); (void) nal_size; auto nal_type = static_cast(sei_reader.get_bits8(8) >> 1); sei_reader.skip_bits(8); // SEI if (nal_type == 39 || nal_type == 40) { if (sei_reader.get_bits_remaining() < 16) { return {heif_error_Invalid_input, heif_suberror_End_of_data, "HEVC SEI NAL too short"}; } // TODO: loading of multi-byte sei headers uint8_t payload_id = sei_reader.get_bits8(8); uint8_t payload_size = sei_reader.get_bits8(8); (void) payload_size; if (payload_id == 177) { // depth_representation_info Result> seiResult = read_depth_representation_info(sei_reader); if (seiResult.error) { return seiResult.error; } msgs.push_back(seiResult.value); } } break; // TODO: read next SEI } return Error::Ok; } static std::vector remove_start_code_emulation(const uint8_t* sps, size_t size) { std::vector out_data; for (size_t i = 0; i < size; i++) { if (i + 2 < size && sps[i] == 0 && sps[i + 1] == 0 && sps[i + 2] == 3) { out_data.push_back(0); out_data.push_back(0); i += 2; } else { out_data.push_back(sps[i]); } } return out_data; } Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size, Box_hvcC::configuration* config, int* width, int* height) { // remove start-code emulation bytes from SPS header stream std::vector sps_no_emul = remove_start_code_emulation(sps, size); sps = sps_no_emul.data(); size = sps_no_emul.size(); BitReader reader(sps, (int) size); // skip NAL header reader.skip_bits(2 * 8); // skip VPS ID reader.skip_bits(4); uint8_t nMaxSubLayersMinus1 = reader.get_bits8(3); config->temporal_id_nested = reader.get_bits8(1); // --- profile_tier_level --- config->general_profile_space = reader.get_bits8(2); config->general_tier_flag = reader.get_bits8(1); config->general_profile_idc = reader.get_bits8(5); config->general_profile_compatibility_flags = reader.get_bits32(32); reader.skip_bits(16); // skip reserved bits reader.skip_bits(16); // skip reserved bits reader.skip_bits(16); // skip reserved bits config->general_level_idc = reader.get_bits8(8); std::vector layer_profile_present(nMaxSubLayersMinus1); std::vector layer_level_present(nMaxSubLayersMinus1); for (int i = 0; i < nMaxSubLayersMinus1; i++) { layer_profile_present[i] = reader.get_bits(1); layer_level_present[i] = reader.get_bits(1); } if (nMaxSubLayersMinus1 > 0) { for (int i = nMaxSubLayersMinus1; i < 8; i++) { reader.skip_bits(2); } } for (int i = 0; i < nMaxSubLayersMinus1; i++) { if (layer_profile_present[i]) { reader.skip_bits(2 + 1 + 5); reader.skip_bits(32); reader.skip_bits(16); } if (layer_level_present[i]) { reader.skip_bits(8); } } // --- SPS continued --- int dummy, value; reader.get_uvlc(&dummy); // skip seq_parameter_seq_id reader.get_uvlc(&value); config->chroma_format = (uint8_t) value; if (config->chroma_format == 3) { reader.skip_bits(1); } reader.get_uvlc(width); reader.get_uvlc(height); bool conformance_window = reader.get_bits(1); if (conformance_window) { int left, right, top, bottom; reader.get_uvlc(&left); reader.get_uvlc(&right); reader.get_uvlc(&top); reader.get_uvlc(&bottom); //printf("conformance borders: %d %d %d %d\n",left,right,top,bottom); int subH = 1, subV = 1; if (config->chroma_format == 1) { subV = 2; subH = 2; } if (config->chroma_format == 2) { subH = 2; } *width -= subH * (left + right); *height -= subV * (top + bottom); } reader.get_uvlc(&value); config->bit_depth_luma = (uint8_t) (value + 8); reader.get_uvlc(&value); config->bit_depth_chroma = (uint8_t) (value + 8); // --- init static configuration fields --- config->configuration_version = 1; config->min_spatial_segmentation_idc = 0; // TODO: get this value from the VUI, 0 should be safe config->parallelism_type = 0; // TODO, 0 should be safe config->avg_frame_rate = 0; // makes no sense for HEIF config->constant_frame_rate = 0; // makes no sense for HEIF config->num_temporal_layers = 1; // makes no sense for HEIF return Error::Ok; } libheif-1.19.8/libheif/codecs/hevc_boxes.h000664 001750 001750 00000006354 15003473471 021470 0ustar00farindkfarindk000000 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 HEIF_HEVC_BOXES_H #define HEIF_HEVC_BOXES_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include #include #include #include "image-items/image_item.h" class Box_hvcC : public Box { // allow access to protected parse() method friend class Box_mini; public: Box_hvcC() { set_short_type(fourcc("hvcC")); } bool is_essential() const override { return true; } struct configuration { uint8_t configuration_version; uint8_t general_profile_space; bool general_tier_flag; uint8_t general_profile_idc; uint32_t general_profile_compatibility_flags; static const int NUM_CONSTRAINT_INDICATOR_FLAGS = 48; std::bitset general_constraint_indicator_flags; uint8_t general_level_idc; uint16_t min_spatial_segmentation_idc; uint8_t parallelism_type; uint8_t chroma_format; uint8_t bit_depth_luma; uint8_t bit_depth_chroma; uint16_t avg_frame_rate; uint8_t constant_frame_rate; uint8_t num_temporal_layers; uint8_t temporal_id_nested; }; std::string dump(Indent&) const override; bool get_headers(std::vector* dest) const; void set_configuration(const configuration& config) { m_configuration = config; } const configuration& get_configuration() const { return m_configuration; } void append_nal_data(const std::vector& nal); void append_nal_data(const uint8_t* data, size_t size); Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: struct NalArray { uint8_t m_array_completeness; uint8_t m_NAL_unit_type; std::vector > m_nal_units; }; configuration m_configuration; uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths std::vector m_nal_array; }; class SEIMessage { public: virtual ~SEIMessage() = default; }; class SEIMessage_depth_representation_info : public SEIMessage, public heif_depth_representation_info { public: }; Error decode_hevc_aux_sei_messages(const std::vector& data, std::vector>& msgs); Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size, Box_hvcC::configuration* inout_config, int* width, int* height); #endif libheif-1.19.8/libheif/codecs/jpeg2000_boxes.h000664 001750 001750 00000034107 15003473471 021767 0ustar00farindkfarindk000000 000000 /* * HEIF JPEG 2000 codec. * Copyright (c) 2023 Brad Hards * * 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_JPEG2000_BOXES_H #define LIBHEIF_JPEG2000_BOXES_H #include "box.h" #include "file.h" #include "context.h" #include #include #include #include #include /** * JPEG 2000 Channel Definition box. * * This is defined in ITU-800 / IEC 15444-1. * * The Channel Definition box provides the type and ordering of the components * within the codestream. The mapping between actual components from the * codestream to channels is specified in the Component Mapping box. If the * header does not contain a Component Mapping box, then a reader shall map * component * @code{i} to channel @code{i}, for all components in the codestream. * * This box contains an array of channel descriptions. For each description, * three values are specified: the index of the channel described by that * association, the type of that channel, and the association of that channel * with particular colours. This box may specify multiple descriptions for a * single channel. If a multiple component transform is specified within the * codestream, the image must be in an RGB colourspace and the red, green and * blue colours as channels 0, 1 and 2 in the codestream, respectively. * * This box is required within the JPEG 2000 header item property (`j2kH`). */ class Box_cdef : public Box { public: Box_cdef() { set_short_type(fourcc("cdef")); } std::string dump(Indent &) const override; Error write(StreamWriter &writer) const override; struct Channel { /** * Channel index (`Cn`). * * This field specifies the index of the channel for this description. * The value of this field represents the index of the channel as * defined within the Component Mapping box (or the actual component * from the codestream if the file does not contain a Component Mapping * box). */ uint16_t channel_index; /** * Channel type (`Typ`). * * Each channel type value shall be equal to 0 (colour), 1 (alpha) * or 2 (pre-multiplied alpha). * * At most one channel type value shall be equal to 1 or 2 (i.e. a * single alpha channel), and the corresponding association field * value shall be 0. */ uint16_t channel_type; /** * Channel association (`Asoc`). * * If the channel type value is 0 (colour), then the channel association * value shall be in the range [1, 216 -2]. * Interpretation of this depends on the colourspace. */ uint16_t channel_association; }; /** * Get the channels in this channel definition box. * * @return the channels as a read-only vector. */ const std::vector &get_channels() const { return m_channels; } /** * Add a channel to this channel definition box. * * @param channel the channel to add */ void add_channel(Channel channel) { m_channels.push_back(channel); } void set_channels(heif_colorspace colorspace); protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; private: std::vector m_channels; }; /** * JPEG 2000 Component Mapping box. * * The Component Mapping box defines how image channels are identified from the * actual components decoded from the codestream. This abstraction allows a * single structure (the Channel Definition box) to specify the colour or type * of both palettised images and non-palettised images. This box contains an * array of CMPi, MTYPi and PCOLi fields. Each * group of these fields represents the definition of one channel in the image. * The channels are numbered in order starting with zero, and the number of * channels specified in the Component Mapping box is determined by the length * of the box. * * There shall be at most one Component Mapping box inside a JP2 Header box. * If the JP2 Header box contains a Palette box, then the JP2 Header box * shall also contain a Component Mapping box. If the JP2 Header box does * not contain a Component Mapping box, the components shall be mapped * directly to channels, such that component @code{i} is mapped to channel * @code {i}. */ class Box_cmap : public Box { public: Box_cmap() { set_short_type(fourcc("cmap")); } std::string dump(Indent &) const override; Error write(StreamWriter &writer) const override; struct Component { uint16_t component_index; uint8_t mapping_type; uint8_t palette_colour; }; /** * Get the components in this component mapping box. * * @return the components as a read-only vector. */ const std::vector &get_components() const { return m_components; } /** * Add a component to this component mapping box. * * @param component the component to add */ void add_component(Component component) { m_components.push_back(component); } protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; private: std::vector m_components; }; /** * JPEG 2000 Palette box. * * This box defines the palette to be used to create multiple components * from a single component. */ class Box_pclr : public Box { public: Box_pclr() { set_short_type(fourcc("pclr")); } std::string dump(Indent &) const override; Error write(StreamWriter &writer) const override; struct PaletteEntry { // JPEG 2000 supports 38 bits and signed/unsigned, but we only // do up to 16 bit unsigned. std::vector columns; }; /** * Get the entries in this palette box. * * @return the entries as a read-only vector. */ const std::vector& get_entries() const { return m_entries; } /** * Get the bit depths for the columns in this palette box. * * @return the bit depths as a read-only vector. */ const std::vector& get_bit_depths() const { return m_bitDepths; } const uint8_t get_num_entries() const { return (uint8_t)(m_entries.size()); } const uint8_t get_num_columns() const { return (uint8_t)(m_bitDepths.size()); } void add_entry(const PaletteEntry entry) { m_entries.push_back(entry); } /** * Set columns for the palette mapping. * * Each column has the same bit depth. * * This will reset any existing columns and entries. * * @param num_columns the number of columns (e.g. 3 for RGB) * @param bit_depth the bit depth for each column (e.g. 8 for 24-bit RGB) */ void set_columns(uint8_t num_columns, uint8_t bit_depth); protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; private: std::vector m_bitDepths; std::vector m_entries; }; /** * JPEG 2000 layers box. * * The JPEG 2000 layers box declares a list of quality and resolution layers * for a JPEG 2000 codestream. * * @note the JPEG 2000 codestream can contain layers not listed in the JPEG 2000 * layers box. */ class Box_j2kL : public FullBox { public: Box_j2kL() { set_short_type(fourcc("j2kL")); } std::string dump(Indent &) const override; Error write(StreamWriter &writer) const override; struct Layer { /** * Unique identifier for the layer. */ uint16_t layer_id; /** * Number of resolution levels of the codestream that can be discarded. */ uint8_t discard_levels; /** * Minimum number of quality layers of the codestream to be decoded. */ uint16_t decode_layers; }; /** * Get the layers in this layer box. * * @return the layers as a read-only vector. */ const std::vector &get_layers() const { return m_layers; } /** * Add a layer to the layers box. * * @param layer the layer to add */ void add_layer(Layer layer) { m_layers.push_back(layer); } protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; private: std::vector m_layers; }; class Box_j2kH : public Box { public: Box_j2kH() { set_short_type(fourcc("j2kH")); } bool is_essential() const override { return true; } std::string dump(Indent &) const override; // Default write behaviour for a container is to write children protected: Error parse(BitstreamRange &range, const heif_security_limits* limits) override; }; class Jpeg2000ImageCodec { public: // static Error decode_jpeg2000_image(const HeifContext* context, // heif_item_id ID, // std::shared_ptr& img, // const std::vector& uncompressed_data); static Error encode_jpeg2000_image(const std::shared_ptr& heif_file, const std::shared_ptr& src_image, void* encoder_struct, const struct heif_encoding_options& options, std::shared_ptr& out_image); }; struct JPEG2000_SIZ_segment { /** * Decoder capabilities bitmap (Rsiz). */ uint16_t decoder_capabilities = 0; /** * Width of the reference grid (Xsiz). */ uint32_t reference_grid_width = 0; /** * Height of the reference grid (Ysiz). */ uint32_t reference_grid_height = 0; /** * Horizontal offset from reference grid origin to image left side (XOsiz). */ uint32_t image_horizontal_offset = 0; /** * Vertical offset from reference grid origin to image top size (YOsiz). */ uint32_t image_vertical_offset = 0; /** * Width of one reference tile with respect to the reference grid (XTsiz). */ uint32_t tile_width = 0; /** * Height of one reference tile with respect to the reference grid (YTsiz). */ uint32_t tile_height = 0; /** * Horizontal offset from the origin of the reference grid to left side of first tile (XTOsiz). */ uint32_t tile_offset_x = 0; /** * Vertical offset from the origin of the reference grid to top side of first tile (YTOsiz). */ uint32_t tile_offset_y = 0; struct component { uint8_t h_separation, v_separation; uint8_t precision; bool is_signed; }; std::vector components; }; class JPEG2000_Extension_Capability { public: JPEG2000_Extension_Capability(const uint8_t i) : ident(i) {} uint8_t getIdent() const { return ident; } uint16_t getValue() const { return value; } void setValue(uint16_t val) { value = val; } private: const uint8_t ident; uint16_t value = 0; }; class JPEG2000_Extension_Capability_HT : public JPEG2000_Extension_Capability { public: static const int IDENT = 15; JPEG2000_Extension_Capability_HT(): JPEG2000_Extension_Capability(IDENT) {}; }; class JPEG2000_CAP_segment { public: void push_back(JPEG2000_Extension_Capability ccap) { extensions.push_back(ccap); } bool hasHighThroughputExtension() const { for (auto &extension : extensions) { if (extension.getIdent() == 15) { return true; } } return false; } private: std::vector extensions; }; struct JPEG2000MainHeader { public: JPEG2000MainHeader() = default; Error parseHeader(const std::vector& compressedImageData); // Use parseHeader instead - these are mainly for unit testing Error doParse(); void setHeaderData(std::vector data) { headerData = std::move(data); } heif_chroma get_chroma_format() const; int get_precision(uint32_t index) const { if (index >= siz.components.size()) { return -1; } // TODO: this is a quick hack. It is more complicated for JPEG2000 because these can be any kind of colorspace (e.g. RGB). return siz.components[index].precision; } bool hasHighThroughputExtension() { return cap.hasHighThroughputExtension(); } uint32_t getXSize() const { return siz.reference_grid_width; } uint32_t getYSize() const { return siz.reference_grid_height; } const JPEG2000_SIZ_segment get_SIZ() { return siz; } private: Error parse_SOC_segment(); Error parse_SIZ_segment(); Error parse_CAP_segment_body(); void parse_Ccap15(); static const int MARKER_LEN = 2; JPEG2000_SIZ_segment siz; JPEG2000_CAP_segment cap; uint8_t read8() { uint8_t res = headerData[cursor]; cursor += 1; return res; } uint16_t read16() { uint16_t res = (uint16_t)((headerData[cursor] << 8) | headerData[cursor + 1]); cursor += 2; return res; } uint32_t read32() { uint32_t res = (headerData[cursor] << 24) | (headerData[cursor + 1] << 16) | (headerData[cursor + 2] << 8) | headerData[cursor + 3]; cursor += 4; return res; } std::vector headerData; size_t cursor = 0; }; #endif // LIBHEIF_JPEG2000_BOXES_H libheif-1.19.8/libheif/codecs/avif_boxes.cc000664 001750 001750 00000035414 15003473471 021625 0ustar00farindkfarindk000000 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 "pixelimage.h" #include "avif_boxes.h" #include "avif_dec.h" #include "bitstream.h" #include "common_utils.h" #include "libheif/api_structs.h" #include "file.h" #include #include #include #include // https://aomediacodec.github.io/av1-spec/av1-spec.pdf Error Box_av1C::parse(BitstreamRange& range, const heif_security_limits* limits) { //parse_full_box_header(range); if (!has_fixed_box_size()) { // Note: in theory, it is allowed to have an av1C box with unspecified size (until the end of the file), // but that would be very uncommon and give us problems in the calculation of `configOBUs_bytes` below. // It's better to error on this case than to open a DoS vulnerability. return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "av1C with unspecified box size"}; } uint8_t byte; auto& c = m_configuration; // abbreviation byte = range.read8(); if ((byte & 0x80) == 0) { // error: marker bit not set } c.version = byte & 0x7F; byte = range.read8(); c.seq_profile = (byte >> 5) & 0x7; c.seq_level_idx_0 = byte & 0x1f; byte = range.read8(); c.seq_tier_0 = (byte >> 7) & 1; c.high_bitdepth = (byte >> 6) & 1; c.twelve_bit = (byte >> 5) & 1; c.monochrome = (byte >> 4) & 1; c.chroma_subsampling_x = (byte >> 3) & 1; c.chroma_subsampling_y = (byte >> 2) & 1; c.chroma_sample_position = byte & 3; byte = range.read8(); c.initial_presentation_delay_present = (byte >> 4) & 1; if (c.initial_presentation_delay_present) { c.initial_presentation_delay_minus_one = byte & 0x0F; } const size_t configOBUs_bytes = range.get_remaining_bytes(); m_config_OBUs.resize(configOBUs_bytes); if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { // error } return range.get_error(); } Error Box_av1C::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); const auto& c = m_configuration; // abbreviation writer.write8(c.version | 0x80); writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | (c.seq_level_idx_0 & 0x1f))); writer.write8((uint8_t) ((c.seq_tier_0 ? 0x80 : 0) | (c.high_bitdepth ? 0x40 : 0) | (c.twelve_bit ? 0x20 : 0) | (c.monochrome ? 0x10 : 0) | (c.chroma_subsampling_x ? 0x08 : 0) | (c.chroma_subsampling_y ? 0x04 : 0) | (c.chroma_sample_position & 0x03))); writer.write8(0); // TODO initial_presentation_delay prepend_header(writer, box_start); return Error::Ok; } std::string Box_av1C::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); const auto& c = m_configuration; // abbreviation sstr << indent << "version: " << ((int) c.version) << "\n" << indent << "seq_profile: " << ((int) c.seq_profile) << "\n" << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n" << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n" << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n" << indent << "monochrome: " << ((int) c.monochrome) << "\n" << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n" << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n" << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n" << indent << "initial_presentation_delay: "; if (c.initial_presentation_delay_present) { sstr << c.initial_presentation_delay_minus_one + 1 << "\n"; } else { sstr << "not present\n"; } sstr << indent << "config OBUs:"; for (size_t i = 0; i < m_config_OBUs.size(); i++) { sstr << " " << std::hex << std::setfill('0') << std::setw(2) << ((int) m_config_OBUs[i]); } sstr << std::dec << "\n"; return sstr.str(); } Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std::shared_ptr& image) { int bpp = image->get_bits_per_pixel(heif_channel_Y); heif_chroma chroma = image->get_chroma_format(); uint8_t profile = compute_avif_profile(bpp, chroma); int width = image->get_width(heif_channel_Y); int height = image->get_height(heif_channel_Y); uint8_t level; if (width <= 8192 && height <= 4352 && (width * height) <= 8912896) { level = 13; // 5.1 } else if (width <= 16384 && height <= 8704 && (width * height) <= 35651584) { level = 17; // 6.1 } else { level = 31; // maximum } inout_config->seq_profile = profile; inout_config->seq_level_idx_0 = level; inout_config->high_bitdepth = (bpp > 8) ? 1 : 0; inout_config->twelve_bit = (bpp >= 12) ? 1 : 0; inout_config->monochrome = (chroma == heif_chroma_monochrome) ? 1 : 0; inout_config->chroma_subsampling_x = uint8_t(chroma_h_subsampling(chroma) >> 1); inout_config->chroma_subsampling_y = uint8_t(chroma_v_subsampling(chroma) >> 1); // 0 - CSP_UNKNOWN // 1 - CSP_VERTICAL // 2 - CSP_COLOCATED // 3 - CSP_RESERVED inout_config->chroma_sample_position = (chroma == heif_chroma_420 ? 0 : 2); return Error::Ok; } Error Box_a1op::parse(BitstreamRange& range, const heif_security_limits* limits) { op_index = range.read8(); return range.get_error(); } std::string Box_a1op::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "op-index: " << ((int) op_index) << "\n"; return sstr.str(); } Error Box_a1op::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write8(op_index); prepend_header(writer, box_start); return Error::Ok; } Error Box_a1lx::parse(BitstreamRange& range, const heif_security_limits* limits) { uint8_t flags = range.read8(); for (int i = 0; i < 3; i++) { if (flags & 1) { layer_size[i] = range.read32(); } else { layer_size[i] = range.read16(); } } return range.get_error(); } std::string Box_a1lx::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "layer-sizes: [" << layer_size[0] << "," << layer_size[1] << "," << layer_size[2] << "]\n"; return sstr.str(); } Error Box_a1lx::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); bool large = (layer_size[0] > 0xFFFF || layer_size[1] > 0xFFFF || layer_size[2] > 0xFFFF); writer.write8(large ? 1 : 0); for (int i = 0; i < 3; i++) { if (large) { writer.write32(layer_size[i]); } else { writer.write16((uint16_t) layer_size[i]); } } prepend_header(writer, box_start); return Error::Ok; } static uint64_t leb128(BitReader& reader) { uint64_t val = 0; for (int i = 0; i < 8; i++) { int64_t v = reader.get_bits(8); val |= (v & 0x7F) << (i * 7); if (!(v & 0x80)) { break; } } return val; } struct obu_header_info { int type; bool has_size; uint64_t size = 0; }; static obu_header_info read_obu_header_type(BitReader& reader) { obu_header_info info; reader.skip_bits(1); info.type = reader.get_bits(4); bool has_extension = reader.get_bits(1); info.has_size = reader.get_bits(1); reader.skip_bits(1); if (has_extension) { reader.skip_bits(8); } if (info.has_size) { info.size = leb128(reader); } return info; } const static int HEIF_OBU_SEQUENCE_HEADER = 1; const static int CP_UNSPECIFIED = 2; const static int TC_UNSPECIFIED = 2; const static int MC_UNSPECIFIED = 2; const static int CP_BT_709 = 1; const static int TC_SRGB = 13; const static int MC_IDENTITY = 0; const static int HEIF_CSP_UNKNOWN = 0; // 1 - CSP_VERTICAL // 2 - CSP_COLOCATED // 3 - CSP_RESERVED bool fill_av1C_configuration_from_stream(Box_av1C::configuration* out_config, const uint8_t* data, int dataSize) { BitReader reader(data, dataSize); // --- find OBU_SEQUENCE_HEADER bool seq_header_found = false; while (reader.get_bits_remaining() > 0) { obu_header_info header_info = read_obu_header_type(reader); if (header_info.type == HEIF_OBU_SEQUENCE_HEADER) { seq_header_found = true; break; } else if (header_info.has_size) { if (header_info.size > (uint64_t)std::numeric_limits::max()) { return false; } reader.skip_bytes((int)header_info.size); } else { return false; } } if (!seq_header_found) { return false; } // --- read sequence header int dummy; // throw away value bool decoder_model_info_present = false; int buffer_delay_length_minus1 = 0; out_config->seq_profile = (uint8_t)reader.get_bits(3); bool still_picture = reader.get_bits(1); (void) still_picture; bool reduced_still_picture = reader.get_bits(1); if (reduced_still_picture) { out_config->seq_level_idx_0 = (uint8_t)reader.get_bits(5); out_config->seq_tier_0 = 0; } else { bool timing_info_present_flag = reader.get_bits(1); if (timing_info_present_flag) { // --- skip timing info reader.skip_bytes(2 * 4); bool equal_picture_interval = reader.get_bits(1); if (equal_picture_interval) { reader.get_uvlc(&dummy); } // --- skip decoder_model_info decoder_model_info_present = reader.get_bits(1); if (decoder_model_info_present) { buffer_delay_length_minus1 = reader.get_bits(5); reader.skip_bits(32); reader.skip_bits(10); } } bool initial_display_delay_present_flag = reader.get_bits(1); int operating_points_cnt_minus1 = reader.get_bits(5); for (int i = 0; i <= operating_points_cnt_minus1; i++) { reader.skip_bits(12); auto level = (uint8_t) reader.get_bits(5); if (i == 0) { out_config->seq_level_idx_0 = level; } if (level > 7) { auto tier = (uint8_t) reader.get_bits(1); if (i == 0) { out_config->seq_tier_0 = tier; } } if (decoder_model_info_present) { bool decoder_model_present_for_this = reader.get_bits(1); if (decoder_model_present_for_this) { int n = buffer_delay_length_minus1 + 1; reader.skip_bits(n); reader.skip_bits(n); reader.skip_bits(1); } } if (initial_display_delay_present_flag) { bool initial_display_delay_present_for_this = reader.get_bits(1); if (i==0) { out_config->initial_presentation_delay_present = initial_display_delay_present_for_this; } if (initial_display_delay_present_for_this) { auto delay = (uint8_t)reader.get_bits(4); if (i==0) { out_config->initial_presentation_delay_minus_one = delay; } } } } } int frame_width_bits_minus1 = reader.get_bits(4); int frame_height_bits_minus1 = reader.get_bits(4); int max_frame_width_minus1 = reader.get_bits(frame_width_bits_minus1 + 1); int max_frame_height_minus1 = reader.get_bits(frame_height_bits_minus1 + 1); (void)max_frame_width_minus1; (void)max_frame_height_minus1; // printf("max size: %d x %d\n", max_frame_width_minus1+1, max_frame_height_minus1+1); int frame_id_numbers_present_flag = 0; if (!reduced_still_picture) { frame_id_numbers_present_flag = reader.get_bits(1); } if (frame_id_numbers_present_flag) { reader.skip_bits(7); } reader.skip_bits(3); if (!reduced_still_picture) { reader.skip_bits(4); // order hint bool enable_order_hint = reader.get_bits(1); if (enable_order_hint) { reader.skip_bits(2); } // screen content int force_screen_content_tools = 2; if (reader.get_bits(1) == 0) { force_screen_content_tools = reader.get_bits(1); } if (force_screen_content_tools > 0) { // integer mv if (reader.get_bits(1) == 0) { reader.skip_bits(1); } } if (enable_order_hint) { reader.skip_bits(3); } } reader.skip_bits(3); // --- color config out_config->high_bitdepth = (uint8_t)reader.get_bits(1); if (out_config->seq_profile == 2 && out_config->high_bitdepth) { out_config->twelve_bit = (uint8_t)reader.get_bits(1); } else { out_config->twelve_bit = 0; } if (out_config->seq_profile == 1) { out_config->monochrome = 0; } else { out_config->monochrome = (uint8_t)reader.get_bits(1); } int color_primaries = CP_UNSPECIFIED; int transfer_characteristics = TC_UNSPECIFIED; int matrix_coefficients = MC_UNSPECIFIED; bool color_description_preset_flag = reader.get_bits(1); if (color_description_preset_flag) { color_primaries = reader.get_bits(8); transfer_characteristics = reader.get_bits(8); matrix_coefficients = reader.get_bits(8); } else { // color description unspecified } if (out_config->monochrome) { reader.skip_bits(1); out_config->chroma_subsampling_x = 1; out_config->chroma_subsampling_y = 1; out_config->chroma_sample_position = HEIF_CSP_UNKNOWN; } else if (color_primaries == CP_BT_709 && transfer_characteristics == TC_SRGB && matrix_coefficients == MC_IDENTITY) { out_config->chroma_subsampling_x = 0; out_config->chroma_subsampling_y = 0; } else { reader.skip_bits(1); if (out_config->seq_profile == 0) { out_config->chroma_subsampling_x = 1; out_config->chroma_subsampling_y = 1; } else if (out_config->seq_profile == 1) { out_config->chroma_subsampling_x = 0; out_config->chroma_subsampling_y = 0; } else { if (out_config->twelve_bit) { out_config->chroma_subsampling_x = (uint8_t)reader.get_bits(1); if (out_config->chroma_subsampling_x) { out_config->chroma_subsampling_y = (uint8_t)reader.get_bits(1); } else { out_config->chroma_subsampling_y = 0; } } else { out_config->chroma_subsampling_x = 1; out_config->chroma_subsampling_y = 0; } } if (out_config->chroma_subsampling_x && out_config->chroma_subsampling_y) { out_config->chroma_sample_position = (uint8_t)reader.get_bits(2); } } reader.skip_bits(1); // separate_uv_delta return true; } libheif-1.19.8/libheif/codecs/avc_dec.cc000664 001750 001750 00000003121 15003473471 021052 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "avc_dec.h" #include "avc_boxes.h" #include "error.h" #include "context.h" #include Result> Decoder_AVC::read_bitstream_configuration_data() const { std::vector data; m_avcC->get_header_nals(data); return data; } int Decoder_AVC::get_luma_bits_per_pixel() const { return m_avcC->get_configuration().bit_depth_luma; } int Decoder_AVC::get_chroma_bits_per_pixel() const { return m_avcC->get_configuration().bit_depth_chroma; } Error Decoder_AVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { *out_chroma = m_avcC->get_configuration().chroma_format; if (*out_chroma == heif_chroma_monochrome) { *out_colorspace = heif_colorspace_monochrome; } else { *out_colorspace = heif_colorspace_YCbCr; } return Error::Ok; } libheif-1.19.8/libheif/codecs/jpeg2000_dec.h000664 001750 001750 00000003123 15003473471 021374 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_JPEG2000_DEC_H #define HEIF_JPEG2000_DEC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "image-items/jpeg2000.h" #include class Decoder_JPEG2000 : public Decoder { public: Decoder_JPEG2000(const std::shared_ptr& j2kH) : m_j2kH(j2kH) {} heif_compression_format get_compression_format() const override { return heif_compression_JPEG2000; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_j2kH; }; #endif libheif-1.19.8/libheif/codecs/avc_dec.h000664 001750 001750 00000002752 15003473471 020725 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 HEIF_AVC_DEC_H #define HEIF_AVC_DEC_H #include "libheif/heif.h" #include "error.h" #include #include #include class Box_avcC; class Decoder_AVC : public Decoder { public: explicit Decoder_AVC(const std::shared_ptr& avcC) : m_avcC(avcC) {} heif_compression_format get_compression_format() const override { return heif_compression_AVC; } int get_luma_bits_per_pixel() const override; int get_chroma_bits_per_pixel() const override; Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; Result> read_bitstream_configuration_data() const override; private: const std::shared_ptr m_avcC; }; #endif libheif-1.19.8/libheif/codecs/jpeg_boxes.h000664 001750 001750 00000002644 15003473471 021466 0ustar00farindkfarindk000000 000000 /* * HEIF JPEG codec. * Copyright (c) 2023 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_JPEG_BOXES_H #define LIBHEIF_JPEG_BOXES_H #include "box.h" #include #include #include "image-items/image_item.h" #include class Box_jpgC : public Box { public: Box_jpgC() { set_short_type(fourcc("jpgC")); } const std::vector& get_data() const { return m_data; } void set_data(const std::vector& data) { m_data = data; } std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: std::vector m_data; }; #endif // LIBHEIF_JPEG_BOXES_H libheif-1.19.8/libheif/plugins_unix.h000664 001750 001750 00000002754 15003473471 020627 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2023 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_PLUGINS_UNIX_H #define LIBHEIF_PLUGINS_UNIX_H #include #include #include "init.h" std::vector get_plugin_directories_from_environment_variable_unix(); std::vector list_all_potential_plugins_in_directory_unix(const char*); class PluginLibrary_Unix : public PluginLibrary { public: heif_error load_from_file(const char* filename) override; void release() override; heif_plugin_info* get_plugin_info() override { return m_plugin_info; } bool operator==(const PluginLibrary_Unix& b) const { return m_library_handle == b.m_library_handle; } private: void* m_library_handle = nullptr; heif_plugin_info* m_plugin_info = nullptr; }; #endif //LIBHEIF_PLUGINS_UNIX_H libheif-1.19.8/libheif/bitstream.cc000664 001750 001750 00000042700 15003473471 020226 0ustar00farindkfarindk000000 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 "bitstream.h" #include #include #include #if ((defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__PGI)) && __GNUC__ < 9) || (defined(__clang__) && __clang_major__ < 10) #include #else #include #endif #define MAX_UVLC_LEADING_ZEROS 20 StreamReader_istream::StreamReader_istream(std::unique_ptr&& istr) : m_istr(std::move(istr)) { m_istr->seekg(0, std::ios_base::end); m_length = m_istr->tellg(); m_istr->seekg(0, std::ios_base::beg); } uint64_t StreamReader_istream::get_position() const { return m_istr->tellg(); } StreamReader::grow_status StreamReader_istream::wait_for_file_size(uint64_t target_size) { return (target_size > m_length) ? grow_status::size_beyond_eof : grow_status::size_reached; } bool StreamReader_istream::read(void* data, size_t size) { uint64_t end_pos = get_position() + size; if (end_pos > m_length) { return false; } m_istr->read((char*) data, size); return true; } bool StreamReader_istream::seek(uint64_t position) { if (position > m_length) return false; m_istr->seekg(position, std::ios_base::beg); return true; } StreamReader_memory::StreamReader_memory(const uint8_t* data, size_t size, bool copy) : m_length(size), m_position(0) { if (copy) { m_owned_data = new uint8_t[m_length]; memcpy(m_owned_data, data, size); m_data = m_owned_data; } else { m_data = data; } } StreamReader_memory::~StreamReader_memory() { if (m_owned_data) { delete[] m_owned_data; } } uint64_t StreamReader_memory::get_position() const { return m_position; } StreamReader::grow_status StreamReader_memory::wait_for_file_size(uint64_t target_size) { return (target_size > m_length) ? grow_status::size_beyond_eof : grow_status::size_reached; } bool StreamReader_memory::read(void* data, size_t size) { uint64_t end_pos = m_position + size; if (end_pos > m_length) { return false; } memcpy(data, &m_data[m_position], size); m_position += size; return true; } bool StreamReader_memory::seek(uint64_t position) { if (position > m_length) return false; m_position = position; return true; } StreamReader_CApi::StreamReader_CApi(const heif_reader* func_table, void* userdata) : m_func_table(func_table), m_userdata(userdata) { } StreamReader::grow_status StreamReader_CApi::wait_for_file_size(uint64_t target_size) { heif_reader_grow_status status = m_func_table->wait_for_file_size(target_size, m_userdata); switch (status) { case heif_reader_grow_status_size_reached: return grow_status::size_reached; case heif_reader_grow_status_timeout: return grow_status::timeout; case heif_reader_grow_status_size_beyond_eof: return grow_status::size_beyond_eof; default: assert(0); return grow_status::size_beyond_eof; } } BitstreamRange::BitstreamRange(std::shared_ptr istr, size_t length, BitstreamRange* parent) : m_istr(std::move(istr)), m_parent_range(parent), m_remaining(length) { if (parent) { m_nesting_level = parent->m_nesting_level + 1; } } BitstreamRange::BitstreamRange(std::shared_ptr istr, size_t start, size_t end) // one past end : m_istr(std::move(istr)), m_remaining(end) { bool success = m_istr->seek(start); assert(success); (void)success; // TODO } StreamReader::grow_status BitstreamRange::wait_until_range_is_available() { return m_istr->wait_for_file_size(m_istr->get_position() + m_remaining); } uint8_t BitstreamRange::read8() { if (!prepare_read(1)) { return 0; } uint8_t buf; auto istr = get_istream(); bool success = istr->read((char*) &buf, 1); if (!success) { set_eof_while_reading(); return 0; } return buf; } uint16_t BitstreamRange::read16() { if (!prepare_read(2)) { return 0; } uint8_t buf[2]; auto istr = get_istream(); bool success = istr->read((char*) buf, 2); if (!success) { set_eof_while_reading(); return 0; } return static_cast((buf[0] << 8) | (buf[1])); } int16_t BitstreamRange::read16s() { uint16_t v = read16(); if (v & 0x8000) { auto val = static_cast((~v) & 0x7fff); return static_cast(-val - 1); } else { return static_cast(v); } } uint32_t BitstreamRange::read24() { if (!prepare_read(3)) { return 0; } uint8_t buf[3]; auto istr = get_istream(); bool success = istr->read((char*) buf, 3); if (!success) { set_eof_while_reading(); return 0; } return (uint32_t) ((buf[0] << 16) | (buf[1] << 8) | (buf[2])); } uint32_t BitstreamRange::read32() { if (!prepare_read(4)) { return 0; } uint8_t buf[4]; auto istr = get_istream(); bool success = istr->read((char*) buf, 4); if (!success) { set_eof_while_reading(); return 0; } return (uint32_t) ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3])); } uint64_t BitstreamRange::read_uint(int len) { switch (len) { case 8: return read8(); case 16: return read16(); case 24: return read24(); case 32: return read32(); case 64: return read64(); default: assert(false); return 0; } } int32_t BitstreamRange::read32s() { uint32_t v = read32(); if (v & 0x80000000) { return -static_cast((~v) & 0x7fffffff) -1; } else { return static_cast(v); } } uint64_t BitstreamRange::read64() { if (!prepare_read(8)) { return 0; } uint8_t buf[8]; auto istr = get_istream(); bool success = istr->read((char*) buf, 8); if (!success) { set_eof_while_reading(); return 0; } return ((static_cast(buf[0]) << 56) | (static_cast(buf[1]) << 48) | (static_cast(buf[2]) << 40) | (static_cast(buf[3]) << 32) | (static_cast(buf[4]) << 24) | (static_cast(buf[5]) << 16) | (static_cast(buf[6]) << 8) | (static_cast(buf[7]))); } int64_t BitstreamRange::read64s() { uint64_t v = read64(); if (v & 0x8000000000000000) { return -static_cast((~v) & 0x7fffffffffffffff) -1; } else { return static_cast(v); } } float BitstreamRange::read_float32() { #if __cpp_lib_bit_cast >= 201806L uint32_t i = read32(); return std::bit_cast(i); // this works directly on the value layout, thus we do not have to worry about memory layout #else // compiler too old to support bit_cast // TODO: I am not sure this works everywhere as there seem to be systems where // the float byte order is different from the integer endianness // https://en.wikipedia.org/wiki/Endianness#Floating_point int i = read32(); float f; memcpy(&f, &i, sizeof(float)); return f; #endif } void StreamWriter::write_float32(float v) { #if __cpp_lib_bit_cast >= 201806L write32(std::bit_cast(v)); // this works directly on the value layout, thus we do not have to worry about memory layout #else // compiler too old to support bit_cast // TODO: I am not sure this works everywhere as there seem to be systems where // the float byte order is different from the integer endianness // https://en.wikipedia.org/wiki/Endianness#Floating_point uint32_t i; memcpy(&i, &v, sizeof(float)); write32(i); #endif } std::string BitstreamRange::read_string() { std::string str; // Reading a string when no more data is available, returns an empty string. // Such a case happens, for example, when reading a 'url' box without content. if (eof()) { return std::string(); } for (;;) { if (!prepare_read(1)) { return std::string(); } auto istr = get_istream(); char c; bool success = istr->read(&c, 1); if (!success) { set_eof_while_reading(); return std::string(); } if (c == 0) { break; } else { str += (char) c; } } return str; } bool BitstreamRange::read(uint8_t* data, size_t n) { if (!prepare_read(n)) { return false; } auto istr = get_istream(); bool success = istr->read(data, n); if (!success) { set_eof_while_reading(); } return success; } bool BitstreamRange::prepare_read(size_t nBytes) { // Note: we do not test for negative nBytes anymore because we now use the unsigned size_t if (m_remaining < nBytes) { // --- not enough data left in box -> move to end of box and set error flag skip_to_end_of_box(); m_error = true; return false; } else { // --- this is the normal case (m_remaining >= nBytes) if (m_parent_range) { if (!m_parent_range->prepare_read(nBytes)) { return false; } } m_remaining -= nBytes; return true; } } StreamReader::grow_status BitstreamRange::wait_for_available_bytes(size_t nBytes) { int64_t target_size = m_istr->get_position() + nBytes; return m_istr->wait_for_file_size(target_size); } void BitstreamRange::skip_without_advancing_file_pos(size_t n) { assert(n <= m_remaining); m_remaining -= n; if (m_parent_range) { m_parent_range->skip_without_advancing_file_pos(n); } } BitReader::BitReader(const uint8_t* buffer, int len) { data = buffer; data_length = len; bytes_remaining = len; nextbits = 0; nextbits_cnt = 0; refill(); } uint32_t BitReader::get_bits(int n) { assert(n <= 32); if (nextbits_cnt < n) { refill(); } uint64_t val = nextbits; val >>= 64 - n; #if AVOID_FUZZER_FALSE_POSITIVE // Shifting an unsigned integer left such that some MSBs fall out is well defined in C++ despite the fuzzer claiming otherwise. nextbits &= (0xffffffffffffffffULL >> n); #endif nextbits <<= n; nextbits_cnt -= n; return static_cast(val); } uint8_t BitReader::get_bits8(int n) { assert(n>0 && n <= 8); return static_cast(get_bits(n)); } uint16_t BitReader::get_bits16(int n) { assert(n>0 && n <= 16); return static_cast(get_bits(n)); } uint32_t BitReader::get_bits32(int n) { assert(n>0 && n <= 32); return static_cast(get_bits(n)); } int32_t BitReader::get_bits32s() { uint32_t bits = get_bits(32); return static_cast(bits); } bool BitReader::get_flag() { return (get_bits(1) == 0x01); } std::vector BitReader::read_bytes(uint32_t n) { // TODO: this implementation isn't very efficient std::vector bytes; for (uint32_t i = 0; i < n; i++) { bytes.push_back(get_bits8(8)); } return bytes; } int BitReader::get_bits_fast(int n) { assert(nextbits_cnt >= n); uint64_t val = nextbits; val >>= 64 - n; nextbits <<= n; nextbits_cnt -= n; return (int) val; } int BitReader::peek_bits(int n) { if (nextbits_cnt < n) { refill(); } uint64_t val = nextbits; val >>= 64 - n; return (int) val; } void BitReader::skip_bytes(int nBytes) { // TODO: this is slow while (nBytes--) { skip_bits(8); } } void BitReader::skip_bits(int n) { if (nextbits_cnt < n) { refill(); } #if AVOID_FUZZER_FALSE_POSITIVE nextbits &= (0xffffffffffffffffULL >> n); #endif nextbits <<= n; nextbits_cnt -= n; } void BitReader::skip_bits_fast(int n) { #if AVOID_FUZZER_FALSE_POSITIVE nextbits &= (0xffffffffffffffffULL >> n); #endif nextbits <<= n; nextbits_cnt -= n; } void BitReader::skip_to_byte_boundary() { int nskip = (nextbits_cnt & 7); #if AVOID_FUZZER_FALSE_POSITIVE nextbits &= (0xffffffffffffffffULL >> nskip); #endif nextbits <<= nskip; nextbits_cnt -= nskip; } bool BitReader::get_uvlc(int* value) { int num_zeros = 0; while (get_bits(1) == 0) { num_zeros++; if (num_zeros > MAX_UVLC_LEADING_ZEROS) { return false; } } int offset = 0; if (num_zeros != 0) { offset = (int) get_bits(num_zeros); *value = offset + (1 << num_zeros) - 1; assert(*value > 0); return true; } else { *value = 0; return true; } } bool BitReader::get_svlc(int* value) { int v; if (!get_uvlc(&v)) { return false; } else if (v == 0) { *value = v; return true; } bool negative = ((v & 1) == 0); *value = negative ? -v / 2 : (v + 1) / 2; return true; } void BitReader::refill() { #if 0 // TODO: activate me once I'm sure this works while (nextbits_cnt <= 64-8 && bytes_remaining) { uint64_t newval = *data++; bytes_remaining--; nextbits_cnt += 8; newval <<= 64-nextbits_cnt; nextbits |= newval; } #else int shift = 64 - nextbits_cnt; while (shift >= 8 && bytes_remaining) { uint64_t newval = *data++; bytes_remaining--; shift -= 8; newval <<= shift; nextbits |= newval; } nextbits_cnt = 64 - shift; #endif } void StreamWriter::write8(uint8_t v) { if (m_position == m_data.size()) { m_data.push_back(v); m_position++; } else { m_data[m_position++] = v; } } void StreamWriter::write16(uint16_t v) { size_t required_size = m_position + 2; if (required_size > m_data.size()) { m_data.resize(required_size); } m_data[m_position++] = uint8_t((v >> 8) & 0xFF); m_data[m_position++] = uint8_t(v & 0xFF); } void StreamWriter::write16s(int16_t v16s) { uint16_t v; if (v16s >= 0) { v = static_cast(v16s); } else { auto val = static_cast((-v16s-1)); v = static_cast(~val); } write16(v); } void StreamWriter::write24(uint32_t v) { size_t required_size = m_position + 3; if (required_size > m_data.size()) { m_data.resize(required_size); } m_data[m_position++] = uint8_t((v >> 16) & 0xFF); m_data[m_position++] = uint8_t((v >> 8) & 0xFF); m_data[m_position++] = uint8_t(v & 0xFF); } void StreamWriter::write32(uint32_t v) { size_t required_size = m_position + 4; if (required_size > m_data.size()) { m_data.resize(required_size); } m_data[m_position++] = uint8_t((v >> 24) & 0xFF); m_data[m_position++] = uint8_t((v >> 16) & 0xFF); m_data[m_position++] = uint8_t((v >> 8) & 0xFF); m_data[m_position++] = uint8_t(v & 0xFF); } void StreamWriter::write32s(int32_t v32s) { uint32_t v; if (v32s >= 0) { v = static_cast(v32s); } else { v = ~static_cast((-v32s-1)); } write32(v); } void StreamWriter::write64(uint64_t v) { size_t required_size = m_position + 8; if (required_size > m_data.size()) { m_data.resize(required_size); } m_data[m_position++] = uint8_t((v >> 56) & 0xFF); m_data[m_position++] = uint8_t((v >> 48) & 0xFF); m_data[m_position++] = uint8_t((v >> 40) & 0xFF); m_data[m_position++] = uint8_t((v >> 32) & 0xFF); m_data[m_position++] = uint8_t((v >> 24) & 0xFF); m_data[m_position++] = uint8_t((v >> 16) & 0xFF); m_data[m_position++] = uint8_t((v >> 8) & 0xFF); m_data[m_position++] = uint8_t(v & 0xFF); } void StreamWriter::write64s(int64_t v) { write64(reinterpret_cast(v)); } void StreamWriter::write(int size, uint64_t value) { if (size == 1) { assert(value <= 0xFF); write8((uint8_t) value); } else if (size == 2) { assert(value <= 0xFFFF); write16((uint16_t) value); } else if (size == 4) { assert(value <= 0xFFFFFFFF); write32((uint32_t) value); } else if (size == 8) { write64((uint64_t) value); } else { assert(false); // unimplemented size } } void StreamWriter::write(const std::string& str) { size_t required_size = m_position + str.size() + 1; if (required_size > m_data.size()) { m_data.resize(required_size); } for (size_t i = 0; i < str.size(); i++) { m_data[m_position++] = str[i]; } m_data[m_position++] = 0; } void StreamWriter::write(const std::vector& vec) { size_t required_size = m_position + vec.size(); if (required_size > m_data.size()) { m_data.resize(required_size); } memcpy(m_data.data() + m_position, vec.data(), vec.size()); m_position += vec.size(); } void StreamWriter::write(const StreamWriter& writer) { size_t required_size = m_position + writer.get_data().size(); if (required_size > m_data.size()) { m_data.resize(required_size); } const auto& data = writer.get_data(); memcpy(m_data.data() + m_position, data.data(), data.size()); m_position += data.size(); } void StreamWriter::skip(int n) { assert(m_position == m_data.size()); m_data.resize(m_data.size() + n); m_position += n; } void StreamWriter::insert(int nBytes) { assert(nBytes >= 0); if (nBytes == 0) { return; } m_data.resize(m_data.size() + nBytes); if (m_position < m_data.size() - nBytes) { memmove(m_data.data() + m_position + nBytes, m_data.data() + m_position, m_data.size() - nBytes - m_position); } } libheif-1.19.8/libheif/logging.cc000664 001750 001750 00000003434 15003473471 017663 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024 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 "logging.h" #include #include std::string Indent::get_string() const { std::stringstream sstr; for (int i = 0; i < get_indent(); i++) { sstr << "| "; } return sstr.str(); } std::string write_raw_data_as_hex(const uint8_t* data, size_t len, const std::string& firstLineIndent, const std::string& remainingLinesIndent) { std::stringstream sstr; sstr << std::hex << std::setfill('0'); for (size_t i = 0; i < len; i++) { if (i % 16 == 0) { // start of line if (i == 0) { sstr << firstLineIndent; } else { sstr << remainingLinesIndent; } sstr << std::setw(4) << i << ": "; // address } else if (i % 16 == 8) { // space in middle sstr << " "; } else { // space between bytes sstr << " "; } sstr << std::setw(2) << ((int) data[i]); if (i % 16 == 15 || i == len - 1) { sstr << "\n"; } } return sstr.str(); } libheif-1.19.8/libheif/Doxyfile.in000664 001750 001750 00000333415 15003473471 020046 0ustar00farindkfarindk000000 000000 # Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "libheif" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/apidoc/ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif.h \ @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_items.h \ @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.pyw \ *.f90 \ *.f95 \ *.f03 \ *.f08 \ *.f18 \ *.f \ *.for \ *.vhd \ *.vhdl \ *.ucf \ *.qsf \ *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: # http://clang.llvm.org/) for more accurate parsing at the cost of reduced # performance. This can be particularly helpful with template rich C++ code for # which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to # YES then doxygen will add the directory of each input to the include path. # The default value is: YES. CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to # specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /