odori 0.0.6 → 0.0.7

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.
@@ -0,0 +1,202 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Turn a live DOM subtree into an image, deterministically.
6
+ *
7
+ * Chromium can draw an SVG onto a canvas, and an SVG `foreignObject` can hold
8
+ * ordinary markup, which is the whole trick. The reason it needs care is that
9
+ * the foreignObject is a sealed document: nothing outside it reaches in, so a
10
+ * subtree that looks right on the page rasterizes unstyled unless every rule
11
+ * that applied to it is carried across as an inline declaration.
12
+ *
13
+ * There is a native alternative, `CanvasRenderingContext2D.drawElement`, which
14
+ * would make all of this unnecessary. It is not in the pinned render browser,
15
+ * measured rather than assumed, so this is the only path that produces the
16
+ * same pixels in Studio and in an export. `captureMethod()` reports which one
17
+ * is in use so Studio never has to guess.
18
+ */
19
+ /** Which capture strategy this browser can actually offer. */
20
+ type CaptureMethod = "native" | "foreign-object";
21
+ declare const captureMethod: () => CaptureMethod;
22
+ /** The subtree as standalone XHTML, carrying everything it needs to paint. */
23
+ declare const serializeElement: (element: HTMLElement) => string;
24
+ /**
25
+ * Rasterize a subtree at a fixed size.
26
+ *
27
+ * Fonts first: a face that has not loaded when the SVG is parsed falls back,
28
+ * and the fallback is what gets baked into the pixels. The decode is awaited
29
+ * rather than assumed, so a caller holding the frame releases it only when
30
+ * there is something to capture.
31
+ */
32
+ declare const captureElement: (element: HTMLElement, options: {
33
+ width: number;
34
+ height: number;
35
+ }) => Promise<HTMLImageElement>;
36
+
37
+ /**
38
+ * A fragment shader, and everything around it that nobody should have to
39
+ * write twice.
40
+ *
41
+ * An effect is one fragment shader and its named uniforms. The context, the
42
+ * program, the full-screen triangle, the source texture, the framebuffers a
43
+ * multi-pass chain ping-pongs between, and the teardown are all owned here,
44
+ * because every video that reached for a shader was otherwise rebuilding the
45
+ * same two hundred lines and getting the determinism details wrong.
46
+ *
47
+ * Time is not one of the things a shader may ask the browser for. Every
48
+ * built-in uniform derives from the frame, so frame 90 is the same picture
49
+ * whether it was rendered on its own or after eighty-nine others.
50
+ */
51
+ type UniformValue = number | readonly number[];
52
+ type UniformSpec = {
53
+ /** How many floats: 1, 2, 3, or 4. Decides which uniform call is made. */
54
+ size: 1 | 2 | 3 | 4;
55
+ defaultValue: UniformValue;
56
+ };
57
+ declare const numberUniform: (defaultValue: number) => UniformSpec;
58
+ declare const vec2Uniform: (defaultValue: readonly [number, number]) => UniformSpec;
59
+ declare const vec3Uniform: (defaultValue: readonly [number, number, number]) => UniformSpec;
60
+ declare const vec4Uniform: (defaultValue: readonly [number, number, number, number]) => UniformSpec;
61
+ type ShaderEffectDefinition = {
62
+ name: string;
63
+ fragmentShader: string;
64
+ uniforms: Record<string, UniformSpec>;
65
+ };
66
+ /** One configured use of an effect, which is what a surface is handed. */
67
+ type EffectInstance = {
68
+ definition: ShaderEffectDefinition;
69
+ values: Record<string, UniformValue>;
70
+ };
71
+ /** A factory: call it with overrides to get something to put in `effects`. */
72
+ type EffectFactory = (values?: Record<string, UniformValue>) => EffectInstance;
73
+ declare const defineShaderEffect: (definition: ShaderEffectDefinition) => EffectFactory;
74
+ /** Prepended to every effect, so a shader author writes only the body. */
75
+ declare const SHADER_PRELUDE = "#version 300 es\nprecision highp float;\nuniform sampler2D source;\nuniform vec2 resolution;\nuniform float frame;\nuniform float fps;\nuniform float seconds;\nuniform float progress;\nout vec4 odoriColour;\n// sample is a reserved word in GLSL ES 3.00, so the helper is tex.\nvec4 tex(vec2 uv) { return texture(source, uv); }\n";
76
+ declare class ShaderCompileError extends Error {
77
+ readonly effect: string;
78
+ readonly log: string;
79
+ readonly source: string;
80
+ constructor(effect: string, log: string, source: string);
81
+ }
82
+ type PipelineClock = {
83
+ frame: number;
84
+ fps: number;
85
+ durationInFrames: number;
86
+ };
87
+ /**
88
+ * The compiled chain for one surface. Built once, painted every frame, and
89
+ * disposed when the surface unmounts.
90
+ */
91
+ declare class EffectPipeline {
92
+ private readonly gl;
93
+ private readonly passes;
94
+ private readonly sourceTexture;
95
+ private targets;
96
+ private width;
97
+ private height;
98
+ constructor(canvas: HTMLCanvasElement, effects: readonly EffectInstance[]);
99
+ /** The renderer as Studio reports it, for the capability panel. */
100
+ get renderer(): string;
101
+ private resize;
102
+ private setUniforms;
103
+ /** Upload the captured picture and run every pass over it. */
104
+ paint(source: TexImageSource, effects: readonly EffectInstance[], clock: PipelineClock): void;
105
+ private disposeTargets;
106
+ dispose(): void;
107
+ }
108
+
109
+ type SurfaceClock = {
110
+ frame: number;
111
+ fps: number;
112
+ seconds: number;
113
+ /** 0 to 1 across the composition. */
114
+ progress: number;
115
+ durationInFrames: number;
116
+ width: number;
117
+ height: number;
118
+ };
119
+ type HtmlInCanvasPaint<Renderer> = (args: SurfaceClock & {
120
+ renderer: Renderer;
121
+ source: CanvasImageSource;
122
+ canvas: HTMLCanvasElement;
123
+ }) => void;
124
+ type HtmlInCanvasInit<Renderer> = (args: {
125
+ canvas: HTMLCanvasElement;
126
+ width: number;
127
+ height: number;
128
+ }) => Renderer;
129
+ /**
130
+ * What Studio shows about a surface, so a difference between preview and
131
+ * export is diagnosable without opening devtools.
132
+ */
133
+ type SurfaceCapabilities = {
134
+ capture: CaptureMethod;
135
+ backend: "canvas-2d" | "webgl2";
136
+ renderer?: string;
137
+ error?: string;
138
+ };
139
+ /**
140
+ * Capture a React subtree once per frame and hand the picture to a painter.
141
+ *
142
+ * The children are ordinary React, laid out by the browser, which is the whole
143
+ * point: typography and product UI stay in the language they are written in
144
+ * and only the compositing is different. They are rendered off screen rather
145
+ * than into the canvas, captured, and then drawn.
146
+ *
147
+ * Determinism is the contract. The frame is held from before the capture
148
+ * starts until after the paint has finished, so an export never screenshots a
149
+ * surface that is halfway through, and every value a painter is given comes
150
+ * from the frame rather than from a clock.
151
+ */
152
+ declare const HtmlInCanvas: <Renderer>({ children, width, height, onInit, onPaint, onDispose, onCapabilities, }: {
153
+ children: ReactNode;
154
+ /** Defaults to the composition's own size. */
155
+ width?: number;
156
+ height?: number;
157
+ onInit?: HtmlInCanvasInit<Renderer>;
158
+ onPaint?: HtmlInCanvasPaint<Renderer>;
159
+ onDispose?: (renderer: Renderer) => void;
160
+ onCapabilities?: (capabilities: SurfaceCapabilities) => void;
161
+ }) => react_jsx_runtime.JSX.Element;
162
+ /**
163
+ * The same capture, with a chain of shaders over it.
164
+ *
165
+ * One surface runs every effect in a single pipeline. Nesting two surfaces
166
+ * would capture and upload the DOM twice for one picture, which is why the
167
+ * composition is a list here rather than something to wrap repeatedly.
168
+ */
169
+ declare const EffectSurface: ({ children, effects, width, height, onCapabilities, }: {
170
+ children: ReactNode;
171
+ effects: readonly EffectInstance[];
172
+ width?: number;
173
+ height?: number;
174
+ onCapabilities?: (capabilities: SurfaceCapabilities) => void;
175
+ }) => react_jsx_runtime.JSX.Element;
176
+
177
+ /**
178
+ * The effects Odori ships, written the way a project would write its own.
179
+ *
180
+ * Each is one fragment shader against `SHADER_PRELUDE`, so `tex(uv)` reads
181
+ * the captured picture and `resolution`, `frame`, `fps`, `seconds` and
182
+ * `progress` are already in scope. Nothing here reaches for a clock, and
183
+ * nothing carries state between frames, which is what lets a worker render
184
+ * frame 200 without having rendered frame 199.
185
+ */
186
+ /** Pull the picture out through a lens, or push it in. */
187
+ declare const barrelDistortion: EffectFactory;
188
+ /** Separate the channels, the way a cheap lens does at its edges. */
189
+ declare const rgbSplit: EffectFactory;
190
+ /** Horizontal lines, restrained enough to read as a screen and not a costume. */
191
+ declare const scanlines: EffectFactory;
192
+ /** Quantise to blocks. Sizes in output pixels, so it reads the same at any scale. */
193
+ declare const pixelate: EffectFactory;
194
+ /**
195
+ * Grain. Seeded from the frame rather than from a random number, so the same
196
+ * frame has the same grain every time it is rendered.
197
+ */
198
+ declare const filmGrain: EffectFactory;
199
+ /** Magnify a circle of the picture, like a loupe held over it. */
200
+ declare const magnify: EffectFactory;
201
+
202
+ export { type CaptureMethod, type EffectFactory, type EffectInstance, EffectPipeline, EffectSurface, HtmlInCanvas, type HtmlInCanvasInit, type HtmlInCanvasPaint, SHADER_PRELUDE, ShaderCompileError, type ShaderEffectDefinition, type SurfaceCapabilities, type SurfaceClock, type UniformSpec, type UniformValue, barrelDistortion, captureElement, captureMethod, defineShaderEffect, filmGrain, magnify, numberUniform, pixelate, rgbSplit, scanlines, serializeElement, vec2Uniform, vec3Uniform, vec4Uniform };