@lacspace/image 1.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/LICENSE ADDED
@@ -0,0 +1,51 @@
1
+ Lacspace Free Licence
2
+ Version 1.0, August 2026
3
+
4
+ Copyright (c) 2026 Lacspace
5
+
6
+ PREAMBLE
7
+
8
+ This software is published by Lacspace under the Lacspace Free Licence — a free,
9
+ permissive licence that lets you use this software for any purpose, including in
10
+ commercial products and services, at no cost. It grants the same freedoms as
11
+ common permissive open-source licences; the only condition is that this notice
12
+ travels with the software. The canonical, always-current text of this licence is
13
+ maintained at https://lacspace.com/licenses/lacspace-free-1.0
14
+
15
+ GRANT OF RIGHTS
16
+
17
+ Permission is hereby granted, free of charge, to any person or organisation
18
+ obtaining a copy of this software and its associated documentation and data files
19
+ (the "Software"), to deal in the Software without restriction, including without
20
+ limitation the rights to use, copy, modify, merge, publish, distribute,
21
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the
22
+ Software is furnished to do so, subject to the conditions below. These rights are
23
+ granted for any purpose, personal or commercial, and are perpetual, worldwide,
24
+ non-exclusive, and royalty-free.
25
+
26
+ CONDITIONS
27
+
28
+ The above copyright notice, this permission notice, and the name of this licence
29
+ ("Lacspace Free Licence") shall be included in all copies or substantial portions
30
+ of the Software.
31
+
32
+ TRADEMARKS
33
+
34
+ This licence does not grant permission to use the trade names, trademarks, service
35
+ marks, logos, or product names of Lacspace, except as required to reproduce the
36
+ notice above or to describe the origin of the Software in a truthful manner.
37
+
38
+ DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42
+ FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
43
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
44
+ AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
45
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
+
47
+ ---
48
+
49
+ The Lacspace Free Licence is a source-available, permissive licence and is not (as
50
+ of this version) an OSI-approved licence. In substance it grants the same freedoms
51
+ as the MIT Licence. Learn more at https://lacspace.com/licenses
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ <div align="center">
2
+
3
+ # @lacspace/image
4
+
5
+ **Generate images without AI — and hit an exact file-size budget.**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@lacspace/image?color=%2316a34a&label=npm)](https://www.npmjs.com/package/@lacspace/image)
8
+ [![install size](https://packagephobia.com/badge?p=@lacspace/image)](https://packagephobia.com/result?p=@lacspace/image)
9
+ [![minzipped](https://img.shields.io/bundlephobia/minzip/@lacspace/image?label=minzip)](https://bundlephobia.com/package/@lacspace/image)
10
+ [![types](https://img.shields.io/badge/types-included-blue)](https://www.npmjs.com/package/@lacspace/image)
11
+ [![license](https://img.shields.io/npm/l/@lacspace/image?color=green)](https://github.com/lacspace/npm-packages/blob/main/LICENSE)
12
+
13
+ </div>
14
+
15
+ > Draw gradients, patterns and backgrounds, resize and crop, then export to **PNG · JPEG · WebP · SVG** — at the **dimensions and file size you need** (`"120kb"`, `"1.5mb"`, …). No AI, no cloud, no keys. Deterministic and reproducible.
16
+
17
+ **Zero-dependency and isomorphic.** In the browser (or a Worker) it uses the native **Canvas** for all formats. In **Node** it ships its own **pure-JS PNG** (via built-in `zlib`) and **baseline JPEG** encoders — nothing to install. An optional `sharp` peer unlocks SVG→raster on the server.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm i @lacspace/image
23
+ ```
24
+
25
+ ## Draw something and export it
26
+
27
+ ```ts
28
+ import { gradient, encode, formatBytes } from "@lacspace/image";
29
+
30
+ const card = gradient(1200, 630, {
31
+ angle: 60,
32
+ stops: [
33
+ { offset: 0, color: "#0BB9D9" },
34
+ { offset: 0.5, color: "#3B82F6" },
35
+ { offset: 1, color: "#7C3AED" },
36
+ ],
37
+ }).pattern("dots", { size: 40, color: "rgba(255,255,255,0.1)" });
38
+
39
+ const { bytes, size } = await encode(card, { format: "png" });
40
+ console.log("wrote", formatBytes(size)); // wrote 86.2 KB
41
+ ```
42
+
43
+ ## Hit a file-size budget
44
+
45
+ `fit()` gets you **at or under** a ceiling. Lossy formats binary-search the quality knob; if that isn't enough it downscales. Lossless PNG downscales toward the target.
46
+
47
+ ```ts
48
+ import { gradient, fit, formatBytes } from "@lacspace/image";
49
+
50
+ const img = gradient(1200, 630, { stops: [
51
+ { offset: 0, color: "#ff5f6d" }, { offset: 1, color: "#00c9a7" },
52
+ ]});
53
+
54
+ const r = await fit(img, { format: "jpeg", maxSize: "25kb" });
55
+ console.log(formatBytes(r.size), "at q", r.quality, `${r.width}×${r.height}`);
56
+ // 24.8 KB at q 55 1020×536 (quality + auto-resize to land under 25 KB)
57
+ ```
58
+
59
+ > It's a best-effort **ceiling** — the closest fit ≤ your budget within the quality/scale bounds, never padded up to it.
60
+
61
+ ## The surface API
62
+
63
+ `Surface` is a plain RGBA pixel buffer with a small, chainable drawing API. Everything is deterministic and identical across Node and the browser.
64
+
65
+ ```ts
66
+ import { Surface } from "@lacspace/image";
67
+
68
+ const s = new Surface(800, 400)
69
+ .fill("#0b1020")
70
+ .radialGradient({ cx: 0.5, cy: 0.3, radius: 0.9, stops: [
71
+ { offset: 0, color: "#1e3a8a" }, { offset: 1, color: "#0b1020" },
72
+ ]})
73
+ .pattern("grid", { size: 32, color: "rgba(255,255,255,0.06)" })
74
+ .rect(40, 40, 200, 80, "rgba(255,255,255,0.9)");
75
+
76
+ const thumb = s.resize(400, 200); // bilinear, returns a new Surface
77
+ const square = s.crop(0, 0, 400, 400); // returns a new Surface
78
+ ```
79
+
80
+ | Method | What it does |
81
+ | --- | --- |
82
+ | `.fill(color)` | Solid fill (replaces) |
83
+ | `.rect(x, y, w, h, color)` | Blend a rectangle |
84
+ | `.linearGradient({ angle, stops })` | Angled gradient |
85
+ | `.radialGradient({ cx, cy, radius, stops })` | Radial gradient |
86
+ | `.pattern(kind, opts)` | `checker` · `grid` · `dots` · `stripes` · `noise` |
87
+ | `.drawImage(src, opts)` | Place another source with `cover`/`contain`/`fill` |
88
+ | `.resize(w, h)` · `.crop(x, y, w, h)` | New resampled / cropped surface |
89
+ | `.flatten(bg)` | Composite transparency onto a background |
90
+
91
+ Convenience builders: `gradient(w, h, opts)`, `radial(w, h, opts)`, `pattern(w, h, base, kind, opts)`.
92
+
93
+ ## SVG → raster (pairs with `@lacspace/og`)
94
+
95
+ Turn any SVG string (for example the zero-dep card from [`@lacspace/og`](https://www.npmjs.com/package/@lacspace/og)) into a raster surface, then export or budget it.
96
+
97
+ ```ts
98
+ import { rasterizeSvg, fit } from "@lacspace/image";
99
+
100
+ const surface = await rasterizeSvg(svgString, { width: 1200, height: 630 });
101
+ const og = await fit(surface, { format: "jpeg", maxSize: "200kb" });
102
+ ```
103
+
104
+ - **Browser:** uses the native Canvas — zero extra deps.
105
+ - **Node:** uses the optional `sharp` peer if installed; otherwise throws a clear message (rich SVG text needs a real renderer). `npm i sharp` to enable it server-side.
106
+
107
+ ## Formats & runtimes
108
+
109
+ | Format | Browser / Worker | Node |
110
+ | --- | --- | --- |
111
+ | PNG | ✅ Canvas | ✅ pure-JS (`zlib`) |
112
+ | JPEG | ✅ Canvas | ✅ pure-JS baseline |
113
+ | WebP | ✅ Canvas | ⛔ needs a Canvas runtime |
114
+ | SVG → raster | ✅ Canvas | ✅ with optional `sharp` |
115
+
116
+ ## Size helpers
117
+
118
+ ```ts
119
+ import { parseSize, formatBytes } from "@lacspace/image";
120
+ parseSize("1.5mb"); // 1572864 (binary units, matches OS/upload limits)
121
+ formatBytes(1536); // "1.5 KB"
122
+ ```
123
+
124
+ ## API
125
+
126
+ - `encode(source, { format, quality?, background? })` → `{ bytes, format, width, height, quality?, size }`
127
+ - `fit(source, { format, maxSize? | maxBytes?, minQuality?, maxQuality?, allowResize?, minScale?, background? })`
128
+ - `encodePng(source, level?)` · `encodePngSync(source)` · `encodeJpeg(source, quality)`
129
+ - `rasterizeSvg(svg, { width?, height?, background? })`
130
+ - `Surface`, `gradient`, `radial`, `pattern`, `parseColor`, `parseSize`, `formatBytes`, `hasCanvas`, `canvasSupports`
131
+
132
+ ## License
133
+
134
+ [Lacspace Free Licence v1.0](https://github.com/lacspace/npm-packages/blob/main/LICENSE) — free to use, permissive, Lacspace-branded.
135
+
136
+ <div align="center"><sub>Part of the <a href="https://developer.lacspace.com/packages">Lacspace</a> ecosystem · zero-dependency · isomorphic · fully typed</sub></div>