@motion-script/web 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/README.md +47 -0
- package/dist/audio/player.d.ts +43 -0
- package/dist/audio/player.d.ts.map +1 -0
- package/dist/audio/player.js +165 -0
- package/dist/audio/player.js.map +1 -0
- package/dist/effects/bloom.d.ts +19 -0
- package/dist/effects/bloom.d.ts.map +1 -0
- package/dist/effects/bloom.js +64 -0
- package/dist/effects/bloom.js.map +1 -0
- package/dist/effects/blur.d.ts +8 -0
- package/dist/effects/blur.d.ts.map +1 -0
- package/dist/effects/blur.js +12 -0
- package/dist/effects/blur.js.map +1 -0
- package/dist/effects/bulge-pinch.d.ts +20 -0
- package/dist/effects/bulge-pinch.d.ts.map +1 -0
- package/dist/effects/bulge-pinch.js +86 -0
- package/dist/effects/bulge-pinch.js.map +1 -0
- package/dist/effects/chromatic-aberration.d.ts +19 -0
- package/dist/effects/chromatic-aberration.d.ts.map +1 -0
- package/dist/effects/chromatic-aberration.js +59 -0
- package/dist/effects/chromatic-aberration.js.map +1 -0
- package/dist/effects/effect.d.ts +32 -0
- package/dist/effects/effect.d.ts.map +1 -0
- package/dist/effects/effect.js +22 -0
- package/dist/effects/effect.js.map +1 -0
- package/dist/effects/grayscale.d.ts +12 -0
- package/dist/effects/grayscale.d.ts.map +1 -0
- package/dist/effects/grayscale.js +31 -0
- package/dist/effects/grayscale.js.map +1 -0
- package/dist/effects/index.d.ts +13 -0
- package/dist/effects/index.d.ts.map +1 -0
- package/dist/effects/index.js +13 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/effects/pixelate.d.ts +23 -0
- package/dist/effects/pixelate.d.ts.map +1 -0
- package/dist/effects/pixelate.js +37 -0
- package/dist/effects/pixelate.js.map +1 -0
- package/dist/effects/registry.d.ts +17 -0
- package/dist/effects/registry.d.ts.map +1 -0
- package/dist/effects/registry.js +56 -0
- package/dist/effects/registry.js.map +1 -0
- package/dist/effects/sksl-cache.d.ts +6 -0
- package/dist/effects/sksl-cache.d.ts.map +1 -0
- package/dist/effects/sksl-cache.js +21 -0
- package/dist/effects/sksl-cache.js.map +1 -0
- package/dist/effects/sksl-layer.d.ts +30 -0
- package/dist/effects/sksl-layer.d.ts.map +1 -0
- package/dist/effects/sksl-layer.js +82 -0
- package/dist/effects/sksl-layer.js.map +1 -0
- package/dist/effects/texture.d.ts +31 -0
- package/dist/effects/texture.d.ts.map +1 -0
- package/dist/effects/texture.js +66 -0
- package/dist/effects/texture.js.map +1 -0
- package/dist/effects/vintage.d.ts +20 -0
- package/dist/effects/vintage.d.ts.map +1 -0
- package/dist/effects/vintage.js +47 -0
- package/dist/effects/vintage.js.map +1 -0
- package/dist/effects/zoom.d.ts +20 -0
- package/dist/effects/zoom.d.ts.map +1 -0
- package/dist/effects/zoom.js +65 -0
- package/dist/effects/zoom.js.map +1 -0
- package/dist/exporter.d.ts +24 -0
- package/dist/exporter.d.ts.map +1 -0
- package/dist/exporter.js +177 -0
- package/dist/exporter.js.map +1 -0
- package/dist/fills/conic-gradient.d.ts +12 -0
- package/dist/fills/conic-gradient.d.ts.map +1 -0
- package/dist/fills/conic-gradient.js +44 -0
- package/dist/fills/conic-gradient.js.map +1 -0
- package/dist/fills/filters/alpha.d.ts +9 -0
- package/dist/fills/filters/alpha.d.ts.map +1 -0
- package/dist/fills/filters/alpha.js +21 -0
- package/dist/fills/filters/alpha.js.map +1 -0
- package/dist/fills/filters/blur.d.ts +9 -0
- package/dist/fills/filters/blur.d.ts.map +1 -0
- package/dist/fills/filters/blur.js +12 -0
- package/dist/fills/filters/blur.js.map +1 -0
- package/dist/fills/filters/color-adjustment.d.ts +14 -0
- package/dist/fills/filters/color-adjustment.d.ts.map +1 -0
- package/dist/fills/filters/color-adjustment.js +147 -0
- package/dist/fills/filters/color-adjustment.js.map +1 -0
- package/dist/fills/filters/color-matrix.d.ts +9 -0
- package/dist/fills/filters/color-matrix.d.ts.map +1 -0
- package/dist/fills/filters/color-matrix.js +14 -0
- package/dist/fills/filters/color-matrix.js.map +1 -0
- package/dist/fills/filters/curves.d.ts +9 -0
- package/dist/fills/filters/curves.d.ts.map +1 -0
- package/dist/fills/filters/curves.js +89 -0
- package/dist/fills/filters/curves.js.map +1 -0
- package/dist/fills/filters/exposure.d.ts +9 -0
- package/dist/fills/filters/exposure.d.ts.map +1 -0
- package/dist/fills/filters/exposure.js +22 -0
- package/dist/fills/filters/exposure.js.map +1 -0
- package/dist/fills/filters/filter.d.ts +17 -0
- package/dist/fills/filters/filter.d.ts.map +1 -0
- package/dist/fills/filters/filter.js +16 -0
- package/dist/fills/filters/filter.js.map +1 -0
- package/dist/fills/filters/grayscale.d.ts +9 -0
- package/dist/fills/filters/grayscale.d.ts.map +1 -0
- package/dist/fills/filters/grayscale.js +25 -0
- package/dist/fills/filters/grayscale.js.map +1 -0
- package/dist/fills/filters/registry.d.ts +16 -0
- package/dist/fills/filters/registry.d.ts.map +1 -0
- package/dist/fills/filters/registry.js +50 -0
- package/dist/fills/filters/registry.js.map +1 -0
- package/dist/fills/gradient-cache.d.ts +29 -0
- package/dist/fills/gradient-cache.d.ts.map +1 -0
- package/dist/fills/gradient-cache.js +57 -0
- package/dist/fills/gradient-cache.js.map +1 -0
- package/dist/fills/handler.d.ts +49 -0
- package/dist/fills/handler.d.ts.map +1 -0
- package/dist/fills/handler.js +172 -0
- package/dist/fills/handler.js.map +1 -0
- package/dist/fills/image.d.ts +34 -0
- package/dist/fills/image.d.ts.map +1 -0
- package/dist/fills/image.js +91 -0
- package/dist/fills/image.js.map +1 -0
- package/dist/fills/linear-gradient.d.ts +12 -0
- package/dist/fills/linear-gradient.d.ts.map +1 -0
- package/dist/fills/linear-gradient.js +48 -0
- package/dist/fills/linear-gradient.js.map +1 -0
- package/dist/fills/noise.d.ts +11 -0
- package/dist/fills/noise.d.ts.map +1 -0
- package/dist/fills/noise.js +82 -0
- package/dist/fills/noise.js.map +1 -0
- package/dist/fills/radial-gradient.d.ts +9 -0
- package/dist/fills/radial-gradient.d.ts.map +1 -0
- package/dist/fills/radial-gradient.js +43 -0
- package/dist/fills/radial-gradient.js.map +1 -0
- package/dist/fills/registry.d.ts +10 -0
- package/dist/fills/registry.d.ts.map +1 -0
- package/dist/fills/registry.js +34 -0
- package/dist/fills/registry.js.map +1 -0
- package/dist/fills/renderer.d.ts +24 -0
- package/dist/fills/renderer.d.ts.map +1 -0
- package/dist/fills/renderer.js +5 -0
- package/dist/fills/renderer.js.map +1 -0
- package/dist/fills/solid.d.ts +7 -0
- package/dist/fills/solid.d.ts.map +1 -0
- package/dist/fills/solid.js +13 -0
- package/dist/fills/solid.js.map +1 -0
- package/dist/fills/stripe.d.ts +7 -0
- package/dist/fills/stripe.d.ts.map +1 -0
- package/dist/fills/stripe.js +85 -0
- package/dist/fills/stripe.js.map +1 -0
- package/dist/fills/video.d.ts +6 -0
- package/dist/fills/video.d.ts.map +1 -0
- package/dist/fills/video.js +14 -0
- package/dist/fills/video.js.map +1 -0
- package/dist/font-style.d.ts +23 -0
- package/dist/font-style.d.ts.map +1 -0
- package/dist/font-style.js +34 -0
- package/dist/font-style.js.map +1 -0
- package/dist/getter.d.ts +9 -0
- package/dist/getter.d.ts.map +1 -0
- package/dist/getter.js +26 -0
- package/dist/getter.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/master-clock.d.ts +42 -0
- package/dist/master-clock.d.ts.map +1 -0
- package/dist/master-clock.js +134 -0
- package/dist/master-clock.js.map +1 -0
- package/dist/measure-scope.d.ts +14 -0
- package/dist/measure-scope.d.ts.map +1 -0
- package/dist/measure-scope.js +29 -0
- package/dist/measure-scope.js.map +1 -0
- package/dist/render-context.d.ts +107 -0
- package/dist/render-context.d.ts.map +1 -0
- package/dist/render-context.js +940 -0
- package/dist/render-context.js.map +1 -0
- package/dist/shapes/alpha-contour.d.ts +27 -0
- package/dist/shapes/alpha-contour.d.ts.map +1 -0
- package/dist/shapes/alpha-contour.js +330 -0
- package/dist/shapes/alpha-contour.js.map +1 -0
- package/dist/shapes/base.d.ts +46 -0
- package/dist/shapes/base.d.ts.map +1 -0
- package/dist/shapes/base.js +95 -0
- package/dist/shapes/base.js.map +1 -0
- package/dist/shapes/boolean.d.ts +28 -0
- package/dist/shapes/boolean.d.ts.map +1 -0
- package/dist/shapes/boolean.js +90 -0
- package/dist/shapes/boolean.js.map +1 -0
- package/dist/shapes/ellipse.d.ts +32 -0
- package/dist/shapes/ellipse.d.ts.map +1 -0
- package/dist/shapes/ellipse.js +50 -0
- package/dist/shapes/ellipse.js.map +1 -0
- package/dist/shapes/image.d.ts +66 -0
- package/dist/shapes/image.d.ts.map +1 -0
- package/dist/shapes/image.js +214 -0
- package/dist/shapes/image.js.map +1 -0
- package/dist/shapes/index.d.ts +67 -0
- package/dist/shapes/index.d.ts.map +1 -0
- package/dist/shapes/index.js +297 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/line.d.ts +25 -0
- package/dist/shapes/line.d.ts.map +1 -0
- package/dist/shapes/line.js +87 -0
- package/dist/shapes/line.js.map +1 -0
- package/dist/shapes/mask.d.ts +28 -0
- package/dist/shapes/mask.d.ts.map +1 -0
- package/dist/shapes/mask.js +106 -0
- package/dist/shapes/mask.js.map +1 -0
- package/dist/shapes/paragraph-layout.d.ts +64 -0
- package/dist/shapes/paragraph-layout.d.ts.map +1 -0
- package/dist/shapes/paragraph-layout.js +156 -0
- package/dist/shapes/paragraph-layout.js.map +1 -0
- package/dist/shapes/path.d.ts +29 -0
- package/dist/shapes/path.d.ts.map +1 -0
- package/dist/shapes/path.js +71 -0
- package/dist/shapes/path.js.map +1 -0
- package/dist/shapes/polygon.d.ts +33 -0
- package/dist/shapes/polygon.d.ts.map +1 -0
- package/dist/shapes/polygon.js +86 -0
- package/dist/shapes/polygon.js.map +1 -0
- package/dist/shapes/polygram.d.ts +34 -0
- package/dist/shapes/polygram.d.ts.map +1 -0
- package/dist/shapes/polygram.js +90 -0
- package/dist/shapes/polygram.js.map +1 -0
- package/dist/shapes/rect.d.ts +41 -0
- package/dist/shapes/rect.d.ts.map +1 -0
- package/dist/shapes/rect.js +111 -0
- package/dist/shapes/rect.js.map +1 -0
- package/dist/shapes/richtext.d.ts +28 -0
- package/dist/shapes/richtext.d.ts.map +1 -0
- package/dist/shapes/richtext.js +32 -0
- package/dist/shapes/richtext.js.map +1 -0
- package/dist/shapes/shape-handler.d.ts +79 -0
- package/dist/shapes/shape-handler.d.ts.map +1 -0
- package/dist/shapes/shape-handler.js +304 -0
- package/dist/shapes/shape-handler.js.map +1 -0
- package/dist/shapes/text.d.ts +13 -0
- package/dist/shapes/text.d.ts.map +1 -0
- package/dist/shapes/text.js +67 -0
- package/dist/shapes/text.js.map +1 -0
- package/dist/shapes/trim.d.ts +10 -0
- package/dist/shapes/trim.d.ts.map +1 -0
- package/dist/shapes/trim.js +49 -0
- package/dist/shapes/trim.js.map +1 -0
- package/dist/storage-adapter.d.ts +56 -0
- package/dist/storage-adapter.d.ts.map +1 -0
- package/dist/storage-adapter.js +188 -0
- package/dist/storage-adapter.js.map +1 -0
- package/dist/stroke/index.d.ts +34 -0
- package/dist/stroke/index.d.ts.map +1 -0
- package/dist/stroke/index.js +360 -0
- package/dist/stroke/index.js.map +1 -0
- package/dist/stroke/stroke-handler.d.ts +45 -0
- package/dist/stroke/stroke-handler.d.ts.map +1 -0
- package/dist/stroke/stroke-handler.js +371 -0
- package/dist/stroke/stroke-handler.js.map +1 -0
- package/dist/video/extract.d.ts +54 -0
- package/dist/video/extract.d.ts.map +1 -0
- package/dist/video/extract.js +192 -0
- package/dist/video/extract.js.map +1 -0
- package/dist/video/extract.worker.d.ts +50 -0
- package/dist/video/extract.worker.d.ts.map +1 -0
- package/dist/video/extract.worker.js +224 -0
- package/dist/video/extract.worker.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @motion-script/web
|
|
2
|
+
|
|
3
|
+
The browser rendering backend for [Motion Script](https://motionscript.dev),
|
|
4
|
+
built on Skia via [`@motion-script/canvaskit`](../canvaskit). It implements the
|
|
5
|
+
`platform`/`render` abstractions from [`@motion-script/core`](../core) so
|
|
6
|
+
scenes can be drawn to a canvas, played back with synchronized audio, and
|
|
7
|
+
exported to video all in the browser.
|
|
8
|
+
|
|
9
|
+
## What's in here
|
|
10
|
+
|
|
11
|
+
- **Render context** — `WebRenderContext` draws the `@motion-script/core` scene
|
|
12
|
+
graph (shapes, fills, strokes, effects, text, video, masks) onto a CanvasKit
|
|
13
|
+
surface.
|
|
14
|
+
- **Exporter** — `exportScenesAsVideo` renders one or more scenes offline and
|
|
15
|
+
muxes video and audio into an MP4 using [mediabunny](https://github.com/Vanilagy/mediabunny).
|
|
16
|
+
- **Audio** — `WebAudioPlayer` drives playback through the Web Audio API.
|
|
17
|
+
- **Storage / assets** — `WebStorageAdapter` and `WebMeasureScope` implement the
|
|
18
|
+
asset-loading and text-measurement abstractions `@motion-script/core` needs.
|
|
19
|
+
- **Clock** — `WebMasterClock` synchronizes scene playback to `requestAnimationFrame`.
|
|
20
|
+
- **CanvasKit access** — `getCanvasKit` loads and caches the CanvasKit/Skia
|
|
21
|
+
WebAssembly module used for rendering.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @motion-script/web @motion-script/core @motion-script/canvaskit
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For a guided setup, scaffold a project instead with:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm create motion-script@latest
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
See the [docs](https://motionscript.dev/docs) for the full feature set and API
|
|
36
|
+
reference.
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
From the monorepo root:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm --filter @motion-script/web build
|
|
44
|
+
pnpm --filter @motion-script/web test
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for the architecture overview.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AudioDevice, AudioRequest } from "@motion-script/core";
|
|
2
|
+
/**
|
|
3
|
+
* Web Audio implementation of {@link AudioDevice} — decodes and caches source
|
|
4
|
+
* buffers, then schedules/plays {@link AudioRequest}s as `AudioBufferSourceNode`s
|
|
5
|
+
* through a shared master gain (for global mute). `schedule` registers the
|
|
6
|
+
* active set of requests for the current frame; `syncTo` then starts/stops
|
|
7
|
+
* sources to match `sceneTime`, so playback follows scrubbing and seeks.
|
|
8
|
+
*/
|
|
9
|
+
export declare class WebAudioDevice extends AudioDevice {
|
|
10
|
+
private context;
|
|
11
|
+
private ownsContext;
|
|
12
|
+
private bufferCache;
|
|
13
|
+
private decoding;
|
|
14
|
+
private active;
|
|
15
|
+
private scheduled;
|
|
16
|
+
private disposed;
|
|
17
|
+
private masterGain;
|
|
18
|
+
private muted;
|
|
19
|
+
constructor(context?: AudioContext);
|
|
20
|
+
getContext(): AudioContext;
|
|
21
|
+
/**
|
|
22
|
+
* Resume the AudioContext. Must be called from within a user gesture
|
|
23
|
+
* (e.g. a click handler) the first time, or browsers keep the context
|
|
24
|
+
* suspended and no audio is heard.
|
|
25
|
+
*/
|
|
26
|
+
unlock(): void;
|
|
27
|
+
has(src: string): boolean;
|
|
28
|
+
append(src: string, data: ArrayBuffer): Promise<void>;
|
|
29
|
+
/** Drops cached buffers and stops active sources for srcs/requests no longer referenced — called when the asset set changes (e.g. seeking across scene boundaries). */
|
|
30
|
+
retain(keep: ReadonlySet<string>): void;
|
|
31
|
+
/** Replaces the active request set; stops any currently-playing source whose request is no longer scheduled. Actual start/stop-to-match-time happens in `syncTo`. */
|
|
32
|
+
schedule(requests: readonly AudioRequest[]): void;
|
|
33
|
+
/** Starts sources whose `[startAt, endAt)` window now contains `sceneTime` (computing the correct buffer offset for the seek point) and stops any that have fallen outside it. */
|
|
34
|
+
syncTo(sceneTime: number): void;
|
|
35
|
+
play(time: number, _speed: number, _reverse: boolean): Promise<void>;
|
|
36
|
+
stop(): void;
|
|
37
|
+
setMuted(muted: boolean): void;
|
|
38
|
+
/** Starts a buffer source mid-clip if `sceneTime` lands after the request's start — `audioOffset` accounts for both the request's trim and the elapsed time since `startAt`. */
|
|
39
|
+
private playBuffer;
|
|
40
|
+
private stopSource;
|
|
41
|
+
dispose(): void;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=player.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/audio/player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQhE;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAC3C,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,CAAC,EAAE,YAAY;IAQlC,UAAU,IAAI,YAAY;IAI1B;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAMd,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAInB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3D,uKAAuK;IACvK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IASvC,qKAAqK;IACrK,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAQjD,kLAAkL;IAClL,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAoBzB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1E,IAAI,IAAI,IAAI;IAMZ,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK9B,gLAAgL;IAChL,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,UAAU;IAYlB,OAAO,IAAI,IAAI;CAgBlB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { AudioDevice } from "@motion-script/core";
|
|
2
|
+
/**
|
|
3
|
+
* Web Audio implementation of {@link AudioDevice} — decodes and caches source
|
|
4
|
+
* buffers, then schedules/plays {@link AudioRequest}s as `AudioBufferSourceNode`s
|
|
5
|
+
* through a shared master gain (for global mute). `schedule` registers the
|
|
6
|
+
* active set of requests for the current frame; `syncTo` then starts/stops
|
|
7
|
+
* sources to match `sceneTime`, so playback follows scrubbing and seeks.
|
|
8
|
+
*/
|
|
9
|
+
export class WebAudioDevice extends AudioDevice {
|
|
10
|
+
context;
|
|
11
|
+
ownsContext;
|
|
12
|
+
bufferCache = new Map();
|
|
13
|
+
decoding = new Map();
|
|
14
|
+
active = new Map();
|
|
15
|
+
scheduled = [];
|
|
16
|
+
disposed = false;
|
|
17
|
+
masterGain;
|
|
18
|
+
muted = false;
|
|
19
|
+
constructor(context) {
|
|
20
|
+
super();
|
|
21
|
+
this.context = context ?? new AudioContext();
|
|
22
|
+
this.ownsContext = !context;
|
|
23
|
+
this.masterGain = this.context.createGain();
|
|
24
|
+
this.masterGain.connect(this.context.destination);
|
|
25
|
+
}
|
|
26
|
+
getContext() {
|
|
27
|
+
return this.context;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resume the AudioContext. Must be called from within a user gesture
|
|
31
|
+
* (e.g. a click handler) the first time, or browsers keep the context
|
|
32
|
+
* suspended and no audio is heard.
|
|
33
|
+
*/
|
|
34
|
+
unlock() {
|
|
35
|
+
if (this.context.state === "suspended") {
|
|
36
|
+
this.context.resume();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
has(src) {
|
|
40
|
+
return this.bufferCache.has(src);
|
|
41
|
+
}
|
|
42
|
+
async append(src, data) {
|
|
43
|
+
if (this.bufferCache.has(src))
|
|
44
|
+
return;
|
|
45
|
+
const existing = this.decoding.get(src);
|
|
46
|
+
if (existing)
|
|
47
|
+
return existing;
|
|
48
|
+
// decodeAudioData detaches `data` — that's fine, the caller in
|
|
49
|
+
// AssetManager.fetchAudio discards its reference immediately after
|
|
50
|
+
// awaiting append. Skipping the defensive .slice(0) saves a multi-MB
|
|
51
|
+
// allocation per audio source.
|
|
52
|
+
const job = this.context.decodeAudioData(data).then((buffer) => {
|
|
53
|
+
this.bufferCache.set(src, buffer);
|
|
54
|
+
}).finally(() => {
|
|
55
|
+
this.decoding.delete(src);
|
|
56
|
+
});
|
|
57
|
+
this.decoding.set(src, job);
|
|
58
|
+
await job;
|
|
59
|
+
}
|
|
60
|
+
/** Drops cached buffers and stops active sources for srcs/requests no longer referenced — called when the asset set changes (e.g. seeking across scene boundaries). */
|
|
61
|
+
retain(keep) {
|
|
62
|
+
for (const src of [...this.bufferCache.keys()]) {
|
|
63
|
+
if (!keep.has(src))
|
|
64
|
+
this.bufferCache.delete(src);
|
|
65
|
+
}
|
|
66
|
+
for (const [id, active] of this.active) {
|
|
67
|
+
if (!keep.has(active.request.src))
|
|
68
|
+
this.stopSource(id, active);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Replaces the active request set; stops any currently-playing source whose request is no longer scheduled. Actual start/stop-to-match-time happens in `syncTo`. */
|
|
72
|
+
schedule(requests) {
|
|
73
|
+
const next = new Set(requests.map(r => r.id));
|
|
74
|
+
for (const [id, active] of this.active) {
|
|
75
|
+
if (!next.has(id))
|
|
76
|
+
this.stopSource(id, active);
|
|
77
|
+
}
|
|
78
|
+
this.scheduled = requests.slice();
|
|
79
|
+
}
|
|
80
|
+
/** Starts sources whose `[startAt, endAt)` window now contains `sceneTime` (computing the correct buffer offset for the seek point) and stops any that have fallen outside it. */
|
|
81
|
+
syncTo(sceneTime) {
|
|
82
|
+
const liveIds = new Set();
|
|
83
|
+
for (const req of this.scheduled) {
|
|
84
|
+
const end = req.endAt ?? Infinity;
|
|
85
|
+
const isInWindow = sceneTime >= req.startAt && sceneTime < end;
|
|
86
|
+
if (!isInWindow)
|
|
87
|
+
continue;
|
|
88
|
+
liveIds.add(req.id);
|
|
89
|
+
if (!this.active.has(req.id)) {
|
|
90
|
+
const buffer = this.bufferCache.get(req.src);
|
|
91
|
+
if (buffer)
|
|
92
|
+
this.playBuffer(buffer, req, sceneTime);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const [id, active] of this.active) {
|
|
96
|
+
if (!liveIds.has(id))
|
|
97
|
+
this.stopSource(id, active);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async play(time, _speed, _reverse) {
|
|
101
|
+
if (this.context.state === "suspended") {
|
|
102
|
+
await this.context.resume();
|
|
103
|
+
}
|
|
104
|
+
this.syncTo(time);
|
|
105
|
+
}
|
|
106
|
+
stop() {
|
|
107
|
+
for (const [id, active] of this.active) {
|
|
108
|
+
this.stopSource(id, active);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
setMuted(muted) {
|
|
112
|
+
this.muted = muted;
|
|
113
|
+
this.masterGain.gain.value = muted ? 0 : 1;
|
|
114
|
+
}
|
|
115
|
+
/** Starts a buffer source mid-clip if `sceneTime` lands after the request's start — `audioOffset` accounts for both the request's trim and the elapsed time since `startAt`. */
|
|
116
|
+
playBuffer(buffer, req, sceneTime) {
|
|
117
|
+
const gainNode = this.context.createGain();
|
|
118
|
+
gainNode.gain.value = req.volume;
|
|
119
|
+
gainNode.connect(this.masterGain);
|
|
120
|
+
const source = this.context.createBufferSource();
|
|
121
|
+
source.buffer = buffer;
|
|
122
|
+
source.loop = req.loop;
|
|
123
|
+
source.connect(gainNode);
|
|
124
|
+
const elapsed = sceneTime - req.startAt;
|
|
125
|
+
const audioOffset = req.trimStart + Math.max(0, elapsed);
|
|
126
|
+
source.start(0, audioOffset);
|
|
127
|
+
source.onended = () => {
|
|
128
|
+
if (this.active.get(req.id)?.source === source) {
|
|
129
|
+
this.active.delete(req.id);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
this.active.set(req.id, { source, gainNode, request: req });
|
|
133
|
+
}
|
|
134
|
+
stopSource(id, active) {
|
|
135
|
+
try {
|
|
136
|
+
active.source.onended = null;
|
|
137
|
+
active.source.stop();
|
|
138
|
+
active.source.disconnect();
|
|
139
|
+
active.gainNode.disconnect();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// already stopped
|
|
143
|
+
}
|
|
144
|
+
this.active.delete(id);
|
|
145
|
+
}
|
|
146
|
+
dispose() {
|
|
147
|
+
if (this.disposed)
|
|
148
|
+
return;
|
|
149
|
+
this.disposed = true;
|
|
150
|
+
super.dispose();
|
|
151
|
+
this.bufferCache.clear();
|
|
152
|
+
this.decoding.clear();
|
|
153
|
+
this.scheduled.length = 0;
|
|
154
|
+
try {
|
|
155
|
+
this.masterGain.disconnect();
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// already disconnected
|
|
159
|
+
}
|
|
160
|
+
if (this.ownsContext && this.context.state !== "closed") {
|
|
161
|
+
this.context.close();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.js","sourceRoot":"","sources":["../../src/audio/player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAgB,MAAM,qBAAqB,CAAC;AAQhE;;;;;;GAMG;AACH,MAAM,OAAO,cAAe,SAAQ,WAAW;IACnC,OAAO,CAAe;IACtB,WAAW,CAAU;IACrB,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC7C,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC5C,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IACzC,SAAS,GAAmB,EAAE,CAAC;IAC/B,QAAQ,GAAY,KAAK,CAAC;IAC1B,UAAU,CAAW;IACrB,KAAK,GAAG,KAAK,CAAC;IAEtB,YAAY,OAAsB;QAC9B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,IAAI,YAAY,EAAE,CAAC;QAC7C,IAAI,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC5C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,UAAU;QACN,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,MAAM;QACF,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,GAAG,CAAC,GAAW;QACX,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,IAAiB;QACvC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,+DAA+D;QAC/D,mEAAmE;QACnE,qEAAqE;QACrE,+BAA+B;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YAC3D,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,MAAM,GAAG,CAAC;IACd,CAAC;IAED,uKAAuK;IACvK,MAAM,CAAC,IAAyB;QAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnE,CAAC;IACL,CAAC;IAED,qKAAqK;IACrK,QAAQ,CAAC,QAAiC;QACtC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9C,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;IACtC,CAAC;IAED,kLAAkL;IAClL,MAAM,CAAC,SAAiB;QACpB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,IAAI,QAAQ,CAAC;YAClC,MAAM,UAAU,GAAG,SAAS,IAAI,GAAG,CAAC,OAAO,IAAI,SAAS,GAAG,GAAG,CAAC;YAC/D,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC7C,IAAI,MAAM;oBAAE,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YACxD,CAAC;QACL,CAAC;QAED,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,MAAc,EAAE,QAAiB;QACtD,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACrC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,IAAI;QACA,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,KAAc;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,gLAAgL;IACxK,UAAU,CAAC,MAAmB,EAAE,GAAiB,EAAE,SAAiB;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QACjD,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACvB,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAEzB,MAAM,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC;QACxC,MAAM,WAAW,GAAG,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAEzD,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QAE7B,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;YAClB,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC7C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/B,CAAC;QACL,CAAC,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IAEO,UAAU,CAAC,EAAU,EAAE,MAAoB;QAC/C,IAAI,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC3B,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACL,kBAAkB;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO;QACH,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC;YACD,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACL,uBAAuB;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CanvasKit } from "@motion-script/canvaskit";
|
|
2
|
+
import type { BloomEffect } from "@motion-script/core";
|
|
3
|
+
import { CanvasKitEffect } from "./effect";
|
|
4
|
+
/**
|
|
5
|
+
* Bloom glow effect. Extracts pixels above `threshold`, blurs them, then
|
|
6
|
+
* Screen-blends that bright-pass back onto the original layer.
|
|
7
|
+
*
|
|
8
|
+
* Screen blend: result = 1 − (1 − source) × (1 − bloom)
|
|
9
|
+
* This ensures the bloom only brightens, never darkens, and naturally caps at white.
|
|
10
|
+
*
|
|
11
|
+
* MakeBlend(Screen, background=null, foreground=bloomFilter):
|
|
12
|
+
* background = null → the dynamic source (the saveLayer content)
|
|
13
|
+
* foreground = blurred threshold pass of the same source
|
|
14
|
+
*/
|
|
15
|
+
export declare class BloomCanvasKitEffect extends CanvasKitEffect<BloomEffect> {
|
|
16
|
+
constructor();
|
|
17
|
+
makeImageFilter(effect: BloomEffect, ck: CanvasKit): any;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=bloom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloom.d.ts","sourceRoot":"","sources":["../../src/effects/bloom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,qBAAa,oBAAqB,SAAQ,eAAe,CAAC,WAAW,CAAC;;IAKlE,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,GAAG,GAAG;CAoD3D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CanvasKitEffect } from "./effect";
|
|
2
|
+
/**
|
|
3
|
+
* Bloom glow effect. Extracts pixels above `threshold`, blurs them, then
|
|
4
|
+
* Screen-blends that bright-pass back onto the original layer.
|
|
5
|
+
*
|
|
6
|
+
* Screen blend: result = 1 − (1 − source) × (1 − bloom)
|
|
7
|
+
* This ensures the bloom only brightens, never darkens, and naturally caps at white.
|
|
8
|
+
*
|
|
9
|
+
* MakeBlend(Screen, background=null, foreground=bloomFilter):
|
|
10
|
+
* background = null → the dynamic source (the saveLayer content)
|
|
11
|
+
* foreground = blurred threshold pass of the same source
|
|
12
|
+
*/
|
|
13
|
+
export class BloomCanvasKitEffect extends CanvasKitEffect {
|
|
14
|
+
constructor() {
|
|
15
|
+
super("bloom");
|
|
16
|
+
}
|
|
17
|
+
makeImageFilter(effect, ck) {
|
|
18
|
+
if (effect.radius <= 0 || effect.intensity <= 0)
|
|
19
|
+
return null;
|
|
20
|
+
const t = Math.max(0, Math.min(1, effect.threshold));
|
|
21
|
+
const sigma = effect.radius / 2;
|
|
22
|
+
// Color matrix that zeroes out pixels below threshold and rescales the rest.
|
|
23
|
+
// Each channel: output = max(0, input − t) / (1 − t)
|
|
24
|
+
// In 5×4 matrix terms (row-major, applied to non-premultiplied colors):
|
|
25
|
+
// output_R = 1/(1-t) * input_R + (-t/(1-t))
|
|
26
|
+
const scale = t < 1 ? 1 / (1 - t) : 1;
|
|
27
|
+
const bias = -t * scale;
|
|
28
|
+
// prettier-ignore
|
|
29
|
+
const thresholdMatrix = [
|
|
30
|
+
scale, 0, 0, 0, bias,
|
|
31
|
+
0, scale, 0, 0, bias,
|
|
32
|
+
0, 0, scale, 0, bias,
|
|
33
|
+
0, 0, 0, 1, 0,
|
|
34
|
+
];
|
|
35
|
+
const thresholdCF = ck.ColorFilter.MakeMatrix(thresholdMatrix);
|
|
36
|
+
const thresholdIF = ck.ImageFilter.MakeColorFilter(thresholdCF, null);
|
|
37
|
+
thresholdCF.delete();
|
|
38
|
+
// Blur the bright pass.
|
|
39
|
+
const blurIF = ck.ImageFilter.MakeBlur(sigma, sigma, ck.TileMode.Decal, thresholdIF);
|
|
40
|
+
thresholdIF.delete();
|
|
41
|
+
// Scale the bloom pass by intensity using another color matrix.
|
|
42
|
+
let bloomIF = blurIF;
|
|
43
|
+
if (effect.intensity !== 1) {
|
|
44
|
+
const i = effect.intensity;
|
|
45
|
+
// prettier-ignore
|
|
46
|
+
const intensityMatrix = [
|
|
47
|
+
i, 0, 0, 0, 0,
|
|
48
|
+
0, i, 0, 0, 0,
|
|
49
|
+
0, 0, i, 0, 0,
|
|
50
|
+
0, 0, 0, 1, 0,
|
|
51
|
+
];
|
|
52
|
+
const intensityCF = ck.ColorFilter.MakeMatrix(intensityMatrix);
|
|
53
|
+
bloomIF = ck.ImageFilter.MakeColorFilter(intensityCF, blurIF);
|
|
54
|
+
intensityCF.delete();
|
|
55
|
+
blurIF.delete();
|
|
56
|
+
}
|
|
57
|
+
// Screen-blend the bloom pass onto the source layer.
|
|
58
|
+
// background = null → source layer; foreground = bloomIF
|
|
59
|
+
const result = ck.ImageFilter.MakeBlend(ck.BlendMode.Screen, null, bloomIF);
|
|
60
|
+
bloomIF.delete();
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=bloom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloom.js","sourceRoot":"","sources":["../../src/effects/bloom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,OAAO,oBAAqB,SAAQ,eAA4B;IAClE;QACI,KAAK,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;IAED,eAAe,CAAC,MAAmB,EAAE,EAAa;QAC9C,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAE7D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAEhC,6EAA6E;QAC7E,qDAAqD;QACrD,wEAAwE;QACxE,8CAA8C;QAC9C,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAExB,kBAAkB;QAClB,MAAM,eAAe,GAAG;YACpB,KAAK,EAAE,CAAC,EAAM,CAAC,EAAM,CAAC,EAAE,IAAI;YAC5B,CAAC,EAAM,KAAK,EAAE,CAAC,EAAM,CAAC,EAAE,IAAI;YAC5B,CAAC,EAAM,CAAC,EAAM,KAAK,EAAE,CAAC,EAAE,IAAI;YAC5B,CAAC,EAAM,CAAC,EAAM,CAAC,EAAM,CAAC,EAAE,CAAC;SAC5B,CAAC;QAEF,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACtE,WAAW,CAAC,MAAM,EAAE,CAAC;QAErB,wBAAwB;QACxB,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACrF,WAAW,CAAC,MAAM,EAAE,CAAC;QAErB,gEAAgE;QAChE,IAAI,OAAO,GAAQ,MAAM,CAAC;QAC1B,IAAI,MAAM,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC;YAC3B,kBAAkB;YAClB,MAAM,eAAe,GAAG;gBACpB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;aAChB,CAAC;YACF,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;YAC/D,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC9D,WAAW,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC;QAED,qDAAqD;QACrD,yDAAyD;QACzD,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5E,OAAO,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CanvasKit } from "@motion-script/canvaskit";
|
|
2
|
+
import { CanvasKitEffect } from "./effect";
|
|
3
|
+
import { type BlurEffect } from "@motion-script/core";
|
|
4
|
+
export declare class BlurCanvasKitEffect extends CanvasKitEffect<BlurEffect> {
|
|
5
|
+
constructor();
|
|
6
|
+
makeImageFilter(effect: BlurEffect, ck: CanvasKit): any;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=blur.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blur.d.ts","sourceRoot":"","sources":["../../src/effects/blur.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,qBAAa,mBAAoB,SAAQ,eAAe,CAAC,UAAU,CAAC;;IAKhE,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,GAAG,GAAG;CAK1D"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CanvasKitEffect } from "./effect";
|
|
2
|
+
export class BlurCanvasKitEffect extends CanvasKitEffect {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("blur");
|
|
5
|
+
}
|
|
6
|
+
makeImageFilter(effect, ck) {
|
|
7
|
+
// Skia's blur sigma is roughly half the perceived "radius" of the blur.
|
|
8
|
+
const sigma = effect.radius / 2;
|
|
9
|
+
return ck.ImageFilter.MakeBlur(sigma, sigma, ck.TileMode.Decal, null);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=blur.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blur.js","sourceRoot":"","sources":["../../src/effects/blur.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,MAAM,OAAO,mBAAoB,SAAQ,eAA2B;IAChE;QACI,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,eAAe,CAAC,MAAkB,EAAE,EAAa;QAC7C,wEAAwE;QACxE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAChC,OAAO,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC;CACJ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CanvasKit, Shader } from "@motion-script/canvaskit";
|
|
2
|
+
import { type BulgePinchEffect } from "@motion-script/core";
|
|
3
|
+
/** Drop the cached RuntimeEffect (called when the draw context is disposed). */
|
|
4
|
+
export declare function disposeBulgePinch(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Build a paint shader that draws the backdrop bulged/pinched within the lens
|
|
7
|
+
* disc and passes it through untouched elsewhere. The caller draws it over the
|
|
8
|
+
* surface (clipped to the node silhouette), replacing the backdrop region with
|
|
9
|
+
* the warped version. Returns null when the effect is a no-op.
|
|
10
|
+
*
|
|
11
|
+
* @param effect bulge/pinch params (strength, radius=reach, centre).
|
|
12
|
+
* @param ck live CanvasKit instance.
|
|
13
|
+
* @param backdrop child shader wrapping the snapshot of the content beneath.
|
|
14
|
+
* @param centerX lens centre X in device px (node centre).
|
|
15
|
+
* @param centerY lens centre Y in device px.
|
|
16
|
+
* @param width node width in device px (the lens box width).
|
|
17
|
+
* @param height node height in device px (the lens box height).
|
|
18
|
+
*/
|
|
19
|
+
export declare function makeBulgePinchShader(effect: BulgePinchEffect, ck: CanvasKit, backdrop: Shader, centerX: number, centerY: number, width: number, height: number): Shader | null;
|
|
20
|
+
//# sourceMappingURL=bulge-pinch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulge-pinch.d.ts","sourceRoot":"","sources":["../../src/effects/bulge-pinch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAsD5D,gFAAgF;AAChF,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,gBAAgB,EACxB,EAAE,EAAE,SAAS,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAmBf"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magnifier/pinch lens that *resamples the backdrop* — it reads the content
|
|
3
|
+
* beneath the node (passed in as the `u_backdrop` child shader) at a remapped
|
|
4
|
+
* position, so the area under the node is genuinely bulged or pinched, not just
|
|
5
|
+
* nudged. This is the resample approach a magnifying glass uses.
|
|
6
|
+
*
|
|
7
|
+
* The lens fills the node's *bounding box* (not a circle): position is normalised
|
|
8
|
+
* into the box as `n = (uv − centre) / halfExtent`, and the box metric
|
|
9
|
+
* `q = max(|n.x|, |n.y|)` runs 0 at the centre → 1 at the box edge. So a wide
|
|
10
|
+
* rect distorts across its whole width, a tall one across its height, etc. The
|
|
11
|
+
* caller clips the draw to the node's exact silhouette, so rounded corners /
|
|
12
|
+
* ellipses / polygons round or shape the lens to match.
|
|
13
|
+
*
|
|
14
|
+
* `u_reach` (0–1) sets how far from the centre the warp extends within the box;
|
|
15
|
+
* beyond it the field eases to identity so there is no seam at the shape edge.
|
|
16
|
+
* Inside, a spherical (barrel) profile drives the magnification:
|
|
17
|
+
*
|
|
18
|
+
* t = clamp((reach − q) / reach, 0, 1) 1 at centre → 0 at the reach edge
|
|
19
|
+
* z = sqrt(1 − (1 − t)²) dome height; 1 at centre → 0 at edge
|
|
20
|
+
* factor = 1 − strength · z strength>0 magnifies; <0 compresses
|
|
21
|
+
* sample = centre + (uv − centre) · factor
|
|
22
|
+
*/
|
|
23
|
+
const BULGE_PINCH_SKSL = `
|
|
24
|
+
uniform shader u_backdrop;
|
|
25
|
+
uniform vec2 u_center; // lens centre, device px
|
|
26
|
+
uniform vec2 u_half; // node half-extent (halfWidth, halfHeight), device px
|
|
27
|
+
uniform float u_reach; // 0–1 fraction of the box the warp spans
|
|
28
|
+
uniform float u_strength; // + bulge (magnify), − pinch (compress)
|
|
29
|
+
|
|
30
|
+
vec4 main(vec2 fragCoord) {
|
|
31
|
+
vec2 n = (fragCoord - u_center) / max(u_half, vec2(1.0));
|
|
32
|
+
float q = max(abs(n.x), abs(n.y)); // box metric: 0 centre → 1 box edge
|
|
33
|
+
float reach = max(u_reach, 0.001);
|
|
34
|
+
if (q >= reach) {
|
|
35
|
+
return u_backdrop.eval(fragCoord);
|
|
36
|
+
}
|
|
37
|
+
float t = clamp((reach - q) / reach, 0.0, 1.0); // 1 centre → 0 at reach edge
|
|
38
|
+
float e = 1.0 - t;
|
|
39
|
+
float z = sqrt(max(0.0, 1.0 - e * e)); // dome falloff
|
|
40
|
+
float factor = 1.0 - u_strength * z;
|
|
41
|
+
vec2 samplePos = u_center + (fragCoord - u_center) * factor;
|
|
42
|
+
return u_backdrop.eval(samplePos);
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
let cachedEffect = null;
|
|
46
|
+
function getRuntimeEffect(ck) {
|
|
47
|
+
if (!cachedEffect)
|
|
48
|
+
cachedEffect = ck.RuntimeEffect.Make(BULGE_PINCH_SKSL);
|
|
49
|
+
return cachedEffect;
|
|
50
|
+
}
|
|
51
|
+
/** Drop the cached RuntimeEffect (called when the draw context is disposed). */
|
|
52
|
+
export function disposeBulgePinch() {
|
|
53
|
+
cachedEffect?.delete();
|
|
54
|
+
cachedEffect = null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build a paint shader that draws the backdrop bulged/pinched within the lens
|
|
58
|
+
* disc and passes it through untouched elsewhere. The caller draws it over the
|
|
59
|
+
* surface (clipped to the node silhouette), replacing the backdrop region with
|
|
60
|
+
* the warped version. Returns null when the effect is a no-op.
|
|
61
|
+
*
|
|
62
|
+
* @param effect bulge/pinch params (strength, radius=reach, centre).
|
|
63
|
+
* @param ck live CanvasKit instance.
|
|
64
|
+
* @param backdrop child shader wrapping the snapshot of the content beneath.
|
|
65
|
+
* @param centerX lens centre X in device px (node centre).
|
|
66
|
+
* @param centerY lens centre Y in device px.
|
|
67
|
+
* @param width node width in device px (the lens box width).
|
|
68
|
+
* @param height node height in device px (the lens box height).
|
|
69
|
+
*/
|
|
70
|
+
export function makeBulgePinchShader(effect, ck, backdrop, centerX, centerY, width, height) {
|
|
71
|
+
const strength = effect.strength;
|
|
72
|
+
if (strength === 0 || effect.radius <= 0 || width <= 0 || height <= 0)
|
|
73
|
+
return null;
|
|
74
|
+
// center is a 0–1 offset from the node centre (0.5,0.5 = dead centre).
|
|
75
|
+
const cx = centerX + (effect.center.x - 0.5) * width;
|
|
76
|
+
const cy = centerY + (effect.center.y - 0.5) * height;
|
|
77
|
+
const halfW = width / 2;
|
|
78
|
+
const halfH = height / 2;
|
|
79
|
+
// radius is a 0–1 reach: how far across the node box the warp spans.
|
|
80
|
+
const reach = Math.min(1, effect.radius);
|
|
81
|
+
const runtimeEffect = getRuntimeEffect(ck);
|
|
82
|
+
if (!runtimeEffect)
|
|
83
|
+
return null;
|
|
84
|
+
return runtimeEffect.makeShaderWithChildren([cx, cy, halfW, halfH, reach, strength], [backdrop]);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=bulge-pinch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulge-pinch.js","sourceRoot":"","sources":["../../src/effects/bulge-pinch.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBxB,CAAC;AAEF,IAAI,YAAY,GAAyB,IAAI,CAAC;AAE9C,SAAS,gBAAgB,CAAC,EAAa;IACnC,IAAI,CAAC,YAAY;QAAE,YAAY,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC1E,OAAO,YAAY,CAAC;AACxB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,GAAG,IAAI,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAwB,EACxB,EAAa,EACb,QAAgB,EAChB,OAAe,EACf,OAAe,EACf,KAAa,EACb,MAAc;IAEd,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,IAAI,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnF,uEAAuE;IACvE,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC;IACrD,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;IACxB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC;IACzB,qEAAqE;IACrE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzC,MAAM,aAAa,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAEhC,OAAO,aAAa,CAAC,sBAAsB,CACvC,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,EACvC,CAAC,QAAQ,CAAC,CACb,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CanvasKit } from "@motion-script/canvaskit";
|
|
2
|
+
import type { ChromaticAberrationEffect } from "@motion-script/core";
|
|
3
|
+
import { CanvasKitEffect } from "./effect";
|
|
4
|
+
/**
|
|
5
|
+
* Chromatic aberration — red/blue lens-dispersion fringing.
|
|
6
|
+
*
|
|
7
|
+
* Implemented as three ImageFilter passes composed via Screen-blend:
|
|
8
|
+
* 1. R ghost: source shifted by (+dx, +dy) with red colour boost
|
|
9
|
+
* 2. B ghost: source shifted by (-dx, -dy) with blue colour boost
|
|
10
|
+
* 3. Screen-blend both ghosts onto the original source
|
|
11
|
+
*
|
|
12
|
+
* Screen-blend keeps the result from over-brightening and naturally
|
|
13
|
+
* saturates highlights, mimicking real lens dispersion.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ChromaticAberrationCanvasKitEffect extends CanvasKitEffect<ChromaticAberrationEffect> {
|
|
16
|
+
constructor();
|
|
17
|
+
makeImageFilter(effect: ChromaticAberrationEffect, ck: CanvasKit): any;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=chromatic-aberration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chromatic-aberration.d.ts","sourceRoot":"","sources":["../../src/effects/chromatic-aberration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,qBAAa,kCAAmC,SAAQ,eAAe,CAAC,yBAAyB,CAAC;;IAK9F,eAAe,CAAC,MAAM,EAAE,yBAAyB,EAAE,EAAE,EAAE,SAAS,GAAG,GAAG;CA8CzE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CanvasKitEffect } from "./effect";
|
|
2
|
+
/**
|
|
3
|
+
* Chromatic aberration — red/blue lens-dispersion fringing.
|
|
4
|
+
*
|
|
5
|
+
* Implemented as three ImageFilter passes composed via Screen-blend:
|
|
6
|
+
* 1. R ghost: source shifted by (+dx, +dy) with red colour boost
|
|
7
|
+
* 2. B ghost: source shifted by (-dx, -dy) with blue colour boost
|
|
8
|
+
* 3. Screen-blend both ghosts onto the original source
|
|
9
|
+
*
|
|
10
|
+
* Screen-blend keeps the result from over-brightening and naturally
|
|
11
|
+
* saturates highlights, mimicking real lens dispersion.
|
|
12
|
+
*/
|
|
13
|
+
export class ChromaticAberrationCanvasKitEffect extends CanvasKitEffect {
|
|
14
|
+
constructor() {
|
|
15
|
+
super("chromaticAberration");
|
|
16
|
+
}
|
|
17
|
+
makeImageFilter(effect, ck) {
|
|
18
|
+
if (effect.amount <= 0)
|
|
19
|
+
return null;
|
|
20
|
+
const rad = (effect.angle * Math.PI) / 180;
|
|
21
|
+
const dx = Math.cos(rad) * effect.amount;
|
|
22
|
+
const dy = Math.sin(rad) * effect.amount;
|
|
23
|
+
// R ghost: shift source by (+dx, +dy), boost red channel, suppress others.
|
|
24
|
+
// prettier-ignore
|
|
25
|
+
const redMatrix = [
|
|
26
|
+
1.4, -0.2, -0.2, 0, 0,
|
|
27
|
+
0, 0, 0, 0, 0,
|
|
28
|
+
0, 0, 0, 0, 0,
|
|
29
|
+
0, 0, 0, 1, 0,
|
|
30
|
+
];
|
|
31
|
+
const redCF = ck.ColorFilter.MakeMatrix(redMatrix);
|
|
32
|
+
const redOffset = ck.ImageFilter.MakeOffset(dx, dy, null);
|
|
33
|
+
const rGhost = ck.ImageFilter.MakeColorFilter(redCF, redOffset);
|
|
34
|
+
redCF.delete();
|
|
35
|
+
redOffset.delete();
|
|
36
|
+
// B ghost: shift source by (-dx, -dy), boost blue channel, suppress others.
|
|
37
|
+
// prettier-ignore
|
|
38
|
+
const blueMatrix = [
|
|
39
|
+
0, 0, 0, 0, 0,
|
|
40
|
+
0, 0, 0, 0, 0,
|
|
41
|
+
-0.2, -0.2, 1.4, 0, 0,
|
|
42
|
+
0, 0, 0, 1, 0,
|
|
43
|
+
];
|
|
44
|
+
const blueCF = ck.ColorFilter.MakeMatrix(blueMatrix);
|
|
45
|
+
const blueOffset = ck.ImageFilter.MakeOffset(-dx, -dy, null);
|
|
46
|
+
const bGhost = ck.ImageFilter.MakeColorFilter(blueCF, blueOffset);
|
|
47
|
+
blueCF.delete();
|
|
48
|
+
blueOffset.delete();
|
|
49
|
+
// Screen-blend R ghost onto source (background = null = source).
|
|
50
|
+
const step1 = ck.ImageFilter.MakeBlend(ck.BlendMode.Screen, null, rGhost);
|
|
51
|
+
rGhost.delete();
|
|
52
|
+
// Screen-blend B ghost onto the result of step1.
|
|
53
|
+
const result = ck.ImageFilter.MakeBlend(ck.BlendMode.Screen, step1, bGhost);
|
|
54
|
+
step1.delete();
|
|
55
|
+
bGhost.delete();
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=chromatic-aberration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chromatic-aberration.js","sourceRoot":"","sources":["../../src/effects/chromatic-aberration.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,OAAO,kCAAmC,SAAQ,eAA0C;IAC9F;QACI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,MAAiC,EAAE,EAAa;QAC5D,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAEpC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QAEzC,2EAA2E;QAC3E,kBAAkB;QAClB,MAAM,SAAS,GAAG;YACd,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YACrB,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;YACrB,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;YACrB,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;SACxB,CAAC;QACF,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAChE,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,SAAS,CAAC,MAAM,EAAE,CAAC;QAEnB,4EAA4E;QAC5E,kBAAkB;QAClB,MAAM,UAAU,GAAG;YACf,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;YACrB,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;YACrB,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;YACrB,CAAC,EAAK,CAAC,EAAK,CAAC,EAAI,CAAC,EAAE,CAAC;SACxB,CAAC;QACF,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClE,MAAM,CAAC,MAAM,EAAE,CAAC;QAChB,UAAU,CAAC,MAAM,EAAE,CAAC;QAEpB,iEAAiE;QACjE,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1E,MAAM,CAAC,MAAM,EAAE,CAAC;QAEhB,iDAAiD;QACjD,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5E,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,EAAE,CAAC;QAEhB,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type SceneEffect as IEffect } from "@motion-script/core";
|
|
2
|
+
import type { CanvasKit } from "@motion-script/canvaskit";
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base for CanvasKit effect renderers.
|
|
5
|
+
*
|
|
6
|
+
* Subclasses implement `makeImageFilter` using either built-in CanvasKit filters
|
|
7
|
+
* (ImageFilter.MakeBlur, ColorFilter.MakeMatrix, etc.) or custom SkSL shaders
|
|
8
|
+
* via CanvasKit.RuntimeEffect.Make(sksl).
|
|
9
|
+
*
|
|
10
|
+
* The returned ImageFilter is applied as a saveLayer paint wrapping the node's
|
|
11
|
+
* draw calls, so it composites the entire node before applying the filter.
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class CanvasKitEffect<T extends IEffect = IEffect> {
|
|
14
|
+
readonly type: string;
|
|
15
|
+
constructor(type: string);
|
|
16
|
+
/**
|
|
17
|
+
* Produce a Skia ImageFilter for this effect.
|
|
18
|
+
*
|
|
19
|
+
* @param effect Effect data (radius, amount, etc.)
|
|
20
|
+
* @param ck Live CanvasKit instance
|
|
21
|
+
* @param width Surface width in pixels — needed for size-relative effects (e.g. pixelate)
|
|
22
|
+
* @param height Surface height in pixels
|
|
23
|
+
* @returns An ImageFilter object, or null if the effect cannot be applied
|
|
24
|
+
*/
|
|
25
|
+
abstract makeImageFilter(effect: T, ck: CanvasKit, width: number, height: number): any;
|
|
26
|
+
/**
|
|
27
|
+
* Clean up any persistent CanvasKit objects (e.g. cached RuntimeEffect).
|
|
28
|
+
* Called when the draw context is disposed.
|
|
29
|
+
*/
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../../src/effects/effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,IAAI,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;;;;;;GASG;AACH,8BAAsB,eAAe,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;IAIxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG;IAEtF;;;OAGG;IACH,OAAO,IAAI,IAAI;CAClB"}
|