mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-14 17:49:52 +00:00
Expose some options for AVIF
This commit is contained in:
@@ -4,10 +4,41 @@
|
|||||||
|
|
||||||
using namespace emscripten;
|
using namespace emscripten;
|
||||||
|
|
||||||
|
struct AvifOptions {
|
||||||
|
// [0 - 63]
|
||||||
|
// 0 = lossless
|
||||||
|
// 63 = worst quality
|
||||||
|
int minQuantizer;
|
||||||
|
int maxQuantizer;
|
||||||
|
// [0 - 6]
|
||||||
|
// Creates 2^n tiles in that dimension
|
||||||
|
int tileRowsLog2;
|
||||||
|
int tileColsLog2;
|
||||||
|
// [0 - 10]
|
||||||
|
// 0 = slowest
|
||||||
|
// 10 = fastest
|
||||||
|
int speed;
|
||||||
|
// 0 = 4:2:0
|
||||||
|
// 1 = 4:2:2
|
||||||
|
// 2 = 4:4:4
|
||||||
|
int subsample;
|
||||||
|
};
|
||||||
|
|
||||||
avifRWData output = AVIF_DATA_EMPTY;
|
avifRWData output = AVIF_DATA_EMPTY;
|
||||||
val encode(std::string buffer, int width, int height) {
|
val encode(std::string buffer, int width, int height, AvifOptions options) {
|
||||||
int depth = 8;
|
int depth = 8;
|
||||||
avifPixelFormat format = AVIF_PIXEL_FORMAT_YUV420;
|
avifPixelFormat format;
|
||||||
|
switch (options.subsample) {
|
||||||
|
case 0:
|
||||||
|
format = AVIF_PIXEL_FORMAT_YUV420;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
format = AVIF_PIXEL_FORMAT_YUV422;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
format = AVIF_PIXEL_FORMAT_YUV444;
|
||||||
|
break;
|
||||||
|
}
|
||||||
avifImage *image = avifImageCreate(width, height, depth, format);
|
avifImage *image = avifImageCreate(width, height, depth, format);
|
||||||
|
|
||||||
uint8_t *rgba = (uint8_t *)buffer.c_str();
|
uint8_t *rgba = (uint8_t *)buffer.c_str();
|
||||||
@@ -27,15 +58,15 @@ val encode(std::string buffer, int width, int height) {
|
|||||||
|
|
||||||
avifEncoder *encoder = avifEncoderCreate();
|
avifEncoder *encoder = avifEncoderCreate();
|
||||||
encoder->maxThreads = 1;
|
encoder->maxThreads = 1;
|
||||||
encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
|
encoder->minQuantizer = options.minQuantizer;
|
||||||
encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
|
encoder->maxQuantizer = options.maxQuantizer;
|
||||||
|
encoder->tileRowsLog2 = options.tileRowsLog2;
|
||||||
|
encoder->tileColsLog2 = options.tileColsLog2;
|
||||||
|
encoder->speed = options.speed;
|
||||||
avifResult encodeResult = avifEncoderWrite(encoder, image, &output);
|
avifResult encodeResult = avifEncoderWrite(encoder, image, &output);
|
||||||
if (encodeResult != AVIF_RESULT_OK) {
|
if (encodeResult != AVIF_RESULT_OK) {
|
||||||
return val::null();
|
return val::null();
|
||||||
}
|
}
|
||||||
// output contains a valid .avif file's contents
|
|
||||||
// ... output.data;
|
|
||||||
// ... output.size;
|
|
||||||
avifImageDestroy(image);
|
avifImageDestroy(image);
|
||||||
avifEncoderDestroy(encoder);
|
avifEncoderDestroy(encoder);
|
||||||
return val(typed_memory_view(output.size, output.data));
|
return val(typed_memory_view(output.size, output.data));
|
||||||
@@ -44,6 +75,14 @@ val encode(std::string buffer, int width, int height) {
|
|||||||
void free_result() { avifRWDataFree(&output); }
|
void free_result() { avifRWDataFree(&output); }
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(my_module) {
|
EMSCRIPTEN_BINDINGS(my_module) {
|
||||||
|
value_object<AvifOptions>("AvifOptions")
|
||||||
|
.field("minQuantizer", &AvifOptions::minQuantizer)
|
||||||
|
.field("maxQuantizer", &AvifOptions::maxQuantizer)
|
||||||
|
.field("tileRowsLog2", &AvifOptions::tileRowsLog2)
|
||||||
|
.field("tileColsLog2", &AvifOptions::tileColsLog2)
|
||||||
|
.field("speed", &AvifOptions::speed)
|
||||||
|
.field("subsample", &AvifOptions::subsample);
|
||||||
|
|
||||||
function("encode", &encode);
|
function("encode", &encode);
|
||||||
function("free_result", &free_result);
|
function("free_result", &free_result);
|
||||||
}
|
}
|
||||||
|
|||||||
4
codecs/avif_enc/avif_enc.d.ts
vendored
4
codecs/avif_enc/avif_enc.d.ts
vendored
@@ -1,7 +1,7 @@
|
|||||||
// import { EncodeOptions } from '../../src/codecs/webp/encoder-meta';
|
import { EncodeOptions } from '../../src/codecs/avif/encoder-meta';
|
||||||
|
|
||||||
interface AVIFModule extends EmscriptenWasm.Module {
|
interface AVIFModule extends EmscriptenWasm.Module {
|
||||||
encode(data: BufferSource, width: number, height: number): Uint8Array;
|
encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array;
|
||||||
free_result(): void;
|
free_result(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,10 +1,23 @@
|
|||||||
export interface EncodeOptions { }
|
export interface EncodeOptions {
|
||||||
|
minQuantizer: number;
|
||||||
|
maxQuantizer: number;
|
||||||
|
tileRowsLog2: number;
|
||||||
|
tileColsLog2: number;
|
||||||
|
speed: number;
|
||||||
|
subsample: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const type = 'avif';
|
export const type = 'avif';
|
||||||
export const label = 'AVIF';
|
export const label = 'AVIF';
|
||||||
export const mimeType = 'image/avif';
|
export const mimeType = 'image/avif';
|
||||||
export const extension = 'avif';
|
export const extension = 'avif';
|
||||||
export const defaultOptions: EncodeOptions = {
|
export const defaultOptions: EncodeOptions = {
|
||||||
|
minQuantizer: 16,
|
||||||
|
maxQuantizer: 16,
|
||||||
|
tileColsLog2: 0,
|
||||||
|
tileRowsLog2: 0,
|
||||||
|
speed: 10,
|
||||||
|
subsample: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface EncoderState { type: typeof type; options: EncodeOptions; }
|
export interface EncoderState { type: typeof type; options: EncodeOptions; }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export async function encode(data: ImageData, options: EncodeOptions): Promise<A
|
|||||||
if (!emscriptenModule) emscriptenModule = initEmscriptenModule(avif_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);
|
const resultView = module.encode(data.data, data.width, data.height, options);
|
||||||
const result = new Uint8Array(resultView);
|
const result = new Uint8Array(resultView);
|
||||||
module.free_result();
|
module.free_result();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
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 } 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 {
|
||||||
@@ -14,20 +17,58 @@ interface Props {
|
|||||||
onChange(newOptions: EncodeOptions): void;
|
onChange(newOptions: EncodeOptions): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State{}
|
interface State {}
|
||||||
|
|
||||||
export default class AVIFEncoderOptions extends Component<Props, State> {
|
export default class AVIFEncoderOptions extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {};
|
||||||
};
|
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
onChange(event: Event) {
|
onChange(event: Event) {
|
||||||
|
const form = (event.currentTarget as HTMLInputElement).closest(
|
||||||
|
'form',
|
||||||
|
) as HTMLFormElement;
|
||||||
|
const { options } = this.props;
|
||||||
|
const newOptions: EncodeOptions = {
|
||||||
|
// Copy over options the form doesn't currently care about, eg arithmetic
|
||||||
|
...this.props.options,
|
||||||
|
minQuantizer: inputFieldValueAsNumber(
|
||||||
|
form.quantizer,
|
||||||
|
options.minQuantizer,
|
||||||
|
),
|
||||||
|
maxQuantizer: inputFieldValueAsNumber(
|
||||||
|
form.quantizer,
|
||||||
|
options.maxQuantizer,
|
||||||
|
),
|
||||||
|
speed: inputFieldValueAsNumber(form.speed, options.speed),
|
||||||
|
};
|
||||||
|
this.props.onChange(newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ options }: Props) {
|
render({ options }: Props) {
|
||||||
return (
|
return (
|
||||||
<form class={style.optionsSection} onSubmit={preventDefault}>
|
<form class={style.optionsSection} onSubmit={preventDefault}>
|
||||||
Lol
|
<div class={style.optionOneCell}>
|
||||||
|
<Range
|
||||||
|
name="quantizer"
|
||||||
|
min="0"
|
||||||
|
max="63"
|
||||||
|
value={options.minQuantizer}
|
||||||
|
onInput={this.onChange}
|
||||||
|
>
|
||||||
|
Quantizer
|
||||||
|
</Range>
|
||||||
|
</div>
|
||||||
|
<div class={style.optionOneCell}>
|
||||||
|
<Range
|
||||||
|
name="speed"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
value={options.speed}
|
||||||
|
onInput={this.onChange}
|
||||||
|
>
|
||||||
|
Speed
|
||||||
|
</Range>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
|
|||||||
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
||||||
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
|
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
|
||||||
import WebPEncoderOptions from '../../codecs/webp/options';
|
import WebPEncoderOptions from '../../codecs/webp/options';
|
||||||
|
import AvifEncoderOptions from '../../codecs/avif/options';
|
||||||
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
|
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
|
||||||
|
|
||||||
import QuantizerOptionsComponent from '../../codecs/imagequant/options';
|
import QuantizerOptionsComponent from '../../codecs/imagequant/options';
|
||||||
@@ -16,6 +17,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';
|
||||||
@@ -47,6 +49,7 @@ const encoderOptionsComponentMap: {
|
|||||||
[oxiPNG.type]: OxiPNGEncoderOptions,
|
[oxiPNG.type]: OxiPNGEncoderOptions,
|
||||||
[mozJPEG.type]: MozJpegEncoderOptions,
|
[mozJPEG.type]: MozJpegEncoderOptions,
|
||||||
[webP.type]: WebPEncoderOptions,
|
[webP.type]: WebPEncoderOptions,
|
||||||
|
[avif.type]: AvifEncoderOptions,
|
||||||
[browserPNG.type]: undefined,
|
[browserPNG.type]: undefined,
|
||||||
[browserJPEG.type]: BrowserJPEGEncoderOptions,
|
[browserJPEG.type]: BrowserJPEGEncoderOptions,
|
||||||
[browserWebP.type]: BrowserWebPEncoderOptions,
|
[browserWebP.type]: BrowserWebPEncoderOptions,
|
||||||
|
|||||||
Reference in New Issue
Block a user