forked from external-repos/squoosh
Adding browser's webp encoder (#72)
* Adding WebP (without feature detect in place) * Adding WebP check * Remove unused import
This commit is contained in:
23
src/codecs/browser-webp/encoder.ts
Normal file
23
src/codecs/browser-webp/encoder.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { canvasEncode } from '../../lib/util';
|
||||
|
||||
export interface EncodeOptions { quality: number; }
|
||||
export interface EncoderState { type: typeof type; options: EncodeOptions; }
|
||||
|
||||
export const type = 'browser-webp';
|
||||
export const label = 'Browser WebP';
|
||||
export const mimeType = 'image/webp';
|
||||
export const extension = 'webp';
|
||||
export const defaultOptions: EncodeOptions = { quality: 0.5 };
|
||||
|
||||
export async function featureTest() {
|
||||
const data = new ImageData(1, 1);
|
||||
const blob = await encode(data, defaultOptions);
|
||||
// According to the spec, the blob should be null if the format isn't supported…
|
||||
if (!blob) return false;
|
||||
// …but Safari falls back to PNG, so we need to check the mime type.
|
||||
return blob.type === mimeType;
|
||||
}
|
||||
|
||||
export function encode(data: ImageData, { quality }: EncodeOptions) {
|
||||
return canvasEncode(data, mimeType, quality);
|
||||
}
|
||||
3
src/codecs/browser-webp/options.ts
Normal file
3
src/codecs/browser-webp/options.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import qualityOption from '../generic/quality-option';
|
||||
|
||||
export default qualityOption({ min: 0, max: 1, step: 0 });
|
||||
@@ -2,12 +2,18 @@ import * as mozJPEG from './mozjpeg/encoder';
|
||||
import * as identity from './identity/encoder';
|
||||
import * as browserPNG from './browser-png/encoder';
|
||||
import * as browserJPEG from './browser-jpeg/encoder';
|
||||
import * as browserWebP from './browser-webp/encoder';
|
||||
|
||||
export interface EncoderSupportMap {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export type EncoderState =
|
||||
identity.EncoderState | mozJPEG.EncoderState | browserPNG.EncoderState | browserJPEG.EncoderState;
|
||||
identity.EncoderState | mozJPEG.EncoderState | browserPNG.EncoderState |
|
||||
browserJPEG.EncoderState | browserWebP.EncoderState;
|
||||
export type EncoderOptions =
|
||||
identity.EncodeOptions | mozJPEG.EncodeOptions | browserPNG.EncodeOptions |
|
||||
browserJPEG.EncodeOptions;
|
||||
browserJPEG.EncodeOptions | browserWebP.EncodeOptions;
|
||||
export type EncoderType = keyof typeof encoderMap;
|
||||
|
||||
export const encoderMap = {
|
||||
@@ -15,6 +21,20 @@ export const encoderMap = {
|
||||
[mozJPEG.type]: mozJPEG,
|
||||
[browserPNG.type]: browserPNG,
|
||||
[browserJPEG.type]: browserJPEG,
|
||||
[browserWebP.type]: browserWebP,
|
||||
};
|
||||
|
||||
export const encoders = Array.from(Object.values(encoderMap));
|
||||
|
||||
/** Does this browser support a given encoder? Indexed by label */
|
||||
export const encodersSupported = Promise.resolve().then(async () => {
|
||||
const encodersSupported: EncoderSupportMap = {};
|
||||
|
||||
await Promise.all(encoders.map(async (encoder) => {
|
||||
// If the encoder provides a featureTest, call it, otherwise assume supported.
|
||||
const isSupported = !('featureTest' in encoder) || await encoder.featureTest();
|
||||
encodersSupported[encoder.type] = isSupported;
|
||||
}));
|
||||
|
||||
return encodersSupported;
|
||||
});
|
||||
|
||||
@@ -12,7 +12,13 @@ import * as mozJPEG from '../../codecs/mozjpeg/encoder';
|
||||
import * as identity from '../../codecs/identity/encoder';
|
||||
import * as browserPNG from '../../codecs/browser-png/encoder';
|
||||
import * as browserJPEG from '../../codecs/browser-jpeg/encoder';
|
||||
import { EncoderState, EncoderType, EncoderOptions, encoderMap } from '../../codecs/encoders';
|
||||
import * as browserWebP from '../../codecs/browser-webp/encoder';
|
||||
import {
|
||||
EncoderState,
|
||||
EncoderType,
|
||||
EncoderOptions,
|
||||
encoderMap,
|
||||
} from '../../codecs/encoders';
|
||||
|
||||
interface SourceImage {
|
||||
file: File;
|
||||
@@ -54,7 +60,8 @@ async function compressImage(
|
||||
case mozJPEG.type: return mozJPEG.encode(source.data, encodeData.options);
|
||||
case browserPNG.type: return browserPNG.encode(source.data, encodeData.options);
|
||||
case browserJPEG.type: return browserJPEG.encode(source.data, encodeData.options);
|
||||
default: throw Error(`Unexpected encoder name`);
|
||||
case browserWebP.type: return browserWebP.encode(source.data, encodeData.options);
|
||||
default: throw Error(`Unexpected encoder ${JSON.stringify(encodeData)}`);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
@@ -3,18 +3,28 @@ import * as style from './style.scss';
|
||||
import { bind } from '../../lib/util';
|
||||
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
||||
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
|
||||
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
|
||||
|
||||
import { type as mozJPEGType } from '../../codecs/mozjpeg/encoder';
|
||||
import { type as identityType } from '../../codecs/identity/encoder';
|
||||
import { type as browserPNGType } from '../../codecs/browser-png/encoder';
|
||||
import { type as browserJPEGType } from '../../codecs/browser-jpeg/encoder';
|
||||
import { EncoderState, EncoderType, EncoderOptions, encoders } from '../../codecs/encoders';
|
||||
import * as mozJPEG from '../../codecs/mozjpeg/encoder';
|
||||
import * as identity from '../../codecs/identity/encoder';
|
||||
import * as browserPNG from '../../codecs/browser-png/encoder';
|
||||
import * as browserJPEG from '../../codecs/browser-jpeg/encoder';
|
||||
import * as browserWebP from '../../codecs/browser-webp/encoder';
|
||||
import {
|
||||
EncoderState,
|
||||
EncoderType,
|
||||
EncoderOptions,
|
||||
encoders,
|
||||
encodersSupported,
|
||||
EncoderSupportMap,
|
||||
} from '../../codecs/encoders';
|
||||
|
||||
const encoderOptionsComponentMap = {
|
||||
[mozJPEGType]: MozJpegEncoderOptions,
|
||||
[identityType]: undefined,
|
||||
[browserPNGType]: undefined,
|
||||
[browserJPEGType]: BrowserJPEGEncoderOptions,
|
||||
[mozJPEG.type]: MozJpegEncoderOptions,
|
||||
[identity.type]: undefined,
|
||||
[browserPNG.type]: undefined,
|
||||
[browserJPEG.type]: BrowserJPEGEncoderOptions,
|
||||
[browserWebP.type]: BrowserWebPEncoderOptions,
|
||||
};
|
||||
|
||||
interface Props {
|
||||
@@ -24,11 +34,18 @@ interface Props {
|
||||
onOptionsChange(newOptions: EncoderOptions): void;
|
||||
}
|
||||
|
||||
interface State {}
|
||||
interface State {
|
||||
encoderSupportMap?: EncoderSupportMap;
|
||||
}
|
||||
|
||||
export default class Options extends Component<Props, State> {
|
||||
typeSelect?: HTMLSelectElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
encodersSupported.then(encoderSupportMap => this.setState({ encoderSupportMap }));
|
||||
}
|
||||
|
||||
@bind
|
||||
onTypeChange(event: Event) {
|
||||
const el = event.currentTarget as HTMLSelectElement;
|
||||
@@ -39,18 +56,22 @@ export default class Options extends Component<Props, State> {
|
||||
this.props.onTypeChange(type);
|
||||
}
|
||||
|
||||
render({ class: className, encoderState, onOptionsChange }: Props) {
|
||||
render({ class: className, encoderState, onOptionsChange }: Props, { encoderSupportMap }: State) {
|
||||
const EncoderOptionComponent = encoderOptionsComponentMap[encoderState.type];
|
||||
|
||||
return (
|
||||
<div class={`${style.options}${className ? (' ' + className) : ''}`}>
|
||||
<label>
|
||||
Mode:
|
||||
<select value={encoderState.type} onChange={this.onTypeChange}>
|
||||
{encoders.map(encoder => (
|
||||
<option value={encoder.type}>{encoder.label}</option>
|
||||
))}
|
||||
</select>
|
||||
{encoderSupportMap ?
|
||||
<select value={encoderState.type} onChange={this.onTypeChange}>
|
||||
{encoders.filter(encoder => encoderSupportMap[encoder.type]).map(encoder => (
|
||||
<option value={encoder.type}>{encoder.label}</option>
|
||||
))}
|
||||
</select>
|
||||
:
|
||||
<select><option>Loading…</option></select>
|
||||
}
|
||||
</label>
|
||||
{EncoderOptionComponent &&
|
||||
<EncoderOptionComponent
|
||||
|
||||
Reference in New Issue
Block a user