simdra 0.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 -0
- package/README.md +218 -0
- package/dist/core/index.d.ts +385 -0
- package/dist/core/index.js +1913 -0
- package/dist/core/microsharp/index.d.ts +546 -0
- package/dist/core/microsharp/index.js +1751 -0
- package/dist/core/node-zigar-addon/darwin.arm64.node +0 -0
- package/dist/core/node-zigar-addon/darwin.x64.node +0 -0
- package/dist/core/node-zigar-addon/linux-musl.arm64.node +0 -0
- package/dist/core/node-zigar-addon/linux-musl.x64.node +0 -0
- package/dist/core/node-zigar-addon/linux.arm64.node +0 -0
- package/dist/core/node-zigar-addon/linux.x64.node +0 -0
- package/dist/core/node-zigar-addon/win32.x64.node +0 -0
- package/dist/core/simdra.zigar/darwin.arm64.dylib +0 -0
- package/dist/core/simdra.zigar/darwin.x64.dylib +0 -0
- package/dist/core/simdra.zigar/linux-musl.arm64.so +0 -0
- package/dist/core/simdra.zigar/linux-musl.x64.so +0 -0
- package/dist/core/simdra.zigar/linux.arm64.so +0 -0
- package/dist/core/simdra.zigar/linux.x64.so +0 -0
- package/dist/core/simdra.zigar/win32.x64.dll +0 -0
- package/dist/core/zig.js +37025 -0
- package/dist/wasm/index.mjs +43921 -0
- package/dist/wasm/simdra.wasm +0 -0
- package/package.json +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Narek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./image.png" alt="simdra — SIMD-accelerated 2D canvas, in a Worker" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# simdra
|
|
6
|
+
|
|
7
|
+
**SIMD-accelerated 2D canvas and image manipulation. In a Worker.**
|
|
8
|
+
|
|
9
|
+
Cloudflare Workers · browser Web Workers · Vercel Edge · Deno Deploy ·
|
|
10
|
+
any V8 isolate with WASM-SIMD.
|
|
11
|
+
|
|
12
|
+
📖 **[See the docs → bynarek.com/simdra](https://bynarek.com/simdra/)** — installation, API reference, integration examples.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install simdra
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
[`sharp`](https://sharp.pixelplumbing.com) is great. So is
|
|
21
|
+
[`@napi-rs/canvas`](https://github.com/Brooooooklyn/canvas). Neither
|
|
22
|
+
runs in a Cloudflare Worker, browser Web Worker, or any other V8
|
|
23
|
+
isolate that doesn't let you spawn threads. simdra fills that gap:
|
|
24
|
+
HTML5 Canvas 2D plus a sharp-shaped fluent surface, both compiled to
|
|
25
|
+
one ~500 KB gzipped WASM bundle with NEON / SSE / WASM-SIMD code
|
|
26
|
+
paths under the hood.
|
|
27
|
+
|
|
28
|
+
| Library | Cloudflare Workers? | Browser? | Native deps | Bundle |
|
|
29
|
+
|---|---|---|---|---|
|
|
30
|
+
| `sharp` | ❌ needs libvips | ❌ | yes (libvips) | — |
|
|
31
|
+
| `@napi-rs/canvas` | ❌ Node-API only | ❌ | yes (Skia) | — |
|
|
32
|
+
| `node-canvas` | ❌ Cairo native | ❌ | yes (Cairo) | — |
|
|
33
|
+
| `canvaskit-wasm` | ❌ too large | ✅ | no | ~7 MB |
|
|
34
|
+
| **`simdra`** | ✅ | ✅ | **no** | **~500 KB gz** |
|
|
35
|
+
|
|
36
|
+
CanvasKit is the closest comparable — both are WASM, both work in
|
|
37
|
+
the browser — but CanvasKit is a full Skia port (~7 MB) that's too
|
|
38
|
+
large for the Cloudflare Workers bundle limit and gives you the Skia
|
|
39
|
+
API rather than HTML5 Canvas. simdra targets the smaller, more
|
|
40
|
+
familiar Canvas surface plus the sharp shape, in 1/14 the size.
|
|
41
|
+
|
|
42
|
+
## What
|
|
43
|
+
|
|
44
|
+
Three surfaces, one SIMD-accelerated Zig core:
|
|
45
|
+
|
|
46
|
+
- **🎨 Canvas 2D** — drop-in HTML5 Canvas (`createCanvas`,
|
|
47
|
+
`getContext('2d')`, `Path2D`, `ImageData`, `DOMMatrix`,
|
|
48
|
+
`CanvasGradient`, `CanvasPattern`).
|
|
49
|
+
- **🖼 MicroSharp** — drop-in sharp-shaped fluent API
|
|
50
|
+
(`microsharp(input).resize(...).toBuffer()`).
|
|
51
|
+
- **🦎 Zig core** — Skia-style `Sm*` primitives (`SmCanvas`,
|
|
52
|
+
`SmPaint`, `SmPath`, `SmBitmap`). Embed in your own Zig project.
|
|
53
|
+
|
|
54
|
+
The three are independent at the consumer layer but call the same
|
|
55
|
+
Zig types underneath — same SIMD kernels, same encoders, same
|
|
56
|
+
decoders.
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
### Resize in a Cloudflare Worker
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
import { microsharp } from 'simdra';
|
|
64
|
+
|
|
65
|
+
export default {
|
|
66
|
+
async fetch(request) {
|
|
67
|
+
const out = await microsharp(request.body)
|
|
68
|
+
.resize(800, 600, { fit: 'cover', kernel: 'lanczos3' })
|
|
69
|
+
.jpeg({ quality: 85 })
|
|
70
|
+
.toBuffer();
|
|
71
|
+
return new Response(out, {
|
|
72
|
+
headers: { 'content-type': 'image/jpeg' },
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Canvas 2D in a Web Worker
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import { createCanvas } from 'simdra';
|
|
82
|
+
|
|
83
|
+
const canvas = createCanvas(400, 300);
|
|
84
|
+
const ctx = canvas.getContext('2d');
|
|
85
|
+
|
|
86
|
+
ctx.fillStyle = '#10b981';
|
|
87
|
+
ctx.fillRect(0, 0, 400, 300);
|
|
88
|
+
ctx.fillStyle = '#fff';
|
|
89
|
+
ctx.font = '24px sans-serif';
|
|
90
|
+
ctx.fillText('Hello, Worker', 20, 40);
|
|
91
|
+
|
|
92
|
+
const png = canvas.toBytes();
|
|
93
|
+
postMessage(png, [png.buffer]);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Sharp-style chain in the browser
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { microsharp } from 'simdra';
|
|
100
|
+
|
|
101
|
+
async function autoCrop(file) {
|
|
102
|
+
const out = await microsharp(file)
|
|
103
|
+
.rotate() // autoOrient via EXIF
|
|
104
|
+
.resize(1200, 800, { fit: 'cover', position: 'attention' })
|
|
105
|
+
.modulate({ brightness: 1.1, saturation: 1.2 })
|
|
106
|
+
.sharpen()
|
|
107
|
+
.jpeg({ quality: 90 })
|
|
108
|
+
.toBuffer();
|
|
109
|
+
return new Blob([out], { type: 'image/jpeg' });
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Sharp's image-ops chain — single-thread, in WASM
|
|
114
|
+
|
|
115
|
+
simdra implements sharp's full image-operations API (~22 ops):
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const out = await microsharp(input)
|
|
119
|
+
.rotate(90) // 90° / 180° / 270° byte-exact
|
|
120
|
+
.flip().flop() // mirrors
|
|
121
|
+
.affine([1, 0.3, 0.1, 0.7]) // affine transform
|
|
122
|
+
.blur(2) // separable Gaussian
|
|
123
|
+
.sharpen({ sigma: 1, m1: 1, m2: 2 }) // libvips USM
|
|
124
|
+
.median(3).dilate(1).erode(1) // morphology
|
|
125
|
+
.convolve({ width: 3, height: 3, kernel: [-1,0,1,-2,0,2,-1,0,1] })
|
|
126
|
+
.gamma(2.2).negate({ alpha: false }) // tone curves
|
|
127
|
+
.linear(1.2, -10).threshold(128) // levels
|
|
128
|
+
.normalise().clahe({ width: 16, height: 16 })
|
|
129
|
+
.modulate({ brightness: 1.1, hue: 30 })
|
|
130
|
+
.tint('#ff8800').greyscale()
|
|
131
|
+
.png()
|
|
132
|
+
.toBuffer();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
See [`COMPATIBILITY.md`](./COMPATIBILITY.md) for sharp-API divergences.
|
|
136
|
+
|
|
137
|
+
## Two builds, one package
|
|
138
|
+
|
|
139
|
+
- `simdra` / `simdra/core` — Node, native via
|
|
140
|
+
[node-zigar](https://github.com/chung-leong/node-zigar). NEON
|
|
141
|
+
kernels on aarch64.
|
|
142
|
+
- `simdra/wasm` — browsers, Cloudflare Workers, Vercel Edge, Deno.
|
|
143
|
+
WASM bundle, ~440 KB raw / ~175 KB gzip.
|
|
144
|
+
|
|
145
|
+
## When NOT to use simdra
|
|
146
|
+
|
|
147
|
+
- Multi-core image-processing servers — use `sharp`. simdra is
|
|
148
|
+
single-thread by design; sharp wins on a 16-core box.
|
|
149
|
+
- GPU-bound workloads — use Skia / WebGPU.
|
|
150
|
+
- Wide-gamut, 16-bit, ICC-aware pipelines — use libvips.
|
|
151
|
+
- WebP / AVIF / JXL output — `stb_image_write` doesn't ship them;
|
|
152
|
+
use `sharp`.
|
|
153
|
+
|
|
154
|
+
The bullseye is: *"I'm shipping image processing in a Cloudflare
|
|
155
|
+
Worker / Vercel Edge function / browser Web Worker / single-vCPU
|
|
156
|
+
lambda, and the Node-API libraries don't run there."*
|
|
157
|
+
|
|
158
|
+
## What you give up vs sharp / @napi-rs/canvas
|
|
159
|
+
|
|
160
|
+
- No multi-core throughput on a 16-core box.
|
|
161
|
+
- No WebP / AVIF / JXL output.
|
|
162
|
+
- No 16-bit / Lab / CMYK pipelines. RGBA8 sRGB only.
|
|
163
|
+
- No EXIF / ICC / XMP metadata round-trip (only `Orientation` is
|
|
164
|
+
read for `autoOrient`).
|
|
165
|
+
- No tile-streaming for very-large-image inputs.
|
|
166
|
+
|
|
167
|
+
If any of those are dealbreakers, use sharp.
|
|
168
|
+
|
|
169
|
+
## Architecture
|
|
170
|
+
|
|
171
|
+
simdra is a two-layer library:
|
|
172
|
+
|
|
173
|
+
- **Zig** is a pure drawing library — Skia-style class taxonomy with
|
|
174
|
+
the **`Sm` prefix** (analogous to Skia's `Sk*`): `SmSurface`,
|
|
175
|
+
`SmCanvas`, `SmPaint`, `SmBitmap`, `SmPath`, `SmMatrix`,
|
|
176
|
+
`SmGradient`. No HTML5 names anywhere in Zig. Folder structure
|
|
177
|
+
mirrors Skia's `include/{core,effects,encode}/` and `src/{opts,utils}/`.
|
|
178
|
+
- **TypeScript** (`src/index.ts` + `src/microsharp/index.ts`) is the
|
|
179
|
+
HTML5 / WebIDL compatibility layer plus the sharp-shaped fluent
|
|
180
|
+
API. Both lower to the same vectorised raster pipeline.
|
|
181
|
+
|
|
182
|
+
Every hot loop is vectorised through Zig's `@Vector` primitives:
|
|
183
|
+
`@Vector(N, u8)` for byte ops, `@Vector(4, f32)` for per-pixel FMA
|
|
184
|
+
chains, `@select` for branchless masking, `@reduce(.Add, ...)` for
|
|
185
|
+
dot-products, `@splat` for kernel-weight broadcast. Same source
|
|
186
|
+
compiles to NEON on aarch64, SSE on x86, WASM-SIMD in browsers.
|
|
187
|
+
|
|
188
|
+
See the [Zig core docs](./docs/zig/index.md) for the full
|
|
189
|
+
architecture writeup.
|
|
190
|
+
|
|
191
|
+
## Documentation
|
|
192
|
+
|
|
193
|
+
📖 **Full docs: [bynarek.com/simdra](https://bynarek.com/simdra/)** — searchable, with the integration examples laid out side-by-side.
|
|
194
|
+
|
|
195
|
+
Quick links:
|
|
196
|
+
|
|
197
|
+
- [Installation](https://bynarek.com/simdra/installation) — Cloudflare Workers, Vercel Edge, Deno, Bun, browsers, Web Workers.
|
|
198
|
+
- [Examples](https://bynarek.com/simdra/examples/) — image-resize API, OG cards, avatar pipeline, watermarking, format converter, document & chart renderers, plus canvg / pdfjs-serverless / unpdf integrations.
|
|
199
|
+
- [Canvas 2D API](https://bynarek.com/simdra/canvas/api) — drawing, paths, transforms, text, images, encoding.
|
|
200
|
+
- [MicroSharp API](https://bynarek.com/simdra/microsharp/api) — full sharp-shaped pipeline, all 22 image-ops methods.
|
|
201
|
+
- [Zig core](https://bynarek.com/simdra/zig/) — Skia-shaped class taxonomy, Scan→Blitter pipeline, SIMD backends.
|
|
202
|
+
- [Compatibility matrix](./COMPATIBILITY.md) — HTML5 + sharp spec coverage and divergences.
|
|
203
|
+
- [Roadmap](./Roadmap.md) — pixel format expansion (F16 / F32 / 10:10:10:2 / single-channel), codec independence (replace stb with pure Zig).
|
|
204
|
+
|
|
205
|
+
## Marketing reference
|
|
206
|
+
|
|
207
|
+
Internal copy decks for landing-page / launch-post / CFP work:
|
|
208
|
+
|
|
209
|
+
- [`Marketing.md`](./Marketing.md) — cross-audience positioning,
|
|
210
|
+
pitch ladder, framing notes.
|
|
211
|
+
- [`Marketing-js.md`](./Marketing-js.md) — JS / TS audience.
|
|
212
|
+
Competitive matrix, code snippets, "in a Worker" framing.
|
|
213
|
+
- [`Marketing-zig.md`](./Marketing-zig.md) — Zig audience.
|
|
214
|
+
`@Vector` patterns, file-is-struct, comptime backend dispatch.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT.
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { parseCssColor } from '../zig/simdra.zig';
|
|
2
|
+
import type { SmSurface as ZigSurface, SmCanvas as ZigCanvas, SmBitmap as ZigBitmap, SmMatrix as ZigMatrix, SmPath as ZigPath, SmGradient as ZigGradient, SmPattern as ZigPattern } from '../zig/simdra.zig';
|
|
3
|
+
/**
|
|
4
|
+
* Module-private symbols — wrappers store their underlying Zig proxy and
|
|
5
|
+
* provide internal-construction factories. Same-module code can read them;
|
|
6
|
+
* consumers cannot, because they don't have the symbol reference.
|
|
7
|
+
*/
|
|
8
|
+
declare const ZIG: unique symbol;
|
|
9
|
+
declare const FROM_ZIG: unique symbol;
|
|
10
|
+
export interface ImageDataSettings {
|
|
11
|
+
colorSpace?: 'srgb' | 'display_p3';
|
|
12
|
+
pixelFormat?: 'rgba_unorm8' | 'rgba_float16';
|
|
13
|
+
}
|
|
14
|
+
export declare class ImageData {
|
|
15
|
+
/** @internal */ [ZIG]: ZigBitmap;
|
|
16
|
+
constructor(width: number, height: number, settings?: ImageDataSettings);
|
|
17
|
+
constructor(data: ArrayBufferView, width: number, height?: number, settings?: ImageDataSettings);
|
|
18
|
+
/** @internal — wrap an existing Zig bitmap (used by getImageData). */
|
|
19
|
+
static [FROM_ZIG](bitmap: ZigBitmap): ImageData;
|
|
20
|
+
get data(): Uint8Array;
|
|
21
|
+
get width(): number;
|
|
22
|
+
get height(): number;
|
|
23
|
+
get colorSpace(): string;
|
|
24
|
+
get pixelFormat(): string;
|
|
25
|
+
}
|
|
26
|
+
export declare class Image {
|
|
27
|
+
/** @internal */ [ZIG]: ZigBitmap;
|
|
28
|
+
private constructor();
|
|
29
|
+
/** Decode PNG / JPEG / BMP / GIF (first frame) bytes into an Image. */
|
|
30
|
+
static fromBytes(bytes: ArrayBufferView | Uint8Array | Buffer): Image;
|
|
31
|
+
/** @internal — wrap an existing Zig bitmap (used by the sharp-shaped
|
|
32
|
+
* binding's pipeline so it can hand over a decoded buffer). */
|
|
33
|
+
static [FROM_ZIG](bitmap: ZigBitmap): Image;
|
|
34
|
+
get width(): number;
|
|
35
|
+
get height(): number;
|
|
36
|
+
}
|
|
37
|
+
export declare class DOMMatrix {
|
|
38
|
+
/** @internal */ [ZIG]: ZigMatrix;
|
|
39
|
+
constructor(init?: number[] | string);
|
|
40
|
+
/** @internal */
|
|
41
|
+
static [FROM_ZIG](zig: ZigMatrix): DOMMatrix;
|
|
42
|
+
static fromFloat32Array(arr: Float32Array): DOMMatrix;
|
|
43
|
+
static fromFloat64Array(arr: Float64Array): DOMMatrix;
|
|
44
|
+
static fromMatrix(other?: DOMMatrix | DOMMatrix2DInit): DOMMatrix;
|
|
45
|
+
get a(): number;
|
|
46
|
+
get b(): number;
|
|
47
|
+
get c(): number;
|
|
48
|
+
get d(): number;
|
|
49
|
+
get e(): number;
|
|
50
|
+
get f(): number;
|
|
51
|
+
set a(v: number);
|
|
52
|
+
set b(v: number);
|
|
53
|
+
set c(v: number);
|
|
54
|
+
set d(v: number);
|
|
55
|
+
set e(v: number);
|
|
56
|
+
set f(v: number);
|
|
57
|
+
get m11(): number;
|
|
58
|
+
get m12(): number;
|
|
59
|
+
get m21(): number;
|
|
60
|
+
get m22(): number;
|
|
61
|
+
get m41(): number;
|
|
62
|
+
get m42(): number;
|
|
63
|
+
set m11(v: number);
|
|
64
|
+
set m12(v: number);
|
|
65
|
+
set m21(v: number);
|
|
66
|
+
set m22(v: number);
|
|
67
|
+
set m41(v: number);
|
|
68
|
+
set m42(v: number);
|
|
69
|
+
get m13(): number;
|
|
70
|
+
get m14(): number;
|
|
71
|
+
get m23(): number;
|
|
72
|
+
get m24(): number;
|
|
73
|
+
get m31(): number;
|
|
74
|
+
get m32(): number;
|
|
75
|
+
get m33(): number;
|
|
76
|
+
get m34(): number;
|
|
77
|
+
get m43(): number;
|
|
78
|
+
get m44(): number;
|
|
79
|
+
get is2D(): boolean;
|
|
80
|
+
get isIdentity(): boolean;
|
|
81
|
+
multiplySelf(other: DOMMatrix): DOMMatrix;
|
|
82
|
+
preMultiplySelf(other: DOMMatrix): DOMMatrix;
|
|
83
|
+
translateSelf(tx: number, ty: number): DOMMatrix;
|
|
84
|
+
scaleSelf(sx: number, sy: number): DOMMatrix;
|
|
85
|
+
rotateSelf(angleDegrees: number): DOMMatrix;
|
|
86
|
+
rotateFromVectorSelf(x: number, y: number): DOMMatrix;
|
|
87
|
+
rotateAxisAngleSelf(x: number, y: number, z: number, angleDegrees: number): DOMMatrix;
|
|
88
|
+
scale3dSelf(scale: number, originX?: number, originY?: number, originZ?: number): DOMMatrix;
|
|
89
|
+
skewXSelf(angleDegrees: number): DOMMatrix;
|
|
90
|
+
skewYSelf(angleDegrees: number): DOMMatrix;
|
|
91
|
+
invertSelf(): DOMMatrix;
|
|
92
|
+
}
|
|
93
|
+
export declare class Path2D {
|
|
94
|
+
/** @internal */ [ZIG]: ZigPath;
|
|
95
|
+
constructor(other?: Path2D | string);
|
|
96
|
+
closePath(): void;
|
|
97
|
+
moveTo(x: number, y: number): void;
|
|
98
|
+
lineTo(x: number, y: number): void;
|
|
99
|
+
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
|
100
|
+
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
|
|
101
|
+
rect(x: number, y: number, w: number, h: number): void;
|
|
102
|
+
arc(cx: number, cy: number, r: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
103
|
+
arcTo(x1: number, y1: number, x2: number, y2: number, r: number): void;
|
|
104
|
+
roundRect(x: number, y: number, w: number, h: number, radii?: number | DOMPointInit | Array<number | DOMPointInit>): void;
|
|
105
|
+
ellipse(cx: number, cy: number, rx: number, ry: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
106
|
+
addPath(other: Path2D, transform?: DOMMatrix): void;
|
|
107
|
+
}
|
|
108
|
+
export declare class CanvasGradient {
|
|
109
|
+
/** @internal */ [ZIG]: ZigGradient;
|
|
110
|
+
/** @internal — module-internal construction only. */
|
|
111
|
+
constructor(zig: ZigGradient);
|
|
112
|
+
addColorStop(offset: number, color: string): void;
|
|
113
|
+
}
|
|
114
|
+
export interface DOMMatrix2DInit {
|
|
115
|
+
a?: number;
|
|
116
|
+
b?: number;
|
|
117
|
+
c?: number;
|
|
118
|
+
d?: number;
|
|
119
|
+
e?: number;
|
|
120
|
+
f?: number;
|
|
121
|
+
m11?: number;
|
|
122
|
+
m12?: number;
|
|
123
|
+
m21?: number;
|
|
124
|
+
m22?: number;
|
|
125
|
+
m41?: number;
|
|
126
|
+
m42?: number;
|
|
127
|
+
}
|
|
128
|
+
export declare class CanvasPattern {
|
|
129
|
+
/** @internal */ [ZIG]: ZigPattern;
|
|
130
|
+
/** @internal — module-internal construction only. */
|
|
131
|
+
constructor(zig: ZigPattern);
|
|
132
|
+
setTransform(matrix?: DOMMatrix | DOMMatrix2DInit): void;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Register a TTF/OTF font under one or more CSS family names. Mirrors the
|
|
136
|
+
* non-spec `registerFont` extension that node-canvas / @napi-rs/canvas
|
|
137
|
+
* expose. Family lookup is case-insensitive.
|
|
138
|
+
*
|
|
139
|
+
* After registration, set `ctx.font` to a CSS shorthand string that names
|
|
140
|
+
* the family (e.g. `'14px MyFont'`) to use it.
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Single entry in the `fonts` array passed to `createCanvas(w, h, { fonts })`.
|
|
144
|
+
* `data` must be already-fetched TTF or OTF bytes; WOFF / WOFF2 are not decoded.
|
|
145
|
+
* `name` becomes the family selector for `ctx.font = '<size>px <name>'`.
|
|
146
|
+
* `weight` / `style` describe the registered face — when omitted, simdra
|
|
147
|
+
* auto-detects them from the font's `OS/2.usWeightClass` / `head.macStyle`.
|
|
148
|
+
*/
|
|
149
|
+
export interface FontInit {
|
|
150
|
+
name: string;
|
|
151
|
+
data: ArrayBuffer | ArrayBufferView;
|
|
152
|
+
weight?: number | string;
|
|
153
|
+
style?: FontStyle;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Optional second-arg bag for `createCanvas` / `new Canvas`. Today only carries
|
|
157
|
+
* `fonts`, a non-spec convenience that registers each entry via `registerFont`
|
|
158
|
+
* before the constructor returns.
|
|
159
|
+
*/
|
|
160
|
+
export interface CanvasOptions {
|
|
161
|
+
fonts?: FontInit[];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Optional descriptor for `registerFont` — pin the face's weight/style
|
|
165
|
+
* explicitly. When omitted, simdra reads `OS/2.usWeightClass` and the
|
|
166
|
+
* `head.macStyle` italic bit from the TTF/OTF bytes.
|
|
167
|
+
*/
|
|
168
|
+
export interface FontFaceDescriptor {
|
|
169
|
+
weight?: number | string;
|
|
170
|
+
style?: FontStyle;
|
|
171
|
+
}
|
|
172
|
+
export declare function registerFont(bytes: ArrayBufferView | ArrayBuffer, family: string, descriptor?: FontFaceDescriptor): void;
|
|
173
|
+
type FontStyle = 'normal' | 'italic' | 'oblique';
|
|
174
|
+
type DOMPointInit = {
|
|
175
|
+
x?: number;
|
|
176
|
+
y?: number;
|
|
177
|
+
};
|
|
178
|
+
export declare class TextMetrics {
|
|
179
|
+
readonly width: number;
|
|
180
|
+
/** @internal */ constructor(width: number);
|
|
181
|
+
}
|
|
182
|
+
export declare class CanvasRenderingContext2D {
|
|
183
|
+
#private;
|
|
184
|
+
/** @internal */ [ZIG]: ZigCanvas;
|
|
185
|
+
/** @internal — only `Canvas.getContext('2d')` constructs these. */
|
|
186
|
+
constructor(canvas: Canvas, zig: ZigCanvas);
|
|
187
|
+
get canvas(): Canvas;
|
|
188
|
+
getContextAttributes(): {
|
|
189
|
+
alpha: boolean;
|
|
190
|
+
colorSpace: 'srgb';
|
|
191
|
+
desynchronized: boolean;
|
|
192
|
+
willReadFrequently: boolean;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Drop the JS-side references this context holds. The underlying SmCanvas
|
|
196
|
+
* is owned by the SmSurface and is deinit'd when `Canvas#destroy()` runs;
|
|
197
|
+
* this method is a no-op for Zig memory but releases JS-held strong refs
|
|
198
|
+
* to gradient / pattern style objects so they GC sooner. Idempotent.
|
|
199
|
+
*/
|
|
200
|
+
destroy(): void;
|
|
201
|
+
[Symbol.dispose](): void;
|
|
202
|
+
save(): void;
|
|
203
|
+
restore(): void;
|
|
204
|
+
reset(): void;
|
|
205
|
+
translate(tx: number, ty: number): void;
|
|
206
|
+
rotate(angleRadians: number): void;
|
|
207
|
+
scale(sx: number, sy: number): void;
|
|
208
|
+
transform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
|
209
|
+
setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
|
210
|
+
resetTransform(): void;
|
|
211
|
+
getTransform(): DOMMatrix;
|
|
212
|
+
get fillStyle(): string | CanvasGradient | CanvasPattern;
|
|
213
|
+
set fillStyle(v: string | CanvasGradient | CanvasPattern);
|
|
214
|
+
get strokeStyle(): string | CanvasGradient | CanvasPattern;
|
|
215
|
+
set strokeStyle(v: string | CanvasGradient | CanvasPattern);
|
|
216
|
+
get lineWidth(): number;
|
|
217
|
+
set lineWidth(v: number);
|
|
218
|
+
get lineCap(): string;
|
|
219
|
+
set lineCap(v: string);
|
|
220
|
+
get lineJoin(): string;
|
|
221
|
+
set lineJoin(v: string);
|
|
222
|
+
get miterLimit(): number;
|
|
223
|
+
set miterLimit(v: number);
|
|
224
|
+
get lineDashOffset(): number;
|
|
225
|
+
set lineDashOffset(v: number);
|
|
226
|
+
setLineDash(segments: number[]): void;
|
|
227
|
+
getLineDash(): number[];
|
|
228
|
+
get globalAlpha(): number;
|
|
229
|
+
set globalAlpha(v: number);
|
|
230
|
+
get globalCompositeOperation(): string;
|
|
231
|
+
set globalCompositeOperation(v: string);
|
|
232
|
+
fillRect(x: number, y: number, w: number, h: number): void;
|
|
233
|
+
strokeRect(x: number, y: number, w: number, h: number): void;
|
|
234
|
+
clearRect(x: number, y: number, w: number, h: number): void;
|
|
235
|
+
beginPath(): void;
|
|
236
|
+
closePath(): void;
|
|
237
|
+
moveTo(x: number, y: number): void;
|
|
238
|
+
lineTo(x: number, y: number): void;
|
|
239
|
+
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
|
240
|
+
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
|
|
241
|
+
rect(x: number, y: number, w: number, h: number): void;
|
|
242
|
+
arc(cx: number, cy: number, r: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
243
|
+
arcTo(x1: number, y1: number, x2: number, y2: number, r: number): void;
|
|
244
|
+
roundRect(x: number, y: number, w: number, h: number, radii?: number | DOMPointInit | Array<number | DOMPointInit>): void;
|
|
245
|
+
ellipse(cx: number, cy: number, rx: number, ry: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
246
|
+
fill(pathOrRule?: Path2D | 'nonzero' | 'evenodd', maybeRule?: 'nonzero' | 'evenodd'): void;
|
|
247
|
+
stroke(path?: Path2D): void;
|
|
248
|
+
isPointInPath(x: number, y: number, fillRule?: 'nonzero' | 'evenodd'): boolean;
|
|
249
|
+
isPointInPath(path: Path2D, x: number, y: number, fillRule?: 'nonzero' | 'evenodd'): boolean;
|
|
250
|
+
isPointInStroke(x: number, y: number): boolean;
|
|
251
|
+
isPointInStroke(path: Path2D, x: number, y: number): boolean;
|
|
252
|
+
clip(pathOrRule?: Path2D | 'nonzero' | 'evenodd', maybeRule?: 'nonzero' | 'evenodd'): void;
|
|
253
|
+
get font(): string;
|
|
254
|
+
set font(v: string);
|
|
255
|
+
get textAlign(): string;
|
|
256
|
+
set textAlign(v: string);
|
|
257
|
+
get textBaseline(): string;
|
|
258
|
+
set textBaseline(v: string);
|
|
259
|
+
get direction(): string;
|
|
260
|
+
set direction(v: string);
|
|
261
|
+
get letterSpacing(): string;
|
|
262
|
+
set letterSpacing(v: string);
|
|
263
|
+
get wordSpacing(): string;
|
|
264
|
+
set wordSpacing(v: string);
|
|
265
|
+
get fontKerning(): string;
|
|
266
|
+
set fontKerning(v: string);
|
|
267
|
+
get fontStretch(): string;
|
|
268
|
+
set fontStretch(v: string);
|
|
269
|
+
get fontVariantCaps(): string;
|
|
270
|
+
set fontVariantCaps(v: string);
|
|
271
|
+
get textRendering(): string;
|
|
272
|
+
set textRendering(v: string);
|
|
273
|
+
get filter(): string;
|
|
274
|
+
set filter(v: string);
|
|
275
|
+
get shadowBlur(): number;
|
|
276
|
+
set shadowBlur(v: number);
|
|
277
|
+
get shadowColor(): string;
|
|
278
|
+
set shadowColor(v: string);
|
|
279
|
+
get shadowOffsetX(): number;
|
|
280
|
+
set shadowOffsetX(v: number);
|
|
281
|
+
get shadowOffsetY(): number;
|
|
282
|
+
set shadowOffsetY(v: number);
|
|
283
|
+
get imageSmoothingEnabled(): boolean;
|
|
284
|
+
set imageSmoothingEnabled(v: boolean);
|
|
285
|
+
get imageSmoothingQuality(): string;
|
|
286
|
+
set imageSmoothingQuality(v: string);
|
|
287
|
+
fillText(text: string, x: number, y: number, _maxWidth?: number): void;
|
|
288
|
+
strokeText(text: string, x: number, y: number, maxWidth?: number): void;
|
|
289
|
+
measureText(text: string): TextMetrics;
|
|
290
|
+
createImageData(width: number, height: number, settings?: ImageDataSettings): ImageData;
|
|
291
|
+
createImageData(imagedata: ImageData): ImageData;
|
|
292
|
+
getImageData(sx: number, sy: number, sw: number, sh: number, settings?: ImageDataSettings): ImageData;
|
|
293
|
+
putImageData(imageData: ImageData, dx: number, dy: number, dirtyX?: number, dirtyY?: number, dirtyW?: number, dirtyH?: number): void;
|
|
294
|
+
drawImage(image: ImageData | Image | Canvas, dx: number, dy: number): void;
|
|
295
|
+
drawImage(image: ImageData | Image | Canvas, dx: number, dy: number, dw: number, dh: number): void;
|
|
296
|
+
drawImage(image: ImageData | Image | Canvas, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
297
|
+
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
|
|
298
|
+
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
299
|
+
createConicGradient(startAngle: number, x: number, y: number): CanvasGradient;
|
|
300
|
+
createPattern(image: ImageData | Image | Canvas, repetition: string | null): CanvasPattern;
|
|
301
|
+
}
|
|
302
|
+
export declare class Canvas {
|
|
303
|
+
#private;
|
|
304
|
+
/** @internal */ [ZIG]: ZigSurface;
|
|
305
|
+
constructor(width: number, height: number, opts?: CanvasOptions);
|
|
306
|
+
get width(): number;
|
|
307
|
+
set width(n: number);
|
|
308
|
+
get height(): number;
|
|
309
|
+
set height(n: number);
|
|
310
|
+
getContext(kind: '2d', _attrs?: object): CanvasRenderingContext2D;
|
|
311
|
+
toDataURL(type?: string, quality?: number): string;
|
|
312
|
+
/**
|
|
313
|
+
* Encode the canvas and return the raw bytes. Skips the base64 round-trip
|
|
314
|
+
* that `toDataURL()` does — pass directly to `new Response(...)`,
|
|
315
|
+
* `fs.writeFile`, etc.
|
|
316
|
+
*
|
|
317
|
+
* `type` defaults to `'image/png'`; `'image/jpeg'` is also supported with
|
|
318
|
+
* an optional `quality` in the HTML5 0.0–1.0 range (default 0.92).
|
|
319
|
+
*
|
|
320
|
+
* The returned `Uint8Array` is a JS-owned defensive copy; safe to retain
|
|
321
|
+
* past further drawing.
|
|
322
|
+
*/
|
|
323
|
+
toBytes(type?: string, quality?: number): Uint8Array;
|
|
324
|
+
/**
|
|
325
|
+
* Promise-returning sibling of `toBytes`. Mirrors `@napi-rs/canvas`'s
|
|
326
|
+
* `encode(format)`: encodes off the JS thread on Node (real pthread
|
|
327
|
+
* offload via zigar's WorkQueue — equivalent to N-API `AsyncWorker`).
|
|
328
|
+
*
|
|
329
|
+
* On WASM targets — browsers, Cloudflare Workers, edge runtimes — the
|
|
330
|
+
* Zig WorkQueue is comptime-gated out and this falls through to a
|
|
331
|
+
* microtask-yielded sync encode. CF Workers can't spawn worker threads
|
|
332
|
+
* in any case, so the API shape stays uniform; only the offload behavior
|
|
333
|
+
* differs by target.
|
|
334
|
+
*/
|
|
335
|
+
toBytesAsync(type?: string, quality?: number): Promise<Uint8Array>;
|
|
336
|
+
/**
|
|
337
|
+
* Promise-returning sibling of `toDataURL`. Built on top of
|
|
338
|
+
* `toBytesAsync`, so the same target-dependent offload story applies —
|
|
339
|
+
* see that method's docstring.
|
|
340
|
+
*/
|
|
341
|
+
toDataURLAsync(type?: string, quality?: number): Promise<string>;
|
|
342
|
+
/**
|
|
343
|
+
* Eagerly release the underlying Zig surface (pixel buffer, cached
|
|
344
|
+
* SmCanvas, last encoded payload). Idempotent. Calling any other method
|
|
345
|
+
* after `destroy()` is undefined behavior — the wrapper's Zig handle is
|
|
346
|
+
* dangling. Mirrors the pdf.js `BaseCanvasFactory#destroy` contract and
|
|
347
|
+
* supports the Stage-3 `using` syntax (via `Symbol.dispose`).
|
|
348
|
+
*
|
|
349
|
+
* Without this, the same cleanup happens lazily through the
|
|
350
|
+
* FinalizationRegistry when the wrapper is GC'd. Use `destroy()` to
|
|
351
|
+
* release sooner — large canvases on long-lived servers, request-scoped
|
|
352
|
+
* canvases under Cloudflare Workers' tight memory caps, etc.
|
|
353
|
+
*/
|
|
354
|
+
destroy(): void;
|
|
355
|
+
[Symbol.dispose](): void;
|
|
356
|
+
}
|
|
357
|
+
export declare function createCanvas(width: number, height: number, opts?: CanvasOptions): Canvas;
|
|
358
|
+
export { microsharp, MicroSharpPipeline } from './microsharp/index.ts';
|
|
359
|
+
export type { ImageFormat, ImageFormatName, ResizeOptions, ResizeKernel, ResizeFit, ResizePosition, ExtendOptions, ExtendWithMode, ExtractRegion, TrimOptions, CompositeImage, CompositeBlend, CompositeGravity, CompositeOverlayInput, CompositeRawDescriptor, CompositeCreateInput, ChannelSelector, BandBoolOp, PngOptions, JpegOptions, Metadata, MicroSharpInput, BackgroundInput, BackgroundColor, OutputInfo, ToBufferOptions, } from './microsharp/index.ts';
|
|
360
|
+
export { parseCssColor };
|
|
361
|
+
/**
|
|
362
|
+
* WASM source accepted by async `init()`.
|
|
363
|
+
*/
|
|
364
|
+
export type InitInput = ArrayBuffer | ArrayBufferView | Response | WebAssembly.Module | Promise<ArrayBuffer | ArrayBufferView | Response | WebAssembly.Module>;
|
|
365
|
+
/**
|
|
366
|
+
* Synchronous init from a pre-compiled `WebAssembly.Module`. Designed for
|
|
367
|
+
* Cloudflare Workers / Vercel Edge — call at module-init scope:
|
|
368
|
+
*
|
|
369
|
+
* import { initSync, createCanvas } from 'simdra/wasm';
|
|
370
|
+
* import wasm from 'simdra/wasm/simdra.wasm';
|
|
371
|
+
* initSync(wasm);
|
|
372
|
+
*
|
|
373
|
+
* Workers forbids `WebAssembly.compile()` on raw bytes, but allows
|
|
374
|
+
* `new WebAssembly.Instance(precompiledModule, imports)`. The runtime
|
|
375
|
+
* compiles the imported `.wasm` at deploy time, so this stays sync.
|
|
376
|
+
*
|
|
377
|
+
* In dev (`npm test` via node-zigar) zigar loads the module itself, so this
|
|
378
|
+
* is a no-op.
|
|
379
|
+
*/
|
|
380
|
+
export declare function initSync(mod: WebAssembly.Module): void;
|
|
381
|
+
/**
|
|
382
|
+
* Async init for environments where `WebAssembly.compile()` is allowed
|
|
383
|
+
* (Node, browsers). Accepts bytes / Response / a `Module`. Idempotent.
|
|
384
|
+
*/
|
|
385
|
+
export default function init(input?: InitInput): Promise<void>;
|