@nkzw/palette-swap 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ lib
2
+ node_modules
package/.npmignore ADDED
@@ -0,0 +1,6 @@
1
+ example
2
+ lib/tsconfig.tsbuildinfo
3
+ pnpm-lock.yaml
4
+ src
5
+ tsconfig.json
6
+ tsdown.config.ts
package/README.md CHANGED
@@ -9,7 +9,15 @@ Image palette swapping and color replacement tool based on canvas for Node.js an
9
9
  ## Installation
10
10
 
11
11
  ```
12
- npm install @nkzw/palette-swap canvas
12
+ npm install @nkzw/palette-swap
13
+ ```
14
+
15
+ The package selects the browser or Node.js entry point automatically. Browser bundles use native `HTMLCanvasElement` and `ImageData` and do not include the Node backend. Node.js uses `@napi-rs/canvas`, whose platform binaries are installed as optional dependencies; keep optional dependencies enabled.
16
+
17
+ For Node.js applications that also load images or create canvases, add the backend as a direct dependency:
18
+
19
+ ```sh
20
+ npm install @napi-rs/canvas
13
21
  ```
14
22
 
15
23
  ## Usage
@@ -19,9 +27,9 @@ npm install @nkzw/palette-swap canvas
19
27
  Here is an example of how to use `paletteSwap()` to create two variants from a base image:
20
28
 
21
29
  ```js
22
- import { loadImage } from 'canvas';
30
+ import { loadImage } from '@napi-rs/canvas';
23
31
  import { writeFileSync } from 'node:fs';
24
- import paletteSwap from 'paletteSwap';
32
+ import paletteSwap from '@nkzw/palette-swap';
25
33
 
26
34
  const results = paletteSwap(
27
35
  await loadImage('./Yoshi.png'),
@@ -36,23 +44,37 @@ const results = paletteSwap(
36
44
  ],
37
45
  [3, 220],
38
46
  ]),
39
- new Set([
40
- '#000000',
41
- '#903020',
42
- '#f84020',
43
- '#f89000',
44
- '#ff0000',
45
- '#f8c0a8',
46
- '#f8e0d0',
47
- '#f8f8f8',
48
- ]),
47
+ new Set(['#000000', '#903020', '#f84020', '#f89000', '#ff0000', '#f8c0a8', '#f8e0d0', '#f8f8f8']),
49
48
  );
50
49
 
51
50
  for (const [variant, canvas] of results) {
52
- writeFileSync(`./Yoshi-${variant}.png`, canvas.toBuffer());
51
+ writeFileSync(`./Yoshi-${variant}.png`, canvas.toBuffer('image/png'));
52
+ }
53
+ ```
54
+
55
+ ### Browser
56
+
57
+ Use the same import and palette definitions with a loaded browser image:
58
+
59
+ ```js
60
+ import paletteSwap from '@nkzw/palette-swap';
61
+
62
+ const image = new Image();
63
+ image.src = './Yoshi.png';
64
+ await image.decode();
65
+
66
+ const variants = new Map([[2, new Map([['#006000', '#570061']])]]);
67
+ for (const canvas of paletteSwap(image, variants).values()) {
68
+ document.body.append(canvas);
53
69
  }
54
70
  ```
55
71
 
72
+ Modern bundlers select the `browser` export automatically. The browser fallback also supports bundlers using the `browser` field. Each entry point has matching TypeScript declarations. With TypeScript's `bundler` module resolution, set `customConditions: ["browser"]` to select the browser declarations explicitly.
73
+
74
+ ### Migrating from node-canvas
75
+
76
+ Browser callers keep the same API. Node.js callers should create and load images using `@napi-rs/canvas` instead of `canvas`, and use `canvas.toBuffer('image/png')` to encode results. Returned Node canvases are `@napi-rs/canvas` objects; native objects from the old `canvas` package are not interchangeable.
77
+
56
78
  ## Parameters
57
79
 
58
80
  ### `image: Image`
