HEIC decoder module of the HEIC to JPG tool =========================================== This folder holds a build of libheif, without any change to its source code. libheif is distributed under the GNU Lesser General Public License, version 3 or (at your option) any later version (LGPL-3.0): - libheif 1.23.7 Copyright (c) 2017-2020 Struktur AG Copyright (c) 2017-2026 Dirk Farin https://github.com/strukturag/libheif The build also contains the Emscripten 6.0.12 runtime (MIT or University of Illinois/NCSA license); musl, its C library (Copyright (c) 2005-2020 Rich Felker, et al., MIT license), including math code under Sun Microsystems' fdlibm notice; and LLVM's libc++ (Apache-2.0 with LLVM exceptions). How it decodes -------------- The module contains no HEVC decoder of its own. libheif reads the HEIC file (its boxes, tiles, rotation, crop, alpha plane and colors) and hands each HEVC image to the browser's own video decoder through WebCodecs, with libheif's experimental WebCodecs plugin. Safari decodes HEIC itself and does not load this module. Where the browser has no HEVC decoder, the page says so instead of converting. The page adapts the VideoDecoder the plugin uses (source/wrapper/ webcodecs.ts.txt): it names the codec from the hvcC record the plugin passes, as ISO/IEC 14496-15 Annex E says, because browsers reject the name libheif 1.23.7 writes; it closes each decoder once its frame is out; and it records when the browser already converted a frame's colors to sRGB. Frames the browser hands over without planes (10-bit ones, in Chrome) are drawn on a canvas, as the plugin would draw them, and handed to the plugin as 8-bit full-range YCbCr planes made from that picture (source/wrapper/ycbcr.ts.txt), the inverse of libheif's own conversion to RGB: the plugin would hand libheif interleaved RGBA instead, which libheif 1.23.7 turns and mirrors wrongly and cannot attach an alpha plane to. The plugin also brings every frame to full range and to 8 bits, so the page hands libheif a copy of the file whose colr boxes say full range and whose hvcC boxes say 8 bits (source/wrapper/decode.ts.txt): otherwise libheif would expand the range of an image tagged as limited range a second time, and would refuse the 8-bit planes of a 10-bit image. A colr box of type nclc has no range flag to set: an image whose nclc box means limited range is not converted. Files ----- libheif.js, libheif.wasm The module: libheif's own JavaScript API (embind bindings and post.js) compiled with Emscripten, with the WebCodecs plugin and no other codec. SHA-256 in SHA256SUMS. source/libheif-1.23.7.tar.gz The exact source code of this build: the release file published on GitHub, checked against its SHA-256 by build.sh. source/build.sh The build script, with every option and flag used. source/wrapper/ The page's own code that calls the library, as written (TypeScript, saved as .txt to read in the browser): worker.ts loads the module, answer.ts decides what the worker answers for each file, decode.ts calls libheif's API, webcodecs.ts adapts the browser's VideoDecoder, ycbcr.ts turns the frames it draws back into planes, container.ts reads the file's boxes and color.ts converts the colors to sRGB. COPYING-libheif.txt The license texts of libheif (LGPL-3.0 and GPL-3.0). LICENSE-emscripten.txt, LICENSE-musl.txt, NOTICE-fdlibm.txt, LICENSE-llvm.txt The licenses and notices of the Emscripten runtime, musl, the fdlibm math code and LLVM's libc++. Rebuilding ---------- 1. Install and activate emsdk 6.0.12 (https://emscripten.org), CMake 3.16.3 or newer and curl. This build used CMake 4.4.4 and Ninja 1.13.2. 2. Copy source/libheif-1.23.7.tar.gz into : build.sh checks its SHA-256 and does not download it again. 3. Run: source/build.sh Built this way, in separate folders of the same machine, the output was byte-identical to libheif.js and libheif.wasm here (same SHA-256). The system libraries that come prebuilt with emsdk embed their own build paths, so another emsdk setup may give small differences. Replacing the library --------------------- The tool loads libheif.js as an ES module inside a Web Worker, fetches libheif.wasm, and calls the module's default export with { wasmBinary }. It then uses only libheif's JavaScript API, as defined in libheif/api/libheif/heif_emscripten.h: heif_context_alloc, heif_context_read_from_memory, heif_js_context_get_primary_image_handle, heif_image_handle_get_width, heif_image_handle_get_height, heif_image_handle_has_alpha_channel, heif_image_handle_is_premultiplied_alpha, heif_js_decode_image2 (its result is awaited, so it may return a Promise), heif_image_release, heif_image_handle_release, heif_context_free, the enums heif_error_code, heif_suberror_code, heif_colorspace, heif_chroma and heif_channel, and the exported C function _heif_context_set_maximum_image_size_limit (called with the context's address, embind's $$.ptr). Any build of libheif that exposes that API, including one you modify, can take the place of these two files: for example, through your browser's local overrides for this folder. The site's terms let you modify these components, use the site's code that calls them with your modified version, and reverse engineer that code to debug your modifications. A build whose HEVC decoder is not the WebCodecs plugin hands libheif samples in the file's own range and bit depth: with it, answer.ts must pass fullRangeFrames: false and eightBitFrames: false, or images tagged as limited range lose contrast and images above 8 bits are refused.