image-to-pixel 3.0.0 → 3.1.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/LICENSE +21 -21
- package/README.md +2 -2
- package/dist/editor.css +1 -1
- package/dist/editor.css.map +2 -2
- package/dist/editor.esm.js +60 -60
- package/dist/editor.esm.js.map +4 -4
- package/dist/fonts/README.md +6 -6
- package/dist/fonts/geist-LICENSE.txt +93 -93
- package/dist/image-to-pixel.bundle.esm.js +16 -16
- package/dist/image-to-pixel.bundle.esm.js.map +3 -3
- package/dist/image-to-pixel.cjs +2 -2
- package/dist/image-to-pixel.cjs.map +2 -2
- package/dist/image-to-pixel.esm.js +2 -2
- package/dist/image-to-pixel.esm.js.map +2 -2
- package/dist/image-to-pixel.js +2 -2
- package/dist/image-to-pixel.js.map +2 -2
- package/dist/image-to-pixel.min.js +11 -11
- package/dist/image-to-pixel.min.js.map +3 -3
- package/dist/video.esm.js +2 -2
- package/dist/video.esm.js.map +4 -4
- package/lib/core/color.js +69 -69
- package/lib/core/cpu/pipeline.js +21 -21
- package/lib/core/cpu/stages.js +227 -227
- package/lib/core/dither/bluenoise-data.js +3 -3
- package/lib/core/dither/kernels.js +27 -27
- package/lib/core/dither/matrices.js +95 -95
- package/lib/core/engine.js +158 -158
- package/lib/core/export/canvas.js +22 -22
- package/lib/core/export/crc32.js +15 -15
- package/lib/core/export/gif.js +153 -153
- package/lib/core/export/png.js +94 -94
- package/lib/core/export/scale.js +103 -103
- package/lib/core/export/sheet.js +95 -95
- package/lib/core/export/zip.js +55 -55
- package/lib/core/gpu/backend.js +159 -159
- package/lib/core/gpu/shaders.js +258 -258
- package/lib/core/palette/builtin.js +36 -36
- package/lib/core/palette/extract.js +69 -69
- package/lib/core/palette/lospec.js +24 -24
- package/lib/core/palette/parse.js +112 -112
- package/lib/core/plan.js +58 -58
- package/lib/core/presets.js +42 -42
- package/lib/core/settings.js +274 -274
- package/lib/core/source.js +89 -89
- package/lib/core/video/audio.js +159 -0
- package/lib/core/video/frames.js +38 -0
- package/lib/core/video/mux-mp4.js +213 -24
- package/lib/core/video/mux-webm.js +63 -22
- package/lib/editor/controls.js +139 -139
- package/lib/editor/dom.js +65 -65
- package/lib/editor/exporter.js +43 -3
- package/lib/editor/icons.js +4 -0
- package/lib/editor/index.js +218 -9
- package/lib/editor/inspector.js +19 -4
- package/lib/editor/library.js +8 -2
- package/lib/editor/persist.js +18 -18
- package/lib/editor/samples.js +90 -90
- package/lib/editor/stage.js +239 -239
- package/lib/editor/store.js +80 -80
- package/lib/editor/styles/editor.css +34 -1
- package/lib/editor/styles/fonts/README.md +6 -6
- package/lib/editor/styles/fonts/geist-LICENSE.txt +93 -93
- package/lib/editor/styles/tokens.css +89 -89
- package/lib/editor/ui.js +97 -97
- package/lib/editor/workspace.js +62 -0
- package/lib/index.js +31 -29
- package/lib/video.js +37 -7
- package/package.json +1 -1
- package/types/editor.d.ts +34 -34
- package/types/index.d.ts +196 -196
- package/types/video.d.ts +61 -5
package/lib/core/gpu/shaders.js
CHANGED
|
@@ -1,258 +1,258 @@
|
|
|
1
|
-
// GLSL (WebGL 2) for the GPU pipeline. Each pass mirrors a function in cpu/stages.js.
|
|
2
|
-
// ShaderMate adds the #version line, precision, fragColor and the built-in uniforms.
|
|
3
|
-
//
|
|
4
|
-
// prep ─▶ quant ──────────────┐
|
|
5
|
-
// └───▶ diffuse (N waves) ─▶ resolve ─▶ orphan ─▶ smooth ─▶ out (screen)
|
|
6
|
-
//
|
|
7
|
-
// Note: no trailing comments on uniform lines; ShaderMate reads trailing numbers there as default values.
|
|
8
|
-
// All passes address pixels top-down (`P`, row 0 = top) so the maths matches the CPU.
|
|
9
|
-
|
|
10
|
-
export const COMMON = /* glsl */`
|
|
11
|
-
// 256×1, palette colours in x = 0..count-1
|
|
12
|
-
uniform sampler2D u_palette;
|
|
13
|
-
uniform int u_count;
|
|
14
|
-
// 0 = OKLab, 1 = weighted RGB
|
|
15
|
-
uniform int u_distance;
|
|
16
|
-
|
|
17
|
-
ivec2 topDown() { return ivec2(int(gl_FragCoord.x), int(u_resolution.y) - 1 - int(gl_FragCoord.y)); }
|
|
18
|
-
vec4 at(sampler2D t, ivec2 p) { ivec2 s = textureSize(t, 0); return texelFetch(t, ivec2(p.x, s.y - 1 - p.y), 0); }
|
|
19
|
-
bool inside(ivec2 p) { return p.x >= 0 && p.y >= 0 && p.x < int(u_resolution.x) && p.y < int(u_resolution.y); }
|
|
20
|
-
vec3 palColor(int i) { return texelFetch(u_palette, ivec2(i, 0), 0).rgb; }
|
|
21
|
-
|
|
22
|
-
float linear1(float c) { return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); }
|
|
23
|
-
vec3 oklab(vec3 c) {
|
|
24
|
-
c = vec3(linear1(c.r), linear1(c.g), linear1(c.b));
|
|
25
|
-
float l = pow(0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b, 1.0 / 3.0);
|
|
26
|
-
float m = pow(0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b, 1.0 / 3.0);
|
|
27
|
-
float s = pow(0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b, 1.0 / 3.0);
|
|
28
|
-
return vec3(0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
|
29
|
-
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
|
30
|
-
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s);
|
|
31
|
-
}
|
|
32
|
-
vec3 space(vec3 c) { return u_distance == 1 ? c : oklab(c); }
|
|
33
|
-
int nearestIndex(vec3 c) {
|
|
34
|
-
vec3 p = space(c);
|
|
35
|
-
vec3 w = u_distance == 1 ? vec3(0.299, 0.587, 0.114) : vec3(1.0);
|
|
36
|
-
int best = 0; float bd = 1e20;
|
|
37
|
-
for (int i = 0; i < 256; i++) {
|
|
38
|
-
if (i >= u_count) break;
|
|
39
|
-
vec3 d = p - space(palColor(i));
|
|
40
|
-
float dist = dot(w * d, d);
|
|
41
|
-
if (dist < bd) { bd = dist; best = i; }
|
|
42
|
-
}
|
|
43
|
-
return best;
|
|
44
|
-
}
|
|
45
|
-
int colorKey(vec4 c) {
|
|
46
|
-
if (c.a < 0.5) return -1;
|
|
47
|
-
ivec3 b = ivec3(floor(c.rgb * 255.0 + 0.5));
|
|
48
|
-
return b.r * 65536 + b.g * 256 + b.b;
|
|
49
|
-
}
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
/** Resample (box / nearest) + adjust + alpha cut. iChannel0 = source image. */
|
|
53
|
-
export const PREP = /* glsl */`
|
|
54
|
-
// 0 box, 1 nearest
|
|
55
|
-
uniform int u_resample;
|
|
56
|
-
// brightness, contrast, saturation, gamma
|
|
57
|
-
uniform vec4 u_adjust;
|
|
58
|
-
// hue rotation in degrees
|
|
59
|
-
uniform float u_hue;
|
|
60
|
-
uniform float u_alphaCut;
|
|
61
|
-
// source region in texels: x, y (from the top-left), width, height
|
|
62
|
-
uniform ivec4 u_crop;
|
|
63
|
-
|
|
64
|
-
void main() {
|
|
65
|
-
ivec2 P = topDown();
|
|
66
|
-
ivec2 R = ivec2(u_resolution);
|
|
67
|
-
ivec2 O = u_crop.xy;
|
|
68
|
-
ivec2 S = u_crop.zw;
|
|
69
|
-
int nx = u_resample == 1 ? 1 : clamp((S.x + R.x - 1) / R.x, 1, 16);
|
|
70
|
-
int ny = u_resample == 1 ? 1 : clamp((S.y + R.y - 1) / R.y, 1, 16);
|
|
71
|
-
vec4 acc = vec4(0.0);
|
|
72
|
-
for (int j = 0; j < 16; j++) {
|
|
73
|
-
if (j >= ny) break;
|
|
74
|
-
int sy = min(S.y - 1, ((2 * (P.y * ny + j) + 1) * S.y) / (2 * R.y * ny));
|
|
75
|
-
for (int i = 0; i < 16; i++) {
|
|
76
|
-
if (i >= nx) break;
|
|
77
|
-
int sx = min(S.x - 1, ((2 * (P.x * nx + i) + 1) * S.x) / (2 * R.x * nx));
|
|
78
|
-
vec4 c = at(iChannel0, O + ivec2(sx, sy));
|
|
79
|
-
acc += vec4(c.rgb * c.a, c.a);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
vec3 c = acc.a > 0.0 ? acc.rgb / acc.a : vec3(0.0);
|
|
83
|
-
float a = acc.a / float(nx * ny);
|
|
84
|
-
|
|
85
|
-
float br = u_adjust.x, co = u_adjust.y, sa = u_adjust.z, ga = u_adjust.w;
|
|
86
|
-
if (br != 0.0 || co != 0.0 || sa != 0.0 || ga != 1.0 || u_hue != 0.0) {
|
|
87
|
-
float f = co >= 0.0 ? 1.0 / (1.0 - 0.99 * co) : 1.0 + co;
|
|
88
|
-
c = (c + br - 0.5) * f + 0.5;
|
|
89
|
-
float l = dot(c, vec3(0.2126, 0.7152, 0.0722));
|
|
90
|
-
c = l + (c - l) * (1.0 + sa);
|
|
91
|
-
if (u_hue != 0.0) {
|
|
92
|
-
float h = radians(u_hue), k = cos(h), q = sin(h);
|
|
93
|
-
c = vec3(
|
|
94
|
-
(0.213 + 0.787 * k - 0.213 * q) * c.r + (0.715 - 0.715 * k - 0.715 * q) * c.g + (0.072 - 0.072 * k + 0.928 * q) * c.b,
|
|
95
|
-
(0.213 - 0.213 * k + 0.143 * q) * c.r + (0.715 + 0.285 * k + 0.140 * q) * c.g + (0.072 - 0.072 * k - 0.283 * q) * c.b,
|
|
96
|
-
(0.213 - 0.213 * k - 0.787 * q) * c.r + (0.715 - 0.715 * k + 0.715 * q) * c.g + (0.072 + 0.928 * k + 0.072 * q) * c.b);
|
|
97
|
-
}
|
|
98
|
-
c = pow(clamp(c, 0.0, 1.0), vec3(1.0 / ga));
|
|
99
|
-
}
|
|
100
|
-
a = (a >= u_alphaCut && a > 0.0) ? 1.0 : 0.0;
|
|
101
|
-
fragColor = vec4(c * a, a);
|
|
102
|
-
}
|
|
103
|
-
`;
|
|
104
|
-
|
|
105
|
-
/** No dither / ordered dither. iChannel0 = prep. */
|
|
106
|
-
export const QUANT = /* glsl */`
|
|
107
|
-
// threshold bytes in .r
|
|
108
|
-
uniform sampler2D u_matrix;
|
|
109
|
-
// 0 pass-through, 1 nearest, 2 ordered
|
|
110
|
-
uniform int u_mode;
|
|
111
|
-
uniform float u_levels;
|
|
112
|
-
// spread × strength
|
|
113
|
-
uniform float u_amount;
|
|
114
|
-
|
|
115
|
-
void main() {
|
|
116
|
-
ivec2 P = topDown();
|
|
117
|
-
vec4 p = at(iChannel0, P);
|
|
118
|
-
if (p.a < 0.5) { fragColor = vec4(0.0); return; }
|
|
119
|
-
vec3 c = p.rgb;
|
|
120
|
-
if (u_mode == 0) { fragColor = vec4(floor(c * 255.0 + 0.5) / 255.0, 1.0); return; }
|
|
121
|
-
if (u_mode == 2) {
|
|
122
|
-
ivec2 ms = textureSize(u_matrix, 0);
|
|
123
|
-
float v = floor(at(u_matrix, ivec2(P.x % ms.x, P.y % ms.y)).r * 255.0 + 0.5);
|
|
124
|
-
float t = (v + 0.5) / u_levels - 0.5;
|
|
125
|
-
c = clamp(c + t * u_amount, 0.0, 1.0);
|
|
126
|
-
}
|
|
127
|
-
fragColor = vec4(palColor(nearestIndex(c)), 1.0);
|
|
128
|
-
}
|
|
129
|
-
`;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Error diffusion as a wavefront. Iteration r computes every pixel with x + skew*y == r,
|
|
133
|
-
* pulling the stored error of already-computed neighbours.
|
|
134
|
-
* Output: rgb = quantisation error, a = palette index (-1 not yet computed, -2 transparent).
|
|
135
|
-
* iChannel0 = prep, iChannel1 = this pass (previous iteration).
|
|
136
|
-
*/
|
|
137
|
-
export const DIFFUSE = /* glsl */`
|
|
138
|
-
// iteration index, set by ShaderMate for passes with iterations (declared here: a FragCoord.xyz built-in)
|
|
139
|
-
uniform int u_recursion;
|
|
140
|
-
uniform int u_on;
|
|
141
|
-
uniform int u_skew;
|
|
142
|
-
uniform float u_strength;
|
|
143
|
-
uniform int u_kn;
|
|
144
|
-
// push offsets (dx, dy) and weight
|
|
145
|
-
uniform vec3 u_kernel[12];
|
|
146
|
-
|
|
147
|
-
void main() {
|
|
148
|
-
if (u_on == 0) { fragColor = vec4(0.0, 0.0, 0.0, -1.0); return; }
|
|
149
|
-
ivec2 P = topDown();
|
|
150
|
-
int k = P.x + u_skew * P.y;
|
|
151
|
-
int r = u_recursion;
|
|
152
|
-
if (k < r) { fragColor = at(iChannel1, P); return; }
|
|
153
|
-
if (k > r) { fragColor = vec4(0.0, 0.0, 0.0, -1.0); return; }
|
|
154
|
-
vec4 p = at(iChannel0, P);
|
|
155
|
-
if (p.a < 0.5) { fragColor = vec4(0.0, 0.0, 0.0, -2.0); return; }
|
|
156
|
-
vec3 acc = vec3(0.0);
|
|
157
|
-
for (int i = 0; i < 12; i++) {
|
|
158
|
-
if (i >= u_kn) break;
|
|
159
|
-
ivec2 Q = P - ivec2(u_kernel[i].xy);
|
|
160
|
-
if (!inside(Q)) continue;
|
|
161
|
-
vec4 e = at(iChannel1, Q);
|
|
162
|
-
if (e.a >= 0.0) acc += e.rgb * u_kernel[i].z;
|
|
163
|
-
}
|
|
164
|
-
vec3 c = clamp(p.rgb + acc * u_strength, 0.0, 1.0);
|
|
165
|
-
int idx = nearestIndex(c);
|
|
166
|
-
fragColor = vec4(c - palColor(idx), float(idx));
|
|
167
|
-
}
|
|
168
|
-
`;
|
|
169
|
-
|
|
170
|
-
/** Pick the ordered result or turn diffusion indices into colours. iChannel0 = quant, iChannel1 = diffuse. */
|
|
171
|
-
export const RESOLVE = /* glsl */`
|
|
172
|
-
uniform int u_on;
|
|
173
|
-
void main() {
|
|
174
|
-
ivec2 P = topDown();
|
|
175
|
-
if (u_on == 0) { fragColor = at(iChannel0, P); return; }
|
|
176
|
-
vec4 d = at(iChannel1, P);
|
|
177
|
-
fragColor = d.a < 0.0 ? vec4(0.0) : vec4(palColor(int(d.a + 0.5)), 1.0);
|
|
178
|
-
}
|
|
179
|
-
`;
|
|
180
|
-
|
|
181
|
-
/** Orphan removal. iChannel0 = resolve. */
|
|
182
|
-
export const ORPHAN = /* glsl */`
|
|
183
|
-
uniform int u_on;
|
|
184
|
-
void main() {
|
|
185
|
-
ivec2 P = topDown();
|
|
186
|
-
vec4 me = at(iChannel0, P);
|
|
187
|
-
fragColor = me;
|
|
188
|
-
if (u_on == 0) return;
|
|
189
|
-
int c = colorKey(me);
|
|
190
|
-
ivec2 off[4] = ivec2[4](ivec2(0, -1), ivec2(1, 0), ivec2(0, 1), ivec2(-1, 0));
|
|
191
|
-
int ks[4]; vec4 cs[4]; int n = 0;
|
|
192
|
-
for (int i = 0; i < 4; i++) {
|
|
193
|
-
ivec2 Q = P + off[i];
|
|
194
|
-
if (!inside(Q)) continue;
|
|
195
|
-
cs[n] = at(iChannel0, Q); ks[n] = colorKey(cs[n]);
|
|
196
|
-
if (ks[n] == c) return;
|
|
197
|
-
n++;
|
|
198
|
-
}
|
|
199
|
-
if (n < 2) return;
|
|
200
|
-
int best = 0, bc = 0;
|
|
201
|
-
for (int i = 0; i < 4; i++) {
|
|
202
|
-
if (i >= n) break;
|
|
203
|
-
int m = 0;
|
|
204
|
-
for (int j = 0; j < 4; j++) { if (j >= n) break; if (ks[j] == ks[i]) m++; }
|
|
205
|
-
if (m > bc) { bc = m; best = i; }
|
|
206
|
-
}
|
|
207
|
-
fragColor = cs[best];
|
|
208
|
-
}
|
|
209
|
-
`;
|
|
210
|
-
|
|
211
|
-
/** 3×3 majority smoothing. iChannel0 = orphan. */
|
|
212
|
-
export const SMOOTH = /* glsl */`
|
|
213
|
-
uniform int u_on;
|
|
214
|
-
void main() {
|
|
215
|
-
ivec2 P = topDown();
|
|
216
|
-
vec4 me = at(iChannel0, P);
|
|
217
|
-
fragColor = me;
|
|
218
|
-
if (u_on == 0) return;
|
|
219
|
-
int c = colorKey(me);
|
|
220
|
-
ivec2 R = ivec2(u_resolution);
|
|
221
|
-
int ks[9]; vec4 cs[9];
|
|
222
|
-
for (int j = 0; j < 3; j++) for (int i = 0; i < 3; i++) {
|
|
223
|
-
ivec2 Q = clamp(P + ivec2(i - 1, j - 1), ivec2(0), R - 1);
|
|
224
|
-
cs[j * 3 + i] = at(iChannel0, Q); ks[j * 3 + i] = colorKey(cs[j * 3 + i]);
|
|
225
|
-
}
|
|
226
|
-
for (int i = 0; i < 9; i++) {
|
|
227
|
-
if (ks[i] == c) continue;
|
|
228
|
-
int m = 0;
|
|
229
|
-
for (int j = 0; j < 9; j++) if (ks[j] == ks[i]) m++;
|
|
230
|
-
if (m >= 5) { fragColor = cs[i]; return; }
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
`;
|
|
234
|
-
|
|
235
|
-
/** Outline, drawn to the canvas. iChannel0 = smooth. */
|
|
236
|
-
export const OUTLINE = /* glsl */`
|
|
237
|
-
// 0 none, 1 outer, 2 inner
|
|
238
|
-
uniform int u_outline;
|
|
239
|
-
uniform int u_corners;
|
|
240
|
-
uniform vec3 u_outlineColor;
|
|
241
|
-
bool opaqueAt(ivec2 q) { return inside(q) && at(iChannel0, q).a > 0.5; }
|
|
242
|
-
void main() {
|
|
243
|
-
ivec2 P = topDown();
|
|
244
|
-
vec4 me = at(iChannel0, P);
|
|
245
|
-
fragColor = me;
|
|
246
|
-
if (u_outline == 0) return;
|
|
247
|
-
bool o = me.a > 0.5;
|
|
248
|
-
ivec2 off[8] = ivec2[8](ivec2(0, -1), ivec2(1, 0), ivec2(0, 1), ivec2(-1, 0), ivec2(-1, -1), ivec2(1, -1), ivec2(1, 1), ivec2(-1, 1));
|
|
249
|
-
int n = u_corners == 1 ? 8 : 4;
|
|
250
|
-
for (int i = 0; i < 8; i++) {
|
|
251
|
-
if (i >= n) break;
|
|
252
|
-
ivec2 Q = P + off[i];
|
|
253
|
-
if (!inside(Q)) continue;
|
|
254
|
-
bool other = opaqueAt(Q);
|
|
255
|
-
if (u_outline == 1 ? (!o && other) : (o && !other)) { fragColor = vec4(u_outlineColor, 1.0); return; }
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
`;
|
|
1
|
+
// GLSL (WebGL 2) for the GPU pipeline. Each pass mirrors a function in cpu/stages.js.
|
|
2
|
+
// ShaderMate adds the #version line, precision, fragColor and the built-in uniforms.
|
|
3
|
+
//
|
|
4
|
+
// prep ─▶ quant ──────────────┐
|
|
5
|
+
// └───▶ diffuse (N waves) ─▶ resolve ─▶ orphan ─▶ smooth ─▶ out (screen)
|
|
6
|
+
//
|
|
7
|
+
// Note: no trailing comments on uniform lines; ShaderMate reads trailing numbers there as default values.
|
|
8
|
+
// All passes address pixels top-down (`P`, row 0 = top) so the maths matches the CPU.
|
|
9
|
+
|
|
10
|
+
export const COMMON = /* glsl */`
|
|
11
|
+
// 256×1, palette colours in x = 0..count-1
|
|
12
|
+
uniform sampler2D u_palette;
|
|
13
|
+
uniform int u_count;
|
|
14
|
+
// 0 = OKLab, 1 = weighted RGB
|
|
15
|
+
uniform int u_distance;
|
|
16
|
+
|
|
17
|
+
ivec2 topDown() { return ivec2(int(gl_FragCoord.x), int(u_resolution.y) - 1 - int(gl_FragCoord.y)); }
|
|
18
|
+
vec4 at(sampler2D t, ivec2 p) { ivec2 s = textureSize(t, 0); return texelFetch(t, ivec2(p.x, s.y - 1 - p.y), 0); }
|
|
19
|
+
bool inside(ivec2 p) { return p.x >= 0 && p.y >= 0 && p.x < int(u_resolution.x) && p.y < int(u_resolution.y); }
|
|
20
|
+
vec3 palColor(int i) { return texelFetch(u_palette, ivec2(i, 0), 0).rgb; }
|
|
21
|
+
|
|
22
|
+
float linear1(float c) { return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); }
|
|
23
|
+
vec3 oklab(vec3 c) {
|
|
24
|
+
c = vec3(linear1(c.r), linear1(c.g), linear1(c.b));
|
|
25
|
+
float l = pow(0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b, 1.0 / 3.0);
|
|
26
|
+
float m = pow(0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b, 1.0 / 3.0);
|
|
27
|
+
float s = pow(0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b, 1.0 / 3.0);
|
|
28
|
+
return vec3(0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
|
29
|
+
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
|
30
|
+
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s);
|
|
31
|
+
}
|
|
32
|
+
vec3 space(vec3 c) { return u_distance == 1 ? c : oklab(c); }
|
|
33
|
+
int nearestIndex(vec3 c) {
|
|
34
|
+
vec3 p = space(c);
|
|
35
|
+
vec3 w = u_distance == 1 ? vec3(0.299, 0.587, 0.114) : vec3(1.0);
|
|
36
|
+
int best = 0; float bd = 1e20;
|
|
37
|
+
for (int i = 0; i < 256; i++) {
|
|
38
|
+
if (i >= u_count) break;
|
|
39
|
+
vec3 d = p - space(palColor(i));
|
|
40
|
+
float dist = dot(w * d, d);
|
|
41
|
+
if (dist < bd) { bd = dist; best = i; }
|
|
42
|
+
}
|
|
43
|
+
return best;
|
|
44
|
+
}
|
|
45
|
+
int colorKey(vec4 c) {
|
|
46
|
+
if (c.a < 0.5) return -1;
|
|
47
|
+
ivec3 b = ivec3(floor(c.rgb * 255.0 + 0.5));
|
|
48
|
+
return b.r * 65536 + b.g * 256 + b.b;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
/** Resample (box / nearest) + adjust + alpha cut. iChannel0 = source image. */
|
|
53
|
+
export const PREP = /* glsl */`
|
|
54
|
+
// 0 box, 1 nearest
|
|
55
|
+
uniform int u_resample;
|
|
56
|
+
// brightness, contrast, saturation, gamma
|
|
57
|
+
uniform vec4 u_adjust;
|
|
58
|
+
// hue rotation in degrees
|
|
59
|
+
uniform float u_hue;
|
|
60
|
+
uniform float u_alphaCut;
|
|
61
|
+
// source region in texels: x, y (from the top-left), width, height
|
|
62
|
+
uniform ivec4 u_crop;
|
|
63
|
+
|
|
64
|
+
void main() {
|
|
65
|
+
ivec2 P = topDown();
|
|
66
|
+
ivec2 R = ivec2(u_resolution);
|
|
67
|
+
ivec2 O = u_crop.xy;
|
|
68
|
+
ivec2 S = u_crop.zw;
|
|
69
|
+
int nx = u_resample == 1 ? 1 : clamp((S.x + R.x - 1) / R.x, 1, 16);
|
|
70
|
+
int ny = u_resample == 1 ? 1 : clamp((S.y + R.y - 1) / R.y, 1, 16);
|
|
71
|
+
vec4 acc = vec4(0.0);
|
|
72
|
+
for (int j = 0; j < 16; j++) {
|
|
73
|
+
if (j >= ny) break;
|
|
74
|
+
int sy = min(S.y - 1, ((2 * (P.y * ny + j) + 1) * S.y) / (2 * R.y * ny));
|
|
75
|
+
for (int i = 0; i < 16; i++) {
|
|
76
|
+
if (i >= nx) break;
|
|
77
|
+
int sx = min(S.x - 1, ((2 * (P.x * nx + i) + 1) * S.x) / (2 * R.x * nx));
|
|
78
|
+
vec4 c = at(iChannel0, O + ivec2(sx, sy));
|
|
79
|
+
acc += vec4(c.rgb * c.a, c.a);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
vec3 c = acc.a > 0.0 ? acc.rgb / acc.a : vec3(0.0);
|
|
83
|
+
float a = acc.a / float(nx * ny);
|
|
84
|
+
|
|
85
|
+
float br = u_adjust.x, co = u_adjust.y, sa = u_adjust.z, ga = u_adjust.w;
|
|
86
|
+
if (br != 0.0 || co != 0.0 || sa != 0.0 || ga != 1.0 || u_hue != 0.0) {
|
|
87
|
+
float f = co >= 0.0 ? 1.0 / (1.0 - 0.99 * co) : 1.0 + co;
|
|
88
|
+
c = (c + br - 0.5) * f + 0.5;
|
|
89
|
+
float l = dot(c, vec3(0.2126, 0.7152, 0.0722));
|
|
90
|
+
c = l + (c - l) * (1.0 + sa);
|
|
91
|
+
if (u_hue != 0.0) {
|
|
92
|
+
float h = radians(u_hue), k = cos(h), q = sin(h);
|
|
93
|
+
c = vec3(
|
|
94
|
+
(0.213 + 0.787 * k - 0.213 * q) * c.r + (0.715 - 0.715 * k - 0.715 * q) * c.g + (0.072 - 0.072 * k + 0.928 * q) * c.b,
|
|
95
|
+
(0.213 - 0.213 * k + 0.143 * q) * c.r + (0.715 + 0.285 * k + 0.140 * q) * c.g + (0.072 - 0.072 * k - 0.283 * q) * c.b,
|
|
96
|
+
(0.213 - 0.213 * k - 0.787 * q) * c.r + (0.715 - 0.715 * k + 0.715 * q) * c.g + (0.072 + 0.928 * k + 0.072 * q) * c.b);
|
|
97
|
+
}
|
|
98
|
+
c = pow(clamp(c, 0.0, 1.0), vec3(1.0 / ga));
|
|
99
|
+
}
|
|
100
|
+
a = (a >= u_alphaCut && a > 0.0) ? 1.0 : 0.0;
|
|
101
|
+
fragColor = vec4(c * a, a);
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
/** No dither / ordered dither. iChannel0 = prep. */
|
|
106
|
+
export const QUANT = /* glsl */`
|
|
107
|
+
// threshold bytes in .r
|
|
108
|
+
uniform sampler2D u_matrix;
|
|
109
|
+
// 0 pass-through, 1 nearest, 2 ordered
|
|
110
|
+
uniform int u_mode;
|
|
111
|
+
uniform float u_levels;
|
|
112
|
+
// spread × strength
|
|
113
|
+
uniform float u_amount;
|
|
114
|
+
|
|
115
|
+
void main() {
|
|
116
|
+
ivec2 P = topDown();
|
|
117
|
+
vec4 p = at(iChannel0, P);
|
|
118
|
+
if (p.a < 0.5) { fragColor = vec4(0.0); return; }
|
|
119
|
+
vec3 c = p.rgb;
|
|
120
|
+
if (u_mode == 0) { fragColor = vec4(floor(c * 255.0 + 0.5) / 255.0, 1.0); return; }
|
|
121
|
+
if (u_mode == 2) {
|
|
122
|
+
ivec2 ms = textureSize(u_matrix, 0);
|
|
123
|
+
float v = floor(at(u_matrix, ivec2(P.x % ms.x, P.y % ms.y)).r * 255.0 + 0.5);
|
|
124
|
+
float t = (v + 0.5) / u_levels - 0.5;
|
|
125
|
+
c = clamp(c + t * u_amount, 0.0, 1.0);
|
|
126
|
+
}
|
|
127
|
+
fragColor = vec4(palColor(nearestIndex(c)), 1.0);
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Error diffusion as a wavefront. Iteration r computes every pixel with x + skew*y == r,
|
|
133
|
+
* pulling the stored error of already-computed neighbours.
|
|
134
|
+
* Output: rgb = quantisation error, a = palette index (-1 not yet computed, -2 transparent).
|
|
135
|
+
* iChannel0 = prep, iChannel1 = this pass (previous iteration).
|
|
136
|
+
*/
|
|
137
|
+
export const DIFFUSE = /* glsl */`
|
|
138
|
+
// iteration index, set by ShaderMate for passes with iterations (declared here: a FragCoord.xyz built-in)
|
|
139
|
+
uniform int u_recursion;
|
|
140
|
+
uniform int u_on;
|
|
141
|
+
uniform int u_skew;
|
|
142
|
+
uniform float u_strength;
|
|
143
|
+
uniform int u_kn;
|
|
144
|
+
// push offsets (dx, dy) and weight
|
|
145
|
+
uniform vec3 u_kernel[12];
|
|
146
|
+
|
|
147
|
+
void main() {
|
|
148
|
+
if (u_on == 0) { fragColor = vec4(0.0, 0.0, 0.0, -1.0); return; }
|
|
149
|
+
ivec2 P = topDown();
|
|
150
|
+
int k = P.x + u_skew * P.y;
|
|
151
|
+
int r = u_recursion;
|
|
152
|
+
if (k < r) { fragColor = at(iChannel1, P); return; }
|
|
153
|
+
if (k > r) { fragColor = vec4(0.0, 0.0, 0.0, -1.0); return; }
|
|
154
|
+
vec4 p = at(iChannel0, P);
|
|
155
|
+
if (p.a < 0.5) { fragColor = vec4(0.0, 0.0, 0.0, -2.0); return; }
|
|
156
|
+
vec3 acc = vec3(0.0);
|
|
157
|
+
for (int i = 0; i < 12; i++) {
|
|
158
|
+
if (i >= u_kn) break;
|
|
159
|
+
ivec2 Q = P - ivec2(u_kernel[i].xy);
|
|
160
|
+
if (!inside(Q)) continue;
|
|
161
|
+
vec4 e = at(iChannel1, Q);
|
|
162
|
+
if (e.a >= 0.0) acc += e.rgb * u_kernel[i].z;
|
|
163
|
+
}
|
|
164
|
+
vec3 c = clamp(p.rgb + acc * u_strength, 0.0, 1.0);
|
|
165
|
+
int idx = nearestIndex(c);
|
|
166
|
+
fragColor = vec4(c - palColor(idx), float(idx));
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
/** Pick the ordered result or turn diffusion indices into colours. iChannel0 = quant, iChannel1 = diffuse. */
|
|
171
|
+
export const RESOLVE = /* glsl */`
|
|
172
|
+
uniform int u_on;
|
|
173
|
+
void main() {
|
|
174
|
+
ivec2 P = topDown();
|
|
175
|
+
if (u_on == 0) { fragColor = at(iChannel0, P); return; }
|
|
176
|
+
vec4 d = at(iChannel1, P);
|
|
177
|
+
fragColor = d.a < 0.0 ? vec4(0.0) : vec4(palColor(int(d.a + 0.5)), 1.0);
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
/** Orphan removal. iChannel0 = resolve. */
|
|
182
|
+
export const ORPHAN = /* glsl */`
|
|
183
|
+
uniform int u_on;
|
|
184
|
+
void main() {
|
|
185
|
+
ivec2 P = topDown();
|
|
186
|
+
vec4 me = at(iChannel0, P);
|
|
187
|
+
fragColor = me;
|
|
188
|
+
if (u_on == 0) return;
|
|
189
|
+
int c = colorKey(me);
|
|
190
|
+
ivec2 off[4] = ivec2[4](ivec2(0, -1), ivec2(1, 0), ivec2(0, 1), ivec2(-1, 0));
|
|
191
|
+
int ks[4]; vec4 cs[4]; int n = 0;
|
|
192
|
+
for (int i = 0; i < 4; i++) {
|
|
193
|
+
ivec2 Q = P + off[i];
|
|
194
|
+
if (!inside(Q)) continue;
|
|
195
|
+
cs[n] = at(iChannel0, Q); ks[n] = colorKey(cs[n]);
|
|
196
|
+
if (ks[n] == c) return;
|
|
197
|
+
n++;
|
|
198
|
+
}
|
|
199
|
+
if (n < 2) return;
|
|
200
|
+
int best = 0, bc = 0;
|
|
201
|
+
for (int i = 0; i < 4; i++) {
|
|
202
|
+
if (i >= n) break;
|
|
203
|
+
int m = 0;
|
|
204
|
+
for (int j = 0; j < 4; j++) { if (j >= n) break; if (ks[j] == ks[i]) m++; }
|
|
205
|
+
if (m > bc) { bc = m; best = i; }
|
|
206
|
+
}
|
|
207
|
+
fragColor = cs[best];
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
/** 3×3 majority smoothing. iChannel0 = orphan. */
|
|
212
|
+
export const SMOOTH = /* glsl */`
|
|
213
|
+
uniform int u_on;
|
|
214
|
+
void main() {
|
|
215
|
+
ivec2 P = topDown();
|
|
216
|
+
vec4 me = at(iChannel0, P);
|
|
217
|
+
fragColor = me;
|
|
218
|
+
if (u_on == 0) return;
|
|
219
|
+
int c = colorKey(me);
|
|
220
|
+
ivec2 R = ivec2(u_resolution);
|
|
221
|
+
int ks[9]; vec4 cs[9];
|
|
222
|
+
for (int j = 0; j < 3; j++) for (int i = 0; i < 3; i++) {
|
|
223
|
+
ivec2 Q = clamp(P + ivec2(i - 1, j - 1), ivec2(0), R - 1);
|
|
224
|
+
cs[j * 3 + i] = at(iChannel0, Q); ks[j * 3 + i] = colorKey(cs[j * 3 + i]);
|
|
225
|
+
}
|
|
226
|
+
for (int i = 0; i < 9; i++) {
|
|
227
|
+
if (ks[i] == c) continue;
|
|
228
|
+
int m = 0;
|
|
229
|
+
for (int j = 0; j < 9; j++) if (ks[j] == ks[i]) m++;
|
|
230
|
+
if (m >= 5) { fragColor = cs[i]; return; }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
`;
|
|
234
|
+
|
|
235
|
+
/** Outline, drawn to the canvas. iChannel0 = smooth. */
|
|
236
|
+
export const OUTLINE = /* glsl */`
|
|
237
|
+
// 0 none, 1 outer, 2 inner
|
|
238
|
+
uniform int u_outline;
|
|
239
|
+
uniform int u_corners;
|
|
240
|
+
uniform vec3 u_outlineColor;
|
|
241
|
+
bool opaqueAt(ivec2 q) { return inside(q) && at(iChannel0, q).a > 0.5; }
|
|
242
|
+
void main() {
|
|
243
|
+
ivec2 P = topDown();
|
|
244
|
+
vec4 me = at(iChannel0, P);
|
|
245
|
+
fragColor = me;
|
|
246
|
+
if (u_outline == 0) return;
|
|
247
|
+
bool o = me.a > 0.5;
|
|
248
|
+
ivec2 off[8] = ivec2[8](ivec2(0, -1), ivec2(1, 0), ivec2(0, 1), ivec2(-1, 0), ivec2(-1, -1), ivec2(1, -1), ivec2(1, 1), ivec2(-1, 1));
|
|
249
|
+
int n = u_corners == 1 ? 8 : 4;
|
|
250
|
+
for (int i = 0; i < 8; i++) {
|
|
251
|
+
if (i >= n) break;
|
|
252
|
+
ivec2 Q = P + off[i];
|
|
253
|
+
if (!inside(Q)) continue;
|
|
254
|
+
bool other = opaqueAt(Q);
|
|
255
|
+
if (u_outline == 1 ? (!o && other) : (o && !other)) { fragColor = vec4(u_outlineColor, 1.0); return; }
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
// Built-in palettes. Most come from Lospec (https://lospec.com/palette-list); credit to their authors.
|
|
2
|
-
// The list matches the classic editor plus a few staples.
|
|
3
|
-
|
|
4
|
-
/** @type {{ id: string, name: string, author: string, colors: string[] }[]} */
|
|
5
|
-
export const PALETTES = [
|
|
6
|
-
{ id: "1-bit-monitor", name: "1-Bit Monitor", author: "", colors: ["222323","f0f6f0"] },
|
|
7
|
-
{ id: "apollo", name: "Apollo", author: "AdamCYounis", colors: ["172038","253a5e","3c5e8b","4f8fba","73bed3","a4dddb","19332d","25562e","468232","75a743","a8ca58","d0da91","4d2b32","7a4841","ad7757","c09473","d7b594","e7d5b3","341c27","602c2c","884b2b","be772b","de9e41","e8c170","241527","411d31","752438","a53030","cf573c","da863e","1e1d39","402751","7a367b","a23e8c","c65197","df84a5","090a14","10141f","151d28","202e37","394a50","577277","819796","a8b5b2","c7cfcc","ebede9"] },
|
|
8
|
-
{ id: "batpalette", name: "Batpalette", author: "el Rojaz", colors: ["000000","7f7f7f","ffffff","681e99","3f48cc","88ff82","bfa621","cc3d18"] },
|
|
9
|
-
{ id: "beached-boogaloo", name: "Beached Boogaloo", author: ".tomais", colors: ["ff0b20","8f0434","500037","a0ff00","57e71e","0ea717","057833","2cdeef","2483e7","153ab7","0d0a87","ff0b88","a60fff","6409cf","eaef30","ff880b","b75308","802202","000000","231940","514378","ffffff","73a3a7","366770","f7b771","c77650","974c38","873939","601e2c","300c1f"] },
|
|
10
|
-
{ id: "cc-29", name: "CC-29", author: "Alpha6", colors: ["f2f0e5","b8b5b9","868188","646365","45444f","3a3858","212123","352b42","43436a","4b80ca","68c2d3","a2dcc7","ede19e","d3a068","b45252","6a536e","4b4158","80493a","a77b5b","e5ceb4","c2d368","8ab060","567b79","4e584a","7b7243","b2b47e","edc8c4","cf8acb","5f556a"] },
|
|
11
|
-
{ id: "cga-16", name: "CGA 16", author: "", colors: ["000000","0000aa","00aa00","00aaaa","aa0000","aa00aa","aa5500","aaaaaa","555555","5555ff","55ff55","55ffff","ff5555","ff55ff","ffff55","ffffff"] },
|
|
12
|
-
{ id: "chaotic-neutral", name: "Chaotic neutral", author: "Dodecahedron ⷽ", colors: ["e1d4cb","c19f90","a56457","6d504d","484044","392b29","31312f","d8eef0","97999f","81a4c0","6a7f92","415973","43425a","45334b","4b1f40","671087","955ab9"] },
|
|
13
|
-
{ id: "endesga-32", name: "Endesga 32", author: "ENDESGA", colors: ["be4a2f","d77643","ead4aa","e4a672","b86f50","733e39","3e2731","a22633","e43b44","f77622","feae34","fee761","63c74d","3e8948","265c42","193c3e","124e89","0099db","2ce8f5","ffffff","c0cbdc","8b9bb4","5a6988","3a4466","262b44","181425","ff0044","68386c","b55088","f6757a","e8b796","c28569"] },
|
|
14
|
-
{ id: "fantasy-24", name: "Fantasy 24", author: "", colors: ["1f240a","39571c","a58c27","efac28","efd8a1","ab5c1c","183f39","ef692f","efb775","a56243","773421","724113","2a1d0d","392a1c","684c3c","927e6a","276468","ef3a0c","45230d","3c9f9c","9b1a0a","36170c","550f0a","300f0a"] },
|
|
15
|
-
{ id: "game-boy-dmg", name: "Game Boy (DMG)", author: "", colors: ["0f380f","306230","8bac0f","9bbc0f"] },
|
|
16
|
-
{ id: "grayscale-4", name: "Grayscale 4", author: "", colors: ["000000","555555","aaaaaa","ffffff"] },
|
|
17
|
-
{ id: "hidden-library", name: "Hidden Library", author: "Pixel Hoo", colors: ["100b2a","2a213a","314743","65856d","adca9a","ecfeca","582d27","974133","d67654","5d3441","a3685b","ffb379","ffe6a9","ffffff"] },
|
|
18
|
-
{ id: "intacto14", name: "Intacto14", author: "Anubi", colors: ["221529","4b5561","879ba3","cbf5ef","62b4de","5660d6","5b29a6","d64562","f2735c","edbb8e","ab6375","522152","37ada2","8cde8c"] },
|
|
19
|
-
{ id: "journey", name: "Journey", author: "🍍PineappleOnPizza🍍", colors: ["050914","110524","3b063a","691749","9c3247","d46453","f5a15d","ffcf8e","ff7a7d","ff417d","d61a88","94007a","42004e","220029","100726","25082c","3d1132","73263d","bd4035","ed7b39","ffb84a","fff540","c6d831","77b02a","429058","2c645e","153c4a","052137","0e0421","0c0b42","032769","144491","488bd4","78d7ff","b0fff1","faffff","c7d4e1","928fb8","5b537d","392946","24142c","0e0f2c","132243","1a466b","10908e","28c074","3dff6e","f8ffb8","f0c297","cf968c","8f5765","52294b","0f022e","35003b","64004c","9b0e3e","d41e3c","ed4c40","ff9757","d4662f","9c341a","691b22","450c28","2d002e"] },
|
|
20
|
-
{ id: "lospec500", name: "Lospec500", author: "", colors: ["10121c","2c1e31","6b2643","ac2847","ec273f","94493a","de5d3a","e98537","f3a833","4d3533","6e4c30","a26d3f","ce9248","dab163","e8d282","f7f3b7","1e4044","006554","26854c","5ab552","9de64e","008b8b","62a477","a6cb96","d3eed3","3e3b65","3859b3","3388de","36c5f4","6dead6","5e5b8c","8c78a5","b0a7b8","deceed","9a4d76","c878af","cc99ff","fa6e79","ffa2ac","ffd1d5","f6e8e0","ffffff"] },
|
|
21
|
-
{ id: "nyx8", name: "Nyx8", author: "Javier Guerrero", colors: ["08141e","0f2a3f","20394f","4e495f","816271","997577","c3a38a","f6d6bd"] },
|
|
22
|
-
{ id: "oil-6", name: "Oil 6", author: "", colors: ["fbf5ef","f2d3ab","c69fa5","8b6d9c","494d7e","272744"] },
|
|
23
|
-
{ id: "pico-8", name: "PICO-8", author: "", colors: ["000000","1d2b53","7e2553","008751","ab5236","5f574f","c2c3c7","fff1e8","ff004d","ffa300","ffec27","00e436","29adff","83769c","ff77a8","ffccaa"] },
|
|
24
|
-
{ id: "resurrect-64", name: "Resurrect 64", author: "Kerrie Lake", colors: ["2e222f","3e3546","625565","966c6c","ab947a","694f62","7f708a","9babb2","c7dcd0","ffffff","6e2727","b33831","ea4f36","f57d4a","ae2334","e83b3b","fb6b1d","f79617","f9c22b","7a3045","9e4539","cd683d","e6904e","fbb954","4c3e24","676633","a2a947","d5e04b","fbff86","165a4c","239063","1ebc73","91db69","cddf6c","313638","374e4a","547e64","92a984","b2ba90","0b5e65","0b8a8f","0eaf9b","30e1b9","8ff8e2","323353","484a77","4d65b4","4d9be6","8fd3ff","45293f","6b3e75","905ea9","a884f3","eaaded","753c54","a24b6f","cf657f","ed8099","831c5d","c32454","f04f78","f68181","fca790","fdcbb0"] },
|
|
25
|
-
{ id: "slso8", name: "SLSO8", author: "Luis Miguel Maldonado", colors: ["0d2b45","203c56","544e68","8d697a","d08159","ffaa5e","ffd4a3","ffecd6"] },
|
|
26
|
-
{ id: "sweet-canyon-extended-64", name: "Sweet Canyon Extended 64", author: "Mr Slime", colors: ["0f0e11","2d2c33","40404a","51545c","6b7179","7c8389","a8b2b6","d5d5d5","eeebe0","f1dbb1","eec99f","e1a17e","cc9562","ab7b49","9a643a","86482f","783a29","6a3328","541d29","42192c","512240","782349","8b2e5d","a93e89","d062c8","ec94ea","f2bdfc","eaebff","a2fafa","64e7e7","54cfd8","2fb6c3","2c89af","25739d","2a5684","214574","1f2966","101445","3c0d3b","66164c","901f3d","bb3030","dc473c","ec6a45","fb9b41","f0c04c","f4d66e","fffb76","ccf17a","97d948","6fba3b","229443","1d7e45","116548","0c4f3f","0a3639","251746","48246d","69189c","9f20c0","e527d2","ff51cf","ff7ada","ff9edb"] },
|
|
27
|
-
{ id: "sweetie-16", name: "Sweetie 16", author: "", colors: ["1a1c2c","5d275d","b13e53","ef7d57","ffcd75","a7f070","38b764","257179","29366f","3b5dc9","41a6f6","73eff7","f4f4f4","94b0c2","566c86","333c57"] },
|
|
28
|
-
{ id: "toxic-haze", name: "Toxic Haze", author: "sillyTheJester", colors: ["e5ff00","b8d14a","9ab06e","708763","4c5c52","393e40","242424"] },
|
|
29
|
-
{ id: "vinik24", name: "Vinik24", author: "Vinik", colors: ["000000","6f6776","9a9a97","c5ccb8","8b5580","c38890","a593a5","666092","9a4f50","c28d75","7ca1c0","416aa3","8d6268","be955c","68aca9","387080","6e6962","93a167","6eaa78","557064","9d9f7f","7e9e99","5d6872","433455"] },
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
/** Look a built-in palette up by id or name (case-insensitive). */
|
|
33
|
-
export function getPalette(idOrName) {
|
|
34
|
-
const k = String(idOrName || "").toLowerCase();
|
|
35
|
-
return PALETTES.find(p => p.id === k || p.name.toLowerCase() === k) || null;
|
|
36
|
-
}
|
|
1
|
+
// Built-in palettes. Most come from Lospec (https://lospec.com/palette-list); credit to their authors.
|
|
2
|
+
// The list matches the classic editor plus a few staples.
|
|
3
|
+
|
|
4
|
+
/** @type {{ id: string, name: string, author: string, colors: string[] }[]} */
|
|
5
|
+
export const PALETTES = [
|
|
6
|
+
{ id: "1-bit-monitor", name: "1-Bit Monitor", author: "", colors: ["222323","f0f6f0"] },
|
|
7
|
+
{ id: "apollo", name: "Apollo", author: "AdamCYounis", colors: ["172038","253a5e","3c5e8b","4f8fba","73bed3","a4dddb","19332d","25562e","468232","75a743","a8ca58","d0da91","4d2b32","7a4841","ad7757","c09473","d7b594","e7d5b3","341c27","602c2c","884b2b","be772b","de9e41","e8c170","241527","411d31","752438","a53030","cf573c","da863e","1e1d39","402751","7a367b","a23e8c","c65197","df84a5","090a14","10141f","151d28","202e37","394a50","577277","819796","a8b5b2","c7cfcc","ebede9"] },
|
|
8
|
+
{ id: "batpalette", name: "Batpalette", author: "el Rojaz", colors: ["000000","7f7f7f","ffffff","681e99","3f48cc","88ff82","bfa621","cc3d18"] },
|
|
9
|
+
{ id: "beached-boogaloo", name: "Beached Boogaloo", author: ".tomais", colors: ["ff0b20","8f0434","500037","a0ff00","57e71e","0ea717","057833","2cdeef","2483e7","153ab7","0d0a87","ff0b88","a60fff","6409cf","eaef30","ff880b","b75308","802202","000000","231940","514378","ffffff","73a3a7","366770","f7b771","c77650","974c38","873939","601e2c","300c1f"] },
|
|
10
|
+
{ id: "cc-29", name: "CC-29", author: "Alpha6", colors: ["f2f0e5","b8b5b9","868188","646365","45444f","3a3858","212123","352b42","43436a","4b80ca","68c2d3","a2dcc7","ede19e","d3a068","b45252","6a536e","4b4158","80493a","a77b5b","e5ceb4","c2d368","8ab060","567b79","4e584a","7b7243","b2b47e","edc8c4","cf8acb","5f556a"] },
|
|
11
|
+
{ id: "cga-16", name: "CGA 16", author: "", colors: ["000000","0000aa","00aa00","00aaaa","aa0000","aa00aa","aa5500","aaaaaa","555555","5555ff","55ff55","55ffff","ff5555","ff55ff","ffff55","ffffff"] },
|
|
12
|
+
{ id: "chaotic-neutral", name: "Chaotic neutral", author: "Dodecahedron ⷽ", colors: ["e1d4cb","c19f90","a56457","6d504d","484044","392b29","31312f","d8eef0","97999f","81a4c0","6a7f92","415973","43425a","45334b","4b1f40","671087","955ab9"] },
|
|
13
|
+
{ id: "endesga-32", name: "Endesga 32", author: "ENDESGA", colors: ["be4a2f","d77643","ead4aa","e4a672","b86f50","733e39","3e2731","a22633","e43b44","f77622","feae34","fee761","63c74d","3e8948","265c42","193c3e","124e89","0099db","2ce8f5","ffffff","c0cbdc","8b9bb4","5a6988","3a4466","262b44","181425","ff0044","68386c","b55088","f6757a","e8b796","c28569"] },
|
|
14
|
+
{ id: "fantasy-24", name: "Fantasy 24", author: "", colors: ["1f240a","39571c","a58c27","efac28","efd8a1","ab5c1c","183f39","ef692f","efb775","a56243","773421","724113","2a1d0d","392a1c","684c3c","927e6a","276468","ef3a0c","45230d","3c9f9c","9b1a0a","36170c","550f0a","300f0a"] },
|
|
15
|
+
{ id: "game-boy-dmg", name: "Game Boy (DMG)", author: "", colors: ["0f380f","306230","8bac0f","9bbc0f"] },
|
|
16
|
+
{ id: "grayscale-4", name: "Grayscale 4", author: "", colors: ["000000","555555","aaaaaa","ffffff"] },
|
|
17
|
+
{ id: "hidden-library", name: "Hidden Library", author: "Pixel Hoo", colors: ["100b2a","2a213a","314743","65856d","adca9a","ecfeca","582d27","974133","d67654","5d3441","a3685b","ffb379","ffe6a9","ffffff"] },
|
|
18
|
+
{ id: "intacto14", name: "Intacto14", author: "Anubi", colors: ["221529","4b5561","879ba3","cbf5ef","62b4de","5660d6","5b29a6","d64562","f2735c","edbb8e","ab6375","522152","37ada2","8cde8c"] },
|
|
19
|
+
{ id: "journey", name: "Journey", author: "🍍PineappleOnPizza🍍", colors: ["050914","110524","3b063a","691749","9c3247","d46453","f5a15d","ffcf8e","ff7a7d","ff417d","d61a88","94007a","42004e","220029","100726","25082c","3d1132","73263d","bd4035","ed7b39","ffb84a","fff540","c6d831","77b02a","429058","2c645e","153c4a","052137","0e0421","0c0b42","032769","144491","488bd4","78d7ff","b0fff1","faffff","c7d4e1","928fb8","5b537d","392946","24142c","0e0f2c","132243","1a466b","10908e","28c074","3dff6e","f8ffb8","f0c297","cf968c","8f5765","52294b","0f022e","35003b","64004c","9b0e3e","d41e3c","ed4c40","ff9757","d4662f","9c341a","691b22","450c28","2d002e"] },
|
|
20
|
+
{ id: "lospec500", name: "Lospec500", author: "", colors: ["10121c","2c1e31","6b2643","ac2847","ec273f","94493a","de5d3a","e98537","f3a833","4d3533","6e4c30","a26d3f","ce9248","dab163","e8d282","f7f3b7","1e4044","006554","26854c","5ab552","9de64e","008b8b","62a477","a6cb96","d3eed3","3e3b65","3859b3","3388de","36c5f4","6dead6","5e5b8c","8c78a5","b0a7b8","deceed","9a4d76","c878af","cc99ff","fa6e79","ffa2ac","ffd1d5","f6e8e0","ffffff"] },
|
|
21
|
+
{ id: "nyx8", name: "Nyx8", author: "Javier Guerrero", colors: ["08141e","0f2a3f","20394f","4e495f","816271","997577","c3a38a","f6d6bd"] },
|
|
22
|
+
{ id: "oil-6", name: "Oil 6", author: "", colors: ["fbf5ef","f2d3ab","c69fa5","8b6d9c","494d7e","272744"] },
|
|
23
|
+
{ id: "pico-8", name: "PICO-8", author: "", colors: ["000000","1d2b53","7e2553","008751","ab5236","5f574f","c2c3c7","fff1e8","ff004d","ffa300","ffec27","00e436","29adff","83769c","ff77a8","ffccaa"] },
|
|
24
|
+
{ id: "resurrect-64", name: "Resurrect 64", author: "Kerrie Lake", colors: ["2e222f","3e3546","625565","966c6c","ab947a","694f62","7f708a","9babb2","c7dcd0","ffffff","6e2727","b33831","ea4f36","f57d4a","ae2334","e83b3b","fb6b1d","f79617","f9c22b","7a3045","9e4539","cd683d","e6904e","fbb954","4c3e24","676633","a2a947","d5e04b","fbff86","165a4c","239063","1ebc73","91db69","cddf6c","313638","374e4a","547e64","92a984","b2ba90","0b5e65","0b8a8f","0eaf9b","30e1b9","8ff8e2","323353","484a77","4d65b4","4d9be6","8fd3ff","45293f","6b3e75","905ea9","a884f3","eaaded","753c54","a24b6f","cf657f","ed8099","831c5d","c32454","f04f78","f68181","fca790","fdcbb0"] },
|
|
25
|
+
{ id: "slso8", name: "SLSO8", author: "Luis Miguel Maldonado", colors: ["0d2b45","203c56","544e68","8d697a","d08159","ffaa5e","ffd4a3","ffecd6"] },
|
|
26
|
+
{ id: "sweet-canyon-extended-64", name: "Sweet Canyon Extended 64", author: "Mr Slime", colors: ["0f0e11","2d2c33","40404a","51545c","6b7179","7c8389","a8b2b6","d5d5d5","eeebe0","f1dbb1","eec99f","e1a17e","cc9562","ab7b49","9a643a","86482f","783a29","6a3328","541d29","42192c","512240","782349","8b2e5d","a93e89","d062c8","ec94ea","f2bdfc","eaebff","a2fafa","64e7e7","54cfd8","2fb6c3","2c89af","25739d","2a5684","214574","1f2966","101445","3c0d3b","66164c","901f3d","bb3030","dc473c","ec6a45","fb9b41","f0c04c","f4d66e","fffb76","ccf17a","97d948","6fba3b","229443","1d7e45","116548","0c4f3f","0a3639","251746","48246d","69189c","9f20c0","e527d2","ff51cf","ff7ada","ff9edb"] },
|
|
27
|
+
{ id: "sweetie-16", name: "Sweetie 16", author: "", colors: ["1a1c2c","5d275d","b13e53","ef7d57","ffcd75","a7f070","38b764","257179","29366f","3b5dc9","41a6f6","73eff7","f4f4f4","94b0c2","566c86","333c57"] },
|
|
28
|
+
{ id: "toxic-haze", name: "Toxic Haze", author: "sillyTheJester", colors: ["e5ff00","b8d14a","9ab06e","708763","4c5c52","393e40","242424"] },
|
|
29
|
+
{ id: "vinik24", name: "Vinik24", author: "Vinik", colors: ["000000","6f6776","9a9a97","c5ccb8","8b5580","c38890","a593a5","666092","9a4f50","c28d75","7ca1c0","416aa3","8d6268","be955c","68aca9","387080","6e6962","93a167","6eaa78","557064","9d9f7f","7e9e99","5d6872","433455"] },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/** Look a built-in palette up by id or name (case-insensitive). */
|
|
33
|
+
export function getPalette(idOrName) {
|
|
34
|
+
const k = String(idOrName || "").toLowerCase();
|
|
35
|
+
return PALETTES.find(p => p.id === k || p.name.toLowerCase() === k) || null;
|
|
36
|
+
}
|