@@ -105,3 +127,13 @@ const results = paletteSwap(
105
127
  ### `options?: { imageName?: string; ignoreMissing?: boolean; }`
106
128
 
107
129
  By default `paletteSwap` will throw an error for any missing color mapping. Options can be specified to ignore missing color mappings or to provide an image name used for the error message when a color mapping is missing.
130
+
131
+ ## Development
132
+
133
+ ```sh
134
+ pnpm install
135
+ pnpm exec playwright install chromium
136
+ pnpm test
137
+ ```
138
+
139
+ Tests build the published entry points, verify JavaScript and TypeScript export resolution, reject native dependencies in browser bundles, and compare browser and Node pixels, including transparent edges. The existing example pixel hashes remain regression checks.
@@ -0,0 +1,9 @@
1
+ import { n as HSL, r as VariantName, t as HEX } from "./palette-swap-BQyCssvL.js";
2
+ //#region src/browser.d.ts
3
+ export type Image = HTMLImageElement | HTMLCanvasElement | ImageBitmap | OffscreenCanvas;
4
+ declare const _default: (image: Image, inputVariants: ReadonlyMap<VariantName, ReadonlyMap<HEX, HEX> | number>, staticColors?: Set<HEX> | null, images?: ReadonlyMap<VariantName, Image> | null | undefined, options?: {
5
+ imageName?: string;
6
+ ignoreMissing?: boolean;
7
+ } | undefined) => ReadonlyMap<VariantName, HTMLCanvasElement>;
8
+ //#endregion
9
+ export { type HEX, type HSL, type VariantName, _default as default };
package/lib/browser.js ADDED
@@ -0,0 +1,24 @@
1
+ import { t as createPaletteSwap } from "./palette-swap-D-ZznABu.js";
2
+ //#region src/browser.ts
3
+ var browser_default = createPaletteSwap({
4
+ readImage(image, width = image.width, height = image.height) {
5
+ const canvas = document.createElement("canvas");
6
+ canvas.width = image.width;
7
+ canvas.height = image.height;
8
+ const context = canvas.getContext("2d");
9
+ if (!context) throw new Error("paletteSwap: Could not create a canvas context.");
10
+ context.drawImage(image, 0, 0);
11
+ return context.getImageData(0, 0, width, height);
12
+ },
13
+ writeImage({ data, height, width }) {
14
+ const canvas = document.createElement("canvas");
15
+ canvas.width = width;
16
+ canvas.height = height;
17
+ const context = canvas.getContext("2d");
18
+ if (!context) throw new Error("paletteSwap: Could not create a canvas context.");
19
+ context.putImageData(new ImageData(data, width, height), 0, 0);
20
+ return canvas;
21
+ }
22
+ });
23
+ //#endregion
24
+ export { browser_default as default };
package/lib/node.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { n as HSL, r as VariantName, t as HEX } from "./palette-swap-BQyCssvL.js";
2
+ import { Canvas, Image as Image$1 } from "@napi-rs/canvas";
3
+ //#region src/node.d.ts
4
+ export type Image = Image$1 | Canvas;
5
+ declare const _default: (image: Image, inputVariants: ReadonlyMap<VariantName, ReadonlyMap<HEX, HEX> | number>, staticColors?: Set<HEX> | null, images?: ReadonlyMap<VariantName, Image> | null | undefined, options?: {
6
+ imageName?: string;
7
+ ignoreMissing?: boolean;
8
+ } | undefined) => ReadonlyMap<VariantName, Canvas>;
9
+ //#endregion
10
+ export { type HEX, type HSL, type VariantName, _default as default };
package/lib/node.js ADDED
@@ -0,0 +1,17 @@
1
+ import { t as createPaletteSwap } from "./palette-swap-D-ZznABu.js";
2
+ import { ImageData, createCanvas } from "@napi-rs/canvas";
3
+ //#region src/node.ts
4
+ var node_default = createPaletteSwap({
5
+ readImage(image, width = image.width, height = image.height) {
6
+ const context = createCanvas(image.width, image.height).getContext("2d");
7
+ context.drawImage(image, 0, 0);
8
+ return context.getImageData(0, 0, width, height);
9
+ },
10
+ writeImage({ data, height, width }) {
11
+ const canvas = createCanvas(width, height);
12
+ canvas.getContext("2d").putImageData(new ImageData(data, width, height), 0, 0);
13
+ return canvas;
14
+ }
15
+ });
16
+ //#endregion
17
+ export { node_default as default };
@@ -0,0 +1,6 @@
1
+ //#region src/palette-swap.d.ts
2
+ type HEX = `#${string}`;
3
+ type HSL = [hue: number, saturation: number, lightness: number];
4
+ type VariantName = number | string;
5
+ //#endregion
6
+ export { HSL as n, VariantName as r, HEX as t };
@@ -0,0 +1,199 @@
1
+ //#region src/palette-swap.ts
2
+ const hexToUint32LE = (hex) => {
3
+ const n = Number.parseInt(hex.slice(1), 16) & 16777215;
4
+ const [r, g, b] = int32ToRgb(n);
5
+ return b << 16 | g << 8 | r;
6
+ };
7
+ const int32ToRgb = (color) => {
8
+ return [
9
+ color >> 16 & 255,
10
+ color >> 8 & 255,
11
+ color & 255
12
+ ];
13
+ };
14
+ const hex = (value) => Math.round(value).toString(16).padStart(2, "0");
15
+ const rgbToHex = ([r, g, b]) => `#${hex(r)}${hex(g)}${hex(b)}`;
16
+ const int32ToHex = (color) => {
17
+ const [r, g, b] = int32ToRgb(color);
18
+ return rgbToHex([
19
+ b,
20
+ g,
21
+ r
22
+ ]);
23
+ };
24
+ const applyHue = (color, hue) => {
25
+ const h = hue / 360;
26
+ const r = (color & 255) / 255;
27
+ const g = (color >> 8 & 255) / 255;
28
+ const b = (color >> 16 & 255) / 255;
29
+ const min = Math.min(r, g, b);
30
+ const max = Math.max(r, g, b);
31
+ const delta = max - min;
32
+ let s;
33
+ const l = (min + max) / 2;
34
+ if (max === min) s = 0;
35
+ else if (l <= .5) s = delta / (max + min);
36
+ else s = delta / (2 - max - min);
37
+ let t2;
38
+ let t3;
39
+ let val;
40
+ if (s === 0) {
41
+ val = l * 255;
42
+ return val << 16 | val << 8 | val;
43
+ }
44
+ if (l < .5) t2 = l * (1 + s);
45
+ else t2 = l + s - l * s;
46
+ const t1 = 2 * l - t2;
47
+ let result0 = 0;
48
+ let result1 = 0;
49
+ let result2 = 0;
50
+ for (let i = 0; i < 3; i++) {
51
+ t3 = h + 1 / 3 * -(i - 1);
52
+ if (t3 < 0) t3++;
53
+ if (t3 > 1) t3--;
54
+ if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3;
55
+ else if (2 * t3 < 1) val = t2;
56
+ else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
57
+ else val = t1;
58
+ if (i === 0) result0 = val * 255;
59
+ else if (i === 1) result1 = val * 255;
60
+ else result2 = val * 255;
61
+ }
62
+ return result2 << 16 | result1 << 8 | result0;
63
+ };
64
+ const equals = (a, b) => {
65
+ if (a.width !== b.width || a.height !== b.height) return false;
66
+ for (let i = 0; i < a.data.length; i++) if (a.data[i] !== b.data[i]) return false;
67
+ return true;
68
+ };
69
+ const colorCacheSlot = (keys, key, mask) => {
70
+ let slot = Math.imul(key, 2654435761) & mask;
71
+ let cachedKey = keys[slot];
72
+ while (cachedKey !== 0 && cachedKey !== key) {
73
+ slot = slot + 1 & mask;
74
+ cachedKey = keys[slot];
75
+ }
76
+ return slot;
77
+ };
78
+ const maxCachedColors = 4096;
79
+ function createPaletteSwap({ readImage, writeImage }) {
80
+ return function paletteSwap(image, inputVariants, staticColors, images, options) {
81
+ const pixels = readImage(image);
82
+ const { height, width } = pixels;
83
+ const size = width * height;
84
+ const imageData = new Uint32Array(pixels.data.buffer, pixels.data.byteOffset, size);
85
+ const results = /* @__PURE__ */ new Map();
86
+ const variantNames = [];
87
+ const variantImageData = [];
88
+ const variantBuffers = [];
89
+ const variantHues = [];
90
+ let i = 0;
91
+ const palettes = /* @__PURE__ */ new Map();
92
+ for (const [variant, palette] of inputVariants) {
93
+ const newImageData = {
94
+ data: new Uint8ClampedArray(size * 4),
95
+ height,
96
+ width
97
+ };
98
+ if (typeof palette !== "number") for (const [key, value] of palette) {
99
+ const k = hexToUint32LE(key);
100
+ const v = palettes.get(k) || [];
101
+ v[i] = hexToUint32LE(value);
102
+ palettes.set(k, v);
103
+ }
104
+ variantNames.push(variant);
105
+ variantImageData.push(newImageData);
106
+ variantBuffers.push(new Uint32Array(newImageData.data.buffer));
107
+ variantHues.push(typeof palette === "number" ? palette : null);
108
+ i++;
109
+ }
110
+ const variantCount = variantNames.length;
111
+ const staticColorNumbers = staticColors ? new Set([...staticColors].map(hexToUint32LE)) : null;
112
+ const trackMissing = !options?.ignoreMissing;
113
+ const missing = /* @__PURE__ */ new Set();
114
+ const colorCacheKeys = new Uint32Array(maxCachedColors * 2);
115
+ const colorCacheValues = new Uint32Array(colorCacheKeys.length * variantCount);
116
+ const colorCacheMask = colorCacheKeys.length - 1;
117
+ let colorCacheSize = 0;
118
+ const uncachedColors = new Uint32Array(variantCount);
119
+ const buffer0 = variantBuffers[0];
120
+ const buffer1 = variantBuffers[1];
121
+ const buffer2 = variantBuffers[2];
122
+ const buffer3 = variantBuffers[3];
123
+ const buffer4 = variantBuffers[4];
124
+ const buffer5 = variantBuffers[5];
125
+ const buffer6 = variantBuffers[6];
126
+ const buffer7 = variantBuffers[7];
127
+ for (let index = 0; index < size; index++) {
128
+ const originalColor = imageData[index];
129
+ const a = originalColor >> 24 & 255;
130
+ if (a === 0) continue;
131
+ const key = originalColor >>> 0;
132
+ const slot = colorCacheSlot(colorCacheKeys, key, colorCacheMask);
133
+ const cachedKey = colorCacheKeys[slot];
134
+ const canCache = cachedKey === key || colorCacheSize < maxCachedColors;
135
+ const colors = canCache ? colorCacheValues : uncachedColors;
136
+ const colorOffset = canCache ? slot * variantCount : 0;
137
+ if (cachedKey !== key) {
138
+ if (canCache) {
139
+ colorCacheKeys[slot] = key;
140
+ colorCacheSize++;
141
+ }
142
+ const color = originalColor & 16777215;
143
+ const isStatic = staticColorNumbers?.has(color);
144
+ const paletteColors = !isStatic ? palettes.get(color) : null;
145
+ for (let i = 0; i < variantCount; i++) {
146
+ if (isStatic) {
147
+ colors[colorOffset + i] = originalColor;
148
+ continue;
149
+ }
150
+ const hue = variantHues[i];
151
+ if (hue) colors[colorOffset + i] = a << 24 | applyHue(color, hue);
152
+ else if (paletteColors?.[i]) colors[colorOffset + i] = a << 24 | paletteColors[i];
153
+ else if (!staticColorNumbers) colors[colorOffset + i] = originalColor;
154
+ else {
155
+ colors[colorOffset + i] = 0;
156
+ if (trackMissing) missing.add([variantNames[i], color]);
157
+ }
158
+ }
159
+ }
160
+ if (variantCount === 8) {
161
+ const color0 = colors[colorOffset];
162
+ const color1 = colors[colorOffset + 1];
163
+ const color2 = colors[colorOffset + 2];
164
+ const color3 = colors[colorOffset + 3];
165
+ const color4 = colors[colorOffset + 4];
166
+ const color5 = colors[colorOffset + 5];
167
+ const color6 = colors[colorOffset + 6];
168
+ const color7 = colors[colorOffset + 7];
169
+ if (color0) buffer0[index] = color0;
170
+ if (color1) buffer1[index] = color1;
171
+ if (color2) buffer2[index] = color2;
172
+ if (color3) buffer3[index] = color3;
173
+ if (color4) buffer4[index] = color4;
174
+ if (color5) buffer5[index] = color5;
175
+ if (color6) buffer6[index] = color6;
176
+ if (color7) buffer7[index] = color7;
177
+ } else if (variantCount === 2) {
178
+ const color0 = colors[colorOffset];
179
+ const color1 = colors[colorOffset + 1];
180
+ if (color0) buffer0[index] = color0;
181
+ if (color1) buffer1[index] = color1;
182
+ } else for (let i = 0; i < variantCount; i++) {
183
+ const color = colors[colorOffset + i];
184
+ if (color) variantBuffers[i][index] = color;
185
+ }
186
+ }
187
+ if (trackMissing && missing.size) throw new Error(`${options?.imageName ? `${options.imageName}` : "Palette Swap"}: Missing ${Array.from(new Set([...missing].map(([variant, color]) => `'${int32ToHex(color)}' in '${variant}'`))).join(", ")}.`);
188
+ for (let i = 0; i < variantCount; i++) {
189
+ const name = variantNames[i];
190
+ const existingImage = images?.get(name);
191
+ const existingImageData = existingImage ? readImage(existingImage, width, height) : null;
192
+ const newImageData = variantImageData[i];
193
+ if (!existingImageData || !equals(newImageData, existingImageData)) results.set(name, writeImage(newImageData));
194
+ }
195
+ return results;
196
+ };
197
+ }
198
+ //#endregion
199
+ export { createPaletteSwap as t };
package/package.json CHANGED
@@ -1,33 +1,51 @@
1
1
  {
2
2
  "name": "@nkzw/palette-swap",
3
- "version": "3.1.0",
4
- "author": "Christoph Nakazawa <christoph.pojer@gmail.com>",
3
+ "version": "4.0.0",
5
4
  "description": "Image palette swapping and color replacement tool based on canvas for Node.js and browsers.",
5
+ "keywords": [
6
+ "canvas",
7
+ "color",
8
+ "palette"
9
+ ],
10
+ "license": "MIT",
11
+ "author": "Christoph Nakazawa <christoph.pojer@gmail.com>",
6
12
  "repository": {
7
13
  "type": "git",
8
14
  "url": "https://github.com/cpojer/palette-swap.git"
9
15
  },
10
- "license": "MIT",
11
- "main": "lib/palette-swap.js",
12
16
  "type": "module",
13
- "keywords": [
14
- "color",
15
- "palette",
16
- "canvas"
17
- ],
18
- "engines": {
19
- "node": ">=22.0.0"
17
+ "main": "lib/node.js",
18
+ "browser": "./lib/browser.js",
19
+ "types": "lib/node.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "browser": {
23
+ "types": "./lib/browser.d.ts",
24
+ "default": "./lib/browser.js"
25
+ },
26
+ "node": {
27
+ "types": "./lib/node.d.ts",
28
+ "default": "./lib/node.js"
29
+ },
30
+ "default": "./lib/browser.js"
31
+ }
20
32
  },
