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;
|
||||
|
||||
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;
|
||||
val encode(std::string buffer, int width, int height) {
|
||||
val encode(std::string buffer, int width, int height, AvifOptions options) {
|
||||
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);
|
||||
|
||||
uint8_t *rgba = (uint8_t *)buffer.c_str();
|
||||
@@ -27,15 +58,15 @@ val encode(std::string buffer, int width, int height) {
|
||||
|
||||
avifEncoder *encoder = avifEncoderCreate();
|
||||
encoder->maxThreads = 1;
|
||||
encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
|
||||
encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
|
||||
encoder->minQuantizer = options.minQuantizer;
|
||||
encoder->maxQuantizer = options.maxQuantizer;
|
||||
encoder->tileRowsLog2 = options.tileRowsLog2;
|
||||
encoder->tileColsLog2 = options.tileColsLog2;
|
||||
encoder->speed = options.speed;
|
||||
avifResult encodeResult = avifEncoderWrite(encoder, image, &output);
|
||||
if (encodeResult != AVIF_RESULT_OK) {
|
||||
return val::null();
|
||||
}
|
||||
// output contains a valid .avif file's contents
|
||||
// ... output.data;
|
||||
// ... output.size;
|
||||
avifImageDestroy(image);
|
||||
avifEncoderDestroy(encoder);
|
||||
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); }
|
||||
|
||||
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("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 {
|
||||
encode(data: BufferSource, width: number, height: number): Uint8Array;
|
||||
encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array;
|
||||
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 label = 'AVIF';
|
||||
export const mimeType = 'image/avif';
|
||||
export const extension = 'avif';
|
||||
export const defaultOptions: EncodeOptions = {
|
||||
minQuantizer: 16,
|
||||
maxQuantizer: 16,
|
||||
tileColsLog2: 0,
|
||||
tileRowsLog2: 0,
|
||||
speed: 10,
|
||||
subsample: 0,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
module.free_result();
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { h, Component } from 'preact';
|
||||
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 * as style from '../../components/Options/style.scss';
|
||||
// import Checkbox from '../../components/checkbox';
|
||||
// import Expander from '../../components/expander';
|
||||
// import Select from '../../components/select';
|
||||
// import Range from '../../components/range';
|
||||
import Range from '../../components/range';
|
||||
// import linkState from 'linkstate';
|
||||
|
||||
interface Props {
|
||||
@@ -17,17 +20,55 @@ interface Props {
|
||||
interface State {}
|
||||
|
||||
export default class AVIFEncoderOptions extends Component<Props, State> {
|
||||
state: State = {
|
||||
};
|
||||
state: State = {};
|
||||
|
||||
@bind
|
||||
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) {
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
|
||||
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
||||
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
|
||||
import WebPEncoderOptions from '../../codecs/webp/options';
|
||||
import AvifEncoderOptions from '../../codecs/avif/options';
|
||||
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/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 mozJPEG from '../../codecs/mozjpeg/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 browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
|
||||
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
|
||||
@@ -47,6 +49,7 @@ const encoderOptionsComponentMap: {
|
||||
[oxiPNG.type]: OxiPNGEncoderOptions,
|
||||
[mozJPEG.type]: MozJpegEncoderOptions,
|
||||
[webP.type]: WebPEncoderOptions,
|
||||
[avif.type]: AvifEncoderOptions,
|
||||
[browserPNG.type]: undefined,
|
||||
[browserJPEG.type]: BrowserJPEGEncoderOptions,
|
||||
[browserWebP.type]: BrowserWebPEncoderOptions,
|
||||
|
||||
Reference in New Issue
Block a user