Files
squoosh/src/lib/codec-wrappers/mozjpeg-enc.ts
2018-05-21 13:38:13 +01:00

77 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {Encoder} from './codec';
import mozjpeg_enc from '../../../codecs/mozjpeg_enc/mozjpeg_enc';
// Using require() so TypeScript doesnt complain about this not being a module.
const wasmBinaryUrl = require('../../../codecs/mozjpeg_enc/mozjpeg_enc.wasm');
// API exposed by wasm module. Details in the codecs README.
interface ModuleAPI {
version(): number;
create_buffer(width: number, height: number): number;
destroy_buffer(pointer: number): void;
encode(buffer: number, width: number, height: number, quality: number): void;
free_result(): void;
get_result_pointer(): number;
get_result_size(): number;
}
export class MozJpegEncoder implements Encoder {
private emscriptenModule: Promise<EmscriptenWasm.Module>;
private api: Promise<ModuleAPI>;
constructor() {
this.emscriptenModule = new Promise(resolve => {
const m = mozjpeg_enc({
// Just to be safe, dont automatically invoke any wasm functions
noInitialRun: false,
locateFile(url: string): string {
// Redirect the request for the wasm binary to whatever webpack gave us.
if(url.endsWith('.wasm')) {
return wasmBinaryUrl;
}
return url;
},
onRuntimeInitialized() {
// An Emscripten is a then-able that, for some reason, `then()`s itself,
// causing an infite loop when you wrap it in a real promise. Deleten the `then`
// prop solves this for now.
// See: https://github.com/kripken/emscripten/blob/incoming/src/postamble.js#L129
// TODO(surma@): File a bug with Emscripten on this.
delete (m as any).then;
resolve(m);
}
});
});
this.api = (async () => {
// Not sure why, but TypeScript complains that I am using `emscriptenModule` before its getting assigned, which is clearly not true :shrug: Using `any`
const m = await (this as any).emscriptenModule;
return {
version: m.cwrap('version', 'number', []),
create_buffer: m.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: m.cwrap('destroy_buffer', '', ['number']),
encode: m.cwrap('encode', '', ['number', 'number', 'number', 'number']),
free_result: m.cwrap('free_result', '', []),
get_result_pointer: m.cwrap('get_result_pointer', 'number', []),
get_result_size: m.cwrap('get_result_size', 'number', []),
};
})();
}
async encode(data: ImageData): Promise<ArrayBuffer | SharedArrayBuffer> {
const m = await this.emscriptenModule;
const api = await this.api;
const p = api.create_buffer(data.width, data.height);
m.HEAP8.set(data.data, p);
api.encode(p, data.width, data.height, 2);
const resultPointer = api.get_result_pointer();
const resultSize = api.get_result_size();
const resultView = new Uint8Array(m.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
api.free_result();
api.destroy_buffer(p);
return result.buffer;
}
}