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

@@ -10,6 +10,7 @@ import ResultCache from './result-cache';
import * as quantizer from '../../codecs/imagequant/quantizer';
import * as optiPNG from '../../codecs/optipng/encoder';
import * as resizer from '../../codecs/resize/resize';
import * as mozJPEG from '../../codecs/mozjpeg/encoder';
import * as webP from '../../codecs/webp/encoder';
import * as identity from '../../codecs/identity/encoder';
@@ -79,6 +80,9 @@ async function preprocessImage(
preprocessData: PreprocessorState,
): Promise<ImageData> {
let result = source.data;
if (preprocessData.resize.enabled) {
result = await resizer.resize(result, preprocessData.resize);
}
if (preprocessData.quantizer.enabled) {
result = await quantizer.quantize(result, preprocessData.quantizer);
}
@@ -227,10 +231,21 @@ export default class App extends Component<Props, State> {
// compute the corresponding ImageData once since it only changes when the file changes:
const data = await bitmapToImageData(bmp);
this.setState({
let newState = {
...this.state,
source: { data, bmp, file },
loading: false,
});
};
// Default resize values come from the image:
for (const i of [0, 1]) {
newState = cleanMerge(newState, `images.${i}.preprocessorState.resize`, {
width: data.width,
height: data.height,
});
}
this.setState(newState);
} catch (err) {
console.error(err);
this.showError(`Invalid image`);
@@ -314,17 +329,22 @@ export default class App extends Component<Props, State> {
}
render({ }: Props, { loading, images, source, orientation }: State) {
const [leftImage, rightImage] = images;
const [leftImageBmp, rightImageBmp] = images.map(i => i.bmp);
const anyLoading = loading || images.some(image => image.loading);
return (
<file-drop accept="image/*" onfiledrop={this.onFileDrop}>
<div id="app" class={`${style.app} ${style[orientation]}`}>
{(leftImageBmp && rightImageBmp) ? (
{(leftImageBmp && rightImageBmp && source) ? (
<Output
orientation={orientation}
imgWidth={source.bmp.width}
imgHeight={source.bmp.height}
leftImg={leftImageBmp}
rightImg={rightImageBmp}
leftImgContain={leftImage.preprocessorState.resize.fitMethod === 'cover'}
rightImgContain={rightImage.preprocessorState.resize.fitMethod === 'cover'}
/>
) : (
<div class={style.welcome}>
@@ -332,9 +352,10 @@ export default class App extends Component<Props, State> {
<input type="file" onChange={this.onFileChange} />
</div>
)}
{(leftImageBmp && rightImageBmp) && images.map((image, index) => (
{(leftImageBmp && rightImageBmp && source) && images.map((image, index) => (
<Options
orientation={orientation}
sourceAspect={source.bmp.width / source.bmp.height}
imageIndex={index}
imageFile={image.file}
sourceImageFile={source && source.file}

View File

@@ -10,6 +10,7 @@ import WebPEncoderOptions from '../../codecs/webp/options';
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
import QuantizerOptionsComponent from '../../codecs/imagequant/options';
import ResizeOptionsComponent from '../../codecs/resize/options';
import * as identity from '../../codecs/identity/encoder';
import * as optiPNG from '../../codecs/optipng/encoder';
@@ -33,9 +34,8 @@ import {
encoderMap,
} from '../../codecs/encoders';
import { QuantizeOptions } from '../../codecs/imagequant/quantizer';
import { ResizeOptions } from '../../codecs/resize/resize';
import { PreprocessorState } from '../../codecs/preprocessors';
import FileSize from '../FileSize';
import { DownloadIcon } from '../../lib/icons';
@@ -62,6 +62,7 @@ const titles = {
interface Props {
orientation: 'horizontal' | 'vertical';
sourceAspect: number;
imageIndex: number;
sourceImageFile?: File;
imageFile?: File;
@@ -112,9 +113,17 @@ export default class Options extends Component<Props, State> {
);
}
@bind
onResizeOptionsChange(opts: ResizeOptions) {
this.props.onPreprocessorOptionsChange(
cleanMerge(this.props.preprocessorState, 'resize', opts),
);
}
render(
{
sourceImageFile,
sourceAspect,
imageIndex,
imageFile,
downloadUrl,
@@ -161,7 +170,23 @@ export default class Options extends Component<Props, State> {
</section>
{encoderState.type !== 'identity' && (
<div key="quantization" class={style.quantization}>
<div key="preprocessors" class={style.preprocessors}>
<label class={style.toggle}>
<input
name="resize.enable"
type="checkbox"
checked={!!preprocessorState.resize.enabled}
onChange={this.onPreprocessorEnabledChange}
/>
Resize
</label>
{preprocessorState.resize.enabled &&
<ResizeOptionsComponent
aspect={sourceAspect}
options={preprocessorState.resize}
onChange={this.onResizeOptionsChange}
/>
}
<label class={style.toggle}>
<input
name="quantizer.enable"
@@ -169,7 +194,7 @@ export default class Options extends Component<Props, State> {
checked={!!preprocessorState.quantizer.enabled}
onChange={this.onPreprocessorEnabledChange}
/>
Enable Quantization
Quantize
</label>
{preprocessorState.quantizer.enabled &&
<QuantizerOptionsComponent

View File

@@ -153,7 +153,7 @@ Note: These styles are temporary. They will be replaced before going live.
}
.quantization {
.preprocessors {
padding: 5px 0;
margin: 5px 0;
box-shadow: inset 0 -.5px 0 rgba(0,0,0,0.25), 0 .5px 0 rgba(255,255,255,0.15);

View File

@@ -11,6 +11,10 @@ interface Props {
orientation: 'horizontal' | 'vertical';
leftImg: ImageBitmap;
rightImg: ImageBitmap;
imgWidth: number;
imgHeight: number;
leftImgContain: boolean;
rightImgContain: boolean;
}
interface State {
@@ -145,7 +149,7 @@ export default class Output extends Component<Props, State> {
}
render(
{ orientation, leftImg, rightImg }: Props,
{ orientation, leftImg, rightImg, imgWidth, imgHeight, leftImgContain, rightImgContain }: Props,
{ scale, editingScale, altBackground }: State,
) {
return (
@@ -165,18 +169,26 @@ export default class Output extends Component<Props, State> {
ref={linkRef(this, 'pinchZoomLeft')}
>
<canvas
class={style.outputCanvas}
ref={linkRef(this, 'canvasLeft')}
width={leftImg.width}
height={leftImg.height}
style={{
width: imgWidth,
height: imgHeight,
objectFit: leftImgContain ? 'contain' : '',
}}
/>
</pinch-zoom>
<pinch-zoom ref={linkRef(this, 'pinchZoomRight')}>
<canvas
class={style.outputCanvas}
ref={linkRef(this, 'canvasRight')}
width={rightImg.width}
height={rightImg.height}
style={{
width: imgWidth,
height: imgHeight,
objectFit: rightImgContain ? 'contain' : '',
}}
/>
</pinch-zoom>
</two-up>

View File

@@ -134,7 +134,3 @@ Note: These styles are temporary. They will be replaced before going live.
border-bottom-right-radius: 0;
}
}
.outputCanvas {
image-rendering: pixelated;
}