mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-12 08:47:31 +00:00
MozJPEG options updated
This commit is contained in:
@@ -30,15 +30,14 @@ export default class QuantizerOptions extends Component<Props, State> {
|
|||||||
@bind
|
@bind
|
||||||
onChange(event: Event) {
|
onChange(event: Event) {
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
|
const { options } = this.props;
|
||||||
|
|
||||||
const options: QuantizeOptions = {
|
const newOptions: QuantizeOptions = {
|
||||||
zx: form.zx ? inputFieldValueAsNumber(form.zx) : this.props.options.zx,
|
zx: inputFieldValueAsNumber(form.zx, options.zx),
|
||||||
maxNumColors: form.maxNumColors
|
maxNumColors: inputFieldValueAsNumber(form.maxNumColors, options.maxNumColors),
|
||||||
? inputFieldValueAsNumber(form.maxNumColors)
|
|
||||||
: this.props.options.maxNumColors,
|
|
||||||
dither: inputFieldValueAsNumber(form.dither),
|
dither: inputFieldValueAsNumber(form.dither),
|
||||||
};
|
};
|
||||||
this.props.onChange(options);
|
this.props.onChange(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ options }: Props, { extendedSettings }: State) {
|
render({ options }: Props, { extendedSettings }: State) {
|
||||||
|
|||||||
@@ -2,108 +2,140 @@ import { h, Component } from 'preact';
|
|||||||
import { bind } from '../../lib/initial-util';
|
import { bind } from '../../lib/initial-util';
|
||||||
import { inputFieldChecked, inputFieldValueAsNumber } from '../../lib/util';
|
import { inputFieldChecked, inputFieldValueAsNumber } from '../../lib/util';
|
||||||
import { EncodeOptions, MozJpegColorSpace } from './encoder-meta';
|
import { EncodeOptions, MozJpegColorSpace } from './encoder-meta';
|
||||||
import '../../custom-els/RangeInput';
|
import * as style from '../../components/options/style.scss';
|
||||||
|
import Checkbox from '../../components/checkbox';
|
||||||
|
import Expander from '../../components/expander';
|
||||||
|
import Select from '../../components/select';
|
||||||
|
import Range from '../../components/range';
|
||||||
|
import linkState from 'linkstate';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
options: EncodeOptions,
|
options: EncodeOptions;
|
||||||
onChange(newOptions: EncodeOptions): void,
|
onChange(newOptions: EncodeOptions): void;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
showAdvanced: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MozJPEGEncoderOptions extends Component<Props, State> {
|
||||||
|
state: State = {
|
||||||
|
showAdvanced: false,
|
||||||
|
};
|
||||||
|
|
||||||
export default class MozJPEGEncoderOptions extends Component<Props, {}> {
|
|
||||||
@bind
|
@bind
|
||||||
onChange(event: Event) {
|
onChange(event: Event) {
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
||||||
|
const { options } = this.props;
|
||||||
|
|
||||||
const options: EncodeOptions = {
|
const newOptions: EncodeOptions = {
|
||||||
// Copy over options the form doesn't currently care about, eg arithmetic
|
// Copy over options the form doesn't currently care about, eg arithmetic
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
// And now stuff from the form:
|
// And now stuff from the form:
|
||||||
// .checked
|
// .checked
|
||||||
baseline: inputFieldChecked(form.baseline),
|
baseline: inputFieldChecked(form.baseline, options.baseline),
|
||||||
progressive: inputFieldChecked(form.progressive),
|
progressive: inputFieldChecked(form.progressive, options.progressive),
|
||||||
optimize_coding: inputFieldChecked(form.optimize_coding),
|
optimize_coding: inputFieldChecked(form.optimize_coding, options.optimize_coding),
|
||||||
trellis_multipass: inputFieldChecked(form.trellis_multipass),
|
trellis_multipass: inputFieldChecked(form.trellis_multipass, options.trellis_multipass),
|
||||||
trellis_opt_zero: inputFieldChecked(form.trellis_opt_zero),
|
trellis_opt_zero: inputFieldChecked(form.trellis_opt_zero, options.trellis_opt_zero),
|
||||||
trellis_opt_table: inputFieldChecked(form.trellis_opt_table),
|
trellis_opt_table: inputFieldChecked(form.trellis_opt_table, options.trellis_opt_table),
|
||||||
// .value
|
// .value
|
||||||
quality: inputFieldValueAsNumber(form.quality),
|
quality: inputFieldValueAsNumber(form.quality, options.quality),
|
||||||
smoothing: inputFieldValueAsNumber(form.smoothing),
|
smoothing: inputFieldValueAsNumber(form.smoothing, options.smoothing),
|
||||||
color_space: inputFieldValueAsNumber(form.color_space),
|
color_space: inputFieldValueAsNumber(form.color_space, options.color_space),
|
||||||
quant_table: inputFieldValueAsNumber(form.quant_table),
|
quant_table: inputFieldValueAsNumber(form.quant_table, options.quant_table),
|
||||||
trellis_loops: inputFieldValueAsNumber(form.trellis_loops),
|
trellis_loops: inputFieldValueAsNumber(form.trellis_loops, options.trellis_loops),
|
||||||
};
|
};
|
||||||
this.props.onChange(options);
|
this.props.onChange(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ options }: Props) {
|
render({ options }: Props, { showAdvanced }: 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
|
||||||
// gathering the data.
|
// gathering the data.
|
||||||
return (
|
return (
|
||||||
<form>
|
<form class={style.optionsSection}>
|
||||||
<label>
|
<div class={style.optionOneCell}>
|
||||||
Quality:
|
<Range
|
||||||
<range-input
|
|
||||||
name="quality"
|
name="quality"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
value={'' + options.quality}
|
value={options.quality}
|
||||||
onChange={this.onChange}
|
onInput={this.onChange}
|
||||||
|
>
|
||||||
|
Quality:
|
||||||
|
</Range>
|
||||||
|
</div>
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
|
checked={showAdvanced}
|
||||||
|
onChange={linkState(this, 'showAdvanced')}
|
||||||
/>
|
/>
|
||||||
|
Show advanced settings
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<Expander>
|
||||||
<input
|
{showAdvanced ?
|
||||||
|
<div>
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
name="baseline"
|
name="baseline"
|
||||||
type="checkbox"
|
|
||||||
checked={options.baseline}
|
checked={options.baseline}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Baseline (worse but legacy-compatible)</span>
|
Pointless spec compliance
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: options.baseline ? 'none' : '' }}>
|
<Expander>
|
||||||
<input
|
{options.baseline ? null :
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
name="progressive"
|
name="progressive"
|
||||||
type="checkbox"
|
|
||||||
checked={options.progressive}
|
checked={options.progressive}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Progressive multi-pass rendering</span>
|
Progressive rendering
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: options.baseline ? '' : 'none' }}>
|
}
|
||||||
<input
|
</Expander>
|
||||||
|
<Expander>
|
||||||
|
{options.baseline ?
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
name="optimize_coding"
|
name="optimize_coding"
|
||||||
type="checkbox"
|
|
||||||
checked={options.optimize_coding}
|
checked={options.optimize_coding}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Optimize Huffman table</span>
|
Optimize Huffman table
|
||||||
</label>
|
</label>
|
||||||
<label>
|
: null
|
||||||
Smoothing:
|
}
|
||||||
<range-input
|
</Expander>
|
||||||
|
<div class={style.optionOneCell}>
|
||||||
|
<Range
|
||||||
name="smoothing"
|
name="smoothing"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
value={'' + options.smoothing}
|
value={options.smoothing}
|
||||||
onChange={this.onChange}
|
onInput={this.onChange}
|
||||||
/>
|
>
|
||||||
</label>
|
Smoothing:
|
||||||
<label>
|
</Range>
|
||||||
Output color space:
|
</div>
|
||||||
<select
|
<label class={style.optionTextFirst}>
|
||||||
|
Channels:
|
||||||
|
<Select
|
||||||
name="color_space"
|
name="color_space"
|
||||||
value={'' + options.color_space}
|
value={options.color_space}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
>
|
>
|
||||||
<option value={MozJpegColorSpace.GRAYSCALE}>Grayscale</option>
|
<option value={MozJpegColorSpace.GRAYSCALE}>Grayscale</option>
|
||||||
<option value={MozJpegColorSpace.RGB}>RGB (sub-optimal)</option>
|
<option value={MozJpegColorSpace.RGB}>RGB</option>
|
||||||
<option value={MozJpegColorSpace.YCbCr}>YCbCr (optimized for color)</option>
|
<option value={MozJpegColorSpace.YCbCr}>YCbCr</option>
|
||||||
</select>
|
</Select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class={style.optionTextFirst}>
|
||||||
Quantization table:
|
Quantization:
|
||||||
<select
|
<Select
|
||||||
name="quant_table"
|
name="quant_table"
|
||||||
value={'' + options.quant_table}
|
value={options.quant_table}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
>
|
>
|
||||||
<option value="0">JPEG Annex K</option>
|
<option value="0">JPEG Annex K</option>
|
||||||
@@ -115,45 +147,52 @@ export default class MozJPEGEncoderOptions extends Component<Props, {}> {
|
|||||||
<option value="6">Watson et al</option>
|
<option value="6">Watson et al</option>
|
||||||
<option value="7">Ahumada et al</option>
|
<option value="7">Ahumada et al</option>
|
||||||
<option value="8">Peterson et al</option>
|
<option value="8">Peterson et al</option>
|
||||||
</select>
|
</Select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class={style.optionInputFirst}>
|
||||||
<input
|
<Checkbox
|
||||||
name="trellis_multipass"
|
name="trellis_multipass"
|
||||||
type="checkbox"
|
|
||||||
checked={options.trellis_multipass}
|
checked={options.trellis_multipass}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Consider multiple scans during trellis quantization</span>
|
Trellis multipass
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: options.trellis_multipass ? '' : 'none' }}>
|
<Expander>
|
||||||
<input
|
{options.trellis_multipass ?
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
name="trellis_opt_zero"
|
name="trellis_opt_zero"
|
||||||
type="checkbox"
|
|
||||||
checked={options.trellis_opt_zero}
|
checked={options.trellis_opt_zero}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Optimize runs of zero blocks</span>
|
Optimize zero block runs
|
||||||
</label>
|
</label>
|
||||||
<label>
|
: null
|
||||||
<input
|
}
|
||||||
|
</Expander>
|
||||||
|
<label class={style.optionInputFirst}>
|
||||||
|
<Checkbox
|
||||||
name="trellis_opt_table"
|
name="trellis_opt_table"
|
||||||
type="checkbox"
|
|
||||||
checked={options.trellis_opt_table}
|
checked={options.trellis_opt_table}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
<span>Optimize after trellis quantization</span>
|
Optimize after trellis quantization
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<div class={style.optionOneCell}>
|
||||||
Trellis quantization passes:
|
<Range
|
||||||
<range-input
|
|
||||||
name="trellis_loops"
|
name="trellis_loops"
|
||||||
min="1"
|
min="1"
|
||||||
max="50"
|
max="50"
|
||||||
value={'' + options.trellis_loops}
|
value={options.trellis_loops}
|
||||||
onChange={this.onChange}
|
onInput={this.onChange}
|
||||||
/>
|
>
|
||||||
</label>
|
Trellis quantization passes:
|
||||||
|
</Range>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</Expander>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import linkState from 'linkstate';
|
import linkState from 'linkstate';
|
||||||
import { bind, linkRef } from '../../lib/initial-util';
|
import { bind, linkRef } from '../../lib/initial-util';
|
||||||
import { inputFieldValueAsNumber } from '../../lib/util';
|
import { inputFieldValueAsNumber, inputFieldValue } from '../../lib/util';
|
||||||
import { ResizeOptions } from './processor-meta';
|
import { ResizeOptions } from './processor-meta';
|
||||||
import * as style from '../../components/options/style.scss';
|
import * as style from '../../components/options/style.scss';
|
||||||
import Checkbox from '../../components/checkbox';
|
import Checkbox from '../../components/checkbox';
|
||||||
@@ -27,18 +27,21 @@ export default class ResizerOptions extends Component<Props, State> {
|
|||||||
form?: HTMLFormElement;
|
form?: HTMLFormElement;
|
||||||
|
|
||||||
private reportOptions() {
|
private reportOptions() {
|
||||||
const width = this.form!.width as HTMLInputElement;
|
const form = this.form!;
|
||||||
const height = this.form!.height as HTMLInputElement;
|
const width = form.width as HTMLInputElement;
|
||||||
|
const height = form.height as HTMLInputElement;
|
||||||
|
const { options } = this.props;
|
||||||
|
|
||||||
if (!width.checkValidity() || !height.checkValidity()) return;
|
if (!width.checkValidity() || !height.checkValidity()) return;
|
||||||
|
|
||||||
const options: ResizeOptions = {
|
const newOptions: ResizeOptions = {
|
||||||
width: inputFieldValueAsNumber(width),
|
width: inputFieldValueAsNumber(width),
|
||||||
height: inputFieldValueAsNumber(height),
|
height: inputFieldValueAsNumber(height),
|
||||||
method: this.form!.resizeMethod.value,
|
method: form.resizeMethod.value,
|
||||||
fitMethod: this.form!.fitMethod ? this.form!.fitMethod.value : this.props.options.fitMethod,
|
// Casting, as the formfield only returns the correct values.
|
||||||
|
fitMethod: inputFieldValue(form.fitMethod, options.fitMethod) as ResizeOptions['fitMethod'],
|
||||||
};
|
};
|
||||||
this.props.onChange(options);
|
this.props.onChange(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
|
|||||||
@@ -192,8 +192,6 @@ export default class Options extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/*
|
|
||||||
|
|
||||||
{EncoderOptionComponent &&
|
{EncoderOptionComponent &&
|
||||||
<EncoderOptionComponent
|
<EncoderOptionComponent
|
||||||
options={
|
options={
|
||||||
@@ -205,6 +203,8 @@ export default class Options extends Component<Props, State> {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{/*
|
||||||
|
|
||||||
<div class={style.row}>
|
<div class={style.row}>
|
||||||
<button onClick={this.onCopyToOtherClick}>Copy settings to other side</button>
|
<button onClick={this.onCopyToOtherClick}>Copy settings to other side</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-decoration-style: dotted;
|
text-decoration-style: dotted;
|
||||||
|
text-underline-position: under;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|||||||
@@ -203,25 +203,40 @@ export function nativeResize(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
||||||
|
* @param defaultVal Value to return if 'field' doesn't exist.
|
||||||
*/
|
*/
|
||||||
export function inputFieldValueAsNumber(field: any): number {
|
export function inputFieldValueAsNumber(field: any, defaultVal: number = 0): number {
|
||||||
return Number((field as HTMLInputElement).value);
|
if (!field) return defaultVal;
|
||||||
|
return Number(inputFieldValue(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
||||||
|
* @param defaultVal Value to return if 'field' doesn't exist.
|
||||||
*/
|
*/
|
||||||
export function inputFieldCheckedAsNumber(field: any): number {
|
export function inputFieldCheckedAsNumber(field: any, defaultVal: number = 0): number {
|
||||||
|
if (!field) return defaultVal;
|
||||||
return Number(inputFieldChecked(field));
|
return Number(inputFieldChecked(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
||||||
|
* @param defaultVal Value to return if 'field' doesn't exist.
|
||||||
*/
|
*/
|
||||||
export function inputFieldChecked(field: any): boolean {
|
export function inputFieldChecked(field: any, defaultVal: boolean = false): boolean {
|
||||||
|
if (!field) return defaultVal;
|
||||||
return (field as HTMLInputElement).checked;
|
return (field as HTMLInputElement).checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field An HTMLInputElement, but the casting is done here to tidy up onChange.
|
||||||
|
* @param defaultVal Value to return if 'field' doesn't exist.
|
||||||
|
*/
|
||||||
|
export function inputFieldValue(field: any, defaultVal: string = ''): string {
|
||||||
|
if (!field) return defaultVal;
|
||||||
|
return (field as HTMLInputElement).value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a promise that resolves when the user types the konami code.
|
* Creates a promise that resolves when the user types the konami code.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user