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.
- package/dist/{chunk-XN6DA264.js → chunk-EVICSOWU.js} +27 -74
- package/dist/chunk-EVICSOWU.js.map +1 -0
- package/dist/chunk-UMNWCL6I.js +74 -0
- package/dist/chunk-UMNWCL6I.js.map +1 -0
- package/dist/effects.d.ts +202 -0
- package/dist/effects.js +568 -0
- package/dist/effects.js.map +1 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/preview.js +4 -2
- package/dist/preview.js.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-XN6DA264.js.map +0 -1
|
@@ -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 };
|