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 >
<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>

 

栏目