imagescript 1.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +4 -1
- package/README.md +64 -26
- package/THIRD_PARTY_NOTICES.md +74 -0
- package/dist/codecs/crc.d.ts +9 -0
- package/dist/codecs/crc.js +29 -0
- package/dist/codecs/exif.d.ts +11 -0
- package/dist/codecs/exif.js +27 -0
- package/dist/codecs/gif.d.ts +44 -0
- package/dist/codecs/gif.js +401 -0
- package/dist/codecs/jpeg.d.ts +35 -0
- package/dist/codecs/jpeg.js +980 -0
- package/dist/codecs/png.d.ts +44 -0
- package/dist/codecs/png.js +442 -0
- package/dist/codecs/quantize.d.ts +25 -0
- package/dist/codecs/quantize.js +302 -0
- package/dist/codecs/tiff.d.ts +6 -0
- package/dist/codecs/tiff.js +546 -0
- package/dist/codecs/zlib.d.ts +22 -0
- package/dist/codecs/zlib.js +607 -0
- package/dist/frame.d.ts +57 -0
- package/dist/frame.js +65 -0
- package/dist/gif.d.ts +44 -0
- package/dist/gif.js +93 -0
- package/dist/image-type.d.ts +20 -0
- package/dist/image-type.js +49 -0
- package/dist/image.d.ts +521 -0
- package/dist/image.js +1118 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +13 -0
- package/dist/package.json +1 -0
- package/dist/render/font.d.ts +63 -0
- package/dist/render/font.js +857 -0
- package/dist/render/svg.d.ts +11 -0
- package/dist/render/svg.js +1717 -0
- package/dist/render/text.d.ts +61 -0
- package/dist/render/text.js +317 -0
- package/dist/text-layout.d.ts +46 -0
- package/dist/text-layout.js +35 -0
- package/dist/util/blur.d.ts +13 -0
- package/dist/util/blur.js +146 -0
- package/dist/util/mem.d.ts +10 -0
- package/dist/util/mem.js +23 -0
- package/dist/util/resample.d.ts +12 -0
- package/dist/util/resample.js +99 -0
- package/dist/util/utf8.d.ts +5 -0
- package/dist/util/utf8.js +55 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/licenses/Apache-2.0.txt +202 -0
- package/licenses/README.ijg +260 -0
- package/package.json +31 -7
- package/ImageScript.d.ts +0 -235
- package/ImageScript.js +0 -1669
- package/bench/_.mjs +0 -166
- package/bench/all.mjs +0 -4
- package/bench/blur.mjs +0 -59
- package/bench/flip.mjs +0 -63
- package/bench/overlay.mjs +0 -35
- package/bench/package.json +0 -7
- package/bench/readme.md +0 -64
- package/bench/resize.mjs +0 -52
- package/codecs/LICENSE +0 -21
- package/codecs/lib.d.ts +0 -50
- package/codecs/lib.js +0 -6
- package/codecs/lib.mjs +0 -1
- package/codecs/node/bin/arm64-darwin.node +0 -0
- package/codecs/node/bin/arm64-linux.node +0 -0
- package/codecs/node/bin/x64-darwin.node +0 -0
- package/codecs/node/bin/x64-linux.node +0 -0
- package/codecs/node/bin/x64-win32.node +0 -0
- package/codecs/node/index.js +0 -3
- package/codecs/node/index.mjs +0 -6
- package/codecs/package.json +0 -14
- package/codecs/readme.md +0 -38
- package/codecs/tests/gif.js +0 -0
- package/codecs/tests/jpeg.js +0 -0
- package/codecs/tests/png.js +0 -0
- package/codecs/tests/results/gif/1024.nis.gif +0 -0
- package/codecs/tests/results/gif/1024.skyra.gif +0 -0
- package/codecs/tests/results/gif/256.nis.gif +0 -0
- package/codecs/tests/results/gif/256.skyra.gif +0 -0
- package/codecs/tests/results/jpeg/256.jpegjs.jpeg +0 -0
- package/codecs/tests/results/jpeg/256.nis.jpeg +0 -0
- package/codecs/tests/results/jpeg/256.sharp.jpeg +0 -0
- package/codecs/tests/results/jpeg/512.jpegjs.jpeg +0 -0
- package/codecs/tests/results/jpeg/512.nis.jpeg +0 -0
- package/codecs/tests/results/jpeg/512.sharp.jpeg +0 -0
- package/codecs/tests/results/jpeg/64.jpegjs.jpeg +0 -0
- package/codecs/tests/results/jpeg/64.nis.jpeg +0 -0
- package/codecs/tests/results/jpeg/64.sharp.jpeg +0 -0
- package/codecs/tests/results/png/256.jis.png +0 -0
- package/codecs/tests/results/png/256.nis.png +0 -0
- package/codecs/tests/results/png/256.sharp.png +0 -0
- package/codecs/tests/results/png/256.upng.png +0 -0
- package/codecs/tests/results/png/512.jis.png +0 -0
- package/codecs/tests/results/png/512.nis.png +0 -0
- package/codecs/tests/results/png/512.sharp.png +0 -0
- package/codecs/tests/results/png/512.upng.png +0 -0
- package/codecs/tests/results/png/64.jis.png +0 -0
- package/codecs/tests/results/png/64.nis.png +0 -0
- package/codecs/tests/results/png/64.sharp.png +0 -0
- package/codecs/tests/results/png/64.upng.png +0 -0
- package/codecs/tests/webp.js +0 -0
- package/codecs/wasm/bin/codecs.wasm +0 -0
- package/codecs/wasm/bin/simd.wasm +0 -0
- package/codecs/wasm/bundle/browser.js +0 -1
- package/codecs/wasm/index.js +0 -1
- package/codecs/wasm/index.mjs +0 -1
- package/png/README.md +0 -1
- package/png/node.js +0 -1137
- package/png/src/crc.mjs +0 -49
- package/png/src/mem.mjs +0 -24
- package/png/src/png.mjs +0 -254
- package/png/src/zlib.mjs +0 -618
- package/tsconfig.json +0 -21
- package/utils/mem.js +0 -26
- package/v2/codecs/magic.js +0 -25
- package/v2/framebuffer.d.ts +0 -67
- package/v2/framebuffer.js +0 -2352
- package/v2/framebuffer.mjs +0 -126
- package/v2/framebuffer.rs +0 -1264
- package/v2/index.d.ts +0 -84
- package/v2/index.js +0 -261
- package/v2/ops/blur.mjs +0 -303
- package/v2/ops/color.mjs +0 -277
- package/v2/ops/crop.mjs +0 -64
- package/v2/ops/fill.mjs +0 -33
- package/v2/ops/flip.mjs +0 -33
- package/v2/ops/index.mjs +0 -9
- package/v2/ops/iterator.mjs +0 -34
- package/v2/ops/overlay.mjs +0 -63
- package/v2/ops/resize.mjs +0 -166
- package/v2/ops/rotate.mjs +0 -117
- package/v2/util/mem.mjs +0 -24
- package/wasm/any/font.wasm +0 -0
- package/wasm/any/gif.wasm +0 -0
- package/wasm/any/jpeg.wasm +0 -0
- package/wasm/any/png.wasm +0 -0
- package/wasm/any/svg.wasm +0 -0
- package/wasm/any/tiff.wasm +0 -0
- package/wasm/node/font.js +0 -142
- package/wasm/node/gif.js +0 -76
- package/wasm/node/jpeg.js +0 -92
- package/wasm/node/png.js +0 -49
- package/wasm/node/svg.js +0 -56
- package/wasm/node/tiff.js +0 -47
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { quantize } from "./quantize.js";
|
|
2
|
+
const DISPOSAL_TO_GIF = [0, 1, 3, 2];
|
|
3
|
+
const DISPOSAL_FROM_GIF = [0, 1, 3, 2, 0, 0, 0, 0];
|
|
4
|
+
const MAX_CODE = 4095;
|
|
5
|
+
const UNEXPECTED_END = new Error('GIF: unexpected end of data');
|
|
6
|
+
export function encode({ width, height, loopCount = -1, frames }, quality = 95) {
|
|
7
|
+
const out = new ByteWriter();
|
|
8
|
+
out.bytes(0x47, 0x49, 0x46, 0x38, 0x39, 0x61);
|
|
9
|
+
out.u16(width);
|
|
10
|
+
out.u16(height);
|
|
11
|
+
out.bytes(0x00, 0x00, 0x00);
|
|
12
|
+
if (loopCount !== 0) {
|
|
13
|
+
out.bytes(0x21, 0xff, 0x0b);
|
|
14
|
+
out.string('NETSCAPE2.0');
|
|
15
|
+
out.bytes(0x03, 0x01);
|
|
16
|
+
out.u16(loopCount === -1 ? 0 : loopCount);
|
|
17
|
+
out.bytes(0x00);
|
|
18
|
+
}
|
|
19
|
+
for (const frame of frames) {
|
|
20
|
+
const { palette, indices, transparentIndex } = quantize(frame.pixels, frame.width, { quality });
|
|
21
|
+
const entries = palette.length / 3;
|
|
22
|
+
const bits = Math.max(1, Math.ceil(Math.log2(entries)));
|
|
23
|
+
out.bytes(0x21, 0xf9, 0x04);
|
|
24
|
+
out.bytes(DISPOSAL_TO_GIF[frame.dispose] << 2 | (transparentIndex === -1 ? 0 : 1));
|
|
25
|
+
out.u16(Math.min(0xffff, Math.max(0, Math.round(frame.delay))));
|
|
26
|
+
out.bytes(transparentIndex === -1 ? 0 : transparentIndex, 0x00);
|
|
27
|
+
out.bytes(0x2c);
|
|
28
|
+
out.u16(frame.x);
|
|
29
|
+
out.u16(frame.y);
|
|
30
|
+
out.u16(frame.width);
|
|
31
|
+
out.u16(frame.height);
|
|
32
|
+
out.bytes(0x80 | bits - 1);
|
|
33
|
+
const table = new Uint8Array(3 << bits);
|
|
34
|
+
table.set(palette);
|
|
35
|
+
out.array(table);
|
|
36
|
+
const minCodeSize = Math.max(2, bits);
|
|
37
|
+
out.bytes(minCodeSize);
|
|
38
|
+
writeSubBlocks(out, lzwEncode(indices, minCodeSize));
|
|
39
|
+
}
|
|
40
|
+
out.bytes(0x3b);
|
|
41
|
+
return out.finish();
|
|
42
|
+
}
|
|
43
|
+
function lzwEncode(indices, minCodeSize) {
|
|
44
|
+
const out = new ByteWriter(indices.length);
|
|
45
|
+
const clear = 1 << minCodeSize;
|
|
46
|
+
const eoi = clear + 1;
|
|
47
|
+
const dictionary = new Int16Array(4096 * 256);
|
|
48
|
+
let codeSize = 0, next = 0, buffer = 0, bufferBits = 0;
|
|
49
|
+
const emit = (code) => {
|
|
50
|
+
buffer |= code << bufferBits;
|
|
51
|
+
bufferBits += codeSize;
|
|
52
|
+
while (bufferBits >= 8) {
|
|
53
|
+
out.byte(buffer & 0xff);
|
|
54
|
+
buffer >>>= 8;
|
|
55
|
+
bufferBits -= 8;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const reset = () => {
|
|
59
|
+
dictionary.fill(0);
|
|
60
|
+
codeSize = minCodeSize + 1;
|
|
61
|
+
next = eoi + 1;
|
|
62
|
+
};
|
|
63
|
+
reset();
|
|
64
|
+
emit(clear);
|
|
65
|
+
if (!indices.length) {
|
|
66
|
+
emit(eoi);
|
|
67
|
+
if (bufferBits)
|
|
68
|
+
out.byte(buffer & 0xff);
|
|
69
|
+
return out.finish();
|
|
70
|
+
}
|
|
71
|
+
let prefix = indices[0];
|
|
72
|
+
for (let i = 1; i < indices.length; i++) {
|
|
73
|
+
const index = indices[i];
|
|
74
|
+
const key = prefix << 8 | index;
|
|
75
|
+
const code = dictionary[key];
|
|
76
|
+
if (code) {
|
|
77
|
+
prefix = code;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
emit(prefix);
|
|
81
|
+
if (next <= MAX_CODE) {
|
|
82
|
+
dictionary[key] = next;
|
|
83
|
+
if (next === 1 << codeSize && codeSize < 12)
|
|
84
|
+
codeSize++;
|
|
85
|
+
next++;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
emit(clear);
|
|
89
|
+
reset();
|
|
90
|
+
}
|
|
91
|
+
prefix = index;
|
|
92
|
+
}
|
|
93
|
+
emit(prefix);
|
|
94
|
+
emit(eoi);
|
|
95
|
+
if (bufferBits)
|
|
96
|
+
out.byte(buffer & 0xff);
|
|
97
|
+
return out.finish();
|
|
98
|
+
}
|
|
99
|
+
function writeSubBlocks(out, data) {
|
|
100
|
+
for (let i = 0; i < data.length; i += 255) {
|
|
101
|
+
const block = data.subarray(i, i + 255);
|
|
102
|
+
out.byte(block.length);
|
|
103
|
+
out.array(block);
|
|
104
|
+
}
|
|
105
|
+
out.byte(0x00);
|
|
106
|
+
}
|
|
107
|
+
export function decode(data, onlyFirstFrame = false) {
|
|
108
|
+
const reader = new ByteReader(data);
|
|
109
|
+
const signature = reader.string(6);
|
|
110
|
+
if (signature !== 'GIF87a' && signature !== 'GIF89a')
|
|
111
|
+
throw new Error('GIF: invalid signature');
|
|
112
|
+
const width = reader.u16();
|
|
113
|
+
const height = reader.u16();
|
|
114
|
+
const flags = reader.u8();
|
|
115
|
+
reader.skip(2);
|
|
116
|
+
const globalTable = flags & 0x80 ? reader.slice(3 << (flags & 7) + 1) : null;
|
|
117
|
+
const frames = [];
|
|
118
|
+
let loopCount = 0;
|
|
119
|
+
let control = null;
|
|
120
|
+
try {
|
|
121
|
+
parse();
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (err !== UNEXPECTED_END)
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
return { width, height, loopCount, frames };
|
|
128
|
+
function parse() {
|
|
129
|
+
while (reader.offset < data.length) {
|
|
130
|
+
const block = reader.u8();
|
|
131
|
+
if (block === 0x3b)
|
|
132
|
+
break;
|
|
133
|
+
if (block === 0x21) {
|
|
134
|
+
const label = reader.u8();
|
|
135
|
+
if (label === 0xf9) {
|
|
136
|
+
const size = reader.u8();
|
|
137
|
+
const packed = reader.u8();
|
|
138
|
+
control = {
|
|
139
|
+
dispose: DISPOSAL_FROM_GIF[packed >> 2 & 7],
|
|
140
|
+
delay: reader.u16(),
|
|
141
|
+
transparentIndex: packed & 1 ? reader.u8() : (reader.skip(1), -1)
|
|
142
|
+
};
|
|
143
|
+
reader.skip(size - 4);
|
|
144
|
+
skipSubBlocks(reader);
|
|
145
|
+
}
|
|
146
|
+
else if (label === 0xff) {
|
|
147
|
+
const size = reader.u8();
|
|
148
|
+
const id = reader.string(size);
|
|
149
|
+
if (id === 'NETSCAPE2.0' || id === 'ANIMEXTS1.0') {
|
|
150
|
+
for (let length; (length = reader.u8());) {
|
|
151
|
+
const sub = reader.slice(length);
|
|
152
|
+
if (sub[0] === 1 && length >= 3) {
|
|
153
|
+
const count = sub[1] | sub[2] << 8;
|
|
154
|
+
loopCount = count === 0 ? -1 : count;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
skipSubBlocks(reader);
|
|
160
|
+
}
|
|
161
|
+
else
|
|
162
|
+
skipSubBlocks(reader);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (block !== 0x2c) {
|
|
166
|
+
if (frames.length)
|
|
167
|
+
break;
|
|
168
|
+
throw new Error(`GIF: unknown block type 0x${block.toString(16)}`);
|
|
169
|
+
}
|
|
170
|
+
const x = reader.u16();
|
|
171
|
+
const y = reader.u16();
|
|
172
|
+
const fw = reader.u16();
|
|
173
|
+
const fh = reader.u16();
|
|
174
|
+
const descriptor = reader.u8();
|
|
175
|
+
const localTable = descriptor & 0x80 ? reader.slice(3 << (descriptor & 7) + 1) : null;
|
|
176
|
+
const interlaced = !!(descriptor & 0x40);
|
|
177
|
+
const table = localTable || globalTable;
|
|
178
|
+
const minCodeSize = reader.u8();
|
|
179
|
+
const compressed = readSubBlocks(reader);
|
|
180
|
+
const indices = lzwDecode(compressed, minCodeSize, fw * fh);
|
|
181
|
+
const { dispose = 0, delay = 0, transparentIndex = -1 } = control || {};
|
|
182
|
+
control = null;
|
|
183
|
+
if (!fw || !fh)
|
|
184
|
+
continue;
|
|
185
|
+
const pixels = new Uint8Array(fw * fh * 4);
|
|
186
|
+
const rows = interlaced ? interlacedRows(fh) : null;
|
|
187
|
+
for (let row = 0; row < fh; row++) {
|
|
188
|
+
const targetRow = rows ? rows[row] : row;
|
|
189
|
+
for (let column = 0; column < fw; column++) {
|
|
190
|
+
const index = indices[row * fw + column];
|
|
191
|
+
if (index === transparentIndex)
|
|
192
|
+
continue;
|
|
193
|
+
const o = (targetRow * fw + column) * 4;
|
|
194
|
+
if (table && index * 3 + 2 < table.length) {
|
|
195
|
+
pixels[o] = table[index * 3];
|
|
196
|
+
pixels[o + 1] = table[index * 3 + 1];
|
|
197
|
+
pixels[o + 2] = table[index * 3 + 2];
|
|
198
|
+
}
|
|
199
|
+
pixels[o + 3] = 0xff;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
frames.push({ x, y, width: fw, height: fh, delay, dispose, pixels });
|
|
203
|
+
if (onlyFirstFrame)
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export function composite({ width, height, frames }) {
|
|
209
|
+
const canvas = new Uint32Array(width * height);
|
|
210
|
+
const output = [];
|
|
211
|
+
for (const frame of frames) {
|
|
212
|
+
const previous = frame.dispose === 2 ? canvas.slice() : null;
|
|
213
|
+
const src = new Uint32Array(frame.pixels.buffer, frame.pixels.byteOffset, frame.width * frame.height);
|
|
214
|
+
const x0 = Math.min(frame.x, width), x1 = Math.min(frame.x + frame.width, width);
|
|
215
|
+
const y0 = Math.min(frame.y, height), y1 = Math.min(frame.y + frame.height, height);
|
|
216
|
+
for (let y = y0; y < y1; y++) {
|
|
217
|
+
const srcRow = (y - frame.y) * frame.width - frame.x;
|
|
218
|
+
const dstRow = y * width;
|
|
219
|
+
for (let x = x0; x < x1; x++)
|
|
220
|
+
if (frame.pixels[(srcRow + x) * 4 + 3] !== 0)
|
|
221
|
+
canvas[dstRow + x] = src[srcRow + x];
|
|
222
|
+
}
|
|
223
|
+
output.push({ pixels: new Uint8Array(canvas.slice().buffer), delay: frame.delay });
|
|
224
|
+
if (frame.dispose === 3) {
|
|
225
|
+
for (let y = y0; y < y1; y++)
|
|
226
|
+
canvas.fill(0, y * width + x0, y * width + x1);
|
|
227
|
+
}
|
|
228
|
+
else if (previous)
|
|
229
|
+
canvas.set(previous);
|
|
230
|
+
}
|
|
231
|
+
if (!output.length)
|
|
232
|
+
output.push({ pixels: new Uint8Array(width * height * 4), delay: 0 });
|
|
233
|
+
return output;
|
|
234
|
+
}
|
|
235
|
+
function interlacedRows(height) {
|
|
236
|
+
const rows = [];
|
|
237
|
+
for (const [start, step] of [[0, 8], [4, 8], [2, 4], [1, 2]])
|
|
238
|
+
for (let y = start; y < height; y += step)
|
|
239
|
+
rows.push(y);
|
|
240
|
+
return rows;
|
|
241
|
+
}
|
|
242
|
+
function lzwDecode(data, minCodeSize, pixelCount) {
|
|
243
|
+
const output = new Uint8Array(pixelCount);
|
|
244
|
+
if (minCodeSize < 1 || minCodeSize > 11)
|
|
245
|
+
return output;
|
|
246
|
+
const clear = 1 << minCodeSize;
|
|
247
|
+
const eoi = clear + 1;
|
|
248
|
+
const prefix = new Uint16Array(4096);
|
|
249
|
+
const suffix = new Uint8Array(4096);
|
|
250
|
+
const first = new Uint8Array(4096);
|
|
251
|
+
const lengths = new Uint16Array(4096);
|
|
252
|
+
for (let i = 0; i < clear; i++) {
|
|
253
|
+
suffix[i] = first[i] = i;
|
|
254
|
+
lengths[i] = 1;
|
|
255
|
+
}
|
|
256
|
+
let codeSize = minCodeSize + 1;
|
|
257
|
+
let next = eoi + 1;
|
|
258
|
+
let previous = -1;
|
|
259
|
+
let buffer = 0, bufferBits = 0, position = 0, out = 0;
|
|
260
|
+
while (out < pixelCount) {
|
|
261
|
+
while (bufferBits < codeSize) {
|
|
262
|
+
if (position >= data.length)
|
|
263
|
+
return output;
|
|
264
|
+
buffer |= data[position++] << bufferBits;
|
|
265
|
+
bufferBits += 8;
|
|
266
|
+
}
|
|
267
|
+
const code = buffer & (1 << codeSize) - 1;
|
|
268
|
+
buffer >>>= codeSize;
|
|
269
|
+
bufferBits -= codeSize;
|
|
270
|
+
if (code === clear) {
|
|
271
|
+
codeSize = minCodeSize + 1;
|
|
272
|
+
next = eoi + 1;
|
|
273
|
+
previous = -1;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (code === eoi)
|
|
277
|
+
break;
|
|
278
|
+
let entry;
|
|
279
|
+
if (code < next)
|
|
280
|
+
entry = code;
|
|
281
|
+
else if (code === next && previous !== -1)
|
|
282
|
+
entry = previous;
|
|
283
|
+
else
|
|
284
|
+
break;
|
|
285
|
+
if (previous === -1 && code >= clear)
|
|
286
|
+
break;
|
|
287
|
+
const length = lengths[entry];
|
|
288
|
+
const end = Math.min(out + length, pixelCount);
|
|
289
|
+
let c = entry;
|
|
290
|
+
for (let i = out + length - 1; i >= out; i--) {
|
|
291
|
+
if (i < end)
|
|
292
|
+
output[i] = suffix[c];
|
|
293
|
+
c = prefix[c];
|
|
294
|
+
}
|
|
295
|
+
let written = end - out;
|
|
296
|
+
if (code === next) {
|
|
297
|
+
if (out + length < pixelCount) {
|
|
298
|
+
output[out + length] = first[previous];
|
|
299
|
+
written++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
out += written;
|
|
303
|
+
if (previous !== -1 && next <= MAX_CODE) {
|
|
304
|
+
prefix[next] = previous;
|
|
305
|
+
suffix[next] = first[code === next ? previous : code];
|
|
306
|
+
first[next] = first[previous];
|
|
307
|
+
lengths[next] = lengths[previous] + 1;
|
|
308
|
+
next++;
|
|
309
|
+
if (next === 1 << codeSize && codeSize < 12)
|
|
310
|
+
codeSize++;
|
|
311
|
+
}
|
|
312
|
+
previous = code;
|
|
313
|
+
}
|
|
314
|
+
return output;
|
|
315
|
+
}
|
|
316
|
+
function readSubBlocks(reader) {
|
|
317
|
+
const blocks = [];
|
|
318
|
+
let total = 0;
|
|
319
|
+
for (let length; reader.offset < reader.data.length && (length = reader.u8());) {
|
|
320
|
+
const block = reader.slice(length);
|
|
321
|
+
blocks.push(block);
|
|
322
|
+
total += block.length;
|
|
323
|
+
}
|
|
324
|
+
const out = new Uint8Array(total);
|
|
325
|
+
let offset = 0;
|
|
326
|
+
for (const block of blocks) {
|
|
327
|
+
out.set(block, offset);
|
|
328
|
+
offset += block.length;
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
function skipSubBlocks(reader) {
|
|
333
|
+
for (let length; reader.offset < reader.data.length && (length = reader.u8());)
|
|
334
|
+
reader.skip(length);
|
|
335
|
+
}
|
|
336
|
+
class ByteReader {
|
|
337
|
+
data;
|
|
338
|
+
offset;
|
|
339
|
+
constructor(data) {
|
|
340
|
+
this.data = data;
|
|
341
|
+
this.offset = 0;
|
|
342
|
+
}
|
|
343
|
+
u8() {
|
|
344
|
+
if (this.offset >= this.data.length)
|
|
345
|
+
throw UNEXPECTED_END;
|
|
346
|
+
return this.data[this.offset++];
|
|
347
|
+
}
|
|
348
|
+
u16() {
|
|
349
|
+
return this.u8() | this.u8() << 8;
|
|
350
|
+
}
|
|
351
|
+
skip(length) {
|
|
352
|
+
this.offset += length;
|
|
353
|
+
}
|
|
354
|
+
slice(length) {
|
|
355
|
+
const slice = this.data.subarray(this.offset, this.offset + length);
|
|
356
|
+
this.offset += length;
|
|
357
|
+
return slice;
|
|
358
|
+
}
|
|
359
|
+
string(length) {
|
|
360
|
+
return String.fromCharCode(...this.slice(length));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
class ByteWriter {
|
|
364
|
+
buffer;
|
|
365
|
+
length;
|
|
366
|
+
constructor(capacity = 1024) {
|
|
367
|
+
this.buffer = new Uint8Array(Math.max(16, capacity));
|
|
368
|
+
this.length = 0;
|
|
369
|
+
}
|
|
370
|
+
reserve(size) {
|
|
371
|
+
if (this.length + size <= this.buffer.length)
|
|
372
|
+
return;
|
|
373
|
+
const buffer = new Uint8Array(Math.max(this.buffer.length * 2, this.length + size));
|
|
374
|
+
buffer.set(this.buffer.subarray(0, this.length));
|
|
375
|
+
this.buffer = buffer;
|
|
376
|
+
}
|
|
377
|
+
byte(value) {
|
|
378
|
+
this.reserve(1);
|
|
379
|
+
this.buffer[this.length++] = value;
|
|
380
|
+
}
|
|
381
|
+
bytes(...values) {
|
|
382
|
+
for (const value of values)
|
|
383
|
+
this.byte(value);
|
|
384
|
+
}
|
|
385
|
+
u16(value) {
|
|
386
|
+
this.byte(value & 0xff);
|
|
387
|
+
this.byte(value >> 8 & 0xff);
|
|
388
|
+
}
|
|
389
|
+
string(value) {
|
|
390
|
+
for (let i = 0; i < value.length; i++)
|
|
391
|
+
this.byte(value.charCodeAt(i));
|
|
392
|
+
}
|
|
393
|
+
array(values) {
|
|
394
|
+
this.reserve(values.length);
|
|
395
|
+
this.buffer.set(values, this.length);
|
|
396
|
+
this.length += values.length;
|
|
397
|
+
}
|
|
398
|
+
finish() {
|
|
399
|
+
return this.buffer.slice(0, this.length);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* JPEG codec. This software is based in part on the work of the Independent JPEG Group: the IDCT, FDCT, upsampling,
|
|
3
|
+
* color conversion, progressive refinement and optimal Huffman tables are translated from libjpeg/libjpeg-turbo
|
|
4
|
+
* (IJG license, see licenses/README.ijg). The scan decoder follows the structure of pdf.js' jpg.js
|
|
5
|
+
* (Copyright 2014 Mozilla Foundation, Apache License 2.0). See THIRD_PARTY_NOTICES.md
|
|
6
|
+
*/
|
|
7
|
+
export interface DecodeOptions {
|
|
8
|
+
/** overrides the color transform detected from JFIF/Adobe markers (used by TIFF) */
|
|
9
|
+
transform?: 'ycc' | 'rgb' | 'cmyk' | null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Decodes a JPEG into 8-bit RGBA pixel data
|
|
13
|
+
* Supports baseline, extended and progressive huffman coded JPEGs with any
|
|
14
|
+
* sampling factors, restart intervals and greyscale, YCbCr, RGB, CMYK and YCCK color
|
|
15
|
+
* The output matches libjpeg's defaults (islow IDCT, fancy upsampling)
|
|
16
|
+
* @param {Uint8Array} data
|
|
17
|
+
* @param {{transform?: 'ycc'|'rgb'|'cmyk'}} [options] overrides the color transform detected from JFIF/Adobe markers (used by TIFF)
|
|
18
|
+
* @return {{width: number, height: number, pixels: Uint8Array}}
|
|
19
|
+
*/
|
|
20
|
+
export declare function decode(data: Uint8Array, { transform }?: DecodeOptions): {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
pixels: Uint8Array;
|
|
24
|
+
orientation: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Encodes RGBA pixel data into a baseline JPEG (alpha is ignored)
|
|
28
|
+
* Uses 4:2:0 chroma subsampling up to quality 90 and 4:4:4 above, with optimized huffman tables
|
|
29
|
+
* @param {Uint8Array} pixels
|
|
30
|
+
* @param {number} width
|
|
31
|
+
* @param {number} height
|
|
32
|
+
* @param {number} [quality=90] 1-100
|
|
33
|
+
* @return {Uint8Array}
|
|
34
|
+
*/
|
|
35
|
+
export declare function encode(pixels: Uint8Array, width: number, height: number, quality?: number): Uint8Array;
|