D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
whatsapp-api
/
node_modules
/
node-webpmux
/
Filename :
parser.js
back
Copy
const IO = require('./io.js'); const nullByte = Buffer.alloc(1); nullByte[0] = 0; const intfTypes = { NONE: 0, FILE: 1, BUFFER: 2 }; const constants = { TYPE_LOSSY: 0, TYPE_LOSSLESS: 1, TYPE_EXTENDED: 2 }; function VP8Width(data) { return ((data[7] << 8) | data[6]) & 0b0011111111111111; } function VP8Height(data) { return ((data[9] << 8) | data[8]) & 0b0011111111111111; } function VP8LWidth(data) { return (((data[2] << 8) | data[1]) & 0b0011111111111111) + 1; } function VP8LHeight(data) { return ((((data[4] << 16) | (data[3] << 8) | data[2]) >> 6) & 0b0011111111111111) + 1; } function doesVP8LHaveAlpha(data) { return !!(data[4] & 0b00010000); } function createBasicChunk(name, data) { let header = Buffer.alloc(8), size = data.length; header.write(name, 0); header.writeUInt32LE(size, 4); if (size&1) { return { size: size + 9, chunks: [ header, data, nullByte ] }; } else { return { size: size + 8, chunks: [ header, data ] }; } } class WebPReader { constructor() { this.type = intfTypes.NONE; } readFile(path) { this.type = intfTypes.FILE; this.path = path; } readBuffer(buf) { this.type = intfTypes.BUFFER; this.buf = buf; this.cursor = 0; } async readBytes(n, mod) { let { type } = this; if (type == intfTypes.FILE) { let b = Buffer.alloc(n), br; br = (await IO.read(this.fp, b, 0, n, undefined)).bytesRead; return mod ? b : br == n ? b : undefined; } else if (type == intfTypes.BUFFER) { let b = this.buf.slice(this.cursor, this.cursor + n); this.cursor += n; return b; } else { throw new Error('Reader not initialized'); } } async readFileHeader() { let buf = await this.readBytes(12); if (buf === undefined) { throw new Error('Reached end while reading header'); } if (buf.toString('utf8', 0, 4) != 'RIFF') { throw new Error('Bad header (not RIFF)'); } if (buf.toString('utf8', 8, 12) != 'WEBP') { throw new Error('Bad header (not WEBP)'); } return { fileSize: buf.readUInt32LE(4) }; } async readChunkHeader() { let buf = await this.readBytes(8, true); if (buf.length == 0) { return { fourCC: '\x00\x00\x00\x00', size: 0 }; } else if (buf.length < 8) { throw new Error('Reached end while reading chunk header'); } return { fourCC: buf.toString('utf8', 0, 4), size: buf.readUInt32LE(4) }; } async readChunkContents(size) { let buf = await this.readBytes(size); if (size & 1) { await this.readBytes(1); } return buf; } async readChunk_raw(n, size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error(`Reached end while reading ${n} chunk`); } return { raw: buf }; } async readChunk_VP8(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while reading VP8 chunk'); } return { raw: buf, width: VP8Width(buf), height: VP8Height(buf) }; } async readChunk_VP8L(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while reading VP8L chunk'); } return { raw: buf, alpha: doesVP8LHaveAlpha(buf), width: VP8LWidth(buf), height: VP8LHeight(buf) }; } async readChunk_VP8X(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while reading VP8X chunk'); } return { raw: buf, hasICCP: !!(buf[0] & 0b00100000), hasAlpha: !!(buf[0] & 0b00010000), hasEXIF: !!(buf[0] & 0b00001000), hasXMP: !!(buf[0] & 0b00000100), hasAnim: !!(buf[0] & 0b00000010), width: buf.readUIntLE(4, 3) + 1, height: buf.readUIntLE(7, 3) + 1 }; } async readChunk_ANIM(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while reading ANIM chunk'); } return { raw: buf, bgColor: buf.slice(0, 4), loops: buf.readUInt16LE(4) }; } async readChunk_ANMF(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while reading ANMF chunk'); } let out = { raw: buf, x: buf.readUIntLE(0, 3), y: buf.readUIntLE(3, 3), width: buf.readUIntLE(6, 3) + 1, height: buf.readUIntLE(9, 3) + 1, delay: buf.readUIntLE(12, 3), blend: !(buf[15] & 0b00000010), dispose: !!(buf[15] & 0b00000001) }, keepLooping = true, anmfReader = new WebPReader(); anmfReader.readBuffer(buf); anmfReader.cursor = 16; while (keepLooping) { let header = await anmfReader.readChunkHeader(); switch (header.fourCC) { case 'VP8 ': if (!out.vp8) { out.type = constants.TYPE_LOSSY; out.vp8 = await anmfReader.readChunk_VP8(header.size); if (out.alph) { out.vp8.alpha = true; } } break; case 'VP8L': if (!out.vp8l) { out.type = constants.TYPE_LOSSLESS; out.vp8l = await anmfReader.readChunk_VP8L(header.size); } break; case 'ALPH': if (!out.alph) { out.alph = await anmfReader.readChunk_ALPH(header.size); if (out.vp8) { out.vp8.alpha = true; } } break; case '\x00\x00\x00\x00': default: keepLooping = false; break; } if (anmfReader.cursor >= buf.length) { break; } } return out; } async readChunk_ALPH(size) { return this.readChunk_raw('ALPH', size); } async readChunk_ICCP(size) { return this.readChunk_raw('ICCP', size); } async readChunk_EXIF(size) { return this.readChunk_raw('EXIF', size); } async readChunk_XMP(size) { return this.readChunk_raw('XMP ', size); } async readChunk_skip(size) { let buf = await this.readChunkContents(size); if (buf === undefined) { throw new Error('Reached end while skipping chunk'); } } async read() { if (this.type == intfTypes.FILE) { this.fp = await IO.open(this.path, 'r'); } let keepLooping = true, first = true, { fileSize } = await this.readFileHeader(), out = {}; while (keepLooping) { let { fourCC, size } = await this.readChunkHeader(); switch (fourCC) { case 'VP8 ': if (!out.vp8) { out.vp8 = await this.readChunk_VP8(size); if (out.alph) { out.vp8.alpha = true; } if (first) { out.type = constants.TYPE_LOSSY; keepLooping = false; } } else { await this.readChunk_skip(size); } break; case 'VP8L': if (!out.vp8l) { out.vp8l = await this.readChunk_VP8L(size); if (first) { out.type = constants.TYPE_LOSSLESS; keepLooping = false; } } else { await this.readChunk_skip(size); } break; case 'VP8X': if (!out.extended) { out.type = constants.TYPE_EXTENDED; out.extended = await this.readChunk_VP8X(size); } else { await this.readChunk_skip(size); } break; case 'ANIM': if (!out.anim) { let { raw, bgColor, loops } = await this.readChunk_ANIM(size); out.anim = { bgColor: [ bgColor[2], bgColor[1], bgColor[0], bgColor[3] ], loops, frames: [], raw }; } else { await this.readChunk_skip(size); } break; case 'ANMF': out.anim.frames.push(await this.readChunk_ANMF(size)); break; case 'ALPH': if (!out.alph) { out.alph = await this.readChunk_ALPH(size); if (out.vp8) { out.vp8.alpha = true; } } else { await this.readChunk_skip(size); } break; case 'ICCP': if (!out.iccp) { out.iccp = await this.readChunk_ICCP(size); } else { await this.readChunk_skip(size); } break; case 'EXIF': if (!out.exif) { out.exif = await this.readChunk_EXIF(size); } else { await this.readChunk_skip(size); } break; case 'XMP ': if (!out.xmp) { out.xmp = await this.readChunk_XMP(size); } else { await this.readChunk_skip(size); } break; case '\x00\x00\x00\x00': keepLooping = false; break; default: await this.readChunk_skip(size); break; } first = false; } if (this.type == intfTypes.FILE) { await IO.close(this.fp); } return out; } } class WebPWriter { constructor() { this.type = intfTypes.NONE; this.chunks = []; this.width = this.height = 0; } reset() { this.chunks.length = 0; width = 0; height = 0; } writeFile(path) { this.type = intfTypes.FILE; this.path = path; } writeBuffer() { this.type = intfTypes.BUFFER; } async commit() { let { chunks } = this, size = 4, fp; if (this.type == intfTypes.NONE) { throw new Error('Writer not initialized'); } if (chunks.length == 0) { throw new Error('Nothing to write'); } for (let i = 1, l = chunks.length; i < l; i++) { size += chunks[i].length; } chunks[0].writeUInt32LE(size, 4); if (this.type == intfTypes.FILE) { fp = await IO.open(this.path, 'w'); for (let i = 0, l = chunks.length; i < l; i++) { await IO.write(fp, chunks[i], 0, undefined, undefined); } await IO.close(fp); } else { return Buffer.concat(chunks); } } writeBytes(...chunks) { if (this.type == intfTypes.NONE) { throw new Error('Writer not initialized'); } this.chunks.push(...chunks); } writeFileHeader() { let buf = Buffer.alloc(12); buf.write('RIFF', 0); buf.write('WEBP', 8); this.writeBytes(buf); } writeChunk_VP8(vp8) { this.writeBytes(...((createBasicChunk('VP8 ', vp8.raw)).chunks)); } writeChunk_VP8L(vp8l) { this.writeBytes(...((createBasicChunk('VP8L', vp8l.raw)).chunks)); } writeChunk_VP8X(vp8x) { let buf = Buffer.alloc(18); buf.write('VP8X', 0); buf.writeUInt32LE(10, 4); buf.writeUIntLE(vp8x.width - 1, 12, 3); buf.writeUIntLE(vp8x.height - 1, 15, 3); if (vp8x.hasICCP) { buf[8] |= 0b00100000; } if (vp8x.hasAlpha) { buf[8] |= 0b00010000; } if (vp8x.hasEXIF) { buf[8] |= 0b00001000; } if (vp8x.hasXMP) { buf[8] |= 0b00000100; } if (vp8x.hasAnim) { buf[8] |= 0b00000010; } this.vp8x = buf; this.writeBytes(buf); } updateChunk_VP8X_size(width, height) { this.vp8x.writeUIntLE(width, 12, 3); this.vp8x.writeUIntLE(height, 15, 3); } writeChunk_ANIM(anim) { let buf = Buffer.alloc(14); buf.write('ANIM', 0); buf.writeUInt32LE(6, 4); buf.writeUInt8(anim.bgColor[2], 8); buf.writeUInt8(anim.bgColor[1], 9); buf.writeUInt8(anim.bgColor[0], 10); buf.writeUInt8(anim.bgColor[3], 11); buf.writeUInt16LE(anim.loops, 12); this.writeBytes(buf); } writeChunk_ANMF(anmf) { let buf = Buffer.alloc(24), { img } = anmf, size = 16, alpha = false; buf.write('ANMF', 0); buf.writeUIntLE(anmf.x, 8, 3); buf.writeUIntLE(anmf.y, 11, 3); buf.writeUIntLE(anmf.delay, 20, 3); if (!anmf.blend) { buf[23] |= 0b00000010; } if (anmf.dispose) { buf[23] |= 0b00000001; } switch (img.type) { case constants.TYPE_LOSSY: { let b; this.width = Math.max(this.width, img.vp8.width); this.height = Math.max(this.height, img.vp8.height); buf.writeUIntLE(img.vp8.width - 1, 14, 3); buf.writeUIntLE(img.vp8.height - 1, 17, 3); this.writeBytes(buf); if (img.vp8.alpha) { b = createBasicChunk('ALPH', img.alph.raw); this.writeBytes(...b.chunks); size += b.size; } b = createBasicChunk('VP8 ', img.vp8.raw); this.writeBytes(...b.chunks); size += b.size; } break; case constants.TYPE_LOSSLESS: { let b = createBasicChunk('VP8L', img.vp8l.raw); this.width = Math.max(this.width, img.vp8l.width); this.height = Math.max(this.height, img.vp8l.height); buf.writeUIntLE(img.vp8l.width - 1, 14, 3); buf.writeUIntLE(img.vp8l.height - 1, 17, 3); if (img.vp8l.alpha) { alpha = true; } this.writeBytes(buf, ...b.chunks); size += b.size; } break; case constants.TYPE_EXTENDED: if (img.extended.hasAnim) { let fr = img.anim.frames; if (img.extended.hasAlpha) { alpha = true; } for (let i = 0, l = fr.length; i < l; i++) { let b = Buffer.alloc(8), c = fr[i].raw; this.width = Math.max(this.width, fr[i].width + anmf.x); this.height = Math.max(this.height, fr[i].height + anmf.y); b.write('ANMF', 0); b.writeUInt32LE(c.length, 4); c.writeUIntLE(anmf.x, 0, 3); c.writeUIntLE(anmf.y, 3, 3); c.writeUIntLE(anmf.delay, 12, 3); if (!anmf.blend) { c[15] |= 0b00000010; } else { c[15] &= 0b11111101; } if (anmf.dispose) { c[15] |= 0b00000001; } else { c[15] &= 0b11111110; } this.writeBytes(b, c); if (c.length & 1) { this.writeBytes(nullByte); } } } else { let b; this.width = Math.max(this.width, img.extended.width); this.height = Math.max(this.height, img.extended.height); if (img.vp8) { buf.writeUIntLE(img.vp8.width - 1, 14, 3); buf.writeUIntLE(img.vp8.height - 1, 17, 3); this.writeBytes(buf); if (img.alph) { b = createBasicChunk('ALPH', img.alph.raw); alpha = true; this.writeBytes(...b.chunks); size += b.size; } b = createBasicChunk('VP8 ', img.vp8.raw); this.writeBytes(...b.chunks); size += b.size; } else if (img.vp8l) { buf.writeUIntLE(img.vp8l.width - 1, 14, 3); buf.writeUIntLE(img.vp8l.height - 1, 17, 3); if (img.vp8l.alpha) { alpha = true; } b = createBasicChunk('VP8L', img.vp8l.raw); this.writeBytes(buf, ...b.chunks); size += b.size; } } break; default: throw new Error('Unknown image type'); } buf.writeUInt32LE(size, 4); if (alpha) { this.vp8x[8] |= 0b00010000; } } writeChunk_ALPH(alph) { this.writeBytes(...((createBasicChunk('ALPH', alph.raw)).chunks)); } writeChunk_ICCP(iccp) { this.writeBytes(...((createBasicChunk('ICCP', iccp.raw)).chunks)); } writeChunk_EXIF(exif) { this.writeBytes(...((createBasicChunk('EXIF', exif.raw)).chunks)); } writeChunk_XMP(xmp) { this.writeBytes(...((createBasicChunk('XMP ', xmp.raw)).chunks)); } } module.exports = { WebPReader, WebPWriter };