Mozjpeg opts (#140)

* Switching to embind

* Adding options to mozjpeg wasm

* Updating packages

* Ditching enum - causing more problems than it's worth

* Adding mozjpeg options UI

* Forgot about this enum

* Bools just work
This commit is contained in:
Jake Archibald
2018-08-17 16:25:28 +01:00
committed by GitHub
parent bff515b63f
commit 1ae65dd4a1
13 changed files with 426 additions and 392 deletions

View File

@@ -1,7 +1,7 @@
<!doctype html>
<script src='mozjpeg_enc.js'></script>
<script>
const Module = mozjpeg_enc();
const module = mozjpeg_enc();
async function loadImage(src) {
// Load image
@@ -17,27 +17,32 @@
return ctx.getImageData(0, 0, img.width, img.height);
}
Module.onRuntimeInitialized = async _ => {
const api = {
version: Module.cwrap('version', 'number', []),
create_buffer: Module.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: Module.cwrap('destroy_buffer', '', ['number']),
encode: Module.cwrap('encode', '', ['number', 'number', 'number', 'number']),
free_result: Module.cwrap('free_result', '', ['number']),
get_result_pointer: Module.cwrap('get_result_pointer', 'number', []),
get_result_size: Module.cwrap('get_result_size', 'number', []),
};
console.log('Version:', api.version().toString(16));
module.onRuntimeInitialized = async _ => {
console.log('Version:', module.version().toString(16));
const image = await loadImage('../example.png');
const p = api.create_buffer(image.width, image.height);
Module.HEAP8.set(image.data, p);
api.encode(p, image.width, image.height, 2);
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
const resultView = new Uint8Array(Module.HEAP8.buffer, resultPointer, resultSize);
const p = module.create_buffer(image.width, image.height);
module.HEAP8.set(image.data, p);
module.encode(p, image.width, image.height, {
quality: 40,
baseline: false,
arithmetic: false,
progressive: true,
optimize_coding: true,
smoothing: 0,
color_space: 3,
quant_table: 3,
trellis_multipass: true,
trellis_opt_zero: true,
trellis_opt_table: true,
trellis_loops: 1,
});
const resultPointer = module.get_result_pointer();
const resultSize = module.get_result_size();
console.log('size', resultSize);
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
api.free_result(resultPointer);
api.destroy_buffer(p);
module.free_result();
module.destroy_buffer(p);
const blob = new Blob([result], {type: 'image/jpeg'});
const blobURL = URL.createObjectURL(blob);

View File

@@ -4,14 +4,33 @@
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include "jpeglib.h"
#include <emscripten/bind.h>
#include "config.h"
#include "jpeglib.h"
#include "cdjpeg.h"
// 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
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
EMSCRIPTEN_KEEPALIVE
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;
};
int version() {
char buffer[] = xstr(MOZJPEG_VERSION);
int version = 0;
@@ -28,27 +47,17 @@ int version() {
return version;
}
EMSCRIPTEN_KEEPALIVE
uint8_t* create_buffer(int width, int height) {
return malloc(width * height * 4 * sizeof(uint8_t));
int create_buffer(int width, int height) {
return (int) malloc(width * height * 4 * sizeof(uint8_t));
}
EMSCRIPTEN_KEEPALIVE
void destroy_buffer(uint8_t* p) {
free(p);
void destroy_buffer(int p) {
free((uint8_t*) p);
}
int result[2];
EMSCRIPTEN_KEEPALIVE
void encode(uint8_t* image_buffer, int image_width, int image_height, int quality) {
// Manually convert RGBA data into RGB
for(int y = 0; y < image_height; y++) {
for(int x = 0; x < image_width; x++) {
image_buffer[(y*image_width + x)*3 + 0] = image_buffer[(y*image_width + x)*4 + 0];
image_buffer[(y*image_width + x)*3 + 1] = image_buffer[(y*image_width + x)*4 + 1];
image_buffer[(y*image_width + x)*3 + 2] = image_buffer[(y*image_width + x)*4 + 2];
}
}
void encode(int image_in, int image_width, int image_height, MozJpegOptions opts) {
uint8_t* image_buffer = (uint8_t*) image_in;
// The code below is basically the `write_JPEG_file` function from
// https://github.com/mozilla/mozjpeg/blob/master/example.c
@@ -109,18 +118,48 @@ void encode(uint8_t* image_buffer, int image_width, int image_height, int qualit
*/
cinfo.image_width = image_width; /* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
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);
/* Now you can set any non-default parameters you wish to.
* Here we just illustrate the use of quality (quantization table) scaling:
*/
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
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);
std::string quality_str = std::to_string(opts.quality);
char const *pqual = quality_str.c_str();
set_quality_ratings(&cinfo, (char*) pqual, opts.baseline);
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.
@@ -136,7 +175,7 @@ void encode(uint8_t* image_buffer, int image_width, int image_height, int qualit
* To keep things simple, we pass one scanline per call; you can pass
* more if you wish, though.
*/
row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */
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.
@@ -161,18 +200,39 @@ void encode(uint8_t* image_buffer, int image_width, int image_height, int qualit
/* And we're done! */
}
EMSCRIPTEN_KEEPALIVE
void free_result() {
free(result[0]); // not sure if this is right with mozjpeg
free((void*)result[0]); // not sure if this is right with mozjpeg
}
EMSCRIPTEN_KEEPALIVE
int get_result_pointer() {
return result[0];
}
EMSCRIPTEN_KEEPALIVE
int get_result_size() {
return result[1];
}
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)
;
function("version", &version);
function("create_buffer", &create_buffer, allow_raw_pointers());
function("destroy_buffer", &destroy_buffer, allow_raw_pointers());
function("encode", &encode, allow_raw_pointers());
function("free_result", &free_result);
function("get_result_pointer", &get_result_pointer, allow_raw_pointers());
function("get_result_size", &get_result_size, allow_raw_pointers());
}

View File

@@ -1 +1,13 @@
export default function(opts: EmscriptenWasm.ModuleOpts): EmscriptenWasm.Module;
import { EncodeOptions } from '../../src/codecs/mozjpeg/encoder';
interface MozJPEGModule extends EmscriptenWasm.Module {
create_buffer(width: number, height: number): number;
encode(pointer: number, width: number, height: number, options: EncodeOptions): void;
get_result_pointer(): number;
get_result_size(): number;
free_result(): void;
destroy_buffer(pointer: number): void;
}
export default function(opts: EmscriptenWasm.ModuleOpts): MozJPEGModule;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -4,7 +4,7 @@
"install": "napa",
"build": "npm run build:library && npm run build:wasm",
"build:library": "cd node_modules/mozjpeg && autoreconf -fiv && docker run --rm -v $(pwd):/src trzeci/emscripten emconfigure ./configure --without-simd && docker run --rm -v $(pwd):/src trzeci/emscripten emmake make libjpeg.la",
"build:wasm": "docker run --rm -v $(pwd):/src trzeci/emscripten emcc -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]' -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME=\"mozjpeg_enc\"' -I node_modules/mozjpeg -o ./mozjpeg_enc.js mozjpeg_enc.c node_modules/mozjpeg/.libs/libjpeg.a"
"build:wasm": "docker run --rm -v $(pwd):/src trzeci/emscripten emcc --bind -O3 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME=\"mozjpeg_enc\"' -I node_modules/mozjpeg -o ./mozjpeg_enc.js -Wno-deprecated-register -Wno-writable-strings node_modules/mozjpeg/rdswitch.c -x c++ -std=c++11 mozjpeg_enc.cpp node_modules/mozjpeg/.libs/libjpeg.a"
},
"napa": {
"mozjpeg": "mozilla/mozjpeg#v3.3.1"

View File

@@ -2,7 +2,7 @@
"name": "webp_enc",
"scripts": {
"install": "napa",
"build": "docker run --rm -v $(pwd):/src trzeci/emscripten emcc --bind -O3 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME=\"webp_enc\"' -I node_modules/libwebp -o ./webp_enc.js -x c node_modules/libwebp/src/{dec,dsp,demux,enc,mux,utils}/*.c -x c++ -std=c++11 webp_enc.cpp "
"build": "docker run --rm -v $(pwd):/src trzeci/emscripten emcc --bind -O3 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME=\"webp_enc\"' -I node_modules/libwebp -o ./webp_enc.js -x c node_modules/libwebp/src/{dec,dsp,demux,enc,mux,utils}/*.c -x c++ -std=c++11 webp_enc.cpp"
},
"napa": {
"libwebp": "webmproject/libwebp#v1.0.0"

368
package-lock.json generated
View File

@@ -1243,13 +1243,15 @@
}
},
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
"co": "^4.6.0",
"json-stable-stringify": "^1.0.1"
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"ajv-keywords": {
@@ -1426,10 +1428,13 @@
"dev": true
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
"dev": true
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"asn1.js": {
"version": "4.10.1",
@@ -1469,9 +1474,9 @@
}
},
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"assign-symbols": {
@@ -1552,15 +1557,15 @@
}
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true
},
"babel-code-frame": {
@@ -2306,9 +2311,9 @@
}
},
"babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.13",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.13.tgz",
"integrity": "sha1-Mxz8BQmagII4MR14MZwnRg1IEYk=",
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.14.tgz",
"integrity": "sha512-fMlqvLZPX9v7k0Sl4SVzHofQOKTDXvQbpKx+glCm5Ar5L5KQZ8VxjCsYslQwP+KrYXCG6BY7hOp+O1tUVATTDA==",
"dev": true
},
"babel-plugin-transform-regenerator": {
@@ -2765,15 +2770,6 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"boom": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"dev": true,
"requires": {
"hoek": "2.x.x"
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3462,12 +3458,6 @@
"requires": {
"for-in": "^1.0.1"
}
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}
},
@@ -3902,15 +3892,6 @@
"which": "^1.2.9"
}
},
"cryptiles": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
"dev": true,
"requires": {
"boom": "2.x.x"
}
},
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@@ -4093,14 +4074,6 @@
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"date-fns": {
@@ -4490,13 +4463,14 @@
}
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"dev": true,
"optional": true,
"requires": {
"jsbn": "~0.1.0"
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"editions": {
@@ -4530,9 +4504,9 @@
"dev": true
},
"elliptic": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
"integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -5287,13 +5261,13 @@
"dev": true
},
"form-data": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.5",
"combined-stream": "1.0.6",
"mime-types": "^2.1.12"
}
},
@@ -5986,14 +5960,6 @@
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"gh-got": {
@@ -6305,19 +6271,19 @@
"dev": true
},
"har-schema": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"requires": {
"ajv": "^4.9.1",
"har-schema": "^1.0.5"
"ajv": "^5.1.0",
"har-schema": "^2.0.0"
}
},
"has": {
@@ -6429,18 +6395,6 @@
"minimalistic-assert": "^1.0.1"
}
},
"hawk": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
"dev": true,
"requires": {
"boom": "2.x.x",
"cryptiles": "2.x.x",
"hoek": "2.x.x",
"sntp": "1.x.x"
}
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
@@ -6458,12 +6412,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
},
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -6658,12 +6606,12 @@
}
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "^0.2.0",
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
@@ -7619,15 +7567,6 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"json-stable-stringify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
"integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
"dev": true,
"requires": {
"jsonify": "~0.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -7661,14 +7600,6 @@
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"keyv": {
@@ -8848,8 +8779,7 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"dev": true,
"optional": true
"dev": true
},
"nanomatch": {
"version": "1.2.13",
@@ -8916,9 +8846,9 @@
"dev": true
},
"node-gyp": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.7.0.tgz",
"integrity": "sha512-qDQE/Ft9xXP6zphwx4sD0t+VhwV7yFaloMpfbL2QnnDZcyaiakWlLdtFGGQfTAwpFHdpbRhRxVhIHN1OKAjgbg==",
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
"integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
"dev": true,
"requires": {
"fstream": "^1.0.0",
@@ -8928,49 +8858,13 @@
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"osenv": "0",
"request": ">=2.9.0 <2.82.0",
"request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"tar": "^2.0.0",
"which": "1"
},
"dependencies": {
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
"dev": true
},
"request": {
"version": "2.81.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
"integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
"dev": true,
"requires": {
"aws-sign2": "~0.6.0",
"aws4": "^1.2.1",
"caseless": "~0.12.0",
"combined-stream": "~1.0.5",
"extend": "~3.0.0",
"forever-agent": "~0.6.1",
"form-data": "~2.1.1",
"har-validator": "~4.2.1",
"hawk": "~3.1.3",
"http-signature": "~1.1.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.7",
"oauth-sign": "~0.8.1",
"performance-now": "^0.2.0",
"qs": "~6.4.0",
"safe-buffer": "^5.0.1",
"stringstream": "~0.0.4",
"tough-cookie": "~2.3.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.0.0"
}
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
@@ -9011,9 +8905,9 @@
}
},
"node-sass": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
"integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==",
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz",
"integrity": "sha512-XzXyGjO+84wxyH7fV6IwBOTrEBe2f0a6SBze9QWWYR/cL74AcQUks2AsqcCZenl/Fp/JVbuEaLpgrLtocwBUww==",
"dev": true,
"requires": {
"async-foreach": "^0.1.3",
@@ -9029,7 +8923,7 @@
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.10.0",
"node-gyp": "^3.3.1",
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "2.87.0",
"sass-graph": "^2.2.4",
@@ -9060,12 +8954,6 @@
"which": "^1.2.9"
}
},
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -9748,9 +9636,9 @@
}
},
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
"pify": {
@@ -10413,9 +10301,9 @@
}
},
"preact": {
"version": "8.2.9",
"resolved": "https://registry.npmjs.org/preact/-/preact-8.2.9.tgz",
"integrity": "sha512-ThuGXBmJS3VsT+jIP+eQufD3L8pRw/PY3FoCys6O9Pu6aF12Pn9zAJDX99TfwRAFOCEKm/P0lwiPTbqKMJp0fA=="
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-8.3.1.tgz",
"integrity": "sha512-s8H1Y8O9e+mOBo3UP1jvWqArPmjCba2lrrGLlq/0kN1XuIINUbYtf97iiXKxCuG3eYwmppPKnyW2DBrNj/TuTg=="
},
"preact-i18n": {
"version": "1.2.2",
@@ -11362,76 +11250,6 @@
"tough-cookie": "~2.3.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.1.0"
},
"dependencies": {
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "1.0.6",
"mime-types": "^2.1.12"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"requires": {
"ajv": "^5.1.0",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
}
}
},
"require-directory": {
@@ -11691,16 +11509,17 @@
}
},
"sass-loader": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.7.tgz",
"integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz",
"integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
"dev": true,
"requires": {
"clone-deep": "^2.0.1",
"loader-utils": "^1.0.1",
"lodash.tail": "^4.1.1",
"neo-async": "^2.5.0",
"pify": "^3.0.0"
"pify": "^3.0.0",
"semver": "^5.5.0"
},
"dependencies": {
"pify": {
@@ -12131,15 +11950,6 @@
}
}
},
"sntp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
"dev": true,
"requires": {
"hoek": "2.x.x"
}
},
"sockjs": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
@@ -12377,14 +12187,6 @@
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"ssri": {
@@ -12537,12 +12339,6 @@
"safe-buffer": "~5.1.0"
}
},
"stringstream": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
"integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@@ -12595,9 +12391,9 @@
}
},
"style-loader": {
"version": "0.20.3",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz",
"integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==",
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.22.1.tgz",
"integrity": "sha512-WXUrLeinPIR1Oat3PfCDro7qTniwNTJqGqv1KcQiL3JR5PzrVLTyNsd9wTsPXG/qNCJ7lzR2NY/QDjFsP7nuSQ==",
"dev": true,
"requires": {
"loader-utils": "^1.1.0",
@@ -13157,9 +12953,9 @@
}
},
"uglifyjs-webpack-plugin": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz",
"integrity": "sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
"integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
"dev": true,
"requires": {
"cacache": "^10.0.4",
@@ -13506,14 +13302,6 @@
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"vinyl": {
@@ -13571,9 +13359,9 @@
}
},
"webpack": {
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.2.tgz",
"integrity": "sha512-Fw+RtyJD9ekQ6Mh6e/hYeoafIKK6bP6qS7EVnZ3hejt+1Ah3JCJZTGE0e5S6Eq4ijIVht6ktWOEqJfm92+5MLw==",
"version": "4.16.5",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.5.tgz",
"integrity": "sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.5.13",

View File

@@ -27,7 +27,7 @@
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-react-constant-elements": "^6.23.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.13",
"babel-plugin-transform-react-remove-prop-types": "^0.4.14",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0",
"clean-webpack-plugin": "^0.1.19",
@@ -40,14 +40,14 @@
"if-env": "^1.0.4",
"loader-utils": "^1.1.0",
"mini-css-extract-plugin": "^0.3.0",
"node-sass": "^4.9.2",
"node-sass": "^4.9.3",
"optimize-css-assets-webpack-plugin": "^4.0.3",
"progress-bar-webpack-plugin": "^1.11.0",
"raw-loader": "^0.5.1",
"sass-loader": "^6.0.7",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "^2.0.1",
"source-map-loader": "^0.2.3",
"style-loader": "^0.20.3",
"style-loader": "^0.22.1",
"ts-loader": "^4.4.2",
"tslint": "^5.11.0",
"tslint-config-airbnb": "^5.9.2",
@@ -55,7 +55,7 @@
"tslint-react": "^3.6.0",
"typescript": "^2.9.2",
"typings-for-css-modules-loader": "^1.7.0",
"webpack": "^4.16.2",
"webpack": "^4.16.5",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-cli": "^2.1.5",
"webpack-dev-server": "^3.1.5",
@@ -68,7 +68,7 @@
"comlink-loader": "^1.0.0",
"filesize": "^3.6.1",
"material-components-web": "^0.32.0",
"preact": "^8.2.9",
"preact": "^8.3.1",
"preact-i18n": "^1.2.2",
"preact-material-components": "^1.4.7",
"preact-router": "^2.6.1"

View File

@@ -1,22 +1,10 @@
import mozjpeg_enc from '../../../codecs/mozjpeg_enc/mozjpeg_enc';
import mozjpeg_enc, { MozJPEGModule } from '../../../codecs/mozjpeg_enc/mozjpeg_enc';
// Using require() so TypeScript doesnt complain about this not being a module.
import { EncodeOptions } from './encoder';
const wasmBinaryUrl = require('../../../codecs/mozjpeg_enc/mozjpeg_enc.wasm');
// API exposed by wasm module. Details in the codecs README.
interface ModuleAPI {
version(): number;
create_buffer(width: number, height: number): number;
destroy_buffer(pointer: number): void;
encode(buffer: number, width: number, height: number, quality: number): void;
free_result(): void;
get_result_pointer(): number;
get_result_size(): number;
}
export default class MozJpegEncoder {
private emscriptenModule: Promise<EmscriptenWasm.Module>;
private api: Promise<ModuleAPI>;
private emscriptenModule: Promise<MozJPEGModule>;
constructor() {
this.emscriptenModule = new Promise((resolve) => {
@@ -41,37 +29,20 @@ export default class MozJpegEncoder {
},
});
});
this.api = (async () => {
// Not sure why, but TypeScript complains that I am using
// `emscriptenModule` before its getting assigned, which is clearly not
// true :shrug: Using `any`
const m = await (this as any).emscriptenModule;
return {
version: m.cwrap('version', 'number', []),
create_buffer: m.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: m.cwrap('destroy_buffer', '', ['number']),
encode: m.cwrap('encode', '', ['number', 'number', 'number', 'number']),
free_result: m.cwrap('free_result', '', []),
get_result_pointer: m.cwrap('get_result_pointer', 'number', []),
get_result_size: m.cwrap('get_result_size', 'number', []),
};
})();
}
async encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
const m = await this.emscriptenModule;
const api = await this.api;
const module = await this.emscriptenModule;
const p = api.create_buffer(data.width, data.height);
m.HEAP8.set(data.data, p);
api.encode(p, data.width, data.height, options.quality);
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
const resultView = new Uint8Array(m.HEAP8.buffer, resultPointer, resultSize);
const p = module.create_buffer(data.width, data.height);
module.HEAP8.set(data.data, p);
module.encode(p, data.width, data.height, options);
const resultPointer = module.get_result_pointer();
const resultSize = module.get_result_size();
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
api.free_result();
api.destroy_buffer(p);
module.free_result();
module.destroy_buffer(p);
// wasm cant run on SharedArrayBuffers, so we hard-cast to ArrayBuffer.
return result.buffer as ArrayBuffer;

