Compare commits

...

38 Commits
dev ... nix

Author SHA1 Message Date
Surma
13ff0d5934 tmp 2024-11-26 11:44:40 +00:00
Surma
2287e4517e Move src to var 2024-09-08 17:41:54 +01:00
Surma
16b75e7919 Rename install script to repo binary updater 2024-09-08 17:29:56 +01:00
Surma
8a1c95b7dc Format everything 2024-09-08 13:18:41 +01:00
Surma
d895e0a6b6 Extract variant helper code 2024-09-08 13:18:15 +01:00
Surma
63441ef371 Make a variant builder helper 2024-09-08 12:59:17 +01:00
Surma
dfc9b36b6e Improve variant builder 2024-09-08 11:07:02 +01:00
Surma
aa7b284e65 Filter sources 2024-09-08 10:46:45 +01:00
Surma
24e63eafc6 Improve cpp nix builder 2024-09-07 14:15:49 +01:00
Surma
00082becab Introduce mkInstallable 2024-09-07 00:22:06 +01:00
Surma
fae5959392 Minor refactor 2024-09-06 20:04:42 +01:00
Surma
53fc922870 Port rotate codec to nix 2024-09-06 18:09:43 +01:00
Surma
4f93abb105 Update resize codec build 2024-09-06 17:54:54 +01:00
Surma
738d07d98a Move away from subflakes 2024-09-06 17:42:48 +01:00
Surma
7e564e240a Update mozjpeg flake 2024-09-06 15:00:10 +01:00
Surma
c1be464503 Remove cargo home variable 2024-08-14 15:15:17 +01:00
Surma
ee61b9fb18 Make rust library work 2024-08-14 15:12:06 +01:00
Surma
6d061d8fa2 WIP 2024-08-14 14:07:21 +01:00
Surma
d278efb148 Make cross compiling work with vendoring 2024-08-14 10:20:02 +01:00
Surma
e4cec57fe3 Properly install wasm-bindgen 2024-08-14 00:35:27 +01:00
Surma
a9c16df263 Properly build wasm-bindgen 2024-08-14 00:14:53 +01:00
Surma
15df976a8c Making wasm-bindgen work 2024-08-13 18:26:31 +01:00
Surma
9bbfac62e8 Trying to do a wasm-bindgen build 2024-08-08 00:28:21 +01:00
Surma
13cbf8ddb1 First attempt at rust 2024-08-07 23:40:38 +01:00
Surma
b5bd766a4e Build webp 2024-08-07 18:48:23 +01:00
Surma
a0f9fea679 Make webp compile 2024-08-07 16:05:03 +01:00
Surma
dd290accc1 Better caching 2024-08-07 15:08:13 +01:00
Surma
b19064702e Merge branch 'terser-plugin' into nix 2024-08-07 14:54:33 +01:00
Surma
3237a7e0ee Update Node version 2024-08-07 14:51:57 +01:00
Surma
c4cfc1d884 Replace deprecated terser plugin 2024-08-07 14:51:57 +01:00
Surma
ba01d36c20 Types were handwritten 2024-08-07 14:42:22 +01:00
Surma
8c76d43a68 Have a proper install script 2024-08-07 13:05:26 +01:00
Surma
c308108279 Simplify 2024-08-07 12:31:09 +01:00
Surma
2bd1eeceb8 Work on all systems 2024-08-07 10:34:05 +01:00
Surma
95a16e3919 nixfmt 2024-08-07 01:08:23 +01:00
Surma
ec2a05ec33 It finally compiles 2024-08-07 01:08:17 +01:00
Surma
11cd02c87b Slightly more progress 2024-08-07 00:50:25 +01:00
Surma
9ce5da7531 Attempt at Nix’ing mozjpeg 2024-08-06 18:30:17 +01:00
91 changed files with 2248 additions and 352 deletions

2
.nvmrc
View File

@@ -1 +1 @@
14.15.1
20.16.0

View File

@@ -15,6 +15,8 @@ However, Squoosh utilizes Google Analytics to collect the following:
# Developing
## Web App
To develop for Squoosh:
1. Clone the repository
@@ -31,8 +33,28 @@ 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 Normal file
View File

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

View File

@@ -1,61 +1,31 @@
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 enc/mozjpeg_node_enc.js dec/mozjpeg_node_dec.js
OUT_JS := enc/mozjpeg_enc.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
enc/mozjpeg_node_enc.js dec/mozjpeg_node_dec.js: ENVIRONMENT = node
%.js: $(CODEC_OUT)
%.js:
$(CXX) \
-I $(CODEC_DIR) \
${CXXFLAGS} \
${LDFLAGS} \
--bind \
-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 \
${CXXFLAGS} \
${LDFLAGS} \
-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

@@ -1,57 +0,0 @@
#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);
}

File diff suppressed because one or more lines are too long

View File

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

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

