原文链接: webp 前端使用wasm转换图片格式
上一篇: weakmap 和nodelist的forEach 方法的 Polyfill
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
git clone https://github.com/webmproject/libwebp.git --depth=1
在目录同级新建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>