@lacspace/image 1.0.0 → 1.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/README.md +18 -0
- package/dist/index.cjs +116 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -9
- package/dist/index.d.ts +47 -9
- package/dist/index.js +114 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,20 @@ console.log(formatBytes(r.size), "at q", r.quality, `${r.width}×${r.height}`);
|
|
|
58
58
|
|
|
59
59
|
> It's a best-effort **ceiling** — the closest fit ≤ your budget within the quality/scale bounds, never padded up to it.
|
|
60
60
|
|
|
61
|
+
## 🆕 Generators (v1.1.0)
|
|
62
|
+
|
|
63
|
+
Deterministic, no-AI building blocks — same input, same image:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { identicon, mesh, placeholder, encode } from "@lacspace/image";
|
|
67
|
+
|
|
68
|
+
const avatar = identicon("ada@lacspace.com", { size: 240 }); // symmetric avatar from any string
|
|
69
|
+
const bg = mesh(1200, 630, { seed: "brand" }); // smooth multi-point mesh gradient
|
|
70
|
+
const lqip = placeholder(600, 315, { seed: "hero-1" }); // tasteful seeded gradient placeholder
|
|
71
|
+
|
|
72
|
+
const png = await encode(avatar, { format: "png" });
|
|
73
|
+
```
|
|
74
|
+
|
|
61
75
|
## The surface API
|
|
62
76
|
|
|
63
77
|
`Surface` is a plain RGBA pixel buffer with a small, chainable drawing API. Everything is deterministic and identical across Node and the browser.
|
|
@@ -129,6 +143,10 @@ formatBytes(1536); // "1.5 KB"
|
|
|
129
143
|
- `rasterizeSvg(svg, { width?, height?, background? })`
|
|
130
144
|
- `Surface`, `gradient`, `radial`, `pattern`, `parseColor`, `parseSize`, `formatBytes`, `hasCanvas`, `canvasSupports`
|
|
131
145
|
|
|
146
|
+
## Contributing 💙
|
|
147
|
+
|
|
148
|
+
New patterns, generators and encoders are welcome — see [CONTRIBUTING.md](https://github.com/lacspace/npm-packages/blob/main/CONTRIBUTING.md). Open an issue or PR; first-time contributors welcome.
|
|
149
|
+
|
|
132
150
|
## License
|
|
133
151
|
|
|
134
152
|
[Lacspace Free Licence v1.0](https://github.com/lacspace/npm-packages/blob/main/LICENSE) — free to use, permissive, Lacspace-branded.
|
package/dist/index.cjs
CHANGED
|
@@ -369,6 +369,119 @@ function pattern(width, height, base, kind, opts) {
|
|
|
369
369
|
return new Surface(width, height).fill(base).pattern(kind, opts);
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
// src/generators.ts
|
|
373
|
+
function hash32(str) {
|
|
374
|
+
let h = 2166136261 >>> 0;
|
|
375
|
+
for (let i = 0; i < str.length; i++) {
|
|
376
|
+
h ^= str.charCodeAt(i);
|
|
377
|
+
h = Math.imul(h, 16777619);
|
|
378
|
+
}
|
|
379
|
+
return h >>> 0;
|
|
380
|
+
}
|
|
381
|
+
function rng2(seed) {
|
|
382
|
+
let s = seed >>> 0 || 1;
|
|
383
|
+
return () => {
|
|
384
|
+
s |= 0;
|
|
385
|
+
s = s + 1831565813 | 0;
|
|
386
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
387
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
388
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function hsl(h, s, l) {
|
|
392
|
+
h = (h % 360 + 360) % 360;
|
|
393
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
394
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
395
|
+
const m = l - c / 2;
|
|
396
|
+
let r = 0, g = 0, b = 0;
|
|
397
|
+
if (h < 60) [r, g, b] = [c, x, 0];
|
|
398
|
+
else if (h < 120) [r, g, b] = [x, c, 0];
|
|
399
|
+
else if (h < 180) [r, g, b] = [0, c, x];
|
|
400
|
+
else if (h < 240) [r, g, b] = [0, x, c];
|
|
401
|
+
else if (h < 300) [r, g, b] = [x, 0, c];
|
|
402
|
+
else [r, g, b] = [c, 0, x];
|
|
403
|
+
return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255), 255];
|
|
404
|
+
}
|
|
405
|
+
function identicon(seed, opts = {}) {
|
|
406
|
+
const size = opts.size ?? 240;
|
|
407
|
+
const grid = Math.max(3, (opts.grid ?? 5) | 1);
|
|
408
|
+
const h = hash32(seed);
|
|
409
|
+
const rand = rng2(h);
|
|
410
|
+
const fgRgba = hsl(h % 360, 0.62, 0.58);
|
|
411
|
+
const fg = opts.color ?? `rgb(${fgRgba[0]},${fgRgba[1]},${fgRgba[2]})`;
|
|
412
|
+
const s = new Surface(size, size).fill(opts.background ?? "#0f1117");
|
|
413
|
+
const pad = Math.round(size * (opts.padding ?? 0.12));
|
|
414
|
+
const cell = (size - pad * 2) / grid;
|
|
415
|
+
const half = Math.ceil(grid / 2);
|
|
416
|
+
for (let col = 0; col < half; col++) {
|
|
417
|
+
for (let row = 0; row < grid; row++) {
|
|
418
|
+
if (rand() > 0.5) {
|
|
419
|
+
const mirror = grid - 1 - col;
|
|
420
|
+
const y = pad + row * cell;
|
|
421
|
+
s.rect(pad + col * cell, y, Math.ceil(cell), Math.ceil(cell), fg);
|
|
422
|
+
if (mirror !== col) s.rect(pad + mirror * cell, y, Math.ceil(cell), Math.ceil(cell), fg);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return s;
|
|
427
|
+
}
|
|
428
|
+
var DEFAULT_MESH = ["#0BB9D9", "#3B82F6", "#7C3AED", "#EC4899", "#22D3EE"];
|
|
429
|
+
function mesh(width, height, opts = {}) {
|
|
430
|
+
const palette = (opts.colors ?? DEFAULT_MESH).map(parseColor);
|
|
431
|
+
const n = Math.max(2, opts.points ?? palette.length);
|
|
432
|
+
const seed = typeof opts.seed === "number" ? opts.seed : hash32(String(opts.seed ?? "mesh"));
|
|
433
|
+
const rand = rng2(seed);
|
|
434
|
+
const pts = Array.from({ length: n }, (_, i) => ({
|
|
435
|
+
x: rand() * width,
|
|
436
|
+
y: rand() * height,
|
|
437
|
+
c: palette[i % palette.length]
|
|
438
|
+
}));
|
|
439
|
+
const out = new Surface(width, height);
|
|
440
|
+
const d = out.data;
|
|
441
|
+
for (let y = 0; y < height; y++) {
|
|
442
|
+
for (let x = 0; x < width; x++) {
|
|
443
|
+
let wr = 0, wg = 0, wb = 0, wsum = 0;
|
|
444
|
+
for (const p of pts) {
|
|
445
|
+
const dx = x - p.x;
|
|
446
|
+
const dy = y - p.y;
|
|
447
|
+
const w = 1 / (dx * dx + dy * dy + 800);
|
|
448
|
+
wr += p.c[0] * w;
|
|
449
|
+
wg += p.c[1] * w;
|
|
450
|
+
wb += p.c[2] * w;
|
|
451
|
+
wsum += w;
|
|
452
|
+
}
|
|
453
|
+
const i = (y * width + x) * 4;
|
|
454
|
+
d[i] = wr / wsum;
|
|
455
|
+
d[i + 1] = wg / wsum;
|
|
456
|
+
d[i + 2] = wb / wsum;
|
|
457
|
+
d[i + 3] = 255;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
function placeholder(width, height, opts = {}) {
|
|
463
|
+
const seed = typeof opts.seed === "number" ? opts.seed : hash32(String(opts.seed ?? `${width}x${height}`));
|
|
464
|
+
const rand = rng2(seed);
|
|
465
|
+
let from, to;
|
|
466
|
+
if (opts.colors) {
|
|
467
|
+
[from, to] = [parseColor(opts.colors[0]), parseColor(opts.colors[1])];
|
|
468
|
+
} else {
|
|
469
|
+
const base = Math.floor(rand() * 360);
|
|
470
|
+
from = hsl(base, 0.55, 0.55);
|
|
471
|
+
to = hsl(base + 40 + rand() * 60, 0.55, 0.45);
|
|
472
|
+
}
|
|
473
|
+
const angle = Math.floor(rand() * 180);
|
|
474
|
+
const s = new Surface(width, height).linearGradient({
|
|
475
|
+
angle,
|
|
476
|
+
stops: [
|
|
477
|
+
{ offset: 0, color: `rgb(${from[0]},${from[1]},${from[2]})` },
|
|
478
|
+
{ offset: 1, color: `rgb(${to[0]},${to[1]},${to[2]})` }
|
|
479
|
+
]
|
|
480
|
+
});
|
|
481
|
+
if (opts.texture !== false) s.pattern("dots", { size: Math.max(20, Math.round(width / 24)), color: "rgba(255,255,255,0.08)" });
|
|
482
|
+
return s;
|
|
483
|
+
}
|
|
484
|
+
|
|
372
485
|
// src/env.ts
|
|
373
486
|
function hasCanvas() {
|
|
374
487
|
if (typeof OffscreenCanvas !== "undefined") return true;
|
|
@@ -1589,9 +1702,12 @@ exports.fit = fit;
|
|
|
1589
1702
|
exports.formatBytes = formatBytes;
|
|
1590
1703
|
exports.gradient = gradient;
|
|
1591
1704
|
exports.hasCanvas = hasCanvas;
|
|
1705
|
+
exports.identicon = identicon;
|
|
1706
|
+
exports.mesh = mesh;
|
|
1592
1707
|
exports.parseColor = parseColor;
|
|
1593
1708
|
exports.parseSize = parseSize;
|
|
1594
1709
|
exports.pattern = pattern;
|
|
1710
|
+
exports.placeholder = placeholder;
|
|
1595
1711
|
exports.radial = radial;
|
|
1596
1712
|
exports.rasterizeSvg = rasterizeSvg;
|
|
1597
1713
|
//# sourceMappingURL=index.cjs.map
|