Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 0 additions & 43 deletions .github/workflows/ci.yml

This file was deleted.

42 changes: 0 additions & 42 deletions .github/workflows/release.yml

This file was deleted.

7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ node_modules
*.o
wasm
*.tmp
.npmrc
.npmrc
.idea

# build artifacts (generated by npm run build-wasm:dev)
src/webp-wasm.js
src/webp-wasm.wasm
8 changes: 2 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
[submodule "libwebp"]
path = libwebp
branch = "v1.3.2"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better to specify a branch or tag here. @heidi-alps

url = https://github.com/webmproject/libwebp.git
ignore = all
url = https://chromium.googlesource.com/webm/libwebp
[submodule "emsdk"]
path = emsdk
branch = "3.1.56"
url = https://github.com/juj/emsdk.git
ignore = all
url = https://github.com/emscripten-core/emsdk.git
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

webp-wasm is a pure WebAssembly/JavaScript port of libwebp that enables WebP encoding/decoding (including animated WebP) in browsers and Node.js without native dependencies.

## Build Commands

```bash
# Full build (ESM with TypeScript)
npm run build

# Development build (outputs to src/ for hot reload)
npm run build-wasm:dev

# Build variants
npm run build:esm # ESM module build
npm run build:cjs # CommonJS build

# Run tests
npm test

# Development server (Vite)
npm run dev
```

## Architecture

### Build Pipeline

```
libwebp (C) → Emscripten → webp-wasm.wasm + webp-wasm.js → TypeScript wrapper → dist/
```

