Add Hq{2,3,4}x (#624)

* Scaling works on native

* It works in wasm

* Integrate with UI

* Remove benchmark

* Integrate Hqx into Resizer module

* Link against repo for hqx

* Remove unused defaultOpts

* Re-add test file

* Adding size dropdown

* Chrome: go and sit on the naughty step

* Better docs

* Review

* Add link to crbug

* Update src/codecs/processor-worker/index.ts

Co-Authored-By: Jake Archibald <jaffathecake@gmail.com>

* Terminate worker inbetween resize jobs
This commit is contained in:
Surma
2019-06-18 12:23:22 +01:00
committed by Jake Archibald
parent 24254df7db
commit 66feffcc49
25 changed files with 539 additions and 23 deletions

View File

@@ -12,8 +12,9 @@ import Select from '../../components/select';
interface Props {
isVector: Boolean;
inputWidth: number;
inputHeight: number;
options: ResizeOptions;
aspect: number;
onChange(newOptions: ResizeOptions): void;
}
@@ -21,12 +22,34 @@ interface State {
maintainAspect: boolean;
}
const sizePresets = [0.25, 0.3333, 0.5, 1, 2, 3, 4];
/**
* Should we allow the user to select hqx? Chrome currently has a wasm bug, so we currently avoid it
* there, unless overridden.
* crbug.com/974804
*/
const allowHqx: boolean = (() => {
const url = new URL(location.href);
return url.searchParams.has('allow-hqx')
// Yep. UA sniffing. Let's hope we can remove this soon.
// Block browsers with Chrome/, unless they also have Edge/ (since the Edge UA includes Chrome/)
|| !navigator.userAgent.includes('Chrome/') || navigator.userAgent.includes('Edge/');
})();
export default class ResizerOptions extends Component<Props, State> {
state: State = {
maintainAspect: true,
};
form?: HTMLFormElement;
private form?: HTMLFormElement;
private presetWidths: { [idx: number]: number } = {};
private presetHeights: { [idx: number]: number } = {};
constructor(props: Props) {
super(props);
this.generatePresetValues(props.inputWidth, props.inputHeight);
}
private reportOptions() {
const form = this.form!;
@@ -53,18 +76,31 @@ export default class ResizerOptions extends Component<Props, State> {
this.reportOptions();
}
private getAspect() {
return this.props.inputWidth / this.props.inputHeight;
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (!prevState.maintainAspect && this.state.maintainAspect) {
this.form!.height.value = Math.round(Number(this.form!.width.value) / this.props.aspect);
this.form!.height.value = Math.round(Number(this.form!.width.value) / this.getAspect());
this.reportOptions();
}
}
componentWillReceiveProps(nextProps: Props) {
if (
this.props.inputWidth !== nextProps.inputWidth ||
this.props.inputHeight !== nextProps.inputHeight
) {
this.generatePresetValues(nextProps.inputWidth, nextProps.inputHeight);
}
}
@bind
private onWidthInput() {
if (this.state.maintainAspect) {
const width = inputFieldValueAsNumber(this.form!.width);
this.form!.height.value = Math.round(width / this.props.aspect);
this.form!.height.value = Math.round(width / this.getAspect());
}
this.reportOptions();
@@ -74,12 +110,44 @@ export default class ResizerOptions extends Component<Props, State> {
private onHeightInput() {
if (this.state.maintainAspect) {
const height = inputFieldValueAsNumber(this.form!.height);
this.form!.width.value = Math.round(height * this.props.aspect);
this.form!.width.value = Math.round(height * this.getAspect());
}
this.reportOptions();
}
private generatePresetValues(width: number, height: number) {
for (const preset of sizePresets) {
this.presetWidths[preset] = Math.round(width * preset);
this.presetHeights[preset] = Math.round(height * preset);
}
}
private getPreset(): number | string {
const { width, height } = this.props.options;
for (const preset of sizePresets) {
if (
width === this.presetWidths[preset] &&
height === this.presetHeights[preset]
) return preset;
}
return 'custom';
}
@bind
private onPresetChange(event: Event) {
const select = event.target as HTMLSelectElement;
if (select.value === 'custom') return;
const multiplier = Number(select.value);
(this.form!.width as HTMLInputElement).value =
Math.round(this.props.inputWidth * multiplier) + '';
(this.form!.height as HTMLInputElement).value =
Math.round(this.props.inputHeight * multiplier) + '';
this.reportOptions();
}
render({ options, isVector }: Props, { maintainAspect }: State) {
return (
<form ref={linkRef(this, 'form')} class={style.optionsSection} onSubmit={preventDefault}>
@@ -95,12 +163,22 @@ export default class ResizerOptions extends Component<Props, State> {
<option value="mitchell">Mitchell</option>
<option value="catrom">Catmull-Rom</option>
<option value="triangle">Triangle (bilinear)</option>
{allowHqx && <option value="hqx">hqx (pixel art)</option>}
<option value="browser-pixelated">Browser pixelated</option>
<option value="browser-low">Browser low quality</option>
<option value="browser-medium">Browser medium quality</option>
<option value="browser-high">Browser high quality</option>
</Select>
</label>
<label class={style.optionTextFirst}>
Preset:
<Select value={this.getPreset()} onChange={this.onPresetChange}>
{sizePresets.map(preset =>
<option value={preset}>{preset * 100}%</option>,
)}
<option value="custom">Custom</option>
</Select>
</label>
<label class={style.optionTextFirst}>
Width:
<input