canvas-emulator 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.
Files changed (147) hide show
  1. package/.gitignore +233 -0
  2. package/CLAUDE.md +61 -0
  3. package/README.md +128 -0
  4. package/dist/cjs/playground.d.ts +2 -0
  5. package/dist/cjs/playground.d.ts.map +1 -0
  6. package/dist/cjs/playground.js +37 -0
  7. package/dist/cjs/playground.js.map +1 -0
  8. package/dist/cjs/src/canvas/canvas.d.ts +41 -0
  9. package/dist/cjs/src/canvas/canvas.d.ts.map +1 -0
  10. package/dist/cjs/src/canvas/canvas.js +83 -0
  11. package/dist/cjs/src/canvas/canvas.js.map +1 -0
  12. package/dist/cjs/src/canvas/context-2d.d.ts +37 -0
  13. package/dist/cjs/src/canvas/context-2d.d.ts.map +1 -0
  14. package/dist/cjs/src/canvas/context-2d.js +245 -0
  15. package/dist/cjs/src/canvas/context-2d.js.map +1 -0
  16. package/dist/cjs/src/canvas/index.d.ts +2 -0
  17. package/dist/cjs/src/canvas/index.d.ts.map +1 -0
  18. package/dist/cjs/src/canvas/index.js +18 -0
  19. package/dist/cjs/src/canvas/index.js.map +1 -0
  20. package/dist/cjs/src/common/array.d.ts +40 -0
  21. package/dist/cjs/src/common/array.d.ts.map +1 -0
  22. package/dist/cjs/src/common/array.js +99 -0
  23. package/dist/cjs/src/common/array.js.map +1 -0
  24. package/dist/cjs/src/common/colors.d.ts +62 -0
  25. package/dist/cjs/src/common/colors.d.ts.map +1 -0
  26. package/dist/cjs/src/common/colors.js +93 -0
  27. package/dist/cjs/src/common/colors.js.map +1 -0
  28. package/dist/cjs/src/common/hash.d.ts +3 -0
  29. package/dist/cjs/src/common/hash.d.ts.map +1 -0
  30. package/dist/cjs/src/common/hash.js +44 -0
  31. package/dist/cjs/src/common/hash.js.map +1 -0
  32. package/dist/cjs/src/common/index.d.ts +7 -0
  33. package/dist/cjs/src/common/index.d.ts.map +1 -0
  34. package/dist/cjs/src/common/index.js +23 -0
  35. package/dist/cjs/src/common/index.js.map +1 -0
  36. package/dist/cjs/src/common/ppm.d.ts +2 -0
  37. package/dist/cjs/src/common/ppm.d.ts.map +1 -0
  38. package/dist/cjs/src/common/ppm.js +21 -0
  39. package/dist/cjs/src/common/ppm.js.map +1 -0
  40. package/dist/cjs/src/common/scalar.d.ts +27 -0
  41. package/dist/cjs/src/common/scalar.d.ts.map +1 -0
  42. package/dist/cjs/src/common/scalar.js +65 -0
  43. package/dist/cjs/src/common/scalar.js.map +1 -0
  44. package/dist/cjs/src/common/types.d.ts +9 -0
  45. package/dist/cjs/src/common/types.d.ts.map +1 -0
  46. package/dist/cjs/src/common/types.js +3 -0
  47. package/dist/cjs/src/common/types.js.map +1 -0
  48. package/dist/cjs/src/data/font/index.d.ts +2 -0
  49. package/dist/cjs/src/data/font/index.d.ts.map +1 -0
  50. package/dist/cjs/src/data/font/index.js +18 -0
  51. package/dist/cjs/src/data/font/index.js.map +1 -0
  52. package/dist/cjs/src/data/font/noto-mono-regular-48.d.ts +747 -0
  53. package/dist/cjs/src/data/font/noto-mono-regular-48.d.ts.map +1 -0
  54. package/dist/cjs/src/data/font/noto-mono-regular-48.js +15544 -0
  55. package/dist/cjs/src/data/font/noto-mono-regular-48.js.map +1 -0
  56. package/dist/cjs/src/data/font/noto-mono-regular-48.json +15540 -0
  57. package/dist/cjs/src/data/index.d.ts +2 -0
  58. package/dist/cjs/src/data/index.d.ts.map +1 -0
  59. package/dist/cjs/src/data/index.js +18 -0
  60. package/dist/cjs/src/data/index.js.map +1 -0
  61. package/dist/cjs/src/index.d.ts +4 -0
  62. package/dist/cjs/src/index.d.ts.map +1 -0
  63. package/dist/cjs/src/index.js +20 -0
  64. package/dist/cjs/src/index.js.map +1 -0
  65. package/dist/esm/playground.d.ts +2 -0
  66. package/dist/esm/playground.d.ts.map +1 -0
  67. package/dist/esm/playground.js +37 -0
  68. package/dist/esm/playground.js.map +1 -0
  69. package/dist/esm/src/canvas/canvas.d.ts +41 -0
  70. package/dist/esm/src/canvas/canvas.d.ts.map +1 -0
  71. package/dist/esm/src/canvas/canvas.js +83 -0
  72. package/dist/esm/src/canvas/canvas.js.map +1 -0
  73. package/dist/esm/src/canvas/context-2d.d.ts +37 -0
  74. package/dist/esm/src/canvas/context-2d.d.ts.map +1 -0
  75. package/dist/esm/src/canvas/context-2d.js +245 -0
  76. package/dist/esm/src/canvas/context-2d.js.map +1 -0
  77. package/dist/esm/src/canvas/index.d.ts +2 -0
  78. package/dist/esm/src/canvas/index.d.ts.map +1 -0
  79. package/dist/esm/src/canvas/index.js +18 -0
  80. package/dist/esm/src/canvas/index.js.map +1 -0
  81. package/dist/esm/src/common/array.d.ts +40 -0
  82. package/dist/esm/src/common/array.d.ts.map +1 -0
  83. package/dist/esm/src/common/array.js +99 -0
  84. package/dist/esm/src/common/array.js.map +1 -0
  85. package/dist/esm/src/common/colors.d.ts +62 -0
  86. package/dist/esm/src/common/colors.d.ts.map +1 -0
  87. package/dist/esm/src/common/colors.js +93 -0
  88. package/dist/esm/src/common/colors.js.map +1 -0
  89. package/dist/esm/src/common/hash.d.ts +3 -0
  90. package/dist/esm/src/common/hash.d.ts.map +1 -0
  91. package/dist/esm/src/common/hash.js +44 -0
  92. package/dist/esm/src/common/hash.js.map +1 -0
  93. package/dist/esm/src/common/index.d.ts +7 -0
  94. package/dist/esm/src/common/index.d.ts.map +1 -0
  95. package/dist/esm/src/common/index.js +23 -0
  96. package/dist/esm/src/common/index.js.map +1 -0
  97. package/dist/esm/src/common/ppm.d.ts +2 -0
  98. package/dist/esm/src/common/ppm.d.ts.map +1 -0
  99. package/dist/esm/src/common/ppm.js +21 -0
  100. package/dist/esm/src/common/ppm.js.map +1 -0
  101. package/dist/esm/src/common/scalar.d.ts +27 -0
  102. package/dist/esm/src/common/scalar.d.ts.map +1 -0
  103. package/dist/esm/src/common/scalar.js +65 -0
  104. package/dist/esm/src/common/scalar.js.map +1 -0
  105. package/dist/esm/src/common/types.d.ts +9 -0
  106. package/dist/esm/src/common/types.d.ts.map +1 -0
  107. package/dist/esm/src/common/types.js +3 -0
  108. package/dist/esm/src/common/types.js.map +1 -0
  109. package/dist/esm/src/data/font/index.d.ts +2 -0
  110. package/dist/esm/src/data/font/index.d.ts.map +1 -0
  111. package/dist/esm/src/data/font/index.js +18 -0
  112. package/dist/esm/src/data/font/index.js.map +1 -0
  113. package/dist/esm/src/data/font/noto-mono-regular-48.d.ts +747 -0
  114. package/dist/esm/src/data/font/noto-mono-regular-48.d.ts.map +1 -0
  115. package/dist/esm/src/data/font/noto-mono-regular-48.js +15544 -0
  116. package/dist/esm/src/data/font/noto-mono-regular-48.js.map +1 -0
  117. package/dist/esm/src/data/font/noto-mono-regular-48.json +15540 -0
  118. package/dist/esm/src/data/index.d.ts +2 -0
  119. package/dist/esm/src/data/index.d.ts.map +1 -0
  120. package/dist/esm/src/data/index.js +18 -0
  121. package/dist/esm/src/data/index.js.map +1 -0
  122. package/dist/esm/src/index.d.ts +4 -0
  123. package/dist/esm/src/index.d.ts.map +1 -0
  124. package/dist/esm/src/index.js +20 -0
  125. package/dist/esm/src/index.js.map +1 -0
  126. package/package.json +43 -0
  127. package/playground.ts +41 -0
  128. package/pnpm-lock.yaml +352 -0
  129. package/render_font.py +98 -0
  130. package/src/canvas/canvas.ts +123 -0
  131. package/src/canvas/context-2d.ts +305 -0
  132. package/src/canvas/index.ts +1 -0
  133. package/src/common/array.ts +176 -0
  134. package/src/common/colors.ts +93 -0
  135. package/src/common/hash.ts +46 -0
  136. package/src/common/index.ts +6 -0
  137. package/src/common/ppm.ts +28 -0
  138. package/src/common/scalar.ts +72 -0
  139. package/src/common/types.ts +10 -0
  140. package/src/data/font/index.ts +1 -0
  141. package/src/data/font/noto-mono-regular-48.json +15540 -0
  142. package/src/data/font/noto-mono-regular-48.ts +15540 -0
  143. package/src/data/index.ts +1 -0
  144. package/src/index.ts +3 -0
  145. package/tsconfig.cjs.json +10 -0
  146. package/tsconfig.esm.json +10 -0
  147. package/tsconfig.json +23 -0
