1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Decode Blurhash pixels in a WebWorker

This commit is contained in:
Fernando Fernández 2021-11-04 20:07:07 +01:00
parent 302094523e
commit a47dd1b35c
No known key found for this signature in database
GPG key ID: 44495B839CCFF8CF
2 changed files with 46 additions and 14 deletions

View file

@ -0,0 +1,25 @@
import { decode } from 'blurhash';
import { expose } from 'comlink';
/**
* Decodes blurhash outside the main thread, in a web worker
*
* @param {string} hash - Hash to decode.
* @param {number} width - Width of the decoded pixel array
* @param {number} height - Height of the decoded pixel array.
* @param {number} punch - Contrast of the decoded pixels
* @returns {Uint8ClampedArray} - Returns the decoded pixels in the proxied response by Comlink
*/
function getPixels({
hash,
width,
height
}): Uint8ClampedArray {
try {
return decode(hash, width, height);
} catch {
throw new TypeError(`Blurhash ${hash} is not valid`);
}
}
expose(getPixels);

View file

@ -1,6 +1,12 @@
import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver'; import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver';
import * as userSettings from '../../scripts/settings/userSettings'; import * as userSettings from '../../scripts/settings/userSettings';
import { decode, isBlurhashValid } from 'blurhash'; import { wrap } from 'comlink';
const getPixels = wrap(
new Worker(
// eslint-disable-next-line compat/compat
new URL('./blurhash.worker.ts', import.meta.url)
)
);
import './style.scss'; import './style.scss';
/* eslint-disable indent */ /* eslint-disable indent */
@ -12,21 +18,18 @@ import './style.scss';
fillImageElement(elem, source); fillImageElement(elem, source);
} }
function itemBlurhashing(target, blurhashstr) { async function itemBlurhashing(target, hash) {
if (isBlurhashValid(blurhashstr)) { try {
// Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us, // Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us,
// improving the performance and reducing the memory usage, while retaining almost full blur quality. // improving the performance and reducing the memory usage, while retaining almost full blur quality.
// Lower values had more visible pixelation // Lower values had more visible pixelation
const width = 18; const width = 32;
const height = 18; const height = 32;
let pixels; const pixels = await getPixels({
try { hash,
pixels = decode(blurhashstr, width, height); width,
} catch (err) { height
console.error('Blurhash decode error: ', err); });
target.classList.add('non-blurhashable');
return;
}
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
@ -48,6 +51,10 @@ import './style.scss';
target.classList.add('blurhashed'); target.classList.add('blurhashed');
target.removeAttribute('data-blurhash'); target.removeAttribute('data-blurhash');
}); });
} catch (err) {
console.log(err);
target.classList.add('non-blurhashable');
return;
} }
} }
@ -142,7 +149,7 @@ import './style.scss';
for (const lazyElem of elem.querySelectorAll('.lazy')) { for (const lazyElem of elem.querySelectorAll('.lazy')) {
const blurhashstr = lazyElem.getAttribute('data-blurhash'); const blurhashstr = lazyElem.getAttribute('data-blurhash');
if (!lazyElem.classList.contains('blurhashed', 'non-blurhashable') && blurhashstr) { if (!lazyElem.classList.contains('blurhashed', 'non-blurhashable') && blurhashstr) {
itemBlurhashing(lazyElem, blurhashstr); Promise.resolve(itemBlurhashing(lazyElem, blurhashstr));
} else if (!blurhashstr && !lazyElem.classList.contains('blurhashed')) { } else if (!blurhashstr && !lazyElem.classList.contains('blurhashed')) {
lazyElem.classList.add('non-blurhashable'); lazyElem.classList.add('non-blurhashable');
} }