View File

@@ -1,13 +1,46 @@
import EncoderWorker from './Encoder.worker';
export interface EncodeOptions { quality: number; }
export 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;
}
export interface EncoderState { type: typeof type; options: EncodeOptions; }
export const type = 'mozjpeg';
export const label = 'MozJPEG';
export const mimeType = 'image/jpeg';
export const extension = 'jpg';
export const defaultOptions: EncodeOptions = { quality: 7 };
export const defaultOptions: EncodeOptions = {
quality: 75,
baseline: false,
arithmetic: false,
progressive: true,
optimize_coding: true,
smoothing: 0,
color_space: MozJpegColorSpace.YCbCr,
quant_table: 3,
trellis_multipass: false,
trellis_opt_zero: false,
trellis_opt_table: false,
trellis_loops: 1,
};
export async function encode(data: ImageData, options: EncodeOptions) {
// We need to await this because it's been comlinked.

View File

@@ -1,3 +1,161 @@
import qualityOption from '../generic/quality-option';
import { h, Component } from 'preact';
import { bind, inputFieldChecked, inputFieldValueAsNumber } from '../../lib/util';
import { EncodeOptions, MozJpegColorSpace } from './encoder';
export default qualityOption();
type Props = {
options: EncodeOptions,
onChange(newOptions: EncodeOptions): void,
};
export default class MozJPEGEncoderOptions extends Component<Props, {}> {
@bind
onChange(event: Event) {
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
const options: EncodeOptions = {
// Copy over options the form doesn't currently care about, eg arithmetic
...this.props.options,
// And now stuff from the form:
// .checked
baseline: inputFieldChecked(form.baseline),
progressive: inputFieldChecked(form.progressive),
optimize_coding: inputFieldChecked(form.optimize_coding),
trellis_multipass: inputFieldChecked(form.trellis_multipass),
trellis_opt_zero: inputFieldChecked(form.trellis_opt_zero),
trellis_opt_table: inputFieldChecked(form.trellis_opt_table),
// .value
quality: inputFieldValueAsNumber(form.quality),
smoothing: inputFieldValueAsNumber(form.smoothing),
color_space: inputFieldValueAsNumber(form.color_space),
quant_table: inputFieldValueAsNumber(form.quant_table),
trellis_loops: inputFieldValueAsNumber(form.trellis_loops),
};
this.props.onChange(options);
}
render({ options }: Props) {
// I'm rendering both lossy and lossless forms, as it becomes much easier when
// gathering the data.
return (
<form>
<label>
Quality:
<input
name="quality"
type="range"
min="0"
max="100"
value={'' + options.quality}
onChange={this.onChange}
/>
</label>
<label>
<input
name="baseline"
type="checkbox"
checked={options.baseline}
onChange={this.onChange}
/>
Baseline (worse but legacy-compatible)
</label>
<label style={{ display: options.baseline ? 'none' : '' }}>
<input
name="progressive"
type="checkbox"
checked={options.progressive}
onChange={this.onChange}
/>
Progressive multi-pass rendering
</label>
<label style={{ display: options.baseline ? '' : 'none' }}>
<input
name="optimize_coding"
type="checkbox"
checked={options.optimize_coding}
onChange={this.onChange}
/>
Optimize Huffman table
</label>
<label>
Smoothing:
<input
name="smoothing"
type="range"
min="0"
max="100"
value={'' + options.smoothing}
onChange={this.onChange}
/>
</label>
<label>
Output color space:
<select
name="color_space"
value={'' + options.color_space}
onChange={this.onChange}
>
<option value={MozJpegColorSpace.GRAYSCALE}>Grayscale</option>
<option value={MozJpegColorSpace.RGB}>RGB (sub-optimal)</option>
<option value={MozJpegColorSpace.YCbCr}>YCbCr (optimized for color)</option>
</select>
</label>
<label>
Quantization table:
<select
name="quant_table"
value={'' + options.quant_table}
onChange={this.onChange}
>
<option value="0">JPEG Annex K</option>
<option value="1">Flat</option>
<option value="2">MSSIM-tuned Kodak</option>
<option value="3">ImageMagick</option>
<option value="4">PSNR-HVS-M-tuned Kodak</option>
<option value="5">Klein et al</option>
<option value="6">Watson et al</option>
<option value="7">Ahumada et al</option>
<option value="8">Peterson et al</option>
</select>
</label>
<label>
<input
name="trellis_multipass"
type="checkbox"
checked={options.trellis_multipass}
onChange={this.onChange}
/>
Consider multiple scans during trellis quantization
</label>
<label style={{ display: options.trellis_multipass ? '' : 'none' }}>
<input
name="trellis_opt_zero"
type="checkbox"
checked={options.trellis_opt_zero}
onChange={this.onChange}
/>
Optimize runs of zero blocks
</label>
<label>
<input
name="trellis_opt_table"
type="checkbox"
checked={options.trellis_opt_table}
onChange={this.onChange}
/>
Optimize after trellis quantization
</label>
<label>
Trellis quantization passes:
<input
name="trellis_loops"
type="range"
min="1"
max="50"
value={'' + options.trellis_loops}
onChange={this.onChange}
/>
</label>
</form>
);
}
}

View File

@@ -157,7 +157,14 @@ export function inputFieldValueAsNumber(field: any): number {
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
*/
export function inputFieldCheckedAsNumber(field: any): number {
return Number((field as HTMLInputElement).checked);
return Number(inputFieldChecked(field));
}
/**
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
*/
export function inputFieldChecked(field: any): boolean {
return (field as HTMLInputElement).checked;
}
/**