import { EncodeOptions, defaultOptions, AVIFTune } from '../shared/meta'; import type WorkerBridge from 'client/lazy-app/worker-bridge'; import { h, Component } from 'preact'; import { preventDefault, shallowEqual } from 'client/lazy-app/util'; import * as style from 'client/lazy-app/Compress/Options/style.css'; import Checkbox from 'client/lazy-app/Compress/Options/Checkbox'; import Expander from 'client/lazy-app/Compress/Options/Expander'; import Select from 'client/lazy-app/Compress/Options/Select'; import Range from 'client/lazy-app/Compress/Options/Range'; import linkState from 'linkstate'; import Revealer from 'client/lazy-app/Compress/Options/Revealer'; export const encode = ( signal: AbortSignal, workerBridge: WorkerBridge, imageData: ImageData, options: EncodeOptions, ) => workerBridge.avifEncode(signal, imageData, options); interface Props { options: EncodeOptions; onChange(newOptions: EncodeOptions): void; } interface State { options: EncodeOptions; lossless: boolean; quality: number; showAdvanced: boolean; separateAlpha: boolean; alphaQuality: number; chromaDeltaQ: boolean; subsample: number; tileRows: number; tileCols: number; effort: number; sharpness: number; denoiseLevel: number; aqMode: number; tune: AVIFTune; } const maxQuant = 63; const maxSpeed = 10; export class Options extends Component { static getDerivedStateFromProps( props: Props, state: State, ): Partial | null { if (state.options && shallowEqual(state.options, props.options)) { return null; } const { options } = props; const lossless = options.cqLevel === 0 && options.cqAlphaLevel <= 0 && options.subsample == 3; const separateAlpha = options.cqAlphaLevel !== -1; const cqLevel = lossless ? defaultOptions.cqLevel : options.cqLevel; // Create default form state from options return { options, lossless, quality: maxQuant - cqLevel, separateAlpha, alphaQuality: maxQuant - (separateAlpha ? options.cqAlphaLevel : defaultOptions.cqLevel), subsample: options.subsample === 0 || lossless ? defaultOptions.subsample : options.subsample, tileRows: options.tileRowsLog2, tileCols: options.tileColsLog2, effort: maxSpeed - options.speed, chromaDeltaQ: options.chromaDeltaQ, sharpness: options.sharpness, denoiseLevel: options.denoiseLevel, tune: options.tune, }; } // The rest of the defaults are set in getDerivedStateFromProps state: State = { showAdvanced: false, } as State; private _inputChangeCallbacks = new Map void>(); private _inputChange = ( prop: keyof State, type: 'number' | 'boolean' | 'string', ) => { // Cache the callback for performance if (!this._inputChangeCallbacks.has(prop)) { this._inputChangeCallbacks.set(prop, (event: Event) => { const formEl = event.target as HTMLInputElement | HTMLSelectElement; const newVal = type === 'boolean' ? 'checked' in formEl ? formEl.checked : !!formEl.value : type === 'number' ? Number(formEl.value) : formEl.value; const newState: Partial = { [prop]: newVal, }; const optionState = { ...this.state, ...newState, }; const newOptions: EncodeOptions = { cqLevel: optionState.lossless ? 0 : maxQuant - optionState.quality, cqAlphaLevel: optionState.lossless || !optionState.separateAlpha ? -1 : maxQuant - optionState.alphaQuality, // Always set to 4:4:4 if lossless subsample: optionState.lossless ? 3 : optionState.subsample, tileColsLog2: optionState.tileCols, tileRowsLog2: optionState.tileRows, speed: maxSpeed - optionState.effort, chromaDeltaQ: optionState.chromaDeltaQ, sharpness: optionState.sharpness, denoiseLevel: optionState.denoiseLevel, tune: optionState.tune, }; // Updating options, so we don't recalculate in getDerivedStateFromProps. newState.options = newOptions; this.setState( // It isn't clear to me why I have to cast this :) newState as State, ); this.props.onChange(newOptions); }); } return this._inputChangeCallbacks.get(prop)!; }; render( _: Props, { effort, lossless, alphaQuality, separateAlpha, quality, showAdvanced, subsample, tileCols, tileRows, chromaDeltaQ, sharpness, denoiseLevel, tune, }: State, ) { return (
{!lossless && (
Quality:
)}
{showAdvanced && (
{!lossless && (
{separateAlpha && (
Alpha quality:
)}
Sharpness:
Noise synthesis:
)}
Log2 of tile rows:
Log2 of tile cols:
)}
Effort:
); } }