2023-08-12 03:40:30 +00:00
|
|
|
// @ts-check
|
|
|
|
|
|
|
|
import { GifNodeType } from "./constants.js";
|
|
|
|
/** @typedef {import("../../types/gif.d.ts").GifNode} GifNode */
|
|
|
|
|
2023-08-16 16:45:11 +00:00
|
|
|
/**
|
|
|
|
* @typedef {object} LogicalScreenDescriptorPackedField
|
|
|
|
* @prop {boolean} globalColorTableFlag
|
|
|
|
* @prop {number} colorResolution
|
|
|
|
* @prop {boolean} sortFlag
|
|
|
|
* @prop {number} sizeOfGlobalColorTable
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} byte
|
|
|
|
* @returns {LogicalScreenDescriptorPackedField}
|
|
|
|
*/
|
|
|
|
export const parseLogicalScreenDescriptorPackedField = (byte) => ({
|
|
|
|
globalColorTableFlag: (byte & 0b10000000) !== 0,
|
|
|
|
colorResolution: ((byte & 0b01110000) >> 4) + 1,
|
|
|
|
sortFlag: (byte & 0b00001000) !== 0,
|
|
|
|
sizeOfGlobalColorTable: 2 ** ((byte & 0b00000111) + 1),
|
|
|
|
});
|
|
|
|
|
2023-08-12 03:40:30 +00:00
|
|
|
/**
|
|
|
|
* @param {Uint8Array} bytes
|
2023-08-16 15:34:09 +00:00
|
|
|
* @returns {null | GifNode} The root node of the GIF tree, or null if the GIF is invalid.
|
2023-08-12 03:40:30 +00:00
|
|
|
*/
|
|
|
|
export default (bytes) => {
|
2023-08-16 16:45:11 +00:00
|
|
|
const widthBytes = bytes.subarray(6, 6 + 2);
|
|
|
|
const heightBytes = bytes.subarray(8, 8 + 2);
|
|
|
|
const packedFieldBytes = bytes.subarray(10, 10 + 1);
|
|
|
|
const packedFieldByte = packedFieldBytes[0];
|
|
|
|
|
2023-08-12 03:40:30 +00:00
|
|
|
/** @type {GifNode[]} */
|
|
|
|
const children = [
|
|
|
|
{
|
|
|
|
type: GifNodeType.header,
|
|
|
|
bytes: bytes.subarray(0, 6),
|
|
|
|
children: [
|
|
|
|
{
|
|
|
|
type: GifNodeType.headerSignature,
|
|
|
|
bytes: bytes.subarray(0, 3),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.headerVersion,
|
2023-08-16 15:46:31 +00:00
|
|
|
bytes: bytes.subarray(3, 3 + 3),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenDescriptor,
|
|
|
|
bytes: bytes.subarray(6, 6 + 6),
|
|
|
|
children: [
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenWidth,
|
2023-08-16 16:45:11 +00:00
|
|
|
bytes: widthBytes,
|
2023-08-16 15:46:31 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenHeight,
|
2023-08-16 16:45:11 +00:00
|
|
|
bytes: heightBytes,
|
2023-08-16 15:46:31 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenDescriptorPackedFields,
|
2023-08-16 16:45:11 +00:00
|
|
|
bytes: packedFieldBytes,
|
2023-08-16 15:46:31 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenBackgroundColorIndex,
|
|
|
|
bytes: bytes.subarray(11, 11 + 1),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: GifNodeType.logicalScreenPixelAspectRatio,
|
|
|
|
bytes: bytes.subarray(12, 12 + 1),
|
2023-08-12 03:40:30 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
return { type: GifNodeType.root, bytes, children };
|
|
|
|
};
|