Implement libheif v1.19.3 instead of heic2any to prevent browser crashes with some iOS 18 heic image files

This commit is contained in:
schlagmichdoch 2024-11-11 20:39:46 +01:00
parent 00d2757fdc
commit fb6fe7ae61
9 changed files with 110 additions and 31 deletions

View file

@ -103,7 +103,7 @@ Connect to others in complex network situations, or over the Internet.
* [zip.js](https://gildas-lormeau.github.io/zip.js/) library * [zip.js](https://gildas-lormeau.github.io/zip.js/) library
* [cyrb53](https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js) super-fast hash function * [cyrb53](https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js) super-fast hash function
* [NoSleep](https://github.com/richtr/NoSleep.js) display sleep, add wake lock ([MIT](licenses/MIT-NoSleep)) * [NoSleep](https://github.com/richtr/NoSleep.js) display sleep, add wake lock ([MIT](licenses/MIT-NoSleep))
* [heic2any](https://github.com/alexcorvi/heic2any) HEIC/HEIF to PNG/GIF/JPEG ([MIT](licenses/MIT-heic2any)) * [libheif](https://github.com/strukturag/libheif) library to handle HEIC/HEIF files (GPLv3)
* [Weblate](https://weblate.org/) web-based localization tool * [Weblate](https://weblate.org/) web-based localization tool
[FAQ](docs/faq.md) [FAQ](docs/faq.md)

View file

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2020 Alex Corvi
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.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,36 @@
function HeifConvert(libheif) {
this.libheif = libheif;
this.decoder = new libheif.HeifDecoder();
}
HeifConvert.prototype.convert = async function (buffer) {
const decodeResult = this.decoder.decode(buffer);
const image = decodeResult[0];
let w = image.get_width();
let h = image.get_height();
const canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(w, h);
await copyData(imageData, image);
ctx.putImageData(imageData, 0, 0);
image.free();
return canvas;
};
function copyData(dataContainer, image) {
return new Promise((resolve, reject) => {
image.display(
dataContainer,
function () {
resolve()
}
);
})
}

41
public/scripts/libheif.js Normal file

File diff suppressed because one or more lines are too long

BIN
public/scripts/libheif.wasm Normal file

Binary file not shown.

View file

@ -17,7 +17,8 @@ class PairDrop {
"scripts/qr-code.min.js", "scripts/qr-code.min.js",
"scripts/zip.min.js", "scripts/zip.min.js",
"scripts/no-sleep.min.js", "scripts/no-sleep.min.js",
"scripts/heic2any.min.js" "scripts/heif-convert.js",
"scripts/libheif.js"
]; ];
this.registerServiceWorker(); this.registerServiceWorker();

View file

@ -478,12 +478,7 @@ function getThumbnailAsDataUrl(file, width = undefined, height = undefined, qual
try { try {
if (file.type === "image/heif" || file.type === "image/heic") { if (file.type === "image/heif" || file.type === "image/heic") {
// browsers can't show heic files --> convert to jpeg before creating thumbnail // browsers can't show heic files --> convert to jpeg before creating thumbnail
let blob = await fileToBlob(file); file = await heicToJpeg(file, 0.5);
file = await heic2any({
blob,
toType: "image/jpeg",
quality: quality
});
} }
let imageUrl = URL.createObjectURL(file); let imageUrl = URL.createObjectURL(file);
@ -541,6 +536,32 @@ function getThumbnailAsDataUrl(file, width = undefined, height = undefined, qual
}) })
} }
function initHeicConverter() {
return new Promise((resolve, reject) => {
fetch("libheif.wasm")
.then((res) => res.arrayBuffer())
.then(async (wasmBinary) => {
resolve(new HeifConvert(libheif({ wasmBinary: wasmBinary })));
})
.catch(reject);
});
}
async function heicToJpeg(file, quality) {
const heicConverter = await initHeicConverter();
console.log("Using libheif", heicConverter.libheif.heif_get_version());
const buffer = await file.arrayBuffer();
const canvas = await heicConverter.convert(buffer);
return new Promise(resolve => {
canvas.toBlob(blob => resolve(blob),
'image/jpeg',
quality
);
});
}
// Resolves returned promise when image is loaded and throws error if image cannot be shown // Resolves returned promise when image is loaded and throws error if image cannot be shown
function waitUntilImageIsLoaded(imageUrl, timeout = 10000) { function waitUntilImageIsLoaded(imageUrl, timeout = 10000) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View file

@ -7,6 +7,9 @@ const relativePathsToCache = [
'manifest.json', 'manifest.json',
'styles/styles-main.css', 'styles/styles-main.css',
'styles/styles-deferred.css', 'styles/styles-deferred.css',
'scripts/heif-convert.js',
'scripts/libheif.js',
'scripts/libheif.wasm',
'scripts/localization.js', 'scripts/localization.js',
'scripts/main.js', 'scripts/main.js',
'scripts/network.js', 'scripts/network.js',