webp 前端使用wasm转换图片格式
node库
https://github.com/scionoftech/webp-converter#readme
可能会需要设置权限
sudo chmod a+x /Users/ace/Documents/code/demo/test-demo/node_modules/webp-converter/bin/libwebp_osx/bin/cwebp
使用
const webp = require("webp-converter");
const imgPath = "./s2.jpg"
const jsOutPath = ["./js-out", +new Date(), ".wepb"].join("")
const result = webp.cwebp(imgPath, jsOutPath, "-q 80");
result.then((response) => {
console.log(response);
});
直接引入即可
https://webpjs.appspot.com/
或者使用挂载脚本
(function () {
var WebP = new Image();
WebP.onload = WebP.onerror = function () {
if (WebP.height != 2) {
var sc = document.createElement("script");
sc.type = "text/javascript";
sc.async = true;
var s = document.getElementsByTagName("script")[0];
sc.src = "js/webpjs-0.0.2.min.js";
s.parentNode.insertBefore(sc, s);
}
};
WebP.src = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
})();
编译自己的webp库
clone 仓库
https://github.com/webmproject/libwebp
在目录同级新建webp.c, 编译库
#include "emscripten.h"
#include "src/webp/encode.h"
#include <stdlib.h>
int result[2];
EMSCRIPTEN_KEEPALIVE
int version()
{
return WebPGetEncoderVersion();
}
EMSCRIPTEN_KEEPALIVE
uint8_t *create_buffer(int width, int height)
{
return malloc(width * height * 4 * sizeof(uint8_t));
}
EMSCRIPTEN_KEEPALIVE
void destroy_buffer(uint8_t *p)
{
free(p);
}
EMSCRIPTEN_KEEPALIVE
void encode(uint8_t *img_in, int width, int height, float quality)
{
uint8_t *img_out;
size_t size;
size = WebPEncodeRGBA(img_in, width, height, width * 4, quality, &img_out);
result[0] = (int)img_out;
result[1] = size;
}
EMSCRIPTEN_KEEPALIVE
void free_result(uint8_t *result)
{
WebPFree(result);
}
EMSCRIPTEN_KEEPALIVE
int get_result_pointer()
{
return result[0];
}
EMSCRIPTEN_KEEPALIVE
int get_result_size()
{
return result[1];
}
/*
允许动态增加内存, 不然会OOM
emcc -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS="["cwrap"]"
-I libwebp
webp.c
-s ALLOW_MEMORY_GROWTH
libwebp/src/{dec,dsp,demux,enc,mux,utils}/*.c
*/
得到产物
node的使用方式, 需要借助库image-pixels获取imageData
const Module = require("./a.out")
const fs = require("fs")
var pixels = require("image-pixels")
const webp = require("webp-converter");
// 图片太大会OOM
// const imgPath = "./s.jpg"
const imgPath = "./s3.jpg"
const wasmOutPath = "./wasm-out.webp"
async function getImageData() {
return pixels(imgPath)
}
Module.onRuntimeInitialized = async () => {
const api = {
version: Module.cwrap("version", "number", []),
create_buffer: Module.cwrap("create_buffer", "number", ["number", "number"]),
destroy_buffer: Module.cwrap("destroy_buffer", "", ["number"]),
encode: Module.cwrap("encode", "", ["number", "number", "number", "number"]),
free_result: Module.cwrap("free_result", "", ["number"]),
get_result_pointer: Module.cwrap("get_result_pointer", "number", []),
get_result_size: Module.cwrap("get_result_size", "number", []),
};
console.log(api.version());
const {data, width, height} = await getImageData()
const q = 75
const st1 = +new Date()
const p = api.create_buffer(width, height);
Module.HEAP8.set(data, p);
api.encode(p, width, height, q);
console.log("time1:", +new Date() - st1)
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
console.log("time12:", +new Date() - st1)
console.log(resultPointer, resultSize)
const resultView = new Uint8Array(Module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
console.log("time123:", +new Date() - st1)
console.log(result.length / 1024)
// console.log("time1:", +new Date() - st1)
// const blob = new Blob([result], {type: "image/webp"});
api.destroy_buffer(p);
const jsOutPath = "./js-out" + (+new Date()) + ".webp"
const st2 = +new Date()
const result2 = await webp.cwebp(imgPath, jsOutPath, `-q ${q}`);
console.log("time2:", +new Date() - st2)
}
web的使用方式
// const Module = require("./a.out")
// const fs = require("fs")
// 图片太大会OOM
const imgPath = "./t.jpg"
const wasmOutPath = "./wasm-out.webp"
Module.onRuntimeInitialized = async () => {
const api = {
version: Module.cwrap("version", "number", []),
create_buffer: Module.cwrap("create_buffer", "number", ["number", "number"]),
destroy_buffer: Module.cwrap("destroy_buffer", "", ["number"]),
encode: Module.cwrap("encode", "", ["number", "number", "number", "number"]),
free_result: Module.cwrap("free_result", "", ["number"]),
get_result_pointer: Module.cwrap("get_result_pointer", "number", []),
get_result_size: Module.cwrap("get_result_size", "number", []),
};
console.log(api.version());
// const buffer = fs.readFileSync(imgPath)
const imgBlob = await fetch(imgPath).then(resp => resp.blob());
const blobURL = URL.createObjectURL(imgBlob);
const img = document.createElement("img")
img.src = blobURL
document.body.appendChild(img);
// const blobURL = URL.createObjectURL(buffer);
// const img = document.createElement("img")
// img.src = imgPath
img.onload = () => {
console.log("===", "onload");
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
const data = ctx.getImageData(0, 0, img.width, img.height)
console.log("data", data,img.width,data.width)
const p = api.create_buffer(data.width, data.height);
Module.HEAP8.set(data.data, p);
api.encode(p, data.width, data.height, 75);
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
console.log(resultPointer, resultSize)
const resultView = new Uint8Array(Module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
const blob = new Blob([result], {type: "image/webp"});
const blobURL = URL.createObjectURL(blob);
const img2 = document.createElement("img");
img2.src = blobURL;
document.body.appendChild(img2);
api.destroy_buffer(p);
const WebP = new Image();
WebP.onload = WebP.onerror = function () {
if (WebP.height !== 2) {
const sc = document.createElement("script");
sc.type = "text/javascript";
sc.async = true;
const s = document.getElementsByTagName("script")[0];
sc.src = "js/webpjs-0.0.2.min.js";
s.parentNode.insertBefore(sc, s);
}
};
WebP.src = imgPath
}
// console.log(blobURL)
/* async function loadImage(imgBlob) {
// const imgBlob = await fetch(src).then(resp => resp.blob());
const img = await createImageBitmap(imgBlob);
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
return {
width: img.width,
height: img.height,
data: ctx.getImageData(0, 0, img.width, img.height)
}
}
const imageInfo = await loadImage(imageBlob);
const p = api.create_buffer(imageInfo.width, imageInfo.height);
Module.HEAP8.set(imageInfo.data.data, p);
api.encode(p, imageInfo.width, imageInfo.height, 75);
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
api.destroy_buffer(p);*/
}
html文件中引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/webpjs-0.0.2.min.js"></script>
<script src="a.out.js"></script>
<script src="./wasm.js"></script>
</head>
<body>
</body>
</html>