@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/dist/index.d.cts CHANGED
@@ -136,6 +136,52 @@ declare function radial(width: number, height: number, opts: RadialGradientOptio
136
136
  /** Convenience: a solid surface with a pattern painted over it. */
137
137
  declare function pattern(width: number, height: number, base: Color, kind: PatternKind, opts?: PatternOptions): Surface;
138
138
 
139
+ /**
140
+ * Tiny, dependency-free colour parser → RGBA tuple (0..255 each).
141
+ * Supports: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb()/rgba(), and "transparent".
142
+ */
143
+
144
+ type RGBA = [number, number, number, number];
145
+ declare function parseColor(input: Color): RGBA;
146
+
147
+ /**
148
+ * Deterministic, no-AI image generators built on Surface:
149
+ * - identicon(): a symmetric avatar from any string (GitHub-style)
150
+ * - mesh(): a smooth multi-point mesh gradient
151
+ * - placeholder(): a tasteful gradient placeholder / LQIP from a seed
152
+ * All are seeded, so the same input always yields the same image.
153
+ */
154
+
155
+ interface IdenticonOptions {
156
+ size?: number;
157
+ /** Odd number of cells across (default 5). */
158
+ grid?: number;
159
+ background?: Color;
160
+ /** Force the foreground colour; default derived from the seed. */
161
+ color?: Color;
162
+ /** Cell padding fraction (default 0.12 of the size). */
163
+ padding?: number;
164
+ }
165
+ /** A symmetric identicon avatar from any string. */
166
+ declare function identicon(seed: string, opts?: IdenticonOptions): Surface;
167
+ interface MeshOptions {
168
+ colors?: Color[];
169
+ /** Number of colour points (default = colors.length or 5). */
170
+ points?: number;
171
+ seed?: string | number;
172
+ }
173
+ /** A smooth multi-point mesh gradient (inverse-distance blend of seeded points). */
174
+ declare function mesh(width: number, height: number, opts?: MeshOptions): Surface;
175
+ interface PlaceholderOptions {
176
+ seed?: string | number;
177
+ /** Force the two gradient endpoints instead of deriving from the seed. */
178
+ colors?: [Color, Color];
179
+ /** Add a faint dot grid over the gradient (default true). */
180
+ texture?: boolean;
181
+ }
182
+ /** A tasteful, seeded gradient placeholder (great as an LQIP or empty state). */
183
+ declare function placeholder(width: number, height: number, opts?: PlaceholderOptions): Surface;
184
+
139
185
  /**
140
186
  * Unified, isomorphic encode(): pick the best available encoder for the format
141
187
  * and runtime. Browser/Worker → native Canvas (all formats). Node → pure-JS PNG
@@ -218,12 +264,4 @@ declare function hasCanvas(): boolean;
218
264
  /** True where the platform can actually emit this format via canvas. */
219
265
  declare function canvasSupports(format: ImageFormat): Promise<boolean>;
220
266
 
221
- /**
222
- * Tiny, dependency-free colour parser → RGBA tuple (0..255 each).
223
- * Supports: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb()/rgba(), and "transparent".
224
- */
225
-
226
- type RGBA = [number, number, number, number];
227
- declare function parseColor(input: Color): RGBA;
228
-
229
- export { type Color, type DrawImageOptions, type EncodeOptions, type EncodeResult, type Fit, type FitOptions, type GradientStop, type ImageFormat, type LinearGradientOptions, type PatternKind, type PatternOptions, type PixelSource, type RGBA, type RadialGradientOptions, type RasterizeSvgOptions, Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, parseColor, parseSize, pattern, radial, rasterizeSvg };
267
+ export { type Color, type DrawImageOptions, type EncodeOptions, type EncodeResult, type Fit, type FitOptions, type GradientStop, type IdenticonOptions, type ImageFormat, type LinearGradientOptions, type MeshOptions, type PatternKind, type PatternOptions, type PixelSource, type PlaceholderOptions, type RGBA, type RadialGradientOptions, type RasterizeSvgOptions, Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, identicon, mesh, parseColor, parseSize, pattern, placeholder, radial, rasterizeSvg };
package/dist/index.d.ts CHANGED
@@ -136,6 +136,52 @@ declare function radial(width: number, height: number, opts: RadialGradientOptio
136
136
  /** Convenience: a solid surface with a pattern painted over it. */
137
137
  declare function pattern(width: number, height: number, base: Color, kind: PatternKind, opts?: PatternOptions): Surface;
138
138
 
139
+ /**
140
+ * Tiny, dependency-free colour parser → RGBA tuple (0..255 each).
141
+ * Supports: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb()/rgba(), and "transparent".
142
+ */
143
+
144
+ type RGBA = [number, number, number, number];
145
+ declare function parseColor(input: Color): RGBA;
146
+
147
+ /**
148
+ * Deterministic, no-AI image generators built on Surface:
149
+ * - identicon(): a symmetric avatar from any string (GitHub-style)
150
+ * - mesh(): a smooth multi-point mesh gradient
151
+ * - placeholder(): a tasteful gradient placeholder / LQIP from a seed
152
+ * All are seeded, so the same input always yields the same image.
153
+ */
154
+
155
+ interface IdenticonOptions {
156
+ size?: number;
157
+ /** Odd number of cells across (default 5). */
158
+ grid?: number;
159
+ background?: Color;
160
+ /** Force the foreground colour; default derived from the seed. */
161
+ color?: Color;
162
+ /** Cell padding fraction (default 0.12 of the size). */
163
+ padding?: number;
164
+ }
165
+ /** A symmetric identicon avatar from any string. */
166
+ declare function identicon(seed: string, opts?: IdenticonOptions): Surface;
167
+ interface MeshOptions {
168
+ colors?: Color[];
169
+ /** Number of colour points (default = colors.length or 5). */
170
+ points?: number;
171
+ seed?: string | number;
172
+ }
173
+ /** A smooth multi-point mesh gradient (inverse-distance blend of seeded points). */
174
+ declare function mesh(width: number, height: number, opts?: MeshOptions): Surface;
175
+ interface PlaceholderOptions {
176
+ seed?: string | number;
177
+ /** Force the two gradient endpoints instead of deriving from the seed. */
178
+ colors?: [Color, Color];
179
+ /** Add a faint dot grid over the gradient (default true). */
180
+ texture?: boolean;
181
+ }
182
+ /** A tasteful, seeded gradient placeholder (great as an LQIP or empty state). */
183
+ declare function placeholder(width: number, height: number, opts?: PlaceholderOptions): Surface;
184
+
139
185
  /**
140
186
  * Unified, isomorphic encode(): pick the best available encoder for the format
141
187
  * and runtime. Browser/Worker → native Canvas (all formats). Node → pure-JS PNG
@@ -218,12 +264,4 @@ declare function hasCanvas(): boolean;
218
264
  /** True where the platform can actually emit this format via canvas. */
219
265
  declare function canvasSupports(format: ImageFormat): Promise<boolean>;
220
266
 
221
- /**
222
- * Tiny, dependency-free colour parser → RGBA tuple (0..255 each).
223
- * Supports: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb()/rgba(), and "transparent".
224
- */
225
-
226
- type RGBA = [number, number, number, number];
227
- declare function parseColor(input: Color): RGBA;
228
-
229
- export { type Color, type DrawImageOptions, type EncodeOptions, type EncodeResult, type Fit, type FitOptions, type GradientStop, type ImageFormat, type LinearGradientOptions, type PatternKind, type PatternOptions, type PixelSource, type RGBA, type RadialGradientOptions, type RasterizeSvgOptions, Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, parseColor, parseSize, pattern, radial, rasterizeSvg };
267
+ export { type Color, type DrawImageOptions, type EncodeOptions, type EncodeResult, type Fit, type FitOptions, type GradientStop, type IdenticonOptions, type ImageFormat, type LinearGradientOptions, type MeshOptions, type PatternKind, type PatternOptions, type PixelSource, type PlaceholderOptions, type RGBA, type RadialGradientOptions, type RasterizeSvgOptions, Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, identicon, mesh, parseColor, parseSize, pattern, placeholder, radial, rasterizeSvg };
package/dist/index.js CHANGED
@@ -367,6 +367,119 @@ function pattern(width, height, base, kind, opts) {
367
367
  return new Surface(width, height).fill(base).pattern(kind, opts);
368
368
  }
369
369
 
370
+ // src/generators.ts
371
+ function hash32(str) {
372
+ let h = 2166136261 >>> 0;
373
+ for (let i = 0; i < str.length; i++) {
374
+ h ^= str.charCodeAt(i);
375
+ h = Math.imul(h, 16777619);
376
+ }
377
+ return h >>> 0;
378
+ }
379
+ function rng2(seed) {
380
+ let s = seed >>> 0 || 1;
381
+ return () => {
382
+ s |= 0;
383
+ s = s + 1831565813 | 0;
384
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
385
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
386
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
387
+ };
388
+ }
389
+ function hsl(h, s, l) {
390
+ h = (h % 360 + 360) % 360;
391
+ const c = (1 - Math.abs(2 * l - 1)) * s;
392
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
393
+ const m = l - c / 2;
394
+ let r = 0, g = 0, b = 0;
395
+ if (h < 60) [r, g, b] = [c, x, 0];
396
+ else if (h < 120) [r, g, b] = [x, c, 0];
397
+ else if (h < 180) [r, g, b] = [0, c, x];
398
+ else if (h < 240) [r, g, b] = [0, x, c];
399
+ else if (h < 300) [r, g, b] = [x, 0, c];
400
+ else [r, g, b] = [c, 0, x];
401
+ return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255), 255];
402
+ }
403
+ function identicon(seed, opts = {}) {
404
+ const size = opts.size ?? 240;
405
+ const grid = Math.max(3, (opts.grid ?? 5) | 1);
406
+ const h = hash32(seed);
407
+ const rand = rng2(h);
408
+ const fgRgba = hsl(h % 360, 0.62, 0.58);
409
+ const fg = opts.color ?? `rgb(${fgRgba[0]},${fgRgba[1]},${fgRgba[2]})`;
410
+ const s = new Surface(size, size).fill(opts.background ?? "#0f1117");
411
+ const pad = Math.round(size * (opts.padding ?? 0.12));
412
+ const cell = (size - pad * 2) / grid;
413
+ const half = Math.ceil(grid / 2);
414
+ for (let col = 0; col < half; col++) {
415
+ for (let row = 0; row < grid; row++) {
416
+ if (rand() > 0.5) {
417
+ const mirror = grid - 1 - col;
418
+ const y = pad + row * cell;
419
+ s.rect(pad + col * cell, y, Math.ceil(cell), Math.ceil(cell), fg);
420
+ if (mirror !== col) s.rect(pad + mirror * cell, y, Math.ceil(cell), Math.ceil(cell), fg);
421
+ }
422
+ }
423
+ }
424
+ return s;
425
+ }
426
+ var DEFAULT_MESH = ["#0BB9D9", "#3B82F6", "#7C3AED", "#EC4899", "#22D3EE"];
427
+ function mesh(width, height, opts = {}) {
428
+ const palette = (opts.colors ?? DEFAULT_MESH).map(parseColor);
429
+ const n = Math.max(2, opts.points ?? palette.length);
430
+ const seed = typeof opts.seed === "number" ? opts.seed : hash32(String(opts.seed ?? "mesh"));
431
+ const rand = rng2(seed);
432
+ const pts = Array.from({ length: n }, (_, i) => ({
433
+ x: rand() * width,
434
+ y: rand() * height,
435
+ c: palette[i % palette.length]
436
+ }));
437
+ const out = new Surface(width, height);
438
+ const d = out.data;
439
+ for (let y = 0; y < height; y++) {
440
+ for (let x = 0; x < width; x++) {
441
+ let wr = 0, wg = 0, wb = 0, wsum = 0;
442
+ for (const p of pts) {
443
+ const dx = x - p.x;
444
+ const dy = y - p.y;
445
+ const w = 1 / (dx * dx + dy * dy + 800);
446
+ wr += p.c[0] * w;
447
+ wg += p.c[1] * w;
448
+ wb += p.c[2] * w;
449
+ wsum += w;
450
+ }
451
+ const i = (y * width + x) * 4;
452
+ d[i] = wr / wsum;
453
+ d[i + 1] = wg / wsum;
454
+ d[i + 2] = wb / wsum;
455
+ d[i + 3] = 255;
456
+ }
457
+ }
458
+ return out;
459
+ }
460
+ function placeholder(width, height, opts = {}) {
461
+ const seed = typeof opts.seed === "number" ? opts.seed : hash32(String(opts.seed ?? `${width}x${height}`));
462
+ const rand = rng2(seed);
463
+ let from, to;
464
+ if (opts.colors) {
465
+ [from, to] = [parseColor(opts.colors[0]), parseColor(opts.colors[1])];
466
+ } else {
467
+ const base = Math.floor(rand() * 360);
468
+ from = hsl(base, 0.55, 0.55);
469
+ to = hsl(base + 40 + rand() * 60, 0.55, 0.45);
470
+ }
471
+ const angle = Math.floor(rand() * 180);
472
+ const s = new Surface(width, height).linearGradient({
473
+ angle,
474
+ stops: [
475
+ { offset: 0, color: `rgb(${from[0]},${from[1]},${from[2]})` },
476
+ { offset: 1, color: `rgb(${to[0]},${to[1]},${to[2]})` }
477
+ ]
478
+ });
479
+ if (opts.texture !== false) s.pattern("dots", { size: Math.max(20, Math.round(width / 24)), color: "rgba(255,255,255,0.08)" });
480
+ return s;
481
+ }
482
+
370
483
  // src/env.ts
371
484
  function hasCanvas() {
372
485
  if (typeof OffscreenCanvas !== "undefined") return true;
@@ -1577,6 +1690,6 @@ async function rasterizeSvg(svg, opts = {}) {
1577
1690
  return svgToSurfaceNode(svg, opts);
1578
1691
  }
1579
1692
 
1580
- export { Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, parseColor, parseSize, pattern, radial, rasterizeSvg };
1693
+ export { Surface, canvasSupports, encode, encodeJpeg, encodePng, encodePngSync, fit, formatBytes, gradient, hasCanvas, identicon, mesh, parseColor, parseSize, pattern, placeholder, radial, rasterizeSvg };
1581
1694
  //# sourceMappingURL=index.js.map
1582
1695
  //# sourceMappingURL=index.js.map