Skip to content

Commit

Permalink
Implement libheif v1.19.3 instead of heic2any to prevent browser cras…
Browse files Browse the repository at this point in the history
…hes with some iOS 18 heic image files
  • Loading branch information
schlagmichdoch committed Nov 11, 2024
1 parent 00d2757 commit fb6fe7a
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 31 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
* [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))
* [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

[FAQ](docs/faq.md)
Expand Down
22 changes: 0 additions & 22 deletions licenses/MIT-heic2any

This file was deleted.

1 change: 0 additions & 1 deletion public/scripts/heic2any.min.js

This file was deleted.

36 changes: 36 additions & 0 deletions public/scripts/heif-convert.js
Original file line number Diff line number Diff line change
@@ -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 changes: 41 additions & 0 deletions public/scripts/libheif.js

Large diffs are not rendered by default.

Binary file added public/scripts/libheif.wasm
Binary file not shown.
3 changes: 2 additions & 1 deletion public/scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class PairDrop {
"scripts/qr-code.min.js",
"scripts/zip.min.js",
"scripts/no-sleep.min.js",
"scripts/heic2any.min.js"
"scripts/heif-convert.js",
"scripts/libheif.js"
];

this.registerServiceWorker();
Expand Down
33 changes: 27 additions & 6 deletions public/scripts/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,7 @@ function getThumbnailAsDataUrl(file, width = undefined, height = undefined, qual
try {
if (file.type === "image/heif" || file.type === "image/heic") {
// browsers can't show heic files --> convert to jpeg before creating thumbnail
let blob = await fileToBlob(file);
file = await heic2any({
blob,
toType: "image/jpeg",
quality: quality
});
file = await heicToJpeg(file, 0.5);
}

let imageUrl = URL.createObjectURL(file);
Expand Down Expand Up @@ -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
function waitUntilImageIsLoaded(imageUrl, timeout = 10000) {
return new Promise((resolve, reject) => {
Expand Down
3 changes: 3 additions & 0 deletions public/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const relativePathsToCache = [
'manifest.json',
'styles/styles-main.css',
'styles/styles-deferred.css',
'scripts/heif-convert.js',
'scripts/libheif.js',
'scripts/libheif.wasm',
'scripts/localization.js',
'scripts/main.js',
'scripts/network.js',
Expand Down

0 comments on commit fb6fe7a

Please sign in to comment.