Make webp compile

This commit is contained in:
Surma
2024-08-07 16:05:03 +01:00
parent dd290accc1
commit a0f9fea679
28 changed files with 6651 additions and 126 deletions

View File

@@ -1,11 +1,7 @@
CODEC_URL = https://github.com/webmproject/libwebp/archive/d2e245ea9e959a5a79e1db0ed2085206947e98f2.tar.gz
CODEC_DIR = node_modules/libwebp
CODEC_BUILD_ROOT := $(CODEC_DIR)/build
CODEC_BASELINE_BUILD_DIR := $(CODEC_BUILD_ROOT)/baseline
CODEC_SIMD_BUILD_DIR := $(CODEC_BUILD_ROOT)/simd
ENVIRONMENT = worker
OUT_JS = enc/webp_enc.js enc/webp_enc_simd.js dec/webp_dec.js enc/webp_node_enc.js dec/webp_node_dec.js
# OUT_JS = enc/webp_enc.js enc/webp_enc_simd.js dec/webp_dec.js
OUT_JS = enc/webp_enc.js dec/webp_dec.js
OUT_WASM := $(OUT_JS:.js=.wasm)
.PHONY: all clean
@@ -15,54 +11,25 @@ all: $(OUT_JS)
# Define dependencies for all variations of build artifacts.
$(filter enc/%,$(OUT_JS)): enc/webp_enc.o
$(filter dec/%,$(OUT_JS)): dec/webp_dec.o
enc/webp_node_enc.js dec/webp_node_dec.js: ENVIRONMENT = node
enc/webp_node_enc.js dec/webp_node_dec.js: $(CODEC_BASELINE_BUILD_DIR)/libwebp.a
enc/webp_enc.js dec/webp_dec.js: $(CODEC_BASELINE_BUILD_DIR)/libwebp.a
enc/webp_enc_simd.js: $(CODEC_SIMD_BUILD_DIR)/libwebp.a
# enc/webp_enc.js dec/webp_dec.js: $(CODEC_BASELINE_BUILD_DIR)/libwebp.a
# enc/webp_enc_simd.js: $(CODEC_SIMD_BUILD_DIR)/libwebp.a
LIBWEBP_FLAGS = -I${WEBP}/include -L${WEBP}/lib -lwebp
$(OUT_JS):
$(LD) \
$(LIBWEBP_FLAGS) \
$(LDFLAGS) \
--bind \
-lembind \
-s ENVIRONMENT=$(ENVIRONMENT) \
-s EXPORT_ES6=1 \
-o $@ \
$+
%.o: %.cpp $(CODEC_DIR)/CMakeLists.txt
%.o: %.cpp
$(CXX) -c \
$(LIBWEBP_FLAGS) \
$(CXXFLAGS) \
-I $(CODEC_DIR) \
-o $@ \
$<
%/libwebp.a: %/Makefile
$(MAKE) -C $(@D)
# Enable SIMD on a SIMD build.
$(CODEC_SIMD_BUILD_DIR)/Makefile: CMAKE_FLAGS+=-DWEBP_ENABLE_SIMD=1
%/Makefile: $(CODEC_DIR)/CMakeLists.txt
emcmake cmake \
$(CMAKE_FLAGS) \
-DCMAKE_DISABLE_FIND_PACKAGE_Threads=1 \
-DWEBP_BUILD_ANIM_UTILS=0 \
-DWEBP_BUILD_CWEBP=0 \
-DWEBP_BUILD_DWEBP=0 \
-DWEBP_BUILD_GIF2WEBP=0 \
-DWEBP_BUILD_IMG2WEBP=0 \
-DWEBP_BUILD_VWEBP=0 \
-DWEBP_BUILD_WEBPINFO=0 \
-DWEBP_BUILD_WEBPMUX=0 \
-DWEBP_BUILD_EXTRAS=0 \
-B $(@D) \
$(<D)
$(CODEC_DIR)/CMakeLists.txt:
mkdir -p $(CODEC_DIR)
curl -sL $(CODEC_URL) | tar xz --strip 1 -C $(CODEC_DIR)
clean:
$(RM) $(OUT_JS) $(OUT_WASM)
$(MAKE) -C $(CODEC_BASELINE_BUILD_DIR) clean
$(MAKE) -C $(CODEC_SIMD_BUILD_DIR) clean