1. **libwebp/** - Vendored libwebp v1.3.2 source (git submodule)
2. **webp/** - C++ wrapper using Emscripten bindings (`emscripten/bind.h`)
3. **src/index.ts** - TypeScript API that loads and wraps the WASM module
4. **Makefile** - Orchestrates WASM compilation using emcc/em++

### Key Files

| File | Purpose |
|------|---------|
| `webp/webp.cpp` | Emscripten bindings (EMSCRIPTEN_BINDINGS macro) |
| `webp/encode.cpp` | WebP encoding including animation |
| `webp/decode.cpp` | WebP decoding including animation |
| `src/index.ts` | Public TypeScript API |
| `src/types.d.ts` | TypeScript type definitions |
| `build.sh` | Build script with emsdk auto-setup |

### Dependencies (Git Submodules)

| Submodule | Version | Source |
|-----------|---------|--------|
| libwebp | v1.3.2 | chromium.googlesource.com/webm/libwebp |
| emsdk | 3.1.74 | github.com/emscripten-core/emsdk |

The build script automatically installs emsdk if `EMSDK` environment variable is not set.

## API Structure

The library exports async functions that load the WASM module on first call:

- **Encoding**: `encodeRGB`, `encodeRGBA`, `encode`, `encodeAnimation`
- **Decoding**: `decodeRGB`, `decodeRGBA`, `decodeAnimation`
- **Utilities**: `encoderVersion`, `decoderVersion`

Animation encoding supports per-frame quality settings via `WebPAnimationFrame.config`.

## Testing

Tests use Jest with experimental WASM module support:
```bash
npm test # Run all tests
npm test -- tests/encode.test.js # Run single test file
```

Test fixtures are PNG images in `tests/fixtures/`.
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
SRC = webp
CODEC_DIR = libwebp
EMSDK_INCLUDE_DIR = emsdk/upstream/emscripten/cache/sysroot/include

# Use EMSDK env var if set, otherwise use local submodule
EMSDK_PATH ?= $(if $(EMSDK),$(EMSDK),./emsdk)
EMSDK_INCLUDE_DIR = $(EMSDK_PATH)/upstream/emscripten/cache/sysroot/include
EMCC = $(EMSDK_PATH)/upstream/emscripten/emcc
EMPP = $(EMSDK_PATH)/upstream/emscripten/em++
EMCMAKE = $(EMSDK_PATH)/upstream/emscripten/emcmake

.PHONY: clean

webp-wasm.js: webp.o decode.o encode.o version.o ${CODEC_DIR}/libwebp.a ${CODEC_DIR}/libsharpyuv.a ${CODEC_DIR}/libwebpmux.a ${CODEC_DIR}/libwebpdemux.a
emsdk/upstream/emscripten/emcc \
$(EMCC) \
-lembind \
-s EXPORT_ES6=$(EXPORT_ES6) \
-s MODULARIZE \
Expand All @@ -15,7 +21,7 @@ webp-wasm.js: webp.o decode.o encode.o version.o ${CODEC_DIR}/libwebp.a ${CODEC_
-v

%.o: ${SRC}/%.cpp
emsdk/upstream/emscripten/em++ -c \
$(EMPP) -c \
-std=c++17 \
-I ${CODEC_DIR} \
-I ${EMSDK_INCLUDE_DIR} \
Expand All @@ -27,7 +33,7 @@ webp-wasm.js: webp.o decode.o encode.o version.o ${CODEC_DIR}/libwebp.a ${CODEC_
$(MAKE) -C $(@D)

$(CODEC_DIR)/Makefile: ${CODEC_DIR}/CMakeLists.txt
emsdk/upstream/emscripten/emcmake cmake \
$(EMCMAKE) cmake \
-DCMAKE_DISABLE_FIND_PACKAGE_Threads=1 \
-DWEBP_BUILD_CWEBP=ON \
-DWEBP_BUILD_DWEBP=ON \
Expand Down
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,103 @@ npm run build-wasm:dev && npm run dev

## Building

### Prerequisites

- **Node.js 18+**
- **CMake 3.16+**

### Build Steps

```shell
# Clone with submodules
git clone --recursive <repository-url>
cd webp-wasm

# Install dependencies
npm install

# Build (automatically installs emsdk if needed)
npm run build
```

If you have a system-wide emsdk installation, you can use it instead:
```shell
source /path/to/emsdk_env.sh
npm run build
```

### Dependencies

This project uses git submodules:

| Submodule | Version | Source |
|-----------|---------|--------|
| libwebp | v1.3.2 | https://chromium.googlesource.com/webm/libwebp |
| emsdk | 3.1.74 | https://github.com/emscripten-core/emsdk |

```shell
# Initialize submodules after clone
git submodule update --init --recursive
```

To upgrade dependencies:
```shell
# Upgrade libwebp
cd libwebp && git fetch --tags && git checkout v1.4.0 && cd ..
git add libwebp && git commit -m "chore: upgrade libwebp to v1.4.0"

# Upgrade emsdk
cd emsdk && git fetch --tags && git checkout 3.1.75 && cd ..
git add emsdk && git commit -m "chore: upgrade emsdk to 3.1.75"
# Note: Update EMSDK_VERSION in build.sh accordingly
```

## Integration Guide

### Local Development (npm link)

For local development, you can link this library to your project:

```shell
# In webp-wasm directory
npm link

# In your project directory
npm link wasm-webp
```

### Vite Configuration

When using with Vite, add the following configuration:

```typescript
// vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
plugins: [
wasm(),
topLevelAwait(),
],
worker: {
format: 'es',
plugins: () => [
wasm(),
topLevelAwait(),
],
},
optimizeDeps: {
exclude: ['wasm-webp'],
},
// For npm link support
server: {
fs: {
allow: [
// Add the path to linked wasm-webp
'/path/to/webp-wasm',
],
},
},
})
```
33 changes: 30 additions & 3 deletions build.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
#!/bin/bash

EMSDK_VERSION="3.1.74"

# Setup emsdk: use env var or local submodule
if [ -z "$EMSDK" ]; then
echo "EMSDK not set, using local submodule..."

# Initialize submodule if needed
if [ ! -f "./emsdk/emsdk.py" ]; then
echo "Initializing emsdk submodule..."
git submodule update --init emsdk
fi

# Install and activate if not already done
if [ ! -d "./emsdk/upstream" ]; then
echo "Installing emsdk ${EMSDK_VERSION}..."
./emsdk/emsdk install ${EMSDK_VERSION}
./emsdk/emsdk activate ${EMSDK_VERSION}
fi

# Source emsdk environment
source ./emsdk/emsdk_env.sh
fi

echo "Using EMSDK: $EMSDK"

DIST_DIR="./dist"
WASM_OUT_DIR="./dist/cjs"
EXPORT_ES6=0
Expand All @@ -8,11 +35,11 @@ if [ ! -d $DIST_DIR ]; then
fi


if [ $1 == "-dev" ]; then
if [ "$1" == "-dev" ]; then
WASM_OUT_DIR="./src"
CLEAN_WASM_DIR="${WASM_OUT_DIR}/webp-wasm.*"
EXPORT_ES6=1
elif [ $1 == "-es6" ]; then
elif [ "$1" == "-es6" ]; then
WASM_OUT_DIR="./dist/esm"
EXPORT_ES6=1
fi
Expand All @@ -25,4 +52,4 @@ mkdir $WASM_OUT_DIR
make -C \
./ WASM_OUT_DIR=$WASM_OUT_DIR \
EXPORT_ES6=$EXPORT_ES6 \
CLEAN_WASM_DIR=$CLEAN_WASM_DIR
CLEAN_WASM_DIR=$CLEAN_WASM_DIR
2 changes: 1 addition & 1 deletion emsdk
Submodule emsdk updated 90 files
+37 −72 .circleci/config.yml
+17 −17 .flake8
+39 −0 .github/workflows/create-release.yml
+78 −0 .github/workflows/tag-release.yml
+4 −3 README.md
+1 −0 bazel/BUILD
+1 −0 bazel/MODULE.bazel
+40 −9 bazel/README.md
+32 −6 bazel/deps.bzl
+19 −17 bazel/emscripten_deps.bzl
+3 −3 bazel/emscripten_toolchain/BUILD.bazel
+0 −0 bazel/emscripten_toolchain/default_config
+7 −6 bazel/emscripten_toolchain/link_wrapper.py
+23 −3 bazel/emscripten_toolchain/toolchain.bzl
+22 −13 bazel/emscripten_toolchain/wasm_cc_binary.bzl
+126 −0 bazel/revisions.bzl
+1 −0 bazel/test_external/MODULE.bazel
+61 −0 bazel/test_external/long_command_line/BUILD.bazel
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file01.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file02.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file03.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file04.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file05.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file06.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file07.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file08.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file09.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file10.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file11.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file12.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file13.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file14.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file15.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file16.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file17.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file18.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file19.hh
+3 −0 bazel/test_external/long_command_line/include/long_command_line_file20.hh
+43 −0 bazel/test_external/long_command_line/long_command_line.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file01.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file02.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file03.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file04.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file05.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file06.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file07.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file08.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file09.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file10.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file11.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file12.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file13.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file14.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file15.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file16.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file17.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file18.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file19.cc
+5 −0 bazel/test_external/long_command_line/long_command_line_file20.cc
+1 −0 bazel/test_secondary_lto_cache/.bazelrc
+4 −0 bazel/test_secondary_lto_cache/.gitignore
+19 −0 bazel/test_secondary_lto_cache/BUILD
+1 −0 bazel/test_secondary_lto_cache/MODULE.bazel
+30 −0 bazel/test_secondary_lto_cache/WORKSPACE
+6 −0 bazel/test_secondary_lto_cache/hello-world.cc
+95 −1 bazel/toolchains.bzl
+2 −2 docker/Dockerfile
+2 −2 docker/README.md
+38 −2 emscripten-releases-tags.json
+1 −11 emsdk
+0 −21 emsdk.bat
+1 −9 emsdk.ps1
+67 −38 emsdk.py
+2 −0 emsdk_env.fish
+30 −324 emsdk_manifest.json
+16 −8 scripts/create_release.py
+44 −0 scripts/get_emscripten_revision_info.py
+25 −0 scripts/get_release_info.py
+1 −1 scripts/update_bazel_workspace.py
+0 −23 scripts/update_linux_arm64.sh
+15 −11 scripts/update_node.py
+12 −22 scripts/update_python.py
+19 −0 scripts/zip.py
+1 −1 test/test.bat
+7 −7 test/test.py
+0 −7 test/test_activation.ps1
+9 −0 test/test_bazel.ps1
+9 −7 test/test_bazel.sh
+8 −6 test/test_bazel_mac.sh
+0 −3 test/test_path_preservation.ps1
2 changes: 1 addition & 1 deletion libwebp
Submodule libwebp updated from 6c484c to ca3322
Loading