Skip to content

Instantly share code, notes, and snippets.

@deviationist
Last active March 12, 2024 17:27
Show Gist options
  • Save deviationist/bc5f190922b03e35535ddc68a70877ea to your computer and use it in GitHub Desktop.
Save deviationist/bc5f190922b03e35535ddc68a70877ea to your computer and use it in GitHub Desktop.
A simple HEIC to JPEG-implementation for Node using libheif-js (https://www.npmjs.com/package/libheif-js) and canvas (https://www.npmjs.com/package/canvas). Not type safe, should be refined. Based of catdad-experiments/heic-convert.
import libheif from 'libheif-js';
import { ImageData, Canvas, createCanvas } from 'canvas'
type Props = {
buffer: Buffer;
quality: number;
};
const processSingleImage = (image: any): Promise<any> => {
return new Promise((resolve, reject) => {
const w = image.get_width();
const h = image.get_height();
const whiteImage: any = new ImageData(w, h);
for (let i = 0; i < w * h; i++) {
whiteImage.data[i * 4 + 3] = 255;
}
image.display(whiteImage, (imageData: any) => {
if (!imageData) {
return reject(
"ERR_LIBHEIF Error while processing single image and generating image data, could not ensure image"
);
}
resolve(imageData);
});
});
};
const decodeBuffer = (buffer: Buffer): Promise<any> => {
return new Promise(async (resolve, reject) => {
const decoder = new libheif.HeifDecoder();
let imagesArr = decoder.decode(buffer);
imagesArr = imagesArr.filter((x: any) => {
let valid = true;
try {
/*
sometimes the heic container is valid
yet the images themselves are corrupt
*/
x.get_height();
} catch (e) {
valid = false;
}
return valid;
});
if (!imagesArr.length) {
reject('No valid images found');
}
imagesArr = await Promise.all(imagesArr.map((image: libheif.DecodeResult) => processSingleImage(image)));
resolve(imagesArr);
});
}
const imageDataToBlob = ({
imageData,
quality = 0.92,
}: {
imageData: any;
toType?: string;
quality?: number;
}): Promise<Blob> => {
// normalize quality
if (quality > 1 || quality < 0) {
quality = 0.92;
}
return new Promise((resolve, reject) => {
let canvas: Canvas | null = null;
try {
canvas = createCanvas(imageData.width, imageData.height);
} catch (e) {}
if (!canvas) {
return reject(
"ERR_CANVAS Error on converting imagedata to blob: Could not create canvas element"
);
}
const ctx = canvas.getContext("2d");
if (!ctx) {
return reject(
"ERR_CANVAS Error on converting imagedata to blob: Could not get canvas context"
);
}
ctx.putImageData(imageData as ImageData, 0, 0);
// Convert canvas to buffer, then to blob, then resolve
const buf = canvas.toBuffer('image/jpeg', { quality: quality });
resolve(new Blob([buf], { type: 'image/jpeg' }));
});
};
export const heic2jpeg = ({ buffer, quality }: Props): Promise<Blob[]> => {
return new Promise(async (resolve, reject) => {
const imagesArr = await decodeBuffer(buffer);
const files = await Promise.all(
imagesArr.map((imageData: any) =>
imageDataToBlob({
imageData,
quality,
})
)
);
resolve(files);
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment