zx-kit 0.38.1 → 0.40.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/README.md +5 -0
- package/dist/attrscreen.d.ts +22 -1
- package/dist/attrscreen.d.ts.map +1 -1
- package/dist/attrscreen.js +34 -1
- package/dist/attrscreen.js.map +1 -1
- package/dist/glow.d.ts +104 -0
- package/dist/glow.d.ts.map +1 -0
- package/dist/glow.js +128 -0
- package/dist/glow.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -187,6 +187,7 @@ Zero runtime dependencies, `sideEffects: false`, fully tree-shakeable.
|
|
|
187
187
|
| `lighting` | Dithered cave darkness, one blit per frame | [rendering](docs/rendering.md#lightingts--dithered-cave-darkness) |
|
|
188
188
|
| `audio` | 1-bit beeper: square-wave notes, patterns, stereo pan, volume + built-in auto-hide volume bar | [audio](docs/audio.md#audiots--beeper-audio) |
|
|
189
189
|
| `ay` | AY-3-8912: 3-channel tone, LFSR noise, 16 envelopes, per-channel stereo pan + volume | [audio](docs/audio.md#ayts--ay-3-8912-melodik-audio) |
|
|
190
|
+
| `aydump` | Real ZX-scene tunes: PSG register-dump player, sample-accurate AudioWorklet chip emulator | [audio](docs/audio.md#aydump--real-zx-scene-tunes-psg-register-dumps--experimental-037) |
|
|
190
191
|
| `music` | AY music by note name (`A5`, `C#4`) + looping | [audio](docs/audio.md#musicts--note-name-ay-music) |
|
|
191
192
|
| `collision` | AABB / rect-vs-tile / pixel-precise mask overlap | [collision](docs/collision.md) |
|
|
192
193
|
| `save` | Typed save/load: versioning, migration, slots, throttle, optional tamper signature | [save](docs/save.md) |
|
|
@@ -230,6 +231,8 @@ zx-kit/
|
|
|
230
231
|
│ ├── ay.ts # AY-3-8912: createAY, playAY, AYChannel, AYNote, AYChip,
|
|
231
232
|
│ │ # AYHandle, AYStereoMode (pan / setStereoMode / volume / fade),
|
|
232
233
|
│ │ # AY_VOL, AY_CLOCK, AY_ENVELOPE_SHAPES
|
|
234
|
+
│ ├── aydump.ts # PSG register-dump player: parsePSG, loadPSG, playAYDump,
|
|
235
|
+
│ │ # renderAYDump (AudioWorklet AY/YM chip emulator)
|
|
233
236
|
│ ├── music.ts # noteToFreq, seq, playAYLoop (note-name AY music)
|
|
234
237
|
│ ├── input.ts # initInput, tickMovement, consumeFlag,
|
|
235
238
|
│ │ # consumePause, consumeDebug, consumeAnyKey,
|
|
@@ -248,6 +251,8 @@ zx-kit/
|
|
|
248
251
|
│ ├── camera.ts # scrolling viewport, lerp, deadzone, bounds
|
|
249
252
|
│ ├── scene.ts # stack-based scene manager
|
|
250
253
|
│ ├── save.ts # typed localStorage save/load with migrations
|
|
254
|
+
│ ├── hiscore.ts # high-score table over the save envelope:
|
|
255
|
+
│ │ # createHighScores, insertScore, isHighScore
|
|
251
256
|
│ ├── presentation.ts # blinkVisible, drawBlinkingText, drawTapeStripes, drawMenuOptions
|
|
252
257
|
│ ├── debug.ts # createDebugMonitor, beginFrame/endFrame, drawDebugOverlay
|
|
253
258
|
│ └── i18n.ts # pickLocale runtime locale selection
|
package/dist/attrscreen.d.ts
CHANGED
|
@@ -58,6 +58,10 @@ export interface AttrScreen {
|
|
|
58
58
|
readonly cellInk: Uint32Array;
|
|
59
59
|
/** Packed RGBA paper colour per cell, `cols*rows`. */
|
|
60
60
|
readonly cellPaper: Uint32Array;
|
|
61
|
+
/** Per-cell **GLOW** flag — `1` = emissive light source (opt-in, like the FLASH
|
|
62
|
+
* attribute bit). Set by `stampMono(..., glow=true)`; `0` otherwise. The normal
|
|
63
|
+
* clash flush ignores it; {@link drawAttrGlowSources} feeds it to `glow.ts`. */
|
|
64
|
+
readonly cellGlow: Uint8Array;
|
|
61
65
|
/** Resolved RGBA frame buffer (`width*height*4`), filled by {@link flushAttrScreen}. */
|
|
62
66
|
readonly rgba: Uint8ClampedArray;
|
|
63
67
|
/** Reusable `ImageData` backing {@link rgba} (`null` when headless). */
|
|
@@ -83,8 +87,25 @@ export declare function clearAttrScreen(scr: AttrScreen, paper: SpectrumColor, i
|
|
|
83
87
|
* the pixel plane and re-attributes every cell that receives a lit pixel, per
|
|
84
88
|
* `policy` (default `'both'`). Lit-pixel-only: existing pixels are never cleared,
|
|
85
89
|
* so other sprites/background in a touched cell bleed to the new colour.
|
|
90
|
+
*
|
|
91
|
+
* **`glow`** (default `false`, opt-in): also flags every cell this stamp touches as
|
|
92
|
+
* an emissive GLOW source ({@link AttrScreen.cellGlow}), for `glow.ts`'s bloom via
|
|
93
|
+
* {@link drawAttrGlowSources}. Leaving it `false` is byte-for-byte the old behaviour.
|
|
94
|
+
*/
|
|
95
|
+
export declare function stampMono(scr: AttrScreen, bitmap: Bitmap, x: number, y: number, ink: SpectrumColor, paper: SpectrumColor, policy?: AttrPolicy, glow?: boolean): void;
|
|
96
|
+
/**
|
|
97
|
+
* Draws every {@link AttrScreen.cellGlow}-flagged cell as a solid 8×8 block in its
|
|
98
|
+
* ink colour into the emissive context `g`. Use it as the `drawSources` callback of
|
|
99
|
+
* `glow.ts`'s `renderGlow`, so an attribute screen's glow cells feed the bloom.
|
|
100
|
+
* Cell-level, exactly like the FLASH bit — the whole flagged cell is the light
|
|
101
|
+
* source. Draws nothing when no cell is flagged; needs no canvas of its own.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* renderGlow(glowLayer, ctx, (g) => drawAttrGlowSources(scr, g))
|
|
106
|
+
* ```
|
|
86
107
|
*/
|
|
87
|
-
export declare function
|
|
108
|
+
export declare function drawAttrGlowSources(scr: AttrScreen, g: CanvasRenderingContext2D): void;
|
|
88
109
|
/**
|
|
89
110
|
* Resolves the two planes into RGBA (each pixel takes its cell's ink or paper)
|
|
90
111
|
* and uploads the result with one `putImageData` + `drawImage`. The image is
|
package/dist/attrscreen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attrscreen.d.ts","sourceRoot":"","sources":["../src/attrscreen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,CAAA;AAE3D,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAChC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAChC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAA;CAC1C;AAqBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAK,EAAE,IAAI,SAAK,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"attrscreen.d.ts","sourceRoot":"","sources":["../src/attrscreen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,CAAA;AAE3D,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAC/B;;qFAEiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAA;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAChC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAChC,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAA;CAC1C;AAqBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAK,EAAE,IAAI,SAAK,GAAG,UAAU,CAkCjE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,GAAE,aAAqB,GAAG,IAAI,CAKvG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,aAAa,EACpB,MAAM,GAAE,UAAmB,EAC3B,IAAI,UAAQ,GACX,IAAI,CAgCN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAWtF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,wBAAwB,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAoBpF"}
|
package/dist/attrscreen.js
CHANGED
|
@@ -89,6 +89,7 @@ export function createAttrScreen(cols = 32, rows = 24) {
|
|
|
89
89
|
pixels: new Uint8Array(width * height),
|
|
90
90
|
cellInk: new Uint32Array(cols * rows),
|
|
91
91
|
cellPaper: new Uint32Array(cols * rows),
|
|
92
|
+
cellGlow: new Uint8Array(cols * rows),
|
|
92
93
|
rgba,
|
|
93
94
|
image,
|
|
94
95
|
canvas,
|
|
@@ -103,6 +104,7 @@ export function clearAttrScreen(scr, paper, ink = paper) {
|
|
|
103
104
|
scr.pixels.fill(0);
|
|
104
105
|
scr.cellPaper.fill(hexToU32(paper));
|
|
105
106
|
scr.cellInk.fill(hexToU32(ink));
|
|
107
|
+
scr.cellGlow.fill(0); // glow is per-frame opt-in — nothing glows until a stamp sets it
|
|
106
108
|
}
|
|
107
109
|
/**
|
|
108
110
|
* Stamps a monochrome {@link Bitmap} at screen pixel `(x, y)` (rounded; may be
|
|
@@ -110,8 +112,12 @@ export function clearAttrScreen(scr, paper, ink = paper) {
|
|
|
110
112
|
* the pixel plane and re-attributes every cell that receives a lit pixel, per
|
|
111
113
|
* `policy` (default `'both'`). Lit-pixel-only: existing pixels are never cleared,
|
|
112
114
|
* so other sprites/background in a touched cell bleed to the new colour.
|
|
115
|
+
*
|
|
116
|
+
* **`glow`** (default `false`, opt-in): also flags every cell this stamp touches as
|
|
117
|
+
* an emissive GLOW source ({@link AttrScreen.cellGlow}), for `glow.ts`'s bloom via
|
|
118
|
+
* {@link drawAttrGlowSources}. Leaving it `false` is byte-for-byte the old behaviour.
|
|
113
119
|
*/
|
|
114
|
-
export function stampMono(scr, bitmap, x, y, ink, paper, policy = 'both') {
|
|
120
|
+
export function stampMono(scr, bitmap, x, y, ink, paper, policy = 'both', glow = false) {
|
|
115
121
|
const inkU32 = hexToU32(ink);
|
|
116
122
|
const paperU32 = hexToU32(paper);
|
|
117
123
|
const setInk = policy !== 'paper-only';
|
|
@@ -144,10 +150,37 @@ export function stampMono(scr, bitmap, x, y, ink, paper, policy = 'both') {
|
|
|
144
150
|
scr.cellInk[cell] = inkU32;
|
|
145
151
|
if (setPaper)
|
|
146
152
|
scr.cellPaper[cell] = paperU32;
|
|
153
|
+
if (glow)
|
|
154
|
+
scr.cellGlow[cell] = 1;
|
|
147
155
|
}
|
|
148
156
|
}
|
|
149
157
|
}
|
|
150
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Draws every {@link AttrScreen.cellGlow}-flagged cell as a solid 8×8 block in its
|
|
161
|
+
* ink colour into the emissive context `g`. Use it as the `drawSources` callback of
|
|
162
|
+
* `glow.ts`'s `renderGlow`, so an attribute screen's glow cells feed the bloom.
|
|
163
|
+
* Cell-level, exactly like the FLASH bit — the whole flagged cell is the light
|
|
164
|
+
* source. Draws nothing when no cell is flagged; needs no canvas of its own.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* renderGlow(glowLayer, ctx, (g) => drawAttrGlowSources(scr, g))
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function drawAttrGlowSources(scr, g) {
|
|
172
|
+
const { cellGlow, cellInk, cols, rows } = scr;
|
|
173
|
+
for (let row = 0; row < rows; row++) {
|
|
174
|
+
for (let col = 0; col < cols; col++) {
|
|
175
|
+
const cell = row * cols + col;
|
|
176
|
+
if (!cellGlow[cell])
|
|
177
|
+
continue;
|
|
178
|
+
const u = cellInk[cell]; // packed little-endian RGBA (a<<24 | b<<16 | g<<8 | r)
|
|
179
|
+
g.fillStyle = `rgba(${u & 0xff},${(u >> 8) & 0xff},${(u >> 16) & 0xff},${((u >>> 24) & 0xff) / 255})`;
|
|
180
|
+
g.fillRect(col * CELL, row * CELL, CELL, CELL);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
151
184
|
/**
|
|
152
185
|
* Resolves the two planes into RGBA (each pixel takes its cell's ink or paper)
|
|
153
186
|
* and uploads the result with one `putImageData` + `drawImage`. The image is
|
package/dist/attrscreen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attrscreen.js","sourceRoot":"","sources":["../src/attrscreen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAE,IAAI,EAAsB,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"attrscreen.js","sourceRoot":"","sources":["../src/attrscreen.ts"],"names":[],"mappings":"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"}
|
package/dist/glow.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module glow
|
|
3
|
+
*
|
|
4
|
+
* **Optional phosphor GLOW / bloom — opt-in, purely additive.** The additive twin
|
|
5
|
+
* of {@link "lighting" | renderDarkness}: where lighting *subtracts* (dithered
|
|
6
|
+
* darkness), glow *adds* (a soft emissive bloom). A game marks its **light
|
|
7
|
+
* sources** — a torch, the moon, a sonar/radar blip — and their colour bleeds into
|
|
8
|
+
* the neighbours, so overlapping haloes sum into *perceived* extra colours **on the
|
|
9
|
+
* glass only**, while the crisp pixel-art framebuffer keeps its flat palette.
|
|
10
|
+
*
|
|
11
|
+
* **Emissive, not brighter (path B).** Glow is a marker on *sources of light*, not
|
|
12
|
+
* a lighter flat colour — the palette is never diluted. Everything the game does
|
|
13
|
+
* NOT draw into the glow layer stays exactly as before: this module adds new
|
|
14
|
+
* exports only, touches no existing render path, and a game that never calls
|
|
15
|
+
* {@link createGlowLayer} / {@link renderGlow} is byte-for-byte unaffected (it
|
|
16
|
+
* tree-shakes away).
|
|
17
|
+
*
|
|
18
|
+
* **Cheap bloom, no WebGL:** the emissive layer is **downscaled** to a small buffer
|
|
19
|
+
* and **upscaled** back with bilinear smoothing — that scaling IS the blur, for
|
|
20
|
+
* free — then blitted onto the frame with `globalCompositeOperation = 'lighter'`
|
|
21
|
+
* (additive). One offscreen buffer, a couple of `drawImage`s, headless-safe.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const glow = createGlowLayer(256, 192) // once, view-sized
|
|
26
|
+
* // each frame, AFTER drawing the scene, BEFORE scanlines:
|
|
27
|
+
* renderGlow(glow, ctx, (g) => {
|
|
28
|
+
* drawGlowSource(g, { x: torchX, y: torchY, radius: 24, color: C.B_YELLOW, intensity: 0.9 })
|
|
29
|
+
* drawGlowSource(g, { x: moonX, y: moonY, radius: 40, color: C.B_WHITE, intensity: 0.5 })
|
|
30
|
+
* // …or draw sprites/pixels directly for pixel-exact glow
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
import { type SpectrumColor } from './palette.js';
|
|
35
|
+
/** Tunables for a {@link GlowLayer}. All optional; defaults are an eye-tuning start. */
|
|
36
|
+
export interface GlowOptions {
|
|
37
|
+
/** Blur strength: the emissive layer is scaled down by this factor before the
|
|
38
|
+
* bilinear upscale spreads it. Bigger = softer & cheaper. Clamped ≥ 1. Default `4`. */
|
|
39
|
+
downscale?: number;
|
|
40
|
+
/** How many additive blits of the bloom (brightness/saturation of the halo).
|
|
41
|
+
* Clamped ≥ 1. Default `1`. */
|
|
42
|
+
passes?: number;
|
|
43
|
+
/** Global bloom strength, `0..1` — the `globalAlpha` of each additive blit.
|
|
44
|
+
* Clamped to `0..1`. Default `0.5`. */
|
|
45
|
+
alpha?: number;
|
|
46
|
+
}
|
|
47
|
+
/** A view-sized glow layer: one emissive canvas + one downscale buffer. Allocate
|
|
48
|
+
* once, reuse across frames. Both canvases are `null` when headless (no document). */
|
|
49
|
+
export interface GlowLayer {
|
|
50
|
+
readonly width: number;
|
|
51
|
+
readonly height: number;
|
|
52
|
+
readonly downscale: number;
|
|
53
|
+
readonly passes: number;
|
|
54
|
+
readonly alpha: number;
|
|
55
|
+
/** Emissive layer — sources drawn here each frame (transparent elsewhere). `null` headless. */
|
|
56
|
+
readonly source: HTMLCanvasElement | null;
|
|
57
|
+
/** Small downscale buffer (the blur). `null` headless. */
|
|
58
|
+
readonly blur: HTMLCanvasElement | null;
|
|
59
|
+
}
|
|
60
|
+
/** A single light source for {@link drawGlowSource}: a soft coloured blob whose
|
|
61
|
+
* bloom is scaled by `intensity`. Blooms in its **own** `color` — never white-only. */
|
|
62
|
+
export interface GlowSource {
|
|
63
|
+
/** Centre in screen px. */
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
/** Reach in px (the blob's radius). */
|
|
67
|
+
radius: number;
|
|
68
|
+
/** The source's colour — the halo takes this colour. */
|
|
69
|
+
color: SpectrumColor;
|
|
70
|
+
/** Brightness scale `0..1` (default `1`). A dim torch vs a bright radar. */
|
|
71
|
+
intensity?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Downscale-buffer dimensions for a `width`×`height` view at `downscale` — pure,
|
|
75
|
+
* exported for tests. Each axis is floored, never below 1.
|
|
76
|
+
*/
|
|
77
|
+
export declare function glowBufferSize(width: number, height: number, downscale: number): {
|
|
78
|
+
w: number;
|
|
79
|
+
h: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Creates a view-sized glow layer. `opts` tune the bloom ({@link GlowOptions}).
|
|
83
|
+
* Allocate once; reuse. Headless (no `document`): both canvases are `null` and
|
|
84
|
+
* {@link renderGlow} becomes a no-op.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createGlowLayer(width: number, height: number, opts?: GlowOptions): GlowLayer;
|
|
87
|
+
/**
|
|
88
|
+
* Draws one {@link GlowSource} — a soft radial blob in the source's own colour,
|
|
89
|
+
* scaled by `intensity` — into the emissive canvas context `g` (the one handed to
|
|
90
|
+
* the {@link renderGlow} callback). The blob fades to transparent at `radius`, so
|
|
91
|
+
* it adds nothing outside its reach when the layer is blitted with `'lighter'`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function drawGlowSource(g: CanvasRenderingContext2D, source: GlowSource): void;
|
|
94
|
+
/**
|
|
95
|
+
* Renders the bloom onto `ctx`, additively. Call each frame AFTER the scene is
|
|
96
|
+
* drawn (and before scanlines / `curveDisplay`). `drawSources` receives the
|
|
97
|
+
* emissive canvas context — draw your light sources into it (via
|
|
98
|
+
* {@link drawGlowSource} or any drawing). Their colour is then blurred (downscale
|
|
99
|
+
* → bilinear upscale) and blitted onto `ctx` with `'lighter'` at the layer's
|
|
100
|
+
* `alpha`, `passes` times. Restores `ctx`'s composite/alpha/smoothing afterwards,
|
|
101
|
+
* so it composes cleanly with the rest of the frame. Headless: a silent no-op.
|
|
102
|
+
*/
|
|
103
|
+
export declare function renderGlow(layer: GlowLayer, ctx: CanvasRenderingContext2D, drawSources: (g: CanvasRenderingContext2D) => void): void;
|
|
104
|
+
//# sourceMappingURL=glow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glow.d.ts","sourceRoot":"","sources":["../src/glow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD,wFAAwF;AACxF,MAAM,WAAW,WAAW;IAC1B;4FACwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;oCACgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;4CACwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;uFACuF;AACvF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAA;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAA;CACxC;AAED;wFACwF;AACxF,MAAM,WAAW,UAAU;IACzB,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,KAAK,EAAE,aAAa,CAAA;IACpB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAQD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAGzG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,SAAS,CAqBhG;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,wBAAwB,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAWpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,wBAAwB,EAC7B,WAAW,EAAE,CAAC,CAAC,EAAE,wBAAwB,KAAK,IAAI,GACjD,IAAI,CA6BN"}
|
package/dist/glow.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module glow
|
|
3
|
+
*
|
|
4
|
+
* **Optional phosphor GLOW / bloom — opt-in, purely additive.** The additive twin
|
|
5
|
+
* of {@link "lighting" | renderDarkness}: where lighting *subtracts* (dithered
|
|
6
|
+
* darkness), glow *adds* (a soft emissive bloom). A game marks its **light
|
|
7
|
+
* sources** — a torch, the moon, a sonar/radar blip — and their colour bleeds into
|
|
8
|
+
* the neighbours, so overlapping haloes sum into *perceived* extra colours **on the
|
|
9
|
+
* glass only**, while the crisp pixel-art framebuffer keeps its flat palette.
|
|
10
|
+
*
|
|
11
|
+
* **Emissive, not brighter (path B).** Glow is a marker on *sources of light*, not
|
|
12
|
+
* a lighter flat colour — the palette is never diluted. Everything the game does
|
|
13
|
+
* NOT draw into the glow layer stays exactly as before: this module adds new
|
|
14
|
+
* exports only, touches no existing render path, and a game that never calls
|
|
15
|
+
* {@link createGlowLayer} / {@link renderGlow} is byte-for-byte unaffected (it
|
|
16
|
+
* tree-shakes away).
|
|
17
|
+
*
|
|
18
|
+
* **Cheap bloom, no WebGL:** the emissive layer is **downscaled** to a small buffer
|
|
19
|
+
* and **upscaled** back with bilinear smoothing — that scaling IS the blur, for
|
|
20
|
+
* free — then blitted onto the frame with `globalCompositeOperation = 'lighter'`
|
|
21
|
+
* (additive). One offscreen buffer, a couple of `drawImage`s, headless-safe.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const glow = createGlowLayer(256, 192) // once, view-sized
|
|
26
|
+
* // each frame, AFTER drawing the scene, BEFORE scanlines:
|
|
27
|
+
* renderGlow(glow, ctx, (g) => {
|
|
28
|
+
* drawGlowSource(g, { x: torchX, y: torchY, radius: 24, color: C.B_YELLOW, intensity: 0.9 })
|
|
29
|
+
* drawGlowSource(g, { x: moonX, y: moonY, radius: 40, color: C.B_WHITE, intensity: 0.5 })
|
|
30
|
+
* // …or draw sprites/pixels directly for pixel-exact glow
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
import {} from './palette.js';
|
|
35
|
+
const clampInt1 = (n, dflt) => Number.isFinite(n) && n >= 1 ? Math.floor(n) : dflt;
|
|
36
|
+
const clamp01 = (n, dflt) => Number.isFinite(n) ? (n < 0 ? 0 : n > 1 ? 1 : n) : dflt;
|
|
37
|
+
/**
|
|
38
|
+
* Downscale-buffer dimensions for a `width`×`height` view at `downscale` — pure,
|
|
39
|
+
* exported for tests. Each axis is floored, never below 1.
|
|
40
|
+
*/
|
|
41
|
+
export function glowBufferSize(width, height, downscale) {
|
|
42
|
+
const d = clampInt1(downscale, 4);
|
|
43
|
+
return { w: Math.max(1, Math.floor(width / d)), h: Math.max(1, Math.floor(height / d)) };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a view-sized glow layer. `opts` tune the bloom ({@link GlowOptions}).
|
|
47
|
+
* Allocate once; reuse. Headless (no `document`): both canvases are `null` and
|
|
48
|
+
* {@link renderGlow} becomes a no-op.
|
|
49
|
+
*/
|
|
50
|
+
export function createGlowLayer(width, height, opts = {}) {
|
|
51
|
+
if (!Number.isInteger(width) || width <= 0 || !Number.isInteger(height) || height <= 0) {
|
|
52
|
+
throw new Error(`createGlowLayer: width and height must be positive integers, got ${width}×${height}`);
|
|
53
|
+
}
|
|
54
|
+
const downscale = clampInt1(opts.downscale ?? 4, 4);
|
|
55
|
+
const passes = clampInt1(opts.passes ?? 1, 1);
|
|
56
|
+
const alpha = clamp01(opts.alpha ?? 0.5, 0.5);
|
|
57
|
+
let source = null;
|
|
58
|
+
let blur = null;
|
|
59
|
+
if (typeof document !== 'undefined') {
|
|
60
|
+
const { w, h } = glowBufferSize(width, height, downscale);
|
|
61
|
+
source = document.createElement('canvas');
|
|
62
|
+
source.width = width;
|
|
63
|
+
source.height = height;
|
|
64
|
+
blur = document.createElement('canvas');
|
|
65
|
+
blur.width = w;
|
|
66
|
+
blur.height = h;
|
|
67
|
+
}
|
|
68
|
+
return { width, height, downscale, passes, alpha, source, blur };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Draws one {@link GlowSource} — a soft radial blob in the source's own colour,
|
|
72
|
+
* scaled by `intensity` — into the emissive canvas context `g` (the one handed to
|
|
73
|
+
* the {@link renderGlow} callback). The blob fades to transparent at `radius`, so
|
|
74
|
+
* it adds nothing outside its reach when the layer is blitted with `'lighter'`.
|
|
75
|
+
*/
|
|
76
|
+
export function drawGlowSource(g, source) {
|
|
77
|
+
const intensity = clamp01(source.intensity ?? 1, 1);
|
|
78
|
+
if (intensity <= 0 || source.radius <= 0)
|
|
79
|
+
return;
|
|
80
|
+
const grad = g.createRadialGradient(source.x, source.y, 0, source.x, source.y, source.radius);
|
|
81
|
+
grad.addColorStop(0, source.color);
|
|
82
|
+
grad.addColorStop(1, 'transparent');
|
|
83
|
+
const prevAlpha = g.globalAlpha;
|
|
84
|
+
g.globalAlpha = intensity;
|
|
85
|
+
g.fillStyle = grad;
|
|
86
|
+
g.fillRect(source.x - source.radius, source.y - source.radius, source.radius * 2, source.radius * 2);
|
|
87
|
+
g.globalAlpha = prevAlpha;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Renders the bloom onto `ctx`, additively. Call each frame AFTER the scene is
|
|
91
|
+
* drawn (and before scanlines / `curveDisplay`). `drawSources` receives the
|
|
92
|
+
* emissive canvas context — draw your light sources into it (via
|
|
93
|
+
* {@link drawGlowSource} or any drawing). Their colour is then blurred (downscale
|
|
94
|
+
* → bilinear upscale) and blitted onto `ctx` with `'lighter'` at the layer's
|
|
95
|
+
* `alpha`, `passes` times. Restores `ctx`'s composite/alpha/smoothing afterwards,
|
|
96
|
+
* so it composes cleanly with the rest of the frame. Headless: a silent no-op.
|
|
97
|
+
*/
|
|
98
|
+
export function renderGlow(layer, ctx, drawSources) {
|
|
99
|
+
const { source, blur, width, height, passes, alpha } = layer;
|
|
100
|
+
if (!source || !blur)
|
|
101
|
+
return; // headless → nothing to draw
|
|
102
|
+
// Draw the emissive sources, then downscale into the small blur buffer. In a
|
|
103
|
+
// browser both contexts exist; headless-ish (jsdom, getContext null) this block
|
|
104
|
+
// is skipped and only the blit below runs — mirrors renderDarkness.
|
|
105
|
+
const g = source.getContext('2d');
|
|
106
|
+
const b = blur.getContext('2d');
|
|
107
|
+
if (g && b) {
|
|
108
|
+
g.clearRect(0, 0, width, height); // transparent = additive-neutral
|
|
109
|
+
drawSources(g);
|
|
110
|
+
b.imageSmoothingEnabled = true;
|
|
111
|
+
b.clearRect(0, 0, blur.width, blur.height);
|
|
112
|
+
b.drawImage(source, 0, 0, blur.width, blur.height); // downscale (average)
|
|
113
|
+
}
|
|
114
|
+
// Upscale + additive blit: drawing the small buffer back to full size with
|
|
115
|
+
// smoothing on IS the blur; 'lighter' sums the haloes over the flat scene.
|
|
116
|
+
const prevSmooth = ctx.imageSmoothingEnabled;
|
|
117
|
+
const prevOp = ctx.globalCompositeOperation;
|
|
118
|
+
const prevAlpha = ctx.globalAlpha;
|
|
119
|
+
ctx.imageSmoothingEnabled = true;
|
|
120
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
121
|
+
ctx.globalAlpha = alpha;
|
|
122
|
+
for (let i = 0; i < passes; i++)
|
|
123
|
+
ctx.drawImage(blur, 0, 0, width, height);
|
|
124
|
+
ctx.imageSmoothingEnabled = prevSmooth;
|
|
125
|
+
ctx.globalCompositeOperation = prevOp;
|
|
126
|
+
ctx.globalAlpha = prevAlpha;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=glow.js.map
|
package/dist/glow.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glow.js","sourceRoot":"","sources":["../src/glow.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zx-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zrebec/zx-kit.git"
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc",
|
|
25
25
|
"prepublishOnly": "npm run build",
|
|
26
|
-
"test": "vitest run"
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:coverage": "vitest run --coverage"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
30
|
"zx-spectrum",
|