# 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"]; // ... }); ``` ![whenready-1.png](whenready-1.png) ![whenready-2.png](whenready-2.png) ### 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 }, }, ]); ```