From f05c5f32d348641312b8610caedabe5fe9df0149 Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Tue, 1 Jul 2025 00:35:22 -0400 Subject: [PATCH 1/6] freeing memory after images are converted --- lib.js | 22 +++++++++++++++++++--- test/index.test.js | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib.js b/lib.js index 44d054f..31013a3 100644 --- a/lib.js +++ b/lib.js @@ -53,20 +53,36 @@ module.exports = libheif => { const decoder = new libheif.HeifDecoder(); const data = decoder.decode(buffer); + const free = () => { + for (const image of data) { + image.free(); + } + + decoder.decoder.delete(); + }; + if (!data.length) { throw new Error('HEIF image not found'); } if (!all) { - return await decodeImage(data[0]); + try { + return await decodeImage(data[0]); + } finally { + free(); + } } - return data.map(image => { + return Object.defineProperty(data.map(image => { return { width: image.get_width(), height: image.get_height(), - decode: async () => await decodeImage(image) + decode: async () => await decodeImage(image), }; + }), 'free', { + enumerable: false, + configurable: false, + value: free }); }; diff --git a/test/index.test.js b/test/index.test.js index e16e66f..3a7d587 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -63,6 +63,7 @@ function runTests(decode) { const images = await decode.all({ buffer }); expect(images).to.have.lengthOf(3); + expect(images).to.have.property('free').and.to.be.a('function'); const controls = await Promise.all([ readControl('0003-0-control.png'), @@ -86,6 +87,8 @@ function runTests(decode) { compare(control.data, image.data, control.width, control.height, `actual image at index ${i} did not match control`); } + + images.free(); }); it('throws if data other than a HEIC image is passed in', async () => { From 1d3dcdccf030bd8a958b3ade95bad26d7bccb740 Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Tue, 1 Jul 2025 00:35:37 -0400 Subject: [PATCH 2/6] updating libheif to latest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de0fc8b..28219b3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/catdad-experiments/heic-decode#readme", "dependencies": { - "libheif-js": "^1.17.1" + "libheif-js": "^1.19.8" }, "devDependencies": { "buffer-to-uint8array": "^1.1.0", From 76a16dfa58707efc1c852bd1ec3fea477ce2e70c Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Tue, 1 Jul 2025 00:37:30 -0400 Subject: [PATCH 3/6] renaming free -> dispose --- lib.js | 8 ++++---- test/index.test.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib.js b/lib.js index 31013a3..0b31816 100644 --- a/lib.js +++ b/lib.js @@ -53,7 +53,7 @@ module.exports = libheif => { const decoder = new libheif.HeifDecoder(); const data = decoder.decode(buffer); - const free = () => { + const dispose = () => { for (const image of data) { image.free(); } @@ -69,7 +69,7 @@ module.exports = libheif => { try { return await decodeImage(data[0]); } finally { - free(); + dispose(); } } @@ -79,10 +79,10 @@ module.exports = libheif => { height: image.get_height(), decode: async () => await decodeImage(image), }; - }), 'free', { + }), 'dispose', { enumerable: false, configurable: false, - value: free + value: dispose }); }; diff --git a/test/index.test.js b/test/index.test.js index 3a7d587..173760d 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -63,7 +63,7 @@ function runTests(decode) { const images = await decode.all({ buffer }); expect(images).to.have.lengthOf(3); - expect(images).to.have.property('free').and.to.be.a('function'); + expect(images).to.have.property('dispose').and.to.be.a('function'); const controls = await Promise.all([ readControl('0003-0-control.png'), @@ -88,7 +88,7 @@ function runTests(decode) { compare(control.data, image.data, control.width, control.height, `actual image at index ${i} did not match control`); } - images.free(); + images.dispose(); }); it('throws if data other than a HEIC image is passed in', async () => { From fda608548fed8fd6d2bf61204f3e485aedf114c8 Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Tue, 1 Jul 2025 00:42:38 -0400 Subject: [PATCH 4/6] updating README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 5d382bd..0a85258 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,14 @@ const decode = require('heic-decode'); data // Uint8ClampedArray containing pixel data } = await image.decode(); } + + // when you are done, make sure to free all memory used to convert the images + images.dispose(); })(); ``` +> Note: when decoding a single image (i.e. using `decode`), all resources are freed automatically after the conversion. However, when decoding all images in a file (i.e. using `decode.all`), you can decode the images at any time, so there is no safe time for the library to free resources -- you need to make sure to call `dispose` once you are done. + When the images are decoded, the return value is a plain object in the format of [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData). You can use this object to integrate with other imaging libraries for processing. _Note that while the decoder returns a Promise, it does the majority of the work synchronously, so you should consider using a worker thread in order to not block the main thread in highly concurrent production environments._ From dc8954086a112284d5aea8f728efd2904b632ecb Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Tue, 1 Jul 2025 00:55:33 -0400 Subject: [PATCH 5/6] writable: false --- lib.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib.js b/lib.js index 0b31816..6c278fa 100644 --- a/lib.js +++ b/lib.js @@ -82,6 +82,7 @@ module.exports = libheif => { }), 'dispose', { enumerable: false, configurable: false, + writable: false, value: dispose }); }; From f2a54664d29ca557dcfdfde8dd9aae91a6e73413 Mon Sep 17 00:00:00 2001 From: Kiril Vatev Date: Wed, 2 Jul 2025 00:19:35 -0400 Subject: [PATCH 6/6] just putting this back --- lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.js b/lib.js index 6c278fa..efa6537 100644 --- a/lib.js +++ b/lib.js @@ -77,7 +77,7 @@ module.exports = libheif => { return { width: image.get_width(), height: image.get_height(), - decode: async () => await decodeImage(image), + decode: async () => await decodeImage(image) }; }), 'dispose', { enumerable: false,