View File

@@ -1,8 +1,8 @@
#include <string>
#include "emscripten/bind.h"
#include "emscripten/val.h"
#include "src/webp/decode.h"
#include "src/webp/demux.h"
#include "webp/decode.h"
#include "webp/demux.h"
using namespace emscripten;

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -3,7 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdexcept>
#include "src/webp/encode.h"
#include "webp/encode.h"
using namespace emscripten;

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

79
codecs/webp/flake.lock generated Normal file
View File

@@ -0,0 +1,79 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1717179513,
"narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "63dacb46bf939521bdc93981b4cbb7ecb58427a0",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"webp-src": "webp-src"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"webp-src": {
"flake": false,
"locked": {
"lastModified": 1606186400,
"narHash": "sha256-Emwd2wFbBONurMXSp8iwtwnSHgw4vcqBpuG0gyhZjFA=",
"owner": "webmproject",
"repo": "libwebp",
"rev": "d2e245ea9e959a5a79e1db0ed2085206947e98f2",
"type": "github"
},
"original": {
"owner": "webmproject",
"repo": "libwebp",
"rev": "d2e245ea9e959a5a79e1db0ed2085206947e98f2",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

107
codecs/webp/flake.nix Normal file
View File

@@ -0,0 +1,107 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/24.05";
flake-utils.url = "github:numtide/flake-utils";
webp-src = {
url = "github:webmproject/libwebp/d2e245ea9e959a5a79e1db0ed2085206947e98f2";
flake = false;
};
};
outputs =
{
self,
nixpkgs,
flake-utils,
webp-src,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
with pkgs;
rec {
packages = rec {
default = webp-squoosh;
webp-squoosh = stdenv.mkDerivation {
name = "mozjpeg-squoosh";
# Only copy files that are actually relevant to avoid unnecessary
# cache invalidations.
src = runCommand "src" { } ''
mkdir $out
cp -r ${./.}/enc $out/
cp -r ${./.}/dec $out/
cp ${./.}/Makefile $out/
'';
nativeBuildInputs = [
emscripten
webp
];
WEBP = webp;
dontConfigure = true;
buildPhase = ''
export HOME=$TMPDIR
emmake make -j$(nproc)
'';
installPhase = ''
mkdir -p $out
cp -r enc dec $out
'';
};
webp = stdenv.mkDerivation {
name = "webp";
src = webp-src;
nativeBuildInputs = [
# autoconf
# automake
# libtool
emscripten
# pkg-config
cmake
];
configurePhase = ''
# $HOME is required for Emscripten to work.
# See: https://nixos.org/manual/nixpkgs/stable/#emscripten
export HOME=$TMPDIR
mkdir -p $TMPDIR/build
emcmake cmake \
-DCMAKE_INSTALL_PREFIX=$out \
-DCMAKE_DISABLE_FIND_PACKAGE_Threads=1 \
-DWEBP_BUILD_ANIM_UTILS=0 \
-DWEBP_BUILD_CWEBP=0 \
-DWEBP_BUILD_DWEBP=0 \
-DWEBP_BUILD_GIF2WEBP=0 \
-DWEBP_BUILD_IMG2WEBP=0 \
-DWEBP_BUILD_VWEBP=0 \
-DWEBP_BUILD_WEBPINFO=0 \
-DWEBP_BUILD_WEBPMUX=0 \
-DWEBP_BUILD_EXTRAS=0 \
-B $TMPDIR/build \
.
'';
buildPhase = ''
export HOME=$TMPDIR
cd $TMPDIR/build
emmake make V=1 -j$(nproc) --trace
'';
installPhase = ''
cd $TMPDIR/build
make install
'';
dontFixup = true;
};
installScript = writeShellScriptBin "install.sh" ''
${pkgs.coreutils}/bin/rm -rf wasm_build
${pkgs.coreutils}/bin/mkdir -p wasm_build
${pkgs.rsync}/bin/rsync --chmod=u+w -r ${webp-squoosh}/* wasm_build/
'';
};
apps = {
install = {
type = "app";
program = "${packages.installScript}/bin/install.sh";
};
};
}
);
}

View File

@@ -0,0 +1,18 @@
# WebP decoder
- Source: <https://github.com/webmproject/libwebp>
- Version: v1.0.2
## Example
See `example.html`
## API
### `int version()`
Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c
### `RawImage decode(std::string buffer)`
Decodes the given webp buffer into raw RGBA. `RawImage` is a class with 3 fields: `buffer`, `width`, and `height`.

View File

@@ -0,0 +1,20 @@
<!doctype html>
<script src='webp_dec.js'></script>
<script>
async function loadFile(src) {
const resp = await fetch(src);
return await resp.arrayBuffer();
}
webp_dec().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadFile('../../example.webp');
const imageData = module.decode(image);
const canvas = document.createElement('canvas');
canvas.width = imageData.width;
canvas.height = imageData.height;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
});
</script>

View File

@@ -0,0 +1,29 @@
#include <string>
#include "emscripten/bind.h"
#include "emscripten/val.h"
#include "webp/decode.h"
#include "webp/demux.h"
using namespace emscripten;
int version() {
return WebPGetDecoderVersion();
}
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
val decode(std::string buffer) {
int width, height;
std::unique_ptr<uint8_t[]> rgba(
WebPDecodeRGBA((const uint8_t*)buffer.c_str(), buffer.size(), &width, &height));
return rgba ? ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(width * height * 4, rgba.get())),
width, height)
: val::null();
}
EMSCRIPTEN_BINDINGS(my_module) {
function("decode", &decode);
function("version", &version);
}

View File

@@ -0,0 +1,7 @@
export interface WebPModule extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

2997
codecs/webp/wasm_build/dec/webp_dec.js generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,22 @@
# WebP encoder
- Source: <https://github.com/webmproject/libwebp>
- Version: v1.0.2
## Dependencies
- Docker
## Example
See `example.html`
## API
### `int version()`
Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c
### `UInt8Array encode(uint8_t* image_buffer, int image_width, int image_height, WebPConfig config)`
Encodes the given image with given dimension to WebP.

View File

@@ -0,0 +1,58 @@
<!doctype html>
<script src='webp_enc.js'></script>
<script>
async function loadImage(src) {
// Load image
const img = document.createElement('img');
img.src = src;
await new Promise(resolve => img.onload = resolve);
// Make canvas same size as image
const canvas = document.createElement('canvas');
[canvas.width, canvas.height] = [img.width, img.height];
// Draw image onto canvas
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return ctx.getImageData(0, 0, img.width, img.height);
}
webp_enc().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadImage('../../example.png');
const result = module.encode(image.data, image.width, image.height, {
quality: 75,
target_size: 0,
target_PSNR: 0,
method: 4,
sns_strength: 50,
filter_strength: 60,
filter_sharpness: 0,
filter_type: 1,
partitions: 0,
segments: 4,
pass: 1,
show_compressed: 0,
preprocessing: 0,
autofilter: 0,
partition_limit: 0,
alpha_compression: 1,
alpha_filtering: 1,
alpha_quality: 100,
lossless: 0,
exact: 0,
image_hint: 0,
emulate_jpeg_size: 0,
thread_level: 0,
low_memory: 0,
near_lossless: 100,
use_delta_palette: 0,
use_sharp_yuv: 0,
});
console.log('size', result.length);
const blob = new Blob([result], {type: 'image/webp'});
const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);
});
</script>

View File

@@ -0,0 +1,85 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <stdlib.h>
#include <string.h>
#include <stdexcept>
#include "webp/encode.h"
using namespace emscripten;
int version() {
return WebPGetEncoderVersion();
}
thread_local const val Uint8Array = val::global("Uint8Array");
val encode(std::string img, int width, int height, WebPConfig config) {
auto img_in = (uint8_t*)img.c_str();
// A lot of this is duplicated from Encode in picture_enc.c
WebPPicture pic;
WebPMemoryWriter wrt;
int ok;
if (!WebPPictureInit(&pic)) {
// shouldn't happen, except if system installation is broken
return val::null();
}
// Allow quality to go higher than 0.
config.qmax = 100;
// Only use use_argb if we really need it, as it's slower.
pic.use_argb = config.lossless || config.use_sharp_yuv || config.preprocessing > 0;
pic.width = width;
pic.height = height;
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &wrt;
WebPMemoryWriterInit(&wrt);
ok = WebPPictureImportRGBA(&pic, img_in, width * 4) && WebPEncode(&config, &pic);
WebPPictureFree(&pic);
val js_result = ok ? Uint8Array.new_(typed_memory_view(wrt.size, wrt.mem)) : val::null();
WebPMemoryWriterClear(&wrt);
return js_result;
}
EMSCRIPTEN_BINDINGS(my_module) {
enum_<WebPImageHint>("WebPImageHint")
.value("WEBP_HINT_DEFAULT", WebPImageHint::WEBP_HINT_DEFAULT)
.value("WEBP_HINT_PICTURE", WebPImageHint::WEBP_HINT_PICTURE)
.value("WEBP_HINT_PHOTO", WebPImageHint::WEBP_HINT_PHOTO)
.value("WEBP_HINT_GRAPH", WebPImageHint::WEBP_HINT_GRAPH);
value_object<WebPConfig>("WebPConfig")
.field("lossless", &WebPConfig::lossless)
.field("quality", &WebPConfig::quality)
.field("method", &WebPConfig::method)
.field("image_hint", &WebPConfig::image_hint)
.field("target_size", &WebPConfig::target_size)
.field("target_PSNR", &WebPConfig::target_PSNR)
.field("segments", &WebPConfig::segments)
.field("sns_strength", &WebPConfig::sns_strength)
.field("filter_strength", &WebPConfig::filter_strength)
.field("filter_sharpness", &WebPConfig::filter_sharpness)
.field("filter_type", &WebPConfig::filter_type)
.field("autofilter", &WebPConfig::autofilter)
.field("alpha_compression", &WebPConfig::alpha_compression)
.field("alpha_filtering", &WebPConfig::alpha_filtering)
.field("alpha_quality", &WebPConfig::alpha_quality)
.field("pass", &WebPConfig::pass)
.field("show_compressed", &WebPConfig::show_compressed)
.field("preprocessing", &WebPConfig::preprocessing)
.field("partitions", &WebPConfig::partitions)
.field("partition_limit", &WebPConfig::partition_limit)
.field("emulate_jpeg_size", &WebPConfig::emulate_jpeg_size)
.field("low_memory", &WebPConfig::low_memory)
.field("near_lossless", &WebPConfig::near_lossless)
.field("exact", &WebPConfig::exact)
.field("use_delta_palette", &WebPConfig::use_delta_palette)
.field("use_sharp_yuv", &WebPConfig::use_sharp_yuv);
function("version", &version);
function("encode", &encode);
}

View File

@@ -0,0 +1,42 @@
export interface EncodeOptions {
quality: number;
target_size: number;
target_PSNR: number;
method: number;
sns_strength: number;
filter_strength: number;
filter_sharpness: number;
filter_type: number;
partitions: number;
segments: number;
pass: number;
show_compressed: number;
preprocessing: number;
autofilter: number;
partition_limit: number;
alpha_compression: number;
alpha_filtering: number;
alpha_quality: number;
lossless: number;
exact: number;
image_hint: number;
emulate_jpeg_size: number;
thread_level: number;
low_memory: number;
near_lossless: number;
use_delta_palette: number;
use_sharp_yuv: number;
}
export interface WebPModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

3173
codecs/webp/wasm_build/enc/webp_enc.js generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1 @@
export { default } from './webp_enc.js';