2020-10-30 20:34:04 +00:00
|
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2020-05-27 21:37:06 +00:00
|
|
|
import loadImage from 'blueimp-load-image';
|
|
|
|
import { encode } from 'blurhash';
|
|
|
|
|
|
|
|
type Input = Parameters<typeof loadImage>[0];
|
|
|
|
|
|
|
|
const loadImageData = async (input: Input): Promise<ImageData> => {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
loadImage(
|
|
|
|
input,
|
|
|
|
canvasOrError => {
|
|
|
|
if (canvasOrError instanceof Event && canvasOrError.type === 'error') {
|
|
|
|
const processError = new Error(
|
|
|
|
'imageToBlurHash: Failed to process image'
|
|
|
|
);
|
2021-04-14 01:27:43 +00:00
|
|
|
processError.originalError = canvasOrError;
|
2020-05-27 21:37:06 +00:00
|
|
|
reject(processError);
|
|
|
|
return;
|
|
|
|
}
|
2020-12-18 17:09:31 +00:00
|
|
|
|
|
|
|
if (!(canvasOrError instanceof HTMLCanvasElement)) {
|
|
|
|
reject(new Error('imageToBlurHash: result is not a canvas'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const context = canvasOrError.getContext('2d');
|
|
|
|
if (!context) {
|
|
|
|
reject(
|
|
|
|
new Error(
|
|
|
|
'imageToBlurHash: cannot get CanvasRenderingContext2D from canvas'
|
2020-05-27 21:37:06 +00:00
|
|
|
)
|
|
|
|
);
|
2020-12-18 17:09:31 +00:00
|
|
|
return;
|
2020-05-27 21:37:06 +00:00
|
|
|
}
|
2020-12-18 17:09:31 +00:00
|
|
|
|
|
|
|
resolve(
|
|
|
|
context.getImageData(0, 0, canvasOrError.width, canvasOrError.height)
|
2020-05-27 21:37:06 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
// Calculating the blurhash on large images is a long-running and
|
|
|
|
// synchronous operation, so here we ensure the images are a reasonable
|
|
|
|
// size before calculating the blurhash. iOS uses a max size of 200x200
|
|
|
|
// and Android uses a max size of 1/16 the original size. 200x200 is
|
|
|
|
// easier for us.
|
|
|
|
{ canvas: true, orientation: true, maxWidth: 200, maxHeight: 200 }
|
|
|
|
);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-09-14 21:56:35 +00:00
|
|
|
export const imageToBlurHash = async (input: Input): Promise<string> => {
|
2020-05-27 21:37:06 +00:00
|
|
|
const { data, width, height } = await loadImageData(input);
|
|
|
|
// 4 horizontal components and 3 vertical components
|
|
|
|
return encode(data, width, height, 4, 3);
|
|
|
|
};
|