# libheif-wasm-sample
**Repository Path**: fe521/libheif-wasm-sample
## Basic Information
- **Project Name**: libheif-wasm-sample
- **Description**: 使用libheif库的WebAssembly解码heic图片例子
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-10-01
- **Last Updated**: 2023-10-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# WebAssembly 使用 libheif 库解码 heic 图片
libheif 提供 在线预览的例子 https://strukturag.github.io/libheif/
## 在线预览体验
在线预览体验 https://docs.ffffee.com/wasm/libheif/index.html
最新文档 https://docs.ffffee.com/wasm/2-libheif-decode-heic-img.html
## 本地启动
```bash
git clone https://github.com/xieerduos/libheif-wasm-sample.git
```
```bash
cd libheif-wasm-sample
```
```bash
npm install
```
```bash
npm start
```
http://localhost:8080
## 日志记录
### 1. 自己构建 wasm 或者使用 在线例子的 wasm
尝试构建失败了,故 这里使用它已经构建好的例子,通过修改例子源码得到我们自己的代码
### 2. 下载 libheif.js libheif.wasm
https://strukturag.github.io/libheif/libheif.wasm
https://strukturag.github.io/libheif/libheif.js
### 3. 修改 libheif.js 增加 whenReady Promise
```js{3-6, 8-10 }
var Module = typeof Module != "undefined" ? Module : {};
(function () {
var resolveReady; // 添加
var whenReady = new Promise(function (resolve) { // 添加
resolveReady = resolve; // 添加
}); // 添加
var Module = {
// ...
onRuntimeInitialized: function () {
// ...
resolveReady(); // 添加
},
};
var libheif = {
HeifDecoder: HeifDecoder,
fourcc: function (s) {
return (
(s.charCodeAt(0) << 24) |
(s.charCodeAt(1) << 16) |
(s.charCodeAt(2) << 8) |
s.charCodeAt(3)
);
},
whenReady, // 添加
};
delete this["Module"];
// ...
});
```


### 4. 创建 index.html
```html
heif web worker sample
```
### 5. heifWorker.js
```js
importScripts("./libheif.js"); // eslint-disable-line
importScripts("./CanvasDrawer.js"); // eslint-disable-line
class HeifWrapper {
constructor(libheif) {
this.libheif = libheif;
this.canvas = new OffscreenCanvas(0, 0);
this.image_data = [];
this.drawer = new CanvasDrawer(this.canvas); // eslint-disable-line
this.decoder = new libheif.HeifDecoder();
}
loadBuffer(buffer, callback = () => {}) {
return new Promise((resolve, reject) => {
// 释放之前的图像数据
this.image_data[0]?.free();
this.image_data = this.decoder.decode(buffer);
this.drawer.draw(this.image_data[0], async (response) => {
if (response === "error-format") {
reject(new Error({ message: "error-format" }));
return;
}
if (response === "success") {
try {
console.time("OffscreenCanvas转成blob耗时");
const blob = await this.drawer.canvas.convertToBlob(); // 使用 convertToBlob
console.timeEnd("OffscreenCanvas转成blob耗时");
resolve(URL.createObjectURL(blob));
// const dataUrl = await this.blobToDataURL(blob); // 将 Blob 转换为 Data URL
// resolve(dataUrl);
} catch (error) {
reject(error);
}
} else {
callback(response);
}
});
});
}
blobToDataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
resolve(reader.result);
};
reader.onerror = function () {
reject(new Error("Failed to convert Blob to Data URL"));
};
reader.readAsDataURL(blob);
});
}
}
libheif.whenReady.then(() => {
console.log("heif.wasm 加载完成");
});
// console.log("libheif", libheif);
onmessage = async function (e) {
try {
const { url } = e.data;
const uint8Array = await fetch(url)
.then((response) => response.arrayBuffer())
.then((buffer) => new Uint8Array(buffer));
// 等待 libheif 加载完成
await libheif.whenReady;
console.time("解码耗时" + url);
const heifWrapper = new HeifWrapper(libheif); // eslint-disable-line
const dataUrl = await heifWrapper.loadBuffer(uint8Array);
console.timeEnd("解码耗时" + url);
postMessage(dataUrl);
} catch (error) {
console.error("[Worker Error]", error);
postMessage("error", error); // 发送错误消息
}
};
```
### 6. CanvasDrawer.js
```js
class CanvasDrawer {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
this.image_data = null;
}
draw(image, callback) {
try {
const w = image.get_width();
const h = image.get_height();
if (
w !== this.canvas.width ||
h !== this.canvas.height ||
!this.image_data
) {
this.canvas.width = w;
this.canvas.height = h;
this.image_data = this.ctx.createImageData(w, h);
this.whiteOutImageData(w, h);
}
callback("decoding");
image.display(this.image_data, (displayImageData) => {
// 请注意,这里直接使用 performance.now()
// console.log(`解码完成: ${performance.now() - startTime} milliseconds`);
callback("decoding-completed");
if (!displayImageData) {
callback("error-format");
return;
}
// 这里删除了使用 requestAnimationFrame 的逻辑,
// 因为在 Worker 中它是不可用的。
this.ctx.putImageData(displayImageData, 0, 0);
callback("success"); // 在此处调用回调
});
} catch (error) {
console.log("[CanvasDrawer error]", error);
}
}
whiteOutImageData(w, h) {
const imageData = this.image_data.data;
for (let i = 0; i < w * h; i++) imageData[i * 4 + 3] = 255;
}
}
```
## electron 支持
electron 上运行 WebAssembly .wasm 文件,需要支持 自定义协议允许 fetch api
增加 `supportFetchAPI: true`
```js
// https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/607#issuecomment-569469770
protocol.registerSchemesAsPrivileged([
{
scheme: "app",
privileges: { secure: true, supportFetchAPI: true, standard: true },
},
]);
```