UNPKG

@remotion/gif

Version:

Embed GIFs in a Remotion video

159 lines (158 loc) • 6.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GIF = void 0; const parser_1 = require("./parser"); const uint8_parser_1 = require("./uint8-parser"); // a set of 0x00 terminated subblocks const subBlocksSchema = { blocks: (stream) => { const terminator = 0x00; const chunks = []; const streamSize = stream.data.length; let total = 0; for (let size = (0, uint8_parser_1.readByte)()(stream); size !== terminator; size = (0, uint8_parser_1.readByte)()(stream)) { // size becomes undefined for some case when file is corrupted and terminator is not proper // null check to avoid recursion if (!size) break; // catch corrupted files with no terminator if (stream.pos + size >= streamSize) { const availableSize = streamSize - stream.pos; chunks.push((0, uint8_parser_1.readBytes)(availableSize)(stream)); total += availableSize; break; } chunks.push((0, uint8_parser_1.readBytes)(size)(stream)); total += size; } const result = new Uint8Array(total); let offset = 0; for (let i = 0; i < chunks.length; i++) { result.set(chunks[i], offset); offset += chunks[i].length; } return result; }, }; // global control extension const gceSchema = (0, parser_1.conditional)({ gce: [ { codes: (0, uint8_parser_1.readBytes)(2) }, { byteSize: (0, uint8_parser_1.readByte)() }, { extras: (0, uint8_parser_1.readBits)({ future: { index: 0, length: 3 }, disposal: { index: 3, length: 3 }, userInput: { index: 6 }, transparentColorGiven: { index: 7 }, }), }, { delay: (0, uint8_parser_1.readUnsigned)(true) }, { transparentColorIndex: (0, uint8_parser_1.readByte)() }, { terminator: (0, uint8_parser_1.readByte)() }, ], }, (stream) => { const codes = (0, uint8_parser_1.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xf9; }); // image pipeline block const imageSchema = (0, parser_1.conditional)({ image: [ { code: (0, uint8_parser_1.readByte)() }, { descriptor: [ { left: (0, uint8_parser_1.readUnsigned)(true) }, { top: (0, uint8_parser_1.readUnsigned)(true) }, { width: (0, uint8_parser_1.readUnsigned)(true) }, { height: (0, uint8_parser_1.readUnsigned)(true) }, { lct: (0, uint8_parser_1.readBits)({ exists: { index: 0 }, interlaced: { index: 1 }, sort: { index: 2 }, future: { index: 3, length: 2 }, size: { index: 5, length: 3 }, }), }, ], }, (0, parser_1.conditional)({ lct: (0, uint8_parser_1.readArray)(3, (_stream, _result, parent) => { return 2 ** (parent.descriptor.lct.size + 1); }), }, (_stream, _result, parent) => { return parent.descriptor.lct.exists; }), { data: [{ minCodeSize: (0, uint8_parser_1.readByte)() }, subBlocksSchema] }, ], }, (stream) => { return (0, uint8_parser_1.peekByte)()(stream) === 0x2c; }); // plain text block const textSchema = (0, parser_1.conditional)({ text: [ { codes: (0, uint8_parser_1.readBytes)(2) }, { blockSize: (0, uint8_parser_1.readByte)() }, { preData: (stream, _result, parent) => (0, uint8_parser_1.readBytes)(parent.text.blockSize)(stream), }, subBlocksSchema, ], }, (stream) => { const codes = (0, uint8_parser_1.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0x01; }); // application block const applicationSchema = (0, parser_1.conditional)({ application: [ { codes: (0, uint8_parser_1.readBytes)(2) }, { blockSize: (0, uint8_parser_1.readByte)() }, { id: (stream, _result, parent) => (0, uint8_parser_1.readString)(parent.blockSize)(stream), }, subBlocksSchema, ], }, (stream) => { const codes = (0, uint8_parser_1.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xff; }); // comment block const commentSchema = (0, parser_1.conditional)({ comment: [{ codes: (0, uint8_parser_1.readBytes)(2) }, subBlocksSchema], }, (stream) => { const codes = (0, uint8_parser_1.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xfe; }); exports.GIF = [ { header: [{ signature: (0, uint8_parser_1.readString)(3) }, { version: (0, uint8_parser_1.readString)(3) }] }, { lsd: [ { width: (0, uint8_parser_1.readUnsigned)(true) }, { height: (0, uint8_parser_1.readUnsigned)(true) }, { gct: (0, uint8_parser_1.readBits)({ exists: { index: 0 }, resolution: { index: 1, length: 3 }, sort: { index: 4 }, size: { index: 5, length: 3 }, }), }, { backgroundColorIndex: (0, uint8_parser_1.readByte)() }, { pixelAspectRatio: (0, uint8_parser_1.readByte)() }, ], }, (0, parser_1.conditional)({ gct: (0, uint8_parser_1.readArray)(3, (_stream, result) => 2 ** (result.lsd.gct.size + 1)), }, (_stream, result) => result.lsd.gct.exists), // content frames { frames: (0, parser_1.loop)([gceSchema, applicationSchema, commentSchema, imageSchema, textSchema], (stream) => { const nextCode = (0, uint8_parser_1.peekByte)()(stream); // rather than check for a terminator, we should check for the existence // of an ext or image block to avoid infinite loops // var terminator = 0x3B; // return nextCode !== terminator; return nextCode === 0x21 || nextCode === 0x2c; }), }, ];