import { h, Component } from 'preact'; import { bind } from '../../lib/initial-util'; import { inputFieldCheckedAsNumber, inputFieldValueAsNumber } from '../../lib/util'; import { EncodeOptions, WebPImageHint } from './encoder-meta'; 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'; interface Props { options: EncodeOptions; onChange(newOptions: EncodeOptions): void; } interface State { showAdvanced: boolean; } // From kLosslessPresets in config_enc.c // The format is [method, quality]. const losslessPresets:[number, number][] = [ [0, 0], [1, 20], [2, 25], [3, 30], [3, 50], [4, 50], [4, 75], [4, 90], [5, 90], [6, 100], ]; const losslessPresetDefault = 6; function determineLosslessQuality(quality: number, method: number): number { const index = losslessPresets.findIndex( ([presetMethod, presetQuality]) => presetMethod === method && presetQuality === quality, ); if (index !== -1) return index; // Quality doesn't match one of the presets. // This can happen when toggling 'lossless'. return losslessPresetDefault; } export default class WebPEncoderOptions extends Component { state: State = { showAdvanced: false, }; @bind onChange(event: Event) { const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement; const lossless = inputFieldCheckedAsNumber(form.lossless); const { options } = this.props; const losslessPresetValue = inputFieldValueAsNumber( form.lossless_preset, determineLosslessQuality(options.quality, options.method), ); const newOptions: EncodeOptions = { // Copy over options the form doesn't care about, eg emulate_jpeg_size ...options, // And now stuff from the form: lossless, // Special-cased inputs: // In lossless mode, the quality is derived from the preset. quality: lossless ? losslessPresets[losslessPresetValue][1] : inputFieldValueAsNumber(form.quality, options.quality), // In lossless mode, the method is derived from the preset. method: lossless ? losslessPresets[losslessPresetValue][0] : inputFieldValueAsNumber(form.method_input, options.method), image_hint: inputFieldCheckedAsNumber(form.image_hint, options.image_hint) ? WebPImageHint.WEBP_HINT_GRAPH : WebPImageHint.WEBP_HINT_DEFAULT, // .checked exact: inputFieldCheckedAsNumber(form.exact, options.exact), alpha_compression: inputFieldCheckedAsNumber( form.alpha_compression, options.alpha_compression, ), autofilter: inputFieldCheckedAsNumber(form.autofilter, options.autofilter), filter_type: inputFieldCheckedAsNumber(form.filter_type, options.filter_type), use_sharp_yuv: inputFieldCheckedAsNumber(form.use_sharp_yuv, options.use_sharp_yuv), // .value near_lossless: 100 - inputFieldValueAsNumber(form.near_lossless, 100 - options.near_lossless), alpha_quality: inputFieldValueAsNumber(form.alpha_quality, options.alpha_quality), alpha_filtering: inputFieldValueAsNumber(form.alpha_filtering, options.alpha_filtering), sns_strength: inputFieldValueAsNumber(form.sns_strength, options.sns_strength), filter_strength: inputFieldValueAsNumber(form.filter_strength, options.filter_strength), filter_sharpness: 7 - inputFieldValueAsNumber(form.filter_sharpness, 7 - options.filter_sharpness), pass: inputFieldValueAsNumber(form.pass, options.pass), preprocessing: inputFieldValueAsNumber(form.preprocessing, options.preprocessing), segments: inputFieldValueAsNumber(form.segments, options.segments), partitions: inputFieldValueAsNumber(form.partitions, options.partitions), }; this.props.onChange(newOptions); } private _losslessSpecificOptions(options: EncodeOptions) { return (
Effort:
Slight loss:
); } private _lossySpecificOptions(options: EncodeOptions) { const { showAdvanced } = this.state; return (
Effort:
Quality:
{showAdvanced ?
Alpha quality:
Alpha filter quality:
{options.autofilter ? null :
Filter strength:
}
Filter sharpness:
Passes:
Spacial noise shaping:
Segments:
Partitions:
: null }
); } render({ options }: Props) { // I'm rendering both lossy and lossless forms, as it becomes much easier when // gathering the data. return (
{options.lossless ? this._losslessSpecificOptions(options) : this._lossySpecificOptions(options) }
); } }