Skip to content

Commit

Permalink
JPEG decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
matmen committed Dec 1, 2020
1 parent d63d921 commit 7d29552
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 3 deletions.
38 changes: 35 additions & 3 deletions ImageScript.js
Expand Up @@ -2,6 +2,7 @@ const png = require('./utils/png');
const gif = require('./utils/gif');
const fontlib = require('./utils/wasm/font');
const svglib = require('./utils/wasm/svg');
const jpeglib = require('./utils/wasm/jpeg');

/**
* Represents an image; provides utility functions
Expand Down Expand Up @@ -962,9 +963,40 @@ class Image {
* @return {Promise<Image>} The decoded image
*/
static async decode(buffer) {
const {width, height, pixels} = await png.decode(new Uint8Array(buffer));
const image = new this(width, height);
image.bitmap.set(pixels);
let image;

if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) { // PNG
const {width, height, pixels} = await png.decode(new Uint8Array(buffer));
image = new this(width, height);
image.bitmap.set(pixels);
} else if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) { // JPEG
const status = await jpeglib.decode(0, buffer, 0, 0);
if (status === 1) throw new Error('Failed decoding JPEG image');
const [pixelType, width, height] = jpeglib.meta(0);
image = new this(width, height);
const data = jpeglib.buffer(0);
jpeglib.free(0);

if (pixelType === 0) {
const view = new DataView(image.bitmap.buffer);

for (let i = 0; i < data.length; i++) {
const pixel = data[i];
view.setUint32(i * 4, pixel << 24 | pixel << 16 | pixel << 8 | 0xff, false);
}
} else if (pixelType === 1) {
image.bitmap.fill(0xff);
for (let i = 0; i < width * height; i++)
image.bitmap.set(data.subarray(i * 3, i * 3 + 3), i * 4);
} else if (pixelType === 2) {
for (let i = 0; i < data.length; i += 4) {
image.bitmap[i] = 0xff * (1 - data[i] / 0xff) * (1 - data[i + 3] / 0xff);
image.bitmap[i + 1] = 0xff * (1 - data[i + 1] / 0xff) * (1 - data[i + 3] / 0xff);
image.bitmap[i + 2] = 0xff * (1 - data[i + 2] / 0xff) * (1 - data[i + 3] / 0xff);
image.bitmap[i + 3] = 0xff;
}
}
} else throw new Error('Unsupported image type');

return image;
}
Expand Down
112 changes: 112 additions & 0 deletions utils/wasm/jpeg.js
@@ -0,0 +1,112 @@
const {readFile} = require('fs').promises;
const {join} = require('path');

let wasm = new Promise(async resolve => {
const module = new WebAssembly.Module(await readFile(join(__dirname, './jpeg.wasm')));
const instance = new WebAssembly.Instance(module);
const wasm = instance.exports;

resolve(wasm);
});

let cachegetUint8Memory0 = null;

function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}

let WASM_VECTOR_LEN = 0;

function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}

let cachegetInt32Memory0 = null;

function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}

let cachegetUint16Memory0 = null;

function getUint16Memory0() {
if (cachegetUint16Memory0 === null || cachegetUint16Memory0.buffer !== wasm.memory.buffer) {
cachegetUint16Memory0 = new Uint16Array(wasm.memory.buffer);
}
return cachegetUint16Memory0;
}

function getArrayU16FromWasm0(ptr, len) {
return getUint16Memory0().subarray(ptr / 2, ptr / 2 + len);
}

function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}

module.exports = {
/**
* @param {number} ptr
* @param {Uint8Array} buffer
* @param {number} _width
* @param {number} _height
* @returns {number}
*/
async decode(ptr, buffer, _width, _height) {
wasm = await wasm;
const ptr0 = passArray8ToWasm0(buffer, wasm.__wbindgen_malloc);
return wasm.decode(ptr, ptr0, WASM_VECTOR_LEN, _width, _height);
},
/**
* @param {number} id
* @returns {Uint16Array}
*/
meta(id) {
try {
const retptr = wasm.__wbindgen_export_1.value - 16;
wasm.__wbindgen_export_1.value = retptr;
wasm.meta(retptr, id);
const r0 = getInt32Memory0()[retptr / 4];
const r1 = getInt32Memory0()[retptr / 4 + 1];
const v0 = getArrayU16FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 2);
return v0;
} finally {
wasm.__wbindgen_export_1.value += 16;
}
},
/**
* @param {number} id
* @returns {Uint8Array}
*/
buffer(id) {
try {
const retptr = wasm.__wbindgen_export_1.value - 16;
wasm.__wbindgen_export_1.value = retptr;
wasm.buffer(retptr, id);
const r0 = getInt32Memory0()[retptr / 4];
const r1 = getInt32Memory0()[retptr / 4 + 1];
const v0 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v0;
} finally {
wasm.__wbindgen_export_1.value += 16;
}
},
/**
* @param {number} id
*/
free(id) {
wasm.free(id);
}
};

Binary file added utils/wasm/jpeg.wasm
Binary file not shown.

0 comments on commit 7d29552

Please sign in to comment.