diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml
index 75f69aac1b..0b36c40a00 100644
--- a/.github/workflows/emscripten.yml
+++ b/.github/workflows/emscripten.yml
@@ -9,7 +9,7 @@ on:
jobs:
emscripten:
env:
- EMSCRIPTEN_VERSION: 3.1.43
+ EMSCRIPTEN_VERSION: 3.1.47
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
diff --git a/build-emscripten.sh b/build-emscripten.sh
index 2648c6eda2..f9449c2888 100755
--- a/build-emscripten.sh
+++ b/build-emscripten.sh
@@ -38,11 +38,11 @@ if [ "$ENABLE_LIBDE265" = "1" ]; then
-L \
-o libde265-${LIBDE265_VERSION}.tar.gz \
https://github.com/strukturag/libde265/releases/download/v${LIBDE265_VERSION}/libde265-${LIBDE265_VERSION}.tar.gz
- if [ ! -s "libde265-${LIBDE265_VERSION}/libde265/.libs/libde265.so" ]; then
+ if [ ! -s "libde265-${LIBDE265_VERSION}/libde265/.libs/libde265.a" ]; then
tar xf libde265-${LIBDE265_VERSION}.tar.gz
cd libde265-${LIBDE265_VERSION}
[ -x configure ] || ./autogen.sh
- CXXFLAGS=-O3 emconfigure ./configure --disable-sse --disable-dec265 --disable-sherlock265
+ CXXFLAGS=-O3 emconfigure ./configure --enable-static --disable-shared --disable-sse --disable-dec265 --disable-sherlock265
emmake make -j${CORES}
cd ..
fi
@@ -72,7 +72,7 @@ if [ "$ENABLE_AOM" = "1" ]; then
-DENABLE_TOOLS=0 \
-DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 \
- -DBUILD_SHARED_LIBS=1 \
+ -DBUILD_SHARED_LIBS=0 \
-DCMAKE_BUILD_TYPE=Release
emmake make -j${CORES}
@@ -92,7 +92,7 @@ if [ "$STANDALONE" = "1" ]; then
EXTRA_COMPILER_FLAGS="-D__EMSCRIPTEN_STANDALONE_WASM__=1"
fi
-CONFIGURE_ARGS="-DENABLE_MULTITHREADING_SUPPORT=OFF -DWITH_GDK_PIXBUF=OFF -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=ON -DENABLE_PLUGIN_LOADING=OFF"
+CONFIGURE_ARGS="-DENABLE_MULTITHREADING_SUPPORT=OFF -DWITH_GDK_PIXBUF=OFF -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DENABLE_PLUGIN_LOADING=OFF"
emcmake cmake ${SRCDIR} $CONFIGURE_ARGS \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS="${EXTRA_COMPILER_FLAGS}" \
@@ -108,13 +108,13 @@ EXPORTED_FUNCTIONS=$($EMSDK/upstream/bin/llvm-nm $LIBHEIFA --format=just-symbols
echo "Running Emscripten..."
-BUILD_FLAGS="-lembind -o libheif.js --pre-js ${SRCDIR}/pre.js --post-js ${SRCDIR}/post.js -sWASM=$USE_WASM"
+BUILD_FLAGS="-lembind -o libheif.js --post-js ${SRCDIR}/post.js -sWASM=$USE_WASM"
RELEASE_BUILD_FLAGS="-O3"
if [ "$STANDALONE" = "1" ]; then
# Note: this intentionally overwrites the BUILD_FLAGS set above
echo "Building in standalone (non-web) build mode"
- BUILD_FLAGS="-s STANDALONE_WASM=1 -s WASM=1 -o libheif.wasm --no-entry"
+ BUILD_FLAGS="-sSTANDALONE_WASM -sWASM -o libheif.wasm --no-entry"
fi
if [ "$DEBUG" = "1" ]; then
@@ -122,11 +122,12 @@ if [ "$DEBUG" = "1" ]; then
RELEASE_BUILD_FLAGS="--profile -g"
fi
-emcc "$LIBHEIFA" \
- -s EXPORTED_FUNCTIONS="$EXPORTED_FUNCTIONS,_free,_malloc,_memcpy" \
- -s ALLOW_MEMORY_GROWTH=1 \
- -s ERROR_ON_UNDEFINED_SYMBOLS=0 \
- -s LLD_REPORT_UNDEFINED \
+emcc -Wl,--whole-archive "$LIBHEIFA" -Wl,--no-whole-archive \
+ -sEXPORTED_FUNCTIONS="$EXPORTED_FUNCTIONS,_free,_malloc,_memcpy" \
+ -sMODULARIZE \
+ -sEXPORT_NAME="libheif" \
+ -sWASM_ASYNC_COMPILATION=0 \
+ -sALLOW_MEMORY_GROWTH \
--memory-init-file 0 \
-std=c++11 \
$LIBRARY_INCLUDE_FLAGS \
diff --git a/examples/demo.html b/examples/demo.html
index beb200346b..1c6f8275a7 100644
--- a/examples/demo.html
+++ b/examples/demo.html
@@ -191,6 +191,8 @@
libheif decoder demo
}.bind(this));
this.drawer = new CanvasDrawer(canvas);
this.decoder = new libheif.HeifDecoder();
+
+ console.log("Using libheif", libheif.heif_get_version());
saveSupported = this.canvas.toBlob &&
((URL && URL.createObjectURL) || navigator.msSaveOrOpenBlob);
if (saveSupported) {
@@ -313,8 +315,7 @@ libheif decoder demo
return;
}
- console.log("Using libheif", libheif.heif_get_version());
- var demo = new HeifDemo(libheif);
+ var demo = new HeifDemo(libheif());
show("form");
diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h
index 38d24292e5..395e1a7566 100644
--- a/libheif/heif_emscripten.h
+++ b/libheif/heif_emscripten.h
@@ -417,7 +417,12 @@ EMSCRIPTEN_BINDINGS(libheif) {
emscripten::class_("heif_image");
emscripten::value_object("heif_error")
.field("code", &heif_error::code)
- .field("subcode", &heif_error::subcode);
+ .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
diff --git a/post.js b/post.js
index d50f260127..d7aaf3824c 100644
--- a/post.js
+++ b/post.js
@@ -14,7 +14,7 @@ var HeifImage = function(handle) {
HeifImage.prototype.free = function() {
if (this.handle) {
- libheif.heif_image_handle_release(this.handle);
+ Module.heif_image_handle_release(this.handle);
this.handle = null;
}
};
@@ -24,8 +24,8 @@ HeifImage.prototype._ensureImage = function() {
return;
}
- var img = libheif.heif_js_decode_image(this.handle,
- libheif.heif_colorspace_YCbCr, libheif.heif_chroma_420);
+ var img = Module.heif_js_decode_image(this.handle,
+ Module.heif_colorspace.heif_colorspace_YCbCr, Module.heif_chroma.heif_chroma_420);
if (!img || img.code) {
console.log("Decoding image failed", this.handle, img);
return;
@@ -36,17 +36,17 @@ HeifImage.prototype._ensureImage = function() {
this.img = img;
if (img.alpha !== undefined) {
- this.alpha = new Uint8Array(StringToArrayBuffer(img.alpha));
- delete img.alpha;
+ this.alpha = new Uint8Array(StringToArrayBuffer(img.alpha));
+ delete img.alpha;
}
};
HeifImage.prototype.get_width = function() {
- return libheif.heif_image_handle_get_width(this.handle);
+ return Module.heif_image_handle_get_width(this.handle);
};
HeifImage.prototype.get_height = function() {
- return libheif.heif_image_handle_get_height(this.handle);
+ return Module.heif_image_handle_get_height(this.handle);
};
HeifImage.prototype.is_primary = function() {
@@ -63,8 +63,8 @@ HeifImage.prototype.display = function(image_data, callback) {
// If image hasn't been loaded yet, decode the image
if (!this.img) {
- var img = libheif.heif_js_decode_image2(this.handle,
- libheif.heif_colorspace_RGB, libheif.heif_chroma_interleaved_RGBA);
+ var img = Module.heif_js_decode_image2(this.handle,
+ Module.heif_colorspace.heif_colorspace_RGB, Module.heif_chroma.heif_chroma_interleaved_RGBA);
if (!img || img.code) {
console.log("Decoding image failed", this.handle, img);
@@ -73,14 +73,13 @@ HeifImage.prototype.display = function(image_data, callback) {
}
for (let c of img.channels) {
- if (c.id == libheif.heif_channel_interleaved) {
+ if (c.id == Module.heif_channel.heif_channel_interleaved) {
// copy image into output array
- if (c.stride == c.width*4) {
+ if (c.stride == c.width * 4) {
image_data.data.set(c.data);
- }
- else {
+ } else {
for (let y = 0; y < c.height; y++) {
let slice = c.data.slice(y * c.stride, y * c.stride + c.width * 4);
let offset = y * c.width * 4;
@@ -90,7 +89,7 @@ HeifImage.prototype.display = function(image_data, callback) {
}
}
- libheif.heif_image_release(img.image);
+ Module.heif_image_release(img.image);
}
callback(image_data);
@@ -103,32 +102,31 @@ var HeifDecoder = function() {
HeifDecoder.prototype.decode = function(buffer) {
if (this.decoder) {
- libheif.heif_context_free(this.decoder);
+ Module.heif_context_free(this.decoder);
}
- this.decoder = libheif.heif_context_alloc();
+ this.decoder = Module.heif_context_alloc();
if (!this.decoder) {
console.log("Could not create HEIF context");
return [];
}
- var error = libheif.heif_context_read_from_memory(this.decoder, buffer);
- if (error.code !== libheif.heif_error_Ok) {
- console.log("Could not parse HEIF file", error);
+ var error = Module.heif_context_read_from_memory(this.decoder, buffer);
+ if (error.code !== Module.heif_error_code.heif_error_Ok) {
+ console.log("Could not parse HEIF file", error.message);
return [];
}
- var ids = libheif.heif_js_context_get_list_of_top_level_image_IDs(this.decoder);
+ var ids = Module.heif_js_context_get_list_of_top_level_image_IDs(this.decoder);
if (!ids || ids.code) {
console.log("Error loading image ids", ids);
return [];
- }
- else if (!ids.length) {
+ } else if (!ids.length) {
console.log("No images found");
return [];
}
var result = [];
for (var i = 0; i < ids.length; i++) {
- var handle = libheif.heif_js_context_get_image_handle(this.decoder, ids[i]);
+ var handle = Module.heif_js_context_get_image_handle(this.decoder, ids[i]);
if (!handle || handle.code) {
console.log("Could not get image data for id", ids[i], handle);
continue;
@@ -139,61 +137,39 @@ HeifDecoder.prototype.decode = function(buffer) {
return result;
};
-var libheif = {
- // Expose high-level API.
- /** @expose */
- HeifDecoder: HeifDecoder,
-
- // Expose low-level API.
- /** @expose */
- fourcc: function(s) {
- return s.charCodeAt(0) << 24 |
- s.charCodeAt(1) << 16 |
- s.charCodeAt(2) << 8 |
- s.charCodeAt(3);
- }
-};
-
-// don't pollute the global namespace
-delete this['Module'];
-
-// On IE this function is called with "undefined" as first parameter. Override
-// with a version that supports this behaviour.
-function createNamedFunction(name, body) {
- if (!name) {
- name = "function_" + (new Date());
- }
- name = makeLegalFunctionName(name);
- /*jshint evil:true*/
- return new Function(
- "body",
- "return function " + name + "() {\n" +
- " \"use strict\";" +
- " return body.apply(this, arguments);\n" +
- "};\n"
- )(body);
+var fourcc = function(s) {
+ return s.charCodeAt(0) << 24 |
+ s.charCodeAt(1) << 16 |
+ s.charCodeAt(2) << 8 |
+ s.charCodeAt(3);
}
-var root = this;
-
-if (typeof exports !== 'undefined') {
- if (typeof module !== 'undefined' && module.exports) {
- /** @expose */
- exports = module.exports = libheif;
+Module.HeifImage = HeifImage;
+Module.HeifDecoder = HeifDecoder;
+Module.fourcc = fourcc;
+
+// Expose enum values.
+const enums = [
+ 'heif_error_code',
+ 'heif_suberror_code',
+ 'heif_compression_format',
+ 'heif_chroma',
+ 'heif_colorspace',
+ 'heif_channel'
+];
+for (const e of enums) {
+ for (const key in Module[e]) {
+ if (!Module[e].hasOwnProperty(key) || key === 'values') {
+ continue;
+ }
+ Module[key] = Module[e][key];
}
- /** @expose */
- exports.libheif = libheif;
-} else {
- /** @expose */
- root.libheif = libheif;
}
-if (typeof define === "function" && define.amd) {
- /** @expose */
- define([], function() {
- return libheif;
- });
+// Expose internal C API.
+for (const key in Module) {
+ if (key.indexOf('_heif_') !== 0 || Module[key.slice(1)] !== undefined) {
+ continue;
+ }
+ Module[key.slice(1)] = Module[key];
}
-
-// NOTE: wrapped inside "(function() {" block from pre.js
-}).call(this);
diff --git a/pre.js b/pre.js
deleted file mode 100644
index 3978ad958f..0000000000
--- a/pre.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * @preserve libheif.js HEIF decoder
- * (c)2017 struktur AG, http://www.struktur.de, opensource@struktur.de
- *
- * This file is part of libheif
- * https://github.com/strukturag/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 .
- */
-(function() {
-var Module = {
- print: function(text) {
- text = Array.prototype.slice.call(arguments).join(' ');
- console.log(text);
- },
- printErr: function(text) {
- text = Array.prototype.slice.call(arguments).join(' ');
- console.error(text);
- },
- canvas: {},
- noInitialRun: true,
- onRuntimeInitialized: function() {
- // Expose enum values.
- var enums = {
- "heif_error_code": true,
- "heif_suberror_code": true,
- "heif_compression_format": true,
- "heif_chroma": true,
- "heif_colorspace": true,
- "heif_channel": true
- };
- var e;
- for (e in enums) {
- if (!enums.hasOwnProperty(e)) {
- continue;
- }
-
- for (key in this[e]) {
- if (!this[e].hasOwnProperty(key) ||
- key === "values") {
- continue;
- }
-
- libheif[key] = this[e][key];
- }
- }
-
- // Expose internal C API.
- for (key in this) {
- if (enums.hasOwnProperty(key.slice(1)) || key.indexOf("_heif_") !== 0) {
- continue;
- }
- libheif[key.slice(1)] = this[key];
- }
-
- // Expose embind API.
- for (key in Module) {
- if (enums.hasOwnProperty(key) || key.indexOf("heif_") !== 0) {
- continue;
- }
- libheif[key] = Module[key];
- }
- }
-};
diff --git a/scripts/test-javascript.js b/scripts/test-javascript.js
index 8b23fddfeb..a75621ef40 100644
--- a/scripts/test-javascript.js
+++ b/scripts/test-javascript.js
@@ -18,41 +18,27 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see .
*/
-(function() {
-
- console.log("Running libheif JavaScript tests ...");
-
- var libheif = require('../libheif.js');
- console.log("Loaded libheif.js", libheif.heif_get_version());
-
- // Ensure that no "undefined" properties are exported.
- var key;
- var missing = [];
- for (key in libheif) {
- if (!libheif.hasOwnProperty(key)) {
- continue;
- }
- if (typeof(libheif[key]) === "undefined") {
- missing.push(key);
- }
- }
- if (missing.length) {
- throw new Error("The following properties are not defined: " + missing);
- }
-
- // Decode the example file and make sure at least one image is returned.
- var fs = require('fs');
- fs.readFile('examples/example.heic', function(err, data) {
- if (err) {
- throw err;
- }
-
- var decoder = new libheif.HeifDecoder();
- var image_data = decoder.decode(data);
- console.log("Loaded images:", image_data.length);
- if (!image_data.length) {
- throw new Error("Should have loaded images");
- }
- });
-
-})();
+
+const assert = require('assert');
+const fs = require('fs');
+
+console.log('Running libheif JavaScript tests ...');
+
+const libheif = require('../libheif.js')();
+
+// Test Embind API.
+console.log('Loaded libheif.js', libheif.heif_get_version());
+
+// Test internal C API.
+assert(libheif.heif_get_version_number_major() === 1, 'libheif major version should be 1')
+
+// Test enum values.
+assert(libheif.heif_error_Ok.value === 0, 'heif_error_Ok should be 0')
+
+// Decode the example file and make sure at least one image is returned.
+const data = fs.readFileSync('examples/example.heic');
+const decoder = new libheif.HeifDecoder();
+const image_data = decoder.decode(data);
+
+console.log('Loaded images:', image_data.length);
+assert(image_data.length > 0, "Should have loaded images")