mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-13 17:27:09 +00:00
JPEG-XL: Better 'effort' and adding noise synth (#1039)
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -10,15 +10,13 @@ using namespace emscripten;
|
|||||||
thread_local const val Uint8Array = val::global("Uint8Array");
|
thread_local const val Uint8Array = val::global("Uint8Array");
|
||||||
|
|
||||||
struct JXLOptions {
|
struct JXLOptions {
|
||||||
// 1 = slowest
|
int effort;
|
||||||
// 7 = fastest
|
|
||||||
int speed;
|
|
||||||
float quality;
|
float quality;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
int epf;
|
int epf;
|
||||||
int nearLossless;
|
|
||||||
bool lossyPalette;
|
bool lossyPalette;
|
||||||
size_t decodingSpeedTier;
|
size_t decodingSpeedTier;
|
||||||
|
float photonNoiseIso;
|
||||||
};
|
};
|
||||||
|
|
||||||
val encode(std::string image, int width, int height, JXLOptions options) {
|
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;
|
pool_ptr = &pool;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cparams.epf = options.epf;
|
size_t st = 10 - options.effort;
|
||||||
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
|
cparams.speed_tier = jxl::SpeedTier(st);
|
||||||
cparams.decoding_speed_tier = options.decodingSpeedTier;
|
|
||||||
|
|
||||||
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.lossy_palette = true;
|
||||||
cparams.palette_colors = 0;
|
cparams.palette_colors = 0;
|
||||||
cparams.options.predictor = jxl::Predictor::Zero;
|
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) {
|
EMSCRIPTEN_BINDINGS(my_module) {
|
||||||
value_object<JXLOptions>("JXLOptions")
|
value_object<JXLOptions>("JXLOptions")
|
||||||
.field("speed", &JXLOptions::speed)
|
.field("effort", &JXLOptions::effort)
|
||||||
.field("quality", &JXLOptions::quality)
|
.field("quality", &JXLOptions::quality)
|
||||||
.field("progressive", &JXLOptions::progressive)
|
.field("progressive", &JXLOptions::progressive)
|
||||||
.field("nearLossless", &JXLOptions::nearLossless)
|
|
||||||
.field("lossyPalette", &JXLOptions::lossyPalette)
|
.field("lossyPalette", &JXLOptions::lossyPalette)
|
||||||
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
|
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
|
||||||
|
.field("photonNoiseIso", &JXLOptions::photonNoiseIso)
|
||||||
.field("epf", &JXLOptions::epf);
|
.field("epf", &JXLOptions::epf);
|
||||||
|
|
||||||
function("encode", &encode);
|
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 {
|
export interface EncodeOptions {
|
||||||
speed: number;
|
effort: number;
|
||||||
quality: number;
|
quality: number;
|
||||||
progressive: boolean;
|
progressive: boolean;
|
||||||
epf: number;
|
epf: number;
|
||||||
nearLossless: number;
|
|
||||||
lossyPalette: boolean;
|
lossyPalette: boolean;
|
||||||
decodingSpeedTier: number;
|
decodingSpeedTier: number;
|
||||||
|
photonNoiseIso: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JXLModule extends EmscriptenWasm.Module {
|
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.
@@ -20,7 +20,7 @@ const REFLECTED_ATTRIBUTES = [
|
|||||||
'disabled',
|
'disabled',
|
||||||
];
|
];
|
||||||
|
|
||||||
function getPrescision(value: string): number {
|
function getPrecision(value: string): number {
|
||||||
const afterDecimal = value.split('.')[1];
|
const afterDecimal = value.split('.')[1];
|
||||||
return afterDecimal ? afterDecimal.length : 0;
|
return afterDecimal ? afterDecimal.length : 0;
|
||||||
}
|
}
|
||||||
@@ -112,18 +112,24 @@ class RangeInputElement extends HTMLElement {
|
|||||||
this.dispatchEvent(retargetted);
|
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 = () => {
|
private _update = () => {
|
||||||
// Not connected?
|
// Not connected?
|
||||||
if (!this._valueDisplay) return;
|
if (!this._valueDisplay) return;
|
||||||
const value = Number(this.value) || 0;
|
const value = Number(this.value) || 0;
|
||||||
const min = Number(this.min) || 0;
|
const min = Number(this.min) || 0;
|
||||||
const max = Number(this.max) || 100;
|
const max = Number(this.max) || 100;
|
||||||
const labelPrecision =
|
|
||||||
Number(this.labelPrecision) || getPrescision(this.step) || 0;
|
|
||||||
const percent = (100 * (value - min)) / (max - min);
|
const percent = (100 * (value - min)) / (max - min);
|
||||||
const displayValue = labelPrecision
|
const displayValue = this._getDisplayValue(value);
|
||||||
? value.toFixed(labelPrecision)
|
|
||||||
: Math.round(value).toString();
|
|
||||||
|
|
||||||
this._valueDisplay!.textContent = displayValue;
|
this._valueDisplay!.textContent = displayValue;
|
||||||
this.style.setProperty('--value-percent', percent + '%');
|
this.style.setProperty('--value-percent', percent + '%');
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
text-decoration-style: dotted;
|
text-decoration-style: dotted;
|
||||||
text-decoration-color: var(--main-theme-color);
|
text-decoration-color: var(--main-theme-color);
|
||||||
text-underline-position: under;
|
text-underline-position: under;
|
||||||
width: 48px;
|
width: 54px;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ interface State {
|
|||||||
slightLoss: boolean;
|
slightLoss: boolean;
|
||||||
autoEdgePreservingFilter: boolean;
|
autoEdgePreservingFilter: boolean;
|
||||||
decodingSpeedTier: number;
|
decodingSpeedTier: number;
|
||||||
|
photonNoiseIso: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxSpeed = 7;
|
|
||||||
|
|
||||||
export class Options extends Component<Props, State> {
|
export class Options extends Component<Props, State> {
|
||||||
static getDerivedStateFromProps(
|
static getDerivedStateFromProps(
|
||||||
props: Props,
|
props: Props,
|
||||||
@@ -47,7 +46,7 @@ export class Options extends Component<Props, State> {
|
|||||||
// Create default form state from options
|
// Create default form state from options
|
||||||
return {
|
return {
|
||||||
options,
|
options,
|
||||||
effort: maxSpeed - options.speed,
|
effort: options.effort,
|
||||||
quality: options.quality,
|
quality: options.quality,
|
||||||
progressive: options.progressive,
|
progressive: options.progressive,
|
||||||
edgePreservingFilter: options.epf === -1 ? 2 : options.epf,
|
edgePreservingFilter: options.epf === -1 ? 2 : options.epf,
|
||||||
@@ -55,6 +54,7 @@ export class Options extends Component<Props, State> {
|
|||||||
slightLoss: options.lossyPalette,
|
slightLoss: options.lossyPalette,
|
||||||
autoEdgePreservingFilter: options.epf === -1,
|
autoEdgePreservingFilter: options.epf === -1,
|
||||||
decodingSpeedTier: options.decodingSpeedTier,
|
decodingSpeedTier: options.decodingSpeedTier,
|
||||||
|
photonNoiseIso: options.photonNoiseIso,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,15 +87,15 @@ export class Options extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const newOptions: EncodeOptions = {
|
const newOptions: EncodeOptions = {
|
||||||
speed: maxSpeed - optionState.effort,
|
effort: optionState.effort,
|
||||||
quality: optionState.lossless ? 100 : optionState.quality,
|
quality: optionState.lossless ? 100 : optionState.quality,
|
||||||
progressive: optionState.progressive,
|
progressive: optionState.progressive,
|
||||||
epf: optionState.autoEdgePreservingFilter
|
epf: optionState.autoEdgePreservingFilter
|
||||||
? -1
|
? -1
|
||||||
: optionState.edgePreservingFilter,
|
: optionState.edgePreservingFilter,
|
||||||
nearLossless: 0,
|
|
||||||
lossyPalette: optionState.lossless ? optionState.slightLoss : false,
|
lossyPalette: optionState.lossless ? optionState.slightLoss : false,
|
||||||
decodingSpeedTier: optionState.decodingSpeedTier,
|
decodingSpeedTier: optionState.decodingSpeedTier,
|
||||||
|
photonNoiseIso: optionState.photonNoiseIso,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updating options, so we don't recalculate in getDerivedStateFromProps.
|
// Updating options, so we don't recalculate in getDerivedStateFromProps.
|
||||||
@@ -121,6 +121,7 @@ export class Options extends Component<Props, State> {
|
|||||||
slightLoss,
|
slightLoss,
|
||||||
autoEdgePreservingFilter,
|
autoEdgePreservingFilter,
|
||||||
decodingSpeedTier,
|
decodingSpeedTier,
|
||||||
|
photonNoiseIso,
|
||||||
}: State,
|
}: State,
|
||||||
) {
|
) {
|
||||||
// I'm rendering both lossy and lossless forms, as it becomes much easier when
|
// 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}>
|
<label class={style.optionToggle}>
|
||||||
Auto edge filter
|
Auto edge filter
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="autoEdgeFilter"
|
|
||||||
checked={autoEdgePreservingFilter}
|
checked={autoEdgePreservingFilter}
|
||||||
onChange={this._inputChange(
|
onChange={this._inputChange(
|
||||||
'autoEdgePreservingFilter',
|
'autoEdgePreservingFilter',
|
||||||
@@ -199,6 +199,17 @@ export class Options extends Component<Props, State> {
|
|||||||
Optimise for decoding speed (worse compression):
|
Optimise for decoding speed (worse compression):
|
||||||
</Range>
|
</Range>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Expander>
|
</Expander>
|
||||||
@@ -212,8 +223,8 @@ export class Options extends Component<Props, State> {
|
|||||||
</label>
|
</label>
|
||||||
<div class={style.optionOneCell}>
|
<div class={style.optionOneCell}>
|
||||||
<Range
|
<Range
|
||||||
min="0"
|
min="3"
|
||||||
max={maxSpeed - 1}
|
max="9"
|
||||||
value={effort}
|
value={effort}
|
||||||
onInput={this._inputChange('effort', 'number')}
|
onInput={this._inputChange('effort', 'number')}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export const label = 'JPEG XL (beta)';
|
|||||||
export const mimeType = 'image/jxl';
|
export const mimeType = 'image/jxl';
|
||||||
export const extension = 'jxl';
|
export const extension = 'jxl';
|
||||||
export const defaultOptions: EncodeOptions = {
|
export const defaultOptions: EncodeOptions = {
|
||||||
speed: 4,
|
effort: 7,
|
||||||
quality: 75,
|
quality: 75,
|
||||||
progressive: false,
|
progressive: false,
|
||||||
epf: -1,
|
epf: -1,
|
||||||
nearLossless: 0,
|
|
||||||
lossyPalette: false,
|
lossyPalette: false,
|
||||||
decodingSpeedTier: 0,
|
decodingSpeedTier: 0,
|
||||||
|
photonNoiseIso: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user