package/.gitignore ADDED
@@ -0,0 +1,233 @@
1
+ /*.ppm
2
+ # Created by https://www.toptal.com/developers/gitignore/api/vim,emacs,yarn,node
3
+ # Edit at https://www.toptal.com/developers/gitignore?templates=vim,emacs,yarn,node
4
+
5
+ ### Emacs ###
6
+ # -*- mode: gitignore; -*-
7
+ *~
8
+ \#*\#
9
+ /.emacs.desktop
10
+ /.emacs.desktop.lock
11
+ *.elc
12
+ auto-save-list
13
+ tramp
14
+ .\#*
15
+
16
+ # Org-mode
17
+ .org-id-locations
18
+ *_archive
19
+
20
+ # flymake-mode
21
+ *_flymake.*
22
+
23
+ # eshell files
24
+ /eshell/history
25
+ /eshell/lastdir
26
+
27
+ # elpa packages
28
+ /elpa/
29
+
30
+ # reftex files
31
+ *.rel
32
+
33
+ # AUCTeX auto folder
34
+ /auto/
35
+
36
+ # cask packages
37
+ .cask/
38
+ dist/
39
+
40
+ # Flycheck
41
+ flycheck_*.el
42
+
43
+ # server auth directory
44
+ /server/
45
+
46
+ # projectiles files
47
+ .projectile
48
+
49
+ # directory configuration
50
+ .dir-locals.el
51
+
52
+ # network security
53
+ /network-security.data
54
+
55
+
56
+ ### Node ###
57
+ # Logs
58
+ logs
59
+ *.log
60
+ npm-debug.log*
61
+ yarn-debug.log*
62
+ yarn-error.log*
63
+ lerna-debug.log*
64
+ .pnpm-debug.log*
65
+
66
+ # Diagnostic reports (https://nodejs.org/api/report.html)
67
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
68
+
69
+ # Runtime data
70
+ pids
71
+ *.pid
72
+ *.seed
73
+ *.pid.lock
74
+
75
+ # Directory for instrumented libs generated by jscoverage/JSCover
76
+ lib-cov
77
+
78
+ # Coverage directory used by tools like istanbul
79
+ coverage
80
+ *.lcov
81
+
82
+ # nyc test coverage
83
+ .nyc_output
84
+
85
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
86
+ .grunt
87
+
88
+ # Bower dependency directory (https://bower.io/)
89
+ bower_components
90
+
91
+ # node-waf configuration
92
+ .lock-wscript
93
+
94
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
95
+ build/Release
96
+
97
+ # Dependency directories
98
+ node_modules/
99
+ jspm_packages/
100
+
101
+ # Snowpack dependency directory (https://snowpack.dev/)
102
+ web_modules/
103
+
104
+ # TypeScript cache
105
+ *.tsbuildinfo
106
+
107
+ # Optional npm cache directory
108
+ .npm
109
+
110
+ # Optional eslint cache
111
+ .eslintcache
112
+
113
+ # Optional stylelint cache
114
+ .stylelintcache
115
+
116
+ # Microbundle cache
117
+ .rpt2_cache/
118
+ .rts2_cache_cjs/
119
+ .rts2_cache_es/
120
+ .rts2_cache_umd/
121
+
122
+ # Optional REPL history
123
+ .node_repl_history
124
+
125
+ # Output of 'npm pack'
126
+ *.tgz
127
+
128
+ # Yarn Integrity file
129
+ .yarn-integrity
130
+
131
+ # dotenv environment variable files
132
+ .env
133
+ .env.development.local
134
+ .env.test.local
135
+ .env.production.local
136
+ .env.local
137
+
138
+ # parcel-bundler cache (https://parceljs.org/)
139
+ .cache
140
+ .parcel-cache
141
+
142
+ # Next.js build output
143
+ .next
144
+ out
145
+
146
+ # Nuxt.js build / generate output
147
+ .nuxt
148
+ dist
149
+
150
+ # Gatsby files
151
+ .cache/
152
+ # Comment in the public line in if your project uses Gatsby and not Next.js
153
+ # https://nextjs.org/blog/next-9-1#public-directory-support
154
+ # public
155
+
156
+ # vuepress build output
157
+ .vuepress/dist
158
+
159
+ # vuepress v2.x temp and cache directory
160
+ .temp
161
+
162
+ # Docusaurus cache and generated files
163
+ .docusaurus
164
+
165
+ # Serverless directories
166
+ .serverless/
167
+
168
+ # FuseBox cache
169
+ .fusebox/
170
+
171
+ # DynamoDB Local files
172
+ .dynamodb/
173
+
174
+ # TernJS port file
175
+ .tern-port
176
+
177
+ # Stores VSCode versions used for testing VSCode extensions
178
+ .vscode-test
179
+
180
+ # yarn v2
181
+ .yarn/cache
182
+ .yarn/unplugged
183
+ .yarn/build-state.yml
184
+ .yarn/install-state.gz
185
+ .pnp.*
186
+
187
+ ### Node Patch ###
188
+ # Serverless Webpack directories
189
+ .webpack/
190
+
191
+ # Optional stylelint cache
192
+
193
+ # SvelteKit build / generate output
194
+ .svelte-kit
195
+
196
+ ### Vim ###
197
+ # Swap
198
+ [._]*.s[a-v][a-z]
199
+ !*.svg # comment out if you don't need vector files
200
+ [._]*.sw[a-p]
201
+ [._]s[a-rt-v][a-z]
202
+ [._]ss[a-gi-z]
203
+ [._]sw[a-p]
204
+
205
+ # Session
206
+ Session.vim
207
+ Sessionx.vim
208
+
209
+ # Temporary
210
+ .netrwhist
211
+ # Auto-generated tag files
212
+ tags
213
+ # Persistent undo
214
+ [._]*.un~
215
+
216
+ ### yarn ###
217
+ # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
218
+
219
+ .yarn/*
220
+ !.yarn/releases
221
+ !.yarn/patches
222
+ !.yarn/plugins
223
+ !.yarn/sdks
224
+ !.yarn/versions
225
+
226
+ # if you are NOT using Zero-installs, then:
227
+ # comment the following lines
228
+ !.yarn/cache
229
+
230
+ # and uncomment the following lines
231
+ # .pnp.*
232
+
233
+ # End of https://www.toptal.com/developers/gitignore/api/vim,emacs,yarn,node
package/CLAUDE.md ADDED
@@ -0,0 +1,61 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ pnpm build # compile to both ESM (dist/esm) and CJS (dist/cjs)
9
+ pnpm check # type-check without emitting (uses tsconfig.json)
10
+ pnpm test # run vitest
11
+ pnpm format # format src/ with prettier
12
+
13
+ # Run the playground script manually (no test infra needed)
14
+ npx tsx playground.ts
15
+ ```
16
+
17
+ ## Architecture
18
+
19
+ This is a **headless Canvas API emulator** — a TypeScript library that replicates the browser `CanvasRenderingContext2D` API for server-side rendering, outputting to PPM image files or raw pixel buffers.
20
+
21
+ ### Key design conventions
22
+
23
+ - **All color values are normalized floats `[0, 1]`**, not `[0, 255]`. This applies to pixels stored in `Canvas.data` and all `V3`/`V4` color tuples.
24
+ - **Pixels are RGBA** arrays `[r, g, b, a]` stored flat in `Canvas.data`. The canvas always has 4 channels.
25
+ - `setAt` performs **alpha compositing** using `smoothstep`-based mixing between the existing pixel and the incoming one.
26
+ - Canvas supports both **pixel-space** (`at`, `setAt` using integer x/y) and **UV-space** (`atUV`, `setAtUV` using `[0,1]` coordinates).
27
+
28
+ ### Module structure
29
+
30
+ - **`src/canvas/`** — Core canvas implementation
31
+ - `canvas.ts` — `Canvas` interface + `CanvasImpl` class (pixel buffer). `createCanvas(cfg)` is the main entry point.
32
+ - `context-2d.ts` — `CanvasContext2DImpl`: mimics `CanvasRenderingContext2D`. Maintains a `State` stack for `save()`/`restore()`. Paths are stored as flat pairs of points (`[p1, p2, p3, p4, ...]`) and consumed two at a time for line segments in `stroke()`. `fill()` uses a scanline ray-casting algorithm.
33
+ - Font rendering (`drawChar`, `fillText`) uses bitmap data from `src/data/font/noto-mono-regular-48.json` — a 48×48px glyph atlas keyed by char code.
34
+
35
+ - **`src/common/`** — Shared math & utilities
36
+ - `types.ts` — Vector aliases: `V2`, `V3`, `V4`
37
+ - `array.ts` — Vector math: `add`, `sub`, `mul`, `div`, `scale`, `dot`, `cross`, `normalize`, `distance`, `toBatched`, `range`, etc.
38
+ - `scalar.ts` — Scalar math: `clamp`, `lerp`, `remap`, `smoothstep`, `smin`, `smax`, `fract`, `amod`, `sigmoid`, etc.
39
+ - `colors.ts` — `toRGB(color: string): V3` parses CSS named colors, `#RGB`/`#RRGGBB` hex, and `rgb()`/`rgba()` strings into normalized `[0,1]` triples.
40
+ - `ppm.ts` — `createPPM(width, height, pixels)` serializes RGB pixels to a P3 PPM string.
41
+
42
+ ### Dual build output
43
+
44
+ `tsconfig.esm.json` → `dist/esm` (ES modules, NodeNext)
45
+ `tsconfig.cjs.json` → `dist/cjs` (CommonJS)
46
+ Both exclude `*.test.ts` and `*.sample.ts` files.
47
+
48
+ ### Typical usage pattern
49
+
50
+ ```ts
51
+ import { createCanvas, createPPM } from 'canvas-emulator';
52
+ import fs from 'fs';
53
+
54
+ const canvas = createCanvas({ width: 512, height: 512 });
55
+ const ctx = canvas.getContext2D();
56
+
57
+ ctx.fillStyle = 'blue';
58
+ ctx.fillRect(10, 10, 100, 100);
59
+
60
+ fs.writeFileSync('out.ppm', createPPM(canvas.width, canvas.height, canvas.renderRGB()));
61
+ ```
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # canvas-emulator
2
+
3
+ A headless Canvas 2D rendering library for Node.js. Draw shapes, text, and paths using a familiar `CanvasRenderingContext2D`-style API, then export to PPM image files or raw pixel buffers — no browser required.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add canvas-emulator
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { createCanvas, createPPM } from 'canvas-emulator';
15
+ import fs from 'fs';
16
+
17
+ const canvas = createCanvas({ width: 512, height: 512 });
18
+ const ctx = canvas.getContext2D();
19
+
20
+ // Draw a filled rectangle
21
+ ctx.fillStyle = 'blue';
22
+ ctx.fillRect(50, 50, 200, 150);
23
+
24
+ // Draw a circle
25
+ ctx.save();
26
+ ctx.beginPath();
27
+ ctx.arc(256, 256, 80, 0);
28
+ ctx.closePath();
29
+ ctx.fillStyle = '#ff6600';
30
+ ctx.fill();
31
+ ctx.restore();
32
+
33
+ // Render text
34
+ ctx.fillStyle = 'white';
35
+ ctx.fillText('Hello!', 100, 100, 48);
36
+
37
+ // Write to a PPM image file
38
+ fs.writeFileSync('output.ppm', createPPM(canvas.width, canvas.height, canvas.renderRGB()));
39
+ ```
40
+
41
+ Open PPM files with GIMP, Preview (macOS), or convert them with ImageMagick (`convert output.ppm output.png`).
42
+
43
+ ## API
44
+
45
+ ### Canvas
46
+
47
+ ```ts
48
+ const canvas = createCanvas({ width: number, height: number });
49
+ ```
50
+
51
+ | Member | Description |
52
+ |---|---|
53
+ | `canvas.width / .height` | Dimensions in pixels |
54
+ | `canvas.data` | Raw flat RGBA array, values normalized to `[0, 1]` |
55
+ | `canvas.at([x, y])` | Read pixel at pixel-space coordinates |
56
+ | `canvas.atUV([u, v])` | Read pixel at UV coordinates (`[0,1]` range) |
57
+ | `canvas.setAt([x, y], pixel)` | Write pixel (alpha-composited) |
58
+ | `canvas.each(fn)` | Iterate over every pixel with `(pos, uv)` |
59
+ | `canvas.renderRGB()` | Returns `[r, g, b][]` — suitable for `createPPM` |
60
+ | `canvas.renderRGBA()` | Returns `[r, g, b, a][]` |
61
+ | `canvas.getContext2D()` | Returns a `CanvasContext2D` drawing context |
62
+
63
+ > **Note:** All color channel values are normalized floats in `[0, 1]`, not `[0, 255]`.
64
+
65
+ ### Context 2D
66
+
67
+ The context closely mirrors the browser [`CanvasRenderingContext2D`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D):
68
+
69
+ ```ts
70
+ const ctx = canvas.getContext2D();
71
+
72
+ ctx.fillStyle = 'red'; // CSS color name, #hex, or rgb()/rgba()
73
+ ctx.strokeStyle = '#00ff00';
74
+
75
+ ctx.save(); // push state
76
+ ctx.restore(); // pop state
77
+
78
+ ctx.fillRect(x, y, w, h);
79
+ ctx.strokeRect(x, y, w, h);
80
+ ctx.clearRect(x, y, w, h);
81
+
82
+ ctx.beginPath();
83
+ ctx.moveTo(x, y);
84
+ ctx.lineTo(x, y);
85
+ ctx.arc(x, y, radius, startAngle);
86
+ ctx.rect(x, y, w, h);
87
+ ctx.closePath();
88
+ ctx.fill();
89
+ ctx.stroke();
90
+
91
+ ctx.fillText(text, x, y);
92
+ ctx.fillText(text, x, y, fontSize); // fontSize in pixels (default: 48)
93
+ ctx.measureText(text); // returns { width }
94
+ ```
95
+
96
+ **Supported color formats:** CSS named colors (`red`, `blue`, `coral`, etc.), `#RGB`, `#RRGGBB`, `rgb(r, g, b)`, `rgba(r, g, b, a)`.
97
+
98
+ ### Utilities
99
+
100
+ ```ts
101
+ import { createPPM, toRGB, clamp, lerp, remap, smoothstep } from 'canvas-emulator';
102
+
103
+ // PPM export
104
+ createPPM(width, height, pixels); // pixels: [r, g, b][] normalized to [0,1]
105
+
106
+ // Color parsing
107
+ toRGB('coral'); // → [1, 0.498, 0.314]
108
+ toRGB('#ff8800'); // → [1, 0.533, 0]
109
+
110
+ // Math helpers
111
+ clamp(x, min, max);
112
+ lerp(from, to, t);
113
+ remap(x, [inMin, inMax], [outMin, outMax]);
114
+ smoothstep(edge0, edge1, value);
115
+ ```
116
+
117
+ Vector math (`add`, `sub`, `mul`, `div`, `scale`, `dot`, `normalize`, `distance`, etc.) is also exported for working directly with pixel data.
118
+
119
+ ## Development
120
+
121
+ ```bash
122
+ pnpm build # compile (outputs dist/esm and dist/cjs)
123
+ pnpm check # type-check only
124
+ pnpm test # run tests
125
+ pnpm format # format source files
126
+
127
+ npx tsx playground.ts # run the playground script
128
+ ```
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=playground.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../playground.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const src_1 = require("./src");
7
+ const canvas_1 = require("./src/canvas/canvas");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const canvas = (0, canvas_1.createCanvas)({
10
+ width: 512,
11
+ height: 512
12
+ });
13
+ const ctx = canvas.getContext2D();
14
+ //ctx.save();
15
+ //ctx.beginPath();
16
+ //ctx.lineTo(canvas.width, canvas.height);
17
+ //ctx.closePath();
18
+ //ctx.stroke();
19
+ //ctx.restore();
20
+ //ctx.save();
21
+ //ctx.beginPath();
22
+ //ctx.arc(512/2, 512/2, 64, 0);
23
+ //ctx.closePath();
24
+ //ctx.fillStyle = 'purple';
25
+ //ctx.fill();
26
+ //ctx.restore();
27
+ //
28
+ //ctx.save();
29
+ //ctx.beginPath();
30
+ //ctx.rect((512/2)-16, (512/2)-16, 32, 32);
31
+ //ctx.closePath();
32
+ //ctx.fill();
33
+ //ctx.restore();
34
+ ctx.fillText('hello', 0, 512 / 4, 128);
35
+ const ppm = (0, src_1.createPPM)(canvas.width, canvas.height, canvas.renderRGB());
36
+ fs_1.default.writeFileSync('image.ppm', ppm, { encoding: 'utf-8' });
37
+ //# sourceMappingURL=playground.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playground.js","sourceRoot":"","sources":["../../playground.ts"],"names":[],"mappings":";;;;;AAAA,+BAAkC;AAClC,gDAAmD;AACnD,4CAAoB;AAEpB,MAAM,MAAM,GAAG,IAAA,qBAAY,EAAC;IAC1B,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;CACZ,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;AAGlC,aAAa;AACb,kBAAkB;AAClB,0CAA0C;AAC1C,kBAAkB;AAClB,eAAe;AACf,gBAAgB;AAIhB,aAAa;AACb,kBAAkB;AAClB,+BAA+B;AAC/B,kBAAkB;AAClB,2BAA2B;AAC3B,aAAa;AACb,gBAAgB;AAChB,EAAE;AACF,aAAa;AACb,kBAAkB;AAClB,2CAA2C;AAC3C,kBAAkB;AAClB,aAAa;AACb,gBAAgB;AAEhB,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,GAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAErC,MAAM,GAAG,GAAG,IAAA,eAAS,EAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;AAEvE,YAAE,CAAC,aAAa,CAAC,WAAW,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type CanvasContext2D } from "./context-2d";
2
+ export type Pixel = [number, number, number, number];
3
+ export type Point = [number, number];
4
+ export interface Canvas {
5
+ width: number;
6
+ height: number;
7
+ channels: number;
8
+ data: Array<number>;
9
+ indexAt(p: Point): number;
10
+ at(p: Point): Pixel;
11
+ atUV(p: Point): Pixel;
12
+ setAt(p: Point, px: Pixel | number): void;
13
+ setAtUV(p: Point, px: Pixel | number): void;
14
+ each(fn: (pos: Point, uv: Point) => void): void;
15
+ renderRGBA(): Array<Pixel>;
16
+ renderRGB(): Array<[number, number, number]>;
17
+ getContext2D(): CanvasContext2D;
18
+ }
19
+ export type CanvasConfig = {
20
+ width: number;
21
+ height: number;
22
+ };
23
+ export declare class CanvasImpl implements Canvas {
24
+ width: number;
25
+ height: number;
26
+ channels: 4;
27
+ data: Array<number>;
28
+ private readonly length;
29
+ constructor(cfg: CanvasConfig);
30
+ indexAt(p: Point): number;
31
+ at(p: Point): Pixel;
32
+ atUV(p: Point): Pixel;
33
+ setAt(p: Point, px: Pixel | number): void;
34
+ setAtUV(p: Point, px: Pixel | number): void;
35
+ each(fn: (pos: Point, uv: Point) => void): void;
36
+ renderRGBA(): Array<Pixel>;
37
+ renderRGB(): Array<[number, number, number]>;
38
+ getContext2D(): CanvasContext2D;
39
+ }
40
+ export declare const createCanvas: (cfg: CanvasConfig) => Canvas;
41
+ //# sourceMappingURL=canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../../src/canvas/canvas.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAEzE,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACrD,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErC,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEpB,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B,EAAE,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAEtB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;IAE5C,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAEhD,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,SAAS,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAE7C,YAAY,IAAI,eAAe,CAAC;CACjC;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,UAAW,YAAW,MAAM;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,CAAK;IAChB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,GAAG,EAAE,YAAY;IAQ7B,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM;IAMzB,EAAE,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK;IAKnB,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK;IAMrB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;IAiBzC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;IAM3C,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAU/C,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC;IAI1B,SAAS,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAW5C,YAAY,IAAI,eAAe;CAGhC;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,YAAY,KAAG,MAEhD,CAAC"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCanvas = exports.CanvasImpl = void 0;
4
+ const array_1 = require("../common/array");
5
+ const scalar_1 = require("../common/scalar");
6
+ const context_2d_1 = require("./context-2d");
7
+ class CanvasImpl {
8
+ width;
9
+ height;
10
+ channels = 4;
11
+ data;
12
+ length;
13
+ constructor(cfg) {
14
+ const { width, height } = cfg;
15
+ this.width = width;
16
+ this.height = height;
17
+ this.length = width * height * this.channels;
18
+ this.data = (0, array_1.range)(this.length).fill(0);
19
+ }
20
+ indexAt(p) {
21
+ const x = Math.floor(p[0]);
22
+ const y = Math.floor(p[1]);
23
+ return (0, scalar_1.clamp)((y * this.width + x) * this.channels, 0, this.length - 1);
24
+ }
25
+ at(p) {
26
+ const idx = this.indexAt(p);
27
+ return this.data.slice(idx, idx + this.channels);
28
+ }
29
+ atUV(p) {
30
+ const x = p[0] * this.width;
31
+ const y = p[1] * this.height;
32
+ return this.at([x, y]);
33
+ }
34
+ setAt(p, px) {
35
+ const idx = this.indexAt(p);
36
+ const nextAlpha = typeof px === "number" ? 1.0 : px[3];
37
+ const current = this.at(p);
38
+ const currentAlpha = current[3];
39
+ const nextData = typeof px === "number" ? [px, px, px, px] : px;
40
+ const mixCurrent = (0, scalar_1.smoothstep)(0, 1, currentAlpha - nextAlpha);
41
+ const mixNext = (0, scalar_1.smoothstep)(0, 1, nextAlpha);
42
+ for (let i = 0; i < nextData.length; i++) {
43
+ this.data[idx + i] =
44
+ this.data[idx + i] * mixCurrent + nextData[i] * mixNext;
45
+ }
46
+ }
47
+ setAtUV(p, px) {
48
+ const x = p[0] * this.width;
49
+ const y = p[1] * this.height;
50
+ return this.setAt([x, y], px);
51
+ }
52
+ each(fn) {
53
+ for (let y = 0; y < this.height; y++) {
54
+ for (let x = 0; x < this.width; x++) {
55
+ const p = [x, y];
56
+ const uv = [x / this.width, y / this.height];
57
+ fn(p, uv);
58
+ }
59
+ }
60
+ }
61
+ renderRGBA() {
62
+ return (0, array_1.toBatched)(this.data, this.channels, 0);
63
+ }
64
+ renderRGB() {
65
+ const pixels = [];
66
+ for (let i = 0; i < this.data.length; i += this.channels) {
67
+ const r = this.data[i];
68
+ const g = this.data[i + 1];
69
+ const b = this.data[i + 2];
70
+ pixels.push([r, g, b]);
71
+ }
72
+ return pixels;
73
+ }
74
+ getContext2D() {
75
+ return new context_2d_1.CanvasContext2DImpl(this);
76
+ }
77
+ }
78
+ exports.CanvasImpl = CanvasImpl;
79
+ const createCanvas = (cfg) => {
80
+ return new CanvasImpl(cfg);
81
+ };
82
+ exports.createCanvas = createCanvas;
83
+ //# sourceMappingURL=canvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../../src/canvas/canvas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type Canvas } from "./canvas";
2
+ export type TextMetrics = {
3
+ width: number;
4
+ };
5
+ export type CanvasContext2D = CanvasContext2DImpl;
6
+ export declare class CanvasContext2DImpl {
7
+ canvas: Canvas;
8
+ private stack;
9
+ constructor(canvas: Canvas);
10
+ private get state();
11
+ get fillStyle(): string;
12
+ set fillStyle(value: string);
13
+ get strokeStyle(): string;
14
+ set strokeStyle(value: string);
15
+ save(): void;
16
+ restore(): void;
17
+ moveTo(x: number, y: number): void;
18
+ lineTo(x: number, y: number): void;
19
+ arc(x: number, y: number, radius: number, startAngle: number): void;
20
+ rect(x: number, y: number, width: number, height: number): void;
21
+ fill(): void;
22
+ stroke(): void;
23
+ strokeRect(x: number, y: number, width: number, height: number): void;
24
+ fillRect(x: number, y: number, width: number, height: number): void;
25
+ clearRect(x: number, y: number, width: number, height: number): void;
26
+ private drawChar;
27
+ fillText(text: string, x: number, y: number): void;
28
+ fillText(text: string, x: number, y: number, maxWidth: number): void;
29
+ fillText(text: string, x: number, y: number, maxWidth: undefined): void;
30
+ strokeText(text: string, x: number, y: number): void;
31
+ strokeText(text: string, x: number, y: number, maxWidth: number): void;
32
+ strokeText(text: string, x: number, y: number, maxWidth: undefined): void;
33
+ measureText(text: string): TextMetrics;
34
+ beginPath(): void;
35
+ closePath(): void;
36
+ }
37
+ //# sourceMappingURL=context-2d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-2d.d.ts","sourceRoot":"","sources":["../../../../src/canvas/context-2d.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAKvC,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAsDF,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD,qBAAa,mBAAmB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,KAAK,CAA+B;gBAEhC,MAAM,EAAE,MAAM;IAI1B,OAAO,KAAK,KAAK,GAIhB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAE1B;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI;IAIJ,OAAO,IAAI,IAAI;IAIf,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAIlC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAKlC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAkBnE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAe/D,IAAI,IAAI,IAAI;IAwCZ,MAAM,IAAI,IAAI;IAgCd,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAKrE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAKnE,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAQpE,OAAO,CAAC,QAAQ;IA2ChB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAUvE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IACpD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IACtE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAKzE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAItC,SAAS,IAAI,IAAI;IAGjB,SAAS,IAAI,IAAI;CAGlB"}