Adding resize preprocessor (#152)

* Adding resize preprocessor

* Using ! on form

* Haha oops

* Using createImageBitmapPolyfill

* Updating package.json

* Oops again

* Ooops again
This commit is contained in:
Jake Archibald
2018-09-05 15:46:26 +01:00
committed by GitHub
parent 700b1f15cd
commit ea5d3c2d78
11 changed files with 286 additions and 19 deletions

View File

@@ -0,0 +1,128 @@
import { h, Component } from 'preact';
import linkState from 'linkstate';
import { bind, inputFieldValueAsNumber } from '../../lib/util';
import { ResizeOptions } from './resize';
interface Props {
options: ResizeOptions;
aspect: number;
onChange(newOptions: ResizeOptions): void;
}
interface State {
maintainAspect: boolean;
}
export default class ResizerOptions extends Component<Props, State> {
state: State = {
maintainAspect: true,
};
form?: HTMLFormElement;
reportOptions() {
const width = this.form!.width as HTMLInputElement;
const height = this.form!.height as HTMLInputElement;
if (!width.checkValidity() || !height.checkValidity()) return;
const options: ResizeOptions = {
width: inputFieldValueAsNumber(width),
height: inputFieldValueAsNumber(height),
method: this.form!.resizeMethod.value,
fitMethod: this.form!.fitMethod.value,
};
this.props.onChange(options);
}
@bind
onChange(event: Event) {
this.reportOptions();
}
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.reportOptions();
}
}
@bind
onWidthInput(event: Event) {
if (!this.state.maintainAspect) return;
const width = inputFieldValueAsNumber(this.form!.width);
this.form!.height.value = Math.round(width / this.props.aspect);
}
@bind
onHeightInput(event: Event) {
if (!this.state.maintainAspect) return;
const height = inputFieldValueAsNumber(this.form!.height);
this.form!.width.value = Math.round(height * this.props.aspect);
}
render({ options, aspect }: Props, { maintainAspect }: State) {
return (
<form ref={el => this.form = el}>
<label>
Method:
<select
name="resizeMethod"
value={options.method}
onChange={this.onChange}
>
<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>
Width:
<input
required
name="width"
type="number"
min="1"
value={'' + options.width}
onChange={this.onChange}
onInput={this.onWidthInput}
/>
</label>
<label>
Height:
<input
required
name="height"
type="number"
min="1"
value={'' + options.height}
onChange={this.onChange}
/>
</label>
<label>
<input
name="maintainAspect"
type="checkbox"
checked={maintainAspect}
onChange={linkState(this, 'maintainAspect')}
/>
Maintain aspect ratio
</label>
<label style={{ display: maintainAspect ? 'none' : '' }}>
Fit method:
<select
name="fitMethod"
value={options.fitMethod}
onChange={this.onChange}
>
<option value="stretch">Stretch</option>
<option value="cover">Cover</option>
</select>
</label>
</form>
);
}
}

View File

@@ -0,0 +1,46 @@
import { bitmapToImageData, createImageBitmapPolyfill } from '../../lib/util';
type CreateImageBitmapResize = 'pixelated' | 'low' | 'medium' | 'high';
export async function resize(data: ImageData, opts: ResizeOptions): Promise<ImageData> {
let sx = 0;
let sy = 0;
let sw = data.width;
let sh = data.height;
if (opts.fitMethod === 'cover') {
const currentAspect = data.width / data.height;
const endAspect = opts.width / opts.height;
if (endAspect > currentAspect) {
sh = opts.height / (opts.width / data.width);
sy = (data.height - sh) / 2;
} else {
sw = opts.width / (opts.height / data.height);
sx = (data.width - sw) / 2;
}
}
const bmp = await createImageBitmapPolyfill(data, sx, sy, sw, sh, {
resizeQuality: opts.method.slice('browser-'.length) as CreateImageBitmapResize,
resizeWidth: opts.width,
resizeHeight: opts.height,
});
return bitmapToImageData(bmp);
}
export interface ResizeOptions {
width: number;
height: number;
method: 'browser-pixelated' | 'browser-low' | 'browser-medium' | 'browser-high';
fitMethod: 'stretch' | 'cover';
}
export const defaultOptions: ResizeOptions = {
// Width and height will always default to the image size.
// This is set elsewhere.
width: 1,
height: 1,
method: 'browser-high',
fitMethod: 'stretch',
};