diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98ed5f2..fa5db24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs -name: Node.js CI +name: CI on: push: diff --git a/README.md b/README.md index 6475bb1..a21bf8f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,218 @@ +# WIP + # `webp.wasm` -A webp wasm library based on libwebp. +webp.wasm is a pure Webassembly / Javascript port of libwebp. + +![CI](https://github.com/nieyuyao/webp-wasm/workflows/CI/badge.svg) + +## APIs + +### Encode + +#### encode + +Get encoder encoderVersion. + +`function encoderVersion(): Promise` + +##### Example + +```javascript +const version = await encoderVersion() +console.log(version) // 1.3.2 +``` + +#### encodeRGB + +Encodes rgb bitmap an outputs webp. + +`function encodeRGB(rgb: Uint8ClampedArray, width: number, height: number, quality?: number): Promise>` + +##### Example + +```javascript +... +const ctx = canvas.getContext('2d')! +const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height) +const buf = new Uint8ClampedArray(3 * canvas.width, canvas.height) +let j = 0 +// remove alpha +imgData.data.forEach((pixel, i) => { + if ((i + 1) % 4 === 0) { + return + } + buf[j] = pixel + j++ +}) +const result = await encodeRGB(buf, canvas.width, canvas.height) +const blob = new Blob([result!], {type: 'image/webp'}) +const blobURL = URL.createObjectURL(blob); +// download webp +const a = document.createElement('a') +a.download = '1.webp' +a.href = blobURL +document.body.appendChild(a) +a.click() +a.remove() +``` + +#### encodeRGBA + +Encodes rgba bitmap an outputs webp. + +`function encodeRGBA(rgba: Uint8ClampedArray, width: number, height: number, quality?: number): Promise>` + +##### Example + +```javascript +... +const ctx = canvas.getContext('2d')! +const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height) +const result = await encodeRGBA(imgData.data, canvas.width, canvas.height) +// download webp +... +``` + +#### encode + +A more advanced API is based on the WebPConfig. Only the lossless and quality parameters are supported now !!!. + +`function encodeRGBA(data: Uint8ClampedArray, width: number, height: number, hasAlpha: boolean,config: Partial): Promise>` + +- hasAlpha: boolean + +Whether to include alpha chanel. + +- WebPConfig.lossless: number + +Lossless encoding (0=lossy(default), 1=lossless). + +- WebPConfig.quality: number + +Between 0 and 100. + +##### Example + +```javascript +... +const ctx = canvas.getContext('2d')! +const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height) +const result = await encode(imgData.data, canvas.width, canvas.height, true, { lossless: 0 }) +// download webp +... +``` + +#### encodeAnimation + +Encodes frame data an outputs animated webp. + +`function encodeAnimation(width: number, height: number, hasAlpha: boolean, frames: WebPAnimationFrame[]): Promise>` + +- hasAlpha: boolean + +Whether to include alpha chanel. + +- WebPAnimationFrame.data: Uint8ClampedArray + +Frame data. + +- WebPAnimationFrame.duration: number + +Duration of frame. + +##### Example + +```javascript +... +// record each frame +frames.push({ + data: ctx.getImageData(0, 0, 100, 100).data, + duration: 20 +}) +const result = await encodeAnimation(100, 100, true, frames) +... +// download webp +``` + +### Decode + +#### decoderVersion + +Get decoder version. + +`function decoderVersion(): Promise` + +##### Example + +```javascript +const version = await decoderVersion() +console.log(version) // 1.3.2 +``` + +#### decodeRGB + +Decodes webp and outputs `ImageData` contains rgb bitmap. + +`function decodeRGB(rgb: Uint8ClampedArray): Promise>` + +##### Example + +```javascript +... +const fr = new FileReader() +fr.onload = () => { + if (!fr.result) { + return + } + webpData = fr.result as Uint8ClampedArray + const result = await decodeRGB(webpData) + // draw imageData + const ctx = canvas.getContext('2d')! + ctx.clearRect(0, 0, canvas.width, canvas.height) + canvas.style.width = `${result.width}px` + canvas.style.height = `${result.height}px` + canvas.width = result.width + canvas.height = result.height + ctx.putImageData(result, 0, 0) +} +// read webp file +fr.readAsArrayBuffer(file) +... +``` + +#### decodeRGBA + +Decodes webp and outputs `ImageData` contains rgba bitmap. + +`function decodeRGB(rgb: Uint8ClampedArray): Promise>` + +##### Example + +```javascript +... +const fr = new FileReader() +fr.onload = () => { + if (!fr.result) { + return + } + webpData = fr.result as Uint8ClampedArray + const result = await decodeRGBA(webpData) + // draw imageData + ... +} +// webp file +fr.readAsArrayBuffer(file) +... +``` -#### Example +## Playing Examples ```shell npm run dev ``` -#### Building +## Building ```shell npm run build diff --git a/example/Decode.vue b/example/Decode.vue index a53801e..a5a992c 100644 --- a/example/Decode.vue +++ b/example/Decode.vue @@ -12,7 +12,6 @@ const onChange = (event) => { } isUploaded = true const file = files[0] - console.log(file) const fr = new FileReader() fr.onload = () => { if (!fr.result) { @@ -33,6 +32,9 @@ const drawWebp = async () => { return } const result = await decodeRGBA(webpData) + if (!result) { + return + } const ctx = canvas.getContext('2d')! ctx.clearRect(0, 0, canvas.width, canvas.height) canvas.style.width = `${result.width}px` diff --git a/package.json b/package.json index da83ec1..cc1f8e0 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "webp-wasm", + "name": "webp.wasm", "version": "0.0.1", "description": "", "main": "dist/cjs/index.js", diff --git a/src/index.d.ts b/src/index.d.ts index 48eb931..e6d47be 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -4,13 +4,13 @@ export declare const encodeRGB: ( rgb: Uint8ClampedArray, width: number, height: number, - quality: number + quality?: number ) => Promise> export declare const encodeRGBA: ( rgba: Uint8ClampedArray, width: number, height: number, - quality: number + quality?: number ) => Promise> export declare const encode: ( data: Uint8ClampedArray, diff --git a/src/index.ts b/src/index.ts index 4af5d6f..af3cbed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,7 @@ export const encodeRGB = async ( quality?: number ): Promise> => { const module = await Module() - quality = typeof quality !== 'number' ? 100 : Math.min(Math.max(0, quality)) + quality = typeof quality !== 'number' ? 100 : Math.min(100, Math.max(0, quality)) return module.encodeRGB(rgb, width, height, quality) } @@ -29,10 +29,10 @@ export const encodeRGBA = async ( rgba: Uint8ClampedArray, width: number, height: number, - quality: number + quality?: number ): Promise> => { const module = await Module() - quality = typeof quality !== 'number' ? 100 : Math.min(Math.max(0, quality)) + quality = typeof quality !== 'number' ? 100 : Math.min(100, Math.max(0, quality)) return module.encodeRGBA(rgba, width, height, quality) } @@ -49,6 +49,8 @@ export const encode = async ( ...defaultWebpConfig, ...config, } + webpConfig.lossless = Math.min(1, Math.max(0, webpConfig.lossless)) + webpConfig.quality = Math.min(100, Math.max(0, webpConfig.quality)) return module.encode(data, width, height, hasAlpha, webpConfig) } diff --git a/tests/encode.test.js b/tests/encode.test.js index 4f197dd..38091e0 100644 --- a/tests/encode.test.js +++ b/tests/encode.test.js @@ -24,7 +24,7 @@ describe('encode', () => { const source = ctx.getImageData(0, 0, img.width, img.height) const buf = Buffer.alloc(3 * img.width * img.height) let j = 0 - // no alpha + // remove alpha source.data.forEach((pixel, i) => { if ((i + 1) % 4 === 0) { return