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 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>;