libheif-1.20.2/README.md000664 001750 001750 00000041165 15044356510 015567 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 HEIC 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.20.2/CPPLINT.cfg000664 001750 001750 00000000062 15044356510 016071 0ustar00farindkfarindk000000 000000 set noparent filter=-,+build/include_what_you_use libheif-1.20.2/COPYING000664 001750 001750 00000126516 15044356510 015347 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.20.2/libheif/000775 001750 001750 00000000000 15044356511 015704 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/security_limits.h000664 001750 001750 00000004710 15044356510 021306 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); class TotalMemoryTracker { public: explicit TotalMemoryTracker(const heif_security_limits* limits_context); ~TotalMemoryTracker(); size_t get_max_total_memory_used() const; void operator=(const TotalMemoryTracker&) = delete; TotalMemoryTracker(const TotalMemoryTracker&) = delete; private: const heif_security_limits* m_limits_context = nullptr; }; class MemoryHandle { public: MemoryHandle() = default; ~MemoryHandle() { free(); } Error alloc(size_t memory_amount, const heif_security_limits* limits_context, const char* reason_description); void free(); void free(size_t memory_amount); const heif_security_limits* get_security_limits() const { return m_limits_context; } void operator=(const MemoryHandle&) = delete; MemoryHandle(const MemoryHandle&) = delete; private: const heif_security_limits* m_limits_context = nullptr; size_t m_memory_amount = 0; }; #endif // LIBHEIF_SECURITY_LIMITS_H libheif-1.20.2/libheif/nclx.h000664 001750 001750 00000012321 15044356510 017017 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.20.2/libheif/bitstream.h000664 001750 001750 00000031751 15044356510 020055 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 { auto result = m_func_table->wait_for_file_size(end_pos, m_userdata); if (result == heif_reader_grow_status_size_reached) { return end_pos; } else { uint64_t pos = m_func_table->get_position(m_userdata); return bisect_filesize(pos,end_pos); } } } uint64_t bisect_filesize(uint64_t mini, uint64_t maxi) { // mini - <= filesize // maxi - > filesize if (maxi == mini + 1) { return mini; } uint64_t pos = (mini + maxi) / 2; auto result = m_func_table->wait_for_file_size(pos, m_userdata); if (result == heif_reader_grow_status_size_reached) { return bisect_filesize(pos, maxi); } else { return bisect_filesize(mini, pos); } } 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(); // A string stored with a fixed number of bytes. The first byte contains the string length and the extra bytes // are filled with a padding 0. std::string read_fixed_string(int len); 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_fixed_string(std::string s, size_t len); 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.20.2/libheif/compression_zlib.cc000664 001750 001750 00000010142 15044356510 021571 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.20.2/libheif/box.h000664 001750 001750 00000134344 15044356510 016655 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 "libheif/heif_tai_timestamps.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; virtual const char* debug_box_name() const { return nullptr; } 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(); virtual void patch_file_pointers(StreamWriter&, size_t offset) {} void patch_file_pointers_recursively(StreamWriter&, size_t offset); 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; const char* debug_box_name() const override { return "File Type"; } 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; const char* debug_box_name() const override { return "Free Space"; } 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; const char* debug_box_name() const override { return "Metadata"; } 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; const char* debug_box_name() const override { return "Handler Reference"; } 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; const char* debug_box_name() const override { return "Primary Item"; } 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; const char* debug_box_name() const override { return "Item Location"; } 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; const char* debug_box_name() const override { return "Item Info Entry"; } 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; const char* debug_box_name() const override { return "Item Information"; } 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; const char* debug_box_name() const override { return "Item Properties"; } 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; const char* debug_box_name() const override { return "Item Property Container"; } 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; const char* debug_box_name() const override { return "Image Spatial Extents"; } 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; const char* debug_box_name() const override { return "Item Property Association"; } 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; const char* debug_box_name() const override { return "Image Properties for Auxiliary Images"; } 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; const char* debug_box_name() const override { return "Image Rotation"; } 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; const char* debug_box_name() const override { return "Image Mirroring"; } [[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; const char* debug_box_name() const override { return "Clean Aperture"; } 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; const char* debug_box_name() const override { return "Item Reference"; } 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; const char* debug_box_name() const override { return "Item Data"; } 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; const char* debug_box_name() const override { return "Groups List"; } 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; const char* debug_box_name() const override { return "Stereo pair"; } 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; const char* debug_box_name() const override { return "Image pyramid group"; } 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; const char* debug_box_name() const override { return "Data Information"; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; }; class Box_dref : public FullBox { public: std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Data Reference"; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; }; class Box_url : public FullBox { public: std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Data Entry URL"; } 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; const char* debug_box_name() const override { return "Pixel Information"; } 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; const char* debug_box_name() const override { return "Pixel Aspect Ratio"; } 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; const char* debug_box_name() const override { return "Layer Selection"; } 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; const char* debug_box_name() const override { return "Content Light Level Information"; } 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; const char* debug_box_name() const override { return "Master Display Colour Volume"; } 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; const char* debug_box_name() const override { return "Ambient Viewing Environment"; } 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; // TODO const char* debug_box_name() const override { return ""; } 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; const char* debug_box_name() const override { return "Camera Intrinsic Matrix"; } 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; const char* debug_box_name() const override { return "Camera Extrinsic Matrix"; } 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; const char* debug_box_name() const override { return "User Description"; } 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; }; void initialize_heif_tai_clock_info(heif_tai_clock_info* taic); void initialize_heif_tai_timestamp_packet(heif_tai_timestamp_packet* itai); class Box_taic : public FullBox { public: Box_taic() { set_short_type(fourcc("taic")); initialize_heif_tai_clock_info(&m_info); } static std::string dump(const heif_tai_clock_info& info, Indent&); std::string dump(Indent&) const override; const char* debug_box_name() const override { return "TAI Clock Information"; } 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_info.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_info.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_info.clock_drift_rate = clock_drift_rate; } /** * clock_type. * * 0 = Clock type is unknown * 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_info.clock_type = clock_type; } uint64_t get_time_uncertainty() const { return m_info.time_uncertainty; } uint32_t get_clock_resolution() const { return m_info.clock_resolution; } int32_t get_clock_drift_rate() const { return m_info.clock_drift_rate; } uint8_t get_clock_type() const { return m_info.clock_type; } void set_from_tai_clock_info(const heif_tai_clock_info* info) { heif_tai_clock_info_copy(&m_info, info); } const heif_tai_clock_info* get_tai_clock_info() const { return &m_info; } bool operator==(const Box& other) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: heif_tai_clock_info m_info; }; bool operator==(const heif_tai_clock_info& a, const heif_tai_clock_info& b); class Box_itai : public FullBox { public: Box_itai() { set_short_type(fourcc("itai")); initialize_heif_tai_timestamp_packet(&m_timestamp); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Item TAI Timestamp"; } Error write(StreamWriter& writer) const override; static std::vector encode_tai_to_bitstream(const heif_tai_timestamp_packet*); static Result decode_tai_from_vector(const std::vector&); /** * The number of nanoseconds since the TAI epoch of 1958-01-01T00:00:00.0Z. */ void set_tai_timestamp(uint64_t timestamp) { m_timestamp.tai_timestamp = timestamp; } /** * synchronization_state (0=unsynchronized, 1=synchronized) */ void set_synchronization_state(bool state) { m_timestamp.synchronization_state = state; } /** * timestamp_generation_failure (0=generated, 1=failed) */ void set_timestamp_generation_failure(bool failure) { m_timestamp.timestamp_generation_failure = failure; } /** * timestamp_is_modified (0=original 1=modified) */ void set_timestamp_is_modified(bool is_modified) { m_timestamp.timestamp_is_modified = is_modified; } uint64_t get_tai_timestamp() const { return m_timestamp.tai_timestamp; } bool get_synchronization_state() const { return m_timestamp.synchronization_state; } bool get_timestamp_generation_failure() const { return m_timestamp.timestamp_generation_failure; } bool get_timestamp_is_modified() const { return m_timestamp.timestamp_is_modified; } void set_from_tai_timestamp_packet(const heif_tai_timestamp_packet* tai) { heif_tai_timestamp_packet_copy(&m_timestamp, tai); } const heif_tai_timestamp_packet* get_tai_timestamp_packet() const { return &m_timestamp; } bool operator==(const Box& other) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: heif_tai_timestamp_packet m_timestamp; }; bool operator==(const heif_tai_timestamp_packet& a, const heif_tai_timestamp_packet& b); #endiflibheif-1.20.2/libheif/context.cc000664 001750 001750 00000156503 15044356510 017710 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 "brands.h" #include #include #include #include #include #include #include #include #include "image-items/image_item.h" #include #include "sequences/track.h" #include "sequences/track_visual.h" #include "sequences/track_metadata.h" #include "libheif/heif_sequences.h" #if ENABLE_PARALLEL_TILE_DECODING #include #endif #include "context.h" #include "file.h" #include "pixelimage.h" #include "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() : m_memory_tracker(&m_limits) { 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->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; if (src->version >= 2) { dst->max_sample_description_box_entries = src->max_sample_description_box_entries; dst->max_sample_group_description_box_entries = src->max_sample_group_description_box_entries; } } void HeifContext::set_security_limits(const heif_security_limits* limits) { // copy default limits if (limits->version < global_security_limits.version) { copy_security_limits(&m_limits, &global_security_limits); } // overwrite with input 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 (m_primary_image == nullptr) return nullptr; else if (!return_error_image && m_primary_image->get_item_error()) return nullptr; else return m_primary_image; } std::shared_ptr HeifContext::get_primary_image(bool return_error_image) const { return const_cast(this)->get_primary_image(return_error_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}); } static uint64_t rescale(uint64_t duration, uint32_t old_base, uint32_t new_base) { // prevent division by zero // TODO: we might emit an error in this case if (old_base == 0) { return 0; } return duration * new_base / old_base; } void HeifContext::write(StreamWriter& writer) { // --- finalize some parameters uint64_t max_sequence_duration = 0; if (auto mvhd = m_heif_file->get_mvhd_box()) { for (const auto& track : m_tracks) { track.second->finalize_track(); // rescale track duration to movie timescale units uint64_t track_duration_in_media_units = track.second->get_duration_in_media_units(); uint32_t media_timescale = track.second->get_timescale(); uint32_t mvhd_timescale = m_heif_file->get_mvhd_box()->get_time_scale(); if (mvhd_timescale == 0) { mvhd_timescale = track.second->get_timescale(); m_heif_file->get_mvhd_box()->set_time_scale(mvhd_timescale); } uint64_t movie_duration = rescale(track_duration_in_media_units, media_timescale, mvhd_timescale); track.second->set_track_duration_in_movie_units(movie_duration); max_sequence_duration = std::max(max_sequence_duration, movie_duration); } mvhd->set_duration(max_sequence_duration); } // --- 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 if (auto ipma = m_heif_file->get_ipma_box()) { ipma->sort_properties(m_heif_file->get_ipco_box()); } // --- derive box versions m_heif_file->derive_box_versions(); // --- determine brands heif_brand2 main_brand; std::vector compatible_brands; compatible_brands = compute_compatible_brands(this, &main_brand); // Note: major brand should be repeated in the compatible brands, according to this: // ISOBMFF (ISO/IEC 14496-12:2020) § K.4: // NOTE This document requires that the major brand be repeated in the compatible-brands, // but this requirement is relaxed in the 'profiles' parameter for compactness. // See https://github.com/strukturag/libheif/issues/478 auto ftyp = m_heif_file->get_ftyp_box(); // set major brand if not set manually yet if (ftyp->get_major_brand() == 0) { ftyp->set_major_brand(main_brand); } ftyp->set_minor_version(0); for (auto brand : compatible_brands) { ftyp->add_compatible_brand(brand); } // --- 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() { if (m_heif_file->has_images()) { Error err = interpret_heif_file_images(); if (err) { return err; } } if (m_heif_file->has_sequences()) { Error err = interpret_heif_file_sequences(); if (err) { return err; } } return Error::Ok; } Error HeifContext::interpret_heif_file_images() { 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(); if (width == 0 || height == 0) { return {heif_error_Invalid_input, heif_suberror_Invalid_image_size, "Zero image width or height"}; } 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 auto img_result = convert_to_output_colorspace(img, out_colorspace, out_chroma, options); if (img_result.error) { return img_result.error; } else { img = *img_result; } img->add_warnings(imgitem->get_decoding_warnings()); return img; } Result> HeifContext::convert_to_output_colorspace(std::shared_ptr img, heif_colorspace out_colorspace, heif_chroma out_chroma, const struct heif_decoding_options& options) const { 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 || (img->has_alpha() && options.color_conversion_options_ext && options.color_conversion_options_ext->alpha_composition_mode != heif_alpha_composition_mode_none)) { return convert_colorspace(img, target_colorspace, target_chroma, nullptr, converted_output_bpp, options.color_conversion_options, options.color_conversion_options_ext, get_security_limits()); } else { 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; std::shared_ptr colorConvertedImage; if (output_image_item->get_encoder()) { if (const auto* nclx = output_image_item->get_encoder()->get_forced_output_nclx()) { options.output_nclx_profile = const_cast(nclx); } Result> srcImageResult; srcImageResult = output_image_item->get_encoder()->convert_colorspace_for_encoding(pixel_image, encoder, options, get_security_limits()); if (srcImageResult.error) { return srcImageResult.error; } colorConvertedImage = srcImageResult.value; } else { colorConvertedImage = pixel_image; } 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; if (auto img = get_image(targetItem, false)) { id = img->add_property(property, essential); } else { 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}; } Error HeifContext::interpret_heif_file_sequences() { m_tracks.clear(); // --- reference all non-hidden images auto moov = m_heif_file->get_moov_box(); assert(moov); auto mvhd = moov->get_child_box(); if (!mvhd) { assert(false); // TODO } auto tracks = moov->get_child_boxes(); for (const auto& track_box : tracks) { auto track = Track::alloc_track(this, track_box); if (!track) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Unknown track handler or track error"}; } m_tracks.insert({track->get_id(), track}); if (track->is_visual_track()) { m_visual_track_id = track->get_id(); } } return Error::Ok; } std::vector HeifContext::get_track_IDs() const { std::vector ids; for (const auto& track : m_tracks) { ids.push_back(track.first); } return ids; } Result> HeifContext::get_track(uint32_t track_id) { assert(has_sequence()); if (track_id != 0) { auto iter = m_tracks.find(track_id); if (iter == m_tracks.end()) { return Error{heif_error_Usage_error, heif_suberror_Unspecified, "Invalid track id"}; } return iter->second; } if (m_visual_track_id != 0) { return m_tracks[m_visual_track_id]; } return m_tracks.begin()->second; } Result> HeifContext::get_track(uint32_t track_id) const { auto result = const_cast(this)->get_track(track_id); if (result.error) { return result.error; } else { Result> my_result; my_result.value = result.value; return my_result; } } uint32_t HeifContext::get_sequence_timescale() const { auto mvhd = m_heif_file->get_mvhd_box(); if (!mvhd) { return 0; } return mvhd->get_time_scale(); } void HeifContext::set_sequence_timescale(uint32_t timescale) { get_heif_file()->init_for_sequence(); auto mvhd = m_heif_file->get_mvhd_box(); /* unnecessary, since mvhd duration is set during writing uint32_t old_timescale = mvhd->get_time_scale(); if (old_timescale != 0) { uint64_t scaled_duration = mvhd->get_duration() * timescale / old_timescale; mvhd->set_duration(scaled_duration); } */ mvhd->set_time_scale(timescale); } uint64_t HeifContext::get_sequence_duration() const { auto mvhd = m_heif_file->get_mvhd_box(); if (!mvhd) { return 0; } return mvhd->get_duration(); } Result> HeifContext::add_visual_sequence_track(const TrackOptions* options, uint32_t handler_type, uint16_t width, uint16_t height) { m_heif_file->init_for_sequence(); std::shared_ptr trak = std::make_shared(this, 0, width, height, options, handler_type); m_tracks.insert({trak->get_id(), trak}); return trak; } Result> HeifContext::add_uri_metadata_sequence_track(const TrackOptions* options, std::string uri) { m_heif_file->init_for_sequence(); std::shared_ptr trak = std::make_shared(this, 0, uri, options); m_tracks.insert({trak->get_id(), trak}); return trak; } libheif-1.20.2/libheif/api/000775 001750 001750 00000000000 15044356511 016455 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/api/libheif/000775 001750 001750 00000000000 15044356511 020057 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/api/libheif/heif_regions.h000664 001750 001750 00000113504 15044356510 022674 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" typedef struct heif_region_item 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 geometry. * * 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 }; typedef struct heif_region 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 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 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 heif_error heif_context_get_region_item(const heif_context* context, heif_item_id region_item_id, 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 0 if the region_item is null) */ LIBHEIF_API heif_item_id heif_region_item_get_id(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(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(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 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 heif_region_item* region_item, 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 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 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 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 heif_error heif_region_get_point(const 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 heif_error heif_region_get_point_transformed(const 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 heif_error heif_region_get_rectangle(const 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 heif_error heif_region_get_rectangle_transformed(const 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 heif_error heif_region_get_ellipse(const 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 heif_error heif_region_get_ellipse_transformed(const 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 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 query, 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 heif_error heif_region_get_polygon_points(const 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 query, 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 heif_error heif_region_get_polygon_points_transformed(const 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 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 query, 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 heif_error heif_region_get_polyline_points(const 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 heif_error heif_region_get_polyline_points_transformed(const 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 heif_error heif_region_get_referenced_mask_ID(const 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 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 heif_error heif_region_get_inline_mask_data(const 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 heif_error heif_region_get_mask_image(const heif_region* region, int32_t* out_x, int32_t* out_y, uint32_t* out_width, uint32_t* out_height, 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 heif_error heif_image_handle_add_region_item(heif_image_handle* image_handle, uint32_t reference_width, uint32_t reference_height, 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 heif_error heif_region_item_add_region_point(heif_region_item* region_item, int32_t x, int32_t y, 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 heif_error heif_region_item_add_region_rectangle(heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, 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 heif_error heif_region_item_add_region_ellipse(heif_region_item* region_item, int32_t x, int32_t y, uint32_t radius_x, uint32_t radius_y, 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 heif_error heif_region_item_add_region_polygon(heif_region_item* region_item, const int32_t* pts_array, int nPoints, 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 heif_error heif_region_item_add_region_polyline(heif_region_item* region_item, const int32_t* pts_array, int nPoints, 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 heif_error heif_region_item_add_region_referenced_mask(heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, heif_item_id mask_item_id, 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 heif_error heif_region_item_add_region_inline_mask_data(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, 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 heif_error heif_region_item_add_region_inline_mask(heif_region_item* region_item, int32_t x, int32_t y, uint32_t width, uint32_t height, heif_image* image, heif_region** out_region); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_encoding.cc000664 001750 001750 00000063515 15044356510 023160 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_encoding.h" #include "api_structs.h" #include "context.h" #include "init.h" #include "plugin_registry.h" #include "image-items/overlay.h" #include "image-items/tiled.h" #include "image-items/grid.h" #include #include #include #include #include #include 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"}; int heif_have_encoder_for_format(enum heif_compression_format format) { auto plugin = get_encoder(format); return plugin != nullptr; } 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; } 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; } 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(); } 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; } } const char* heif_encoder_get_name(const struct heif_encoder* encoder) { return encoder->plugin->get_plugin_name(); } // 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_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_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_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; } static 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; } heif_encoding_options* heif_encoding_options_alloc() { auto options = new heif_encoding_options; set_default_encoding_options(*options); return options; } void heif_encoding_options_copy(heif_encoding_options* dst, const heif_encoding_options* src) { if (src == nullptr) { return; } int min_version = std::min(dst->version, src->version); switch (min_version) { case 7: dst->prefer_uncC_short_form = src->prefer_uncC_short_form; [[fallthrough]]; case 6: dst->color_conversion_options = src->color_conversion_options; [[fallthrough]]; case 5: dst->image_orientation = src->image_orientation; [[fallthrough]]; case 4: dst->output_nclx_profile = src->output_nclx_profile; dst->macOS_compatibility_workaround_no_nclx_profile = src->macOS_compatibility_workaround_no_nclx_profile; [[fallthrough]]; case 3: dst->save_two_colr_boxes_when_ICC_and_nclx_available = src->save_two_colr_boxes_when_ICC_and_nclx_available; [[fallthrough]]; case 2: dst->macOS_compatibility_workaround = src->macOS_compatibility_workaround; [[fallthrough]]; case 1: dst->save_alpha_channel = src->save_alpha_channel; } } 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) { heif_encoding_options_copy(&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_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_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; } void heif_context_set_major_brand(struct heif_context* ctx, heif_brand2 major_brand) { auto ftyp = ctx->context->get_heif_file()->get_ftyp_box(); ftyp->set_major_brand(major_brand); ftyp->add_compatible_brand(major_brand); } 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); } // === DEPRECATED === // 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; } // DEPRECATED 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); } libheif-1.20.2/libheif/api/libheif/heif_context.h000664 001750 001750 00000031226 15044356510 022712 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_CONTEXT_H #define LIBHEIF_HEIF_CONTEXT_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include /** * 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 }; // ========================= 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 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(heif_context*); typedef struct heif_reading_options 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 }; typedef 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 } heif_reader_range_request_result; typedef 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). 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); } heif_reader; // Read a HEIF file from a named disk file. // The heif_reading_options should currently be set to NULL. LIBHEIF_API heif_error heif_context_read_from_file(heif_context*, const char* filename, const 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 heif_error heif_context_read_from_memory(heif_context*, const void* mem, size_t size, const 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 heif_error heif_context_read_from_memory_without_copy(heif_context*, const void* mem, size_t size, const heif_reading_options*); LIBHEIF_API heif_error heif_context_read_from_reader(heif_context*, const heif_reader* reader, void* userdata, const 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(heif_context* ctx); LIBHEIF_API int heif_context_is_top_level_image_ID(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(heif_context* ctx, heif_item_id* ID_array, int count); LIBHEIF_API heif_error heif_context_get_primary_image_ID(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 heif_error heif_context_get_primary_image_handle(heif_context* ctx, heif_image_handle**); // Get the image handle for a known image ID. LIBHEIF_API heif_error heif_context_get_image_handle(heif_context* ctx, heif_item_id id, 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(heif_context* ctx, int fd); // ==================================================================================================== // Write the heif_context to a HEIF file LIBHEIF_API heif_error heif_context_write_to_file(heif_context*, const char* filename); typedef 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. heif_error (* write)(heif_context* ctx, // TODO: why do we need this parameter? const void* data, size_t size, void* userdata); } heif_writer; LIBHEIF_API heif_error heif_context_write(heif_context*, heif_writer* writer, void* userdata); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_properties.h000664 001750 001750 00000024207 15044356510 023423 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 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 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 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(). typedef struct heif_property_user_description { int version; // version 1 const char* lang; const char* name; const char* description; const char* tags; } heif_property_user_description; // Get the "udes" user description property content. // Undefined strings are returned as empty strings. LIBHEIF_API heif_error heif_item_get_property_user_description(const heif_context* context, heif_item_id itemId, heif_property_id propertyId, 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 heif_error heif_item_add_property_user_description(const heif_context* context, heif_item_id itemId, const 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(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 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 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 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 heif_error heif_item_add_raw_property(const 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 heif_error heif_item_get_property_raw_size(const 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 heif_error heif_item_get_property_raw_data(const 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 heif_error heif_item_get_property_uuid_type(const heif_context* context, heif_item_id itemId, heif_property_id propertyId, uint8_t out_extended_type[16]); // ------------------------- intrinsic and extrinsic matrices ------------------------- typedef struct heif_camera_intrinsic_matrix { double focal_length_x; double focal_length_y; double principal_point_x; double principal_point_y; double skew; } heif_camera_intrinsic_matrix; LIBHEIF_API int heif_image_handle_has_camera_intrinsic_matrix(const heif_image_handle* handle); LIBHEIF_API heif_error heif_image_handle_get_camera_intrinsic_matrix(const heif_image_handle* handle, heif_camera_intrinsic_matrix* out_matrix); typedef struct heif_camera_extrinsic_matrix heif_camera_extrinsic_matrix; LIBHEIF_API int heif_image_handle_has_camera_extrinsic_matrix(const heif_image_handle* handle); LIBHEIF_API heif_error heif_image_handle_get_camera_extrinsic_matrix(const heif_image_handle* handle, heif_camera_extrinsic_matrix** out_matrix); LIBHEIF_API void heif_camera_extrinsic_matrix_release(heif_camera_extrinsic_matrix*); LIBHEIF_API heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const heif_camera_extrinsic_matrix*, double* out_matrix_row_major); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_aux_images.cc000664 001750 001750 00000024714 15044356510 023512 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_aux_images.h" #include "api_structs.h" #include #include #include #include // ------------------------- depth images ------------------------- int heif_image_handle_has_depth_image(const struct heif_image_handle* handle) { return handle->image->get_depth_channel() != nullptr; } 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 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; } // ------------------------- thumbnails ------------------------- 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()); } 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 = heif_encoding_options_alloc(); heif_encoding_options_copy(options, input_options); auto encodingResult = ctx->context->encode_thumbnail(image->image, encoder, *options, bbox_size); heif_encoding_options_free(options); 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_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()); } // ------------------------- auxiliary images ------------------------- 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; } } 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()); } // ===================== DEPRECATED ===================== // 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); } libheif-1.20.2/libheif/api/libheif/heif_entity_groups.cc000664 001750 001750 00000006137 15044356510 024302 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_entity_groups.h" #include "box.h" #include "api_structs.h" #include "file.h" #include #include 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 * * 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_LIBRARY_H #define LIBHEIF_HEIF_LIBRARY_H #ifdef __cplusplus extern "C" { #endif #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 // 1.20 7 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)) #include #include /* === 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)) typedef struct heif_context heif_context; typedef struct heif_image_handle heif_image_handle; typedef uint32_t heif_item_id; typedef uint32_t heif_property_id; /** * Free a string returned by libheif in various API functions. * You may pass NULL. */ LIBHEIF_API void heif_string_release(const char*); // ========================= library initialization ====================== typedef struct heif_init_params { int version; // currently no parameters } heif_init_params; /** * 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 heif_error heif_init(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); // --- Codec plugins --- // --- Plugins are currently only supported on Unix platforms. enum heif_plugin_type { heif_plugin_type_encoder, heif_plugin_type_decoder }; typedef 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 } heif_plugin_info; LIBHEIF_API heif_error heif_load_plugin(const char* filename, heif_plugin_info const** out_plugin); LIBHEIF_API heif_error heif_load_plugins(const char* directory, const heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size); LIBHEIF_API heif_error heif_unload_plugin(const 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*); // --- register plugins typedef struct heif_decoder_plugin heif_decoder_plugin; typedef struct heif_encoder_plugin heif_encoder_plugin; LIBHEIF_API heif_error heif_register_decoder_plugin(const heif_decoder_plugin*); LIBHEIF_API heif_error heif_register_encoder_plugin(const heif_encoder_plugin*); // DEPRECATED. Use heif_register_decoder_plugin(const struct heif_decoder_plugin*) instead. LIBHEIF_API heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin*); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_tai_timestamps.h000664 001750 001750 00000017166 15044356510 024260 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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_TAI_TIMESTAMPS_H #define LIBHEIF_HEIF_TAI_TIMESTAMPS_H #include #ifdef __cplusplus extern "C" { #endif typedef struct heif_tai_clock_info { uint8_t version; // --- version 1 // Standard deviation for timestamp generation process. // May be `heif_tai_clock_info_time_uncertainty_unknown` if unknown. uint64_t time_uncertainty; // Receptor clock resolution in nanoseconds. uint32_t clock_resolution; // Clock drift rate in picoseconds/second when synchronization is stopped. // Maybe `heif_tai_clock_info_clock_drift_rate_unknown` if unknown. int32_t clock_drift_rate; // Whether clock is synchronized to an atomic source, // see the clock_type defines below. uint8_t clock_type; } heif_tai_clock_info; #define heif_tai_clock_info_time_uncertainty_unknown UINT64_C(0xFFFFFFFFFFFFFFFF) #define heif_tai_clock_info_clock_drift_rate_unknown INT32_C(0x7FFFFFFF) #define heif_tai_clock_info_clock_type_unknown 0 #define heif_tai_clock_info_clock_type_not_synchronized_to_atomic_source 1 #define heif_tai_clock_info_clock_type_synchronized_to_atomic_source 2 /** * Allocate a new heif_tai_clock_info object and initialize with default values. */ LIBHEIF_API heif_tai_clock_info* heif_tai_clock_info_alloc(void); /** * Copies the source object into the destination object. * Only the fields that are present in both objects are copied. * The version property has to be set in both structs. */ LIBHEIF_API void heif_tai_clock_info_copy(heif_tai_clock_info* dst, const heif_tai_clock_info* src); LIBHEIF_API void heif_tai_clock_info_release(heif_tai_clock_info* clock_info); typedef struct heif_tai_timestamp_packet { uint8_t version; // --- version 1 // number of nanoseconds since TAI epoch (1958-01-01T00:00:00.0) uint64_t tai_timestamp; // whether the remote and receiptor clocks are in sync uint8_t synchronization_state; // bool // whether the receptor clock failed to generate a timestamp uint8_t timestamp_generation_failure; // bool // whether the original clock value has been modified uint8_t timestamp_is_modified; // bool } heif_tai_timestamp_packet; /** * Allocate a new heif_tai_timestamp_packet object and initialize with default values. */ LIBHEIF_API heif_tai_timestamp_packet* heif_tai_timestamp_packet_alloc(void); /** * Copies the source object into the destination object. * Only the fields that are present in both objects are copied. * The version property has to be set in both structs. */ LIBHEIF_API void heif_tai_timestamp_packet_copy(heif_tai_timestamp_packet* dst, const heif_tai_timestamp_packet* src); LIBHEIF_API void heif_tai_timestamp_packet_release(heif_tai_timestamp_packet*); /** * Creates a new clock info property if it doesn't exist yet. * You can only add one tai_clock_info to an image. * * @param clock_info The TAI clock info to set for the item. This object will be copied. * @param out_optional_propertyId Output parameter for the property ID of the tai_clock_info. * This parameter may be NULL if the info is not required. */ LIBHEIF_API heif_error heif_item_set_property_tai_clock_info(heif_context* ctx, heif_item_id itemId, const heif_tai_clock_info* clock_info, heif_property_id* out_optional_propertyId); /** * Get the heif_tai_clock_info attached to the item. * This function allocates a new heif_tai_clock_info and returns it through out_clock. * * @param out_clock This parameter must not be nullptr. The object returned through this parameter must * be released with heif_tai_clock_info_release(). * If no tai_clock_info property exists for the item, out_clock is set to NULL and * no error is returned. */ LIBHEIF_API heif_error heif_item_get_property_tai_clock_info(const heif_context* ctx, heif_item_id itemId, heif_tai_clock_info** out_clock); /** * Creates a new TAI timestamp property if it doesn't exist yet. * You can only add one tai_timestamp to an image. * * @param timestamp The TAI timestamp to set for the item. This object will be copied. * @param out_optional_propertyId Output parameter for the property ID of the TAI timestamp. * This parameter may be NULL if the info is not required. */ LIBHEIF_API heif_error heif_item_set_property_tai_timestamp(heif_context* ctx, heif_item_id itemId, const heif_tai_timestamp_packet* timestamp, heif_property_id* out_optional_propertyId); /** * Get the heif_tai_timestamp_packet attached to the item. * This function allocates a new heif_tai_timestamp_packet and returns it through out_timestamp. * * @param out_timestamp This parameter must not be NULL. The object returned through this parameter must * be released with heif_tai_timestamp_packet_release(). * If no tai_timestamp_packet property exists for the item, *out_timestamp is set to NULL and * no error is returned. */ LIBHEIF_API heif_error heif_item_get_property_tai_timestamp(const heif_context* ctx, heif_item_id itemId, heif_tai_timestamp_packet** out_timestamp); /** * Attach a TAI timestamp to the image. * The main use of this function is for image sequences, but it can also be used for still images. * If used for still images, note that you also have to set the heif_tai_clock_info to the image item * through heif_item_set_property_tai_clock_info(). * * @param timestamp The TAI timestamp to set to the image. This object will be copied. */ LIBHEIF_API heif_error heif_image_set_tai_timestamp(heif_image* img, const heif_tai_timestamp_packet* timestamp); /** * Get the heif_tai_timestamp_packet attached to the image. * The main use of this function is for image sequences, but it can also be used for still images. * This function allocates a new heif_tai_timestamp_packet and returns it through out_timestamp. * * @param out_timestamp This parameter must not be NULL. The object returned through this parameter must * be released with heif_tai_timestamp_packet_release(). * If no tai_timestamp_packet property exists for the image, *out_timestamp is set to NULL and * no error is returned. */ LIBHEIF_API heif_error heif_image_get_tai_timestamp(const heif_image* img, heif_tai_timestamp_packet** out_timestamp); #ifdef __cplusplus } #endif #endif //LIBHEIF_HEIF_TAI_TIMESTAMPS_H libheif-1.20.2/libheif/api/libheif/heif_plugin.cc000664 001750 001750 00000002722 15044356510 022661 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.20.2/libheif/api/libheif/heif.h000664 001750 001750 00000002507 15044356510 021146 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif libheif-1.20.2/libheif/api/libheif/heif_regions.cc000664 001750 001750 00000057467 15044356510 023051 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 0; } 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; } 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); } 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); } 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); } int heif_region_get_polyline_num_points(const struct heif_region* region) { return heif_region_get_poly_num_points(region); } struct heif_error heif_region_get_polyline_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_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; } 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_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; } 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; } size_t stride; uint8_t* p = heif_image_get_plane2(*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; } 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; } 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_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; } 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(); size_t stride; uint8_t* p = heif_image_get_plane2(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; } libheif-1.20.2/libheif/api/libheif/heif_security.cc000664 001750 001750 00000003427 15044356510 023235 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_security.h" #include "api_structs.h" #include "context.h" 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; } // DEPRECATED 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; } libheif-1.20.2/libheif/api/libheif/heif_brands.cc000664 001750 001750 00000025250 15044356510 022635 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_brands.h" #include "heif_error.h" #include "error.h" #include "common_utils.h" #include "bitstream.h" #include "box.h" #include #include #include 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_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)); } 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); } 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 }; static 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; } } static 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 ""; } } 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) { assert(compatible_brands == nullptr); // NOLINT(clang-analyzer-unix.Malloc) 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 ,heif_brand2_msf1 }; auto it = supported_brands.find(main_brand); if (it != supported_brands.end()) { heif_free_list_of_compatible_brands(compatible_brands); return heif_error_success; } 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_success; } } 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); } static 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)); } libheif-1.20.2/libheif/api/libheif/heif_entity_groups.h000664 001750 001750 00000003547 15044356510 024146 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_ENTITY_GROUPS_H #define LIBHEIF_HEIF_ENTITY_GROUPS_H #ifdef __cplusplus extern "C" { #endif #include "libheif/heif_library.h" // ------------------------- entity groups ------------------------ typedef uint32_t heif_entity_group_id; typedef 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; } heif_entity_group; // 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 heif_entity_group* heif_context_get_entity_groups(const 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(heif_entity_group*, int num_groups); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_library.cc000664 001750 001750 00000005503 15044356510 023027 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_library.h" #include "heif_plugin.h" #include "api_structs.h" #include "plugin_registry.h" #ifdef _WIN32 // for _write #include #else #include #endif #include static struct heif_error error_null_parameter = {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; static struct heif_error error_unsupported_plugin_version = {heif_error_Usage_error, heif_suberror_Unsupported_plugin_version, "Unsupported plugin version"}; 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; } 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 > heif_decoder_plugin_latest_version) { 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 > heif_encoder_plugin_latest_version) { return error_unsupported_plugin_version; } register_encoder(encoder_plugin); return heif_error_success; } void heif_string_release(const char* str) { delete[] str; } // DEPRECATED struct heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin* decoder_plugin) { return heif_register_decoder_plugin(decoder_plugin); } libheif-1.20.2/libheif/api/libheif/heif_decoding.h000664 001750 001750 00000013477 15044356510 023012 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_DECODING_H #define LIBHEIF_HEIF_DECODING_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include // 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(heif_context* ctx, int max_threads); // 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); enum heif_progress_step { heif_progress_step_total = 0, heif_progress_step_load_tile = 1 }; typedef 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 heif_color_conversion_options color_conversion_options; // version 6 options int (* cancel_decoding)(void* progress_user_data); // version 7 options // When set to NULL, default options will be used heif_color_conversion_options_ext* color_conversion_options_ext; } heif_decoding_options; // 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 heif_decoding_options* heif_decoding_options_alloc(void); LIBHEIF_API void heif_decoding_options_copy(heif_decoding_options* dst, const heif_decoding_options* src); LIBHEIF_API void heif_decoding_options_free(heif_decoding_options*); typedef struct heif_decoder_descriptor 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 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 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 heif_decoder_descriptor*); // 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 heif_error heif_decode_image(const heif_image_handle* in_handle, heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const heif_decoding_options* options); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif.cc000664 001750 001750 00000002037 15044356510 021302 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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" #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__) #include "api_structs.h" #include "heif_emscripten.h" #endif #include "error.h" const struct heif_error heif_error_success = {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; libheif-1.20.2/libheif/api/libheif/heif_encoding.h000664 001750 001750 00000040144 15044356510 023013 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_ENCODING_H #define LIBHEIF_HEIF_ENCODING_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include // ----- encoder ----- // The encoder used for actually encoding an image. typedef struct heif_encoder heif_encoder; // A description of the encoder's capabilities and name. typedef struct heif_encoder_descriptor 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. typedef struct heif_encoder_parameter heif_encoder_parameter; // 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 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 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 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 heif_encoder_descriptor*); LIBHEIF_API enum heif_compression_format heif_encoder_descriptor_get_compression_format(const heif_encoder_descriptor*); LIBHEIF_API int heif_encoder_descriptor_supports_lossy_compression(const heif_encoder_descriptor*); LIBHEIF_API int heif_encoder_descriptor_supports_lossless_compression(const heif_encoder_descriptor*); // Get an encoder instance that can be used to actually encode images from a descriptor. LIBHEIF_API heif_error heif_context_get_encoder(heif_context* context, const heif_encoder_descriptor*, heif_encoder** out_encoder); // 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 heif_error heif_context_get_encoder_for_format(heif_context* context, enum heif_compression_format format, heif_encoder**); // You have to release the encoder after use. LIBHEIF_API void heif_encoder_release(heif_encoder*); // Get the encoder name from the encoder itself. LIBHEIF_API const char* heif_encoder_get_name(const 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 heif_error heif_encoder_set_lossy_quality(heif_encoder*, int quality); LIBHEIF_API heif_error heif_encoder_set_lossless(heif_encoder*, int enable); // level should be between 0 (= none) to 4 (= full) LIBHEIF_API heif_error heif_encoder_set_logging_level(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 heif_encoder_parameter* const* heif_encoder_list_parameters(heif_encoder*); // Return the parameter name. LIBHEIF_API const char* heif_encoder_parameter_get_name(const 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 heif_encoder_parameter*); // DEPRECATED. Use heif_encoder_parameter_get_valid_integer_values() instead. LIBHEIF_API heif_error heif_encoder_parameter_get_valid_integer_range(const 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 heif_error heif_encoder_parameter_get_valid_integer_values(const heif_encoder_parameter*, int* have_minimum, int* have_maximum, int* minimum, int* maximum, int* num_valid_values, const int** out_integer_array); LIBHEIF_API heif_error heif_encoder_parameter_get_valid_string_values(const heif_encoder_parameter*, const char* const** out_stringarray); LIBHEIF_API heif_error heif_encoder_set_parameter_integer(heif_encoder*, const char* parameter_name, int value); LIBHEIF_API heif_error heif_encoder_get_parameter_integer(heif_encoder*, const char* parameter_name, int* value); // TODO: name should be changed to heif_encoder_get_valid_integer_parameter_range LIBHEIF_API // DEPRECATED. heif_error heif_encoder_parameter_integer_valid_range(heif_encoder*, const char* parameter_name, int* have_minimum_maximum, int* minimum, int* maximum); LIBHEIF_API heif_error heif_encoder_set_parameter_boolean(heif_encoder*, const char* parameter_name, int value); LIBHEIF_API heif_error heif_encoder_get_parameter_boolean(heif_encoder*, const char* parameter_name, int* value); LIBHEIF_API heif_error heif_encoder_set_parameter_string(heif_encoder*, const char* parameter_name, const char* value); LIBHEIF_API heif_error heif_encoder_get_parameter_string(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 heif_error heif_encoder_parameter_string_valid_values(heif_encoder*, const char* parameter_name, const char* const** out_stringarray); LIBHEIF_API heif_error heif_encoder_parameter_integer_valid_values(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 heif_error heif_encoder_set_parameter(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 heif_error heif_encoder_get_parameter(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(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 }; typedef 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. 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 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. } heif_encoding_options; LIBHEIF_API heif_encoding_options* heif_encoding_options_alloc(void); LIBHEIF_API void heif_encoding_options_copy(heif_encoding_options* dst, const heif_encoding_options* src); LIBHEIF_API void heif_encoding_options_free(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 heif_error heif_context_encode_image(heif_context*, const heif_image* image, heif_encoder* encoder, const heif_encoding_options* options, heif_image_handle** out_image_handle); // 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 heif_error heif_context_add_overlay_image(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], heif_image_handle** out_iovl_image_handle); LIBHEIF_API heif_error heif_context_set_primary_image(heif_context*, heif_image_handle* image_handle); // Set the major brand of the file. // If this function is not called, the major brand is determined automatically from // the image or sequence content. LIBHEIF_API void heif_context_set_major_brand(heif_context* ctx, heif_brand2 major_brand); // 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(heif_context* ctx, heif_brand2 compatible_brand); // --- deprecated functions --- // DEPRECATED, typo in function name LIBHEIF_API int heif_encoder_descriptor_supportes_lossy_compression(const heif_encoder_descriptor*); // DEPRECATED, typo in function name LIBHEIF_API int heif_encoder_descriptor_supportes_lossless_compression(const heif_encoder_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(heif_context*, // TODO: why do we need this parameter? enum heif_compression_format format_filter, const char* name_filter, const heif_encoder_descriptor** out_encoders, int count); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_color.h000664 001750 001750 00000033121 15044356510 022340 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_COLOR_H #define LIBHEIF_HEIF_COLOR_H #ifdef __cplusplus extern "C" { #endif #include // forward declaration from other header files typedef struct heif_image heif_image; 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 }; typedef 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). } heif_color_conversion_options; enum heif_alpha_composition_mode { heif_alpha_composition_mode_none, heif_alpha_composition_mode_solid_color, heif_alpha_composition_mode_checkerboard, }; typedef struct heif_color_conversion_options_ext { uint8_t version; // --- version 1 options enum heif_alpha_composition_mode alpha_composition_mode; // color values should be specified in the range [0, 65535] uint16_t background_red, background_green, background_blue; uint16_t secondary_background_red, secondary_background_green, secondary_background_blue; uint16_t checkerboard_square_size; } heif_color_conversion_options_ext; // Assumes that it is a version=1 struct. LIBHEIF_API void heif_color_conversion_options_set_defaults(heif_color_conversion_options*); LIBHEIF_API heif_color_conversion_options_ext* heif_color_conversion_options_ext_alloc(void); LIBHEIF_API void heif_color_conversion_options_ext_copy(heif_color_conversion_options_ext* dst, const heif_color_conversion_options_ext* src); LIBHEIF_API void heif_color_conversion_options_ext_free(heif_color_conversion_options_ext*); // ------------------------- 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 heif_image_handle* handle); LIBHEIF_API size_t heif_image_handle_get_raw_color_profile_size(const 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 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 }; typedef 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; } heif_color_profile_nclx; LIBHEIF_API heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp); LIBHEIF_API heif_error heif_nclx_color_profile_set_transfer_characteristics(heif_color_profile_nclx* nclx, uint16_t transfer_characteristics); LIBHEIF_API heif_error heif_nclx_color_profile_set_matrix_coefficients(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 heif_error heif_image_handle_get_nclx_color_profile(const heif_image_handle* handle, 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 heif_color_profile_nclx* heif_nclx_color_profile_alloc(void); LIBHEIF_API void heif_nclx_color_profile_free(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 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 heif_image* image); // Returns the ICC profile if one is assigned to the image. Otherwise, it returns an error. LIBHEIF_API heif_error heif_image_get_raw_color_profile(const heif_image* image, void* out_data); LIBHEIF_API heif_error heif_image_get_nclx_color_profile(const heif_image* image, heif_color_profile_nclx** out_data); // The color profile is not attached to the image handle because we might need it // for color space transform and encoding. LIBHEIF_API heif_error heif_image_set_raw_color_profile(heif_image* image, const char* profile_type_fourcc_string, const void* profile_data, const size_t profile_size); LIBHEIF_API heif_error heif_image_set_nclx_color_profile(heif_image* image, const 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); // --- content light level --- // 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. typedef struct heif_content_light_level { uint16_t max_content_light_level; uint16_t max_pic_average_light_level; } heif_content_light_level; LIBHEIF_API int heif_image_has_content_light_level(const heif_image*); LIBHEIF_API void heif_image_get_content_light_level(const heif_image*, 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 heif_image_handle*, heif_content_light_level* out); LIBHEIF_API void heif_image_set_content_light_level(const heif_image*, const heif_content_light_level* in); // --- mastering display colour volume --- // 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). typedef 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; } heif_mastering_display_colour_volume; // The units for max_display_mastering_luminance and min_display_mastering_luminance is Candelas per square meter. typedef 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; } heif_decoded_mastering_display_colour_volume; typedef struct heif_ambient_viewing_environment { uint32_t ambient_illumination; uint16_t ambient_light_x; uint16_t ambient_light_y; } heif_ambient_viewing_environment; LIBHEIF_API int heif_image_has_mastering_display_colour_volume(const heif_image*); LIBHEIF_API void heif_image_get_mastering_display_colour_volume(const heif_image*, 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 heif_image_handle*, heif_mastering_display_colour_volume* out); LIBHEIF_API void heif_image_set_mastering_display_colour_volume(const heif_image*, const 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 heif_mastering_display_colour_volume* in, heif_decoded_mastering_display_colour_volume* out); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_experimental.h000664 001750 001750 00000033121 15044356510 023717 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') */ typedef struct heif_property_camera_intrinsic_matrix heif_property_camera_intrinsic_matrix; typedef struct heif_property_camera_extrinsic_matrix heif_property_camera_extrinsic_matrix; //LIBHEIF_API heif_error heif_item_get_property_camera_intrinsic_matrix(const heif_context* context, heif_item_id itemId, heif_property_id propertyId, heif_property_camera_intrinsic_matrix** out_matrix); //LIBHEIF_API void heif_property_camera_intrinsic_matrix_release(heif_property_camera_intrinsic_matrix* matrix); //LIBHEIF_API heif_error heif_property_camera_intrinsic_matrix_get_focal_length(const heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_focal_length_x, double* out_focal_length_y); //LIBHEIF_API heif_error heif_property_camera_intrinsic_matrix_get_principal_point(const heif_property_camera_intrinsic_matrix* matrix, int image_width, int image_height, double* out_principal_point_x, double* out_principal_point_y); //LIBHEIF_API heif_error heif_property_camera_intrinsic_matrix_get_skew(const heif_property_camera_intrinsic_matrix* matrix, double* out_skew); //LIBHEIF_API heif_property_camera_intrinsic_matrix* heif_property_camera_intrinsic_matrix_alloc(void); //LIBHEIF_API void heif_property_camera_intrinsic_matrix_set_simple(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(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 heif_error heif_item_add_property_camera_intrinsic_matrix(const heif_context* context, heif_item_id itemId, const heif_property_camera_intrinsic_matrix* matrix, heif_property_id* out_propertyId); //LIBHEIF_API heif_error heif_item_get_property_camera_extrinsic_matrix(const heif_context* context, heif_item_id itemId, heif_property_id propertyId, heif_property_camera_extrinsic_matrix** out_matrix); //LIBHEIF_API void heif_property_camera_extrinsic_matrix_release(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 heif_error heif_property_camera_extrinsic_matrix_get_rotation_matrix(const 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 heif_error heif_property_camera_extrinsic_matrix_get_position_vector(const heif_property_camera_extrinsic_matrix* matrix, int32_t* out_vector); //LIBHEIF_API heif_error heif_property_camera_extrinsic_matrix_get_world_coordinate_system_id(const heif_property_camera_extrinsic_matrix* matrix, uint32_t* out_wcs_id); #endif // --- Tiled images typedef 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 } heif_tiled_image_parameters; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API heif_error heif_context_add_tiled_image(heif_context* ctx, const heif_tiled_image_parameters* parameters, const heif_encoding_options* options, // TODO: do we need this? const heif_encoder* encoder, heif_image_handle** out_tiled_image_handle); #endif // --- 'pymd' entity group (pyramid layers) typedef 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; } heif_pyramid_layer_info; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES // The input images are automatically sorted according to resolution. You can provide them in any order. LIBHEIF_API heif_error heif_context_add_pyramid_entity_group(heif_context* ctx, const heif_item_id* layer_item_ids, size_t num_layers, heif_item_id* out_group_id); LIBHEIF_API heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(heif_context*, heif_entity_group_id id, int* out_num_layers); LIBHEIF_API void heif_pyramid_layer_info_release(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 heif_error heif_image_add_channel(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(heif_image*, enum heif_channel** out_channels); LIBHEIF_API void heif_channel_release_list(enum heif_channel** channels); #endif typedef struct heif_complex32 { float real, imaginary; } heif_complex32; typedef struct heif_complex64 { double real, imaginary; } heif_complex64; #if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API enum heif_channel_datatype heif_image_get_datatype(const 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 heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const uint32_t* heif_image_get_channel_uint32_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const uint64_t* heif_image_get_channel_uint64_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int16_t* heif_image_get_channel_int16_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int32_t* heif_image_get_channel_int32_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const int64_t* heif_image_get_channel_int64_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const float* heif_image_get_channel_float32_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const double* heif_image_get_channel_float64_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const heif_complex32* heif_image_get_channel_complex32_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API const heif_complex64* heif_image_get_channel_complex64_readonly(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint16_t* heif_image_get_channel_uint16(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint32_t* heif_image_get_channel_uint32(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint64_t* heif_image_get_channel_uint64(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int16_t* heif_image_get_channel_int16(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int32_t* heif_image_get_channel_int32(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API int64_t* heif_image_get_channel_int64(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API float* heif_image_get_channel_float32(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API double* heif_image_get_channel_float64(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API heif_complex32* heif_image_get_channel_complex32(heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API heif_complex64* heif_image_get_channel_complex64(heif_image*, enum heif_channel channel, size_t* out_stride); #endif #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_tai_timestamps.cc000664 001750 001750 00000021207 15044356510 024405 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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 "api_structs.h" #include "box.h" #include "file.h" #include void initialize_heif_tai_clock_info(heif_tai_clock_info* taic) { taic->version = 1; taic->time_uncertainty = heif_tai_clock_info_time_uncertainty_unknown; taic->clock_resolution = 0; taic->clock_drift_rate = heif_tai_clock_info_clock_drift_rate_unknown; taic->clock_type = heif_tai_clock_info_clock_type_unknown; } heif_tai_clock_info* heif_tai_clock_info_alloc() { auto* taic = new heif_tai_clock_info; initialize_heif_tai_clock_info(taic); return taic; } void initialize_heif_tai_timestamp_packet(heif_tai_timestamp_packet* itai) { itai->version = 1; itai->tai_timestamp = 0; itai->synchronization_state = false; itai->timestamp_generation_failure = false; itai->timestamp_is_modified = false; } heif_tai_timestamp_packet* heif_tai_timestamp_packet_alloc() { auto* itai = new heif_tai_timestamp_packet; initialize_heif_tai_timestamp_packet(itai); return itai; } void heif_tai_timestamp_packet_copy(heif_tai_timestamp_packet* dst, const heif_tai_timestamp_packet* src) { if (dst->version >= 1 && src->version >= 1) { dst->tai_timestamp = src->tai_timestamp; dst->synchronization_state = src->synchronization_state; dst->timestamp_is_modified = src->timestamp_is_modified; dst->timestamp_generation_failure = src->timestamp_generation_failure; } // in the future when copying with "src->version > dst->version", // the remaining dst fields have to be filled with defaults } void heif_tai_clock_info_copy(heif_tai_clock_info* dst, const heif_tai_clock_info* src) { if (dst->version >= 1 && src->version >= 1) { dst->time_uncertainty = src->time_uncertainty; dst->clock_resolution = src->clock_resolution; dst->clock_drift_rate = src->clock_drift_rate; dst->clock_type = src->clock_type; } // in the future when copying with "src->version > dst->version", // the remaining dst fields have to be filled with defaults } struct heif_error heif_item_set_property_tai_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->item_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"}; } // make sure that we do not add two taic boxes to one image if (auto img= ctx->context->get_image(itemId, false)) { auto existing_taic = img->get_property(); if (existing_taic) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "item already has an taic property"}; } } // Create new taic (it will be deduplicated automatically in add_property()) auto taic = std::make_shared(); taic->set_from_tai_clock_info(clock); heif_property_id id = ctx->context->add_property(itemId, taic, false); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } struct heif_error heif_item_get_property_tai_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"}; } *out_clock = nullptr; // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->item_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item ID does not exist"}; } // Check if taic exists for itemId auto taic = file->get_property_for_item(itemId); if (!taic) { // return NULL heif_tai_clock_info return heif_error_success; } *out_clock = new heif_tai_clock_info; **out_clock = *taic->get_tai_clock_info(); return heif_error_success; } void heif_tai_clock_info_release(struct heif_tai_clock_info* clock_info) { delete clock_info; } void heif_tai_timestamp_packet_release(struct heif_tai_timestamp_packet* tai) { delete tai; } struct heif_error heif_item_set_property_tai_timestamp(struct heif_context* ctx, heif_item_id itemId, const 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->item_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item does not exist"}; } // make sure that we do not add two TAI timestamps to one image if (auto img= ctx->context->get_image(itemId, false)) { auto existing_itai = img->get_property(); if (existing_itai) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "item already has an itai property"}; } } // Create new itai (it will be deduplicated automatically in add_property()) auto itai = std::make_shared(); itai->set_from_tai_timestamp_packet(timestamp); heif_property_id id = ctx->context->add_property(itemId, itai, false); if (out_propertyId) { *out_propertyId = id; } return heif_error_success; } struct heif_error heif_item_get_property_tai_timestamp(const struct heif_context* ctx, heif_item_id itemId, struct heif_tai_timestamp_packet** out_timestamp) { if (!ctx) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; } else if (!out_timestamp) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_timestamp_packet passed in"}; } *out_timestamp = nullptr; // Check if itemId exists auto file = ctx->context->get_heif_file(); if (!file->item_exists(itemId)) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item does not exist"}; } // Check if itai exists for itemId auto itai = file->get_property_for_item(itemId); if (!itai) { // return NULL heif_tai_timestamp_packet; return heif_error_success; } *out_timestamp = new heif_tai_timestamp_packet; **out_timestamp = *itai->get_tai_timestamp_packet(); return heif_error_success; } struct heif_error heif_image_set_tai_timestamp(struct heif_image* img, const struct heif_tai_timestamp_packet* timestamp) { Error err = img->image->set_tai_timestamp(timestamp); if (err) { return err.error_struct(img->image.get()); } else { return heif_error_success; } } struct heif_error heif_image_get_tai_timestamp(const struct heif_image* img, struct heif_tai_timestamp_packet** out_timestamp) { if (!out_timestamp) { return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_timestamp_packet passed in"}; } *out_timestamp = nullptr; auto* tai = img->image->get_tai_timestamp(); if (!tai) { *out_timestamp = nullptr; return heif_error_success; } *out_timestamp = new heif_tai_timestamp_packet; **out_timestamp = *tai; return heif_error_success; } libheif-1.20.2/libheif/api/libheif/heif_items.cc000664 001750 001750 00000023360 15044356510 022505 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_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; } 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; } } 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; } // ------------------------- 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()); } } libheif-1.20.2/libheif/api/libheif/heif_uncompressed.h000664 001750 001750 00000007376 15044356510 023746 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2024-2025 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_UNCOMPRESSED_H #define LIBHEIF_HEIF_UNCOMPRESSED_H #include "libheif/heif.h" #ifdef __cplusplus extern "C" { #endif /* @file heif_uncompressed.h * @brief Functions for adding ISO 23001-17 (uncompressed) images to a HEIF file. * Despite its name, this is not limited to uncompressed images. * It is also possible to add images with lossless compression methods. * See heif_metadata_compression for more information. */ // --- '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 }; typedef 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: interleave type, padding } heif_unci_image_parameters; LIBHEIF_API heif_unci_image_parameters* heif_unci_image_parameters_alloc(void); LIBHEIF_API void heif_unci_image_parameters_copy(heif_unci_image_parameters* dst, const heif_unci_image_parameters* src); LIBHEIF_API void heif_unci_image_parameters_release(heif_unci_image_parameters*); /* * This adds an empty iso23001-17 (uncompressed) image to the HEIF file. * The actual image data is added later using heif_context_add_image_tile(). * If you do not need tiling, you can use heif_context_encode_image() instead. * However, this will by default disable any compression and any control about * the data layout. * * @param ctx The file context * @param parameters The parameters for the image, must not be NULL. * @param encoding_options Optional, may be NULL. * @param prototype An image with the same channel configuration as the image data * that will be later inserted. The image size need not match this. * Must not be NULL. * @param out_unci_image_handle Returns a handle to the image. The caller is responsible for freeing it. * Must not be NULL because this is required to fill in image data. * @return Returns an error if the passed parameters are incorrect. * If ISO23001-17 images are not supported, returns heif_error_Unsupported_feature. */ LIBHEIF_API heif_error heif_context_add_empty_unci_image(heif_context* ctx, const heif_unci_image_parameters* parameters, const heif_encoding_options* encoding_options, const heif_image* prototype, heif_image_handle** out_unci_image_handle); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_sequences.h000664 001750 001750 00000047243 15044356510 023227 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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_SEQUENCES_H #define LIBHEIF_HEIF_SEQUENCES_H #include "libheif/heif.h" #ifdef __cplusplus extern "C" { #endif // forward declaration of structs defined in heif_tai_timestamps.h typedef struct heif_tai_clock_info heif_tai_clock_info; typedef struct heif_tai_timestamp_packet heif_tai_timestamp_packet; // --- reading sequence tracks /** * Check whether there is an image sequence in the HEIF file. * * @return A boolean whether there is an image sequence in the HEIF file. */ LIBHEIF_API int heif_context_has_sequence(const heif_context*); /** * Get the timescale (clock ticks per second) for timing values in the sequence. * * @note Each track may have its independent timescale. * * @return Clock ticks per second. Returns 0 if there is no sequence in the file. */ LIBHEIF_API uint32_t heif_context_get_sequence_timescale(const heif_context*); /** * Get the total duration of the sequence in timescale clock ticks. * Use `heif_context_get_sequence_timescale()` to get the clock ticks per second. * * @return Sequence duration in clock ticks. Returns 0 if there is no sequence in the file. */ LIBHEIF_API uint64_t heif_context_get_sequence_duration(const heif_context*); // A track, which may be an image sequence, a video track or a metadata track. typedef struct heif_track heif_track; /** * Free a `heif_track` object received from libheif. * Passing NULL is ok. */ LIBHEIF_API void heif_track_release(heif_track*); /** * Get the number of tracks in the HEIF file. * * @return Number of tracks or 0 if there is no sequence in the HEIF file. */ LIBHEIF_API int heif_context_number_of_sequence_tracks(const heif_context*); /** * Returns the IDs for each of the tracks stored in the HEIF file. * The output array must have heif_context_number_of_sequence_tracks() entries. */ LIBHEIF_API void heif_context_get_track_ids(const heif_context* ctx, uint32_t out_track_id_array[]); /** * Get the ID of the passed track. * The track ID will never be 0. */ LIBHEIF_API uint32_t heif_track_get_id(const heif_track* track); /** * Get the heif_track object for the given track ID. * If you pass `id=0`, the first visual track will be returned. * If there is no track with the given ID or if 0 is passed and there is no visual track, NULL will be returned. * * @note Tracks never have a zero ID. This is why we can use this as a special value to find the first visual track. * * @param id Track id or 0 for the first visual track. * @return heif_track object. You must free this after use. */ // Use id=0 for the first visual track. LIBHEIF_API heif_track* heif_context_get_track(const heif_context*, uint32_t id); typedef uint32_t heif_track_type; enum heif_track_type_4cc { heif_track_type_video = heif_fourcc('v', 'i', 'd', 'e'), heif_track_type_image_sequence = heif_fourcc('p', 'i', 'c', 't'), heif_track_type_metadata = heif_fourcc('m', 'e', 't', 'a') }; /** * Get the four-cc track handler type. * Typical codes are "vide" for video sequences, "pict" for image sequences, "meta" for metadata tracks. * These are defined in heif_track_type_4cc, but files may also contain other types. * * @return four-cc handler type */ LIBHEIF_API heif_track_type heif_track_get_track_handler_type(const heif_track*); /** * Get the timescale (clock ticks per second) for this track. * Note that this can be different from the timescale used at sequence level. * * @return clock ticks per second */ LIBHEIF_API uint32_t heif_track_get_timescale(const heif_track*); // --- reading visual tracks /** * Get the image resolution of the track. * If the passed track is no visual track, an error is returned. */ LIBHEIF_API heif_error heif_track_get_image_resolution(const heif_track*, uint16_t* out_width, uint16_t* out_height); /** * Decode the next image in the passed sequence track. * If there is no more image in the sequence, `heif_error_End_of_sequence` is returned. * The parameters `colorspace`, `chroma` and `options` are similar to heif_decode_image(). * If you want to let libheif decide the output colorspace and chroma, set these parameters * to heif_colorspace_undefined / heif_chroma_undefined. Usually, libheif will return the * image in the input colorspace, but it may also modify it for example when it has to rotate the image. * If you want to get the image in a specific colorspace/chroma format, you can specify this * and libheif will convert the image to match this format. */ LIBHEIF_API heif_error heif_track_decode_next_image(heif_track* track, heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const heif_decoding_options* options); /** * Get the image display duration in clock ticks of this track. * Make sure to use the timescale of the track and not the timescale of the total sequence. */ LIBHEIF_API uint32_t heif_image_get_duration(const heif_image*); // --- reading metadata track samples /** * Get the "sample entry type" of the first sample sample cluster in the track. * In the case of metadata tracks, this will usually be "urim" for "URI Meta Sample Entry". * The exact URI can then be obtained with 'heif_track_get_urim_sample_entry_uri_of_first_cluster'. */ LIBHEIF_API uint32_t heif_track_get_sample_entry_type_of_first_cluster(const heif_track*); /** * Get the URI of the first sample cluster in an 'urim' track. * Only call this for tracks with 'urim' sample entry types. It will return an error otherwise. * * @param out_uri A string with the URI will be returned. Free this string with `heif_string_release()`. */ LIBHEIF_API heif_error heif_track_get_urim_sample_entry_uri_of_first_cluster(const heif_track* track, const char** out_uri); /** Sequence sample object that can hold any raw byte data. * Use this to store and read raw metadata samples. */ typedef struct heif_raw_sequence_sample heif_raw_sequence_sample; /** * Get the next raw sample from the (metadata) sequence track. * You have to free the returned sample with heif_raw_sequence_sample_release(). */ LIBHEIF_API heif_error heif_track_get_next_raw_sequence_sample(heif_track*, heif_raw_sequence_sample** out_sample); /** * Release a heif_raw_sequence_sample object. * You may pass NULL. */ LIBHEIF_API void heif_raw_sequence_sample_release(heif_raw_sequence_sample*); /** * Get a pointer to the data of the (metadata) sample. * The data pointer stays valid until the heif_raw_sequence_sample object is released. * * @param out_array_size Size of the returned array (may be NULL). */ LIBHEIF_API const uint8_t* heif_raw_sequence_sample_get_data(const heif_raw_sequence_sample*, size_t* out_array_size); /** * Return the size of the raw data contained in the sample. * This is the same as returned through the 'out_array_size' parameter of 'heif_raw_sequence_sample_get_data()'. */ LIBHEIF_API size_t heif_raw_sequence_sample_get_data_size(const heif_raw_sequence_sample*); /** * Get the sample duration in clock ticks of this track. * Make sure to use the timescale of the track and not the timescale of the total sequence. */ LIBHEIF_API uint32_t heif_raw_sequence_sample_get_duration(const heif_raw_sequence_sample*); // --- writing sequences /** * Set an independent global timescale for the sequence. * If no timescale is set with this function, the timescale of the first track will be used. */ LIBHEIF_API void heif_context_set_sequence_timescale(heif_context*, uint32_t timescale); /** * Specifies whether a 'sample auxiliary info' is stored with the samples. * The difference between `heif_sample_aux_info_presence_optional` and `heif_sample_aux_info_presence_mandatory` * is that `heif_sample_aux_info_presence_mandatory` will throw an error if the data is missing when writing a sample. */ enum heif_sample_aux_info_presence { heif_sample_aux_info_presence_none = 0, heif_sample_aux_info_presence_optional = 1, heif_sample_aux_info_presence_mandatory = 2 }; typedef struct heif_track_options heif_track_options; /** * Allocate track options object that is required to set options for a new track. * When you create a new track, you can also pass a NULL heif_track_options pointer, in which case the default options are used. */ LIBHEIF_API heif_track_options* heif_track_options_alloc(void); LIBHEIF_API void heif_track_options_release(heif_track_options*); /** * Set the track specific timescale. This is the number of clock ticks per second. * The default is 90,000 Hz. * @param timescale */ LIBHEIF_API void heif_track_options_set_timescale(heif_track_options*, uint32_t timescale); /** * Set whether the aux-info data should be stored interleaved with the sequence samples. * Default is: false. * * If 'true', the aux_info data blocks will be interleaved with the compressed image. * This has the advantage that the aux_info is localized near the image data. * * If 'false', all aux_info will be written as one block after the compressed image data. * This has the advantage that no aux_info offsets have to be written. */ LIBHEIF_API void heif_track_options_set_interleaved_sample_aux_infos(heif_track_options*, int interleaved_flag); LIBHEIF_API heif_error heif_track_options_enable_sample_tai_timestamps(heif_track_options*, const heif_tai_clock_info*, enum heif_sample_aux_info_presence); LIBHEIF_API void heif_track_options_enable_sample_gimi_content_ids(heif_track_options*, enum heif_sample_aux_info_presence); /** * Set the GIMI format track ID string. If NULL is passed, no track ID is saved. * @param track_id */ LIBHEIF_API void heif_track_options_set_gimi_track_id(heif_track_options*, const char* track_id); // --- writing visual tracks // This structure is for future use. It is not defined yet. typedef struct heif_sequence_encoding_options { uint8_t version; // version 1 options // Set this to the NCLX parameters to be used in the output images or set to NULL // when the same parameters as in the input images should be used. const heif_color_profile_nclx* output_nclx_profile; heif_color_conversion_options color_conversion_options; } heif_sequence_encoding_options; LIBHEIF_API heif_sequence_encoding_options* heif_sequence_encoding_options_alloc(void); LIBHEIF_API void heif_sequence_encoding_options_release(heif_sequence_encoding_options*); /** * Add a visual track to the sequence. * The track ID is assigned automatically. * * @param width Image resolution width * @param height Image resolution height * @param track_type Has to be heif_track_type_video or heif_track_type_image_sequence * @param track_options Optional track creation options. If NULL, default options will be used. * @param encoding_options Options for sequence encoding. If NULL, default options will be used. * @param out_track Output parameter to receive the track object for the just created track. * @return */ LIBHEIF_API heif_error heif_context_add_visual_sequence_track(heif_context*, uint16_t width, uint16_t height, heif_track_type track_type, const heif_track_options* track_options, const heif_sequence_encoding_options* encoding_options, heif_track** out_track); /** * Set the image display duration in the track's timescale units. */ LIBHEIF_API void heif_image_set_duration(heif_image*, uint32_t duration); /** * Encode the image into a visual track. * If the passed track is no visual track, an error will be returned. * * @param sequence_encoding_options Options for sequence encoding. If NULL, default options will be used. */ LIBHEIF_API heif_error heif_track_encode_sequence_image(heif_track*, const heif_image* image, heif_encoder* encoder, const heif_sequence_encoding_options* sequence_encoding_options); // --- metadata tracks /** * Add a metadata track. * The track content type is specified by the 'uri' parameter. * This will be created as a 'urim' "URI Meta Sample Entry". * * @param options Optional track creation options. If NULL, default options will be used. */ LIBHEIF_API heif_error heif_context_add_uri_metadata_sequence_track(heif_context*, const char* uri, const heif_track_options* options, heif_track** out_track); /** * Allocate a new heif_raw_sequence_sample object. * Free with heif_raw_sequence_sample_release(). */ LIBHEIF_API heif_raw_sequence_sample* heif_raw_sequence_sample_alloc(void); /** * Set the raw sequence sample data. */ LIBHEIF_API heif_error heif_raw_sequence_sample_set_data(heif_raw_sequence_sample*, const uint8_t* data, size_t size); /** * Set the sample duration in track timescale units. */ LIBHEIF_API void heif_raw_sequence_sample_set_duration(heif_raw_sequence_sample*, uint32_t duration); /** * Add a raw sequence sample (usually a metadata sample) to the (metadata) track. */ LIBHEIF_API heif_error heif_track_add_raw_sequence_sample(heif_track*, const heif_raw_sequence_sample*); // --- sample auxiliary data /** * Contains the type of sample auxiliary data assigned to the track samples. */ typedef struct heif_sample_aux_info_type { uint32_t type; uint32_t parameter; } heif_sample_aux_info_type; /** * Returns how many different types of sample auxiliary data units are assigned to this track's samples. */ LIBHEIF_API int heif_track_get_number_of_sample_aux_infos(const heif_track*); /** * Get get the list of sample auxiliary data types used in the track. * The passed array has to have heif_track_get_number_of_sample_aux_infos() entries. */ LIBHEIF_API void heif_track_get_sample_aux_info_types(const heif_track*, heif_sample_aux_info_type out_types[]); // --- GIMI content IDs /** * Get the GIMI content ID for the track (as a whole). * If there is no content ID, nullptr is returned. * * @return The returned string has to be released with `heif_string_release()`. */ LIBHEIF_API const char* heif_track_get_gimi_track_content_id(const heif_track*); /** * Get the GIMI content ID stored in the image sample. * If there is no content ID, NULL is returned. * @return */ LIBHEIF_API const char* heif_image_get_gimi_sample_content_id(const heif_image*); /** * Get the GIMI content ID stored in the metadata sample. * If there is no content ID, NULL is returned. * @return */ LIBHEIF_API const char* heif_raw_sequence_sample_get_gimi_sample_content_id(const heif_raw_sequence_sample*); /** * Set the GIMI content ID for an image sample. It will be stored as SAI. * When passing NULL, a previously set ID will be removed. */ LIBHEIF_API void heif_image_set_gimi_sample_content_id(heif_image*, const char* contentID); /** * Set the GIMI content ID for a (metadata) sample. It will be stored as SAI. * When passing NULL, a previously set ID will be removed. */ LIBHEIF_API void heif_raw_sequence_sample_set_gimi_sample_content_id(heif_raw_sequence_sample*, const char* contentID); // --- TAI timestamps // Note: functions for setting timestamps on images are in heif_tai_timestamps.h /** * Returns whether the raw (metadata) sample has a TAI timestamp attached to it (stored as SAI). * * @return boolean flag whether a TAI exists for this sample. */ LIBHEIF_API int heif_raw_sequence_sample_has_tai_timestamp(const heif_raw_sequence_sample*); /** * Get the TAI timestamp of the (metadata) sample. * If there is no timestamp assigned to it, NULL will be returned. * * @note You should NOT free the returned timestamp with 'heif_tai_timestamp_packet_release()'. * The returned struct stays valid until the heif_raw_sequence_sample is released. */ LIBHEIF_API const heif_tai_timestamp_packet* heif_raw_sequence_sample_get_tai_timestamp(const heif_raw_sequence_sample*); /** * Set the TAI timestamp for a raw sequence sample. * The timestamp will be copied, you can release it after calling this function. */ LIBHEIF_API void heif_raw_sequence_sample_set_tai_timestamp(heif_raw_sequence_sample* sample, const heif_tai_timestamp_packet* timestamp); /** * Returns the TAI clock info of the track. * If there is no TAI clock info, NULL is returned. * You should NOT free the returned heif_tai_clock_info. * The structure stays valid until the heif_track object is released. */ LIBHEIF_API const heif_tai_clock_info* heif_track_get_tai_clock_info_of_first_cluster(heif_track*); // --- track references enum heif_track_reference_type { heif_track_reference_type_description = heif_fourcc('c', 'd', 's', 'c'), // track_description heif_track_reference_type_thumbnails = heif_fourcc('t', 'h', 'm', 'b'), // thumbnails heif_track_reference_type_auxiliary = heif_fourcc('a', 'u', 'x', 'l') // auxiliary data (e.g. depth maps or alpha channel) }; /** * Add a reference between tracks. * 'reference_type' can be one of the four-cc codes listed in heif_track_reference_type or any other type. */ LIBHEIF_API void heif_track_add_reference_to_track(heif_track*, uint32_t reference_type, const heif_track* to_track); /** * Return the number of different reference types used in this track's tref box. */ LIBHEIF_API size_t heif_track_get_number_of_track_reference_types(const heif_track*); /** * List the reference types used in this track. * The passed array must have heif_track_get_number_of_track_reference_types() entries. */ LIBHEIF_API void heif_track_get_track_reference_types(const heif_track*, uint32_t out_reference_types[]); /** * Get the number of references of the passed type. */ LIBHEIF_API size_t heif_track_get_number_of_track_reference_of_type(const heif_track*, uint32_t reference_type); /** * List the track ids this track points to with the passed reference type. * The passed array must have heif_track_get_number_of_track_reference_of_type() entries. */ LIBHEIF_API size_t heif_track_get_references_from_track(const heif_track*, uint32_t reference_type, uint32_t out_to_track_id[]); /** * Find tracks that are referring to the current track through the passed reference_type. * The found track IDs will be filled into the passed array, but no more than `array_size` entries will be filled. * * @return number of tracks found. If this is equal to 'array_size', you should ask again with a larger array size to be sure you got all tracks. */ LIBHEIF_API size_t heif_track_find_referring_tracks(const heif_track*, uint32_t reference_type, uint32_t out_track_id[], size_t array_size); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_tiling.cc000664 001750 001750 00000025077 15044356510 022661 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_tiling.h" #include "api_structs.h" #include "image-items/grid.h" #include "image-items/tiled.h" #if WITH_UNCOMPRESSED_CODEC #include "image-items/unc_image.h" #endif #include #include #include #include static struct heif_error error_null_parameter = {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; 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; } 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 = heif_decoding_options_alloc(); heif_decoding_options_copy(dec_options, input_options); Result> decodingResult = in_handle->context->decode_image(id, colorspace, chroma, *dec_options, true, x0,y0); heif_decoding_options_free(dec_options); 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()); } // --- encoding --- 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_encoding_options_alloc(); heif_color_profile_nclx nclx; if (input_options) { heif_encoding_options_copy(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); heif_encoding_options_free(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_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" }; } } libheif-1.20.2/libheif/api/libheif/heif_items.h000664 001750 001750 00000021170 15044356510 022344 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 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 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 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 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 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 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 heif_context* ctx, heif_item_id item_id); LIBHEIF_API const char* heif_item_get_item_name(const heif_context* ctx, heif_item_id item_id); LIBHEIF_API heif_error heif_item_set_item_name(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 heif_error heif_item_get_item_data(const 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 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 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 heif_context* ctx, heif_item_id** references); LIBHEIF_API heif_error heif_context_add_item_reference(heif_context* ctx, uint32_t reference_type, heif_item_id from_item, heif_item_id to_item); LIBHEIF_API heif_error heif_context_add_item_references(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 heif_error heif_context_add_item(heif_context* ctx, const char* item_type, const void* data, int size, heif_item_id* out_item_id); LIBHEIF_API heif_error heif_context_add_mime_item(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 heif_error heif_context_add_precompressed_mime_item(heif_context* ctx, const char* content_type, const char* content_encoding, const void* data, int size, heif_item_id* out_item_id); LIBHEIF_API heif_error heif_context_add_uri_item(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.20.2/libheif/api/libheif/heif_plugin.h000664 001750 001750 00000024703 15044356510 022526 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 // 1.20 4 3 2 #define heif_decoder_plugin_latest_version 4 #define heif_encoder_plugin_latest_version 3 // ==================================================================================================== // 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(). typedef 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)(void); // Global plugin initialization (may be NULL) void (* init_plugin)(void); // Global plugin deinitialization (may be NULL) void (* deinit_plugin)(void); // 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 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. 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. heif_error (* decode_image)(void* decoder, heif_image** out_img); // --- version 2 functions --- 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 --- const char* id_name; // --- version 4 functions --- heif_error (* decode_next_image)(void* decoder, heif_image** out_img, const heif_security_limits* limits); // --- version 5 functions will follow below ... --- // --- Note: when adding new versions, also update `heif_decoder_plugin_latest_version`. } heif_decoder_plugin; 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 }; typedef 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)(void); // Global plugin initialization (may be NULL) void (* init_plugin)(void); // Global plugin cleanup (may be NULL). // Free data that was allocated in init_plugin() void (* cleanup_plugin)(void); // Create a new decoder context for decoding an image heif_error (* new_encoder)(void** encoder); // Free the decoder context (heif_image can still be used after destruction) void (* free_encoder)(void* encoder); heif_error (* set_parameter_quality)(void* encoder, int quality); heif_error (* get_parameter_quality)(void* encoder, int* quality); heif_error (* set_parameter_lossless)(void* encoder, int lossless); heif_error (* get_parameter_lossless)(void* encoder, int* lossless); heif_error (* set_parameter_logging_level)(void* encoder, int logging); heif_error (* get_parameter_logging_level)(void* encoder, int* logging); const heif_encoder_parameter** (* list_parameters)(void* encoder); heif_error (* set_parameter_integer)(void* encoder, const char* name, int value); heif_error (* get_parameter_integer)(void* encoder, const char* name, int* value); heif_error (* set_parameter_boolean)(void* encoder, const char* name, int value); heif_error (* get_parameter_boolean)(void* encoder, const char* name, int* value); heif_error (* set_parameter_string)(void* encoder, const char* name, const char* value); 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. heif_error (* encode_image)(void* encoder, const 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. 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 ... --- // --- Note: when adding new versions, also update `heif_encoder_plugin_latest_version`. } heif_encoder_plugin; // 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) typedef 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; } heif_encoder_parameter; extern heif_error heif_error_ok; extern heif_error heif_error_unsupported_parameter; extern heif_error heif_error_invalid_parameter_value; #define HEIF_WARN_OR_FAIL(strict, image, cmd, cleanupBlock) \ { 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.20.2/libheif/api/libheif/heif_metadata.h000664 001750 001750 00000013713 15044356510 023007 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_METADATA_H #define LIBHEIF_HEIF_METADATA_H #ifdef __cplusplus extern "C" { #endif #include 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 }; // ------------------------- 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 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 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 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 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 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 heif_error heif_image_handle_get_metadata(const 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 heif_image_handle* handle, heif_item_id metadata_id); // --- writing Exif / XMP metadata --- // Add EXIF metadata to an image. LIBHEIF_API heif_error heif_context_add_exif_metadata(heif_context*, const heif_image_handle* image_handle, const void* data, int size); // Add XMP metadata to an image. LIBHEIF_API heif_error heif_context_add_XMP_metadata(heif_context*, const heif_image_handle* image_handle, const void* data, int size); // New version of heif_context_add_XMP_metadata() with data compression (experimental). LIBHEIF_API heif_error heif_context_add_XMP_metadata2(heif_context*, const 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 heif_error heif_context_add_generic_metadata(heif_context* ctx, const 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 heif_error heif_context_add_generic_uri_metadata(heif_context* ctx, const heif_image_handle* image_handle, const void* data, int size, const char* item_uri_type, heif_item_id* out_item_id); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_image_handle.cc000664 001750 001750 00000007576 15044356510 023774 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_image_handle.h" #include "api_structs.h" void heif_image_handle_release(const struct heif_image_handle* handle) { delete handle; } 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_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_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(); } 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_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; } } 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; } } 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; } libheif-1.20.2/libheif/api/libheif/heif_emscripten.h000664 001750 001750 00000055212 15044356510 023400 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::vector 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: { size_t stride_y; const uint8_t* plane_y = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride_y); size_t stride_u; const uint8_t* plane_u = heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride_u); size_t stride_v; const uint8_t* plane_v = heif_image_get_plane_readonly2(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) { size_t stride_rgb; const uint8_t* plane_rgb = heif_image_get_plane_readonly2(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) { size_t stride_rgba; const uint8_t* plane_rgba = heif_image_get_plane_readonly2(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); size_t stride_grey; const uint8_t* plane_grey = heif_image_get_plane_readonly2(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::vector alpha; size_t stride_alpha; const uint8_t* plane_alpha = heif_image_get_plane_readonly2(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); size_t stride; const uint8_t* plane = heif_image_get_plane_readonly2(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); EXPORT_HEIF_FUNCTION(heif_image_handle_has_alpha_channel); EXPORT_HEIF_FUNCTION(heif_image_handle_is_premultiplied_alpha); 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_End_of_sequence", heif_error_End_of_sequence) .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_moov_box", heif_suberror_No_moov_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.20.2/libheif/api/libheif/heif_uncompressed.cc000664 001750 001750 00000006761 15044356510 024101 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_uncompressed.h" #include "context.h" #include "api_structs.h" #include "image-items/unc_image.h" #include #include #include struct heif_unci_image_parameters* heif_unci_image_parameters_alloc() { auto* params = new heif_unci_image_parameters(); params->version = 1; // --- version 1 params->image_width = 0; params->image_height = 0; // TODO: should we define that tile size = 0 means no tiling? params->tile_width = 0; params->tile_height = 0; params->compression = heif_unci_compression_off; return params; } void heif_unci_image_parameters_copy(struct heif_unci_image_parameters* dst, const struct heif_unci_image_parameters* src) { if (src == nullptr || dst == nullptr) { return; } int min_version = std::min(src->version, dst->version); switch (min_version) { case 1: dst->image_width = src->image_width; dst->image_height = src->image_height; dst->tile_width = src->tile_width; dst->tile_height = src->tile_height; dst->compression = src->compression; break; } } void heif_unci_image_parameters_release(struct heif_unci_image_parameters* params) { delete params; } struct heif_error heif_context_add_empty_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 if (prototype == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "prototype image is NULL"}; } if (out_unci_image_handle == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "out_unci_image_handle image is NULL"}; } 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()); } assert(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 } libheif-1.20.2/libheif/api/libheif/heif_version.h.in000664 001750 001750 00000002471 15044356510 023320 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.20.2/libheif/api/libheif/heif_color.cc000664 001750 001750 00000042646 15044356510 022512 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 #include "common_utils.h" #include #include "heif.h" #include "pixelimage.h" #include "api_structs.h" #include "error.h" #include #include #include #include #include #include #include #include #include #include #include static struct heif_error error_null_parameter = {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; 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; } static void fill_default_color_conversion_options_ext(heif_color_conversion_options_ext& options) { options.version = 1; options.alpha_composition_mode = heif_alpha_composition_mode_none; options.background_red = options.background_green = options.background_blue = 0xFFFF; options.secondary_background_red = options.secondary_background_green = options.secondary_background_blue = 0xCCCC; options.checkerboard_square_size = 16; } struct heif_color_conversion_options_ext* heif_color_conversion_options_ext_alloc() { auto options = new heif_color_conversion_options_ext; fill_default_color_conversion_options_ext(*options); return options; } void heif_color_conversion_options_ext_copy(struct heif_color_conversion_options_ext* dst, const struct heif_color_conversion_options_ext* src) { if (src == nullptr) { return; } int min_version = std::min(dst->version, src->version); switch (min_version) { case 1: dst->alpha_composition_mode = src->alpha_composition_mode; dst->background_red = src->background_red; dst->background_green = src->background_green; dst->background_blue = src->background_blue; dst->secondary_background_red = src->secondary_background_red; dst->secondary_background_green = src->secondary_background_green; dst->secondary_background_blue = src->secondary_background_blue; dst->checkerboard_square_size = src->checkerboard_square_size; } } void heif_color_conversion_options_ext_free(struct heif_color_conversion_options_ext* options) { delete options; } 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; } } 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()); } 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_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); } 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_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; } // --- content light level --- 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); } // --- mastering display colour volume --- 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; } libheif-1.20.2/libheif/api/libheif/heif_brands.h000664 001750 001750 00000024202 15044356510 022473 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_BRANDS_H #define LIBHEIF_HEIF_BRANDS_H #ifdef __cplusplus extern "C" { #endif #include #include #include 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') // H.264 #define heif_brand2_avci heif_fourcc('a','v','c','i') #define heif_brand2_avcs heif_fourcc('a','v','c','s') #define heif_brand2_iso8 heif_fourcc('i','s','o','8') // 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); // ========================= 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 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 ===================== // DEPRECATED, use heif_brand2 and the heif_brand2_* constants 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); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_context.cc000664 001750 001750 00000021206 15044356510 023045 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_context.h" #include "api_structs.h" #include "context.h" #include "init.h" #include "file.h" #include #include #include #include #include #include #ifdef _WIN32 // for _write #include #else #include #endif 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); 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_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_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_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()); } 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_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; } 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; } // ==================================================================================================== // Write the heif_context to a HEIF file 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; } } libheif-1.20.2/libheif/api/libheif/heif_cxx.h000664 001750 001750 00000120026 15044356510 022025 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; // DEPRECATED const uint8_t* get_plane(enum heif_channel channel, int* out_stride) const noexcept; // DEPRECATED uint8_t* get_plane(enum heif_channel channel, int* out_stride) noexcept; const uint8_t* get_plane2(enum heif_channel channel, size_t* out_stride) const noexcept; uint8_t* get_plane2(enum heif_channel channel, size_t* 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, NULL, NULL, NULL, NULL, }; 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 const uint8_t* Image::get_plane2(enum heif_channel channel, size_t* out_stride) const noexcept { return heif_image_get_plane_readonly2(m_image.get(), channel, out_stride); } inline uint8_t* Image::get_plane2(enum heif_channel channel, size_t* out_stride) noexcept { return heif_image_get_plane2(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.20.2/libheif/api/libheif/heif_error.h000664 001750 001750 00000020732 15044356510 022357 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_ERROR_H #define LIBHEIF_HEIF_ERROR_H #ifdef __cplusplus extern "C" { #endif #include #include 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, heif_error_End_of_sequence = 13 }; 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, heif_suberror_No_moov_box = 151, // --- 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 }; typedef 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; } heif_error; // Default success return value. Intended for use in user-supplied callback functions. LIBHEIF_API extern const heif_error heif_error_success; #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_decoding.cc000664 001750 001750 00000016033 15044356510 023137 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_decoding.h" #include "api_structs.h" #include "plugin_registry.h" #include #include #include #include void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads) { ctx->context->set_max_decoding_threads(max_threads); } int heif_have_decoder_for_format(enum heif_compression_format format) { auto plugin = get_decoder(format, nullptr); return plugin != nullptr; } static void fill_default_decoding_options(heif_decoding_options& options) { options.version = 7; 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; // version 7 options.color_conversion_options_ext = nullptr; } heif_decoding_options* heif_decoding_options_alloc() { auto options = new heif_decoding_options; fill_default_decoding_options(*options); return options; } // overwrite the (possibly lower version) input options over the default options void heif_decoding_options_copy(struct heif_decoding_options* dst, const struct heif_decoding_options* src) { if (src == nullptr) { return; } int min_version = std::min(dst->version, src->version); switch (min_version) { case 7: dst->color_conversion_options_ext = src->color_conversion_options_ext; [[fallthrough]]; case 6: dst->cancel_decoding = src->cancel_decoding; [[fallthrough]]; case 5: dst->color_conversion_options = src->color_conversion_options; [[fallthrough]]; case 4: dst->decoder_id = src->decoder_id; [[fallthrough]]; case 3: dst->strict_decoding = src->strict_decoding; [[fallthrough]]; case 2: dst->convert_hdr_to_8bit = src->convert_hdr_to_8bit; [[fallthrough]]; case 1: dst->ignore_transformations = src->ignore_transformations; dst->start_progress = src->start_progress; dst->on_progress = src->on_progress; dst->end_progress = src->end_progress; dst->progress_user_data = src->progress_user_data; } } void heif_decoding_options_free(heif_decoding_options* options) { delete options; } 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; } } 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()"}; } if (in_handle == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL heif_image_handle passed to heif_decode_image()"}; } *out_img = nullptr; heif_item_id id = in_handle->image->get_id(); heif_decoding_options dec_options; fill_default_decoding_options(dec_options); heif_decoding_options_copy(&dec_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()); } libheif-1.20.2/libheif/api/libheif/heif_properties.cc000664 001750 001750 00000041175 15044356510 023564 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; } 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; } 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); } 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; } 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; } // ------------------------- intrinsic and extrinsic matrices ------------------------- 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; } libheif-1.20.2/libheif/api/libheif/heif_experimental.cc000664 001750 001750 00000043535 15044356510 024067 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 "image-items/unc_image.h" #include "image-items/tiled.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; } #if HEIF_ENABLE_EXPERIMENTAL_FEATURES static struct heif_error error_null_parameter = {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx, const heif_item_id* layer_item_ids, size_t num_layers, /* uint16_t tile_width, uint16_t tile_height, uint32_t num_layers, const heif_pyramid_layer_info* in_layers, */ heif_item_id* out_group_id) { if (!layer_item_ids) { return error_null_parameter; } if (num_layers == 0) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Number of layers cannot be 0."}; } std::vector 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_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; } } 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) #endif #if HEIF_ENABLE_EXPERIMENTAL_FEATURES 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; } #endif libheif-1.20.2/libheif/api/libheif/heif_security.h000664 001750 001750 00000006231 15044356510 023073 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_SECURITY_H #define LIBHEIF_HEIF_SECURITY_H #ifdef __cplusplus extern "C" { #endif #include #include #include // --- security limits // If you set a limit to 0, the limit is disabled. typedef 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 // --- version 2 uint64_t max_total_memory; uint32_t max_sample_description_box_entries; uint32_t max_sample_group_description_box_entries; } heif_security_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 heif_security_limits* heif_get_global_security_limits(void); // Returns a set of fully disabled security limits. Use with care and only after user confirmation. LIBHEIF_API const heif_security_limits* heif_get_disabled_security_limits(void); // 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 heif_security_limits* heif_context_get_security_limits(const heif_context*); // Overwrites the security limits of a heif_context. // This is a convenience function to easily copy limits. LIBHEIF_API heif_error heif_context_set_security_limits(heif_context*, const heif_security_limits*); // --- DEPRECATED --- // 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(heif_context* ctx, int maximum_width); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_image.h000664 001750 001750 00000032273 15044356510 022313 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_IMAGE_H #define LIBHEIF_HEIF_IMAGE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include 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 }; // 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. typedef struct heif_image heif_image; typedef struct heif_image_handle heif_image_handle; typedef struct heif_security_limits heif_security_limits; // Get the colorspace format of the image. LIBHEIF_API enum heif_colorspace heif_image_get_colorspace(const heif_image*); // Get the chroma format of the image. LIBHEIF_API enum heif_chroma heif_image_get_chroma_format(const 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 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 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 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 heif_image* img); LIBHEIF_API heif_error heif_image_crop(heif_image* img, int left, int right, int top, int bottom); 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, heif_image** out_image); // 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 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 heif_image*, enum heif_channel channel); LIBHEIF_API int heif_image_has_channel(const 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. // Deprecated, use the safer version heif_image_get_plane_readonly2() instead. LIBHEIF_API const uint8_t* heif_image_get_plane_readonly(const heif_image*, enum heif_channel channel, int* out_stride); // Deprecated, use the safer version heif_image_get_plane2() instead. LIBHEIF_API uint8_t* heif_image_get_plane(heif_image*, enum heif_channel channel, int* out_stride); // These are safer variants of the two functions above. // The 'stride' parameter is often multiplied by the image height in the client application. // For very large images, this can lead to integer overflows and, consequently, illegal memory accesses. // The changed 'stride' parameter types eliminates this common error. LIBHEIF_API const uint8_t* heif_image_get_plane_readonly2(const heif_image*, enum heif_channel channel, size_t* out_stride); LIBHEIF_API uint8_t* heif_image_get_plane2(heif_image*, enum heif_channel channel, size_t* out_stride); typedef struct heif_scaling_options heif_scaling_options; // Currently, heif_scaling_options is not defined yet. Pass a NULL pointer. LIBHEIF_API heif_error heif_image_scale_image(const heif_image* input, heif_image** output, int width, int height, const 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 heif_error heif_image_extend_to_size_fill_with_zero(heif_image* image, uint32_t width, uint32_t height); // 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(heif_image* image, int first_warning_idx, 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(heif_image* image, heif_error err); // Release heif_image. LIBHEIF_API void heif_image_release(const heif_image*); LIBHEIF_API void heif_image_get_pixel_aspect_ratio(const heif_image*, uint32_t* aspect_h, uint32_t* aspect_v); LIBHEIF_API void heif_image_set_pixel_aspect_ratio(heif_image*, uint32_t aspect_h, uint32_t aspect_v); // --- 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 heif_error heif_image_create(int width, int height, enum heif_colorspace colorspace, enum heif_chroma chroma, 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 heif_error heif_image_add_plane(heif_image* image, enum heif_channel channel, int width, int height, int bit_depth); /* * The security limits should preferably be the limits from a heif_context. * The memory allocated will then be registered in the memory budget of that context. */ LIBHEIF_API heif_error heif_image_add_plane_safe(heif_image* image, enum heif_channel channel, int width, int height, int bit_depth, const heif_security_limits* limits); // Signal that the image is premultiplied by the alpha pixel values. LIBHEIF_API void heif_image_set_premultiplied_alpha(heif_image* image, int is_premultiplied_alpha); LIBHEIF_API int heif_image_is_premultiplied_alpha(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 heif_error heif_image_extend_padding_to_size(heif_image* image, int min_physical_width, int min_physical_height); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_sequences.cc000664 001750 001750 00000051744 15044356510 023366 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_sequences.h" #include "context.h" #include "api_structs.h" #include "file.h" #include "sequences/track.h" #include "sequences/track_visual.h" #include "sequences/track_metadata.h" #include #include #include #include #include #include int heif_context_has_sequence(const heif_context* ctx) { return ctx->context->has_sequence(); } uint32_t heif_context_get_sequence_timescale(const heif_context* ctx) { return ctx->context->get_sequence_timescale(); } uint64_t heif_context_get_sequence_duration(const heif_context* ctx) { return ctx->context->get_sequence_duration(); } void heif_track_release(heif_track* track) { delete track; } int heif_context_number_of_sequence_tracks(const struct heif_context* ctx) { return ctx->context->get_number_of_tracks(); } void heif_context_get_track_ids(const struct heif_context* ctx, uint32_t out_track_id_array[]) { std::vector IDs; IDs = ctx->context->get_track_IDs(); for (uint32_t id : IDs) { *out_track_id_array++ = id; } } uint32_t heif_track_get_id(const struct heif_track* track) { return track->track->get_id(); } // Use id=0 for the first visual track. struct heif_track* heif_context_get_track(const struct heif_context* ctx, uint32_t track_id) { auto trackResult = ctx->context->get_track(track_id); if (trackResult.error) { return nullptr; } auto* track = new heif_track; track->track = trackResult.value; track->context = ctx->context; return track; } uint32_t heif_track_get_track_handler_type(const struct heif_track* track) { return track->track->get_handler(); } uint32_t heif_track_get_timescale(const struct heif_track* track) { return track->track->get_timescale(); } struct heif_error heif_track_get_image_resolution(const struct heif_track* track_ptr, uint16_t* out_width, uint16_t* out_height) { auto track = track_ptr->track; auto visual_track = std::dynamic_pointer_cast(track); if (!visual_track) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Cannot get resolution of non-visual track."}; } if (out_width) *out_width = visual_track->get_width(); if (out_height) *out_height = visual_track->get_height(); return heif_error_ok; } struct heif_error heif_track_decode_next_image(struct heif_track* track_ptr, struct heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const struct heif_decoding_options* options) { if (out_img == nullptr) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "Output image pointer is NULL."}; } // --- get the visual track auto track = track_ptr->track; // --- reached end of sequence ? if (track->end_of_sequence_reached()) { *out_img = nullptr; return {heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } // --- decode next sequence image std::unique_ptr opts(heif_decoding_options_alloc(), heif_decoding_options_free); heif_decoding_options_copy(opts.get(), options); auto visual_track = std::dynamic_pointer_cast(track); if (!visual_track) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Cannot get image from non-visual track."}; } auto decodingResult = visual_track->decode_next_image_sample(*opts); if (!decodingResult) { return decodingResult.error.error_struct(track_ptr->context.get()); } std::shared_ptr img = *decodingResult; // --- convert to output colorspace auto conversion_result = track_ptr->context->convert_to_output_colorspace(img, colorspace, chroma, *opts); if (conversion_result.error) { return conversion_result.error.error_struct(track_ptr->context.get()); } else { img = *conversion_result; } *out_img = new heif_image(); (*out_img)->image = std::move(img); return {}; } uint32_t heif_image_get_duration(const heif_image* img) { return img->image->get_sample_duration(); } uint32_t heif_track_get_sample_entry_type_of_first_cluster(const struct heif_track* track) { return track->track->get_first_cluster_sample_entry_type(); } heif_error heif_track_get_urim_sample_entry_uri_of_first_cluster(const struct heif_track* track, const char** out_uri) { Result uriResult = track->track->get_first_cluster_urim_uri(); if (uriResult.error.error_code) { return uriResult.error.error_struct(track->context.get()); } if (out_uri) { const std::string& uri = uriResult.value; char* s = new char[uri.size() + 1]; strncpy(s, uri.c_str(), uri.size()); s[uri.size()] = '\0'; *out_uri = s; } return heif_error_ok; } struct heif_error heif_track_get_next_raw_sequence_sample(struct heif_track* track_ptr, heif_raw_sequence_sample** out_sample) { auto track = track_ptr->track; // --- reached end of sequence ? if (track->end_of_sequence_reached()) { return {heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } // --- get next raw sample auto decodingResult = track->get_next_sample_raw_data(); if (!decodingResult) { return decodingResult.error.error_struct(track_ptr->context.get()); } *out_sample = decodingResult.value; return heif_error_success; } void heif_raw_sequence_sample_release(struct heif_raw_sequence_sample* sample) { delete sample; } const uint8_t* heif_raw_sequence_sample_get_data(const heif_raw_sequence_sample* sample, size_t* out_array_size) { if (out_array_size) { *out_array_size = sample->data.size(); } return sample->data.data(); } size_t heif_raw_sequence_sample_get_data_size(const heif_raw_sequence_sample* sample) { return sample->data.size(); } uint32_t heif_raw_sequence_sample_get_duration(const heif_raw_sequence_sample* sample) { return sample->duration; } // --- writing sequences void heif_context_set_sequence_timescale(heif_context* ctx, uint32_t timescale) { ctx->context->set_sequence_timescale(timescale); } struct heif_track_options { TrackOptions options; }; heif_track_options* heif_track_options_alloc() { return new heif_track_options; } void heif_track_options_release(struct heif_track_options* options) { delete options; } void heif_track_options_set_timescale(struct heif_track_options* options, uint32_t timescale) { options->options.track_timescale = timescale; } void heif_track_options_set_interleaved_sample_aux_infos(struct heif_track_options* options, int interleaved_flag) { options->options.write_sample_aux_infos_interleaved = (interleaved_flag != 0); } struct heif_error heif_track_options_enable_sample_tai_timestamps(struct heif_track_options* options, const struct heif_tai_clock_info* tai_info, enum heif_sample_aux_info_presence presence) { if (presence != heif_sample_aux_info_presence_none && tai_info == nullptr) { return {heif_error_Usage_error, heif_suberror_Unspecified, "NULL tai clock info passed for track with TAI timestamps"}; } options->options.with_sample_tai_timestamps = presence; // delete old object in case we are calling heif_track_options_enable_sample_tai_timestamps() multiple times delete options->options.tai_clock_info; if (tai_info != nullptr) { options->options.tai_clock_info = heif_tai_clock_info_alloc(); heif_tai_clock_info_copy(options->options.tai_clock_info, tai_info); } else { options->options.tai_clock_info = nullptr; } return heif_error_ok; } void heif_track_options_enable_sample_gimi_content_ids(struct heif_track_options* options, enum heif_sample_aux_info_presence presence) { options->options.with_sample_content_ids = presence; } void heif_track_options_set_gimi_track_id(struct heif_track_options* options, const char* track_id) { if (track_id == nullptr) { options->options.gimi_track_content_id.clear(); return; } options->options.gimi_track_content_id = track_id; } heif_sequence_encoding_options* heif_sequence_encoding_options_alloc() { heif_sequence_encoding_options* options = new heif_sequence_encoding_options(); options->version = 1; options->output_nclx_profile = nullptr; 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; return options; } void heif_sequence_encoding_options_release(heif_sequence_encoding_options* options) { delete options; } struct heif_error heif_context_add_visual_sequence_track(heif_context* ctx, uint16_t width, uint16_t height, heif_track_type track_type, const struct heif_track_options* track_options, const struct heif_sequence_encoding_options* encoding_options, heif_track** out_track) { if (track_type != heif_track_type_video && track_type != heif_track_type_image_sequence) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "visual track has to be of type video or image sequence"}; } TrackOptions default_track_info; const TrackOptions* track_info = &default_track_info; if (track_options != nullptr) { track_info = &track_options->options; } Result> addResult = ctx->context->add_visual_sequence_track(track_info, track_type, width,height); if (addResult.error) { return addResult.error.error_struct(ctx->context.get()); } if (out_track) { auto* track = new heif_track; track->track = *addResult; track->context = ctx->context; *out_track = track; } return heif_error_ok; } void heif_image_set_duration(heif_image* img, uint32_t duration) { img->image->set_sample_duration(duration); } struct heif_error heif_track_encode_sequence_image(struct heif_track* track, const struct heif_image* input_image, struct heif_encoder* encoder, const struct heif_sequence_encoding_options* sequence_encoding_options) { // the input track must be a visual track auto visual_track = std::dynamic_pointer_cast(track->track); if (!visual_track) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Cannot encode image for non-visual track."}; } // convert heif_sequence_encoding_options to heif_encoding_options that is used by track->encode_image() heif_encoding_options* encoding_options = heif_encoding_options_alloc(); heif_color_profile_nclx nclx; if (sequence_encoding_options) { // the const_cast<> is ok, because output_nclx_profile will not be changed. It should actually be const, but we cannot change that. encoding_options->output_nclx_profile = const_cast(sequence_encoding_options->output_nclx_profile); encoding_options->color_conversion_options = sequence_encoding_options->color_conversion_options; if (encoding_options->output_nclx_profile == nullptr) { auto input_nclx = input_image->image->get_color_profile_nclx(); if (input_nclx) { encoding_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(); } } } // encode the image auto error = visual_track->encode_image(input_image->image, encoder, *encoding_options, heif_image_input_class_normal); heif_encoding_options_free(encoding_options); if (error.error_code) { return error.error_struct(track->context.get()); } return heif_error_ok; } struct heif_error heif_context_add_uri_metadata_sequence_track(heif_context* ctx, const char* uri, const struct heif_track_options* track_options, heif_track** out_track) { struct TrackOptions default_track_info; const struct TrackOptions* track_info = &default_track_info; if (track_options != nullptr) { track_info = &track_options->options; } Result> addResult = ctx->context->add_uri_metadata_sequence_track(track_info, uri); if (addResult.error) { return addResult.error.error_struct(ctx->context.get()); } if (out_track) { auto* track = new heif_track; track->track = *addResult; track->context = ctx->context; *out_track = track; } return heif_error_ok; } heif_raw_sequence_sample* heif_raw_sequence_sample_alloc() { return new heif_raw_sequence_sample(); } heif_error heif_raw_sequence_sample_set_data(heif_raw_sequence_sample* sample, const uint8_t* data, size_t size) { // TODO: do we have to check the vector memory allocation? sample->data.clear(); sample->data.insert(sample->data.begin(), data, data + size); return heif_error_ok; } void heif_raw_sequence_sample_set_duration(heif_raw_sequence_sample* sample, uint32_t duration) { sample->duration = duration; } struct heif_error heif_track_add_raw_sequence_sample(struct heif_track* track, const struct heif_raw_sequence_sample* sample) { auto metadata_track = std::dynamic_pointer_cast(track->track); if (!metadata_track) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Cannot save metadata in a non-metadata track."}; } auto error = metadata_track->write_raw_metadata(sample); if (error.error_code) { return error.error_struct(track->context.get()); } return heif_error_ok; } int heif_track_get_number_of_sample_aux_infos(const struct heif_track* track) { std::vector aux_info_types = track->track->get_sample_aux_info_types(); return (int)aux_info_types.size(); } void heif_track_get_sample_aux_info_types(const struct heif_track* track, struct heif_sample_aux_info_type out_types[]) { std::vector aux_info_types = track->track->get_sample_aux_info_types(); for (size_t i=0;itrack->get_track_info().gimi_track_content_id; if (contentId.empty()) { return nullptr; } char* id = new char[contentId.length() + 1]; strcpy(id, contentId.c_str()); return id; } const char* heif_image_get_gimi_sample_content_id(const heif_image* img) { if (!img->image->has_gimi_sample_content_id()) { return nullptr; } auto id_string = img->image->get_gimi_sample_content_id(); char* id = new char[id_string.length() + 1]; strcpy(id, id_string.c_str()); return id; } const char* heif_raw_sequence_sample_get_gimi_sample_content_id(const heif_raw_sequence_sample* sample) { char* s = new char[sample->gimi_sample_content_id.size() + 1]; strcpy(s, sample->gimi_sample_content_id.c_str()); return s; } void heif_image_set_gimi_sample_content_id(heif_image* img, const char* contentID) { if (contentID) { img->image->set_gimi_sample_content_id(contentID); } else { img->image->set_gimi_sample_content_id({}); } } void heif_raw_sequence_sample_set_gimi_sample_content_id(heif_raw_sequence_sample* sample, const char* contentID) { if (contentID) { sample->gimi_sample_content_id = contentID; } else { sample->gimi_sample_content_id.clear(); } } int heif_raw_sequence_sample_has_tai_timestamp(const struct heif_raw_sequence_sample* sample) { return sample->timestamp ? 1 : 0; } const struct heif_tai_timestamp_packet* heif_raw_sequence_sample_get_tai_timestamp(const struct heif_raw_sequence_sample* sample) { if (!sample->timestamp) { return nullptr; } return sample->timestamp; } void heif_raw_sequence_sample_set_tai_timestamp(struct heif_raw_sequence_sample* sample, const struct heif_tai_timestamp_packet* timestamp) { // release of timestamp in case we overwrite it heif_tai_timestamp_packet_release(sample->timestamp); sample->timestamp = heif_tai_timestamp_packet_alloc(); heif_tai_timestamp_packet_copy(sample->timestamp, timestamp); } const struct heif_tai_clock_info* heif_track_get_tai_clock_info_of_first_cluster(struct heif_track* track) { auto first_taic = track->track->get_first_cluster_taic(); if (!first_taic) { return nullptr; } return first_taic->get_tai_clock_info(); } void heif_track_add_reference_to_track(heif_track* track, uint32_t reference_type, const heif_track* to_track) { track->track->add_reference_to_track(reference_type, to_track->track->get_id()); } size_t heif_track_get_number_of_track_reference_types(const heif_track* track) { auto tref = track->track->get_tref_box(); if (!tref) { return 0; } return tref->get_number_of_reference_types(); } void heif_track_get_track_reference_types(const heif_track* track, uint32_t out_reference_types[]) { auto tref = track->track->get_tref_box(); if (!tref) { return; } auto refTypes = tref->get_reference_types(); for (size_t i = 0; i < refTypes.size(); i++) { out_reference_types[i] = refTypes[i]; } } size_t heif_track_get_number_of_track_reference_of_type(const heif_track* track, uint32_t reference_type) { auto tref = track->track->get_tref_box(); if (!tref) { return 0; } return tref->get_number_of_references_of_type(reference_type); } size_t heif_track_get_references_from_track(const heif_track* track, uint32_t reference_type, uint32_t out_to_track_id[]) { auto tref = track->track->get_tref_box(); if (!tref) { return 0; } auto refs = tref->get_references(reference_type); for (size_t i = 0; i < refs.size(); i++) { out_to_track_id[i] = refs[i]; } return refs.size(); } size_t heif_track_find_referring_tracks(const heif_track* track, uint32_t reference_type, uint32_t out_track_id[], size_t array_size) { size_t nFound = 0; // iterate through all tracks auto trackIDs = track->context->get_track_IDs(); for (auto id : trackIDs) { // a track should never reference itself if (id == track->track->get_id()) { continue; } // get the other track object auto other_trackResult = track->context->get_track(id); if (other_trackResult.error) { continue; // TODO: should we return an error in this case? } auto other_track = other_trackResult.value; // get the references of the other track auto tref = other_track->get_tref_box(); if (!tref) { continue; } // if the other track has a reference that points to the current track, add the other track to the list std::vector refs = tref->get_references(reference_type); for (uint32_t to_track : refs) { if (to_track == track->track->get_id() && nFound < array_size) { out_track_id[nFound++] = other_track->get_id(); break; } } // quick exit path if (nFound == array_size) break; } return nFound; } libheif-1.20.2/libheif/api/libheif/heif_image.cc000664 001750 001750 00000026443 15044356510 022453 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_image.h" #include "heif.h" #include "pixelimage.h" #include "api_structs.h" #include "error.h" #include #include #include #include #include #include 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; } 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; } 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); } 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; } const uint8_t* heif_image_get_plane_readonly2(const struct heif_image* image, enum heif_channel channel, size_t* out_stride) { if (!out_stride) { return nullptr; } if (!image || !image->image) { *out_stride = 0; return nullptr; } return image->image->get_plane(channel, out_stride); } uint8_t* heif_image_get_plane2(struct heif_image* image, enum heif_channel channel, size_t* out_stride) { if (!out_stride) { return nullptr; } if (!image || !image->image) { *out_stride = 0; return nullptr; } return image->image->get_plane(channel, out_stride); } 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; } 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)); } void heif_image_release(const struct heif_image* img) { delete img; } 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); } 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); } 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; } 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_plane_safe(struct heif_image* image, heif_channel channel, int width, int height, int bit_depth, const heif_security_limits* limits) { if (auto err = image->image->add_plane(channel, width, height, bit_depth, limits)) { return err.error_struct(image->image.get()); } else { return heif_error_success; } } 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; } } libheif-1.20.2/libheif/api/libheif/heif_tiling.h000664 001750 001750 00000012515 15044356510 022514 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_TILING_H #define LIBHEIF_HEIF_TILING_H #ifdef __cplusplus extern "C" { #endif #include #include #include // forward declaration from other headers typedef struct heif_encoder heif_encoder; typedef struct heif_encoding_options heif_encoding_options; typedef 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) } heif_image_tiling; // --- decoding --- // 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 heif_error heif_image_handle_get_image_tiling(const 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 heif_error heif_image_handle_get_grid_image_tile_id(const heif_image_handle* handle, int process_image_transformations, uint32_t tile_x, uint32_t tile_y, heif_item_id* out_tile_item_id); typedef struct heif_decoding_options 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 heif_error heif_image_handle_decode_image_tile(const heif_image_handle* in_handle, heif_image** out_img, enum heif_colorspace colorspace, enum heif_chroma chroma, const heif_decoding_options* options, uint32_t tile_x, uint32_t tile_y); // --- encoding --- /** * @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 heif_error heif_context_encode_grid(heif_context* ctx, heif_image** tiles, uint16_t rows, uint16_t columns, heif_encoder* encoder, const heif_encoding_options* input_options, heif_image_handle** out_image_handle); LIBHEIF_API heif_error heif_context_add_grid_image(heif_context* ctx, uint32_t image_width, uint32_t image_height, uint32_t tile_columns, uint32_t tile_rows, const heif_encoding_options* encoding_options, heif_image_handle** out_grid_image_handle); LIBHEIF_API heif_error heif_context_add_image_tile(heif_context* ctx, heif_image_handle* tiled_image, uint32_t tile_x, uint32_t tile_y, const heif_image* image, heif_encoder* encoder); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_metadata.cc000664 001750 001750 00000016220 15044356510 023141 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_metadata.h" #include "libheif/heif.h" #include "api_structs.h" #include "box.h" #include #include #include #include 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; } 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()); } 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; } 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; } } libheif-1.20.2/libheif/api/libheif/heif_image_handle.h000664 001750 001750 00000011316 15044356510 023621 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_IMAGE_HANDLE_H #define LIBHEIF_HEIF_IMAGE_HANDLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include // ========================= 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 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 heif_image_handle* handle); LIBHEIF_API heif_item_id heif_image_handle_get_item_id(const heif_image_handle* handle); // Get the resolution of an image. LIBHEIF_API int heif_image_handle_get_width(const heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_height(const heif_image_handle* handle); LIBHEIF_API int heif_image_handle_has_alpha_channel(const heif_image_handle*); LIBHEIF_API int heif_image_handle_is_premultiplied_alpha(const 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 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 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 heif_error heif_image_handle_get_preferred_decoding_colorspace(const 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 heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_ispe_height(const heif_image_handle* handle); // 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 heif_image_handle*, uint32_t* aspect_h, uint32_t* aspect_v); // 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 heif_context* heif_image_handle_get_context(const heif_image_handle* handle); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/api/libheif/heif_aux_images.h000664 001750 001750 00000016063 15044356510 023352 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2017-2025 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_AUX_IMAGES_H #define LIBHEIF_HEIF_AUX_IMAGES_H #ifdef __cplusplus extern "C" { #endif #include #include #include typedef struct heif_encoder heif_encoder; typedef struct heif_encoding_options heif_encoding_options; // ------------------------- depth images ------------------------- LIBHEIF_API int heif_image_handle_has_depth_image(const heif_image_handle*); LIBHEIF_API int heif_image_handle_get_number_of_depth_images(const heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_list_of_depth_image_IDs(const heif_image_handle* handle, heif_item_id* ids, int count); LIBHEIF_API heif_error heif_image_handle_get_depth_image_handle(const heif_image_handle* handle, heif_item_id depth_image_id, 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 }; typedef 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 } heif_depth_representation_info; LIBHEIF_API void heif_depth_representation_info_free(const 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 heif_image_handle* handle, heif_item_id depth_image_id, const 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 heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_list_of_thumbnail_IDs(const heif_image_handle* handle, heif_item_id* ids, int count); // Get the image handle of a thumbnail image. LIBHEIF_API heif_error heif_image_handle_get_thumbnail(const heif_image_handle* main_image_handle, heif_item_id thumbnail_id, heif_image_handle** out_thumbnail_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 heif_error heif_context_encode_thumbnail(heif_context*, const heif_image* image, const heif_image_handle* master_image_handle, heif_encoder* encoder, const heif_encoding_options* options, int bbox_size, heif_image_handle** out_thumb_image_handle); // Assign 'thumbnail_image' as the thumbnail image of 'master_image'. LIBHEIF_API heif_error heif_context_assign_thumbnail(struct heif_context*, const heif_image_handle* master_image, const heif_image_handle* thumbnail_image); // ------------------------- 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 heif_image_handle* handle, int aux_filter); LIBHEIF_API int heif_image_handle_get_list_of_auxiliary_image_IDs(const 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 heif_error heif_image_handle_get_auxiliary_type(const heif_image_handle* handle, const char** out_type); LIBHEIF_API void heif_image_handle_release_auxiliary_type(const heif_image_handle* handle, const char** out_type); // Get the image handle of an auxiliary image. LIBHEIF_API heif_error heif_image_handle_get_auxiliary_image_handle(const heif_image_handle* main_image_handle, heif_item_id auxiliary_id, heif_image_handle** out_auxiliary_handle); // ===================== DEPRECATED ===================== // DEPRECATED (because typo in function name). Use heif_image_handle_release_auxiliary_type() instead. LIBHEIF_API void heif_image_handle_free_auxiliary_types(const heif_image_handle* handle, const char** out_type); #ifdef __cplusplus } #endif #endif libheif-1.20.2/libheif/init.cc000664 001750 001750 00000023740 15044356510 017163 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.20.2/libheif/api_structs.h000664 001750 001750 00000004534 15044356510 020422 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 #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_track { std::shared_ptr track; // store reference to keep the context alive while we are using the handle (issue #147) std::shared_ptr context; }; struct heif_raw_sequence_sample { ~heif_raw_sequence_sample() { heif_tai_timestamp_packet_release(timestamp); } std::vector data; uint32_t duration = 0; heif_tai_timestamp_packet* timestamp = nullptr; std::string gimi_sample_content_id; }; 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.20.2/libheif/plugins_windows.h000664 001750 001750 00000003047 15044356510 021313 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.20.2/libheif/error.h000664 001750 001750 00000005673 15044356510 017220 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 Error from_heif_error(const heif_error&); 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.20.2/libheif/brands.h000664 001750 001750 00000001764 15044356510 017335 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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_BRANDS_H #define LIBHEIF_BRANDS_H #include "context.h" #include "error.h" #include #include std::vector compute_compatible_brands(const HeifContext* ctx, heif_brand2* out_main_brand); #endif libheif-1.20.2/libheif/logging.h000664 001750 001750 00000003065 15044356510 017506 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.20.2/libheif/plugins_windows.cc000664 001750 001750 00000005503 15044356510 021450 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.20.2/libheif/nclx.cc000664 001750 001750 00000031603 15044356510 017161 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.20.2/libheif/mdat_data.h000664 001750 001750 00000003173 15044356510 017776 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_MDAT_DATA_H #define LIBHEIF_MDAT_DATA_H #include #include #include #include class MdatData { public: virtual ~MdatData() = default; // returns the start position of the appended data virtual size_t append_data(const std::vector& data) = 0; virtual size_t get_data_size() const = 0; virtual Error write(StreamWriter&) = 0; }; class MdatData_Memory : public MdatData { public: size_t append_data(const std::vector& data) override { size_t startPos = m_data.size(); m_data.insert(m_data.end(), data.begin(), data.end()); return startPos; } size_t get_data_size() const override { return m_data.size(); } Error write(StreamWriter& writer) override { writer.write(m_data); return Error::Ok; } private: std::vector m_data; }; #endif //LIBHEIF_MDAT_DATA_H libheif-1.20.2/libheif/CMakeLists.txt000664 001750 001750 00000025630 15044356510 020451 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_library.h api/libheif/heif_image.h api/libheif/heif_color.h api/libheif/heif_error.h api/libheif/heif_plugin.h api/libheif/heif_properties.h api/libheif/heif_regions.h api/libheif/heif_items.h api/libheif/heif_sequences.h api/libheif/heif_tai_timestamps.h api/libheif/heif_brands.h api/libheif/heif_metadata.h api/libheif/heif_aux_images.h api/libheif/heif_entity_groups.h api/libheif/heif_security.h api/libheif/heif_encoding.h api/libheif/heif_decoding.h api/libheif/heif_image_handle.h api/libheif/heif_context.h api/libheif/heif_tiling.h api/libheif/heif_uncompressed.h api/libheif/heif_cxx.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 brands.cc brands.h api_structs.h api/libheif/heif.cc api/libheif/heif_library.cc api/libheif/heif_image.cc api/libheif/heif_color.cc api/libheif/heif_regions.cc api/libheif/heif_plugin.cc api/libheif/heif_properties.cc api/libheif/heif_items.cc api/libheif/heif_sequences.cc api/libheif/heif_tai_timestamps.cc api/libheif/heif_brands.cc api/libheif/heif_metadata.cc api/libheif/heif_aux_images.cc api/libheif/heif_entity_groups.cc api/libheif/heif_security.cc api/libheif/heif_encoding.cc api/libheif/heif_decoding.cc api/libheif/heif_image_handle.cc api/libheif/heif_context.cc api/libheif/heif_tiling.cc api/libheif/heif_uncompressed.cc codecs/decoder.h codecs/decoder.cc codecs/encoder.h codecs/encoder.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 codecs/hevc_enc.cc codecs/hevc_enc.h image-items/avif.cc image-items/avif.h codecs/avif_enc.cc codecs/avif_enc.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 codecs/jpeg_enc.h codecs/jpeg_enc.cc image-items/jpeg2000.h image-items/jpeg2000.cc codecs/jpeg2000_dec.h codecs/jpeg2000_dec.cc codecs/jpeg2000_enc.h codecs/jpeg2000_enc.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_enc.h codecs/vvc_enc.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 sequences/seq_boxes.h sequences/seq_boxes.cc sequences/chunk.h sequences/chunk.cc sequences/track.h sequences/track.cc sequences/track_visual.h sequences/track_visual.cc sequences/track_metadata.h sequences/track_metadata.cc ${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/unc_enc.h codecs/uncompressed/unc_enc.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.20.2/libheif/compression.h000664 001750 001750 00000005765 15044356510 020432 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.20.2/libheif/mini.cc000664 001750 001750 00000113334 15044356510 017153 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.20.2/libheif/brands.cc000664 001750 001750 00000010214 15044356510 017461 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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 "brands.h" #include "file.h" #include "sequences/track_visual.h" #include static bool check_mif1(const HeifContext* ctx) { auto file = ctx->get_heif_file(); auto meta = file->get_meta_box(); if (!meta || meta->get_version() != 0) { return false; } auto hdlr = meta->get_child_box(); if (!hdlr || hdlr->get_version() != 0) { return false; } auto iloc = meta->get_child_box(); if (!iloc || iloc->get_version() > 2) { return false; } auto iinf = meta->get_child_box(); if (!iinf || iinf->get_version() > 1) { return false; } auto infe = iinf->get_child_box(); if (!infe || infe->get_version() < 2 || infe->get_version() > 3) { return false; } auto pitm = meta->get_child_box(); if (!pitm || pitm->get_version() > 1) { return false; } auto iprp = meta->get_child_box(); if (!iprp) { return false; } return true; } std::vector> get_primary_and_alternative_images(const HeifContext* ctx) { auto img = ctx->get_primary_image(false); if (img) { return {std::move(img)}; } else { return {}; } } std::vector compute_compatible_brands(const HeifContext* ctx, heif_brand2* out_main_brand) { std::vector compatible_brands; heif_brand2 dummy; if (out_main_brand == nullptr) { out_main_brand = &dummy; // so that we do not have to check for NULL out_main_brand in the following } *out_main_brand = 0; // --- "mif" brands bool is_mif1 = check_mif1(ctx); if (is_mif1) { compatible_brands.push_back(heif_brand2_mif1); *out_main_brand = heif_brand2_mif1; } bool is_structural_image = is_mif1; // --- image brand std::vector> images = get_primary_and_alternative_images(ctx); bool miaf_compatible = true; for (auto& img : images) { heif_brand2 brand = img->get_compatible_brand(); if (brand != 0 && is_structural_image && std::find(compatible_brands.begin(), compatible_brands.end(), brand) == compatible_brands.end()) { compatible_brands.push_back(brand); } if (!img->is_miaf_compatible()) { miaf_compatible = false; } } // --- "miaf" if (miaf_compatible && is_structural_image) { compatible_brands.push_back(heif_brand2_miaf); } // --- main brand is first image brand if (!images.empty()) { heif_brand2 brand = images[0]->get_compatible_brand(); if (brand != 0) { *out_main_brand = brand; } } // --- --- sequences if (ctx->has_sequence()) { compatible_brands.push_back(heif_brand2_msf1); compatible_brands.push_back(heif_brand2_iso8); auto track_result = ctx->get_track(0); assert(!track_result.error); std::shared_ptr track = track_result.value; std::shared_ptr visual_track = std::dynamic_pointer_cast(track); heif_brand2 track_brand = visual_track->get_compatible_brand(); if (track_brand != 0) { compatible_brands.push_back(track_brand); if (*out_main_brand == 0) { *out_main_brand = track_brand; } } // if we don't have a track brand, use at least the sequence structural brand if (*out_main_brand == 0) { *out_main_brand = heif_brand2_msf1; } } return compatible_brands; } libheif-1.20.2/libheif/sequences/000775 001750 001750 00000000000 15044356511 017677 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/sequences/chunk.cc000664 001750 001750 00000006475 15044356510 021331 0ustar00farindkfarindk000000 000000 /* * HEIF image base 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 "chunk.h" #include "context.h" #include "codecs/hevc_enc.h" #include "codecs/avif_enc.h" #include "codecs/vvc_enc.h" #include "codecs/jpeg2000_enc.h" #include "codecs/jpeg_enc.h" #if WITH_UNCOMPRESSED_CODEC #include "codecs/uncompressed/unc_enc.h" #endif Chunk::Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format) : m_ctx(ctx), m_track_id(track_id), m_compression_format(format) { switch (format) { case heif_compression_HEVC: m_encoder = std::make_shared(); break; case heif_compression_AV1: m_encoder = std::make_shared(); break; case heif_compression_VVC: m_encoder = std::make_shared(); break; case heif_compression_JPEG2000: m_encoder = std::make_shared(); break; case heif_compression_HTJ2K: m_encoder = std::make_shared(); break; case heif_compression_JPEG: m_encoder = std::make_shared(); break; #if WITH_UNCOMPRESSED_CODEC case heif_compression_uncompressed: m_encoder = std::make_shared(); break; #endif case heif_compression_undefined: default: m_encoder = nullptr; break; } } Chunk::Chunk(HeifContext* ctx, uint32_t track_id, std::shared_ptr sample_description_box, uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr& stsz) { m_ctx = ctx; m_track_id = track_id; m_first_sample = first_sample; m_last_sample = first_sample + num_samples - 1; m_next_sample_to_be_decoded = first_sample; for (uint32_t i=0;ihas_fixed_sample_size()) { range.size = stsz->get_fixed_sample_size(); } else { range.size = stsz->get_sample_sizes()[first_sample + i]; } m_sample_ranges.push_back(range); file_offset += range.size; } if (auto visualSampleDescription = std::dynamic_pointer_cast(sample_description_box)) { m_decoder = Decoder::alloc_for_sequence_sample_description_box(visualSampleDescription); } } DataExtent Chunk::get_data_extent_for_sample(uint32_t n) const { assert(n>= m_first_sample); assert(n<= m_last_sample); DataExtent extent; extent.set_file_range(m_ctx->get_heif_file(), m_sample_ranges[n - m_first_sample].offset, m_sample_ranges[n - m_first_sample].size); return extent; } libheif-1.20.2/libheif/sequences/track_metadata.cc000664 001750 001750 00000011424 15044356510 023153 0ustar00farindkfarindk000000 000000 /* * HEIF image base codec. * Copyright (c) 2025 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 "track_metadata.h" #include "chunk.h" #include "context.h" #include "api_structs.h" #include Track_Metadata::Track_Metadata(HeifContext* ctx, const std::shared_ptr& trak) : Track(ctx, trak) { const std::vector& chunk_offsets = m_stco->get_offsets(); // Metadata tracks are not meant for display m_tkhd->set_flags(m_tkhd->get_flags() & ~(Box_tkhd::Flags::Track_in_movie | Box_tkhd::Flags::Track_in_preview)); // Find sequence resolution if (!chunk_offsets.empty()) { auto* s2c = m_stsc->get_chunk(static_cast(1)); if (!s2c) { return; } Box_stsc::SampleToChunk sampleToChunk = *s2c; auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1); if (!sample_description) { return; } // TODO: read URI } } Track_Metadata::Track_Metadata(HeifContext* ctx, uint32_t track_id, std::string uri, const TrackOptions* options) : Track(ctx, track_id, options, fourcc("meta")), m_uri(std::move(uri)) { auto nmhd = std::make_shared(); m_minf->append_child_box(nmhd); } #if 0 Result> Track_Metadata::read_next_metadata_sample() { if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } while (m_next_sample_to_be_decoded > m_chunks[m_current_chunk]->last_sample_number()) { m_current_chunk++; if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } } const std::shared_ptr& chunk = m_chunks[m_current_chunk]; auto decoder = chunk->get_decoder(); assert(decoder); decoder->set_data_extent(chunk->get_data_extent_for_sample(m_next_sample_to_be_decoded)); Result> decodingResult = decoder->decode_single_frame_from_compressed_data(options); if (decodingResult.error) { m_next_sample_to_be_decoded++; return decodingResult.error; } auto image = decodingResult.value; if (m_stts) { image->set_sample_duration(m_stts->get_sample_duration(m_next_sample_to_be_decoded)); } // --- read sample auxiliary data if (m_aux_reader_content_ids) { auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_decoded); if (readResult.error) { return readResult.error; } Result convResult = vector_to_string(readResult.value); if (convResult.error) { return convResult.error; } image->set_gimi_content_id(convResult.value); } if (m_aux_reader_tai_timestamps) { auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_decoded); if (readResult.error) { return readResult.error; } auto resultTai = Box_itai::decode_tai_from_vector(readResult.value); if (resultTai.error) { return resultTai.error; } image->set_tai_timestamp(&resultTai.value); } m_next_sample_to_be_decoded++; return image; } #endif Error Track_Metadata::write_raw_metadata(const heif_raw_sequence_sample* raw_sample) { // generate new chunk for first metadata packet if (m_chunks.empty()) { // --- write URIMetaSampleEntry ('urim') auto sample_description_box = std::make_shared(); auto uri = std::make_shared(); uri->set_uri(m_uri); sample_description_box->append_child_box(uri); add_chunk(heif_compression_undefined); set_sample_description_box(sample_description_box); } Error err = write_sample_data(raw_sample->data, raw_sample->duration, true, raw_sample->timestamp, raw_sample->gimi_sample_content_id); if (err) { return err; } return Error::Ok; } libheif-1.20.2/libheif/sequences/track.cc000664 001750 001750 00000051023 15044356510 021312 0ustar00farindkfarindk000000 000000 /* * HEIF image base 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 #include "track.h" #include "context.h" #include "sequences/seq_boxes.h" #include "sequences/chunk.h" #include "sequences/track_visual.h" #include "sequences/track_metadata.h" #include "api_structs.h" #include TrackOptions& TrackOptions::operator=(const TrackOptions& src) { if (&src == this) { return *this; } this->track_timescale = src.track_timescale; this->write_sample_aux_infos_interleaved = src.write_sample_aux_infos_interleaved; this->with_sample_tai_timestamps = src.with_sample_tai_timestamps; if (src.tai_clock_info) { this->tai_clock_info = heif_tai_clock_info_alloc(); heif_tai_clock_info_copy(this->tai_clock_info, src.tai_clock_info); } else { this->tai_clock_info = nullptr; } this->with_sample_content_ids = src.with_sample_content_ids; this->gimi_track_content_id = src.gimi_track_content_id; return *this; } SampleAuxInfoHelper::SampleAuxInfoHelper(bool interleaved) : m_interleaved(interleaved) { m_saiz = std::make_shared(); m_saio = std::make_shared(); } void SampleAuxInfoHelper::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter) { m_saiz->set_aux_info_type(aux_info_type, aux_info_type_parameter); m_saio->set_aux_info_type(aux_info_type, aux_info_type_parameter); } Error SampleAuxInfoHelper::add_sample_info(const std::vector& data) { if (data.size() > 0xFF) { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Encoded sample auxiliary information exceeds maximum size"}; } m_saiz->add_sample_size(static_cast(data.size())); m_data.insert(m_data.end(), data.begin(), data.end()); return Error::Ok; } void SampleAuxInfoHelper::add_nonpresent_sample() { m_saiz->add_nonpresent_sample(); } void SampleAuxInfoHelper::write_interleaved(const std::shared_ptr& file) { if (m_interleaved && !m_data.empty()) { uint64_t pos = file->append_mdat_data(m_data); m_saio->add_sample_offset(pos); m_data.clear(); } } void SampleAuxInfoHelper::write_all(const std::shared_ptr& parent, const std::shared_ptr& file) { parent->append_child_box(m_saiz); parent->append_child_box(m_saio); if (!m_data.empty()) { uint64_t pos = file->append_mdat_data(m_data); m_saio->add_sample_offset(pos); } } SampleAuxInfoReader::SampleAuxInfoReader(std::shared_ptr saiz, std::shared_ptr saio) { m_saiz = saiz; m_saio = saio; m_contiguous = (saio->get_num_samples() == 1); if (m_contiguous) { uint64_t offset = saio->get_sample_offset(0); auto nSamples = saiz->get_num_samples(); for (uint32_t i=0;iget_sample_size(i); } // TODO: we could add a special case for contiguous data with constant size } } heif_sample_aux_info_type SampleAuxInfoReader::get_type() const { heif_sample_aux_info_type type; type.type = m_saiz->get_aux_info_type(); type.parameter = m_saiz->get_aux_info_type_parameter(); return type; } Result> SampleAuxInfoReader::get_sample_info(const HeifFile* file, uint32_t idx) { uint64_t offset; if (m_contiguous) { offset = m_contiguous_offsets[idx]; } else { offset = m_saio->get_sample_offset(idx); } uint8_t size = m_saiz->get_sample_size(idx); std::vector data; Error err = file->append_data_from_file_range(data, offset, size); if (err) { return err; } return data; } std::shared_ptr Track::get_file() const { return m_heif_context->get_heif_file(); } Track::Track(HeifContext* ctx, const std::shared_ptr& trak_box) { m_heif_context = ctx; m_trak = trak_box; auto tkhd = trak_box->get_child_box(); if (!tkhd) { return; // TODO: error or dummy error track ? } m_id = tkhd->get_track_id(); auto mdia = trak_box->get_child_box(); if (!mdia) { return; } m_tref = trak_box->get_child_box(); auto hdlr = mdia->get_child_box(); if (!hdlr) { return; } m_handler_type = hdlr->get_handler_type(); m_minf = mdia->get_child_box(); if (!m_minf) { return; } m_mdhd = mdia->get_child_box(); if (!m_mdhd) { return; } auto stbl = m_minf->get_child_box(); if (!stbl) { return; } m_stsd = stbl->get_child_box(); if (!m_stsd) { return; } m_stsc = stbl->get_child_box(); if (!m_stsc) { return; } m_stco = stbl->get_child_box(); if (!m_stco) { return; } m_stsz = stbl->get_child_box(); if (!m_stsz) { return; } m_stts = stbl->get_child_box(); const std::vector& chunk_offsets = m_stco->get_offsets(); assert(chunk_offsets.size() <= (size_t) std::numeric_limits::max()); // There cannot be more than uint32_t chunks. uint32_t current_sample_idx = 0; for (size_t chunk_idx = 0; chunk_idx < chunk_offsets.size(); chunk_idx++) { auto* s2c = m_stsc->get_chunk(static_cast(chunk_idx + 1)); if (!s2c) { return; } Box_stsc::SampleToChunk sampleToChunk = *s2c; auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1); if (!sample_description) { return; } if (m_first_taic == nullptr) { auto taic = sample_description->get_child_box(); if (taic) { m_first_taic = taic; } } auto chunk = std::make_shared(ctx, m_id, sample_description, current_sample_idx, sampleToChunk.samples_per_chunk, m_stco->get_offsets()[chunk_idx], m_stsz); m_chunks.push_back(chunk); current_sample_idx += sampleToChunk.samples_per_chunk; } // --- read sample auxiliary information boxes std::vector> saiz_boxes = stbl->get_child_boxes(); std::vector> saio_boxes = stbl->get_child_boxes(); for (const auto& saiz : saiz_boxes) { uint32_t aux_info_type = saiz->get_aux_info_type(); uint32_t aux_info_type_parameter = saiz->get_aux_info_type_parameter(); // find the corresponding saio box std::shared_ptr saio; for (const auto& candidate : saio_boxes) { if (candidate->get_aux_info_type() == aux_info_type && candidate->get_aux_info_type_parameter() == aux_info_type_parameter) { saio = candidate; break; } } if (saio) { if (aux_info_type == fourcc("suid")) { m_aux_reader_content_ids = std::make_unique(saiz, saio); } if (aux_info_type == fourcc("stai")) { m_aux_reader_tai_timestamps = std::make_unique(saiz, saio); } } } // --- read track properties if (auto meta = trak_box->get_child_box()) { auto iloc = meta->get_child_box(); auto idat = meta->get_child_box(); auto iinf = meta->get_child_box(); if (iinf) { auto infe_boxes = iinf->get_child_boxes(); for (const auto& box : infe_boxes) { if (box->get_item_type_4cc() == fourcc("uri ") && box->get_item_uri_type() == "urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73") { heif_item_id id = box->get_item_ID(); std::vector data; Error err = iloc->read_data(id, ctx->get_heif_file()->get_reader(), idat, &data, ctx->get_security_limits()); if (err) { // TODO } Result contentIdResult = vector_to_string(data); if (contentIdResult.error) { // TODO } m_track_info.gimi_track_content_id = contentIdResult.value; } } } } } Track::Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* options, uint32_t handler_type) { m_heif_context = ctx; m_moov = ctx->get_heif_file()->get_moov_box(); assert(m_moov); // --- find next free track ID if (track_id == 0) { track_id = 1; // minimum track ID for (const auto& track : m_moov->get_child_boxes()) { auto tkhd = track->get_child_box(); if (tkhd->get_track_id() >= track_id) { track_id = tkhd->get_track_id() + 1; } } auto mvhd = m_moov->get_child_box(); mvhd->set_next_track_id(track_id + 1); m_id = track_id; } m_trak = std::make_shared(); m_moov->append_child_box(m_trak); m_tkhd = std::make_shared(); m_trak->append_child_box(m_tkhd); m_tkhd->set_track_id(track_id); auto mdia = std::make_shared(); m_trak->append_child_box(mdia); m_mdhd = std::make_shared(); m_mdhd->set_timescale(options ? options->track_timescale : 90000); mdia->append_child_box(m_mdhd); m_hdlr = std::make_shared(); mdia->append_child_box(m_hdlr); m_hdlr->set_handler_type(handler_type); m_minf = std::make_shared(); mdia->append_child_box(m_minf); // vmhd is added in Track_Visual m_stbl = std::make_shared(); m_minf->append_child_box(m_stbl); m_stsd = std::make_shared(); m_stbl->append_child_box(m_stsd); m_stts = std::make_shared(); m_stbl->append_child_box(m_stts); m_stsc = std::make_shared(); m_stbl->append_child_box(m_stsc); m_stsz = std::make_shared(); m_stbl->append_child_box(m_stsz); m_stco = std::make_shared(); m_stbl->append_child_box(m_stco); m_stss = std::make_shared(); m_stbl->append_child_box(m_stss); if (options) { m_track_info = *options; if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { m_aux_helper_tai_timestamps = std::make_unique(m_track_info.write_sample_aux_infos_interleaved); m_aux_helper_tai_timestamps->set_aux_info_type(fourcc("stai")); } if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) { m_aux_helper_content_ids = std::make_unique(m_track_info.write_sample_aux_infos_interleaved); m_aux_helper_content_ids->set_aux_info_type(fourcc("suid")); } if (!options->gimi_track_content_id.empty()) { auto hdlr_box = std::make_shared(); hdlr_box->set_handler_type(fourcc("meta")); auto uuid_box = std::make_shared(); uuid_box->set_item_type_4cc(fourcc("uri ")); uuid_box->set_item_uri_type("urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73"); uuid_box->set_item_ID(1); auto iinf_box = std::make_shared(); iinf_box->append_child_box(uuid_box); std::vector track_uuid_vector; track_uuid_vector.insert(track_uuid_vector.begin(), options->gimi_track_content_id.c_str(), options->gimi_track_content_id.c_str() + options->gimi_track_content_id.length() + 1); auto iloc_box = std::make_shared(); iloc_box->append_data(1, track_uuid_vector, 1); auto meta_box = std::make_shared(); meta_box->append_child_box(hdlr_box); meta_box->append_child_box(iinf_box); meta_box->append_child_box(iloc_box); m_trak->append_child_box(meta_box); } } } std::shared_ptr Track::alloc_track(HeifContext* ctx, const std::shared_ptr& trak) { auto mdia = trak->get_child_box(); if (!mdia) { return nullptr; } auto hdlr = mdia->get_child_box(); if (!mdia) { return nullptr; } switch (hdlr->get_handler_type()) { case fourcc("pict"): case fourcc("vide"): return std::make_shared(ctx, trak); case fourcc("meta"): return std::make_shared(ctx, trak); default: return nullptr; } } bool Track::is_visual_track() const { return m_handler_type == fourcc("pict"); } uint32_t Track::get_first_cluster_sample_entry_type() const { if (m_stsd->get_num_sample_entries() == 0) { return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry? } return m_stsd->get_sample_entry(0)->get_short_type(); } Result Track::get_first_cluster_urim_uri() const { if (m_stsd->get_num_sample_entries() == 0) { return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "This track has no sample entries."}; } std::shared_ptr sampleEntry = m_stsd->get_sample_entry(0); auto urim = std::dynamic_pointer_cast(sampleEntry); if (!urim) { return Error{heif_error_Usage_error, heif_suberror_Unspecified, "This cluster is no 'urim' sample entry."}; } std::shared_ptr uri = urim->get_child_box(); if (!uri) { return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "The 'urim' box has no 'uri' child box."}; } return uri->get_uri(); } bool Track::end_of_sequence_reached() const { return (m_next_sample_to_be_processed > m_chunks.back()->last_sample_number()); } void Track::finalize_track() { if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_all(m_stbl, get_file()); if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_all(m_stbl, get_file()); uint64_t duration = m_stts->get_total_duration(false); m_mdhd->set_duration(duration); } uint64_t Track::get_duration_in_media_units() const { return m_mdhd->get_duration(); } uint32_t Track::get_timescale() const { return m_mdhd->get_timescale(); } void Track::set_track_duration_in_movie_units(uint64_t total_duration) { m_tkhd->set_duration(total_duration); } void Track::add_chunk(heif_compression_format format) { auto chunk = std::make_shared(m_heif_context, m_id, format); m_chunks.push_back(chunk); int chunkIdx = (uint32_t) m_chunks.size(); m_stsc->add_chunk(chunkIdx); } void Track::set_sample_description_box(std::shared_ptr sample_description_box) { // --- add 'taic' when we store timestamps as sample auxiliary information if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { auto taic = std::make_shared(); taic->set_from_tai_clock_info(m_track_info.tai_clock_info); sample_description_box->append_child_box(taic); } m_stsd->add_sample_entry(sample_description_box); } Error Track::write_sample_data(const std::vector& raw_data, uint32_t sample_duration, bool is_sync_sample, const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID) { size_t data_start = m_heif_context->get_heif_file()->append_mdat_data(raw_data); // first sample in chunk? -> write chunk offset if (m_stsc->last_chunk_empty()) { // if auxiliary data is interleaved, write it between the chunks if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_interleaved(get_file()); if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_interleaved(get_file()); // TODO assert(data_start < 0xFF000000); // add some headroom for header data m_stco->add_chunk_offset(static_cast(data_start)); } m_stsc->increase_samples_in_chunk(1); m_stsz->append_sample_size((uint32_t)raw_data.size()); if (is_sync_sample) { m_stss->add_sync_sample(m_next_sample_to_be_processed + 1); } if (sample_duration == 0) { return {heif_error_Usage_error, heif_suberror_Unspecified, "Sample duration may not be 0"}; } m_stts->append_sample_duration(sample_duration); // --- sample timestamp if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { if (tai) { std::vector tai_data = Box_itai::encode_tai_to_bitstream(tai); auto err = m_aux_helper_tai_timestamps->add_sample_info(tai_data); if (err) { return err; } } else if (m_track_info.with_sample_tai_timestamps == heif_sample_aux_info_presence_optional) { m_aux_helper_tai_timestamps->add_nonpresent_sample(); } else { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Mandatory TAI timestamp missing"}; } } // --- sample content id if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) { if (!gimi_contentID.empty()) { auto id = gimi_contentID; const char* id_str = id.c_str(); std::vector id_vector; id_vector.insert(id_vector.begin(), id_str, id_str + id.length() + 1); auto err = m_aux_helper_content_ids->add_sample_info(id_vector); if (err) { return err; } } else if (m_track_info.with_sample_content_ids == heif_sample_aux_info_presence_optional) { m_aux_helper_content_ids->add_nonpresent_sample(); } else { return {heif_error_Encoding_error, heif_suberror_Unspecified, "Mandatory ContentID missing"}; } } m_next_sample_to_be_processed++; return Error::Ok; } void Track::add_reference_to_track(uint32_t referenceType, uint32_t to_track_id) { if (!m_tref) { m_tref = std::make_shared(); m_trak->append_child_box(m_tref); } m_tref->add_references(to_track_id, referenceType); } Result Track::get_next_sample_raw_data() { if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } while (m_next_sample_to_be_processed > m_chunks[m_current_chunk]->last_sample_number()) { m_current_chunk++; if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } } const std::shared_ptr& chunk = m_chunks[m_current_chunk]; DataExtent extent = chunk->get_data_extent_for_sample(m_next_sample_to_be_processed); auto readResult = extent.read_data(); if (readResult.error) { return readResult.error; } heif_raw_sequence_sample* sample = new heif_raw_sequence_sample(); sample->data = *readResult.value; // read sample duration if (m_stts) { sample->duration = m_stts->get_sample_duration(m_next_sample_to_be_processed); } // --- read sample auxiliary data if (m_aux_reader_content_ids) { auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_processed); if (readResult.error) { return readResult.error; } if (!readResult.value.empty()) { Result convResult = vector_to_string(readResult.value); if (convResult.error) { return convResult.error; } sample->gimi_sample_content_id = convResult.value; } } if (m_aux_reader_tai_timestamps) { auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_processed); if (readResult.error) { return readResult.error; } if (!readResult.value.empty()) { auto resultTai = Box_itai::decode_tai_from_vector(readResult.value); if (resultTai.error) { return resultTai.error; } sample->timestamp = heif_tai_timestamp_packet_alloc(); heif_tai_timestamp_packet_copy(sample->timestamp, &resultTai.value); } } m_next_sample_to_be_processed++; return sample; } std::vector Track::get_sample_aux_info_types() const { std::vector types; if (m_aux_reader_tai_timestamps) types.emplace_back(m_aux_reader_tai_timestamps->get_type()); if (m_aux_reader_content_ids) types.emplace_back(m_aux_reader_content_ids->get_type()); return types; } libheif-1.20.2/libheif/sequences/track_visual.cc000664 001750 001750 00000021540 15044356510 022676 0ustar00farindkfarindk000000 000000 /* * HEIF image base codec. * Copyright (c) 2025 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 "track_visual.h" #include "codecs/decoder.h" #include "codecs/encoder.h" #include "chunk.h" #include "pixelimage.h" #include "context.h" #include "api_structs.h" #include "codecs/hevc_boxes.h" Track_Visual::Track_Visual(HeifContext* ctx, const std::shared_ptr& trak) : Track(ctx, trak) { const std::vector& chunk_offsets = m_stco->get_offsets(); // Find sequence resolution if (!chunk_offsets.empty()) { auto* s2c = m_stsc->get_chunk(static_cast(1)); if (!s2c) { return; } Box_stsc::SampleToChunk sampleToChunk = *s2c; auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1); if (!sample_description) { return; // TODO } auto visual_sample_description = std::dynamic_pointer_cast(sample_description); if (!visual_sample_description) { return; // TODO } m_width = visual_sample_description->get_VisualSampleEntry_const().width; m_height = visual_sample_description->get_VisualSampleEntry_const().height; } } Track_Visual::Track_Visual(HeifContext* ctx, uint32_t track_id, uint16_t width, uint16_t height, const TrackOptions* options, uint32_t handler_type) : Track(ctx, track_id, options, handler_type) { m_tkhd->set_resolution(width, height); //m_hdlr->set_handler_type(handler_type); already done in Track() auto vmhd = std::make_shared(); m_minf->append_child_box(vmhd); } Result> Track_Visual::decode_next_image_sample(const struct heif_decoding_options& options) { if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } while (m_next_sample_to_be_processed > m_chunks[m_current_chunk]->last_sample_number()) { m_current_chunk++; if (m_current_chunk > m_chunks.size()) { return Error{heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"}; } } const std::shared_ptr& chunk = m_chunks[m_current_chunk]; auto decoder = chunk->get_decoder(); assert(decoder); decoder->set_data_extent(chunk->get_data_extent_for_sample(m_next_sample_to_be_processed)); Result> decodingResult = decoder->decode_single_frame_from_compressed_data(options, m_heif_context->get_security_limits()); if (decodingResult.error) { m_next_sample_to_be_processed++; return decodingResult.error; } auto image = decodingResult.value; if (m_stts) { image->set_sample_duration(m_stts->get_sample_duration(m_next_sample_to_be_processed)); } // --- read sample auxiliary data if (m_aux_reader_content_ids) { auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_processed); if (readResult.error) { return readResult.error; } Result convResult = vector_to_string(readResult.value); if (convResult.error) { return convResult.error; } image->set_gimi_sample_content_id(convResult.value); } if (m_aux_reader_tai_timestamps) { auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_processed); if (readResult.error) { return readResult.error; } auto resultTai = Box_itai::decode_tai_from_vector(readResult.value); if (resultTai.error) { return resultTai.error; } image->set_tai_timestamp(&resultTai.value); } m_next_sample_to_be_processed++; return image; } Error Track_Visual::encode_image(std::shared_ptr image, struct heif_encoder* h_encoder, const struct heif_encoding_options& in_options, heif_image_input_class input_class) { if (image->get_width() > 0xFFFF || image->get_height() > 0xFFFF) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Input image resolution too high"}; } // === generate compressed image bitstream // generate new chunk for first image or when compression formats don't match bool add_sample_description = false; if (m_chunks.empty() || m_chunks.back()->get_compression_format() != h_encoder->plugin->compression_format) { add_chunk(h_encoder->plugin->compression_format); add_sample_description = true; } // --- 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; auto encoder = m_chunks.back()->get_encoder(); if (const auto* nclx = encoder->get_forced_output_nclx()) { options.output_nclx_profile = const_cast(nclx); } Result> srcImageResult = encoder->convert_colorspace_for_encoding(image, h_encoder, options, m_heif_context->get_security_limits()); if (srcImageResult.error) { return srcImageResult.error; } std::shared_ptr colorConvertedImage = srcImageResult.value; // --- encode image Result encodeResult = encoder->encode(colorConvertedImage, h_encoder, options, input_class); if (encodeResult.error) { return encodeResult.error; } const Encoder::CodedImageData& data = encodeResult.value; // --- generate SampleDescriptionBox if (add_sample_description) { auto sample_description_box = encoder->get_sample_description_box(data); VisualSampleEntry& visualSampleEntry = sample_description_box->get_VisualSampleEntry(); visualSampleEntry.width = static_cast(colorConvertedImage->get_width()); visualSampleEntry.height = static_cast(colorConvertedImage->get_height()); auto ccst = std::make_shared(); ccst->set_coding_constraints(data.codingConstraints); sample_description_box->append_child_box(ccst); set_sample_description_box(sample_description_box); } Error err = write_sample_data(data.bitstream, colorConvertedImage->get_sample_duration(), data.is_sync_frame, image->get_tai_timestamp(), image->has_gimi_sample_content_id() ? image->get_gimi_sample_content_id() : std::string{}); if (err) { return err; } return Error::Ok; } heif_brand2 Track_Visual::get_compatible_brand() const { if (m_stsd->get_num_sample_entries() == 0) { return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry? } auto sampleEntry = m_stsd->get_sample_entry(0); uint32_t sample_entry_type = sampleEntry->get_short_type(); switch (sample_entry_type) { case fourcc("hvc1"): { auto hvcC = sampleEntry->get_child_box(); if (!hvcC) { return 0; } const auto& config = hvcC->get_configuration(); if (config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_Main) || config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_MainStillPicture)) { return heif_brand2_hevc; } else { return heif_brand2_hevx; } } case fourcc("avc1"): return heif_brand2_avcs; case fourcc("av01"): return heif_brand2_avis; case fourcc("j2ki"): return heif_brand2_j2is; case fourcc("mjpg"): return heif_brand2_jpgs; case fourcc("vvc1"): return heif_brand2_vvis; default: return 0; } } libheif-1.20.2/libheif/sequences/chunk.h000664 001750 001750 00000004402 15044356510 021157 0ustar00farindkfarindk000000 000000 /* * HEIF image base 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_CHUNK_H #define LIBHEIF_CHUNK_H #include "codecs/decoder.h" #include #include class HeifContext; class Box_VisualSampleEntry; class Chunk { public: Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format); Chunk(HeifContext* ctx, uint32_t track_id, std::shared_ptr sample_description_box, uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr& sample_sizes); virtual ~Chunk() = default; heif_compression_format get_compression_format() const { return m_compression_format; } virtual std::shared_ptr get_decoder() const { return m_decoder; } virtual std::shared_ptr get_encoder() const { return m_encoder; } uint32_t first_sample_number() const { return m_first_sample; } uint32_t last_sample_number() const { return m_last_sample; } DataExtent get_data_extent_for_sample(uint32_t n) const; private: HeifContext* m_ctx = nullptr; uint32_t m_track_id = 0; heif_compression_format m_compression_format = heif_compression_undefined; uint32_t m_first_sample = 0; uint32_t m_last_sample = 0; //uint32_t m_sample_description_index = 0; uint32_t m_next_sample_to_be_decoded = 0; struct SampleFileRange { uint64_t offset = 0; uint32_t size = 0; }; std::vector m_sample_ranges; std::shared_ptr m_decoder; std::shared_ptr m_encoder; }; #endif //LIBHEIF_CHUNK_H libheif-1.20.2/libheif/sequences/track_metadata.h000664 001750 001750 00000002530 15044356510 023013 0ustar00farindkfarindk000000 000000 /* * HEIF image base codec. * Copyright (c) 2025 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_TRACK_METADATA_H #define LIBHEIF_TRACK_METADATA_H #include "track.h" #include #include #include class Track_Metadata : public Track { public: //Track(HeifContext* ctx); Track_Metadata(HeifContext* ctx, uint32_t track_id, std::string uri, const TrackOptions* options); Track_Metadata(HeifContext* ctx, const std::shared_ptr&); // when reading the file ~Track_Metadata() override = default; Error write_raw_metadata(const heif_raw_sequence_sample*); private: std::string m_uri; }; #endif //LIBHEIF_TRACK_METADATA_H libheif-1.20.2/libheif/sequences/seq_boxes.h000664 001750 001750 00000050241 15044356510 022041 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 SEQ_BOXES_H #define SEQ_BOXES_H #include "box.h" #include "security_limits.h" #include #include #include class Box_container : public Box { public: Box_container(const char* type) { set_short_type(fourcc(type)); } std::string dump(Indent&) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; }; // Movie Box class Box_moov : public Box_container { public: Box_moov() : Box_container("moov") {} const char* debug_box_name() const override { return "Movie"; } }; // Movie Header Box class Box_mvhd : public FullBox { public: Box_mvhd() { set_short_type(fourcc("mvhd")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Movie Header"; } Error write(StreamWriter& writer) const override; void derive_box_version() override; double get_rate() const { return m_rate / double(0x10000); } float get_volume() const { return float(m_volume) / float(0x100); } double get_matrix_element(int idx) const; uint32_t get_time_scale() const { return m_timescale; } uint64_t get_duration() const { return m_duration; } void set_duration(uint64_t duration) { m_duration = duration; } void set_time_scale(uint32_t timescale) { m_timescale = timescale; } void set_next_track_id(uint32_t next_id) { m_next_track_ID = next_id; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint64_t m_creation_time = 0; uint64_t m_modification_time = 0; uint32_t m_timescale = 0; uint64_t m_duration = 0; uint32_t m_rate = 0x00010000; // typically 1.0 uint16_t m_volume = 0x0100; // typically, full volume uint32_t m_matrix[9] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000}; uint32_t m_next_track_ID = 0; }; // Track Box class Box_trak : public Box_container { public: Box_trak() : Box_container("trak") {} const char* debug_box_name() const override { return "Track"; } }; // Track Header Box class Box_tkhd : public FullBox { public: Box_tkhd() { set_short_type(fourcc("tkhd")); // set default flags according to ISO 14496-12 set_flags(Track_enabled | Track_in_movie | Track_in_preview); } enum Flags { Track_enabled = 0x01, Track_in_movie = 0x02, Track_in_preview = 0x04, Track_size_is_aspect_ratio = 0x08 }; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Track Header"; } Error write(StreamWriter& writer) const override; void derive_box_version() override; float get_volume() const { return float(m_volume) / float(0x100); } double get_matrix_element(int idx) const; double get_width() const { return float(m_width) / double(0x10000); } double get_height() const { return float(m_height) / double(0x10000); } uint32_t get_track_id() const { return m_track_id; } void set_track_id(uint32_t track_id) { m_track_id = track_id; } void set_resolution(double width, double height) { m_width = (uint32_t) (width * 0x10000); m_height = (uint32_t) (height * 0x10000); } uint64_t get_duration() const { return m_duration; } void set_duration(uint64_t duration) { m_duration = duration; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint64_t m_creation_time = 0; uint64_t m_modification_time = 0; uint32_t m_track_id = 0; uint64_t m_duration = 0; uint16_t m_layer = 0; uint16_t m_alternate_group = 0; uint16_t m_volume = 0x0100; // typically, full volume uint32_t m_matrix[9] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000}; uint32_t m_width = 0; uint32_t m_height = 0; }; // Media Box class Box_mdia : public Box_container { public: Box_mdia() : Box_container("mdia") {} const char* debug_box_name() const override { return "Media"; } }; // Media Header Box class Box_mdhd : public FullBox { public: Box_mdhd() { set_short_type(fourcc("mdhd")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Media Header"; } Error write(StreamWriter& writer) const override; void derive_box_version() override; double get_matrix_element(int idx) const; uint32_t get_timescale() const { return m_timescale; } void set_timescale(uint32_t timescale) { m_timescale = timescale; } uint64_t get_duration() const { return m_duration; } void set_duration(uint64_t duration) { m_duration = duration; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint64_t m_creation_time = 0; uint64_t m_modification_time = 0; uint32_t m_timescale = 0; uint64_t m_duration = 0; char m_language[4] = {'u', 'n', 'k', 0}; }; // Media Information Box (container) class Box_minf : public Box_container { public: Box_minf() : Box_container("minf") {} const char* debug_box_name() const override { return "Media Information"; } }; // Video Media Header class Box_vmhd : public FullBox { public: Box_vmhd() { set_short_type(fourcc("vmhd")); set_flags(1); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Video Media Header"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint16_t m_graphics_mode = 0; uint16_t m_op_color[3] = {0, 0, 0}; }; // Null Media Header class Box_nmhd : public FullBox { public: Box_nmhd() { set_short_type(fourcc("nmhd")); set_flags(1); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Null Media Header"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; }; // Sample Table Box (container) class Box_stbl : public Box_container { public: Box_stbl() : Box_container("stbl") {} const char* debug_box_name() const override { return "Sample Table"; } }; // Sample Description Box class Box_stsd : public FullBox { public: Box_stsd() { set_short_type(fourcc("stsd")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Description"; } Error write(StreamWriter& writer) const override; std::shared_ptr get_sample_entry(size_t idx) const { if (idx >= m_sample_entries.size()) { return nullptr; } else { return m_sample_entries[idx]; } } void add_sample_entry(std::shared_ptr entry) { m_sample_entries.push_back(entry); } size_t get_num_sample_entries() const { return m_sample_entries.size(); } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: std::vector> m_sample_entries; }; // Decoding Time to Sample Box class Box_stts : public FullBox { public: Box_stts() { set_short_type(fourcc("stts")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Decoding Time to Sample"; } Error write(StreamWriter& writer) const override; struct TimeToSample { uint32_t sample_count; uint32_t sample_delta; }; uint32_t get_sample_duration(uint32_t sample_idx); void append_sample_duration(uint32_t duration); uint64_t get_total_duration(bool include_last_frame_duration); protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: std::vector m_entries; MemoryHandle m_memory_handle; }; // Sample to Chunk Box class Box_stsc : public FullBox { public: Box_stsc() { set_short_type(fourcc("stsc")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample to Chunk"; } Error write(StreamWriter& writer) const override; struct SampleToChunk { uint32_t first_chunk; uint32_t samples_per_chunk; uint32_t sample_description_index; }; const std::vector& get_chunks() const { return m_entries; } // idx counting starts at 1 const SampleToChunk* get_chunk(uint32_t idx) const; void add_chunk(uint32_t description_index); void increase_samples_in_chunk(uint32_t nFrames); bool last_chunk_empty() const { assert(!m_entries.empty()); return m_entries.back().samples_per_chunk == 0; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: std::vector m_entries; MemoryHandle m_memory_handle; }; // Chunk Offset Box class Box_stco : public FullBox { public: Box_stco() { set_short_type(fourcc("stco")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Offset"; } Error write(StreamWriter& writer) const override; void add_chunk_offset(uint32_t offset) { m_offsets.push_back(offset); } const std::vector& get_offsets() const { return m_offsets; } void patch_file_pointers(StreamWriter&, size_t offset) override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: std::vector m_offsets; MemoryHandle m_memory_handle; mutable size_t m_offset_start_pos = 0; }; // Sample Size Box class Box_stsz : public FullBox { public: Box_stsz() { set_short_type(fourcc("stsz")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Size"; } Error write(StreamWriter& writer) const override; bool has_fixed_sample_size() const { return m_fixed_sample_size != 0; } uint32_t get_fixed_sample_size() const { return m_fixed_sample_size; } const std::vector& get_sample_sizes() const { return m_sample_sizes; } void append_sample_size(uint32_t size); protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_fixed_sample_size = 0; uint32_t m_sample_count = 0; std::vector m_sample_sizes; MemoryHandle m_memory_handle; }; // Sync Sample Box class Box_stss : public FullBox { public: Box_stss() { set_short_type(fourcc("stss")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sync Sample"; } Error write(StreamWriter& writer) const override; void add_sync_sample(uint32_t sample_idx) { m_sync_samples.push_back(sample_idx); } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: std::vector m_sync_samples; MemoryHandle m_memory_handle; }; struct CodingConstraints { bool all_ref_pics_intra = false; bool intra_pred_used = false; uint8_t max_ref_per_pic = 0; // 4 bit }; // Coding Constraints Box class Box_ccst : public FullBox { public: Box_ccst() { set_short_type(fourcc("ccst")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Coding Constraints"; } Error write(StreamWriter& writer) const override; void set_coding_constraints(const CodingConstraints& c) { m_codingConstraints = c; } const CodingConstraints& get_coding_constraints() const { return m_codingConstraints; } protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: CodingConstraints m_codingConstraints; }; struct VisualSampleEntry { // from SampleEntry //const unsigned int(8)[6] reserved = 0; uint16_t data_reference_index; // VisualSampleEntry uint16_t pre_defined = 0; //uint16_t reserved = 0; uint32_t pre_defined2[3] = {0, 0, 0}; uint16_t width = 0; uint16_t height = 0; uint32_t horizresolution = 0x00480000; // 72 dpi uint32_t vertresolution = 0x00480000; // 72 dpi //uint32_t reserved = 0; uint16_t frame_count = 1; std::string compressorname; // max 32 characters uint16_t depth = 0x0018; int16_t pre_defined3 = -1; // other boxes from derived specifications //std::shared_ptr clap; // optional //std::shared_ptr pixi; // optional double get_horizontal_resolution() const { return horizresolution / double(0x10000); } double get_vertical_resolution() const { return vertresolution / double(0x10000); } Error parse(BitstreamRange& range, const heif_security_limits*); Error write(StreamWriter& writer) const; std::string dump(Indent&) const; }; class Box_VisualSampleEntry : public Box { public: Error write(StreamWriter& writer) const override; std::string dump(Indent&) const override; const VisualSampleEntry& get_VisualSampleEntry_const() const { return m_visualSampleEntry; } VisualSampleEntry& get_VisualSampleEntry() { return m_visualSampleEntry; } protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: VisualSampleEntry m_visualSampleEntry; }; class Box_URIMetaSampleEntry : public Box { public: Box_URIMetaSampleEntry() { set_short_type(fourcc("urim")); } Error write(StreamWriter& writer) const override; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "URI Meta Sample Entry"; } protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: // from SampleEntry //const unsigned int(8)[6] reserved = 0; uint16_t data_reference_index; }; class Box_uri : public FullBox { public: Box_uri() { set_short_type(fourcc("uri ")); } void set_uri(std::string uri) { m_uri = uri; } std::string get_uri() const { return m_uri; } Error write(StreamWriter& writer) const override; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "URI"; } protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; private: std::string m_uri; }; // Sample to Group class Box_sbgp : public FullBox { public: Box_sbgp() { set_short_type(fourcc("sbgp")); } void derive_box_version() override; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample to Group"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_grouping_type = 0; // 4cc std::optional m_grouping_type_parameter; struct Entry { uint32_t sample_count; uint32_t group_description_index; }; std::vector m_entries; MemoryHandle m_memory_handle; }; class SampleGroupEntry { public: virtual ~SampleGroupEntry() = default; virtual std::string dump() const = 0; virtual Error write(StreamWriter& writer) const = 0; virtual Error parse(BitstreamRange& range, const heif_security_limits*) = 0; }; class SampleGroupEntry_refs : public SampleGroupEntry { public: std::string dump() const override; Error write(StreamWriter& writer) const override; Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_sample_id; std::vector m_direct_reference_sample_id; }; // Sample Group Description class Box_sgpd : public FullBox { public: Box_sgpd() { set_short_type(fourcc("sgpd")); } void derive_box_version() override; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Group Description"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_grouping_type = 0; // 4cc std::optional m_default_length; // version 1 (0 -> variable length) std::optional m_default_sample_description_index;; // version >= 2 struct Entry { uint32_t description_length = 0; // if version==1 && m_default_length == 0 std::shared_ptr sample_group_entry; }; std::vector m_entries; }; // Bitrate class Box_btrt : public FullBox { public: Box_btrt() { set_short_type(fourcc("btrt")); } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Bitrate"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_bufferSizeDB; uint32_t m_maxBitrate; uint32_t m_avgBitrate; }; class Box_saiz : public FullBox { public: Box_saiz() { set_short_type(fourcc("saiz")); } void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0); uint32_t get_aux_info_type() const { return m_aux_info_type; } uint32_t get_aux_info_type_parameter() const { return m_aux_info_type_parameter; } void add_sample_size(uint8_t s); void add_nonpresent_sample() { add_sample_size(0); } uint8_t get_sample_size(uint32_t idx); uint32_t get_num_samples() const { return m_num_samples; } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Auxiliary Information Sizes"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; private: uint32_t m_aux_info_type = 0; uint32_t m_aux_info_type_parameter = 0; uint8_t m_default_sample_info_size = 0; // 0 -> variable length uint32_t m_num_samples = 0; // needed in case we are using the default sample size std::vector m_sample_sizes; MemoryHandle m_memory_handle; }; class Box_saio : public FullBox { public: Box_saio() { set_short_type(fourcc("saio")); } void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0); uint32_t get_aux_info_type() const { return m_aux_info_type; } uint32_t get_aux_info_type_parameter() const { return m_aux_info_type_parameter; } void add_sample_offset(uint64_t offset); // This will be 1 if all infos are written contiguously size_t get_num_samples() const { return m_sample_offset.size(); } uint64_t get_sample_offset(uint32_t idx) const; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Sample Auxiliary Information Offsets"; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; void patch_file_pointers(StreamWriter& writer, size_t offset) override; private: uint32_t m_aux_info_type = 0; uint32_t m_aux_info_type_parameter = 0; bool m_need_64bit = false; mutable uint64_t m_offset_start_pos; // If sample_offset==1, all samples are stored contiguous in the file std::vector m_sample_offset; MemoryHandle m_memory_handle; }; class Box_tref : public Box { public: Box_tref() { set_short_type(fourcc("tref")); } struct Reference { uint32_t reference_type; std::vector to_track_id; }; std::string dump(Indent&) const override; const char* debug_box_name() const override { return "Track Reference"; } std::vector get_references(uint32_t ref_type) const; size_t get_number_of_references_of_type(uint32_t ref_type) const; size_t get_number_of_reference_types() const { return m_references.size(); } std::vector get_reference_types() const; void add_references(uint32_t to_track_id, uint32_t type); protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; Error write(StreamWriter& writer) const override; Error check_for_double_references() const; private: std::vector m_references; }; #endif //SEQ_BOXES_H libheif-1.20.2/libheif/sequences/seq_boxes.cc000664 001750 001750 00000131452 15044356510 022203 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 "sequences/seq_boxes.h" #include #include #include #include Error Box_container::parse(BitstreamRange& range, const heif_security_limits* limits) { return read_children(range, READ_CHILDREN_ALL, limits); } std::string Box_container::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); sstr << dump_children(indent); return sstr.str(); } double Box_mvhd::get_matrix_element(int idx) const { if (idx == 8) { return 1.0; } return m_matrix[idx] / double(0x10000); } Error Box_mvhd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 1) { return unsupported_version_error("mvhd"); } if (get_version() == 1) { m_creation_time = range.read64(); m_modification_time = range.read64(); m_timescale = range.read32(); m_duration = range.read64(); } else { // version==0 m_creation_time = range.read32(); m_modification_time = range.read32(); m_timescale = range.read32(); m_duration = range.read32(); } m_rate = range.read32(); m_volume = range.read16(); range.skip(2); range.skip(8); for (uint32_t& m : m_matrix) { m = range.read32(); } for (int i = 0; i < 6; i++) { range.skip(4); } m_next_track_ID = range.read32(); return range.get_error(); } std::string Box_mvhd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "creation time: " << m_creation_time << "\n" << indent << "modification time: " << m_modification_time << "\n" << indent << "timescale: " << m_timescale << "\n" << indent << "duration: " << m_duration << "\n"; sstr << indent << "rate: " << get_rate() << "\n" << indent << "volume: " << get_volume() << "\n" << indent << "matrix:\n"; for (int y = 0; y < 3; y++) { sstr << indent << " "; for (int i = 0; i < 3; i++) { sstr << get_matrix_element(i + 3 * y) << " "; } sstr << "\n"; } sstr << indent << "next_track_ID: " << m_next_track_ID << "\n"; return sstr.str(); } void Box_mvhd::derive_box_version() { if (m_creation_time > 0xFFFFFFFF || m_modification_time > 0xFFFFFFFF || m_timescale > 0xFFFFFFFF || m_duration > 0xFFFFFFFF) { set_version(1); } else { set_version(0); } } Error Box_mvhd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (get_version() == 1) { writer.write64(m_creation_time); writer.write64(m_modification_time); writer.write32(m_timescale); writer.write64(m_duration); } else { // version==0 writer.write32(static_cast(m_creation_time)); writer.write32(static_cast(m_modification_time)); writer.write32(static_cast(m_timescale)); writer.write32(static_cast(m_duration)); } writer.write32(m_rate); writer.write16(m_volume); writer.write16(0); writer.write64(0); for (uint32_t m : m_matrix) { writer.write32(m); } for (int i = 0; i < 6; i++) { writer.write32(0); } writer.write32(m_next_track_ID); prepend_header(writer, box_start); return Error::Ok; } double Box_tkhd::get_matrix_element(int idx) const { if (idx == 8) { return 1.0; } return m_matrix[idx] / double(0x10000); } Error Box_tkhd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 1) { return unsupported_version_error("tkhd"); } if (get_version() == 1) { m_creation_time = range.read64(); m_modification_time = range.read64(); m_track_id = range.read32(); range.skip(4); m_duration = range.read64(); } else { // version==0 m_creation_time = range.read32(); m_modification_time = range.read32(); m_track_id = range.read32(); range.skip(4); m_duration = range.read32(); } range.skip(8); m_layer = range.read16(); m_alternate_group = range.read16(); m_volume = range.read16(); range.skip(2); for (uint32_t& m : m_matrix) { m = range.read32(); } m_width = range.read32(); m_height = range.read32(); return range.get_error(); } std::string Box_tkhd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "track enabled: " << ((get_flags() & Track_enabled) ? "yes" : "no") << "\n" << indent << "track in movie: " << ((get_flags() & Track_in_movie) ? "yes" : "no") << "\n" << indent << "track in preview: " << ((get_flags() & Track_in_preview) ? "yes" : "no") << "\n" << indent << "track size is aspect ratio: " << ((get_flags() & Track_size_is_aspect_ratio) ? "yes" : "no") << "\n"; sstr << indent << "creation time: " << m_creation_time << "\n" << indent << "modification time: " << m_modification_time << "\n" << indent << "track ID: " << m_track_id << "\n" << indent << "duration: " << m_duration << "\n"; sstr << indent << "layer: " << m_layer << "\n" << indent << "alternate_group: " << m_alternate_group << "\n" << indent << "volume: " << get_volume() << "\n" << indent << "matrix:\n"; for (int y = 0; y < 3; y++) { sstr << indent << " "; for (int i = 0; i < 3; i++) { sstr << get_matrix_element(i + 3 * y) << " "; } sstr << "\n"; } sstr << indent << "width: " << get_width() << "\n" << indent << "height: " << get_height() << "\n"; return sstr.str(); } void Box_tkhd::derive_box_version() { if (m_creation_time > 0xFFFFFFFF || m_modification_time > 0xFFFFFFFF || m_duration > 0xFFFFFFFF) { set_version(1); } else { set_version(0); } } Error Box_tkhd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (get_version() == 1) { writer.write64(m_creation_time); writer.write64(m_modification_time); writer.write32(m_track_id); writer.write32(0); writer.write64(m_duration); } else { // version==0 writer.write32(static_cast(m_creation_time)); writer.write32(static_cast(m_modification_time)); writer.write32(m_track_id); writer.write32(0); writer.write32(static_cast(m_duration)); } writer.write64(0); writer.write16(m_layer); writer.write16(m_alternate_group); writer.write16(m_volume); writer.write16(0); for (uint32_t m : m_matrix) { writer.write32(m); } writer.write32(m_width); writer.write32(m_height); prepend_header(writer, box_start); return Error::Ok; } Error Box_mdhd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 1) { return unsupported_version_error("mdhd"); } if (get_version() == 1) { m_creation_time = range.read64(); m_modification_time = range.read64(); m_timescale = range.read32(); m_duration = range.read64(); } else { // version==0 m_creation_time = range.read32(); m_modification_time = range.read32(); m_timescale = range.read32(); m_duration = range.read32(); } uint16_t language_packed = range.read16(); m_language[0] = ((language_packed >> 10) & 0x1F) + 0x60; m_language[1] = ((language_packed >> 5) & 0x1F) + 0x60; m_language[2] = ((language_packed >> 0) & 0x1F) + 0x60; m_language[3] = 0; range.skip(2); return range.get_error(); } std::string Box_mdhd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "creation time: " << m_creation_time << "\n" << indent << "modification time: " << m_modification_time << "\n" << indent << "timescale: " << m_timescale << "\n" << indent << "duration: " << m_duration << "\n"; sstr << indent << "language: " << m_language << "\n"; return sstr.str(); } void Box_mdhd::derive_box_version() { if (m_creation_time > 0xFFFFFFFF || m_modification_time > 0xFFFFFFFF || m_duration > 0xFFFFFFFF) { set_version(1); } else { set_version(0); } } Error Box_mdhd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (get_version() == 1) { writer.write64(m_creation_time); writer.write64(m_modification_time); writer.write32(m_timescale); writer.write64(m_duration); } else { // version==0 writer.write32(static_cast(m_creation_time)); writer.write32(static_cast(m_modification_time)); writer.write32(m_timescale); writer.write32(static_cast(m_duration)); } auto language_packed = static_cast((((m_language[0] - 0x60) & 0x1F) << 10) | (((m_language[1] - 0x60) & 0x1F) << 5) | (((m_language[2] - 0x60) & 0x1F) << 0)); writer.write16(language_packed); writer.write16(0); prepend_header(writer, box_start); return Error::Ok; } Error Box_vmhd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("vmhd"); } m_graphics_mode = range.read16(); for (uint16_t& c : m_op_color) { c = range.read16(); } return range.get_error(); } std::string Box_vmhd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "graphics mode: " << m_graphics_mode; if (m_graphics_mode == 0) { sstr << " (copy)"; } sstr << "\n" << indent << "op color: " << m_op_color[0] << "; " << m_op_color[1] << "; " << m_op_color[2] << "\n"; return sstr.str(); } Error Box_vmhd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write16(m_graphics_mode); for (uint16_t c : m_op_color) { writer.write16(c); } prepend_header(writer, box_start); return Error::Ok; } Error Box_nmhd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("nmhd"); } return range.get_error(); } std::string Box_nmhd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); return sstr.str(); } Error Box_nmhd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); prepend_header(writer, box_start); return Error::Ok; } Error Box_stsd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stsd"); } uint32_t entry_count = range.read32(); if (limits->max_sample_description_box_entries && entry_count > limits->max_sample_description_box_entries) { std::stringstream sstr; sstr << "Allocating " << static_cast(entry_count) << " sample description items exceeds the security limit of " << limits->max_sample_description_box_entries << " items"; return {heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded, sstr.str()}; } for (uint32_t i = 0; i < entry_count; i++) { std::shared_ptr entrybox; Error err = Box::read(range, &entrybox, limits); if (err) { return err; } #if 0 auto visualSampleEntry_box = std::dynamic_pointer_cast(entrybox); if (!visualSampleEntry_box) { return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid or unknown VisualSampleEntry in stsd box."}; } #endif m_sample_entries.push_back(entrybox); } return range.get_error(); } std::string Box_stsd::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); for (size_t i = 0; i < m_sample_entries.size(); i++) { sstr << indent << "[" << i << "]\n"; indent++; sstr << m_sample_entries[i]->dump(indent); indent--; } return sstr.str(); } Error Box_stsd::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(static_cast(m_sample_entries.size())); for (const auto& sample : m_sample_entries) { sample->write(writer); } prepend_header(writer, box_start); return Error::Ok; } Error Box_stts::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stts"); } uint32_t entry_count = range.read32(); if (auto err = m_memory_handle.alloc(entry_count * sizeof(TimeToSample), limits, "the 'stts' table")) { return err; } m_entries.resize(entry_count); for (uint32_t i = 0; i < entry_count; i++) { TimeToSample entry{}; entry.sample_count = range.read32(); entry.sample_delta = range.read32(); m_entries[i] = entry; } return range.get_error(); } std::string Box_stts::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); for (size_t i = 0; i < m_entries.size(); i++) { sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", delta=" << m_entries[i].sample_delta << "\n"; } return sstr.str(); } Error Box_stts::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(static_cast(m_entries.size())); for (const auto& sample : m_entries) { writer.write32(sample.sample_count); writer.write32(sample.sample_delta); } prepend_header(writer, box_start); return Error::Ok; } uint32_t Box_stts::get_sample_duration(uint32_t sample_idx) { size_t i = 0; while (i < m_entries.size()) { if (sample_idx < m_entries[i].sample_count) { return m_entries[i].sample_delta; } else { sample_idx -= m_entries[i].sample_count; } } return 0; } void Box_stts::append_sample_duration(uint32_t duration) { if (m_entries.empty() || m_entries.back().sample_delta != duration) { TimeToSample entry{}; entry.sample_delta = duration; entry.sample_count = 1; m_entries.push_back(entry); return; } m_entries.back().sample_count++; } uint64_t Box_stts::get_total_duration(bool include_last_frame_duration) { uint64_t total = 0; for (const auto& entry : m_entries) { total += entry.sample_count * uint64_t(entry.sample_delta); } if (!include_last_frame_duration && !m_entries.empty()) { total -= m_entries.back().sample_delta; } return total; } Error Box_stsc::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stsc"); } uint32_t entry_count = range.read32(); if (auto err = m_memory_handle.alloc(entry_count * sizeof(SampleToChunk), limits, "the 'stsc' table")) { return err; } m_entries.resize(entry_count); for (uint32_t i = 0; i < entry_count; i++) { SampleToChunk entry{}; entry.first_chunk = range.read32(); entry.samples_per_chunk = range.read32(); entry.sample_description_index = range.read32(); m_entries[i] = entry; } return range.get_error(); } std::string Box_stsc::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); for (size_t i = 0; i < m_entries.size(); i++) { sstr << indent << "[" << i << "]\n" << indent << " first chunk: " << m_entries[i].first_chunk << "\n" << indent << " samples per chunk: " << m_entries[i].samples_per_chunk << "\n" << indent << " sample description index: " << m_entries[i].sample_description_index << "\n"; } return sstr.str(); } Error Box_stsc::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(static_cast(m_entries.size())); for (const auto& sample : m_entries) { writer.write32(sample.first_chunk); writer.write32(sample.samples_per_chunk); writer.write32(sample.sample_description_index); } prepend_header(writer, box_start); return Error::Ok; } const Box_stsc::SampleToChunk* Box_stsc::get_chunk(uint32_t idx) const { assert(idx>=1); for (size_t i = 0 ; i < m_entries.size();i++) { if (idx >= m_entries[i].first_chunk && (i==m_entries.size()-1 || idx < m_entries[i+1].first_chunk)) { return &m_entries[i]; } } return nullptr; } void Box_stsc::add_chunk(uint32_t description_index) { SampleToChunk entry{}; entry.first_chunk = 1; // TODO entry.samples_per_chunk = 0; entry.sample_description_index = description_index; m_entries.push_back(entry); } void Box_stsc::increase_samples_in_chunk(uint32_t nFrames) { assert(!m_entries.empty()); m_entries.back().samples_per_chunk += nFrames; } Error Box_stco::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stco"); } uint32_t entry_count = range.read32(); // check required memory uint64_t mem_size = entry_count * sizeof(uint32_t); if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stco' table")) { return err; } for (uint32_t i = 0; i < entry_count; i++) { m_offsets.push_back(range.read32()); if (range.error()) { return range.get_error(); } } return range.get_error(); } std::string Box_stco::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); for (size_t i = 0; i < m_offsets.size(); i++) { sstr << indent << "[" << i << "] : 0x" << std::hex << m_offsets[i] << std::dec << "\n"; } return sstr.str(); } Error Box_stco::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(static_cast(m_offsets.size())); m_offset_start_pos = writer.get_position(); for (uint32_t offset : m_offsets) { writer.write32(offset); } prepend_header(writer, box_start); return Error::Ok; } void Box_stco::patch_file_pointers(StreamWriter& writer, size_t offset) { size_t oldPosition = writer.get_position(); writer.set_position(m_offset_start_pos); for (uint32_t chunk_offset : m_offsets) { if (chunk_offset + offset > std::numeric_limits::max()) { writer.write32(0); // TODO: error } else { writer.write32(static_cast(chunk_offset + offset)); } } writer.set_position(oldPosition); } Error Box_stsz::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stsz"); } m_fixed_sample_size = range.read32(); m_sample_count = range.read32(); if (m_fixed_sample_size == 0) { // check required memory uint64_t mem_size = m_sample_count * sizeof(uint32_t); if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stsz' table")) { return err; } for (uint32_t i = 0; i < m_sample_count; i++) { m_sample_sizes.push_back(range.read32()); if (range.error()) { return range.get_error(); } } } return range.get_error(); } std::string Box_stsz::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "sample count: " << m_sample_count << "\n"; if (m_fixed_sample_size == 0) { for (size_t i = 0; i < m_sample_sizes.size(); i++) { sstr << indent << "[" << i << "] : " << m_sample_sizes[i] << "\n"; } } else { sstr << indent << "fixed sample size: " << m_fixed_sample_size << "\n"; } return sstr.str(); } Error Box_stsz::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(m_fixed_sample_size); writer.write32(m_sample_count); if (m_fixed_sample_size == 0) { assert(m_sample_count == m_sample_sizes.size()); for (uint32_t size : m_sample_sizes) { writer.write32(size); } } prepend_header(writer, box_start); return Error::Ok; } void Box_stsz::append_sample_size(uint32_t size) { if (m_sample_count == 0 && size != 0) { m_fixed_sample_size = size; m_sample_count = 1; return; } if (m_fixed_sample_size == size && size != 0) { m_sample_count++; return; } if (m_fixed_sample_size != 0) { for (uint32_t i = 0; i < m_sample_count; i++) { m_sample_sizes.push_back(m_fixed_sample_size); } m_fixed_sample_size = 0; } m_sample_sizes.push_back(size); m_sample_count++; assert(m_sample_count == m_sample_sizes.size()); } Error Box_stss::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("stss"); } uint32_t sample_count = range.read32(); // check required memory uint64_t mem_size = sample_count * sizeof(uint32_t); if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stss' table")) { return err; } for (uint32_t i = 0; i < sample_count; i++) { m_sync_samples.push_back(range.read32()); if (range.error()) { return range.get_error(); } } return range.get_error(); } std::string Box_stss::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); for (size_t i = 0; i < m_sync_samples.size(); i++) { sstr << indent << "[" << i << "] : " << m_sync_samples[i] << "\n"; } return sstr.str(); } Error Box_stss::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(static_cast(m_sync_samples.size())); for (uint32_t sample : m_sync_samples) { writer.write32(sample); } prepend_header(writer, box_start); return Error::Ok; } Error VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits) { (void)limits; range.skip(6); data_reference_index = range.read16(); pre_defined = range.read16(); range.skip(2); for (uint32_t& p : pre_defined2) { p = range.read32(); } width = range.read16(); height = range.read16(); horizresolution = range.read32(); vertresolution = range.read32(); range.skip(4); frame_count = range.read16(); compressorname = range.read_fixed_string(32); depth = range.read16(); pre_defined3 = range.read16s(); // other boxes from derived specifications //std::shared_ptr clap; // optional // TODO //std::shared_ptr pixi; // optional // TODO return Error::Ok; } Error VisualSampleEntry::write(StreamWriter& writer) const { writer.write32(0); writer.write16(0); writer.write16(data_reference_index); writer.write16(pre_defined); writer.write16(0); for (uint32_t p : pre_defined2) { writer.write32(p); } writer.write16(width); writer.write16(height); writer.write32(horizresolution); writer.write32(vertresolution); writer.write32(0); writer.write16(frame_count); writer.write_fixed_string(compressorname, 32); writer.write16(depth); writer.write16(pre_defined3); return Error::Ok; } std::string VisualSampleEntry::dump(Indent& indent) const { std::stringstream sstr; sstr << indent << "data reference index: " << data_reference_index << "\n" << indent << "width: " << width << "\n" << indent << "height: " << height << "\n" << indent << "horiz. resolution: " << get_horizontal_resolution() << "\n" << indent << "vert. resolution: " << get_vertical_resolution() << "\n" << indent << "frame count: " << frame_count << "\n" << indent << "compressorname: " << compressorname << "\n" << indent << "depth: " << depth << "\n"; return sstr.str(); } Error Box_URIMetaSampleEntry::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(0); writer.write16(0); writer.write16(data_reference_index); write_children(writer); prepend_header(writer, box_start); return Error::Ok; } std::string Box_URIMetaSampleEntry::dump(Indent& indent) const { std::stringstream sstr; sstr << Box::dump(indent); sstr << indent << "data reference index: " << data_reference_index << "\n"; sstr << dump_children(indent); return sstr.str(); } Error Box_URIMetaSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits) { range.skip(6); data_reference_index = range.read16(); Error err = read_children(range, READ_CHILDREN_ALL, limits); if (err) { return err; } return Error::Ok; } Error Box_uri::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("uri "); } m_uri = range.read_string(); return range.get_error(); } std::string Box_uri::dump(Indent& indent) const { std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "uri: " << m_uri << "\n"; return sstr.str(); } Error Box_uri::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write(m_uri); prepend_header(writer, box_start); return Error::Ok; } Error Box_ccst::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 0) { return unsupported_version_error("ccst"); } uint32_t bits = range.read32(); auto& constraints = m_codingConstraints; constraints.all_ref_pics_intra = (bits & 0x80000000) != 0; constraints.intra_pred_used = (bits & 0x40000000) != 0; constraints.max_ref_per_pic = (bits >> 26) & 0x0F; return range.get_error(); } std::string Box_ccst::dump(Indent& indent) const { const auto& constraints = m_codingConstraints; std::ostringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "all ref pics intra: " << std::boolalpha < 0xFFFFFFFF) { return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Too many sbgp entries."}; } writer.write32(static_cast(m_entries.size())); for (const auto& entry : m_entries) { writer.write32(entry.sample_count); writer.write32(entry.group_description_index); } prepend_header(writer, box_start); return Error::Ok; } Error Box_sbgp::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_version() > 1) { return unsupported_version_error("sbgp"); } m_grouping_type = range.read32(); if (get_version() == 1) { m_grouping_type_parameter = range.read32(); } uint32_t count = range.read32(); if (auto err = m_memory_handle.alloc(count * sizeof(Entry), limits, "the 'sample to group' table")) { return err; } for (uint32_t i = 0; i < count; i++) { Entry e{}; e.sample_count = range.read32(); e.group_description_index = range.read32(); m_entries.push_back(e); if (range.error()) { return range.get_error(); } } return range.get_error(); } std::string SampleGroupEntry_refs::dump() const { std::stringstream sstr; if (m_sample_id==0) { sstr << "0 (non-ref) refs ="; } else { sstr << m_sample_id << " refs ="; } for (uint32_t ref : m_direct_reference_sample_id) { sstr << ' ' << ref; } return sstr.str(); } Error SampleGroupEntry_refs::write(StreamWriter& writer) const { return {}; } Error SampleGroupEntry_refs::parse(BitstreamRange& range, const heif_security_limits*) { m_sample_id = range.read32(); uint8_t cnt = range.read8(); for (uint8_t i = 0; i < cnt; i++) { m_direct_reference_sample_id.push_back(range.read32()); } return Error::Ok; } void Box_sgpd::derive_box_version() { if (m_default_length) { set_version(1); assert(!m_default_sample_description_index); return; } if (m_default_sample_description_index) { set_version(2); return; } set_version(0); } std::string Box_sgpd::dump(Indent& indent) const { std::stringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n"; if (m_default_length) { sstr << indent << "default_length: " << *m_default_length << "\n"; } if (m_default_sample_description_index) { sstr << indent << "default_sample_description_index: " << *m_default_sample_description_index << "\n"; } for (size_t i=0; idump() << "\n"; } else { sstr << "empty (description_length=" << m_entries[i].description_length << ")\n"; } } return sstr.str(); } Error Box_sgpd::write(StreamWriter& writer) const { return {}; } Error Box_sgpd::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); m_grouping_type = range.read32(); if (get_version() == 1) { m_default_length = range.read32(); } if (get_version() >= 2) { m_default_sample_description_index = range.read32(); } uint32_t entry_count = range.read32(); if (limits->max_sample_group_description_box_entries && entry_count > limits->max_sample_group_description_box_entries) { std::stringstream sstr; sstr << "Allocating " << static_cast(entry_count) << " sample group description items exceeds the security limit of " << limits->max_sample_group_description_box_entries << " items"; return {heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded, sstr.str()}; } for (uint32_t i = 0; i < entry_count; i++) { Entry entry; if (get_version() == 1) { if (*m_default_length == 0) { entry.description_length = range.read32(); } } switch (m_grouping_type) { case fourcc("refs"): { entry.sample_group_entry = std::make_shared(); Error err = entry.sample_group_entry->parse(range, limits); if (err) { return err; } break; } default: break; } m_entries.emplace_back(std::move(entry)); } return Error::Ok; } std::string Box_btrt::dump(Indent& indent) const { std::stringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "bufferSizeDB: " << m_bufferSizeDB << " bytes\n"; sstr << indent << "max bitrate: " << m_maxBitrate << " bits/sec\n"; sstr << indent << "avg bitrate: " << m_avgBitrate << " bits/sec\n"; return sstr.str(); } Error Box_btrt::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); writer.write32(m_bufferSizeDB); writer.write32(m_maxBitrate); writer.write32(m_avgBitrate); prepend_header(writer, box_start); return Error::Ok; } Error Box_btrt::parse(BitstreamRange& range, const heif_security_limits*) { m_bufferSizeDB = range.read32(); m_maxBitrate = range.read32(); m_avgBitrate = range.read32(); return Error::Ok; } void Box_saiz::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter) { m_aux_info_type = aux_info_type; m_aux_info_type_parameter = aux_info_type_parameter; bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0); set_flags(nonnull ? 1 : 0); } void Box_saiz::add_sample_size(uint8_t s) { // --- it is the first sample -> put into default size (except if it is a zero size = no sample aux info) if (s != 0 && m_num_samples == 0) { m_default_sample_info_size = s; m_num_samples = 1; return; } // --- if it's the default size, just add more to the number of default sizes if (s != 0 && s == m_default_sample_info_size) { m_num_samples++; return; } // --- it is different from the default size -> add the list // first copy samples with the default size into the list if (m_default_sample_info_size != 0) { for (uint32_t i = 0; i < m_num_samples; i++) { m_sample_sizes.push_back(m_default_sample_info_size); } m_default_sample_info_size = 0; } // add the new sample size m_num_samples++; m_sample_sizes.push_back(s); } uint8_t Box_saiz::get_sample_size(uint32_t idx) { if (m_default_sample_info_size != 0) { return m_default_sample_info_size; } if (idx >= m_sample_sizes.size()) { return 0; } return m_sample_sizes[idx]; } std::string Box_saiz::dump(Indent& indent) const { std::stringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "aux_info_type: "; if (m_aux_info_type == 0) { sstr << "0\n"; } else { sstr << fourcc_to_string(m_aux_info_type) << "\n"; } sstr << indent << "aux_info_type_parameter: "; if (m_aux_info_type_parameter == 0) { sstr << "0\n"; } else { sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n"; } sstr << indent << "default sample size: "; if (m_default_sample_info_size == 0) { sstr << "0 (variable)\n"; } else { sstr << ((int)m_default_sample_info_size) << "\n"; } if (m_default_sample_info_size == 0) { for (size_t i = 0; i < m_sample_sizes.size(); i++) { sstr << indent << "[" << i << "] : " << ((int) m_sample_sizes[i]) << "\n"; } } return sstr.str(); } Error Box_saiz::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (get_flags() & 1) { writer.write32(m_aux_info_type); writer.write32(m_aux_info_type_parameter); } writer.write8(m_default_sample_info_size); writer.write32(m_num_samples); if (m_default_sample_info_size == 0) { assert(m_num_samples == m_sample_sizes.size()); for (uint8_t size : m_sample_sizes) { writer.write8(size); } } prepend_header(writer, box_start); return Error::Ok; } Error Box_saiz::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_flags() & 1) { m_aux_info_type = range.read32(); m_aux_info_type_parameter = range.read32(); } m_default_sample_info_size = range.read8(); m_num_samples = range.read32(); if (m_default_sample_info_size == 0) { // check required memory uint64_t mem_size = m_num_samples; if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'sample aux info sizes' (saiz) table")) { return err; } // read whole table at once m_sample_sizes.resize(m_num_samples); range.read(m_sample_sizes.data(), m_num_samples); } return range.get_error(); } void Box_saio::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter) { m_aux_info_type = aux_info_type; m_aux_info_type_parameter = aux_info_type_parameter; bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0); set_flags(nonnull ? 1 : 0); } void Box_saio::add_sample_offset(uint64_t s) { if (s > 0xFFFFFFFF) { m_need_64bit = true; set_version(1); } m_sample_offset.push_back(s); } uint64_t Box_saio::get_sample_offset(uint32_t idx) const { if (idx >= m_sample_offset.size()) { return 0; } else { return m_sample_offset[idx]; } } std::string Box_saio::dump(Indent& indent) const { std::stringstream sstr; sstr << FullBox::dump(indent); sstr << indent << "aux_info_type: "; if (m_aux_info_type == 0) { sstr << "0\n"; } else { sstr << fourcc_to_string(m_aux_info_type) << "\n"; } sstr << indent << "aux_info_type_parameter: "; if (m_aux_info_type_parameter == 0) { sstr << "0\n"; } else { sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n"; } for (size_t i = 0; i < m_sample_offset.size(); i++) { sstr << indent << "[" << i << "] : 0x" << std::hex << m_sample_offset[i] << "\n"; } return sstr.str(); } void Box_saio::patch_file_pointers(StreamWriter& writer, size_t offset) { size_t oldPosition = writer.get_position(); writer.set_position(m_offset_start_pos); for (uint64_t ptr : m_sample_offset) { if (get_version() == 0 && ptr + offset > std::numeric_limits::max()) { writer.write32(0); // TODO: error } else if (get_version() == 0) { writer.write32(static_cast(ptr + offset)); } else { writer.write64(ptr + offset); } } writer.set_position(oldPosition); } Error Box_saio::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); if (get_flags() & 1) { writer.write32(m_aux_info_type); writer.write32(m_aux_info_type_parameter); } if (m_sample_offset.size() > std::numeric_limits::max()) { return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Maximum number of samples exceeded"}; } writer.write32(static_cast(m_sample_offset.size())); m_offset_start_pos = writer.get_position(); for (uint64_t size : m_sample_offset) { if (m_need_64bit) { writer.write64(size); } else { writer.write32(static_cast(size)); } } prepend_header(writer, box_start); return Error::Ok; } Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); if (get_flags() & 1) { m_aux_info_type = range.read32(); m_aux_info_type_parameter = range.read32(); } uint32_t num_samples = range.read32(); // check required memory uint64_t mem_size = num_samples * sizeof(uint64_t); if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'saio' table")) { return err; } m_sample_offset.resize(num_samples); for (uint32_t i = 0; i < num_samples; i++) { uint64_t offset; if (get_version() == 1) { offset = range.read64(); } else { offset = range.read32(); } m_sample_offset[i] = offset; if (range.error()) { return range.get_error(); } } return Error::Ok; } Error Box_tref::parse(BitstreamRange& range, const heif_security_limits* limits) { while (!range.eof()) { BoxHeader header; Error err = header.parse_header(range); if (err != Error::Ok) { return err; } if (header.get_box_size() < header.get_header_size()) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid box size (smaller than header)"}; } uint64_t dataSize = (header.get_box_size() - header.get_header_size()); if (dataSize % 4 != 0 || dataSize < 4) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Input file has a 'tref' TrackReferenceTypeBox with invalid size."}; } uint64_t nRefs = dataSize / 4; if (limits->max_items && nRefs > limits->max_items) { std::stringstream sstr; sstr << "Number of references in tref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references."; return {heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, sstr.str()}; } Reference ref; ref.reference_type = header.get_short_type(); for (uint64_t i = 0; i < nRefs; i++) { if (range.eof()) { std::stringstream sstr; sstr << "tref box should contain " << nRefs << " references, but we can only read " << i << " references."; return {heif_error_Invalid_input, heif_suberror_End_of_data, sstr.str()}; } ref.to_track_id.push_back(static_cast(range.read32())); } m_references.push_back(ref); } // --- check for duplicate references if (auto error = check_for_double_references()) { return error; } return range.get_error(); } Error Box_tref::check_for_double_references() const { for (const auto& ref : m_references) { std::set to_ids; for (const auto to_id : ref.to_track_id) { if (to_ids.find(to_id) == to_ids.end()) { to_ids.insert(to_id); } else { return {heif_error_Invalid_input, heif_suberror_Unspecified, "'tref' has double references"}; } } } return Error::Ok; } Error Box_tref::write(StreamWriter& writer) const { if (auto error = check_for_double_references()) { return error; } size_t box_start = reserve_box_header_space(writer); for (const auto& ref : m_references) { uint32_t box_size = 8 + uint32_t(ref.to_track_id.size() * 4); // we write the BoxHeader ourselves since it is very simple writer.write32(box_size); writer.write32(ref.reference_type); for (uint32_t r : ref.to_track_id) { writer.write32(r); } } prepend_header(writer, box_start); return Error::Ok; } std::string Box_tref::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); for (const auto& ref : m_references) { sstr << indent << "reference with type '" << fourcc_to_string(ref.reference_type) << "'" << " to track IDs: "; for (uint32_t id : ref.to_track_id) { sstr << id << " "; } sstr << "\n"; } return sstr.str(); } std::vector Box_tref::get_references(uint32_t ref_type) const { for (const Reference& ref : m_references) { if (ref.reference_type == ref_type) { return ref.to_track_id; } } return {}; } size_t Box_tref::get_number_of_references_of_type(uint32_t ref_type) const { for (const Reference& ref : m_references) { if (ref.reference_type == ref_type) { return ref.to_track_id.size(); } } return 0; } std::vector Box_tref::get_reference_types() const { std::vector types; types.reserve(m_references.size()); for (const auto& ref : m_references) { types.push_back(ref.reference_type); } return types; } void Box_tref::add_references(uint32_t to_track_id, uint32_t type) { for (auto& ref : m_references) { if (ref.reference_type == type) { ref.to_track_id.push_back(to_track_id); return; } } Reference ref; ref.reference_type = type; ref.to_track_id = {to_track_id}; m_references.push_back(ref); } libheif-1.20.2/libheif/sequences/track_visual.h000664 001750 001750 00000003521 15044356510 022537 0ustar00farindkfarindk000000 000000 /* * HEIF image base codec. * Copyright (c) 2025 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_TRACK_VISUAL_H #define LIBHEIF_TRACK_VISUAL_H #include "track.h" #include #include #include class Track_Visual : public Track { public: //Track(HeifContext* ctx); Track_Visual(HeifContext* ctx, uint32_t track_id, uint16_t width, uint16_t height, const TrackOptions* options, uint32_t handler_type); Track_Visual(HeifContext* ctx, const std::shared_ptr&); // when reading the file ~Track_Visual() override = default; uint16_t get_width() const { return m_width; } uint16_t get_height() const { return m_height; } Result> decode_next_image_sample(const struct heif_decoding_options& options); Error encode_image(std::shared_ptr image, struct heif_encoder* encoder, const struct heif_encoding_options& options, heif_image_input_class image_class); heif_brand2 get_compatible_brand() const; private: uint16_t m_width = 0; uint16_t m_height = 0; }; #endif //LIBHEIF_TRACK_VISUAL_H libheif-1.20.2/libheif/sequences/track.h000664 001750 001750 00000015653 15044356510 021165 0ustar00farindkfarindk000000 000000 /* * HEIF image base 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_TRACK_H #define LIBHEIF_TRACK_H #include "error.h" #include "api_structs.h" #include "libheif/heif_plugin.h" #include "libheif/heif_sequences.h" #include #include #include class HeifContext; class HeifPixelImage; class Chunk; class Box_trak; class SampleAuxInfoHelper { public: SampleAuxInfoHelper(bool interleaved = false); void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0); Error add_sample_info(const std::vector& data); void add_nonpresent_sample(); void write_interleaved(const std::shared_ptr& file); void write_all(const std::shared_ptr& parent, const std::shared_ptr& file); private: std::shared_ptr m_saiz; std::shared_ptr m_saio; std::vector m_data; bool m_interleaved; }; class SampleAuxInfoReader { public: SampleAuxInfoReader(std::shared_ptr, std::shared_ptr); heif_sample_aux_info_type get_type() const; Result> get_sample_info(const HeifFile* file, uint32_t idx); private: std::shared_ptr m_saiz; std::shared_ptr m_saio; bool m_contiguous; std::vector m_contiguous_offsets; }; /** * This structure specifies what will be written in a track and how it will be laid out in the file. */ struct TrackOptions { ~TrackOptions() { heif_tai_clock_info_release(tai_clock_info); } // Timescale (clock ticks per second) for this track. uint32_t track_timescale = 90000; // If 'true', the aux_info data blocks will be interleaved with the compressed image. // This has the advantage that the aux_info is localized near the image data. // // If 'false', all aux_info will be written as one block after the compressed image data. // This has the advantage that no aux_info offsets have to be written. bool write_sample_aux_infos_interleaved = false; // --- TAI timestamps for samples enum heif_sample_aux_info_presence with_sample_tai_timestamps = heif_sample_aux_info_presence_none; struct heif_tai_clock_info* tai_clock_info = nullptr; // --- GIMI content IDs for samples enum heif_sample_aux_info_presence with_sample_content_ids = heif_sample_aux_info_presence_none; // --- GIMI content ID for the track std::string gimi_track_content_id; TrackOptions& operator=(const TrackOptions&); }; class Track : public ErrorBuffer { public: //Track(HeifContext* ctx); Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* info, uint32_t handler_type); Track(HeifContext* ctx, const std::shared_ptr&); // when reading the file virtual ~Track() = default; // Allocate a Track of the correct sub-class (visual or metadata) static std::shared_ptr alloc_track(HeifContext*, const std::shared_ptr&); heif_item_id get_id() const { return m_id; } std::shared_ptr get_file() const; uint32_t get_handler() const { return m_handler_type; } bool is_visual_track() const; uint32_t get_first_cluster_sample_entry_type() const; Result get_first_cluster_urim_uri() const; uint64_t get_duration_in_media_units() const; uint32_t get_timescale() const; // The context will compute the duration in global movie units and set this. void set_track_duration_in_movie_units(uint64_t total_duration); std::shared_ptr get_first_cluster_taic() { return m_first_taic; } bool end_of_sequence_reached() const; // Compute some parameters after all frames have been encoded (for example: track duration). void finalize_track(); const TrackOptions& get_track_info() const { return m_track_info; } void add_reference_to_track(uint32_t referenceType, uint32_t to_track_id); std::shared_ptr get_tref_box() const { return m_tref; } Result get_next_sample_raw_data(); std::vector get_sample_aux_info_types() const; protected: HeifContext* m_heif_context = nullptr; uint32_t m_id = 0; uint32_t m_handler_type = 0; TrackOptions m_track_info; uint32_t m_num_samples = 0; uint32_t m_current_chunk = 0; uint32_t m_next_sample_to_be_processed = 0; std::vector> m_chunks; std::shared_ptr m_moov; std::shared_ptr m_trak; std::shared_ptr m_tkhd; std::shared_ptr m_minf; std::shared_ptr m_mdhd; std::shared_ptr m_hdlr; std::shared_ptr m_stbl; std::shared_ptr m_stsd; std::shared_ptr m_stsc; std::shared_ptr m_stco; std::shared_ptr m_stts; std::shared_ptr m_stss; std::shared_ptr m_stsz; std::shared_ptr m_tref; // optional // --- sample auxiliary information std::unique_ptr m_aux_helper_tai_timestamps; std::unique_ptr m_aux_helper_content_ids; std::unique_ptr m_aux_reader_tai_timestamps; std::unique_ptr m_aux_reader_content_ids; std::shared_ptr m_first_taic; // the TAIC of the first chunk // --- Helper functions for writing samples. // Call when we begin a new chunk of samples, e.g. because the compression format changed void add_chunk(heif_compression_format format); // Call to set the sample_description_box for the last added chunk. // Has to be called when we call add_chunk(). // It is not merged with add_chunk() because the sample_description_box may need information from the // first encoded frame. void set_sample_description_box(std::shared_ptr sample_description_box); // Write the actual sample data. `tai` may be null and `gimi_contentID` may be empty. // In these cases, no timestamp or no contentID will be written, respectively. Error write_sample_data(const std::vector& raw_data, uint32_t sample_duration, bool is_sync_sample, const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID); }; #endif //LIBHEIF_TRACK_H libheif-1.20.2/libheif/codecs/000775 001750 001750 00000000000 15044356511 017144 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/codecs/avif_enc.cc000664 001750 001750 00000005477 15044356510 021241 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_enc.h" #include "avif_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include Result Encoder_AVIF::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { Encoder::CodedImageData codedImage; Box_av1C::configuration config; // Fill preliminary av1C in case we cannot parse the sequence_header() correctly in the code below. // TODO: maybe we can remove this later. fill_av1C_configuration(&config, image); heif_image c_api_image; c_api_image.image = image; struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); if (err.code) { return Error(err.code, err.subcode, err.message); } for (;;) { uint8_t* data; int size; encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); bool found_config = fill_av1C_configuration_from_stream(&config, data, size); (void) found_config; if (data == nullptr) { break; } codedImage.append(data, size); } auto av1C = std::make_shared(); av1C->set_configuration(config); codedImage.properties.push_back(av1C); codedImage.codingConstraints.intra_pred_used = true; codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames return codedImage; } std::shared_ptr Encoder_AVIF::get_sample_description_box(const CodedImageData& data) const { auto av01 = std::make_shared(); av01->get_VisualSampleEntry().compressorname = "AVIF"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("av1C")) { av01->append_child_box(prop); return av01; } } assert(false); // no av1C generated return nullptr; } libheif-1.20.2/libheif/codecs/uncompressed/000775 001750 001750 00000000000 15044356511 021653 5ustar00farindkfarindk000000 000000 libheif-1.20.2/libheif/codecs/uncompressed/decoder_tile_component_interleave.h000664 001750 001750 00000003236 15044356510 030751 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/unc_codec.cc000664 001750 001750 00000112706 15044356510 024112 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}; } UncompressedImageCodec::unci_properties properties; properties.fill_from_image_item(image); auto ispe = properties.ispe; auto uncC = properties.uncC; auto cmpd = properties.cmpd; 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); DataExtent dataExtent; dataExtent.set_from_image_item(file, ID); Error result = decoder->decode_tile(dataExtent, properties, 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; } // TODO: this should be deprecated and replaced with the function taking unci_properties/DataExtent 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}; } UncompressedImageCodec::unci_properties properties; properties.fill_from_image_item(image); auto ispe = properties.ispe; auto uncC = properties.uncC; auto cmpd = properties.cmpd; 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(); DataExtent dataExtent; dataExtent.set_from_image_item(context->get_heif_file(), ID); 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(dataExtent, properties, 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; } void UncompressedImageCodec::unci_properties::fill_from_image_item(const std::shared_ptr& image) { ispe = image->get_property(); cmpd = image->get_property(); uncC = image->get_property(); cmpC = image->get_property(); icef = image->get_property(); } Result> UncompressedImageCodec::decode_uncompressed_image(const UncompressedImageCodec::unci_properties& properties, const DataExtent& extent, const heif_security_limits* securityLimits) { std::shared_ptr img; const std::shared_ptr& ispe = properties.ispe; const std::shared_ptr& cmpd = properties.cmpd; const std::shared_ptr& uncC = properties.uncC; Error 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(securityLimits, width, height); if (error) { return error; } Result> createImgResult = create_image(cmpd, uncC, width, height, securityLimits); 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(extent, properties, 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 img; } 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.20.2/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc000664 001750 001750 00000011560 15044356510 031106 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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(dataExtent, properties, &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.20.2/libheif/codecs/uncompressed/decoder_abstract.cc000664 001750 001750 00000027455 15044356510 025466 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" #include "codecs/decoder.h" #include "codecs/uncompressed/unc_codec.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; } const Error AbstractDecoder::get_compressed_image_data_uncompressed(const DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, std::vector* data, uint64_t range_start_offset, uint64_t range_size, uint32_t tile_idx, const Box_iloc::Item* item) const { // --- get codec configuration std::shared_ptr cmpC_box = properties.cmpC; std::shared_ptr icef_box = properties.icef; if (!cmpC_box) { // assume no generic compression auto readResult = dataExtent.read_data(range_start_offset, range_size); if (readResult.error) { return readResult.error; } data->insert(data->end(), readResult.value.begin(), readResult.value.end()); return Error::Ok; } 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 data needed for one tile Result> readingResult = dataExtent.read_data(unit.unit_offset, unit.unit_size); if (readingResult.error) { return readingResult.error; } const std::vector& compressed_bytes = readingResult.value; // decompress only the unit Error err = do_decompress_data(cmpC_box, compressed_bytes, data); if (err) { return err; } } else if (icef_box) { // get all data and decode all Result*> readResult = dataExtent.read_data(); if (readResult.error) { return readResult.error; } const std::vector& compressed_bytes = *readResult.value; 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; Error 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 Result*> readResult = dataExtent.read_data(); if (readResult.error) { return readResult.error; } std::vector compressed_bytes = *readResult.value; // Decode as a single blob Error 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.20.2/libheif/codecs/uncompressed/unc_boxes.h000664 001750 001750 00000021645 15044356510 024020 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 "sequences/seq_boxes.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; }; class Box_uncv : public Box_VisualSampleEntry { public: Box_uncv() { set_short_type(fourcc("uncv")); } }; #endif //LIBHEIF_UNC_BOXES_H libheif-1.20.2/libheif/codecs/uncompressed/decoder_mixed_interleave.h000664 001750 001750 00000003336 15044356510 027041 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/decoder_row_interleave.h000664 001750 001750 00000003365 15044356510 026544 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/unc_types.h000664 001750 001750 00000013344 15044356510 024041 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.20.2/libheif/codecs/uncompressed/unc_dec.h000664 001750 001750 00000004104 15044356510 023422 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, const std::shared_ptr& ispe) : m_uncC(uncC), m_cmpd(cmpd), m_ispe(ispe) {} 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; Result> decode_single_frame_from_compressed_data(const struct heif_decoding_options& options, const struct heif_security_limits* limits) override; private: const std::shared_ptr m_uncC; const std::shared_ptr m_cmpd; const std::shared_ptr m_ispe; }; #endif libheif-1.20.2/libheif/codecs/uncompressed/decoder_row_interleave.cc000664 001750 001750 00000010052 15044356510 026671 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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(dataExtent, properties, &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.20.2/libheif/codecs/uncompressed/unc_codec.h000664 001750 001750 00000010020 15044356510 023736 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 "libheif/heif_uncompressed.h" #if WITH_UNCOMPRESSED_CODEC #include "unc_boxes.h" #endif #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); struct unci_properties { std::shared_ptr ispe; std::shared_ptr cmpd; std::shared_ptr uncC; std::shared_ptr cmpC; std::shared_ptr icef; // ... void fill_from_image_item(const std::shared_ptr&); }; static Result> decode_uncompressed_image(const unci_properties& properties, const struct DataExtent& extent, const heif_security_limits*); 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.20.2/libheif/codecs/uncompressed/decoder_pixel_interleave.cc000664 001750 001750 00000010666 15044356510 027216 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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(dataExtent, properties, &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.20.2/libheif/codecs/uncompressed/decoder_pixel_interleave.h000664 001750 001750 00000004135 15044356510 027052 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/unc_boxes.cc000664 001750 001750 00000057447 15044356510 024167 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 contain " << 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 contain " << 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.20.2/libheif/codecs/uncompressed/unc_enc.h000664 001750 001750 00000002743 15044356510 023443 0ustar00farindkfarindk000000 000000 /* * HEIF codec. * Copyright (c) 2025 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_ENCODER_UNCOMPRESSED_H #define HEIF_ENCODER_UNCOMPRESSED_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_uncompressed : public Encoder { public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/uncompressed/decoder_component_interleave.h000664 001750 001750 00000003231 15044356510 027727 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/unc_enc.cc000664 001750 001750 00000004462 15044356510 023601 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 "unc_enc.h" #include "unc_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include #include #include Result Encoder_uncompressed::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { Encoder::CodedImageData codedImage; Result codingResult = ImageItem_uncompressed::encode_static(image, options); if (codingResult.error) { return codingResult; } codedImage = std::move(codingResult.value); // codedImage.bitstream = std::move(vec); codedImage.codingConstraints.intra_pred_used = false; codedImage.codingConstraints.all_ref_pics_intra = true; codedImage.codingConstraints.max_ref_per_pic = 0; return {codedImage}; } std::shared_ptr Encoder_uncompressed::get_sample_description_box(const CodedImageData& data) const { auto uncv = std::make_shared(); uncv->get_VisualSampleEntry().compressorname = "iso23001-17"; for (auto prop : data.properties) { switch (prop->get_short_type()) { case fourcc("cmpd"): case fourcc("uncC"): case fourcc("cmpC"): case fourcc("icef"): case fourcc("cpat"): uncv->append_child_box(prop); break; } } return uncv; } libheif-1.20.2/libheif/codecs/uncompressed/decoder_component_interleave.cc000664 001750 001750 00000006744 15044356510 030101 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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(dataExtent, properties, &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.20.2/libheif/codecs/uncompressed/decoder_abstract.h000664 001750 001750 00000015067 15044356510 025324 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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.20.2/libheif/codecs/uncompressed/unc_dec.cc000664 001750 001750 00000012625 15044356510 023567 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; } Result> Decoder_uncompressed::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options, const struct heif_security_limits* limits) { UncompressedImageCodec::unci_properties properties; properties.uncC = m_uncC; properties.cmpd = m_cmpd; properties.ispe = m_ispe; auto decodeResult = UncompressedImageCodec::decode_uncompressed_image(properties, get_data_extent(), limits); if (decodeResult.error) { return decodeResult.error; } else { return decodeResult.value; } } libheif-1.20.2/libheif/codecs/uncompressed/decoder_mixed_interleave.cc000664 001750 001750 00000012007 15044356510 027172 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 DataExtent& dataExtent, const UncompressedImageCodec::unci_properties& properties, 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(dataExtent, properties, &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.20.2/libheif/codecs/jpeg_dec.cc000664 001750 001750 00000010172 15044356510 021213 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.20.2/libheif/codecs/jpeg2000_enc.h000664 001750 001750 00000003205 15044356510 021370 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_ENCODER_JPEG2000_H #define HEIF_ENCODER_JPEG2000_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_JPEG2000 : public Encoder { public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; class Encoder_HTJ2K : public Encoder_JPEG2000 { public: std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/hevc_enc.h000664 001750 001750 00000002713 15044356510 021071 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_ENCODER_HEVC_H #define HEIF_ENCODER_HEVC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_HEVC : public Encoder { public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/decoder.h000664 001750 001750 00000005757 15044356510 020737 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, FileRange } m_source = Source::Raw; // --- raw data mutable std::vector m_raw; // also for cached data // --- image heif_item_id m_item_id = 0; // --- file range uint64_t m_offset = 0; uint32_t m_size = 0; void set_from_image_item(std::shared_ptr file, heif_item_id item); void set_file_range(std::shared_ptr file, uint64_t offset, uint32_t size); 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); static std::shared_ptr alloc_for_sequence_sample_description_box(std::shared_ptr sample_description_box); virtual ~Decoder() = default; virtual heif_compression_format get_compression_format() const = 0; void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); } const DataExtent& get_data_extent() const { return m_data_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, const struct heif_security_limits* limits); private: DataExtent m_data_extent; }; #endif libheif-1.20.2/libheif/codecs/vvc_boxes.h000664 001750 001750 00000006465 15044356510 021325 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 #include "sequences/seq_boxes.h" 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; }; class Box_vvc1 : public Box_VisualSampleEntry { public: Box_vvc1() { set_short_type(fourcc("vvc1")); } }; 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.20.2/libheif/codecs/vvc_dec.h000664 001750 001750 00000002773 15044356510 020736 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.20.2/libheif/codecs/jpeg_dec.h000664 001750 001750 00000003472 15044356510 021062 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.20.2/libheif/codecs/avif_enc.h000664 001750 001750 00000002713 15044356510 021071 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_ENCODER_AVIF_H #define HEIF_ENCODER_AVIF_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_AVIF : public Encoder { public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/jpeg_enc.h000664 001750 001750 00000003026 15044356510 021067 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_ENCODER_JPEG_H #define HEIF_ENCODER_JPEG_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_JPEG : public Encoder { public: const heif_color_profile_nclx* get_forced_output_nclx() const override; Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/vvc_enc.cc000664 001750 001750 00000006073 15044356510 021103 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_enc.h" #include "vvc_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include Result Encoder_VVC::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { Encoder::CodedImageData codedImage; auto vvcC = std::make_shared(); codedImage.properties.push_back(vvcC); heif_image c_api_image; c_api_image.image = image; struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); if (err.code) { return Error(err.code, err.subcode, err.message); } int encoded_width = 0; int encoded_height = 0; for (;;) { uint8_t* data; int size; encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, NULL); if (data == NULL) { break; } const uint8_t NAL_SPS = 15; uint8_t nal_type = 0; if (size>=2) { nal_type = (data[1] >> 3) & 0x1F; } if (nal_type == NAL_SPS) { Box_vvcC::configuration config; parse_sps_for_vvcC_configuration(data, size, &config, &encoded_width, &encoded_height); vvcC->set_configuration(config); } switch (nal_type) { case 14: // VPS case 15: // SPS case 16: // PPS vvcC->append_nal_data(data, size); break; default: codedImage.append_with_4bytes_size(data, size); } } codedImage.codingConstraints.intra_pred_used = true; codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames codedImage.codingConstraints.max_ref_per_pic = 0; return codedImage; } std::shared_ptr Encoder_VVC::get_sample_description_box(const CodedImageData& data) const { auto vvc1 = std::make_shared(); vvc1->get_VisualSampleEntry().compressorname = "VVC"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("vvcC")) { vvc1->append_child_box(prop); return vvc1; } } assert(false); // no hvcC generated return nullptr; } libheif-1.20.2/libheif/codecs/avc_boxes.h000664 001750 001750 00000005204 15044356510 021266 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" #include "sequences/seq_boxes.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; }; class Box_avc1 : public Box_VisualSampleEntry { public: Box_avc1() { set_short_type(fourcc("avc1")); } }; #endif libheif-1.20.2/libheif/codecs/vvc_dec.cc000664 001750 001750 00000003556 15044356510 021074 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.20.2/libheif/codecs/decoder.cc000664 001750 001750 00000021251 15044356510 021060 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 "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" #if WITH_UNCOMPRESSED_CODEC #include "codecs/uncompressed/unc_dec.h" #include "codecs/uncompressed/unc_boxes.h" #endif 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; } void DataExtent::set_file_range(std::shared_ptr file, uint64_t offset, uint32_t size) { m_file = std::move(file); m_source = Source::FileRange; m_offset = offset; m_size = size; } 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 { // file range Error err = m_file->append_data_from_file_range(m_raw, m_offset, m_size); if (err) { return err; } } 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, data, offset, size); if (err) { return err; } return data; } else { // file range Error err = m_file->append_data_from_file_range(data, m_offset, m_size); if (err) { return err; } return data; } } 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(); auto ispe = item->get_property(); return std::make_shared(uncC,cmpd,ispe); } #endif case fourcc("mski"): { return nullptr; // do we need a decoder for this? } default: return nullptr; } } std::shared_ptr Decoder::alloc_for_sequence_sample_description_box(std::shared_ptr sample_description_box) { std::string compressor = sample_description_box->get_VisualSampleEntry_const().compressorname; uint32_t sampleType = sample_description_box->get_short_type(); switch (sampleType) { case fourcc("hvc1"): { auto hvcC = sample_description_box->get_child_box(); return std::make_shared(hvcC); } case fourcc("av01"): { auto av1C = sample_description_box->get_child_box(); return std::make_shared(av1C); } case fourcc("vvc1"): { auto vvcC = sample_description_box->get_child_box(); return std::make_shared(vvcC); } case fourcc("avc1"): { auto avcC = sample_description_box->get_child_box(); return std::make_shared(avcC); } #if WITH_UNCOMPRESSED_CODEC case fourcc("uncv"): { auto uncC = sample_description_box->get_child_box(); auto cmpd = sample_description_box->get_child_box(); auto ispe = std::make_shared(); ispe->set_size(sample_description_box->get_VisualSampleEntry_const().width, sample_description_box->get_VisualSampleEntry_const().height); return std::make_shared(uncC, cmpd, ispe); } #endif case fourcc("j2ki"): { auto j2kH = sample_description_box->get_child_box(); return std::make_shared(j2kH); } case fourcc("mjpg"): { auto jpgC = sample_description_box->get_child_box(); return std::make_shared(jpgC); } 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_security_limits* limits) { 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 plugin."); } 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; if (decoder_plugin->plugin_api_version >= 4 && decoder_plugin->decode_next_image != nullptr) { err = decoder_plugin->decode_next_image(decoder, &decoded_img, limits); if (err.code != heif_error_Ok) { return Error::from_heif_error(err); } } else { err = decoder_plugin->decode_image(decoder, &decoded_img); if (err.code != heif_error_Ok) { return Error::from_heif_error(err); } } 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.20.2/libheif/codecs/encoder.cc000664 001750 001750 00000012641 15044356510 021075 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/encoder.h" #include "error.h" #include "context.h" #include "plugin_registry.h" #include "api_structs.h" #include "color-conversion/colorconversion.h" void Encoder::CodedImageData::append(const uint8_t* data, size_t size) { bitstream.insert(bitstream.end(), data, data + size); } void Encoder::CodedImageData::append_with_4bytes_size(const uint8_t* data, size_t size) { assert(size <= 0xFFFFFFFF); uint8_t size_field[4]; size_field[0] = (uint8_t) ((size >> 24) & 0xFF); size_field[1] = (uint8_t) ((size >> 16) & 0xFF); size_field[2] = (uint8_t) ((size >> 8) & 0xFF); size_field[3] = (uint8_t) ((size >> 0) & 0xFF); bitstream.insert(bitstream.end(), size_field, size_field + 4); bitstream.insert(bitstream.end(), data, data + size); } // TODO: remove me, moved to encoder.cc static std::shared_ptr compute_target_nclx_profile(const std::shared_ptr& image, const heif_color_profile_nclx* output_nclx_profile) { auto target_nclx_profile = std::make_shared(); // If there is an output NCLX specified, use that. if (output_nclx_profile) { target_nclx_profile->set_from_heif_color_profile_nclx(output_nclx_profile); } // Otherwise, if there is an input NCLX, keep that. else if (auto input_nclx = image->get_color_profile_nclx()) { *target_nclx_profile = *input_nclx; } // Otherwise, just use the defaults (set below) else { target_nclx_profile->set_undefined(); } target_nclx_profile->replace_undefined_values_with_sRGB_defaults(); return target_nclx_profile; } // TODO: remove me, moved to encoder.cc static bool nclx_profile_matches_spec(heif_colorspace colorspace, std::shared_ptr image_nclx, const struct heif_color_profile_nclx* spec_nclx) { if (colorspace != heif_colorspace_YCbCr) { return true; } // No target specification -> always matches if (!spec_nclx) { return true; } if (!image_nclx) { // if no input nclx is specified, compare against default one image_nclx = std::make_shared(); } if (image_nclx->get_full_range_flag() != (spec_nclx->full_range_flag == 0 ? false : true)) { return false; } if (image_nclx->get_matrix_coefficients() != spec_nclx->matrix_coefficients) { return false; } // TODO: are the colour primaries relevant for matrix-coefficients != 12,13 ? // If not, we should skip this test for anything else than matrix-coefficients != 12,13. if (image_nclx->get_colour_primaries() != spec_nclx->color_primaries) { return false; } return true; } extern void fill_default_color_conversion_options_ext(heif_color_conversion_options_ext& options); Result> Encoder::convert_colorspace_for_encoding(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, const heif_security_limits* security_limits) { const heif_color_profile_nclx* output_nclx_profile; if (const auto* nclx = get_forced_output_nclx()) { output_nclx_profile = nclx; } else { output_nclx_profile = options.output_nclx_profile; } heif_colorspace colorspace = image->get_colorspace(); heif_chroma chroma = image->get_chroma_format(); if (encoder->plugin->plugin_api_version >= 2) { encoder->plugin->query_input_colorspace2(encoder->encoder, &colorspace, &chroma); } else { encoder->plugin->query_input_colorspace(&colorspace, &chroma); } // If output format forces an NCLX, use that. Otherwise use user selected NCLX. std::shared_ptr target_nclx_profile = compute_target_nclx_profile(image, output_nclx_profile); // --- convert colorspace std::shared_ptr output_image; if (colorspace == image->get_colorspace() && chroma == image->get_chroma_format() && nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), output_nclx_profile)) { return image; } // @TODO: use color profile when converting int output_bpp = 0; // same as input //auto target_nclx = std::make_shared(); //target_nclx->set_from_heif_color_profile_nclx(target_heif_nclx); return convert_colorspace(image, colorspace, chroma, target_nclx_profile, output_bpp, options.color_conversion_options, nullptr, security_limits); } libheif-1.20.2/libheif/codecs/hevc_dec.cc000664 001750 001750 00000003310 15044356510 021207 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.20.2/libheif/codecs/hevc_dec.h000664 001750 001750 00000003000 15044356510 021045 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.20.2/libheif/codecs/jpeg2000_boxes.cc000664 001750 001750 00000034570 15044356510 022112 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 "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.20.2/libheif/codecs/avc_boxes.cc000664 001750 001750 00000020703 15044356510 021425 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.20.2/libheif/codecs/avif_dec.h000664 001750 001750 00000002756 15044356510 021066 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.20.2/libheif/codecs/hevc_enc.cc000664 001750 001750 00000007566 15044356510 021242 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_enc.h" #include "hevc_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include Result Encoder_HEVC::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { CodedImageData codedImage; auto hvcC = std::make_shared(); heif_image c_api_image; c_api_image.image = image; struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); if (err.code) { return Error(err.code, err.subcode, err.message); } int encoded_width = 0; int encoded_height = 0; for (;;) { uint8_t* data; int size; encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); if (data == nullptr) { break; } const uint8_t NAL_SPS = 33; if ((data[0] >> 1) == NAL_SPS) { parse_sps_for_hvcC_configuration(data, size, &hvcC->get_configuration(), &encoded_width, &encoded_height); codedImage.encoded_image_width = encoded_width; codedImage.encoded_image_height = encoded_height; } switch (data[0] >> 1) { case 0x20: case 0x21: case 0x22: hvcC->append_nal_data(data, size); break; default: codedImage.append_with_4bytes_size(data, size); // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size); } } if (!encoded_width || !encoded_height) { return Error(heif_error_Encoder_plugin_error, heif_suberror_Invalid_image_size); } codedImage.properties.push_back(hvcC); // Make sure that the encoder plugin works correctly and the encoded image has the correct size. if (encoder->plugin->plugin_api_version >= 3 && encoder->plugin->query_encoded_size != nullptr) { uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height(); encoder->plugin->query_encoded_size(encoder->encoder, image->get_width(), image->get_height(), &check_encoded_width, &check_encoded_height); assert((int)check_encoded_width == encoded_width); assert((int)check_encoded_height == encoded_height); } codedImage.codingConstraints.intra_pred_used = true; codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames return codedImage; } std::shared_ptr Encoder_HEVC::get_sample_description_box(const CodedImageData& data) const { auto hvc1 = std::make_shared(); hvc1->get_VisualSampleEntry().compressorname = "HEVC"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("hvcC")) { hvc1->append_child_box(prop); return hvc1; } } assert(false); // no hvcC generated return nullptr; } libheif-1.20.2/libheif/codecs/vvc_boxes.cc000664 001750 001750 00000037025 15044356510 021457 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 "api_structs.h" 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.20.2/libheif/codecs/encoder.h000664 001750 001750 00000005216 15044356510 020737 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_ENCODER_H #define HEIF_ENCODER_H #include "libheif/heif.h" #include "error.h" #include "libheif/heif_plugin.h" #include #include #include #include #include "sequences/seq_boxes.h" class HeifPixelImage; class Box; class Encoder { public: virtual ~Encoder() = default; struct CodedImageData { std::vector> properties; std::vector bitstream; CodingConstraints codingConstraints; // If 0, the encoded size is equal to the input size. uint32_t encoded_image_width = 0; uint32_t encoded_image_height = 0; bool is_sync_frame = true; // TODO: set in encoder void append(const uint8_t* data, size_t size); void append_with_4bytes_size(const uint8_t* data, size_t size); }; // If the output format requires a specific nclx (like JPEG), return this. Otherwise, return NULL. virtual const heif_color_profile_nclx* get_forced_output_nclx() const { return nullptr; } Result> convert_colorspace_for_encoding(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, const heif_security_limits* security_limits); virtual Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { return {}; } virtual std::shared_ptr get_sample_description_box(const CodedImageData&) const { return {}; } }; #endif libheif-1.20.2/libheif/codecs/avif_boxes.h000664 001750 001750 00000007745 15044356510 021456 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" #include "sequences/seq_boxes.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_av01 : public Box_VisualSampleEntry { public: Box_av01() { set_short_type(fourcc("av01")); } }; 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.20.2/libheif/codecs/jpeg2000_dec.cc000664 001750 001750 00000004134 15044356510 021516 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.20.2/libheif/codecs/avif_dec.cc000664 001750 001750 00000003504 15044356510 021214 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.20.2/libheif/codecs/jpeg_boxes.cc000664 001750 001750 00000004150 15044356510 021577 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.20.2/libheif/codecs/hevc_boxes.cc000664 001750 001750 00000047241 15044356510 021607 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 #include "api_structs.h" Error HEVCDecoderConfigurationRecord::parse(BitstreamRange& range, const heif_security_limits* limits) { uint8_t byte; configuration_version = range.read8(); byte = range.read8(); general_profile_space = (byte >> 6) & 3; general_tier_flag = (byte >> 5) & 1; general_profile_idc = (byte & 0x1F); general_profile_compatibility_flags = range.read32(); for (int i = 0; i < 6; i++) { byte = range.read8(); for (int b = 0; b < 8; b++) { general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1; } } general_level_idc = range.read8(); min_spatial_segmentation_idc = range.read16() & 0x0FFF; parallelism_type = range.read8() & 0x03; chroma_format = range.read8() & 0x03; bit_depth_luma = static_cast((range.read8() & 0x07) + 8); bit_depth_chroma = static_cast((range.read8() & 0x07) + 8); avg_frame_rate = range.read16(); byte = range.read8(); constant_frame_rate = (byte >> 6) & 0x03; num_temporal_layers = (byte >> 3) & 0x07; 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(); } Error HEVCDecoderConfigurationRecord::write(StreamWriter& writer) const { writer.write8(configuration_version); writer.write8((uint8_t) (((general_profile_space & 3) << 6) | ((general_tier_flag & 1) << 5) | (general_profile_idc & 0x1F))); writer.write32(general_profile_compatibility_flags); for (int i = 0; i < 6; i++) { uint8_t byte = 0; for (int b = 0; b < 8; b++) { if (general_constraint_indicator_flags[i * 8 + b]) { byte |= 1; } byte = (uint8_t) (byte << 1); } writer.write8(byte); } writer.write8(general_level_idc); writer.write16((min_spatial_segmentation_idc & 0x0FFF) | 0xF000); writer.write8(parallelism_type | 0xFC); writer.write8(chroma_format | 0xFC); writer.write8((uint8_t) ((bit_depth_luma - 8) | 0xF8)); writer.write8((uint8_t) ((bit_depth_chroma - 8) | 0xF8)); writer.write16(avg_frame_rate); writer.write8((uint8_t) (((constant_frame_rate & 0x03) << 6) | ((num_temporal_layers & 0x07) << 3) | ((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 HEVCDecoderConfigurationRecord::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); } } return Error::Ok; } bool HEVCDecoderConfigurationRecord::get_general_profile_compatibility_flag(int idx) const { return general_profile_compatibility_flags & (UINT32_C(0x80000000) >> idx); } bool HEVCDecoderConfigurationRecord::is_profile_compatibile(Profile profile) const { return (general_profile_idc == profile || get_general_profile_compatibility_flag(profile)); } Error Box_hvcC::parse(BitstreamRange& range, const heif_security_limits* limits) { //parse_full_box_header(range); return m_configuration.parse(range, limits); } 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 < HEVCDecoderConfigurationRecord::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) c.m_length_size) << "\n"; for (const auto& array : c.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_configuration.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) { HEVCDecoderConfigurationRecord::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_configuration.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); for (auto& nal_array : m_configuration.m_nal_array) { if (nal_array.m_NAL_unit_type == uint8_t(nal[0] >> 1)) { // kvazaar may send the same headers multiple times. Filter out the identical copies. for (auto& nal_unit : nal_array.m_nal_units) { // Note: sometimes kvazaar even sends the same packet twice, but with an extra zero byte. // We detect this by comparing only the common length. This is correct since each NAL // packet must be complete and thus, if a packet is longer than another complete packet, // its extra data must be superfluous. // // Example: //| | | //| | | | array_completeness: 1 //| | | | NAL_unit_type: 34 //| | | | 44 01 c1 71 82 99 20 00 //| | | | 44 01 c1 71 82 99 20 // Check whether packets have similar content. size_t common_length = std::min(nal_unit.size(), nal.size()); bool similar = true; for (size_t i = 0; i < common_length; i++) { if (nal_unit[i] != nal[i]) { similar = false; break; } } if (similar) { // If they are similar, keep the smaller one. if (nal_unit.size() > nal.size()) { nal_unit = std::move(nal); } // Exit. Do not add a copy of the packet. return; } } nal_array.m_nal_units.push_back(std::move(nal)); return; } } HEVCDecoderConfigurationRecord::NalArray array; array.m_array_completeness = 1; array.m_NAL_unit_type = uint8_t(nal[0] >> 1); array.m_nal_units.push_back(std::move(nal)); m_configuration.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 Error err = c.write(writer); if (err) { return err; } 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, HEVCDecoderConfigurationRecord* 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.20.2/libheif/codecs/hevc_boxes.h000664 001750 001750 00000010034 15044356510 021437 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" #include "sequences/seq_boxes.h" struct HEVCDecoderConfigurationRecord { 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; uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths struct NalArray { uint8_t m_array_completeness; uint8_t m_NAL_unit_type; std::vector > m_nal_units; }; enum Profile { Profile_Main = 1, Profile_Main10 = 2, Profile_MainStillPicture = 3, Profile_RExt = 4, Profile_HighThroughput = 5, Profile_ScreenCoding = 9, Profile_HighTHroughputScreenCoding = 11 }; std::vector m_nal_array; Error parse(BitstreamRange& range, const heif_security_limits* limits); Error write(StreamWriter& writer) const; bool get_general_profile_compatibility_flag(int idx) const; bool is_profile_compatibile(Profile) const; }; 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; } std::string dump(Indent&) const override; const char* debug_box_name() const override { return "HEVC Configuration Item"; } bool get_headers(std::vector* dest) const; void set_configuration(const HEVCDecoderConfigurationRecord& config) { m_configuration = config; } const HEVCDecoderConfigurationRecord& get_configuration() const { return m_configuration; } HEVCDecoderConfigurationRecord& get_configuration() { 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: HEVCDecoderConfigurationRecord m_configuration; }; class Box_hvc1 : public Box_VisualSampleEntry { public: Box_hvc1() { set_short_type(fourcc("hvc1")); } }; 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, HEVCDecoderConfigurationRecord* inout_config, int* width, int* height); #endif libheif-1.20.2/libheif/codecs/jpeg_enc.cc000664 001750 001750 00000007523 15044356510 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 "jpeg_enc.h" #include "jpeg_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include #include static uint8_t JPEG_SOS = 0xDA; const heif_color_profile_nclx* Encoder_JPEG::get_forced_output_nclx() const { // JPEG always uses CCIR-601 static heif_color_profile_nclx target_heif_nclx; target_heif_nclx.version = 1; target_heif_nclx.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; target_heif_nclx.color_primaries = heif_color_primaries_ITU_R_BT_601_6; target_heif_nclx.transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_601_6; target_heif_nclx.full_range_flag = true; return &target_heif_nclx; } Result Encoder_JPEG::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { Encoder::CodedImageData codedImage; heif_image c_api_image; c_api_image.image = image; struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); if (err.code) { return Error(err.code, err.subcode, err.message); } std::vector vec; for (;;) { uint8_t* data; int size; encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); if (data == nullptr) { break; } size_t oldsize = vec.size(); vec.resize(oldsize + size); memcpy(vec.data() + oldsize, data, size); } #if 0 // Optional: split the JPEG data into a jpgC box and the actual image data. // Currently disabled because not supported yet in other decoders. if (false) { size_t pos = find_jpeg_marker_start(vec, JPEG_SOS); if (pos > 0) { std::vector jpgC_data(vec.begin(), vec.begin() + pos); auto jpgC = std::make_shared(); jpgC->set_data(jpgC_data); auto ipma_box = m_heif_file->get_ipma_box(); int index = m_heif_file->get_ipco_box()->find_or_append_child_box(jpgC); ipma_box->add_property_for_item_ID(image_id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)}); std::vector image_data(vec.begin() + pos, vec.end()); vec = std::mo ve(image_data); } } #endif (void) JPEG_SOS; codedImage.bitstream = std::move(vec); #if 0 // TODO: extract 'jpgC' header data #endif codedImage.codingConstraints.intra_pred_used = false; codedImage.codingConstraints.all_ref_pics_intra = true; codedImage.codingConstraints.max_ref_per_pic = 0; return {codedImage}; } std::shared_ptr Encoder_JPEG::get_sample_description_box(const CodedImageData& data) const { auto mjpg = std::make_shared(); mjpg->get_VisualSampleEntry().compressorname = "JPEG"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("jpgC")) { mjpg->append_child_box(prop); } } return mjpg; } libheif-1.20.2/libheif/codecs/jpeg2000_enc.cc000664 001750 001750 00000006021 15044356510 021525 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_enc.h" #include "jpeg2000_boxes.h" #include "error.h" #include "context.h" #include "api_structs.h" #include Result Encoder_JPEG2000::encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) { Encoder::CodedImageData codedImageData; heif_image c_api_image; c_api_image.image = image; encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); // get compressed data for (;;) { uint8_t* data; int size; encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); if (data == nullptr) { break; } codedImageData.append(data, size); } // add 'j2kH' property auto j2kH = std::make_shared(); // add 'cdef' to 'j2kH' auto cdef = std::make_shared(); cdef->set_channels(image->get_colorspace()); j2kH->append_child_box(cdef); codedImageData.properties.push_back(j2kH); codedImageData.codingConstraints.intra_pred_used = false; codedImageData.codingConstraints.all_ref_pics_intra = true; codedImageData.codingConstraints.max_ref_per_pic = 0; return codedImageData; } std::shared_ptr Encoder_JPEG2000::get_sample_description_box(const CodedImageData& data) const { auto j2ki = std::make_shared(); j2ki->get_VisualSampleEntry().compressorname = "JPEG2000"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("j2kH")) { j2ki->append_child_box(prop); return j2ki; } } assert(false); // no hvcC generated return nullptr; } std::shared_ptr Encoder_HTJ2K::get_sample_description_box(const CodedImageData& data) const { auto j2ki = std::make_shared(); j2ki->get_VisualSampleEntry().compressorname = "HTJ2K"; for (auto prop : data.properties) { if (prop->get_short_type() == fourcc("j2kH")) { j2ki->append_child_box(prop); return j2ki; } } assert(false); // no hvcC generated return nullptr; } libheif-1.20.2/libheif/codecs/jpeg2000_boxes.h000664 001750 001750 00000034275 15044356510 021756 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; }; class Box_j2ki : public Box_VisualSampleEntry { public: Box_j2ki() { set_short_type(fourcc("j2ki")); } }; #endif // LIBHEIF_JPEG2000_BOXES_H libheif-1.20.2/libheif/codecs/avif_boxes.cc000664 001750 001750 00000035404 15044356510 021605 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 "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.20.2/libheif/codecs/avc_dec.cc000664 001750 001750 00000003121 15044356510 021033 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.20.2/libheif/codecs/vvc_enc.h000664 001750 001750 00000002710 15044356510 020737 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_ENCODER_VVC_H #define HEIF_ENCODER_VVC_H #include "libheif/heif.h" #include "box.h" #include "error.h" #include "file.h" #include #include #include #include #include "codecs/encoder.h" class Encoder_VVC : public Encoder { public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; std::shared_ptr get_sample_description_box(const CodedImageData&) const override; }; #endif libheif-1.20.2/libheif/codecs/jpeg2000_dec.h000664 001750 001750 00000003123 15044356510 021355 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.20.2/libheif/codecs/avc_dec.h000664 001750 001750 00000002752 15044356510 020706 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.20.2/libheif/codecs/jpeg_boxes.h000664 001750 001750 00000003074 15044356510 021445 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 #include "sequences/seq_boxes.h" 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; }; class Box_mjpg : public Box_VisualSampleEntry { public: Box_mjpg() { set_short_type(fourcc("mjpg")); } }; #endif // LIBHEIF_JPEG_BOXES_H libheif-1.20.2/libheif/plugins_unix.h000664 001750 001750 00000002754 15044356510 020610 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.20.2/libheif/bitstream.cc000664 001750 001750 00000044663 15044356510 020221 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 uint32_t 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(); } auto istr = get_istream(); for (;;) { if (!prepare_read(1)) { return std::string(); } 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; } std::string BitstreamRange::read_fixed_string(int len) { std::string str; if (!prepare_read(len)) { return std::string(); } auto istr = get_istream(); uint8_t n; bool success = istr->read(&n, 1); if (!success || n > len - 1) { return {}; } for (int i = 0; i < n; i++) { char c; success = istr->read(&c, 1); if (!success) { set_eof_while_reading(); return std::string(); } str += (char) c; } istr->seek_cur(len-n-1); 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_fixed_string(std::string s, size_t len) { size_t required_size = m_position + len; if (required_size > m_data.size()) { m_data.resize(required_size); } size_t n_chars = std::min(s.length(), len - 1); assert(n_chars <= 255); m_data[m_position++] = static_cast(n_chars); for (size_t i = 0; i < s.size() && i < len - 1; i++) { m_data[m_position++] = s[i]; } for (size_t i = s.size(); i < len - 1; 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.20.2/libheif/logging.cc000664 001750 001750 00000003434 15044356510 017644 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.20.2/libheif/Doxyfile.in000664 001750 001750 00000333415 15044356510 020027 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 # , /