|
|
|
import { buf } from "crc-32";
|
|
|
|
import { Buffer } from "buffer";
|
|
|
|
import type { ImageProcessor } from "./main";
|
|
|
|
|
|
|
|
export type PNGChunk = [
|
|
|
|
string, // name
|
|
|
|
Buffer, // data
|
|
|
|
number, // crc
|
|
|
|
number];// offset
|
|
|
|
|
|
|
|
export class PNGDecoder {
|
|
|
|
repr: Buffer;
|
|
|
|
|
|
|
|
req = 8;
|
|
|
|
|
|
|
|
ptr = 8;
|
|
|
|
|
|
|
|
constructor(private reader: ReadableStreamDefaultReader<Uint8Array>, private strict = true) {
|
|
|
|
this.repr = Buffer.from([]);
|
|
|
|
}
|
|
|
|
|
|
|
|
stopped = false;
|
|
|
|
|
|
|
|
async catchup() {
|
|
|
|
while (this.repr.byteLength < this.req) {
|
|
|
|
const chunk = await this.reader.read();
|
|
|
|
if (chunk.done) {
|
|
|
|
if (this.strict)
|
|
|
|
throw new Error(`Unexpected EOF, got ${this.repr.byteLength}, required ${this.req}, ${chunk.value}`);
|
|
|
|
this.stopped = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.repr = Buffer.concat([this.repr, chunk.value]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async *chunks() {
|
|
|
|
while (true) {
|
|
|
|
this.req += 8; // req length and name
|
|
|
|
await this.catchup();
|
|
|
|
if (this.stopped) break;
|
|
|
|
const length = this.repr.readUInt32BE(this.ptr);
|
|
|
|
const name = this.repr.slice(this.ptr + 4, this.ptr + 8).toString();
|
|
|
|
this.ptr += 4; // set pointer to data;
|
|
|
|
this.req += length + 4; // crc
|
|
|
|
await this.catchup(); // try to get entire chunk
|
|
|
|
// allow last chunk to be partial
|
|
|
|
yield [name,
|
|
|
|
this.repr.slice(this.ptr, this.ptr + length + 4),
|
|
|
|
this.ptr + length > this.repr.length ? -1 : this.repr.readUInt32BE(this.ptr + length + 4),
|
|
|
|
this.ptr] as PNGChunk;
|
|
|
|
if (this.stopped) break;
|
|
|
|
this.ptr += length + 8;
|
|
|
|
if (name == 'IEND')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async dtor() {
|
|
|
|
//ugh
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PNGEncoder {
|
|
|
|
writer: WritableStreamDefaultWriter<Buffer>;
|
|
|
|
|
|
|
|
constructor(bytes: WritableStream<Buffer>) {
|
|
|
|
this.writer = bytes.getWriter();
|
|
|
|
this.writer.write(Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]));
|
|
|
|
}
|
|
|
|
|
|
|
|
async insertchunk(chunk: PNGChunk) {
|
|
|
|
let b = Buffer.alloc(4);
|
|
|
|
const buff = chunk[1];
|
|
|
|
b.writeInt32BE(buff.length - 4, 0);
|
|
|
|
await this.writer.write(b); // write length
|
|
|
|
await this.writer.write(buff); // chunk includes chunkname
|
|
|
|
b = Buffer.alloc(4);
|
|
|
|
b.writeInt32BE(buf(buff), 0);
|
|
|
|
await this.writer.write(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
async dtor() {
|
|
|
|
this.writer.releaseLock();
|
|
|
|
//await this.writer.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const BufferWriteStream = () => {
|
|
|
|
let b = Buffer.from([]);
|
|
|
|
const ret = new WritableStream<Buffer>({
|
|
|
|
write(chunk) {
|
|
|
|
b = Buffer.concat([b, chunk]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return [ret, () => b] as [WritableStream<Buffer>, () => Buffer];
|
|
|
|
};
|