79
codecs/mozjpeg/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"
}
},
"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
}

92
codecs/mozjpeg/flake.nix Normal file
View File

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

View File

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

View File

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

View File

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

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

Binary file not shown.

115
codecs/resize/flake.lock generated Normal file
View File

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

57
codecs/resize/flake.nix Normal file
View File

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

View File

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

View File

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

View File

@@ -1,15 +0,0 @@
{
"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 Normal file
View File

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

54
codecs/rotate/flake.nix Normal file
View File

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

Binary file not shown.

Binary file not shown.

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,41 @@ 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) \
-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) \
--bind \
-lembind \
-s ENVIRONMENT=$(ENVIRONMENT) \
-s EXPORT_ES6=1 \
-o $@ \
$+
%.o: %.cpp $(CODEC_DIR)/CMakeLists.txt
%.o: %.cpp
$(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 "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
}

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

@@ -0,0 +1,130 @@
{
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

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

File diff suppressed because one or more lines are too long

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;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

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

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;

File diff suppressed because one or more lines are too long

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;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

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

10
nix/Dockerfile Normal file
View File

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

@@ -0,0 +1,75 @@
{
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

@@ -0,0 +1,30 @@
{
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

@@ -0,0 +1,36 @@
{
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

@@ -0,0 +1,78 @@
{
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

@@ -0,0 +1,59 @@
{
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,6 +12,7 @@
"@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",
@@ -149,6 +150,70 @@
"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",
@@ -245,6 +310,39 @@
"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",
@@ -404,6 +502,19 @@
"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",
@@ -760,10 +871,11 @@
}
},
"node_modules/buffer-from": {
"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
"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"
},
"node_modules/builtin-modules": {
"version": "3.2.0",
@@ -7884,6 +7996,13 @@
"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",
@@ -7894,10 +8013,11 @@
}
},
"node_modules/source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -8408,14 +8528,16 @@
}
},
"node_modules/terser": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.1.tgz",
"integrity": "sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==",
"version": "5.31.4",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.4.tgz",
"integrity": "sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
@@ -8756,6 +8878,55 @@
}
}
},
"@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",
@@ -8841,6 +9012,28 @@
"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",
@@ -8986,6 +9179,12 @@
"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",
@@ -9274,9 +9473,9 @@
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"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
},
"builtin-modules": {
@@ -15195,6 +15394,12 @@
"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",
@@ -15202,9 +15407,9 @@
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"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==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -15625,14 +15830,15 @@
}
},
"terser": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.1.tgz",
"integrity": "sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==",
"version": "5.31.4",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.4.tgz",
"integrity": "sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw==",
"dev": true,
"requires": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
"source-map-support": "~0.5.20"
},
"dependencies": {
"commander": {

View File

@@ -15,6 +15,7 @@
"@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",
@@ -43,11 +44,10 @@
"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",
"which": "^2.0.2",
"wasm-feature-detect": "^1.2.11"
"wasm-feature-detect": "^1.2.11",
"which": "^2.0.2"
},
"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/dec/webp_dec';
import type { WebPModule } from 'codecs/webp/wasm_build/base/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/dec/webp_dec');
const decoder = await import('codecs/webp/wasm_build/base/dec/webp_dec');
emscriptenModule = initEmscriptenModule(decoder.default);
}

View File

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

View File

@@ -10,7 +10,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mozjpeg_enc, { MozJPEGModule } from 'codecs/mozjpeg/enc/mozjpeg_enc';
import mozjpeg_enc, {
MozJPEGModule,
} from 'codecs/mozjpeg/wasm_build/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/enc/webp_enc';
import type { EncodeOptions } from 'codecs/webp/wasm_build/base/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/enc/webp_enc';
import type { WebPModule } from 'codecs/webp/wasm_build/base/enc/webp_enc';
import type { EncodeOptions } from '../shared/meta';
import { initEmscriptenModule } from 'features/worker-utils';
@@ -20,10 +20,12 @@ let emscriptenModule: Promise<WebPModule>;
async function init() {
if (await simd()) {
const webpEncoder = await import('codecs/webp/enc/webp_enc_simd');
const webpEncoder = await import(
'codecs/webp/wasm_build/simd/enc/webp_enc'
);
return initEmscriptenModule(webpEncoder.default);
}
const webpEncoder = await import('codecs/webp/enc/webp_enc');
const webpEncoder = await import('codecs/webp/wasm_build/base/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/rotate.wasm';
import wasmUrl from 'url:codecs/rotate/wasm_build/optimized.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/dec/webp_dec';
import * as webpDec from 'entry-data:codecs/webp/wasm_build/base/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/enc/webp_enc_simd';
import * as webpEnc from 'entry-data:codecs/webp/enc/webp_enc';
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';
// WP2
import * as wp2EncMtSimd from 'entry-data:codecs/wp2/enc/wp2_enc_mt_simd';