forked from external-repos/squoosh
Add AVIF encoder without options
This commit is contained in:
4
codecs/avif_dec/avif_dec.d.ts
vendored
4
codecs/avif_dec/avif_dec.d.ts
vendored
@@ -4,10 +4,10 @@ interface RawImage {
|
|||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebPModule extends EmscriptenWasm.Module {
|
interface AVIFModule extends EmscriptenWasm.Module {
|
||||||
decode(data: BufferSource): RawImage;
|
decode(data: BufferSource): RawImage;
|
||||||
free_result(): void;
|
free_result(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(opts: EmscriptenWasm.ModuleOpts): WebPModule;
|
export default function(opts: EmscriptenWasm.ModuleOpts): AVIFModule;
|
||||||
|
|
||||||
|
|||||||
4
codecs/avif_enc/avif_enc.d.ts
vendored
4
codecs/avif_enc/avif_enc.d.ts
vendored
@@ -1,9 +1,9 @@
|
|||||||
// import { EncodeOptions } from '../../src/codecs/webp/encoder-meta';
|
// import { EncodeOptions } from '../../src/codecs/webp/encoder-meta';
|
||||||
|
|
||||||
interface WebPModule extends EmscriptenWasm.Module {
|
interface AVIFModule extends EmscriptenWasm.Module {
|
||||||
encode(data: BufferSource, width: number, height: number): Uint8Array;
|
encode(data: BufferSource, width: number, height: number): Uint8Array;
|
||||||
free_result(): void;
|
free_result(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function(opts: EmscriptenWasm.ModuleOpts): WebPModule;
|
export default function(opts: EmscriptenWasm.ModuleOpts): AVIFModule;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import avif_dec, { WebPModule } from '../../../codecs/avif_dec/avif_dec';
|
import avif_dec, { AVIFModule } from '../../../codecs/avif_dec/avif_dec';
|
||||||
import wasmUrl from '../../../codecs/avif_dec/avif_dec.wasm';
|
import wasmUrl from '../../../codecs/avif_dec/avif_dec.wasm';
|
||||||
import { initEmscriptenModule } from '../util';
|
import { initEmscriptenModule } from '../util';
|
||||||
|
|
||||||
let emscriptenModule: Promise<WebPModule>;
|
let emscriptenModule: Promise<AVIFModule>;
|
||||||
|
|
||||||
export async function decode(data: ArrayBuffer): Promise<ImageData> {
|
export async function decode(data: ArrayBuffer): Promise<ImageData> {
|
||||||
if (!emscriptenModule) emscriptenModule = initEmscriptenModule(avif_dec, wasmUrl);
|
if (!emscriptenModule) emscriptenModule = initEmscriptenModule(avif_dec, wasmUrl);
|
||||||
|
|||||||
@@ -1,72 +1,10 @@
|
|||||||
export enum WebPImageHint {
|
export interface EncodeOptions { }
|
||||||
WEBP_HINT_DEFAULT, // default preset.
|
|
||||||
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
|
|
||||||
WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
|
|
||||||
WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EncodeOptions {
|
export const type = 'avif';
|
||||||
quality: number;
|
export const label = 'AVIF';
|
||||||
target_size: number;
|
export const mimeType = 'image/avif';
|
||||||
target_PSNR: number;
|
export const extension = 'avif';
|
||||||
method: number;
|
|
||||||
sns_strength: number;
|
|
||||||
filter_strength: number;
|
|
||||||
filter_sharpness: number;
|
|
||||||
filter_type: number;
|
|
||||||
partitions: number;
|
|
||||||
segments: number;
|
|
||||||
pass: number;
|
|
||||||
show_compressed: number;
|
|
||||||
preprocessing: number;
|
|
||||||
autofilter: number;
|
|
||||||
partition_limit: number;
|
|
||||||
alpha_compression: number;
|
|
||||||
alpha_filtering: number;
|
|
||||||
alpha_quality: number;
|
|
||||||
lossless: number;
|
|
||||||
exact: number;
|
|
||||||
image_hint: number;
|
|
||||||
emulate_jpeg_size: number;
|
|
||||||
thread_level: number;
|
|
||||||
low_memory: number;
|
|
||||||
near_lossless: number;
|
|
||||||
use_delta_palette: number;
|
|
||||||
use_sharp_yuv: number;
|
|
||||||
}
|
|
||||||
export interface EncoderState { type: typeof type; options: EncodeOptions; }
|
|
||||||
|
|
||||||
export const type = 'webp';
|
|
||||||
export const label = 'WebP';
|
|
||||||
export const mimeType = 'image/webp';
|
|
||||||
export const extension = 'webp';
|
|
||||||
// These come from struct WebPConfig in encode.h.
|
|
||||||
export const defaultOptions: EncodeOptions = {
|
export const defaultOptions: EncodeOptions = {
|
||||||
quality: 75,
|
|
||||||
target_size: 0,
|
|
||||||
target_PSNR: 0,
|
|
||||||
method: 4,
|
|
||||||
sns_strength: 50,
|
|
||||||
filter_strength: 60,
|
|
||||||
filter_sharpness: 0,
|
|
||||||
filter_type: 1,
|
|
||||||
partitions: 0,
|
|
||||||
segments: 4,
|
|
||||||
pass: 1,
|
|
||||||
show_compressed: 0,
|
|
||||||
preprocessing: 0,
|
|
||||||
autofilter: 0,
|
|
||||||
partition_limit: 0,
|
|
||||||
alpha_compression: 1,
|
|
||||||
alpha_filtering: 1,
|
|
||||||
alpha_quality: 100,
|
|
||||||
lossless: 0,
|
|
||||||
exact: 0,
|
|
||||||
image_hint: 0,
|
|
||||||
emulate_jpeg_size: 0,
|
|
||||||
thread_level: 0,
|
|
||||||
low_memory: 0,
|
|
||||||
near_lossless: 100,
|
|
||||||
use_delta_palette: 0,
|
|
||||||
use_sharp_yuv: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface EncoderState { type: typeof type; options: EncodeOptions; }
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import webp_enc, { WebPModule } from '../../../codecs/webp_enc/webp_enc';
|
import avif_enc, { AVIFModule } from '../../../codecs/avif_enc/avif_enc';
|
||||||
import wasmUrl from '../../../codecs/webp_enc/webp_enc.wasm';
|
import wasmUrl from '../../../codecs/avif_enc/avif_enc.wasm';
|
||||||
import { EncodeOptions } from './encoder-meta';
|
import { EncodeOptions } from './encoder-meta';
|
||||||
import { initEmscriptenModule } from '../util';
|
import { initEmscriptenModule } from '../util';
|
||||||
|
|
||||||
let emscriptenModule: Promise<WebPModule>;
|
let emscriptenModule: Promise<AVIFModule>;
|
||||||
|
|
||||||
export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
|
export async function encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
|
||||||
if (!emscriptenModule) emscriptenModule = initEmscriptenModule(webp_enc, wasmUrl);
|
if (!emscriptenModule) emscriptenModule = initEmscriptenModule(avif_enc, wasmUrl);
|
||||||
|
|
||||||
const module = await emscriptenModule;
|
const module = await emscriptenModule;
|
||||||
const resultView = module.encode(data.data, data.width, data.height, options);
|
const resultView = module.encode(data.data, data.width, data.height);
|
||||||
const result = new Uint8Array(resultView);
|
const result = new Uint8Array(resultView);
|
||||||
module.free_result();
|
module.free_result();
|
||||||
|
|
||||||
|
|||||||
@@ -1,345 +1,33 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { bind } from '../../lib/initial-util';
|
import { bind } from '../../lib/initial-util';
|
||||||
import { inputFieldCheckedAsNumber, inputFieldValueAsNumber, preventDefault } from '../../lib/util';
|
import { /*inputFieldCheckedAsNumber, inputFieldValueAsNumber,*/ preventDefault } from '../../lib/util';
|
||||||
import { EncodeOptions, WebPImageHint } from './encoder-meta';
|
import { EncodeOptions } from './encoder-meta';
|
||||||
import * as style from '../../components/Options/style.scss';
|
import * as style from '../../components/Options/style.scss';
|
||||||
import Checkbox from '../../components/checkbox';
|
// import Checkbox from '../../components/checkbox';
|
||||||
import Expander from '../../components/expander';
|
// import Expander from '../../components/expander';
|
||||||
import Select from '../../components/select';
|
// import Select from '../../components/select';
|
||||||
import Range from '../../components/range';
|
// import Range from '../../components/range';
|
||||||
import linkState from 'linkstate';
|
// import linkState from 'linkstate';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
options: EncodeOptions;
|
options: EncodeOptions;
|
||||||
onChange(newOptions: EncodeOptions): void;
|
onChange(newOptions: EncodeOptions): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State{}
|
||||||
showAdvanced: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From kLosslessPresets in config_enc.c
|
export default class AVIFEncoderOptions extends Component<Props, State> {
|
||||||
// The format is [method, quality].
|
|
||||||
const losslessPresets:[number, number][] = [
|
|
||||||
[0, 0], [1, 20], [2, 25], [3, 30], [3, 50],
|
|
||||||
[4, 50], [4, 75], [4, 90], [5, 90], [6, 100],
|
|
||||||
];
|
|
||||||
const losslessPresetDefault = 6;
|
|
||||||
|
|
||||||
function determineLosslessQuality(quality: number, method: number): number {
|
|
||||||
const index = losslessPresets.findIndex(
|
|
||||||
([presetMethod, presetQuality]) => presetMethod === method && presetQuality === quality,
|
|
||||||
);
|
|
||||||
if (index !== -1) return index;
|
|
||||||
// Quality doesn't match one of the presets.
|
|
||||||
// This can happen when toggling 'lossless'.
|
|
||||||
return losslessPresetDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WebPEncoderOptions extends Component<Props, State> {
|
|
||||||
state: State = {
|
state: State = {
|
||||||
showAdvanced: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
onChange(event: Event) {
|
onChange(event: Event) {
|
||||||
const form = (event.currentTarget as HTMLInputElement).closest('form') as HTMLFormElement;
|
|
||||||
const lossless = inputFieldCheckedAsNumber(form.lossless);
|
|
||||||
const { options } = this.props;
|
|
||||||
const losslessPresetValue = inputFieldValueAsNumber(
|
|
||||||
form.lossless_preset, determineLosslessQuality(options.quality, options.method),
|
|
||||||
);
|
|
||||||
|
|
||||||
const newOptions: EncodeOptions = {
|
|
||||||
// Copy over options the form doesn't care about, eg emulate_jpeg_size
|
|
||||||
...options,
|
|
||||||
// And now stuff from the form:
|
|
||||||
lossless,
|
|
||||||
// Special-cased inputs:
|
|
||||||
// In lossless mode, the quality is derived from the preset.
|
|
||||||
quality: lossless ?
|
|
||||||
losslessPresets[losslessPresetValue][1] :
|
|
||||||
inputFieldValueAsNumber(form.quality, options.quality),
|
|
||||||
// In lossless mode, the method is derived from the preset.
|
|
||||||
method: lossless ?
|
|
||||||
losslessPresets[losslessPresetValue][0] :
|
|
||||||
inputFieldValueAsNumber(form.method_input, options.method),
|
|
||||||
image_hint: inputFieldCheckedAsNumber(form.image_hint, options.image_hint) ?
|
|
||||||
WebPImageHint.WEBP_HINT_GRAPH :
|
|
||||||
WebPImageHint.WEBP_HINT_DEFAULT,
|
|
||||||
// .checked
|
|
||||||
exact: inputFieldCheckedAsNumber(form.exact, options.exact),
|
|
||||||
alpha_compression: inputFieldCheckedAsNumber(
|
|
||||||
form.alpha_compression, options.alpha_compression,
|
|
||||||
),
|
|
||||||
autofilter: inputFieldCheckedAsNumber(form.autofilter, options.autofilter),
|
|
||||||
filter_type: inputFieldCheckedAsNumber(form.filter_type, options.filter_type),
|
|
||||||
use_sharp_yuv: inputFieldCheckedAsNumber(form.use_sharp_yuv, options.use_sharp_yuv),
|
|
||||||
// .value
|
|
||||||
near_lossless: 100 - inputFieldValueAsNumber(form.near_lossless, 100 - options.near_lossless),
|
|
||||||
alpha_quality: inputFieldValueAsNumber(form.alpha_quality, options.alpha_quality),
|
|
||||||
alpha_filtering: inputFieldValueAsNumber(form.alpha_filtering, options.alpha_filtering),
|
|
||||||
sns_strength: inputFieldValueAsNumber(form.sns_strength, options.sns_strength),
|
|
||||||
filter_strength: inputFieldValueAsNumber(form.filter_strength, options.filter_strength),
|
|
||||||
filter_sharpness:
|
|
||||||
7 - inputFieldValueAsNumber(form.filter_sharpness, 7 - options.filter_sharpness),
|
|
||||||
pass: inputFieldValueAsNumber(form.pass, options.pass),
|
|
||||||
preprocessing: inputFieldValueAsNumber(form.preprocessing, options.preprocessing),
|
|
||||||
segments: inputFieldValueAsNumber(form.segments, options.segments),
|
|
||||||
partitions: inputFieldValueAsNumber(form.partitions, options.partitions),
|
|
||||||
};
|
|
||||||
this.props.onChange(newOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _losslessSpecificOptions(options: EncodeOptions) {
|
|
||||||
return (
|
|
||||||
<div key="lossless">
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="lossless_preset"
|
|
||||||
min="0"
|
|
||||||
max="9"
|
|
||||||
value={determineLosslessQuality(options.quality, options.method)}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Effort:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="near_lossless"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={'' + (100 - options.near_lossless)}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Slight loss:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
{/*
|
|
||||||
Although there are 3 different kinds of image hint, webp only
|
|
||||||
seems to do something with the 'graph' type, and I don't really
|
|
||||||
understand what it does.
|
|
||||||
*/}
|
|
||||||
<Checkbox
|
|
||||||
name="image_hint"
|
|
||||||
checked={options.image_hint === WebPImageHint.WEBP_HINT_GRAPH}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Discrete tone image
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _lossySpecificOptions(options: EncodeOptions) {
|
|
||||||
const { showAdvanced } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key="lossy">
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="method_input"
|
|
||||||
min="0"
|
|
||||||
max="6"
|
|
||||||
value={options.method}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Effort:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="quality"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
step="0.1"
|
|
||||||
value={options.quality}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Quality:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
checked={showAdvanced}
|
|
||||||
onChange={linkState(this, 'showAdvanced')}
|
|
||||||
/>
|
|
||||||
Show advanced settings
|
|
||||||
</label>
|
|
||||||
<Expander>
|
|
||||||
{showAdvanced ?
|
|
||||||
<div>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
name="alpha_compression"
|
|
||||||
checked={!!options.alpha_compression}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Compress alpha
|
|
||||||
</label>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="alpha_quality"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={options.alpha_quality}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Alpha quality:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="alpha_filtering"
|
|
||||||
min="0"
|
|
||||||
max="2"
|
|
||||||
value={options.alpha_filtering}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Alpha filter quality:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
name="autofilter"
|
|
||||||
checked={!!options.autofilter}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Auto adjust filter strength
|
|
||||||
</label>
|
|
||||||
<Expander>
|
|
||||||
{options.autofilter ? null :
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="filter_strength"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={options.filter_strength}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Filter strength:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Expander>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
name="filter_type"
|
|
||||||
checked={!!options.filter_type}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Strong filter
|
|
||||||
</label>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="filter_sharpness"
|
|
||||||
min="0"
|
|
||||||
max="7"
|
|
||||||
value={7 - options.filter_sharpness}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Filter sharpness:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
name="use_sharp_yuv"
|
|
||||||
checked={!!options.use_sharp_yuv}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Sharp RGB→YUV conversion
|
|
||||||
</label>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="pass"
|
|
||||||
min="1"
|
|
||||||
max="10"
|
|
||||||
value={options.pass}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Passes:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="sns_strength"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={options.sns_strength}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Spacial noise shaping:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<label class={style.optionTextFirst}>
|
|
||||||
Preprocess:
|
|
||||||
<Select
|
|
||||||
name="preprocessing"
|
|
||||||
value={options.preprocessing}
|
|
||||||
onChange={this.onChange}
|
|
||||||
>
|
|
||||||
<option value="0">None</option>
|
|
||||||
<option value="1">Segment smooth</option>
|
|
||||||
<option value="2">Pseudo-random dithering</option>
|
|
||||||
</Select>
|
|
||||||
</label>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="segments"
|
|
||||||
min="1"
|
|
||||||
max="4"
|
|
||||||
value={options.segments}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Segments:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
<div class={style.optionOneCell}>
|
|
||||||
<Range
|
|
||||||
name="partitions"
|
|
||||||
min="0"
|
|
||||||
max="3"
|
|
||||||
value={options.partitions}
|
|
||||||
onInput={this.onChange}
|
|
||||||
>
|
|
||||||
Partitions:
|
|
||||||
</Range>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</Expander>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ options }: Props) {
|
render({ options }: Props) {
|
||||||
// I'm rendering both lossy and lossless forms, as it becomes much easier when
|
|
||||||
// gathering the data.
|
|
||||||
return (
|
return (
|
||||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||||
<label class={style.optionInputFirst}>
|
Lol
|
||||||
<Checkbox
|
|
||||||
name="lossless"
|
|
||||||
checked={!!options.lossless}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Lossless
|
|
||||||
</label>
|
|
||||||
{options.lossless
|
|
||||||
? this._losslessSpecificOptions(options)
|
|
||||||
: this._lossySpecificOptions(options)
|
|
||||||
}
|
|
||||||
<label class={style.optionInputFirst}>
|
|
||||||
<Checkbox
|
|
||||||
name="exact"
|
|
||||||
checked={!!options.exact}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
Preserve transparent data
|
|
||||||
</label>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as identity from './identity/encoder-meta';
|
|||||||
import * as oxiPNG from './oxipng/encoder-meta';
|
import * as oxiPNG from './oxipng/encoder-meta';
|
||||||
import * as mozJPEG from './mozjpeg/encoder-meta';
|
import * as mozJPEG from './mozjpeg/encoder-meta';
|
||||||
import * as webP from './webp/encoder-meta';
|
import * as webP from './webp/encoder-meta';
|
||||||
|
import * as avif from './avif/encoder-meta';
|
||||||
import * as browserPNG from './browser-png/encoder-meta';
|
import * as browserPNG from './browser-png/encoder-meta';
|
||||||
import * as browserJPEG from './browser-jpeg/encoder-meta';
|
import * as browserJPEG from './browser-jpeg/encoder-meta';
|
||||||
import * as browserWebP from './browser-webp/encoder-meta';
|
import * as browserWebP from './browser-webp/encoder-meta';
|
||||||
@@ -20,6 +21,7 @@ export type EncoderState =
|
|||||||
oxiPNG.EncoderState |
|
oxiPNG.EncoderState |
|
||||||
mozJPEG.EncoderState |
|
mozJPEG.EncoderState |
|
||||||
webP.EncoderState |
|
webP.EncoderState |
|
||||||
|
avif.EncoderState |
|
||||||
browserPNG.EncoderState |
|
browserPNG.EncoderState |
|
||||||
browserJPEG.EncoderState |
|
browserJPEG.EncoderState |
|
||||||
browserWebP.EncoderState |
|
browserWebP.EncoderState |
|
||||||
@@ -34,6 +36,7 @@ export type EncoderOptions =
|
|||||||
oxiPNG.EncodeOptions |
|
oxiPNG.EncodeOptions |
|
||||||
mozJPEG.EncodeOptions |
|
mozJPEG.EncodeOptions |
|
||||||
webP.EncodeOptions |
|
webP.EncodeOptions |
|
||||||
|
avif.EncodeOptions |
|
||||||
browserPNG.EncodeOptions |
|
browserPNG.EncodeOptions |
|
||||||
browserJPEG.EncodeOptions |
|
browserJPEG.EncodeOptions |
|
||||||
browserWebP.EncodeOptions |
|
browserWebP.EncodeOptions |
|
||||||
@@ -50,6 +53,7 @@ export const encoderMap = {
|
|||||||
[oxiPNG.type]: oxiPNG,
|
[oxiPNG.type]: oxiPNG,
|
||||||
[mozJPEG.type]: mozJPEG,
|
[mozJPEG.type]: mozJPEG,
|
||||||
[webP.type]: webP,
|
[webP.type]: webP,
|
||||||
|
[avif.type]: avif,
|
||||||
[browserPNG.type]: browserPNG,
|
[browserPNG.type]: browserPNG,
|
||||||
[browserJPEG.type]: browserJPEG,
|
[browserJPEG.type]: browserJPEG,
|
||||||
[browserWebP.type]: browserWebP,
|
[browserWebP.type]: browserWebP,
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ async function webpDecode(data: ArrayBuffer): Promise<ImageData> {
|
|||||||
return timed('webpDecode', () => decode(data));
|
return timed('webpDecode', () => decode(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function avifEncode(
|
||||||
|
data: ImageData, options: import('../avif/encoder-meta').EncodeOptions,
|
||||||
|
): Promise<ArrayBuffer> {
|
||||||
|
const { encode } = await import(
|
||||||
|
/* webpackChunkName: "process-avif-enc" */
|
||||||
|
'../avif/encoder');
|
||||||
|
return encode(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
async function avifDecode(data: ArrayBuffer): Promise<ImageData> {
|
async function avifDecode(data: ArrayBuffer): Promise<ImageData> {
|
||||||
const { decode } = await import(
|
const { decode } = await import(
|
||||||
/* webpackChunkName: "process-avif-dec" */
|
/* webpackChunkName: "process-avif-dec" */
|
||||||
@@ -97,6 +106,7 @@ const exports = {
|
|||||||
oxiPngEncode,
|
oxiPngEncode,
|
||||||
webpEncode,
|
webpEncode,
|
||||||
webpDecode,
|
webpDecode,
|
||||||
|
avifEncode,
|
||||||
avifDecode,
|
avifDecode,
|
||||||
};
|
};
|
||||||
export type ProcessorWorkerApi = typeof exports;
|
export type ProcessorWorkerApi = typeof exports;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { canvasEncode, blobToArrayBuffer } from '../lib/util';
|
|||||||
import { EncodeOptions as MozJPEGEncoderOptions } from './mozjpeg/encoder-meta';
|
import { EncodeOptions as MozJPEGEncoderOptions } from './mozjpeg/encoder-meta';
|
||||||
import { EncodeOptions as OxiPNGEncoderOptions } from './oxipng/encoder-meta';
|
import { EncodeOptions as OxiPNGEncoderOptions } from './oxipng/encoder-meta';
|
||||||
import { EncodeOptions as WebPEncoderOptions } from './webp/encoder-meta';
|
import { EncodeOptions as WebPEncoderOptions } from './webp/encoder-meta';
|
||||||
|
import { EncodeOptions as AvifEncoderOptions } from './avif/encoder-meta';
|
||||||
import { EncodeOptions as BrowserJPEGOptions } from './browser-jpeg/encoder-meta';
|
import { EncodeOptions as BrowserJPEGOptions } from './browser-jpeg/encoder-meta';
|
||||||
import { EncodeOptions as BrowserWebpEncodeOptions } from './browser-webp/encoder-meta';
|
import { EncodeOptions as BrowserWebpEncodeOptions } from './browser-webp/encoder-meta';
|
||||||
import { BrowserResizeOptions, VectorResizeOptions } from './resize/processor-meta';
|
import { BrowserResizeOptions, VectorResizeOptions } from './resize/processor-meta';
|
||||||
@@ -169,6 +170,11 @@ export default class Processor {
|
|||||||
return this._workerApi!.avifDecode(data);
|
return this._workerApi!.avifDecode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Processor._processingJob({ needsWorker: true })
|
||||||
|
avifEncode(data: ImageData, opts: AvifEncoderOptions): Promise<ArrayBuffer> {
|
||||||
|
return this._workerApi!.avifEncode(data, opts);
|
||||||
|
}
|
||||||
|
|
||||||
// Not-worker jobs:
|
// Not-worker jobs:
|
||||||
|
|
||||||
@Processor._processingJob()
|
@Processor._processingJob()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import * as identity from '../../codecs/identity/encoder-meta';
|
|||||||
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
|
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
|
||||||
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
|
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
|
||||||
import * as webP from '../../codecs/webp/encoder-meta';
|
import * as webP from '../../codecs/webp/encoder-meta';
|
||||||
|
import * as avif from '../../codecs/avif/encoder-meta';
|
||||||
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
|
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
|
||||||
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
|
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
|
||||||
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
|
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
|
||||||
@@ -136,6 +137,7 @@ async function compressImage(
|
|||||||
case oxiPNG.type: return processor.oxiPngEncode(image, encodeData.options);
|
case oxiPNG.type: return processor.oxiPngEncode(image, encodeData.options);
|
||||||
case mozJPEG.type: return processor.mozjpegEncode(image, encodeData.options);
|
case mozJPEG.type: return processor.mozjpegEncode(image, encodeData.options);
|
||||||
case webP.type: return processor.webpEncode(image, encodeData.options);
|
case webP.type: return processor.webpEncode(image, encodeData.options);
|
||||||
|
case avif.type: return processor.avifEncode(image, encodeData.options);
|
||||||
case browserPNG.type: return processor.browserPngEncode(image);
|
case browserPNG.type: return processor.browserPngEncode(image);
|
||||||
case browserJPEG.type: return processor.browserJpegEncode(image, encodeData.options);
|
case browserJPEG.type: return processor.browserJpegEncode(image, encodeData.options);
|
||||||
case browserWebP.type: return processor.browserWebpEncode(image, encodeData.options);
|
case browserWebP.type: return processor.browserWebpEncode(image, encodeData.options);
|
||||||
|
|||||||
Reference in New Issue
Block a user