From 49db0de05f61e4e73dc926d56cb95d9a1069203d Mon Sep 17 00:00:00 2001 From: Surma Date: Thu, 17 May 2018 22:27:24 +0100 Subject: [PATCH] Actually piping the data through the compressor --- src/components/app/index.tsx | 19 +++++++++++++-- src/lib/codec-wrappers/codec.ts | 2 +- src/lib/codec-wrappers/mozjpeg-enc.ts | 33 ++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/components/app/index.tsx b/src/components/app/index.tsx index 8b3abae7..80103d7b 100644 --- a/src/components/app/index.tsx +++ b/src/components/app/index.tsx @@ -25,14 +25,28 @@ export default class App extends Component { } } + private async getImageData(bitmap: ImageBitmap): Promise { + // Make canvas same size as image + const canvas = document.createElement('canvas'); + [canvas.width, canvas.height] = [bitmap.width, bitmap.height]; + // Draw image onto canvas + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error("Could not create canvas contex"); + } + ctx.drawImage(bitmap, 0, 0); + return ctx.getImageData(0, 0, bitmap.width, bitmap.height); + } + @bind async onFileChange(event: Event) { const fileInput = event.target as HTMLInputElement; if (!fileInput.files || !fileInput.files[0]) return; // TODO: handle decode error - const img = await createImageBitmap(fileInput.files[0]); + const bitmap = await createImageBitmap(fileInput.files[0]); + const data = await this.getImageData(bitmap); const encoder = new MozJpegEncoder(); - const compressedData = await encoder.encode(img); + const compressedData = await encoder.encode(data); const blob = new Blob([compressedData], {type: 'image/jpeg'}); const compressedImage = await createImageBitmap(blob); this.setState({ img: compressedImage }); @@ -53,3 +67,4 @@ export default class App extends Component { ); } } + diff --git a/src/lib/codec-wrappers/codec.ts b/src/lib/codec-wrappers/codec.ts index 88483854..c99ea3a1 100644 --- a/src/lib/codec-wrappers/codec.ts +++ b/src/lib/codec-wrappers/codec.ts @@ -1,5 +1,5 @@ export interface Encoder { - encode(image: ImageBitmap): Promise; + encode(data: ImageData): Promise; } export interface Decoder { diff --git a/src/lib/codec-wrappers/mozjpeg-enc.ts b/src/lib/codec-wrappers/mozjpeg-enc.ts index 6977eeaa..da5a0f4e 100644 --- a/src/lib/codec-wrappers/mozjpeg-enc.ts +++ b/src/lib/codec-wrappers/mozjpeg-enc.ts @@ -6,8 +6,10 @@ const wasmBinaryUrl = require('../../../codecs/mozjpeg_enc/mozjpeg_enc.wasm'); export class MozJpegEncoder implements Encoder { private emscriptenModule: Promise; + private api: any; constructor() { this.emscriptenModule = new Promise(resolve => { + // TODO: See if I can just use m.then()? const m = mozjpeg_enc({ // Just to be safe, don’t automatically invoke any wasm functions // noInitialRun: false, @@ -25,11 +27,36 @@ export class MozJpegEncoder implements Encoder { } }); }); + + this.api = (async () => { + // Not sure why, but TypeScript complains that I am using `emscriptenModule` before it’s 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', '', ['number']), + get_result_pointer: m.cwrap('get_result_pointer', 'number', []), + get_result_size: m.cwrap('get_result_size', 'number', []), + }; + })(); } - async encode(bitmap: ImageBitmap): Promise { + async encode(data: ImageData): Promise { const m = await this.emscriptenModule; - console.log(m); - return Promise.resolve(new Uint8Array([1,2,3]).buffer); + 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(resultPointer); + api.destroy_buffer(p); + + return result.buffer; } }