Replace png encode dependency with optimized CRC32 calculation

ba397fa70a2f0d7cb7533f410172b19f8b513a34

Cohee <18619528+Cohee1207@users.noreply.github.com>

4 files changed, +88 -28Showing whitespace changes
package-lock.json+18 -25
@@ -48,6 +48,7 @@
4848 "cookie-parser": "^1.4.6",
4949 "cookie-session": "^2.1.0",
5050 "cors": "^2.8.5",
51+ "crc": "^4.3.2",
5152 "csrf-sync": "^4.0.3",
5253 "diff-match-patch": "^1.0.5",
5354 "dompurify": "^3.2.4",
@@ -76,7 +77,6 @@
7677 "node-persist": "^4.0.4",
7778 "open": "^8.4.2",
7879 "png-chunk-text": "^1.0.0",
79- "png-chunks-encode": "^1.0.0",
8080 "png-chunks-extract": "^1.0.0",
8181 "proxy-agent": "^6.5.0",
8282 "rate-limiter-flexible": "^5.0.5",
@@ -121,7 +121,6 @@
121121 "@types/node": "^18.19.80",
122122 "@types/node-persist": "^3.1.8",
123123 "@types/png-chunk-text": "^1.0.3",
124- "@types/png-chunks-encode": "^1.0.2",
125124 "@types/png-chunks-extract": "^1.0.2",
126125 "@types/response-time": "^2.3.8",
127126 "@types/select2": "^4.0.63",
@@ -2033,13 +2032,6 @@
20332032 "dev": true,
20342033 "license": "MIT"
20352034 },
2036- "node_modules/@types/png-chunks-encode": {
2037- "version": "1.0.2",
2038- "resolved": "https://registry.npmjs.org/@types/png-chunks-encode/-/png-chunks-encode-1.0.2.tgz",
2039- "integrity": "sha512-Dxn0aXEcSg1wVeHjvNlygm/+fKBDzWMCdxJYhjGUTeefFW/jYxWcrg+W7ppLBfH44iJMqeVBHtHBwtYQUeYvgw==",
2040- "dev": true,
2041- "license": "MIT"
2042- },
20432035 "node_modules/@types/png-chunks-extract": {
20442036 "version": "1.0.2",
20452037 "resolved": "https://registry.npmjs.org/@types/png-chunks-extract/-/png-chunks-extract-1.0.2.tgz",
@@ -3542,6 +3534,23 @@
35423534 "node": ">= 0.10"
35433535 }
35443536 },
3537+ "node_modules/crc": {
3538+ "version": "4.3.2",
3539+ "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz",
3540+ "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==",
3541+ "license": "MIT",
3542+ "engines": {
3543+ "node": ">=12"
3544+ },
3545+ "peerDependencies": {
3546+ "buffer": ">=6.0.3"
3547+ },
3548+ "peerDependenciesMeta": {
3549+ "buffer": {
3550+ "optional": true
3551+ }
3552+ }
3553+ },
35453554 "node_modules/crc-32": {
35463555 "version": "0.3.0",
35473556 "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.3.0.tgz",
@@ -6686,16 +6695,6 @@
66866695 "integrity": "sha512-DEROKU3SkkLGWNMzru3xPVgxyd48UGuMSZvioErCure6yhOc/pRH2ZV+SEn7nmaf7WNf3NdIpH+UTrRdKyq9Lw==",
66876696 "license": "MIT"
66886697 },
6689- "node_modules/png-chunks-encode": {
6690- "version": "1.0.0",
6691- "resolved": "https://registry.npmjs.org/png-chunks-encode/-/png-chunks-encode-1.0.0.tgz",
6692- "integrity": "sha512-J1jcHgbQRsIIgx5wxW9UmCymV3wwn4qCCJl6KYgEU/yHCh/L2Mwq/nMOkRPtmV79TLxRZj5w3tH69pvygFkDqA==",
6693- "license": "MIT",
6694- "dependencies": {
6695- "crc-32": "^0.3.0",
6696- "sliced": "^1.0.1"
6697- }
6698- },
66996698 "node_modules/png-chunks-extract": {
67006699 "version": "1.0.0",
67016700 "resolved": "https://registry.npmjs.org/png-chunks-extract/-/png-chunks-extract-1.0.0.tgz",
@@ -7532,12 +7531,6 @@
75327531 "dev": true,
75337532 "license": "ISC"
75347533 },
7535- "node_modules/sliced": {
7536- "version": "1.0.1",
7537- "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
7538- "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
7539- "license": "MIT"
7540- },
75417534 "node_modules/slidetoggle": {
75427535 "version": "4.0.0",
75437536 "resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz",
package.json+1 -2
@@ -38,6 +38,7 @@
3838 "cookie-parser": "^1.4.6",
3939 "cookie-session": "^2.1.0",
4040 "cors": "^2.8.5",
41+ "crc": "^4.3.2",
4142 "csrf-sync": "^4.0.3",
4243 "diff-match-patch": "^1.0.5",
4344 "dompurify": "^3.2.4",
@@ -66,7 +67,6 @@
6667 "node-persist": "^4.0.4",
6768 "open": "^8.4.2",
6869 "png-chunk-text": "^1.0.0",
69- "png-chunks-encode": "^1.0.0",
7070 "png-chunks-extract": "^1.0.0",
7171 "proxy-agent": "^6.5.0",
7272 "rate-limiter-flexible": "^5.0.5",
@@ -151,7 +151,6 @@
151151 "@types/node": "^18.19.80",
152152 "@types/node-persist": "^3.1.8",
153153 "@types/png-chunk-text": "^1.0.3",
154- "@types/png-chunks-encode": "^1.0.2",
155154 "@types/png-chunks-extract": "^1.0.2",
156155 "@types/response-time": "^2.3.8",
157156 "@types/select2": "^4.0.63",
src/character-card-parser.js+1 -1
@@ -1,7 +1,7 @@
11import fs from 'node:fs';
22import { Buffer } from 'node:buffer';
33
44import encode from './png-chunks-/encode.js';
55import extract from 'png-chunks-extract';
66import PNGtext from 'png-chunk-text';
77
src/png/encode.js+68 -0
@@ -0,0 +1,68 @@
1+import { crc32 } from 'crc';
2+
3+/**
4+ * Encodes PNG chunks into a PNG file format buffer.
5+ * @param {Array<{ name: string; data: Uint8Array }>} chunks Array of PNG chunks
6+ * @returns {Uint8Array} Encoded PNG data
7+ * @copyright Based on https://github.com/hughsk/png-chunks-encode (MIT)
8+ */
9+export default function encode(chunks) {
10+ const uint8 = new Uint8Array(4);
11+ const int32 = new Int32Array(uint8.buffer);
12+ const uint32 = new Uint32Array(uint8.buffer);
13+
14+ let totalSize = 8;
15+ let idx = totalSize;
16+
17+ for (let i = 0; i < chunks.length; i++) {
18+ totalSize += chunks[i].data.length;
19+ totalSize += 12;
20+ }
21+
22+ const output = new Uint8Array(totalSize);
23+
24+ output[0] = 0x89;
25+ output[1] = 0x50;
26+ output[2] = 0x4E;
27+ output[3] = 0x47;
28+ output[4] = 0x0D;
29+ output[5] = 0x0A;
30+ output[6] = 0x1A;
31+ output[7] = 0x0A;
32+
33+ for (let i = 0; i < chunks.length; i++) {
34+ const { name, data } = chunks[i];
35+ const size = data.length;
36+ const nameChars = [
37+ name.charCodeAt(0),
38+ name.charCodeAt(1),
39+ name.charCodeAt(2),
40+ name.charCodeAt(3),
41+ ];
42+
43+ uint32[0] = size;
44+ output[idx++] = uint8[3];
45+ output[idx++] = uint8[2];
46+ output[idx++] = uint8[1];
47+ output[idx++] = uint8[0];
48+
49+ output[idx++] = nameChars[0];
50+ output[idx++] = nameChars[1];
51+ output[idx++] = nameChars[2];
52+ output[idx++] = nameChars[3];
53+
54+ for (let j = 0; j < size;) {
55+ output[idx++] = data[j++];
56+ }
57+
58+ const crc = crc32(data, crc32(new Uint8Array(nameChars)));
59+
60+ int32[0] = crc;
61+ output[idx++] = uint8[3];
62+ output[idx++] = uint8[2];
63+ output[idx++] = uint8[1];
64+ output[idx++] = uint8[0];
65+ }
66+
67+ return output;
68+}