Compare commits

..

60 Commits

Author SHA1 Message Date
Jake Archibald
7346511fa1 Merge v2 codecs (#844)
* wip

* doh, whitespace

* Updating emscripten, restoring export name

* Updating oxipng

* Build wasm

* Fix oxipng; upgrade Rust

* More v2-codec integration

* AVIF now working

* Non-working JXL

* Build hqx with Rust 1.40; refactor build-rust*.sh

* Set web target

* wp2 wip

* wp2 decode options

* Better logo height when loading the logo into squoosh

* Build oxi

* JAKE IS AN IDIOT

* wip oxipng

* Fixing case sensitive imports

* adding log

* another log

* Abort tasks when compress component removed

* Adding progressive option to JXL

* Fix bug going to & from original image

* Exposing epf in jxl

* logs

* Bypass initial CSS plugin

* Revert "logs"

* Adding root

* Fix for finding TSC on Windows

* Use spawn again

* Converting to module paths

* Remove spawnP

* silly

* oops

* logs

* Fixing glob paths in CSS plugin

* Path normalising

* Normalise paths for CSS plugin

* Normalise again

* Use correct func

* Adding lossless mode and near lossless (but hidden in UI)

* Removing useless comments

* Some logging

* Update JXL to v0.1. (#846)

* Rebuild JXL

* Adding slight loss option

Co-authored-by: Ingvar Stepanyan <rreverser@google.com>
Co-authored-by: Luca Versari <veluca93@gmail.com>
2020-11-19 10:55:43 +00:00
Jake Archibald
39ca054112 Change case of dirs 2020-11-12 15:10:05 +00:00
Jake Archibald
1b80dd23ee Patching omt for the service worker 2020-11-12 14:00:10 +00:00
Jake Archibald
d1ee92e884 Inline initial script 2020-11-12 13:59:50 +00:00
Jake Archibald
f1da577ba3 Avoid double builds 2020-11-12 12:28:34 +00:00
Jake Archibald
52b3d0063f Avoid redundant empty file 2020-11-11 16:57:47 +00:00
Jake Archibald
eb9d0d186c Better checking of processor equivalence 2020-11-11 16:49:00 +00:00
Jake Archibald
964390501e Fix initial loading state 2020-11-11 16:16:24 +00:00
Jake Archibald
b412d4c2a5 Moving component instantiation to avoid preact removing it 2020-11-11 15:59:29 +00:00
Jake Archibald
7dfc9310ba This is wrong 2020-11-11 15:50:17 +00:00
Jake Archibald
b38069695a Quantizer bug fix 2020-11-11 15:35:17 +00:00
Jake Archibald
2d21406484 Removing old code, some bugfixes 2020-11-11 15:28:57 +00:00
Jake Archibald
56f9d4b8c8 Bug fixes 2020-11-11 14:49:39 +00:00
Jake Archibald
be4601b93a All options 2020-11-11 12:12:05 +00:00
Jake Archibald
196e6e1aea Adding output 2020-11-10 13:12:31 +00:00
Jake Archibald
6d0d9dc022 Clear loading states 2020-11-10 11:57:28 +00:00
Jake Archibald
324c2b6cab Compress module done (aside from imports) 2020-11-10 11:45:30 +00:00
Jake Archibald
b6fd14b6d3 Progress on single process pass 2020-11-09 18:02:18 +00:00
Jake Archibald
9111aa89ae More work on compress 2020-11-09 12:14:16 +00:00
Jake Archibald
c17b5c36c6 The client needs to be able to see types from the worker 2020-11-09 11:53:49 +00:00
Jake Archibald
4502abb069 Probably should include the client code in the client 2020-11-09 10:58:08 +00:00
Jake Archibald
5a027c7727 Much simpler project structure 2020-11-09 10:54:35 +00:00
Jake Archibald
4da1887826 Processor work 2020-11-09 10:29:25 +00:00
Jake Archibald
b1a639c182 Processors and encoding 2020-11-08 13:25:14 +00:00
Jake Archibald
6dbb182f7b Auto generate client tsconfig 2020-11-08 13:15:25 +00:00
Jake Archibald
a360191759 Standard shape for encoders 2020-11-08 12:01:33 +00:00
Jake Archibald
1cb1c16fa2 Refactor resize 2020-11-06 16:47:37 +00:00
Jake Archibald
ec586bb529 Structure for browser decoders. Some work on decoding. 2020-11-06 13:05:12 +00:00
Jake Archibald
4b8c0178fe Generating processor metadata 2020-11-06 10:25:26 +00:00
Jake Archibald
0d298e3e0a Getting things in to a buildable state 2020-10-07 15:29:15 +01:00
Jake Archibald
13c5b76af6 Adding oxi shared code 2020-10-07 14:29:23 +01:00
Jake Archibald
7836d7e97c Generating feature metadata 2020-09-29 17:56:19 +01:00
Jake Archibald
db2d6f4ca6 Fix worker bridge code 2020-09-29 16:44:58 +01:00
Jake Archibald
b11ae0b8c7 Missing args 2020-09-29 16:36:13 +01:00
Jake Archibald
81d0b38dbd CSS generate comment 2020-09-25 13:57:43 +01:00
Jake Archibald
b47d6b4696 Moving worker and worker bridge 2020-09-25 13:49:58 +01:00
Jake Archibald
f96ae9bdee wip 2020-09-25 13:04:39 +01:00
Jake Archibald
4d8efcea66 SW bits that make it good enough for now 2020-09-25 09:38:19 +01:00
Jake Archibald
e11b4cf22c Service worker building (but not quite right yet) 2020-09-24 17:35:02 +01:00
Jake Archibald
3f2466f44d Re-enable terser 2020-09-24 15:20:13 +01:00
Jake Archibald
778aa41f0d Prerender & client render of intro 2020-09-24 15:19:57 +01:00
Jake Archibald
486957443d Downgrade postcss for modules support 2020-09-24 13:30:53 +01:00
Jake Archibald
02e4eaf4b5 CSS plugin 2020-09-24 11:49:41 +01:00
Jake Archibald
f5d9023ff3 Update postcss 2020-09-23 14:44:13 +01:00
Jake Archibald
455c868e55 First bit of real UI code landed 2020-09-23 14:38:41 +01:00
Jake Archibald
6573103755 Add manifest 2020-09-23 10:58:50 +01:00
Jake Archibald
a30e38856e Avoid infinite rebuilds 2020-09-22 17:48:51 +01:00
Jake Archibald
d2807ebb18 Integrate Oxi 2020-09-22 17:35:47 +01:00
Jake Archibald
fd151fc70d Add hqx 2020-09-22 17:16:55 +01:00
Jake Archibald
812e727de0 Integrate rotate 2020-09-22 14:40:27 +01:00
Jake Archibald
2a6a83f56d Resize working 2020-09-21 15:19:38 +01:00
Jake Archibald
21fc70cbdd wip 2020-09-21 14:25:08 +01:00
Jake Archibald
d9e1177cd8 wip on rust wasm integration 2020-09-21 14:23:04 +01:00
Jake Archibald
300809fdcb Client/shared/worker split for resize 2020-09-21 13:01:07 +01:00
Jake Archibald
7540a15f8d Features folder 2020-09-18 16:28:56 +01:00
Jake Archibald
f92e3c2194 Move encode options definition to the wasm 2020-09-16 12:30:26 +01:00
Jake Archibald
7776134bc2 AVIF in worker 2020-09-16 11:48:13 +01:00
Jake Archibald
2583d689b9 AVIF to module 2020-09-16 11:04:00 +01:00
Jake Archibald
25102095aa Update webp from main branch 2020-09-16 11:03:19 +01:00
Jake Archibald
a6477b82fc wip
# Conflicts:
#	codecs/cpp.Dockerfile
#	codecs/imagequant/example.html
#	codecs/webp/dec/webp_dec.d.ts
#	codecs/webp/dec/webp_dec.js
#	codecs/webp/dec/webp_dec.wasm
#	codecs/webp/enc/webp_enc.d.ts
#	codecs/webp/enc/webp_enc.js
#	codecs/webp/enc/webp_enc.wasm
#	package-lock.json
#	package.json
#	src/codecs/tiny.webp
#	src_old/codecs/encoders.ts
#	src_old/codecs/processor-worker/tiny.avif
#	src_old/codecs/processor-worker/tiny.webp
#	src_old/codecs/tiny.webp
#	src_old/components/compress/index.tsx
#	src_old/lib/util.ts
#	src_old/sw/util.ts
2020-09-16 10:08:50 +01:00
46 changed files with 8695 additions and 1077 deletions

2
.gitattributes vendored
View File

@@ -1,2 +0,0 @@
/codecs/**/*.js linguist-generated=true
/codecs/*/pkg*/*.d.ts linguist-generated=true

View File

@@ -1,22 +0,0 @@
name: Node.js CI
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- id: nvmrc
uses: browniebroke/read-nvmrc-action@v1
- uses: actions/setup-node@v1
with:
node-version: '${{ steps.nvmrc.outputs.node_version }}'
- run: npm ci
- run: npm run build

2
.nvmrc
View File

@@ -1 +1 @@
14.15.1
12.18.3

View File

@@ -1,12 +0,0 @@
codecs
.tmp
node_modules
*.scss.d.ts
*.css.d.ts
build
*.o
# Auto-generated by lib/feature-plugin.js
src/features-worker/index.ts
src/client/lazy-app/worker-bridge/meta.ts
src/client/lazy-app/feature-meta/index.ts

7
.travis.yml Normal file
View File

@@ -0,0 +1,7 @@
language: node_js
cache: npm
script: npm run build
after_success: npm run sizereport
os:
- linux
- windows

0
codecs/avif/enc/avif_enc_mt.wasm Normal file → Executable file
View File

View File

@@ -14,33 +14,12 @@ thread_local const val ImageData = val::global("ImageData");
// R, G, B, A
#define COMPONENTS_PER_PIXEL 4
#ifndef JXL_DEBUG_ON_ALL_ERROR
#define JXL_DEBUG_ON_ALL_ERROR 0
#endif
#if JXL_DEBUG_ON_ALL_ERROR
#define EXPECT_TRUE(a) \
if (!(a)) { \
fprintf(stderr, "Assertion failure (%d): %s\n", __LINE__, #a); \
return val::null(); \
}
#define EXPECT_EQ(a, b) \
{ \
int a_ = a; \
int b_ = b; \
if (a_ != b_) { \
fprintf(stderr, "Assertion failure (%d): %s (%d) != %s (%d)\n", __LINE__, #a, a_, #b, b_); \
return val::null(); \
} \
}
#else
#define EXPECT_TRUE(a) \
if (!(a)) { \
return val::null(); \
}
#define EXPECT_EQ(a, b) EXPECT_TRUE((a) == (b));
#endif
val decode(std::string data) {
std::unique_ptr<JxlDecoder,

Binary file not shown.

View File

@@ -38,14 +38,7 @@ val encode(std::string image, int width, int height, JXLOptions options) {
// Reduce memory usage of tree learning for lossless data.
// TODO(veluca93): this is a mitigation for excessive memory usage in the JXL encoder.
float megapixels = width * height * 0.000001;
if (megapixels > 8) {
cparams.options.nb_repeats = 0.1;
} else if (megapixels > 4) {
cparams.options.nb_repeats = 0.3;
} else {
// default is OK.
}
cparams.options.nb_repeats = 0.1;
float quality = options.quality;
@@ -66,10 +59,8 @@ val encode(std::string image, int width, int height, JXLOptions options) {
if (options.progressive) {
cparams.qprogressive_mode = true;
cparams.progressive_dc = 1;
cparams.responsive = 1;
if (!cparams.modular_mode) {
cparams.progressive_dc = 1;
}
}
if (cparams.modular_mode) {

Binary file not shown.

View File

@@ -247,18 +247,18 @@ checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "libdeflate-sys"
version = "0.6.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f5b1582a0ebf8c55a46166c04d7c66f6bb17add3a6cbf69a082ac2219f31671"
checksum = "21e39efa87b84db3e13ff4e2dfac1e57220abcbd7fe8ec44d238f7f4f787cc1f"
dependencies = [
"cc",
]
[[package]]
name = "libdeflater"
version = "0.6.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93edd93a53970951da84ef733a8b6e30189a8f8a9e19610f69e4cc5bb1f4d654"
checksum = "a4810980d791f26d470e2d7d91a3d4d22aa3a4b709fb7e9c5e43ee54f83a01f2"
dependencies = [
"libdeflate-sys",
]
@@ -359,9 +359,9 @@ checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "oxipng"
version = "4.0.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fefb26bde273c3db896a313151301a69e698a7495ee577fe2168ed7065c29c4"
checksum = "ea40b366cecfce76ee3b082e7e6567b82cdef75644a22442ca8584bc666ff4eb"
dependencies = [
"bit-vec",
"byteorder",

4
codecs/oxipng/build.sh Normal file → Executable file
View File

@@ -3,8 +3,6 @@
set -e
rm -rf pkg,{-parallel}
wasm-pack build -t web
wasm-pack build --target web
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory' wasm-pack build -t web -d pkg-parallel -- -Z build-std=panic_abort,std --features=parallel
# Workaround https://github.com/rustwasm/wasm-bindgen/issues/2133:
sed -i "s|maybe_memory:|maybe_memory?:|" pkg-parallel/squoosh_oxipng.d.ts
rm pkg{,-parallel}/.gitignore

View File

@@ -1,6 +1,6 @@
{
"name": "oxipng",
"scripts": {
"build": "RUST_IMG=rustlang/rust@sha256:744aeea5a38f95aa7a96ec37269a65f0c6197a1cdd87d6534e12bb869141d807 ../build-rust.sh ./build.sh"
"build": "RUST_IMG=rustlang/rust:8bb115b1090d ../build-rust.sh ./build.sh"
}
}

View File

@@ -1,24 +1,29 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data: Uint8Array, level: number): Uint8Array;
/**
* @param {number} num
* @returns {any}
*/
* @param {number} num
* @returns {any}
*/
export function worker_initializer(num: number): any;
/**
*/
*/
export function start_main_thread(): void;
/**
*/
*/
export function start_worker_thread(): void;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export interface InitOutput {
readonly optimise: (a: number, b: number, c: number, d: number) => void;
@@ -34,13 +39,15 @@ export interface InitOutput {
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
* @param {WebAssembly.Memory} maybe_memory
*
* @returns {Promise<InitOutput>}
*/
export default function init (module_or_path?: InitInput | Promise<InitInput>, maybe_memory?: WebAssembly.Memory): Promise<InitOutput>;
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
* @param {WebAssembly.Memory} maybe_memory
*
* @returns {Promise<InitOutput>}
*/
export default function init(
module_or_path?: InitInput | Promise<InitInput>,
maybe_memory?: WebAssembly.Memory,
): Promise<InitOutput>;

View File

@@ -1,4 +1,3 @@
let wasm;
let memory;
@@ -9,172 +8,189 @@ heap.push(undefined, null, true, false);
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
heap[idx] = obj;
return idx;
}
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
let cachedTextDecoder = new TextDecoder('utf-8', {
ignoreBOM: true,
fatal: true,
});
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.__wbindgen_export_0.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetUint8Memory0;
if (
cachegetUint8Memory0 === null ||
cachegetUint8Memory0.buffer !== wasm.__wbindgen_export_0.buffer
) {
cachegetUint8Memory0 = new Uint8Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().slice(ptr, ptr + len));
return cachedTextDecoder.decode(getUint8Memory0().slice(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.__wbindgen_export_0.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetInt32Memory0;
if (
cachegetInt32Memory0 === null ||
cachegetInt32Memory0.buffer !== wasm.__wbindgen_export_0.buffer
) {
cachegetInt32Memory0 = new Int32Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data, level) {
try {
const retptr = wasm.__wbindgen_export_1.value - 16;
wasm.__wbindgen_export_1.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_1.value += 16;
}
try {
const retptr = wasm.__wbindgen_export_1.value - 16;
wasm.__wbindgen_export_1.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_1.value += 16;
}
}
function getObject(idx) { return heap[idx]; }
function getObject(idx) {
return heap[idx];
}
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
const ret = getObject(idx);
dropObject(idx);
return ret;
}
/**
* @param {number} num
* @returns {any}
*/
* @param {number} num
* @returns {any}
*/
export function worker_initializer(num) {
var ret = wasm.worker_initializer(num);
return takeObject(ret);
var ret = wasm.worker_initializer(num);
return takeObject(ret);
}
/**
*/
*/
export function start_main_thread() {
wasm.start_main_thread();
wasm.start_main_thread();
}
/**
*/
*/
export function start_worker_thread() {
wasm.start_worker_thread();
wasm.start_worker_thread();
}
async function load(module, imports, maybe_memory) {
if (typeof Response === 'function' && module instanceof Response) {
memory = imports.wbg.memory = new WebAssembly.Memory({initial:17,maximum:16384,shared:true});
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
memory = imports.wbg.memory = maybe_memory;
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
if (typeof Response === 'function' && module instanceof Response) {
memory = imports.wbg.memory = new WebAssembly.Memory({
initial: 17,
maximum: 16384,
shared: true,
});
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn(
'`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n',
e,
);
} else {
return instance;
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
memory = imports.wbg.memory = maybe_memory;
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input, maybe_memory) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_module = function() {
var ret = init.__wbindgen_wasm_module;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_memory = function() {
var ret = wasm.__wbindgen_export_0;
return addHeapObject(ret);
};
imports.wbg.__wbg_of_6510501edc06d65e = function(arg0, arg1) {
var ret = Array.of(takeObject(arg0), takeObject(arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_module = function () {
var ret = init.__wbindgen_wasm_module;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_memory = function () {
var ret = wasm.__wbindgen_export_0;
return addHeapObject(ret);
};
imports.wbg.__wbg_of_6510501edc06d65e = function (arg0, arg1) {
var ret = Array.of(takeObject(arg0), takeObject(arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_throw = function (arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
if (
typeof input === 'string' ||
(typeof Request === 'function' && input instanceof Request) ||
(typeof URL === 'function' && input instanceof URL)
) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports, maybe_memory);
const { instance, module } = await load(await input, imports, maybe_memory);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
wasm.__wbindgen_start();
return wasm;
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
wasm.__wbindgen_start();
return wasm;
}
export default init;

View File

@@ -1,13 +1,18 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data: Uint8Array, level: number): Uint8Array;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
@@ -19,12 +24,13 @@ export interface InitOutput {
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init(
module_or_path?: InitInput | Promise<InitInput>,
): Promise<InitOutput>;

View File

@@ -1,118 +1,126 @@
let wasm;
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
let cachedTextDecoder = new TextDecoder('utf-8', {
ignoreBOM: true,
fatal: true,
});
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
if (
cachegetUint8Memory0 === null ||
cachegetUint8Memory0.buffer !== wasm.memory.buffer
) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
if (
cachegetInt32Memory0 === null ||
cachegetInt32Memory0.buffer !== wasm.memory.buffer
) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data, level) {
try {
const retptr = wasm.__wbindgen_export_0.value - 16;
wasm.__wbindgen_export_0.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_0.value += 16;
}
try {
const retptr = wasm.__wbindgen_export_0.value - 16;
wasm.__wbindgen_export_0.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_0.value += 16;
}
}
async function load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn(
'`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n',
e,
);
} else {
return instance;
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_throw = function (arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
if (
typeof input === 'string' ||
(typeof Request === 'function' && input instanceof Request) ||
(typeof URL === 'function' && input instanceof URL)
) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports);
const { instance, module } = await load(await input, imports);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
return wasm;
return wasm;
}
export default init;

View File

@@ -1,5 +1,5 @@
use oxipng::AlphaOptim;
use wasm_bindgen::prelude::*;
use oxipng::AlphaOptim;
mod malloc_shim;
@@ -8,14 +8,14 @@ pub mod parallel;
#[wasm_bindgen]
pub fn optimise(data: &[u8], level: u8) -> Vec<u8> {
let mut options = oxipng::Options::from_preset(level);
options.alphas.insert(AlphaOptim::Black);
options.alphas.insert(AlphaOptim::White);
options.alphas.insert(AlphaOptim::Up);
options.alphas.insert(AlphaOptim::Down);
options.alphas.insert(AlphaOptim::Left);
options.alphas.insert(AlphaOptim::Right);
let mut options = oxipng::Options::from_preset(level);
options.alphas.insert(AlphaOptim::Black);
options.alphas.insert(AlphaOptim::White);
options.alphas.insert(AlphaOptim::Up);
options.alphas.insert(AlphaOptim::Down);
options.alphas.insert(AlphaOptim::Left);
options.alphas.insert(AlphaOptim::Right);
options.deflate = oxipng::Deflaters::Libdeflater;
oxipng::optimize_from_memory(data, &options).unwrap_throw()
options.deflate = oxipng::Deflaters::Libdeflater;
oxipng::optimize_from_memory(data, &options).unwrap_throw()
}

View File

@@ -1,4 +1,4 @@
use crossbeam_channel::{bounded, Receiver, Sender};
use crossbeam_channel::{Sender, Receiver, bounded};
use once_cell::sync::OnceCell;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
@@ -35,8 +35,7 @@ extern "C" {
// shared memory and blocks the current thread until they're all grabbed.
// 4) Provide a `worker_initializer` that is expected to be invoked from various workers,
// reads one `threadPtr` from the shared channel and starts running it.
static CHANNEL: OnceCell<(Sender<rayon::ThreadBuilder>, Receiver<rayon::ThreadBuilder>)> =
OnceCell::new();
static CHANNEL: OnceCell<(Sender<rayon::ThreadBuilder>, Receiver<rayon::ThreadBuilder>)> = OnceCell::new();
#[wasm_bindgen]
pub fn worker_initializer(num: usize) -> JsValue {

View File

@@ -7,7 +7,7 @@ RUN wget -qO- https://github.com/rustwasm/wasm-pack/releases/download/v0.9.1/was
FROM $RUST_IMG AS rust
ARG RUST_IMG
RUN rustup target add wasm32-unknown-unknown
RUN case $RUST_IMG in rustlang/rust@*) rustup component add rust-src; esac
RUN if [[ $RUST_IMG = rustlang/rust:* ]] ; then rustup component add rust-src ; fi
COPY --from=wasm-tools /emsdk/upstream/bin/wasm-opt /emsdk/upstream/bin/clang /usr/local/bin/
COPY --from=wasm-tools /emsdk/upstream/lib/ /usr/local/lib/
COPY --from=wasm-tools /emsdk/upstream/emscripten/system/include/libc/ /wasm32/include/

View File

@@ -17,10 +17,7 @@ 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();
return rgba ? ImageData.new_(Uint8ClampedArray.new_(typed_memory_view(width * height * 4, rgba.get())), width, height) : val::null();
}
EMSCRIPTEN_BINDINGS(my_module) {

View File

@@ -7,31 +7,7 @@ using namespace emscripten;
thread_local const val Uint8Array = val::global("Uint8Array");
struct WP2Options {
float quality;
float alpha_quality;
int speed;
int pass;
int uv_mode;
float sns;
int csp_type;
int error_diffusion;
bool use_random_matrix;
};
val encode(std::string image_in, int image_width, int image_height, WP2Options options) {
WP2::EncoderConfig config = {};
config.quality = options.quality;
config.alpha_quality = options.alpha_quality;
config.speed = options.speed;
config.pass = options.pass;
config.uv_mode = static_cast<WP2::EncoderConfig::UVMode>(options.uv_mode);
config.csp_type = static_cast<WP2::Csp>(options.csp_type);
config.sns = options.sns;
config.error_diffusion = options.error_diffusion;
config.use_random_matrix = options.use_random_matrix;
val encode(std::string image_in, int image_width, int image_height, WP2::EncoderConfig config) {
uint8_t* image_buffer = (uint8_t*)image_in.c_str();
WP2::ArgbBuffer src = WP2::ArgbBuffer();
WP2Status status =
@@ -51,16 +27,12 @@ val encode(std::string image_in, int image_width, int image_height, WP2Options o
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<WP2Options>("WP2Options")
.field("quality", &WP2Options::quality)
.field("alpha_quality", &WP2Options::alpha_quality)
.field("speed", &WP2Options::speed)
.field("pass", &WP2Options::pass)
.field("uv_mode", &WP2Options::uv_mode)
.field("csp_type", &WP2Options::csp_type)
.field("error_diffusion", &WP2Options::error_diffusion)
.field("use_random_matrix", &WP2Options::use_random_matrix)
.field("sns", &WP2Options::sns);
value_object<WP2::EncoderConfig>("WP2EncoderConfig")
.field("quality", &WP2::EncoderConfig::quality)
.field("alpha_quality", &WP2::EncoderConfig::alpha_quality)
.field("speed", &WP2::EncoderConfig::speed)
.field("pass", &WP2::EncoderConfig::pass)
.field("sns", &WP2::EncoderConfig::sns);
function("encode", &encode);
}

View File

@@ -4,24 +4,6 @@ export interface EncodeOptions {
speed: number;
pass: number;
sns: number;
uv_mode: UVMode;
csp_type: Csp;
error_diffusion: number;
use_random_matrix: boolean;
}
export const enum UVMode {
UVModeAdapt = 0, // Mix of 420 and 444 (per block)
UVMode420, // All blocks 420
UVMode444, // All blocks 444
UVModeAuto, // Choose any of the above automatically
}
export const enum Csp {
kYCoCg,
kYCbCr,
kCustom,
kYIQ,
}
export interface WP2Module extends EmscriptenWasm.Module {

View File

@@ -576,7 +576,7 @@ var wp2_enc = (function () {
},
});
var ob = {
t: function (a, b, c, d) {
p: function (a, b, c, d) {
A(
'Assertion failed: ' +
C(a) +
@@ -807,7 +807,7 @@ var wp2_enc = (function () {
return [];
});
},
d: function (a, b, c, d, e) {
c: function (a, b, c, d, e) {
function g(l) {
return l;
}
@@ -1000,10 +1000,10 @@ var wp2_enc = (function () {
},
});
},
p: function (a, b, c, d, e, g) {
q: function (a, b, c, d, e, g) {
Fa[a] = { name: U(b), da: Y(c, d), ea: Y(e, g), U: [] };
},
c: function (a, b, c, d, e, g, m, h, k, l) {
e: function (a, b, c, d, e, g, m, h, k, l) {
Fa[a].U.push({
X: U(b),
aa: c,
@@ -1034,7 +1034,7 @@ var wp2_enc = (function () {
m: function (a) {
4 < a && (X[a].T += 1);
},
q: function (a, b, c, d) {
t: function (a, b, c, d) {
a || W('Cannot use deleted val. handle = ' + a);
a = X[a].value;
var e = ib[b];
@@ -1079,7 +1079,7 @@ var wp2_enc = (function () {
u: function (a, b, c) {
D.copyWithin(a, b, b + c);
},
e: function (a) {
d: function (a) {
a >>>= 0;
var b = D.length;
if (2147483648 < a) return !1;

Binary file not shown.

View File

@@ -11,7 +11,6 @@
* limitations under the License.
*/
import rollup from 'rollup';
import * as path from 'path';
const prefix = 'client-bundle:';
const entryPathPlaceholder = 'CLIENT_BUNDLE_PLUGIN_ENTRY_PATH';
@@ -120,10 +119,9 @@ export default function (inputOptions, outputOptions, resolveFileUrl) {
return;
}
const id = path.normalize(entryPointPlaceholderMap.get(num));
const id = entryPointPlaceholderMap.get(num);
const clientEntry = clientOutput.find(
(item) =>
item.facadeModuleId && path.normalize(item.facadeModuleId) === id,
(item) => item.facadeModuleId === id,
);
if (property.startsWith(entryPathPlaceholder)) {

View File

@@ -18,8 +18,6 @@ import {
resolve as resolvePath,
dirname,
normalize as nomalizePath,
sep as pathSep,
posix,
} from 'path';
import postcss from 'postcss';
@@ -174,15 +172,14 @@ export default function (resolveFileUrl) {
return `export default ${cssStr};`;
}
if (id.startsWith(addPrefix)) {
const path = nomalizePath(id.slice(addPrefix.length));
const path = id.slice(addPrefix.length);
return (
`import css from ${JSON.stringify('css:' + path)};\n` +
`import css from 'css:${path}';\n` +
`import appendCss from '${appendCssModule}';\n` +
`appendCss(css);\n`
);
}
if (id.endsWith(moduleSuffix)) {
const path = nomalizePath(id.slice(0, -moduleSuffix.length));
if (!pathToResult.has(id)) {
throw Error(`Cannot find ${id} in pathToResult`);
}

View File

@@ -19,16 +19,15 @@ import glob from 'glob';
const globP = promisify(glob);
const moduleId = 'initial-css:';
const initialCssModule = '\0initialCss';
export default function initialCssPlugin() {
return {
name: 'initial-css-plugin',
resolveId(id) {
if (id === moduleId) return initialCssModule;
if (id === moduleId) return moduleId;
},
async load(id) {
if (id !== initialCssModule) return;
if (id !== moduleId) return;
const matches = await globP('shared/initial-app/**/*.css', {
nodir: true,

View File

@@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { posix as pathUtils, isAbsolute } from 'path';
import { posix as pathUtils } from 'path';
export default function resolveDirs(paths) {
const pathBaseDir = paths.map((path) => [
@@ -30,7 +30,6 @@ export default function resolveDirs(paths) {
if (!resolveResult) {
throw new Error(`Couldn't find ${'./' + id}`);
}
if (isAbsolute(resolveResult.id)) return resolveResult.id;
return pathUtils.resolve(resolveResult.id);
},
};

View File

@@ -127,6 +127,15 @@ export default function simpleTS(mainPath, { noBuild, watch } = {}) {
relative(process.cwd(), id),
).replace(extRe, '.js');
console.log(
`simple-ts mapping`,
id,
'to',
newId,
'with outDir',
config.options.outDir,
);
return fsp.readFile(newId, { encoding: 'utf8' });
},
};

8538
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,8 @@
"scripts": {
"build": "rollup -c && node lib/move-output.js",
"debug": "node --inspect-brk node_modules/.bin/rollup -c",
"dev": "run-p watch serve",
"watch": "rollup -cw",
"serve": "serve --config ../../../serve.json .tmp/build/static"
"dev": "rollup -cw & npm run serve",
"serve": "serve --config server.json .tmp/build/static"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^15.1.0",
@@ -26,7 +25,6 @@
"lint-staged": "^10.5.1",
"lodash.camelcase": "^4.3.0",
"mime-types": "^2.1.27",
"npm-run-all": "^4.1.5",
"pointer-tracker": "^2.4.0",
"postcss": "^7.0.35",
"postcss-modules": "^3.2.2",
@@ -49,9 +47,9 @@
}
},
"lint-staged": {
"*.{js,css,json,md,ts,tsx}": "prettier --write",
"*.{c,h,cpp,hpp}": "clang-format -i",
"*.rs": "rustfmt"
"*.{js,css,json,md,ts,tsx}": [
"prettier --write"
]
},
"dependencies": {
"wasm-feature-detect": "^1.2.9"

View File

@@ -52,7 +52,7 @@ function jsFileName(chunkInfo) {
const parsedPath = path.parse(chunkInfo.facadeModuleId);
if (parsedPath.name !== 'index') return jsPath;
// Come up with a better name than 'index'
const name = parsedPath.dir.split(/\\|\//).slice(-1);
const name = parsedPath.dir.split('/').slice(-1);
return jsPath.replace('[name]', name);
}

View File

@@ -9,6 +9,5 @@
}
]
}
],
"redirects": [{ "source": "/editor", "destination": "/" }]
]
}

View File

@@ -67,7 +67,7 @@ export default class App extends Component<Props, State> {
// really breaks things on Squoosh, as you can easily end up zooming the UI when you mean to
// zoom the image. Once you've done this, it's really difficult to undo. Anyway, this seems to
// prevent it.
document.body.addEventListener('gesturestart', (event: any) => {
document.body.addEventListener('gesturestart', (event) => {
event.preventDefault();
});
@@ -115,7 +115,11 @@ export default class App extends Component<Props, State> {
return (
<div class={style.app}>
<file-drop onfiledrop={this.onFileDrop} class={style.drop}>
<file-drop
accept="image/*"
onfiledrop={this.onFileDrop}
class={style.drop}
>
{showSpinner ? (
<loading-spinner class={style.appLoader} />
) : isEditorOpen ? (

View File

@@ -5,7 +5,6 @@ import 'add-css:./style.css';
import { cleanSet, cleanMerge } from '../../util/clean-modify';
import type { SourceImage, OutputType } from '..';
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
import {
EncoderOptions,
EncoderState,
@@ -19,10 +18,7 @@ import Select from './Select';
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
import { generateCliInvocation } from '../../util/cli-invocation-generator';
interface Props {
showSnack: SnackBarElement['showSnackbar'];
mobileView: boolean;
source?: SourceImage;
encoderState?: EncoderState;
@@ -101,22 +97,6 @@ export default class Options extends Component<Props, State> {
);
};
private onCreateCLIInvocation = () => {
if (!this.props.encoderState) {
return;
}
try {
const cliInvocation = generateCliInvocation(
this.props.encoderState,
this.props.processorState,
);
navigator.clipboard.writeText(cliInvocation);
} catch (e) {
this.props.showSnack(e);
}
};
render(
{ source, encoderState, processorState, onEncoderOptionsChange }: Props,
{ supportedEncoderMap }: State,
@@ -130,9 +110,7 @@ export default class Options extends Component<Props, State> {
<Expander>
{!encoderState ? null : (
<div>
<h3 class={style.optionsTitle}>
<button onClick={this.onCreateCLIInvocation}>CLI</button>Edit
</h3>
<h3 class={style.optionsTitle}>Edit</h3>
<label class={style.sectionEnabler}>
<Checkbox
name="resize.enable"

View File

@@ -649,7 +649,6 @@ export default class Compress extends Component<Props, State> {
});
} catch (err) {
if (err.name === 'AbortError') return;
this.setState({ loading: false });
this.props.showSnack(`Preprocessing error: ${err}`);
throw err;
}
@@ -773,12 +772,6 @@ export default class Compress extends Component<Props, State> {
this.activeSideJobs[sideIndex] = undefined;
} catch (err) {
if (err.name === 'AbortError') return;
this.setState((currentState) => {
const sides = cleanMerge(currentState.sides, sideIndex, {
loading: false,
});
return { sides };
});
this.props.showSnack(`Processing error: ${err}`);
throw err;
}
@@ -786,7 +779,7 @@ export default class Compress extends Component<Props, State> {
}
render(
{ onBack, showSnack }: Props,
{ onBack }: Props,
{ loading, sides, source, mobileView, preprocessorState }: State,
) {
const [leftSide, rightSide] = sides;
@@ -794,7 +787,6 @@ export default class Compress extends Component<Props, State> {
const options = sides.map((side, index) => (
<Options
showSnack={showSnack}
source={source}
mobileView={mobileView}
processorState={side.latestSettings.processorState}

View File

@@ -1,51 +0,0 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EncoderState, ProcessorState } from '../feature-meta';
// Maps our encoder.type values to CLI parameter names
const typeMap = new Map<string, string>([
['avif', '--avif'],
["jxl", "--jxl"],
['mozJPEG', '--mozjpeg'],
['oxiPNG', '--oxipng'],
['webP', '--webp'],
["wp2", "--wp2"],
]);
// Same as JSON.stringify, but with single quotes around the entire value
// so that shells dont do weird stuff.
function cliJson<T>(v: T): string {
return "'" + JSON.stringify(v) + "'";
}
export function generateCliInvocation(
encoder: EncoderState,
processor: ProcessorState,
): string {
if (!typeMap.has(encoder.type)) {
throw Error(`Encoder ${encoder.type} is unsupported in the CLI`);
}
return [
'npx',
'@squoosh/cli',
...(processor.resize.enabled
? ['--resize', cliJson(processor.resize)]
: []),
...(processor.quantize.enabled
? ['--quant', cliJson(processor.quantize)]
: []),
typeMap.get(encoder.type)!,
cliJson(encoder.options),
].join(' ');
}

View File

@@ -315,9 +315,9 @@ export class Options extends Component<Props, State> {
value={subsample}
onChange={this._inputChange('subsample', 'number')}
>
<option value="1">Half</option>
<option value="1">4:2:0</option>
{/*<option value="2">4:2:2</option>*/}
<option value="3">Off</option>
<option value="3">4:4:4</option>
</Select>
</label>
)}

View File

@@ -27,7 +27,6 @@ interface State {
edgePreservingFilter: number;
lossless: boolean;
slightLoss: boolean;
autoEdgePreservingFilter: boolean;
}
const maxSpeed = 7;
@@ -49,10 +48,9 @@ export class Options extends Component<Props, State> {
effort: maxSpeed - options.speed,
quality: options.quality,
progressive: options.progressive,
edgePreservingFilter: options.epf === -1 ? 2 : options.epf,
edgePreservingFilter: options.epf,
lossless: options.quality === 100,
slightLoss: options.lossyPalette,
autoEdgePreservingFilter: options.epf === -1,
};
}
@@ -88,9 +86,7 @@ export class Options extends Component<Props, State> {
speed: maxSpeed - optionState.effort,
quality: optionState.lossless ? 100 : optionState.quality,
progressive: optionState.progressive,
epf: optionState.autoEdgePreservingFilter
? -1
: optionState.edgePreservingFilter,
epf: optionState.edgePreservingFilter,
nearLossless: 0,
lossyPalette: optionState.lossless ? optionState.slightLoss : false,
};
@@ -116,7 +112,6 @@ export class Options extends Component<Props, State> {
edgePreservingFilter,
lossless,
slightLoss,
autoEdgePreservingFilter,
}: State,
) {
// I'm rendering both lossy and lossless forms, as it becomes much easier when
@@ -157,34 +152,16 @@ export class Options extends Component<Props, State> {
Quality:
</Range>
</div>
<label class={style.optionInputFirst}>
<Checkbox
name="autoEdgeFilter"
checked={autoEdgePreservingFilter}
onChange={this._inputChange(
'autoEdgePreservingFilter',
'boolean',
)}
/>
Auto edge filter
</label>
<Expander>
{!autoEdgePreservingFilter && (
<div class={style.optionOneCell}>
<Range
min="0"
max="3"
value={edgePreservingFilter}
onInput={this._inputChange(
'edgePreservingFilter',
'number',
)}
>
Edge preserving filter:
</Range>
</div>
)}
</Expander>
<div class={style.optionOneCell}>
<Range
min="0"
max="3"
value={edgePreservingFilter}
onInput={this._inputChange('edgePreservingFilter', 'number')}
>
Edge preserving filter:
</Range>
</div>
</div>
)}
</Expander>

View File

@@ -18,10 +18,10 @@ export const label = 'JPEG XL (beta)';
export const mimeType = 'image/jpegxl';
export const extension = 'jxl';
export const defaultOptions: EncodeOptions = {
speed: 4,
quality: 75,
speed: 5,
quality: 50,
progressive: false,
epf: -1,
epf: 2,
nearLossless: 0,
lossyPalette: false,
};

View File

@@ -1,14 +1,9 @@
import { EncodeOptions, UVMode, Csp } from '../shared/meta';
import { defaultOptions } from '../shared/meta';
import { EncodeOptions } from '../shared/meta';
import type WorkerBridge from 'client/lazy-app/worker-bridge';
import { h, Component } from 'preact';
import { preventDefault, shallowEqual } from 'client/lazy-app/util';
import { inputFieldValueAsNumber, preventDefault } from 'client/lazy-app/util';
import * as style from 'client/lazy-app/Compress/Options/style.css';
import Range from 'client/lazy-app/Compress/Options/Range';
import Select from 'client/lazy-app/Compress/Options/Select';
import Checkbox from 'client/lazy-app/Compress/Options/Checkbox';
import Expander from 'client/lazy-app/Compress/Options/Expander';
import linkState from 'linkstate';
export const encode = (
signal: AbortSignal,
@@ -23,286 +18,93 @@ interface Props {
}
interface State {
options: EncodeOptions;
effort: number;
quality: number;
alphaQuality: number;
passes: number;
sns: number;
uvMode: number;
lossless: boolean;
slightLoss: number;
colorSpace: number;
errorDiffusion: number;
useRandomMatrix: boolean;
showAdvanced: boolean;
separateAlpha: boolean;
}
export class Options extends Component<Props, State> {
static getDerivedStateFromProps(
props: Props,
state: State,
): Partial<State> | null {
if (state.options && shallowEqual(state.options, props.options)) {
return null;
}
const { options } = props;
const modifyState: Partial<State> = {
options,
effort: options.speed,
alphaQuality: options.alpha_quality,
passes: options.pass,
sns: options.sns,
uvMode: options.uv_mode,
colorSpace: options.csp_type,
errorDiffusion: options.error_diffusion,
useRandomMatrix: options.use_random_matrix,
separateAlpha: options.quality !== options.alpha_quality,
};
// If quality is > 95, it's lossless with slight loss
if (options.quality > 95) {
modifyState.lossless = true;
modifyState.slightLoss = 100 - options.quality;
} else {
modifyState.quality = options.quality;
modifyState.lossless = false;
}
return modifyState;
}
// Other state is set in getDerivedStateFromProps
state: State = {
lossless: false,
slightLoss: 0,
quality: defaultOptions.quality,
showAdvanced: false,
} as State;
private _inputChangeCallbacks = new Map<string, (event: Event) => void>();
private _inputChange = (prop: keyof State, type: 'number' | 'boolean') => {
// Cache the callback for performance
if (!this._inputChangeCallbacks.has(prop)) {
this._inputChangeCallbacks.set(prop, (event: Event) => {
const formEl = event.target as HTMLInputElement | HTMLSelectElement;
const newVal =
type === 'boolean'
? 'checked' in formEl
? formEl.checked
: !!formEl.value
: Number(formEl.value);
const newState: Partial<State> = {
[prop]: newVal,
};
const optionState = {
...this.state,
...newState,
};
const newOptions: EncodeOptions = {
speed: optionState.effort,
quality: optionState.lossless
? 100 - optionState.slightLoss
: optionState.quality,
alpha_quality: optionState.separateAlpha
? optionState.alphaQuality
: optionState.quality,
pass: optionState.passes,
sns: optionState.sns,
uv_mode: optionState.uvMode,
csp_type: optionState.colorSpace,
error_diffusion: optionState.errorDiffusion,
use_random_matrix: optionState.useRandomMatrix,
};
// Updating options, so we don't recalculate in getDerivedStateFromProps.
newState.options = newOptions;
this.setState(newState);
this.props.onChange(newOptions);
});
}
return this._inputChangeCallbacks.get(prop)!;
};
render(
{}: Props,
{
effort,
alphaQuality,
passes,
quality,
sns,
uvMode,
lossless,
slightLoss,
colorSpace,
errorDiffusion,
useRandomMatrix,
separateAlpha,
showAdvanced,
}: State,
) {
private onChange = (event: Event) => {
const form = (event.currentTarget as HTMLInputElement).closest(
'form',
) as HTMLFormElement;
const { options } = this.props;
const newOptions: EncodeOptions = {
quality: inputFieldValueAsNumber(form.quality, options.quality),
alpha_quality: inputFieldValueAsNumber(
form.alpha_quality,
options.alpha_quality,
),
speed: inputFieldValueAsNumber(form.speed, options.speed),
pass: inputFieldValueAsNumber(form.pass, options.pass),
sns: inputFieldValueAsNumber(form.sns, options.sns),
};
this.props.onChange(newOptions);
};
render({ options }: Props) {
return (
<form class={style.optionsSection} onSubmit={preventDefault}>
<label class={style.optionInputFirst}>
<Checkbox
checked={lossless}
onChange={this._inputChange('lossless', 'boolean')}
/>
Lossless
</label>
<Expander>
{lossless && (
<div class={style.optionOneCell}>
<Range
min="0"
max="5"
step="0.1"
value={slightLoss}
onInput={this._inputChange('slightLoss', 'number')}
>
Slight loss:
</Range>
</div>
)}
</Expander>
<Expander>
{!lossless && (
<div>
<div class={style.optionOneCell}>
<Range
min="0"
max="95"
step="0.1"
value={quality}
onInput={this._inputChange('quality', 'number')}
>
Quality:
</Range>
</div>
<label class={style.optionInputFirst}>
<Checkbox
checked={separateAlpha}
onChange={this._inputChange('separateAlpha', 'boolean')}
/>
Separate alpha quality
</label>
<Expander>
{separateAlpha && (
<div class={style.optionOneCell}>
<Range
min="0"
max="100"
step="1"
value={alphaQuality}
onInput={this._inputChange('alphaQuality', 'number')}
>
Alpha Quality:
</Range>
</div>
)}
</Expander>
<label class={style.optionInputFirst}>
<Checkbox
checked={showAdvanced}
onChange={linkState(this, 'showAdvanced')}
/>
Show advanced settings
</label>
<Expander>
{showAdvanced && (
<div>
<div class={style.optionOneCell}>
<Range
min="1"
max="10"
step="1"
value={passes}
onInput={this._inputChange('passes', 'number')}
>
Passes:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
min="0"
max="100"
step="1"
value={sns}
onInput={this._inputChange('sns', 'number')}
>
Spatial noise shaping:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
min="0"
max="100"
step="1"
value={errorDiffusion}
onInput={this._inputChange('errorDiffusion', 'number')}
>
Error diffusion:
</Range>
</div>
<label class={style.optionTextFirst}>
Subsample chroma:
<Select
value={uvMode}
onInput={this._inputChange('uvMode', 'number')}
>
<option value={UVMode.UVModeAuto}>Auto</option>
<option value={UVMode.UVModeAdapt}>Vary</option>
<option value={UVMode.UVMode420}>Half</option>
<option value={UVMode.UVMode444}>Off</option>
</Select>
</label>
<label class={style.optionTextFirst}>
Color space:
<Select
value={colorSpace}
onInput={this._inputChange('colorSpace', 'number')}
>
<option value={Csp.kYCoCg}>YCoCg</option>
<option value={Csp.kYCbCr}>YCbCr</option>
<option value={Csp.kYIQ}>YIQ</option>
</Select>
</label>
<label class={style.optionInputFirst}>
<Checkbox
checked={useRandomMatrix}
onChange={this._inputChange(
'useRandomMatrix',
'boolean',
)}
/>
Random matrix
</label>
</div>
)}
</Expander>
</div>
)}
</Expander>
<div class={style.optionOneCell}>
<Range
name="quality"
min="0"
max="100"
step="1"
value={options.quality}
onInput={this.onChange}
>
Quality:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
name="alpha_quality"
min="0"
max="100"
step="1"
value={options.alpha_quality}
onInput={this.onChange}
>
Alpha Quality:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
name="speed"
min="0"
max="9"
step="1"
value={effort}
onInput={this._inputChange('effort', 'number')}
value={options.speed}
onInput={this.onChange}
>
Effort:
Speed:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
name="pass"
min="1"
max="10"
step="1"
value={options.pass}
onInput={this.onChange}
>
Pass:
</Range>
</div>
<div class={style.optionOneCell}>
<Range
name="sns"
min="0"
max="100"
step="1"
value={options.sns}
onInput={this.onChange}
>
Spatial noise shaping:
</Range>
</div>
</form>

View File

@@ -11,21 +11,16 @@
* limitations under the License.
*/
import type { EncodeOptions } from 'codecs/wp2/enc/wp2_enc';
import { UVMode, Csp } from 'codecs/wp2/enc/wp2_enc';
export { EncodeOptions, UVMode, Csp };
export { EncodeOptions };
export const label = 'WebP v2 (unstable)';
export const mimeType = 'image/webp2';
export const extension = 'wp2';
export const defaultOptions: EncodeOptions = {
quality: 75,
alpha_quality: 75,
alpha_quality: 100,
speed: 5,
pass: 1,
sns: 50,
uv_mode: UVMode.UVModeAuto,
csp_type: Csp.kYCoCg,
error_diffusion: 0,
use_random_matrix: false,
};