From 476789c5a219a7f75507cb23dc1f56689f7b32b7 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Wed, 16 Aug 2023 11:59:33 -0500 Subject: [PATCH] Parsing global color table --- public/gif/constants.js | 3 +++ public/gif/getNodeUi.js | 43 +++++++++++++++++++++++++++++++++++++---- public/gif/parseGif.js | 24 ++++++++++++++++++++++- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/public/gif/constants.js b/public/gif/constants.js index 6339a23..7475d96 100644 --- a/public/gif/constants.js +++ b/public/gif/constants.js @@ -14,6 +14,9 @@ export const GifNodeType = [ "logicalScreenDescriptorPackedFields", "logicalScreenBackgroundColorIndex", "logicalScreenPixelAspectRatio", + + "globalColorTable", + "globalColorTableColor", ].reduce((result, id) => { result[id] = id; return result; diff --git a/public/gif/getNodeUi.js b/public/gif/getNodeUi.js index c344b25..066a19e 100644 --- a/public/gif/getNodeUi.js +++ b/public/gif/getNodeUi.js @@ -23,6 +23,12 @@ const p = (...children) => crel("p", {}, ...children); */ const li = (...children) => crel("li", {}, ...children); +/** + * @param {number} byte + * @returns {string} + */ +const formatByte = (byte) => byte.toString(16).padStart(2, "0"); + /** * @param {Uint8Array} bytes * @returns {number} @@ -77,9 +83,9 @@ const NODE_UI_FNS = { // Logical screen descriptor [GifNodeType.logicalScreenDescriptor]: () => ({ - title: "Logical Screen Descriptor", + title: "Logical Screen Descriptor block", description: p( - "The logical screen descriptor contains information about the overall GIF, such as its dimensions.", + "The Logical Screen Descriptor contains information about the overall GIF, such as its dimensions.", ), }), [GifNodeType.logicalScreenWidth]: ({ bytes }) => ({ @@ -134,8 +140,8 @@ const NODE_UI_FNS = { li( `The last three bits (${ (byte & 0b111).toString(2).padStart(3, "0") - }) encode the size of the global color table. To decode this field, you add 1 and then raise that to the power of 2, so the size of the global color table is ${ - pluralize(packedField.sizeOfGlobalColorTable, "byte") + }) encode the number of colors of the global color table. To decode this field, you add 1 and then raise that to the power of 2, so the size of the global color table is ${ + pluralize(packedField.sizeOfGlobalColorTable, "color") }. `, ), ), @@ -150,6 +156,35 @@ const NODE_UI_FNS = { title: "Pixel Aspect Ratio", description: p("TODO"), }), + + // Global color table + + [GifNodeType.globalColorTable]: ({ children }) => ({ + title: "Global Color Table", + description: p( + `The Global Color Table is a list of colors. Each color is encoded as three bytes: one for red, one for green, and one for blue. The number of colors in the table is ${ + pluralize(children?.length || 0, "color") + }.`, + ), + }), + [GifNodeType.globalColorTableColor]: ({ bytes }) => ({ + title: "Global Color Table color", + description: p( + "A color. It looks like this: ", + crel( + "span", + { + "style": + `display:inline-block;width:1em;height:1em;vertical-align:middle;color:transparent;background:rgb(${ + bytes.join(",") + })`, + }, + `#${formatByte(bytes[0])}${formatByte(bytes[1])}${ + formatByte(bytes[2]) + }`, + ), + ), + }), }; /** diff --git a/public/gif/parseGif.js b/public/gif/parseGif.js index 5ab7dda..d197bf7 100644 --- a/public/gif/parseGif.js +++ b/public/gif/parseGif.js @@ -30,7 +30,6 @@ export default (bytes) => { 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]; /** @type {GifNode[]} */ const children = [ @@ -76,5 +75,28 @@ export default (bytes) => { }, ]; + let offset = 13; + + const packedField = parseLogicalScreenDescriptorPackedField( + packedFieldBytes[0], + ); + if (packedField.globalColorTableFlag) { + /** @type {GifNode[]} */ const colorNodes = []; + for (let i = 0; i < packedField.sizeOfGlobalColorTable; i++) { + colorNodes.push({ + type: GifNodeType.globalColorTableColor, + bytes: bytes.subarray(13 + (i * 3), 13 + (i * 3) + 3), + }); + } + const sizeOfGlobalColorTableInBytes = packedField.sizeOfGlobalColorTable * + 3; + children.push({ + type: GifNodeType.globalColorTable, + bytes: bytes.subarray(13, 13 + sizeOfGlobalColorTableInBytes), + children: colorNodes, + }); + offset += sizeOfGlobalColorTableInBytes; + } + return { type: GifNodeType.root, bytes, children }; };