Skip to content

Commit 99909e9

Browse files
committed
JPEG decoding
1 parent 1bffc97 commit 99909e9

3 files changed

Lines changed: 147 additions & 3 deletions

File tree

‎ImageScript.js‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as png from './utils/png.js';
22
import * as gif from './utils/gif.js';
33
import * as fontlib from './utils/wasm/font.js';
44
import * as svglib from './utils/wasm/svg.js';
5+
import * as jpeglib from './utils/wasm/jpeg.js';
56

67
/**
78
* Represents an image; provides utility functions
@@ -962,9 +963,40 @@ export class Image {
962963
* @return {Promise<Image>} The decoded image
963964
*/
964965
static async decode(buffer) {
965-
const {width, height, pixels} = await png.decode(new Uint8Array(buffer));
966-
const image = new this(width, height);
967-
image.bitmap.set(pixels);
966+
let image;
967+
968+
if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) { // PNG
969+
const {width, height, pixels} = await png.decode(new Uint8Array(buffer));
970+
image = new this(width, height);
971+
image.bitmap.set(pixels);
972+
} else if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) { // JPEG
973+
const status = await jpeglib.decode(0, buffer, 0, 0);
974+
if (status === 1) throw new Error('Failed decoding JPEG image');
975+
const [pixelType, width, height] = jpeglib.meta(0);
976+
image = new this(width, height);
977+
const data = jpeglib.buffer(0);
978+
jpeglib.free(0);
979+
980+
if (pixelType === 0) {
981+
const view = new DataView(image.bitmap.buffer);
982+
983+
for (let i = 0; i < data.length; i++) {
984+
const pixel = data[i];
985+
view.setUint32(i * 4, pixel << 24 | pixel << 16 | pixel << 8 | 0xff, false);
986+
}
987+
} else if (pixelType === 1) {
988+
image.bitmap.fill(0xff);
989+
for (let i = 0; i < width * height; i++)
990+
image.bitmap.set(data.subarray(i * 3, i * 3 + 3), i * 4);
991+
} else if (pixelType === 2) {
992+
for (let i = 0; i < data.length; i += 4) {
993+
image.bitmap[i] = 0xff * (1 - data[i] / 0xff) * (1 - data[i + 3] / 0xff);
994+
image.bitmap[i + 1] = 0xff * (1 - data[i + 1] / 0xff) * (1 - data[i + 3] / 0xff);
995+
image.bitmap[i + 2] = 0xff * (1 - data[i + 2] / 0xff) * (1 - data[i + 3] / 0xff);
996+
image.bitmap[i + 3] = 0xff;
997+
}
998+
}
999+
} else throw new Error('Unsupported image type');
9681000

9691001
return image;
9701002
}

‎utils/wasm/jpeg.js‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const {readFile} = require('fs').promises;
2+
const {join} = require('path');
3+
4+
let wasm = new Promise(async resolve => {
5+
const module = new WebAssembly.Module(await readFile(join(__dirname, './jpeg.wasm')));
6+
const instance = new WebAssembly.Instance(module);
7+
const wasm = instance.exports;
8+
9+
resolve(wasm);
10+
});
11+
12+
let cachegetUint8Memory0 = null;
13+
14+
function getUint8Memory0() {
15+
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
16+
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
17+
}
18+
return cachegetUint8Memory0;
19+
}
20+
21+
let WASM_VECTOR_LEN = 0;
22+
23+
function passArray8ToWasm0(arg, malloc) {
24+
const ptr = malloc(arg.length * 1);
25+
getUint8Memory0().set(arg, ptr / 1);
26+
WASM_VECTOR_LEN = arg.length;
27+
return ptr;
28+
}
29+
30+
let cachegetInt32Memory0 = null;
31+
32+
function getInt32Memory0() {
33+
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
34+
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
35+
}
36+
return cachegetInt32Memory0;
37+
}
38+
39+
let cachegetUint16Memory0 = null;
40+
41+
function getUint16Memory0() {
42+
if (cachegetUint16Memory0 === null || cachegetUint16Memory0.buffer !== wasm.memory.buffer) {
43+
cachegetUint16Memory0 = new Uint16Array(wasm.memory.buffer);
44+
}
45+
return cachegetUint16Memory0;
46+
}
47+
48+
function getArrayU16FromWasm0(ptr, len) {
49+
return getUint16Memory0().subarray(ptr / 2, ptr / 2 + len);
50+
}
51+
52+
function getArrayU8FromWasm0(ptr, len) {
53+
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
54+
}
55+
56+
module.exports = {
57+
/**
58+
* @param {number} ptr
59+
* @param {Uint8Array} buffer
60+
* @param {number} _width
61+
* @param {number} _height
62+
* @returns {number}
63+
*/
64+
async decode(ptr, buffer, _width, _height) {
65+
wasm = await wasm;
66+
const ptr0 = passArray8ToWasm0(buffer, wasm.__wbindgen_malloc);
67+
return wasm.decode(ptr, ptr0, WASM_VECTOR_LEN, _width, _height);
68+
},
69+
/**
70+
* @param {number} id
71+
* @returns {Uint16Array}
72+
*/
73+
meta(id) {
74+
try {
75+
const retptr = wasm.__wbindgen_export_1.value - 16;
76+
wasm.__wbindgen_export_1.value = retptr;
77+
wasm.meta(retptr, id);
78+
const r0 = getInt32Memory0()[retptr / 4];
79+
const r1 = getInt32Memory0()[retptr / 4 + 1];
80+
const v0 = getArrayU16FromWasm0(r0, r1).slice();
81+
wasm.__wbindgen_free(r0, r1 * 2);
82+
return v0;
83+
} finally {
84+
wasm.__wbindgen_export_1.value += 16;
85+
}
86+
},
87+
/**
88+
* @param {number} id
89+
* @returns {Uint8Array}
90+
*/
91+
buffer(id) {
92+
try {
93+
const retptr = wasm.__wbindgen_export_1.value - 16;
94+
wasm.__wbindgen_export_1.value = retptr;
95+
wasm.buffer(retptr, id);
96+
const r0 = getInt32Memory0()[retptr / 4];
97+
const r1 = getInt32Memory0()[retptr / 4 + 1];
98+
const v0 = getArrayU8FromWasm0(r0, r1).slice();
99+
wasm.__wbindgen_free(r0, r1 * 1);
100+
return v0;
101+
} finally {
102+
wasm.__wbindgen_export_1.value += 16;
103+
}
104+
},
105+
/**
106+
* @param {number} id
107+
*/
108+
free(id) {
109+
wasm.free(id);
110+
}
111+
};
112+

‎utils/wasm/jpeg.wasm‎

96.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)