Compare commits
8 Commits
basis
...
libsquoosh
Author | SHA1 | Date | |
---|---|---|---|
4e04053e4c | |||
255dfa434a | |||
0bef05bcd4 | |||
00cfdafdf3 | |||
92f52319da | |||
023304803f | |||
6b08cd2355 | |||
db1a5138e6 |
Binary file not shown.
Binary file not shown.
@ -10,15 +10,13 @@ using namespace emscripten;
|
||||
thread_local const val Uint8Array = val::global("Uint8Array");
|
||||
|
||||
struct JXLOptions {
|
||||
// 1 = slowest
|
||||
// 7 = fastest
|
||||
int speed;
|
||||
int effort;
|
||||
float quality;
|
||||
bool progressive;
|
||||
int epf;
|
||||
int nearLossless;
|
||||
bool lossyPalette;
|
||||
size_t decodingSpeedTier;
|
||||
float photonNoiseIso;
|
||||
};
|
||||
|
||||
val encode(std::string image, int width, int height, JXLOptions options) {
|
||||
@ -33,11 +31,14 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
||||
pool_ptr = &pool;
|
||||
#endif
|
||||
|
||||
cparams.epf = options.epf;
|
||||
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
|
||||
cparams.decoding_speed_tier = options.decodingSpeedTier;
|
||||
size_t st = 10 - options.effort;
|
||||
cparams.speed_tier = jxl::SpeedTier(st);
|
||||
|
||||
if (options.lossyPalette || options.nearLossless) {
|
||||
cparams.epf = options.epf;
|
||||
cparams.decoding_speed_tier = options.decodingSpeedTier;
|
||||
cparams.photon_noise_iso = options.photonNoiseIso;
|
||||
|
||||
if (options.lossyPalette) {
|
||||
cparams.lossy_palette = true;
|
||||
cparams.palette_colors = 0;
|
||||
cparams.options.predictor = jxl::Predictor::Zero;
|
||||
@ -106,12 +107,12 @@ val encode(std::string image, int width, int height, JXLOptions options) {
|
||||
|
||||
EMSCRIPTEN_BINDINGS(my_module) {
|
||||
value_object<JXLOptions>("JXLOptions")
|
||||
.field("speed", &JXLOptions::speed)
|
||||
.field("effort", &JXLOptions::effort)
|
||||
.field("quality", &JXLOptions::quality)
|
||||
.field("progressive", &JXLOptions::progressive)
|
||||
.field("nearLossless", &JXLOptions::nearLossless)
|
||||
.field("lossyPalette", &JXLOptions::lossyPalette)
|
||||
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
|
||||
.field("photonNoiseIso", &JXLOptions::photonNoiseIso)
|
||||
.field("epf", &JXLOptions::epf);
|
||||
|
||||
function("encode", &encode);
|
||||
|
4
codecs/jxl/enc/jxl_enc.d.ts
vendored
4
codecs/jxl/enc/jxl_enc.d.ts
vendored
@ -1,11 +1,11 @@
|
||||
export interface EncodeOptions {
|
||||
speed: number;
|
||||
effort: number;
|
||||
quality: number;
|
||||
progressive: boolean;
|
||||
epf: number;
|
||||
nearLossless: number;
|
||||
lossyPalette: boolean;
|
||||
decodingSpeedTier: number;
|
||||
photonNoiseIso: number;
|
||||
}
|
||||
|
||||
export interface JXLModule extends EmscriptenWasm.Module {
|
||||
|
2
codecs/jxl/enc/jxl_enc.js
generated
2
codecs/jxl/enc/jxl_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_enc_mt.js
generated
2
codecs/jxl/enc/jxl_enc_mt.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_enc_mt_simd.js
generated
2
codecs/jxl/enc/jxl_enc_mt_simd.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2
codecs/jxl/enc/jxl_node_enc.js
generated
2
codecs/jxl/enc/jxl_node_enc.js
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
2248
libsquoosh/package-lock.json
generated
2248
libsquoosh/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,8 @@
|
||||
"/build/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c"
|
||||
"build": "rollup -c",
|
||||
"test": "uvu -r ts-node/register test"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Google Chrome Developers <chromium-dev@google.com>",
|
||||
@ -34,7 +35,9 @@
|
||||
"@types/node": "^15.6.1",
|
||||
"rollup": "^2.46.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.1.3",
|
||||
"uvu": "^0.5.1",
|
||||
"which": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export function instantiateEmscriptenWasm<T extends EmscriptenWasm.Module>(
|
||||
// These will have changed in the bundling process and
|
||||
// we need to inject them here.
|
||||
if (requestPath.endsWith('.wasm')) return pathify(path);
|
||||
if (requestPath.endsWith('.worker.js')) return new URL(workerJS).pathname;
|
||||
if (requestPath.endsWith('.worker.js')) return pathify(workerJS);
|
||||
return requestPath;
|
||||
},
|
||||
});
|
||||
|
43
libsquoosh/test/index.test.ts
Normal file
43
libsquoosh/test/index.test.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import * as path from 'path';
|
||||
import { test } from 'uvu';
|
||||
import * as assert from 'uvu/assert';
|
||||
import { ImagePool } from '..';
|
||||
|
||||
let imagePool: ImagePool;
|
||||
|
||||
test.after.each(async () => {
|
||||
if (imagePool) {
|
||||
try {
|
||||
await imagePool.close();
|
||||
} catch (e) {}
|
||||
}
|
||||
imagePool = undefined;
|
||||
});
|
||||
|
||||
test('smoke test', async () => {
|
||||
imagePool = new ImagePool(1);
|
||||
|
||||
const imagePath = path.resolve(__dirname, '../../icon-large-maskable.png');
|
||||
const image = imagePool.ingestImage(imagePath);
|
||||
|
||||
const { bitmap } = await image.decoded;
|
||||
assert.equal(bitmap.width, 1024);
|
||||
|
||||
await image.preprocess({
|
||||
resize: {
|
||||
enabled: true,
|
||||
width: 100,
|
||||
},
|
||||
});
|
||||
|
||||
await image.encode({
|
||||
mozjpeg: {},
|
||||
});
|
||||
|
||||
const { size } = await image.encodedWith.mozjpeg;
|
||||
// resulting image is 1554b
|
||||
assert.ok(size > 500);
|
||||
assert.ok(size < 5000);
|
||||
});
|
||||
|
||||
test.run();
|
@ -5,5 +5,12 @@
|
||||
"types": ["node"],
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["src/**/*", "../codecs/**/*"]
|
||||
"include": ["src/**/*", "../codecs/**/*"],
|
||||
"ts-node": {
|
||||
"transpileOnly": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
},
|
||||
"include": ["tests/**/*"]
|
||||
}
|
||||
}
|
||||
|
28
package-lock.json
generated
28
package-lock.json
generated
@ -32,7 +32,7 @@
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"mime-types": "^2.1.28",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pointer-tracker": "^2.4.0",
|
||||
"pointer-tracker": "^2.5.3",
|
||||
"postcss": "^7.0.35",
|
||||
"postcss-modules": "^3.2.2",
|
||||
"postcss-nested": "^4.2.3",
|
||||
@ -48,6 +48,20 @@
|
||||
"which": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"../pointer-tracker": {
|
||||
"version": "2.5.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"husky": "^4.2.5",
|
||||
"lint-staged": "^10.2.11",
|
||||
"prettier": "^2.0.5",
|
||||
"rollup": "^2.23.1",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"rollup-plugin-typescript2": "^0.27.2",
|
||||
"typescript": "^3.9.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||
@ -3871,9 +3885,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pointer-tracker": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.4.0.tgz",
|
||||
"integrity": "sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g==",
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.5.3.tgz",
|
||||
"integrity": "sha512-LiJUeIbzk4dXq678YeyrZ++mdY17q4n/2sBHfU9wIuvmSzdiPgMvmvWN2g8mY4J7YwYOIrqrZUWP/MfFHVwYtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
@ -11946,9 +11960,9 @@
|
||||
}
|
||||
},
|
||||
"pointer-tracker": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.4.0.tgz",
|
||||
"integrity": "sha512-pWI2tpaM/XNtc9mUTv42Rmjf6mkHvE8LT5DDEq0G7baPNhxNM9E3CepubPplSoSLk9E5bwQrAMyDcPVmJyTW4g==",
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/pointer-tracker/-/pointer-tracker-2.5.3.tgz",
|
||||
"integrity": "sha512-LiJUeIbzk4dXq678YeyrZ++mdY17q4n/2sBHfU9wIuvmSzdiPgMvmvWN2g8mY4J7YwYOIrqrZUWP/MfFHVwYtg==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
|
@ -32,7 +32,7 @@
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"mime-types": "^2.1.28",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pointer-tracker": "^2.4.0",
|
||||
"pointer-tracker": "^2.5.3",
|
||||
"postcss": "^7.0.35",
|
||||
"postcss-modules": "^3.2.2",
|
||||
"postcss-nested": "^4.2.3",
|
||||
|
@ -20,7 +20,7 @@ const REFLECTED_ATTRIBUTES = [
|
||||
'disabled',
|
||||
];
|
||||
|
||||
function getPrescision(value: string): number {
|
||||
function getPrecision(value: string): number {
|
||||
const afterDecimal = value.split('.')[1];
|
||||
return afterDecimal ? afterDecimal.length : 0;
|
||||
}
|
||||
@ -112,18 +112,24 @@ class RangeInputElement extends HTMLElement {
|
||||
this.dispatchEvent(retargetted);
|
||||
};
|
||||
|
||||
private _getDisplayValue(value: number): string {
|
||||
if (value >= 10000) return (value / 1000).toFixed(1) + 'k';
|
||||
|
||||
const labelPrecision =
|
||||
Number(this.labelPrecision) || getPrecision(this.step) || 0;
|
||||
return labelPrecision
|
||||
? value.toFixed(labelPrecision)
|
||||
: Math.round(value).toString();
|
||||
}
|
||||
|
||||
private _update = () => {
|
||||
// Not connected?
|
||||
if (!this._valueDisplay) return;
|
||||
const value = Number(this.value) || 0;
|
||||
const min = Number(this.min) || 0;
|
||||
const max = Number(this.max) || 100;
|
||||
const labelPrecision =
|
||||
Number(this.labelPrecision) || getPrescision(this.step) || 0;
|
||||
const percent = (100 * (value - min)) / (max - min);
|
||||
const displayValue = labelPrecision
|
||||
? value.toFixed(labelPrecision)
|
||||
: Math.round(value).toString();
|
||||
const displayValue = this._getDisplayValue(value);
|
||||
|
||||
this._valueDisplay!.textContent = displayValue;
|
||||
this.style.setProperty('--value-percent', percent + '%');
|
||||
|
@ -35,7 +35,7 @@
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-color: var(--main-theme-color);
|
||||
text-underline-position: under;
|
||||
width: 48px;
|
||||
width: 54px;
|
||||
position: relative;
|
||||
left: 5px;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import PointerTracker, { Pointer } from 'pointer-tracker';
|
||||
import 'add-css:./styles.css';
|
||||
import { isSafari } from 'client/lazy-app/util';
|
||||
|
||||
interface Point {
|
||||
clientX: number;
|
||||
@ -105,14 +106,23 @@ export default class PinchZoom extends HTMLElement {
|
||||
const pointerTracker: PointerTracker = new PointerTracker(this, {
|
||||
start: (pointer, event) => {
|
||||
// We only want to track 2 pointers at most
|
||||
if (pointerTracker.currentPointers.length === 2 || !this._positioningEl)
|
||||
if (
|
||||
pointerTracker.currentPointers.length === 2 ||
|
||||
!this._positioningEl
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
move: (previousPointers) => {
|
||||
this._onPointerMove(previousPointers, pointerTracker.currentPointers);
|
||||
},
|
||||
// Unfortunately Safari on iOS has a bug where pointer event capturing
|
||||
// doesn't work in some cases, and we hit those cases due to our event
|
||||
// retargeting in pinch-zoom.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=220196
|
||||
avoidPointerEvents: isSafari,
|
||||
});
|
||||
|
||||
this.addEventListener('wheel', (event) => this._onWheel(event));
|
||||
|
@ -5,8 +5,10 @@ import './custom-els/PinchZoom';
|
||||
import './custom-els/TwoUp';
|
||||
import * as style from './style.css';
|
||||
import 'add-css:./style.css';
|
||||
import { shallowEqual } from '../../util';
|
||||
import { shallowEqual, isSafari } from '../../util';
|
||||
import {
|
||||
ToggleAliasingIcon,
|
||||
ToggleAliasingActiveIcon,
|
||||
ToggleBackgroundIcon,
|
||||
AddIcon,
|
||||
RemoveIcon,
|
||||
@ -19,7 +21,6 @@ import { cleanSet } from '../../util/clean-modify';
|
||||
import type { SourceImage } from '../../Compress';
|
||||
import { linkRef } from 'shared/prerendered-app/util';
|
||||
import { drawDataToCanvas } from 'client/lazy-app/util/canvas';
|
||||
|
||||
interface Props {
|
||||
source?: SourceImage;
|
||||
preprocessorState?: PreprocessorState;
|
||||
@ -35,6 +36,7 @@ interface State {
|
||||
scale: number;
|
||||
editingScale: boolean;
|
||||
altBackground: boolean;
|
||||
aliasing: boolean;
|
||||
}
|
||||
|
||||
const scaleToOpts: ScaleToOpts = {
|
||||
@ -49,6 +51,7 @@ export default class Output extends Component<Props, State> {
|
||||
scale: 1,
|
||||
editingScale: false,
|
||||
altBackground: false,
|
||||
aliasing: false,
|
||||
};
|
||||
canvasLeft?: HTMLCanvasElement;
|
||||
canvasRight?: HTMLCanvasElement;
|
||||
@ -145,6 +148,12 @@ export default class Output extends Component<Props, State> {
|
||||
return props.rightCompressed || (props.source && props.source.preprocessed);
|
||||
}
|
||||
|
||||
private toggleAliasing = () => {
|
||||
this.setState((state) => ({
|
||||
aliasing: !state.aliasing,
|
||||
}));
|
||||
};
|
||||
|
||||
private toggleBackground = () => {
|
||||
this.setState({
|
||||
altBackground: !this.state.altBackground,
|
||||
@ -255,7 +264,7 @@ export default class Output extends Component<Props, State> {
|
||||
|
||||
render(
|
||||
{ mobileView, leftImgContain, rightImgContain, source }: Props,
|
||||
{ scale, editingScale, altBackground }: State,
|
||||
{ scale, editingScale, altBackground, aliasing }: State,
|
||||
) {
|
||||
const leftDraw = this.leftDrawable();
|
||||
const rightDraw = this.rightDrawable();
|
||||
@ -275,7 +284,11 @@ export default class Output extends Component<Props, State> {
|
||||
onTouchStartCapture={this.onRetargetableEvent}
|
||||
onTouchEndCapture={this.onRetargetableEvent}
|
||||
onTouchMoveCapture={this.onRetargetableEvent}
|
||||
onPointerDownCapture={this.onRetargetableEvent}
|
||||
onPointerDownCapture={
|
||||
// We avoid pointer events in our PinchZoom due to a Safari bug.
|
||||
// That means we also need to avoid them here too, else we end up preventing the fallback mouse events.
|
||||
isSafari ? undefined : this.onRetargetableEvent
|
||||
}
|
||||
onMouseDownCapture={this.onRetargetableEvent}
|
||||
onWheelCapture={this.onRetargetableEvent}
|
||||
>
|
||||
@ -285,7 +298,9 @@ export default class Output extends Component<Props, State> {
|
||||
ref={linkRef(this, 'pinchZoomLeft')}
|
||||
>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
class={`${style.pinchTarget} ${
|
||||
aliasing ? style.pixelated : ''
|
||||
}`}
|
||||
ref={linkRef(this, 'canvasLeft')}
|
||||
width={leftDraw && leftDraw.width}
|
||||
height={leftDraw && leftDraw.height}
|
||||
@ -301,7 +316,9 @@ export default class Output extends Component<Props, State> {
|
||||
ref={linkRef(this, 'pinchZoomRight')}
|
||||
>
|
||||
<canvas
|
||||
class={style.pinchTarget}
|
||||
class={`${style.pinchTarget} ${
|
||||
aliasing ? style.pixelated : ''
|
||||
}`}
|
||||
ref={linkRef(this, 'canvasRight')}
|
||||
width={rightDraw && rightDraw.width}
|
||||
height={rightDraw && rightDraw.height}
|
||||
@ -345,10 +362,31 @@ export default class Output extends Component<Props, State> {
|
||||
</button>
|
||||
</div>
|
||||
<div class={style.buttonGroup}>
|
||||
<button class={style.firstButton} onClick={this.onRotateClick}>
|
||||
<button
|
||||
class={style.firstButton}
|
||||
onClick={this.onRotateClick}
|
||||
title="Rotate"
|
||||
>
|
||||
<RotateIcon />
|
||||
</button>
|
||||
<button class={style.lastButton} onClick={this.toggleBackground}>
|
||||
{!isSafari && (
|
||||
<button
|
||||
class={style.button}
|
||||
onClick={this.toggleAliasing}
|
||||
title="Toggle smoothing"
|
||||
>
|
||||
{aliasing ? (
|
||||
<ToggleAliasingActiveIcon />
|
||||
) : (
|
||||
<ToggleAliasingIcon />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
class={style.lastButton}
|
||||
onClick={this.toggleBackground}
|
||||
title="Toggle background"
|
||||
>
|
||||
{altBackground ? (
|
||||
<ToggleBackgroundActiveIcon />
|
||||
) : (
|
||||
|
@ -86,8 +86,7 @@
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
/* box-shadow: 0 0 0 2px var(--hot-pink); */
|
||||
&:focus-visible {
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
outline: none;
|
||||
z-index: 1;
|
||||
@ -161,3 +160,8 @@ input.zoom {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.pixelated {
|
||||
image-rendering: crisp-edges;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
@ -11,6 +11,25 @@ const Icon = (props: preact.JSX.HTMLAttributes) => (
|
||||
/>
|
||||
);
|
||||
|
||||
export const ToggleAliasingIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="8"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const ToggleAliasingActiveIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M12 3h5v2h2v2h2v5h-2V9h-2V7h-2V5h-3V3M21 12v5h-2v2h-2v2h-5v-2h3v-2h2v-2h2v-3h2M12 21H7v-2H5v-2H3v-5h2v3h2v2h2v2h3v2M3 12V7h2V5h2V3h5v2H9v2H7v2H5v3H3" />
|
||||
</Icon>
|
||||
);
|
||||
|
||||
export const ToggleBackgroundIcon = (props: preact.JSX.HTMLAttributes) => (
|
||||
<Icon {...props}>
|
||||
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.9 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z" />
|
||||
|
@ -14,6 +14,11 @@
|
||||
import * as WebCodecs from '../util/web-codecs';
|
||||
import { drawableToImageData } from './canvas';
|
||||
|
||||
/** If render engine is Safari */
|
||||
export const isSafari =
|
||||
/Safari\//.test(navigator.userAgent) &&
|
||||
!/Chrom(e|ium)\//.test(navigator.userAgent);
|
||||
|
||||
/**
|
||||
* Compare two objects, returning a boolean indicating if
|
||||
* they have the same properties and strictly equal values.
|
||||
|
@ -29,10 +29,9 @@ interface State {
|
||||
slightLoss: boolean;
|
||||
autoEdgePreservingFilter: boolean;
|
||||
decodingSpeedTier: number;
|
||||
photonNoiseIso: number;
|
||||
}
|
||||
|
||||
const maxSpeed = 7;
|
||||
|
||||
export class Options extends Component<Props, State> {
|
||||
static getDerivedStateFromProps(
|
||||
props: Props,
|
||||
@ -47,7 +46,7 @@ export class Options extends Component<Props, State> {
|
||||
// Create default form state from options
|
||||
return {
|
||||
options,
|
||||
effort: maxSpeed - options.speed,
|
||||
effort: options.effort,
|
||||
quality: options.quality,
|
||||
progressive: options.progressive,
|
||||
edgePreservingFilter: options.epf === -1 ? 2 : options.epf,
|
||||
@ -55,6 +54,7 @@ export class Options extends Component<Props, State> {
|
||||
slightLoss: options.lossyPalette,
|
||||
autoEdgePreservingFilter: options.epf === -1,
|
||||
decodingSpeedTier: options.decodingSpeedTier,
|
||||
photonNoiseIso: options.photonNoiseIso,
|
||||
};
|
||||
}
|
||||
|
||||
@ -87,15 +87,15 @@ export class Options extends Component<Props, State> {
|
||||
};
|
||||
|
||||
const newOptions: EncodeOptions = {
|
||||
speed: maxSpeed - optionState.effort,
|
||||
effort: optionState.effort,
|
||||
quality: optionState.lossless ? 100 : optionState.quality,
|
||||
progressive: optionState.progressive,
|
||||
epf: optionState.autoEdgePreservingFilter
|
||||
? -1
|
||||
: optionState.edgePreservingFilter,
|
||||
nearLossless: 0,
|
||||
lossyPalette: optionState.lossless ? optionState.slightLoss : false,
|
||||
decodingSpeedTier: optionState.decodingSpeedTier,
|
||||
photonNoiseIso: optionState.photonNoiseIso,
|
||||
};
|
||||
|
||||
// Updating options, so we don't recalculate in getDerivedStateFromProps.
|
||||
@ -121,6 +121,7 @@ export class Options extends Component<Props, State> {
|
||||
slightLoss,
|
||||
autoEdgePreservingFilter,
|
||||
decodingSpeedTier,
|
||||
photonNoiseIso,
|
||||
}: State,
|
||||
) {
|
||||
// I'm rendering both lossy and lossless forms, as it becomes much easier when
|
||||
@ -164,7 +165,6 @@ export class Options extends Component<Props, State> {
|
||||
<label class={style.optionToggle}>
|
||||
Auto edge filter
|
||||
<Checkbox
|
||||
name="autoEdgeFilter"
|
||||
checked={autoEdgePreservingFilter}
|
||||
onChange={this._inputChange(
|
||||
'autoEdgePreservingFilter',
|
||||
@ -199,6 +199,17 @@ export class Options extends Component<Props, State> {
|
||||
Optimise for decoding speed (worse compression):
|
||||
</Range>
|
||||
</div>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
min="0"
|
||||
max="50000"
|
||||
step="100"
|
||||
value={photonNoiseIso}
|
||||
onInput={this._inputChange('photonNoiseIso', 'number')}
|
||||
>
|
||||
Noise equivalent to ISO:
|
||||
</Range>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Expander>
|
||||
@ -212,8 +223,8 @@ export class Options extends Component<Props, State> {
|
||||
</label>
|
||||
<div class={style.optionOneCell}>
|
||||
<Range
|
||||
min="0"
|
||||
max={maxSpeed - 1}
|
||||
min="3"
|
||||
max="9"
|
||||
value={effort}
|
||||
onInput={this._inputChange('effort', 'number')}
|
||||
>
|
||||
|
@ -18,11 +18,11 @@ export const label = 'JPEG XL (beta)';
|
||||
export const mimeType = 'image/jxl';
|
||||
export const extension = 'jxl';
|
||||
export const defaultOptions: EncodeOptions = {
|
||||
speed: 4,
|
||||
effort: 7,
|
||||
quality: 75,
|
||||
progressive: false,
|
||||
epf: -1,
|
||||
nearLossless: 0,
|
||||
lossyPalette: false,
|
||||
decodingSpeedTier: 0,
|
||||
photonNoiseIso: 0,
|
||||
};
|
||||
|
@ -25,18 +25,6 @@ import { lookup as lookupMime } from 'mime-types';
|
||||
const manifestSize = ({ width, height }: { width: number; height: number }) =>
|
||||
`${width}x${height}`;
|
||||
|
||||
// Set by Netlify
|
||||
const branch = process.env.BRANCH;
|
||||
|
||||
const branchOriginTrialIds = new Map([
|
||||
[
|
||||
'live',
|
||||
'Aj5GY7W9AHM8di+yvMCajIhLRHoYN7slruwOYXE/Iub5hgmW/r2RQt07vrUuT4eUTkWxcyNCAVkiI+5ugdVW3gAAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MjM4MDE1OTl9',
|
||||
],
|
||||
]);
|
||||
|
||||
const originTrialId = branchOriginTrialIds.get(branch || '');
|
||||
|
||||
interface Output {
|
||||
[outputPath: string]: string;
|
||||
}
|
||||
@ -110,15 +98,6 @@ const toOutput: Output = {
|
||||
/*
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
|
||||
# Origin trial for WebAssembly SIMD.
|
||||
${
|
||||
originTrialId
|
||||
? ` Origin-Trial: ${originTrialId}`
|
||||
: `# Cannot find origin trial ID. process.env.BRANCH is: ${JSON.stringify(
|
||||
branch,
|
||||
)}`
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user