Skip to content

libheif wasm 在 node.js 中运行

安装

bash
git clone https://gitee.com/fe521/libheif-wasm-node
bash
cd libheif-wasm-node
bash
npm install

运行

libheif wasm 比 heic-convert 更快

bash
usename@192 libheif-node % node index-node.js
使用heic-convert耗时: 508.115ms
转换后的图片已经保存到:  /Users/usename/Desktop/libheif-node/result.jpg
bash
usename@192 libheif-node % node index-wasm.js
使用WebAssembly耗时: 92.073ms
JPEG图片已成功保存到: /Users/usename/Desktop/libheif-node/wasm-result.jpg
usename@192 libheif-node %

源码地址

github https://github.com/xieerduos/libheif-wasm-node

gitee https://gitee.com/fe521/libheif-wasm-node

创建 index-wasm.js 文件

在当前目录下要有 libheif.js 、libheif.wasm 文件

js
const libheif = require("./libheif");
const fs = require("fs");
const jpeg = require("jpeg-js");

main();

async function main() {
  await libheif.whenReady;

  console.time("使用WebAssembly耗时");

  const buffer = fs.readFileSync("./example2.heic");

  const decoder = new libheif.HeifDecoder();

  const [image] = decoder.decode(buffer);

  const width = image.get_width(); // 图片的宽高
  const height = image.get_height(); // 图片的宽高

  const imageUint8Array = await new Promise((resolve, reject) => {
    const img = libheif.heif_js_decode_image2(
      image.handle,
      libheif.heif_colorspace_RGB,
      libheif.heif_chroma_interleaved_RGBA
    );

    for (let c of img.channels) {
      if (c.id == libheif.heif_channel_interleaved) {
        resolve(c.data);
        return;
      }
    }

    reject();
  });

  console.timeEnd("使用WebAssembly耗时");

  saveUint8ArrayAsJPEG(imageUint8Array, width, height, "wasm-result.jpg", 100);

  // saveUint8ArrayAsBMP(imageUint8Array, width, height, "result.bmp");
}

function saveUint8ArrayAsBMP(imageUint8Array, width, height, outputPath) {
  // 计算BMP文件的大小
  const fileSize = 54 + width * height * 3; // 54字节是BMP文件头的大小,每个像素3个字节(红、绿、蓝)

  // 创建一个Buffer来存储BMP文件数据
  const bmpBuffer = Buffer.alloc(fileSize);

  // 写入BMP文件头
  bmpBuffer.write("BM", 0); // 文件类型标识
  bmpBuffer.writeInt32LE(fileSize, 2); // 文件大小
  bmpBuffer.writeInt32LE(0, 6); // 保留字段
  bmpBuffer.writeInt32LE(54, 10); // 从文件头到位图数据的偏移量
  bmpBuffer.writeInt32LE(40, 14); // 位图信息头的大小
  bmpBuffer.writeInt32LE(width, 18); // 图片宽度
  bmpBuffer.writeInt32LE(-height, 22); // 图片高度(注意此处为负数,表示颠倒排列)
  bmpBuffer.writeInt16LE(1, 26); // 颜色平面数(1)
  bmpBuffer.writeInt16LE(24, 28); // 每个像素的位数(24位色)
  bmpBuffer.writeInt32LE(0, 30); // 压缩类型(无压缩)
  bmpBuffer.writeInt32LE(width * height * 3, 34); // 图像数据大小

  // 将Uint8Array中的RGB数据复制到BMP文件中
  for (let i = 0; i < imageUint8Array.length; i += 4) {
    bmpBuffer.writeUInt8(imageUint8Array[i + 2], 54 + (i / 4) * 3); // 红色通道
    bmpBuffer.writeUInt8(imageUint8Array[i + 1], 54 + (i / 4) * 3 + 1); // 绿色通道
    bmpBuffer.writeUInt8(imageUint8Array[i], 54 + (i / 4) * 3 + 2); // 蓝色通道
  }

  // 使用fs模块将BMP数据写入文件
  fs.writeFileSync(outputPath, bmpBuffer);

  console.log("彩色图片已成功保存到:", outputPath);
}

function saveUint8ArrayAsJPEG(
  imageUint8Array,
  width,
  height,
  outputPath,
  quality = 100
) {
  // 创建一个JPEG数据对象
  const jpegData = {
    data: imageUint8Array,
    width: width,
    height: height,
  };

  // 使用jpeg-js库的方法将JPEG数据编码为JPEG格式数据
  const jpegImageData = jpeg.encode(jpegData, quality);

  // 使用fs模块将JPEG数据写入文件
  fs.writeFileSync(outputPath, jpegImageData.data);

  console.log("JPEG图片已成功保存到:", outputPath);
}
bash
shaohai.li@192 libheif-node % node index-wasm.js
使用WebAssembly耗时: 92.167ms
JPEG图片已成功保存到: wasm-result.jpg
彩色图片已成功保存到: result.bmp
shaohai.li@192 libheif-node %