PEE/src/png.ts

104 lines
2.9 KiB
TypeScript
Raw Normal View History

import { buf } from "crc-32";
import { Buffer } from "buffer";
2022-01-05 01:14:23 +00:00
import type { ImageProcessor } from "./main";
2022-01-13 09:24:59 +00:00
export type PNGChunk = [
string, // name
2022-07-29 03:35:31 +00:00
() => Promise<Buffer>, // data
() => Promise<number>, // crc
2022-01-13 09:24:59 +00:00
number];// offset
2022-01-12 06:58:46 +00:00
export class PNGDecoder {
repr: Buffer;
2022-01-01 18:52:50 +00:00
req = 8;
2022-01-01 18:52:50 +00:00
ptr = 8;
2022-07-29 01:06:03 +00:00
constructor(private reader: ReadableStreamDefaultReader<Uint8Array>, private strict = true) {
this.repr = Buffer.from([]);
}
2022-07-29 03:35:31 +00:00
stopped = false;
async catchup() {
while (this.repr.byteLength < this.req) {
2022-01-01 18:52:50 +00:00
const chunk = await this.reader.read();
2022-01-04 15:36:43 +00:00
if (chunk.done) {
2022-07-29 01:06:03 +00:00
if (this.strict)
throw new Error(`Unexpected EOF, got ${this.repr.byteLength}, required ${this.req}, ${chunk.value}`);
2022-07-29 03:35:31 +00:00
this.stopped = true;
2022-07-29 01:06:03 +00:00
return;
2022-01-04 15:36:43 +00:00
}
this.repr = Buffer.concat([this.repr, chunk.value]);
}
}
async *chunks() {
while (true) {
this.req += 8; // req length and name
await this.catchup();
2022-07-29 03:35:31 +00:00
if (this.stopped) break;
2022-01-01 18:52:50 +00:00
const length = this.repr.readUInt32BE(this.ptr);
const name = this.repr.slice(this.ptr + 4, this.ptr + 8).toString();
this.ptr += 4;
this.req += length + 4; // crc
2022-01-13 09:24:59 +00:00
//await this.catchup();
2022-07-29 03:35:31 +00:00
const pos = this.ptr;
2022-07-29 01:06:03 +00:00
yield [name,
2022-07-29 03:35:31 +00:00
async () => {
await this.catchup();
return this.repr.slice(pos, pos + length + 4);
},
async () => {
await this.catchup();
return this.repr.readUInt32BE(this.ptr + length + 4);
},
2022-01-13 09:24:59 +00:00
this.ptr] as PNGChunk;
2022-07-29 03:35:31 +00:00
if (this.stopped) break;
this.ptr += length + 8;
if (name == 'IEND')
break;
}
}
async dtor() {
2022-01-01 18:52:50 +00:00
//ugh
}
}
2022-01-12 06:58:46 +00:00
export class PNGEncoder {
2022-07-29 03:35:31 +00:00
writer: WritableStreamDefaultWriter<Buffer>;
2022-07-29 03:35:31 +00:00
constructor(bytes: WritableStream<Buffer>) {
this.writer = bytes.getWriter();
this.writer.write(Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]));
}
2022-07-29 03:35:31 +00:00
async insertchunk(chunk: PNGChunk) {
let b = Buffer.alloc(4);
2022-07-29 03:35:31 +00:00
const buff = await chunk[1]();
2022-01-13 12:55:57 +00:00
b.writeInt32BE(buff.length - 4, 0);
2022-07-29 03:35:31 +00:00
await this.writer.write(b); // write length
await this.writer.write(buff); // chunk includes chunkname
b = Buffer.alloc(4);
2022-01-13 12:55:57 +00:00
b.writeInt32BE(buf(buff), 0);
2022-07-29 03:35:31 +00:00
await this.writer.write(b);
}
async dtor() {
2022-07-29 03:35:31 +00:00
this.writer.releaseLock();
await this.writer.close();
}
2022-01-01 18:52:50 +00:00
}
2022-01-03 22:29:28 +00:00
export const BufferWriteStream = () => {
2022-01-01 18:52:50 +00:00
let b = Buffer.from([]);
const ret = new WritableStream<Buffer>({
write(chunk) {
2022-01-04 15:36:43 +00:00
b = Buffer.concat([b, chunk]);
2022-01-01 18:52:50 +00:00
}
});
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
};