Compare commits

..

1 Commits
nix ... live

Author SHA1 Message Date
Jamie
5f72c2ed74 Fix single threaded avif encoding (#1409)
* Fix single threaded encoding by compiling a separate copy of libsharpyuv
- This version will have threads turned off

* Add the fixed compilations of avif_enc.js and the wasm file

* ensure that ST and MT builds run sequentially

* update OUT_ENC_JS to be an order-only dependency

* use WEBP_BUILD_WEBP_JS=ON flag instead

* update compiled ST files

* refactor to use -DWEBP_USE_THREAD=OFF
2024-08-13 11:11:17 +01:00
94 changed files with 383 additions and 2253 deletions

2
.nvmrc
View File

@@ -1 +1 @@
20.16.0
14.15.1

View File

@@ -15,8 +15,6 @@ However, Squoosh utilizes Google Analytics to collect the following:
# Developing
## Web App
To develop for Squoosh:
1. Clone the repository
@@ -33,28 +31,8 @@ To develop for Squoosh:
npm run dev
```
## Codecs
All build instructions for codecs are written using [Nix]. If you have Nix installed, you can rebuild the WebAssembly binaries by running:
```sh
# Build the codec
cd codec/<codec>
nix run '.#updateRepoBinaries'
```
If you do not have Nix installed, you can use the provided Docker image to create a shell with nix available:
```sh
# Build the image (only needs to be done once).
docker build -t squoosh-nix ./nix
docker run --name squoosh-nix -ti -v $PWD:/app squoosh-nix /bin/sh
# ... continue with the steps above
```
# Contributing
Squoosh is an open-source project that appreciates all community involvement. To contribute to the project, follow the [contribute guide](/CONTRIBUTING.md).
[squoosh]: https://squoosh.app
[nix]: https://nixos.org

1
codecs/.gitignore vendored
View File

@@ -1 +0,0 @@
!wasm_build

View File

@@ -16,6 +16,8 @@ override CFLAGS += "-Wno-unused-macros"
# See libavif/ext/libsharpyuv.cmd for more detail
LIBWEBP_URL_WITH_SHARPYUV = https://chromium.googlesource.com/webm/libwebp/+archive/e2c85878f6a33f29948b43d3492d9cdaf801aa54.tar.gz
LIBWEBP_DIR := $(CODEC_DIR)/ext/libwebp
LIBSHARPYUV_ST := $(LIBWEBP_DIR)/build_st/libsharpyuv.a
LIBSHARPYUV_MT := $(LIBWEBP_DIR)/build_mt/libsharpyuv.a
export LIBSHARPYUV := $(LIBWEBP_DIR)/build/libsharpyuv.a
OUT_ENC_JS = enc/avif_enc.js
@@ -36,7 +38,8 @@ HELPER_MAKEFLAGS := -f helper.Makefile
all: $(OUT_ENC_JS) $(OUT_DEC_JS) $(OUT_ENC_MT_JS)
# ST-Encoding
$(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt $(LIBSHARPYUV)
$(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt $(LIBSHARPYUV_ST)
mkdir -p $(LIBWEBP_DIR)/build && cp $(LIBSHARPYUV_ST) $(LIBSHARPYUV)
$(MAKE) \
$(HELPER_MAKEFLAGS) \
OUT_JS=$@ \
@@ -50,7 +53,9 @@ $(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLis
LIBAVIF_FLAGS="-DAVIF_CODEC_AOM_DECODE=0 -DAVIF_LOCAL_LIBSHARPYUV=ON"
# MT-Encoding
$(OUT_ENC_MT_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt $(LIBSHARPYUV)
# We need to run the ST and MT tasks sequentially to avoid conflicts with the copy of libsharpyuv in the build directory
$(OUT_ENC_MT_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt $(LIBSHARPYUV_MT) | $(OUT_ENC_JS)
mkdir -p $(LIBWEBP_DIR)/build && cp $(LIBSHARPYUV_MT) $(LIBSHARPYUV)
$(MAKE) \
$(HELPER_MAKEFLAGS) \
OUT_JS=$@ \
@@ -106,8 +111,29 @@ $(LIBWEBP_DIR)/CMakeLists.txt: $(CODEC_DIR)/CMakeLists.txt
curl -sL $(LIBWEBP_URL_WITH_SHARPYUV) \
| tar xzm -C $(LIBWEBP_DIR)
# Make libsharpyuv.a
$(LIBSHARPYUV): $(LIBWEBP_DIR)/CMakeLists.txt
# Make libsharpyuv.a for ST-Encoding
$(LIBSHARPYUV_ST): $(LIBWEBP_DIR)/CMakeLists.txt
mkdir -p $(@D)
emcmake cmake \
-DWEBP_USE_THREAD=OFF \
-DWEBP_BUILD_ANIM_UTILS=OFF \
-DWEBP_BUILD_CWEBP=OFF \
-DWEBP_BUILD_DWEBP=OFF \
-DWEBP_BUILD_GIF2WEBP=OFF \
-DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF \
-DWEBP_BUILD_WEBPINFO=OFF \
-DWEBP_BUILD_LIBWEBPMUX=OFF \
-DWEBP_BUILD_WEBPMUX=OFF \
-DWEBP_BUILD_EXTRAS=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_BUILD_TYPE=Release \
-S $(LIBWEBP_DIR) \
-B $(@D)
$(MAKE) -C $(@D) sharpyuv
# Make libsharpyuv.a for MT-Encoding
$(LIBSHARPYUV_MT): $(LIBWEBP_DIR)/CMakeLists.txt
mkdir -p $(@D)
emcmake cmake \
-DWEBP_BUILD_ANIM_UTILS=OFF \

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -1,31 +1,61 @@
CODEC_URL := https://github.com/mozilla/mozjpeg/archive/v3.3.1.tar.gz
CODEC_DIR := node_modules/mozjpeg
CODEC_OUT_RELATIVE := .libs/libjpeg.a rdswitch.o
CODEC_OUT := $(addprefix $(CODEC_DIR)/, $(CODEC_OUT_RELATIVE))
ENVIRONMENT = worker
OUT_JS := enc/mozjpeg_enc.js
OUT_JS := enc/mozjpeg_enc.js enc/mozjpeg_node_enc.js dec/mozjpeg_node_dec.js
OUT_WASM := $(OUT_JS:.js=.wasm)
.PHONY: all clean
all: $(OUT_JS)
# Define dependencies for all variations of build artifacts.
$(filter enc/%,$(OUT_JS)): enc/mozjpeg_enc.cpp
$(filter dec/%,$(OUT_JS)): dec/mozjpeg_dec.cpp
%.js:
enc/mozjpeg_node_enc.js dec/mozjpeg_node_dec.js: ENVIRONMENT = node
%.js: $(CODEC_OUT)
$(CXX) \
-O3 \
-flto \
-s FILESYSTEM=0 \
-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency \
-s ALLOW_MEMORY_GROWTH=1 \
-s TEXTDECODER=2 \
-s NODEJS_CATCH_EXIT=0 -s NODEJS_CATCH_REJECTION=0 \
-s ENVIRONMENT=$(ENVIRONMENT) \
-s EXPORT_ES6=1 \
-lembind \
-I $(CODEC_DIR) \
${CXXFLAGS} \
${LDFLAGS} \
--bind \
-s ENVIRONMENT=$(ENVIRONMENT) \
-s EXPORT_ES6=1 \
-o $@ \
-I ${MOZJPEG}/include \
-L ${MOZJPEG}/lib \
-ljpeg \
${MOZJPEG}/lib/rdswitch.o \
$+
# This one is a bit special: there is no rule for .libs/libjpeg.a
# so we use libjpeg.la which implicitly builds that one instead.
$(CODEC_DIR)/.libs/libjpeg.a: $(CODEC_DIR)/Makefile
$(MAKE) -C $(CODEC_DIR) libjpeg.la
$(CODEC_DIR)/rdswitch.o: $(CODEC_DIR)/Makefile
$(MAKE) -C $(CODEC_DIR) rdswitch.o
$(CODEC_DIR)/Makefile: $(CODEC_DIR)/configure
cd $(CODEC_DIR) && ./configure \
--disable-shared \
--without-turbojpeg \
--without-simd \
--without-arith-enc \
--without-arith-dec \
--with-build-date=squoosh
# ^ If not provided with a dummy value, MozJPEG includes a build date in the
# binary as part of the version string, making binaries different each time.
$(CODEC_DIR)/configure: $(CODEC_DIR)/configure.ac
cd $(CODEC_DIR) && autoreconf -iv
$(CODEC_DIR)/configure.ac: $(CODEC_DIR)
$(CODEC_DIR):
mkdir -p $@
curl -sL $(CODEC_URL) | tar xz --strip 1 -C $@
clean:
$(RM) $(OUT_JS) $(OUT_WASM)
$(MAKE) -C $(CODEC_DIR) clean

View File

@@ -0,0 +1,57 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include "config.h"
#include "jpeglib.h"
extern "C" {
#include "cdjpeg.h"
}
using namespace emscripten;
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
val decode(std::string image_in) {
uint8_t* image_buffer = (uint8_t*)image_in.c_str();
jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr;
// Initialize the JPEG decompression object with default error handling.
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, image_buffer, image_in.length());
// Read file header, set default decompression parameters
jpeg_read_header(&cinfo, TRUE);
// Force RGBA decoding, even for grayscale images
cinfo.out_color_space = JCS_EXT_RGBA;
jpeg_start_decompress(&cinfo);
// Prepare output buffer
size_t output_size = cinfo.output_width * cinfo.output_height * 4;
std::vector<uint8_t> output_buffer(output_size);
auto stride = cinfo.output_width * 4;
// Process data
while (cinfo.output_scanline < cinfo.output_height) {
uint8_t* ptr = &output_buffer[stride * cinfo.output_scanline];
jpeg_read_scanlines(&cinfo, &ptr, 1);
}
jpeg_finish_decompress(&cinfo);
// Step 7: release JPEG compression object
auto data = Uint8ClampedArray.new_(typed_memory_view(output_size, &output_buffer[0]));
auto js_result = ImageData.new_(data, cinfo.output_width, cinfo.output_height);
// This is an important step since it will release a good deal of memory.
jpeg_destroy_decompress(&cinfo);
// And we're done!
return js_result;
}
EMSCRIPTEN_BINDINGS(my_module) {
function("decode", &decode);
}

16
codecs/mozjpeg/dec/mozjpeg_node_dec.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -5,7 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jconfig.h"
#include "config.h"
#include "jpeglib.h"
extern "C" {

16
codecs/mozjpeg/enc/mozjpeg_enc.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

16
codecs/mozjpeg/enc/mozjpeg_node_enc.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -1,79 +0,0 @@
{
"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"
}
},
"mozjpeg-src": {
"flake": false,
"locked": {
"lastModified": 1499684294,
"narHash": "sha256-frpQdkk7bJE5qbV70fdL1FsC4eI0Fm8FWshqBQxCRtk=",
"owner": "mozilla",
"repo": "mozjpeg",
"rev": "f154ccc091cbc22141cdfd531e5ad1fdc5bc53c7",
"type": "github"
},
"original": {
"owner": "mozilla",
"ref": "v3.3.1",
"repo": "mozjpeg",
"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",
"mozjpeg-src": "mozjpeg-src",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,92 +0,0 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/24.05";
flake-utils.url = "github:numtide/flake-utils";
mozjpeg-src = {
url = "github:mozilla/mozjpeg/v3.3.1";
flake = false;
};
};
outputs =
{
self,
nixpkgs,
flake-utils,
mozjpeg-src,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) callPackage stdenv lib;
buildSquooshCppCodec = callPackage (import ../../nix/squoosh-cxx-builder) { };
squooshHelpers = callPackage (import ../../nix/squoosh-helpers) { };
inherit (squooshHelpers) forAllVariants mkRepoBinaryUpdater;
variants = {
base = { };
};
src = lib.sources.sourceByRegex ./. [
"Makefile"
"enc(/.+)?"
];
builder = variantName: opts: {
mozjpeg-squoosh = buildSquooshCppCodec {
name = "mozjpeg-squoosh";
inherit src;
MOZJPEG = self.packages.${system}."mozjpeg-${variantName}";
dontConfigure = true;
decoder = null;
};
mozjpeg = stdenv.mkDerivation {
name = "mozjpeg";
src = mozjpeg-src;
nativeBuildInputs = [
pkgs.autoconf
pkgs.automake
pkgs.libtool
pkgs.emscripten
pkgs.pkg-config
];
configurePhase = ''
# $HOME is required for Emscripten to work.
# See: https://nixos.org/manual/nixpkgs/stable/#emscripten
export HOME=$TMPDIR
autoreconf -ifv
emconfigure ./configure \
--disable-shared \
--without-turbojpeg \
--without-simd \
--without-arith-enc \
--without-arith-dec \
--with-build-date=squoosh \
--prefix=$out
'';
buildPhase = ''
export HOME=$TMPDIR
emmake make V=1 -j$(nproc) --trace
'';
installPhase = ''
make install
cp *.h $out/include
cp rdswitch.o $out/lib
'';
dontFixup = true;
};
};
packageVariants = forAllVariants { inherit builder variants; };
in
mkRepoBinaryUpdater {
packages = packageVariants // {
default = packageVariants."mozjpeg-squoosh-base";
};
}
);
}

4
codecs/mozjpeg/package-lock.json generated Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "mozjpeg_enc",
"lockfileVersion": 1
}

View File

@@ -0,0 +1,5 @@
{
"scripts": {
"build": "../build-cpp.sh"
}
}

View File

@@ -1,239 +0,0 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <inttypes.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jconfig.h"
#include "jpeglib.h"
extern "C" {
#include "cdjpeg.h"
}
using namespace emscripten;
// MozJPEG doesnt expose a numeric version, so I have to do some fun C macro
// hackery to turn it into a string. More details here:
// https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html
#define xstr(s) str(s)
#define str(s) #s
struct MozJpegOptions {
int quality;
bool baseline;
bool arithmetic;
bool progressive;
bool optimize_coding;
int smoothing;
int color_space;
int quant_table;
bool trellis_multipass;
bool trellis_opt_zero;
bool trellis_opt_table;
int trellis_loops;
bool auto_subsample;
int chroma_subsample;
bool separate_chroma_quality;
int chroma_quality;
};
int version() {
char buffer[] = xstr(MOZJPEG_VERSION);
int version = 0;
int last_index = 0;
for (int i = 0; i < strlen(buffer); i++) {
if (buffer[i] == '.') {
buffer[i] = '\0';
version = version << 8 | atoi(&buffer[last_index]);
buffer[i] = '.';
last_index = i + 1;
}
}
version = version << 8 | atoi(&buffer[last_index]);
return version;
}
thread_local const val Uint8Array = val::global("Uint8Array");
val encode(std::string image_in, int image_width, int image_height, MozJpegOptions opts) {
uint8_t* image_buffer = (uint8_t*)image_in.c_str();
// The code below is basically the `write_JPEG_file` function from
// https://github.com/mozilla/mozjpeg/blob/master/example.c
// I just write to memory instead of a file.
/* Step 1: allocate and initialize JPEG compression object */
/* This struct contains the JPEG compression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
* It is possible to have several such structures, representing multiple
* compression/decompression processes, in existence at once. We refer
* to any one struct (and its associated working data) as a "JPEG object".
*/
jpeg_compress_struct cinfo;
/* This struct represents a JPEG error handler. It is declared separately
* because applications often want to supply a specialized error handler
* (see the second half of this file for an example). But here we just
* take the easy way out and use the standard error handler, which will
* print a message on stderr and call exit() if compression fails.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
jpeg_error_mgr jerr;
/* We have to set up the error handler first, in case the initialization
* step fails. (Unlikely, but it could happen if you are out of memory.)
* This routine fills in the contents of struct jerr, and returns jerr's
* address which we place into the link field in cinfo.
*/
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
/* Step 2: specify data destination (eg, a file) */
/* Note: steps 2 and 3 can be done in either order. */
/* Here we use the library-supplied code to send compressed data to a
* stdio stream. You can also write your own code to do something else.
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
* requires it in order to write binary files.
*/
// if ((outfile = fopen(filename, "wb")) == NULL) {
// fprintf(stderr, "can't open %s\n", filename);
// exit(1);
// }
uint8_t* output = nullptr;
unsigned long size = 0;
jpeg_mem_dest(&cinfo, &output, &size);
/* Step 3: set parameters for compression */
/* First we supply a description of the input image.
* Four fields of the cinfo struct must be filled in:
*/
cinfo.image_width = image_width; /* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 4; /* # of color components per pixel */
cinfo.in_color_space = JCS_EXT_RGBA; /* colorspace of input image */
/* Now use the library's routine to set default compression parameters.
* (You must set at least cinfo.in_color_space before calling this,
* since the defaults depend on the source color space.)
*/
jpeg_set_defaults(&cinfo);
jpeg_set_colorspace(&cinfo, (J_COLOR_SPACE)opts.color_space);
if (opts.quant_table != -1) {
jpeg_c_set_int_param(&cinfo, JINT_BASE_QUANT_TBL_IDX, opts.quant_table);
}
cinfo.optimize_coding = opts.optimize_coding;
if (opts.arithmetic) {
cinfo.arith_code = TRUE;
cinfo.optimize_coding = FALSE;
}
cinfo.smoothing_factor = opts.smoothing;
jpeg_c_set_bool_param(&cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, opts.trellis_multipass);
jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_EOB_OPT, opts.trellis_opt_zero);
jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_Q_OPT, opts.trellis_opt_table);
jpeg_c_set_int_param(&cinfo, JINT_TRELLIS_NUM_LOOPS, opts.trellis_loops);
jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 0);
// A little hacky to build a string for this, but it means we can use
// set_quality_ratings which does some useful heuristic stuff.
std::string quality_str = std::to_string(opts.quality);
if (opts.separate_chroma_quality && opts.color_space == JCS_YCbCr) {
quality_str += "," + std::to_string(opts.chroma_quality);
}
char const* pqual = quality_str.c_str();
set_quality_ratings(&cinfo, (char*)pqual, opts.baseline);
if (!opts.auto_subsample && opts.color_space == JCS_YCbCr) {
cinfo.comp_info[0].h_samp_factor = opts.chroma_subsample;
cinfo.comp_info[0].v_samp_factor = opts.chroma_subsample;
if (opts.chroma_subsample > 2) {
// Otherwise encoding fails.
jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
}
}
if (!opts.baseline && opts.progressive) {
jpeg_simple_progression(&cinfo);
} else {
cinfo.num_scans = 0;
cinfo.scan_info = NULL;
}
/* Step 4: Start compressor */
/* TRUE ensures that we will write a complete interchange-JPEG file.
* Pass TRUE unless you are very sure of what you're doing.
*/
jpeg_start_compress(&cinfo, TRUE);
/* Step 5: while (scan lines remain to be written) */
/* jpeg_write_scanlines(...); */
/* Here we use the library's state variable cinfo.next_scanline as the
* loop counter, so that we don't have to keep track ourselves.
* To keep things simple, we pass one scanline per call; you can pass
* more if you wish, though.
*/
int row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) {
/* jpeg_write_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could pass
* more than one scanline at a time if that's more convenient.
*/
JSAMPROW row_pointer =
&image_buffer[cinfo.next_scanline * row_stride]; /* pointer to JSAMPLE row[s] */
(void)jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}
/* Step 6: Finish compression */
jpeg_finish_compress(&cinfo);
/* Step 7: release JPEG compression object */
auto js_result = Uint8Array.new_(typed_memory_view(size, output));
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_compress(&cinfo);
free(output);
/* And we're done! */
return js_result;
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<MozJpegOptions>("MozJpegOptions")
.field("quality", &MozJpegOptions::quality)
.field("baseline", &MozJpegOptions::baseline)
.field("arithmetic", &MozJpegOptions::arithmetic)
.field("progressive", &MozJpegOptions::progressive)
.field("optimize_coding", &MozJpegOptions::optimize_coding)
.field("smoothing", &MozJpegOptions::smoothing)
.field("color_space", &MozJpegOptions::color_space)
.field("quant_table", &MozJpegOptions::quant_table)
.field("trellis_multipass", &MozJpegOptions::trellis_multipass)
.field("trellis_opt_zero", &MozJpegOptions::trellis_opt_zero)
.field("trellis_opt_table", &MozJpegOptions::trellis_opt_table)
.field("trellis_loops", &MozJpegOptions::trellis_loops)
.field("chroma_subsample", &MozJpegOptions::chroma_subsample)
.field("auto_subsample", &MozJpegOptions::auto_subsample)
.field("separate_chroma_quality", &MozJpegOptions::separate_chroma_quality)
.field("chroma_quality", &MozJpegOptions::chroma_quality);
function("version", &version);
function("encode", &encode);
}

View File

@@ -1,37 +0,0 @@
export const enum MozJpegColorSpace {
GRAYSCALE = 1,
RGB,
YCbCr,
}
export interface EncodeOptions {
quality: number;
baseline: boolean;
arithmetic: boolean;
progressive: boolean;
optimize_coding: boolean;
smoothing: number;
color_space: MozJpegColorSpace;
quant_table: number;
trellis_multipass: boolean;
trellis_opt_zero: boolean;
trellis_opt_table: boolean;
trellis_loops: number;
auto_subsample: boolean;
chroma_subsample: number;
separate_chroma_quality: boolean;
chroma_quality: number;
}
export interface MozJPEGModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<MozJPEGModule>;
export default moduleFactory;

File diff suppressed because one or more lines are too long

115
codecs/resize/flake.lock generated
View File

@@ -1,115 +0,0 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1723616992,
"narHash": "sha256-jDHYfEECzFwZm4huz7AbPjlH3jJ4/2ns9PddtFA5XMY=",
"owner": "nix-community",
"repo": "fenix",
"rev": "7bad6c7ff73b784a9c7de9147626c8d5d5072809",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"rev": "7bad6c7ff73b784a9c7de9147626c8d5d5072809",
"type": "github"
}
},
"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": 1725432240,
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ad416d066ca1222956472ab7d0555a6946746a80",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"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": {
"fenix": "fenix",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1725548942,
"narHash": "sha256-ZnF5MaOAeiiKIATYN4rrqNsnhSQOQ+Hvfg0mHLvN04Y=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "124c7482167ff6eea4f7663c0be87ea568ccd8c6",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,57 +0,0 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/24.05";
flake-utils.url = "github:numtide/flake-utils";
fenix.url = "github:nix-community/fenix/7bad6c7ff73b784a9c7de9147626c8d5d5072809";
};
outputs =
{
self,
nixpkgs,
flake-utils,
fenix,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) callPackage lib;
buildSquooshRustCodec = callPackage (import ../../nix/squoosh-rust-builder) {
fenix = fenix.packages.${system};
};
squooshHelpers = callPackage (import ../../nix/squoosh-helpers) { };
inherit (squooshHelpers) mkRepoBinaryUpdater forAllVariants;
variants = {
base = { };
};
src = lib.sources.sourceByRegex ./. [
"Cargo.*"
"build\.rs"
"src(/.+)?"
];
builder = variantName: opts: {
resize-squoosh = buildSquooshRustCodec {
name = "resize-squoosh";
inherit src;
cargoLock = {
lockFile = "${src}/Cargo.lock";
};
wasmBindgen = {
sha256 = "sha256-HTElSB76gqCpDu8S0ZJlfd/S4ftMrbwxFgJM9OXBRz8=";
};
};
};
packageVariants = forAllVariants { inherit builder variants; };
in
mkRepoBinaryUpdater {
packages = packageVariants // {
default = packageVariants."resize-squoosh-base";
};
}
);
}

4
codecs/resize/package-lock.json generated Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "resize",
"lockfileVersion": 1
}

View File

@@ -0,0 +1,6 @@
{
"name": "resize",
"scripts": {
"build": "../build-rust.sh"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "squoosh-resize",
"collaborators": [
"Surma <surma@surma.link>"
],
"version": "0.1.0",
"files": [
"squoosh_resize_bg.wasm",
"squoosh_resize.js",
"squoosh_resize.d.ts"
],
"module": "squoosh_resize.js",
"types": "squoosh_resize.d.ts",
"sideEffects": false
}

Binary file not shown.

115
codecs/rotate/flake.lock generated
View File

@@ -1,115 +0,0 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1723616992,
"narHash": "sha256-jDHYfEECzFwZm4huz7AbPjlH3jJ4/2ns9PddtFA5XMY=",
"owner": "nix-community",
"repo": "fenix",
"rev": "7bad6c7ff73b784a9c7de9147626c8d5d5072809",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"rev": "7bad6c7ff73b784a9c7de9147626c8d5d5072809",
"type": "github"
}
},
"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": 1723362943,
"narHash": "sha256-dFZRVSgmJkyM0bkPpaYRtG/kRMRTorUIDj8BxoOt1T4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a58bc8ad779655e790115244571758e8de055e3d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"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": {
"fenix": "fenix",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1723561310,
"narHash": "sha256-a3KMMsIDvdo+ClLabh5wfJoa17YTSvy2wDLb8yZCXvc=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "78c2bdce860dbd996a8083224d01a96660dd6a15",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,54 +0,0 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/24.05";
flake-utils.url = "github:numtide/flake-utils";
fenix.url = "github:nix-community/fenix/7bad6c7ff73b784a9c7de9147626c8d5d5072809";
};
outputs =
{
self,
nixpkgs,
flake-utils,
fenix,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) callPackage lib;
buildSquooshRustCodec = callPackage (import ../../nix/squoosh-rust-builder) {
fenix = fenix.packages.${system};
};
squooshHelpers = callPackage (import ../../nix/squoosh-helpers) { };
inherit (squooshHelpers) mkRepoBinaryUpdater forAllVariants;
variants = {
base = { };
};
src = lib.sources.sourceByRegex ./. [
"Cargo\.*"
".*\.rs"
];
builder = variantName: opts: {
rotate-squoosh = buildSquooshRustCodec {
name = "rotate-squoosh";
inherit src;
cargoLock = {
lockFile = "${src}/Cargo.lock";
};
wasmBindgen = null;
};
};
packageVariants = forAllVariants { inherit builder variants; };
in
mkRepoBinaryUpdater {
packages = packageVariants // {
default = packageVariants."rotate-squoosh-base";
};
}
);
}

BIN
codecs/rotate/rotate.wasm Executable file

Binary file not shown.

View File

@@ -1,7 +1,11 @@
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
OUT_JS = enc/webp_enc.js dec/webp_dec.js
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_WASM := $(OUT_JS:.js=.wasm)
.PHONY: all clean
@@ -11,41 +15,54 @@ 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_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
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
$(OUT_JS):
$(LD) \
-O3 \
-flto \
-std=c++17 \
-s FILESYSTEM=0 \
-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency \
-s ALLOW_MEMORY_GROWTH=1 \
-s TEXTDECODER=2 \
-s NODEJS_CATCH_EXIT=0 -s NODEJS_CATCH_REJECTION=0 \
$(LIBWEBP_FLAGS) \
$(LDFLAGS) \
-lembind \
--bind \
-s ENVIRONMENT=$(ENVIRONMENT) \
-s EXPORT_ES6=1 \
-o $@ \
$+
%.o: %.cpp
%.o: %.cpp $(CODEC_DIR)/CMakeLists.txt
$(CXX) -c \
-O3 \
-flto \
-std=c++17 \
-s FILESYSTEM=0 \
-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency \
-s ALLOW_MEMORY_GROWTH=1 \
-s TEXTDECODER=2 \
-s NODEJS_CATCH_EXIT=0 -s NODEJS_CATCH_REJECTION=0 \
$(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 "webp/decode.h"
#include "webp/demux.h"
#include "src/webp/decode.h"
#include "src/webp/demux.h"
using namespace emscripten;

16
codecs/webp/dec/webp_dec.js generated Normal file

File diff suppressed because one or more lines are too long

BIN
codecs/webp/dec/webp_dec.wasm Executable file

Binary file not shown.

16
codecs/webp/dec/webp_node_dec.js generated Normal file

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 "webp/encode.h"
#include "src/webp/encode.h"
using namespace emscripten;

16
codecs/webp/enc/webp_enc.js generated Normal file

File diff suppressed because one or more lines are too long

BIN
codecs/webp/enc/webp_enc.wasm Executable file

Binary file not shown.

16
codecs/webp/enc/webp_enc_simd.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

16
codecs/webp/enc/webp_node_enc.js generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

79
codecs/webp/flake.lock generated
View File

@@ -1,79 +0,0 @@
{
"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
}

View File

@@ -1,130 +0,0 @@
{
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};
inherit (pkgs) lib stdenv callPackage;
buildSquooshCppCodec = callPackage (import ../../nix/squoosh-cxx-builder) { };
squooshHelpers = callPackage (import ../../nix/squoosh-helpers) { };
inherit (squooshHelpers) forAllVariants mkRepoBinaryUpdater;
variants = {
base = {
simd = false;
};
simd = {
simd = true;
};
};
src = lib.sources.sourceByRegex ./. [
"Makefile"
"enc(/.+)?"
"dec(/.+)?"
];
builder =
variantName:
{ simd }:
{
"webp-squoosh" = buildSquooshCppCodec {
name = "webp-squoosh-${variantName}";
inherit src;
nativeBuildInputs = [
pkgs.emscripten
self.packages.${system}."webp-${variantName}"
];
WEBP = self.packages.${system}."webp-${variantName}";
dontConfigure = true;
buildPhase = ''
export HOME=$TMPDIR
emmake make -j$(nproc)
'';
installPhase = ''
mkdir -p $out
cp -r enc dec $out
'';
};
"webp" = stdenv.mkDerivation {
name = "webp-${variantName}";
src = webp-src;
nativeBuildInputs = [
pkgs.emscripten
pkgs.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 \
${if simd then "-DWEBP_ENABLE_SIMD=1" else ""} \
-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;
};
};
packageVariants = forAllVariants { inherit builder variants; };
defaultPackage =
let
copyAllCodecs = lib.concatLines (
lib.mapAttrsToList (
name: _: "cp -r ${packageVariants."webp-squoosh-${name}"} $out/${name}"
) variants
);
in
stdenv.mkDerivation {
name = "all-variants";
phases = [ "buildPhase" ];
buildPhase = ''
mkdir -p $out;
${copyAllCodecs}
'';
};
in
mkRepoBinaryUpdater {
packages = packageVariants // {
default = defaultPackage;
};
}
);
}

View File

@@ -1,18 +0,0 @@
# 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

@@ -1,20 +0,0 @@
<!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

@@ -1,29 +0,0 @@
#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

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

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +0,0 @@
# 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

@@ -1,58 +0,0 @@
<!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

@@ -1,85 +0,0 @@
#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

@@ -1,42 +0,0 @@
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;

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,18 +0,0 @@
# 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

@@ -1,20 +0,0 @@
<!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

@@ -1,29 +0,0 @@
#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

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

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +0,0 @@
# 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

@@ -1,58 +0,0 @@
<!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

@@ -1,85 +0,0 @@
#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

@@ -1,42 +0,0 @@
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;

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,10 +0,0 @@
FROM nixos/nix:2.24.5
RUN <<EOF
cat >> /etc/nix/nix.conf << EOL
experimental-features = nix-command flakes
EOL
EOF
WORKDIR /app
CMD ["nix", "run", ".#updateRepoBinaries"]

View File

@@ -1,75 +0,0 @@
{
rustPlatform,
jq,
rsync,
lib,
stdenv,
# openssl,
pkg-config,
fenix,
}:
{
buildRustPackage =
{
target,
src,
cargoLock ? {
lockFile = "${src}/Cargo.lock";
},
release ? true,
...
}@args:
let
# Setup a toolchain for the the host system targeting `target`.
toolchain =
let
inherit (fenix) stable targets combine;
in
combine [
stable.rustc
stable.cargo
targets.${target}.stable.rust-std
];
# Create `vendor` folder with all dependencies.
vendoredDependencies = rustPlatform.importCargoLock cargoLock;
rustcTargetDir = "target/${target}/${if release then "release" else "debug"}";
in
stdenv.mkDerivation (
(removeAttrs args [ "cargoLock" ])
// {
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
toolchain
jq
rsync
pkg-config
# (lib.getDev openssl)
];
buildInputs = [
# openssl
];
dontConfigure = true;
buildPhase = ''
runHook preBuild
cargo build \
--config 'source.crates-io.replace-with="vendored-sources"' \
--config 'source.vendored-sources.directory="${vendoredDependencies}"' \
--offline \
--target ${target} ${if release then "-r" else ""}
runHook postBuild
'';
installPhase = ''
runHook preInstall;
mkdir -p $out
find ${rustcTargetDir} -type f -maxdepth 1 | \
xargs -I ___X -n1 cp ___X $out
runHook postInstall;
'';
}
);
}

View File

@@ -1,30 +0,0 @@
{
pkgs,
stdenv,
runCommand,
}:
{
name,
nativeBuildInputs ? [ ],
encoder ? "enc",
decoder ? "dec",
...
}@args:
stdenv.mkDerivation (
final:
args
// {
inherit name;
nativeBuildInputs = [ pkgs.emscripten ] ++ nativeBuildInputs;
buildPhase = ''
export HOME=$TMPDIR
emmake make -j$(nproc)
'';
installPhase = ''
mkdir -p $out
${if (encoder != null) then "cp -r ${encoder} $out" else ""}
${if (decoder != null) then "cp -r ${decoder} $out" else ""}
'';
}
)

View File

@@ -1,36 +0,0 @@
{
coreutils,
rsync,
writeShellScriptBin,
lib,
}:
let
suffixAttrNames =
suffix: attrs: lib.mapAttrs' (name: val: lib.nameValuePair "${name}${suffix}" val) attrs;
in
{
inherit suffixAttrNames;
mkRepoBinaryUpdater =
flake:
let
script = writeShellScriptBin "updateRepoBinaries.sh" ''
${coreutils}/bin/mkdir -p wasm_build
${rsync}/bin/rsync --chmod=u+w -r ${flake.packages.default}/* wasm_build/
'';
in
lib.recursiveUpdate flake {
apps.updateRepoBinaries = {
type = "app";
program = "${script}/bin/updateRepoBinaries.sh";
};
};
forAllVariants =
{ builder, variants }:
lib.lists.foldl (acc: v: acc // v) { } (
lib.mapAttrsToList (
variantName: value: suffixAttrNames "-${variantName}" (builder variantName value)
) variants
);
}

View File

@@ -1,78 +0,0 @@
{
pkgs,
fenix,
wasm-bindgen ? pkgs.callPackage (import ../wasm-bindgen) { },
rust-helpers ? pkgs.callPackage (import ../rust-helpers) { inherit fenix; },
binaryen,
stdenv,
}:
let
inherit (rust-helpers) buildRustPackage;
in
{
name,
src,
cargoLock ? {
lockFile = "${src}/Cargo.lock";
},
wasmBindgen ? {
sha256 = "";
},
...
}@args:
let
codecBuild = buildRustPackage {
inherit src cargoLock;
name = "${name}-codec";
target = "wasm32-unknown-unknown";
};
wasm-bindgen-bin = wasm-bindgen.buildFromCargoLock {
inherit cargoLock;
sha256 = wasmBindgen.sha256;
};
in
if wasmBindgen != null then
stdenv.mkDerivation (
(removeAttrs args [
"cargoLock"
"wasmBindgen"
])
// {
inherit codecBuild;
dontConfigure = true;
nativeBuildInputs = [ wasm-bindgen-bin ];
buildPhase = ''
runHook preBuild
wasm-bindgen --target web --out-dir $out $codecBuild/*.wasm
runHook postBuild
'';
dontInstall = true;
}
)
else
stdenv.mkDerivation (
(removeAttrs args [
"cargoLock"
"wasmBindgen"
])
// {
inherit codecBuild;
dontConfigure = true;
nativeBuildInputs = [ binaryen ];
buildPhase = ''
runHook preBuild
wasm-opt -O3 --strip -o optimized.wasm $codecBuild/*.wasm
runHook postBuild
'';
installPhase = ''
mkdir -p $out
cp optimized.wasm $out
'';
}
)

View File

@@ -1,59 +0,0 @@
{
lib,
fetchCrate,
rustPlatform,
curl,
stdenv,
openssl_1_1,
pkg-config,
darwin,
}:
rec {
build =
{ version, sha256 }:
let
src = fetchCrate {
pname = "wasm-bindgen-cli";
inherit version sha256;
};
cargoLock = {
lockFile = "${src}/Cargo.lock";
};
in
rustPlatform.buildRustPackage {
name = "wasm-bindgen-cli";
inherit src cargoLock;
nativeBuildInputs = [
pkg-config
];
buildInputs = [
openssl_1_1
] ++ lib.optionals stdenv.isDarwin [
darwin.apple_sdk.frameworks.IOKit
];
doCheck = false;
};
buildFromCargoLock =
{ cargoLock, sha256 }:
assert (cargoLock.lockFile or null == null) != (cargoLock.lockFileContents or null == null);
let
lockFileContents =
if cargoLock.lockFile != null then
builtins.readFile cargoLock.lockFile
else
cargoLock.lockFileContents;
parsedLockFile = builtins.fromTOML lockFileContents;
wasm-bindgen-version =
(lib.lists.findFirst (x: x.name == "wasm-bindgen") null parsedLockFile.package).version;
in
assert wasm-bindgen-version != null;
build {
inherit sha256;
version = wasm-bindgen-version;
};
}

252
package-lock.json generated
View File

@@ -12,7 +12,6 @@
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.1.0",
"@rollup/plugin-replace": "^2.3.4",
"@rollup/plugin-terser": "^0.4.4",
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
"@types/dedent": "^0.7.0",
"@types/mime-types": "^2.1.1",
@@ -150,70 +149,6 @@
"node": ">=4"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
@@ -310,39 +245,6 @@
"magic-string": "^0.25.7"
}
},
"node_modules/@rollup/plugin-terser": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"serialize-javascript": "^6.0.1",
"smob": "^1.0.0",
"terser": "^5.17.4"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
@@ -502,19 +404,6 @@
"node": ">= 0.6"
}
},
"node_modules/acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -871,11 +760,10 @@
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"node_modules/builtin-modules": {
"version": "3.2.0",
@@ -7996,13 +7884,6 @@
"node": ">=8"
}
},
"node_modules/smob": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
"dev": true,
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -8013,11 +7894,10 @@
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -8528,16 +8408,14 @@
}
},
"node_modules/terser": {
"version": "5.31.4",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.4.tgz",
"integrity": "sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.1.tgz",
"integrity": "sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
},
"bin": {
"terser": "bin/terser"
@@ -8878,55 +8756,6 @@
}
}
},
"@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"requires": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true
},
"@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true
},
"@jridgewell/source-map": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"dev": true,
"requires": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
@@ -9012,28 +8841,6 @@
"magic-string": "^0.25.7"
}
},
"@rollup/plugin-terser": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"dev": true,
"requires": {
"serialize-javascript": "^6.0.1",
"smob": "^1.0.0",
"terser": "^5.17.4"
},
"dependencies": {
"serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
}
}
}
},
"@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
@@ -9179,12 +8986,6 @@
"negotiator": "0.6.2"
}
},
"acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"dev": true
},
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -9473,9 +9274,9 @@
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"builtin-modules": {
@@ -15394,12 +15195,6 @@
"is-fullwidth-code-point": "^3.0.0"
}
},
"smob": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -15407,9 +15202,9 @@
"dev": true
},
"source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -15830,15 +15625,14 @@
}
},
"terser": {
"version": "5.31.4",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.4.tgz",
"integrity": "sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.1.tgz",
"integrity": "sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==",
"dev": true,
"requires": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
},
"dependencies": {
"commander": {

View File

@@ -15,7 +15,6 @@
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.1.0",
"@rollup/plugin-replace": "^2.3.4",
"@rollup/plugin-terser": "^0.4.4",
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
"@types/dedent": "^0.7.0",
"@types/mime-types": "^2.1.1",
@@ -44,10 +43,11 @@
"preact-render-to-string": "^5.1.11",
"prettier": "^2.4.1",
"rollup": "^2.38.0",
"rollup-plugin-terser": "^7.0.2",
"serve": "^11.3.2",
"typescript": "^4.4.4",
"wasm-feature-detect": "^1.2.11",
"which": "^2.0.2"
"which": "^2.0.2",
"wasm-feature-detect": "^1.2.11"
},
"lint-staged": {
"*.{js,css,json,md,ts,tsx}": "prettier --write",

View File

@@ -15,7 +15,7 @@ import { promises as fsp } from 'fs';
import del from 'del';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
import { terser } from 'rollup-plugin-terser';
import OMT from '@surma/rollup-plugin-off-main-thread';
import replace from '@rollup/plugin-replace';
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';

View File

@@ -10,14 +10,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { WebPModule } from 'codecs/webp/wasm_build/base/dec/webp_dec';
import type { WebPModule } from 'codecs/webp/dec/webp_dec';
import { initEmscriptenModule, blobToArrayBuffer } from 'features/worker-utils';
let emscriptenModule: Promise<WebPModule>;
export default async function decode(blob: Blob): Promise<ImageData> {
if (!emscriptenModule) {
const decoder = await import('codecs/webp/wasm_build/base/dec/webp_dec');
const decoder = await import('codecs/webp/dec/webp_dec');
emscriptenModule = initEmscriptenModule(decoder.default);
}

View File

@@ -13,7 +13,7 @@
import {
EncodeOptions,
MozJpegColorSpace,
} from 'codecs/mozjpeg/wasm_build/enc/mozjpeg_enc';
} from 'codecs/mozjpeg/enc/mozjpeg_enc';
export { EncodeOptions, MozJpegColorSpace };
export const label = 'MozJPEG';

View File

@@ -10,9 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mozjpeg_enc, {
MozJPEGModule,
} from 'codecs/mozjpeg/wasm_build/enc/mozjpeg_enc';
import mozjpeg_enc, { MozJPEGModule } from 'codecs/mozjpeg/enc/mozjpeg_enc';
import { EncodeOptions } from '../shared/meta';
import { initEmscriptenModule } from 'features/worker-utils';

View File

@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { EncodeOptions } from 'codecs/webp/wasm_build/base/enc/webp_enc';
import type { EncodeOptions } from 'codecs/webp/enc/webp_enc';
export { EncodeOptions };

View File

@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { WebPModule } from 'codecs/webp/wasm_build/base/enc/webp_enc';
import type { WebPModule } from 'codecs/webp/enc/webp_enc';
import type { EncodeOptions } from '../shared/meta';
import { initEmscriptenModule } from 'features/worker-utils';
@@ -20,12 +20,10 @@ let emscriptenModule: Promise<WebPModule>;
async function init() {
if (await simd()) {
const webpEncoder = await import(
'codecs/webp/wasm_build/simd/enc/webp_enc'
);
const webpEncoder = await import('codecs/webp/enc/webp_enc_simd');
return initEmscriptenModule(webpEncoder.default);
}
const webpEncoder = await import('codecs/webp/wasm_build/base/enc/webp_enc');
const webpEncoder = await import('codecs/webp/enc/webp_enc');
return initEmscriptenModule(webpEncoder.default);
}

View File

@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import wasmUrl from 'url:codecs/rotate/wasm_build/optimized.wasm';
import wasmUrl from 'url:codecs/rotate/rotate.wasm';
import { Options } from '../shared/meta';
export interface RotateModuleInstance {

View File

@@ -25,7 +25,7 @@ import * as featuresWorker from 'entry-data:../features-worker';
// Decoders (some are feature detected)
import * as avifDec from 'entry-data:codecs/avif/dec/avif_dec';
import * as webpDec from 'entry-data:codecs/webp/wasm_build/base/dec/webp_dec';
import * as webpDec from 'entry-data:codecs/webp/dec/webp_dec';
// AVIF
import * as avifEncMt from 'entry-data:codecs/avif/enc/avif_enc_mt';
@@ -41,8 +41,8 @@ import * as oxiMt from 'entry-data:codecs/oxipng/pkg-parallel/squoosh_oxipng';
import * as oxi from 'entry-data:codecs/oxipng/pkg/squoosh_oxipng';
// WebP
import * as webpEncSimd from 'entry-data:codecs/webp/wasm_build/simd/enc/webp_enc';
import * as webpEnc from 'entry-data:codecs/webp/wasm_build/base/enc/webp_enc';
import * as webpEncSimd from 'entry-data:codecs/webp/enc/webp_enc_simd';
import * as webpEnc from 'entry-data:codecs/webp/enc/webp_enc';
// WP2
import * as wp2EncMtSimd from 'entry-data:codecs/wp2/enc/wp2_enc_mt_simd';