21
33
  "dependencies": {
22
- "canvas": "^3.1.0"
34
+ "@napi-rs/canvas": "^1.0.9"
23
35
  },
24
36
  "devDependencies": {
25
- "@types/node": "^22.15.21",
26
- "esbuild": "^0.25.4",
27
- "typescript": "^5.8.3"
37
+ "@types/node": "^25.9.7",
38
+ "playwright": "^1.63.0",
39
+ "tsdown": "^0.23.0",
40
+ "typescript": "^7.0.2",
41
+ "vitest": "^5.0.1"
42
+ },
43
+ "engines": {
44
+ "node": ">=22.0.0"
28
45
  },
29
46
  "scripts": {
30
- "build": "mkdir -p lib && rm lib/*; tsc --declaration --emitDeclarationOnly --noEmit false && esbuild --target=node22 --format=esm --platform=node --outfile=lib/palette-swap.js --external:canvas --bundle ./src/palette-swap.js",
31
- "test": "tsc"
47
+ "benchmark": "vitest bench --run",
48
+ "build": "tsdown",
49
+ "test": "pnpm build && tsc && vitest run"
32
50
  }
33
51
  }
@@ -1,11 +0,0 @@
1
- import { Canvas, Image as CanvasImage } from 'canvas';
2
- type Hue = number;
3
- export type HEX = `#${string}`;
4
- export type HSL = [hue: number, saturation: number, lightness: number];
5
- export type VariantName = number | string;
6
- export type Image = CanvasImage | HTMLImageElement;
7
- export default function paletteSwap(image: Image, inputVariants: ReadonlyMap<VariantName, ReadonlyMap<HEX, HEX> | Hue>, staticColors?: Set<HEX> | null, images?: ReadonlyMap<VariantName, Image> | null, options?: {
8
- imageName?: string;
9
- ignoreMissing?: boolean;
10
- }): ReadonlyMap<VariantName, Canvas>;
11
- export {};
@@ -1,182 +0,0 @@
1
- // src/palette-swap.ts
2
- import {
3
- createCanvas,
4
- createImageData
5
- } from "canvas";
6
- var hexToUint32LE = (hex2) => {
7
- const n = Number.parseInt(hex2.slice(1), 16) & 16777215;
8
- const [r, g, b] = int32ToRgb(n);
9
- return b << 16 | g << 8 | r;
10
- };
11
- var int32ToRgb = (color) => {
12
- const r = color >> 16 & 255;
13
- const g = color >> 8 & 255;
14
- const b = color & 255;
15
- return [r, g, b];
16
- };
17
- var hex = (value) => Math.round(value).toString(16).padStart(2, "0");
18
- var rgbToHex = ([r, g, b]) => `#${hex(r)}${hex(g)}${hex(b)}`;
19
- var int32ToHex = (color) => {
20
- const [r, g, b] = int32ToRgb(color);
21
- return rgbToHex([b, g, r]);
22
- };
23
- var applyHue = (rgb, hue) => {
24
- const h = hue / 360;
25
- const r = rgb[2] / 255;
26
- const g = rgb[1] / 255;
27
- const b = rgb[0] / 255;
28
- const min = Math.min(r, g, b);
29
- const max = Math.max(r, g, b);
30
- const delta = max - min;
31
- let s;
32
- const l = (min + max) / 2;
33
- if (max === min) {
34
- s = 0;
35
- } else if (l <= 0.5) {
36
- s = delta / (max + min);
37
- } else {
38
- s = delta / (2 - max - min);
39
- }
40
- let t2;
41
- let t3;
42
- let val;
43
- if (s === 0) {
44
- val = l * 255;
45
- return val << 16 | val << 8 | val;
46
- }
47
- if (l < 0.5) {
48
- t2 = l * (1 + s);
49
- } else {
50
- t2 = l + s - l * s;
51
- }
52
- const t1 = 2 * l - t2;
53
- const result = [0, 0, 0];
54
- for (let i = 0; i < 3; i++) {
55
- t3 = h + 1 / 3 * -(i - 1);
56
- if (t3 < 0) {
57
- t3++;
58
- }
59
- if (t3 > 1) {
60
- t3--;
61
- }
62
- if (6 * t3 < 1) {
63
- val = t1 + (t2 - t1) * 6 * t3;
64
- } else if (2 * t3 < 1) {
65
- val = t2;
66
- } else if (3 * t3 < 2) {
67
- val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
68
- } else {
69
- val = t1;
70
- }
71
- result[i] = val * 255;
72
- }
73
- return result[2] << 16 | result[1] << 8 | result[0];
74
- };
75
- var createCanvasFromImage = (image) => {
76
- const canvas = createCanvas(image.width, image.height);
77
- const context = canvas.getContext("2d");
78
- context.drawImage(image, 0, 0);
79
- return canvas;
80
- };
81
- var equals = (a, b) => {
82
- if (a.width !== b.width || a.height !== b.height) {
83
- return false;
84
- }
85
- for (let i = 0; i < a.data.length; i++) {
86
- if (a.data[i] !== b.data[i]) {
87
- return false;
88
- }
89
- }
90
- return true;
91
- };
92
- function paletteSwap(image, inputVariants, staticColors, images, options) {
93
- const canvas = createCanvasFromImage(image);
94
- const context = canvas.getContext("2d");
95
- const { height, width } = canvas;
96
- const imageData = new Uint32Array(
97
- context.getImageData(0, 0, width, height).data.buffer
98
- );
99
- const results = /* @__PURE__ */ new Map();
100
- const variants = [];
101
- let i = 0;
102
- const palettes = /* @__PURE__ */ new Map();
103
- for (const [variant, palette] of inputVariants) {
104
- const newImageData = createImageData(
105
- new Uint8ClampedArray(width * height * 4),
106
- width
107
- );
108
- if (typeof palette !== "number") {
109
- for (const [key, value] of palette) {
110
- const k = hexToUint32LE(key);
111
- const v = palettes.get(k) || [];
112
- v[i] = hexToUint32LE(value);
113
- palettes.set(k, v);
114
- }
115
- }
116
- variants.push([
117
- variant,
118
- newImageData,
119
- new Uint32Array(newImageData.data.buffer),
120
- typeof palette === "number" ? palette : null
121
- ]);
122
- i++;
123
- }
124
- const staticColorNumbers = staticColors ? new Set([...staticColors].map(hexToUint32LE)) : null;
125
- const missing = /* @__PURE__ */ new Set();
126
- for (var y = 0; y < height; ++y) {
127
- for (var x = 0; x < width; ++x) {
128
- const index = y * width + x;
129
- const originalColor = imageData[index];
130
- const a = originalColor >> 24 & 255;
131
- if (a === 0) {
132
- continue;
133
- }
134
- const color = originalColor & 16777215;
135
- const isStatic = staticColorNumbers?.has(color);
136
- const colors = !isStatic ? palettes.get(color) : null;
137
- for (let i2 = 0; i2 < variants.length; i2++) {
138
- const newImageData = variants[i2][2];
139
- if (isStatic) {
140
- newImageData[index] = originalColor;
141
- continue;
142
- }
143
- const hue = variants[i2][3];
144
- if (hue) {
145
- newImageData[index] = a << 24 | applyHue(int32ToRgb(color), hue);
146
- } else if (colors?.[i2]) {
147
- newImageData[index] = a << 24 | colors[i2];
148
- } else if (!staticColorNumbers) {
149
- newImageData[index] = originalColor;
150
- } else {
151
- missing.add([variants[i2][0], color]);
152
- }
153
- }
154
- }
155
- }
156
- if (!options?.ignoreMissing && missing.size) {
157
- throw new Error(
158
- `${options?.imageName ? `${options.imageName}` : "Palette Swap"}: Missing ${Array.from(
159
- new Set(
160
- [...missing].map(
161
- ([variant, color]) => `'${int32ToHex(color)}' in '${variant}'`
162
- )
163
- )
164
- ).join(", ")}.`
165
- );
166
- }
167
- for (let i2 = 0; i2 < variants.length; i2++) {
168
- const name = variants[i2][0];
169
- const existingImage = images?.get(name);
170
- const existingImageData = existingImage ? createCanvasFromImage(existingImage).getContext("2d").getImageData(0, 0, width, height) : null;
171
- const newImageData = variants[i2][1];
172
- if (!existingImageData || !equals(newImageData, existingImageData)) {
173
- const newCanvas = createCanvasFromImage(image);
174
- newCanvas.getContext("2d").putImageData(newImageData, 0, 0);
175
- results.set(name, newCanvas);
176
- }
177
- }
178
- return results;
179
- }
180
- export {
181
- paletteSwap as default
182
- };
@@ -1,4 +0,0 @@
1
- ignoredBuiltDependencies:
2
- - esbuild
3
- onlyBuiltDependencies:
4
- - canvas