@taole/giftstage 0.1.28 → 0.1.30
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 +89 -36
- package/dist/core/audio-manager.d.ts +19 -0
- package/dist/core/gift-stage.d.ts +117 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/post-animator.d.ts +43 -0
- package/dist/core/render-manager.d.ts +99 -0
- package/dist/core/webcodecs-video-atlas.d.ts +30 -0
- package/dist/gift-stage.cjs.js +225 -225
- package/dist/gift-stage.es.js +2537 -2062
- package/dist/gpu/detect.d.ts +2 -0
- package/dist/gpu/gl-utils.d.ts +10 -0
- package/dist/gpu/index.d.ts +3 -0
- package/dist/gpu/webgl1-backend.d.ts +43 -0
- package/dist/gpu/webgl2-backend.d.ts +69 -0
- package/dist/gpu/webgpu-backend.d.ts +155 -0
- package/dist/index.d.ts +27 -0
- package/dist/parsers/atlas-builder.d.ts +37 -0
- package/dist/parsers/atlas-packing.d.ts +31 -0
- package/dist/parsers/index.d.ts +7 -0
- package/dist/parsers/inflate-js.d.ts +37 -0
- package/dist/parsers/svga-image-decode-worker-client.d.ts +14 -0
- package/dist/parsers/svga-parser-worker-client.d.ts +31 -0
- package/dist/parsers/svga-parser.d.ts +49 -0
- package/dist/parsers/svga-proto-js.d.ts +7 -0
- package/dist/parsers/svga-sprite-table-binary.d.ts +36 -0
- package/dist/parsers/svga-sprite-table.d.ts +59 -0
- package/dist/parsers/svga-worker-payload-binary.d.ts +10 -0
- package/dist/parsers/vap-config-parser.d.ts +20 -0
- package/dist/renderers/alpha-video-renderer.d.ts +48 -0
- package/dist/renderers/image-renderer.d.ts +13 -0
- package/dist/renderers/index.d.ts +4 -0
- package/dist/renderers/svga-batch-renderer.d.ts +113 -0
- package/dist/renderers/vap-renderer.d.ts +58 -0
- package/dist/renderers/vertex-builder.d.ts +5 -0
- package/dist/shaders/index.d.ts +49 -0
- package/dist/types/index.d.ts +699 -0
- package/dist/utils/abort.d.ts +3 -0
- package/dist/utils/atlas-constants.d.ts +9 -0
- package/dist/utils/buffer-pool.d.ts +14 -0
- package/dist/utils/canvas-dpr.d.ts +37 -0
- package/dist/utils/clip-mesh.d.ts +6 -0
- package/dist/utils/detect.d.ts +7 -0
- package/dist/utils/easing.d.ts +7 -0
- package/dist/utils/frame-budget.d.ts +36 -0
- package/dist/utils/gift-object-fit.d.ts +14 -0
- package/dist/utils/gift-position.d.ts +5 -0
- package/dist/utils/gift-size.d.ts +11 -0
- package/dist/utils/gift-transform.d.ts +29 -0
- package/dist/utils/html-video-upload-gate.d.ts +35 -0
- package/dist/utils/image-source.d.ts +9 -0
- package/dist/utils/math.d.ts +9 -0
- package/dist/utils/resource-cache.d.ts +20 -0
- package/dist/utils/slot-content.d.ts +17 -0
- package/dist/utils/svg-path-parser.d.ts +11 -0
- package/dist/utils/svga-binary-cache.d.ts +26 -0
- package/dist/utils/svga-work-scheduler.d.ts +7 -0
- package/dist/utils/triangulate.d.ts +11 -0
- package/dist/utils/utf8.d.ts +2 -0
- package/dist/utils/vap-mix-composer.d.ts +26 -0
- package/dist/utils/wc-atlas-upload-size.d.ts +9 -0
- package/dist/video/mp4box-loader.d.ts +2 -0
- package/dist/video/webcodecs-mp4-core.d.ts +49 -0
- package/dist/video/webcodecs-mp4-playback.d.ts +71 -0
- package/dist/video/webcodecs-mp4.worker.d.ts +18 -0
- package/dist/wasm/wasm-bridge.d.ts +36 -0
- package/dist/workers/parser-worker.d.ts +13 -0
- package/dist/workers/svga-atlas-worker-client.d.ts +25 -0
- package/dist/workers/svga-atlas-worker.d.ts +37 -0
- package/dist/workers/svga-clip-worker-client.d.ts +6 -0
- package/dist/workers/svga-clip-worker.d.ts +17 -0
- package/dist/workers/svga-image-decode-worker.d.ts +19 -0
- package/dist/workers/vertex-worker.d.ts +15 -0
- package/dist/workers/worker-scheduler.d.ts +19 -0
- package/package.json +7 -5
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Normalized atlas sub-rect: min (0,0) + extent (1,1) = full texture. */
|
|
2
|
+
export declare const ATLAS_REMAP_IDENTITY: Float32Array<ArrayBuffer>;
|
|
3
|
+
/** Writes normalized `u_atlasRemap` for a sub-rect in an `tw×th` texture. Reuses `out` when provided. */
|
|
4
|
+
export declare function wcAtlasRemap(tw: number, th: number, rect: {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
} | undefined, out?: Float32Array): Float32Array;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reusable typed-array pool to avoid per-frame allocation.
|
|
3
|
+
* Grows as needed; never shrinks.
|
|
4
|
+
* Keeps separate pools for frame data and vertex data to prevent aliasing.
|
|
5
|
+
*/
|
|
6
|
+
export declare class BufferPool {
|
|
7
|
+
private floatPoolA;
|
|
8
|
+
private floatPoolB;
|
|
9
|
+
private uint32Pool;
|
|
10
|
+
getFloat32(minLength: number): Float32Array;
|
|
11
|
+
getFloat32B(minLength: number): Float32Array;
|
|
12
|
+
getUint32(minLength: number): Uint32Array;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface MaxRenderSize {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
}
|
|
5
|
+
/** Default backing-store cap on phones / tablets. */
|
|
6
|
+
export declare const DEFAULT_MAX_RENDER_SIZE_MOBILE: MaxRenderSize;
|
|
7
|
+
/** Default backing-store cap on desktop / laptop. */
|
|
8
|
+
export declare const DEFAULT_MAX_RENDER_SIZE_DESKTOP: MaxRenderSize;
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Prefer {@link resolveDefaultMaxRenderSize}; kept as the mobile default for callers
|
|
11
|
+
* that still import a single constant.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_MAX_RENDER_SIZE: MaxRenderSize;
|
|
14
|
+
/**
|
|
15
|
+
* Heuristic: treat classic mobile UA and iPadOS-as-Mac (touch Mac) as mobile.
|
|
16
|
+
* Used only to pick the default render-size cap; explicit `maxRenderSize` always wins.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isMobileLikeDevice(nav?: Pick<Navigator, 'userAgent' | 'maxTouchPoints'> | null | undefined): boolean;
|
|
19
|
+
/** Platform-aware default: mobile 1920×1080, desktop 2560×1440. */
|
|
20
|
+
export declare function resolveDefaultMaxRenderSize(nav?: Pick<Navigator, 'userAgent' | 'maxTouchPoints'> | null): MaxRenderSize;
|
|
21
|
+
/**
|
|
22
|
+
* Sync canvas bitmap size to CSS layout × DPR so one buffer pixel maps to one physical pixel.
|
|
23
|
+
* Uses integer dimensions to avoid inconsistent rounding blur on mobile.
|
|
24
|
+
*
|
|
25
|
+
* The backing store is additionally capped by `maxRenderSize` (orientation-adaptive: the
|
|
26
|
+
* long edge is limited by the larger dimension, the short edge by the smaller one).
|
|
27
|
+
* When capped, the CSS size stays unchanged and the browser scales the buffer up.
|
|
28
|
+
* Pass `false` to disable the cap. When omitted, uses {@link resolveDefaultMaxRenderSize}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function syncCanvasBackingStore(canvas: HTMLCanvasElement, dpr: number, maxRenderSize?: MaxRenderSize | false): void;
|
|
31
|
+
/**
|
|
32
|
+
* `addGift` layout (x, y, width, height) is in CSS pixels; rendering uses the bitmap size.
|
|
33
|
+
*/
|
|
34
|
+
export declare function canvasBufferCssScale(canvas: HTMLCanvasElement): {
|
|
35
|
+
sx: number;
|
|
36
|
+
sy: number;
|
|
37
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface ClipMeshData {
|
|
2
|
+
vertices: Float32Array;
|
|
3
|
+
indices: Uint32Array;
|
|
4
|
+
}
|
|
5
|
+
export type ClipMeshTriangulator = (vertices: Float64Array, polygonStarts: Uint32Array) => Uint32Array | null;
|
|
6
|
+
export declare function buildClipMeshFromPath(clipPath: string, triangulator?: ClipMeshTriangulator): ClipMeshData | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function canCopyExternalImageToTexture(): boolean;
|
|
2
|
+
export declare function canWebGPU(): boolean;
|
|
3
|
+
export declare function canWebGL2(): boolean;
|
|
4
|
+
export declare function canWebGL1(): boolean;
|
|
5
|
+
export declare function canSharedArrayBuffer(): boolean;
|
|
6
|
+
export declare function canOffscreenCanvas(): boolean;
|
|
7
|
+
export declare function canVideoFrameCallback(): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function linear(t: number): number;
|
|
2
|
+
export declare function easeInQuad(t: number): number;
|
|
3
|
+
export declare function easeOutQuad(t: number): number;
|
|
4
|
+
export declare function easeInOutQuad(t: number): number;
|
|
5
|
+
export declare function easeOutCubic(t: number): number;
|
|
6
|
+
export declare function easeInOutCubic(t: number): number;
|
|
7
|
+
export declare function easeOutBack(t: number): number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface MainThreadFrameBudgetOptions {
|
|
2
|
+
/** Maximum work time before yielding. Defaults to 12ms. */
|
|
3
|
+
maxBudgetMs?: number;
|
|
4
|
+
/** Minimum work time before yielding. Defaults to 2ms. */
|
|
5
|
+
minBudgetMs?: number;
|
|
6
|
+
/** Fraction of the observed frame interval reserved for this task. Defaults to 0.5. */
|
|
7
|
+
frameFraction?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveFrameBudgetMs(frameIntervalMs: number, options?: MainThreadFrameBudgetOptions): number;
|
|
10
|
+
export declare function smoothFrameInterval(previousMs: number, observedMs: number): number;
|
|
11
|
+
export interface SharedFrameBudget {
|
|
12
|
+
readonly budgetMs: number;
|
|
13
|
+
shouldYield(): boolean;
|
|
14
|
+
yield(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns a process-shared SVGA load budget. Each caller may provide a more
|
|
18
|
+
* restrictive cap while still sharing the same observed display-frame slice.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createSharedSVGAFrameBudget(options?: MainThreadFrameBudgetOptions): SharedFrameBudget;
|
|
21
|
+
/**
|
|
22
|
+
* Time-slices load work against half of the observed display frame interval.
|
|
23
|
+
* The estimate is updated after every rAF yield, so 120Hz displays receive
|
|
24
|
+
* shorter slices while slower displays avoid excessive yield overhead.
|
|
25
|
+
*/
|
|
26
|
+
export declare class MainThreadFrameBudget {
|
|
27
|
+
private frameIntervalMs;
|
|
28
|
+
private lastFrameAt;
|
|
29
|
+
private sliceStartedAt;
|
|
30
|
+
private readonly options;
|
|
31
|
+
constructor(options?: MainThreadFrameBudgetOptions);
|
|
32
|
+
get budgetMs(): number;
|
|
33
|
+
shouldYield(): boolean;
|
|
34
|
+
resetSlice(): void;
|
|
35
|
+
yield(): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GiftObjectFit, GiftSlot } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Default when `objectFit` is omitted: all gift types use `contain` (uniform fit inside width×height).
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveGiftObjectFit(slot: GiftSlot): GiftObjectFit;
|
|
6
|
+
export interface GiftObjectFitLayout {
|
|
7
|
+
scaleX: number;
|
|
8
|
+
scaleY: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
offsetX: number;
|
|
12
|
+
offsetY: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveGiftObjectFitLayout(slot: GiftSlot, contentWidth: number, contentHeight: number, out?: GiftObjectFitLayout): GiftObjectFitLayout;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function resolveIntrinsicAlphaVideoSize(videoWidth: number, videoHeight: number): GiftIntrinsicSize;
|
|
2
|
+
export interface GiftIntrinsicSize {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
export interface GiftDisplaySizeInput {
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
useOriginalSize?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveGiftDisplaySize(requested: GiftDisplaySizeInput, intrinsic: GiftIntrinsicSize, stageWidth: number, stageHeight: number): GiftIntrinsicSize;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { GiftSlot } from '../types';
|
|
2
|
+
export interface GiftTransformOrigin {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Affine2D {
|
|
7
|
+
a: number;
|
|
8
|
+
b: number;
|
|
9
|
+
c: number;
|
|
10
|
+
d: number;
|
|
11
|
+
tx: number;
|
|
12
|
+
ty: number;
|
|
13
|
+
}
|
|
14
|
+
/** Resolve the fixed local origin to stage buffer pixels for the slot's current scale. */
|
|
15
|
+
export declare function resolveGiftTransformOrigin(slot: GiftSlot, out?: GiftTransformOrigin): GiftTransformOrigin;
|
|
16
|
+
/**
|
|
17
|
+
* Build a column-major mat4 mapping a unit quad to the supplied stage-space rectangle,
|
|
18
|
+
* then rotating it clockwise (in the screen's y-down coordinate system) around the gift origin.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fillGiftTransformMatrix(slot: GiftSlot, x: number, y: number, width: number, height: number, out: Float32Array): Float32Array;
|
|
21
|
+
/** Apply the gift's parent rotation to an existing local-to-stage affine transform. */
|
|
22
|
+
export declare function rotateGiftAffine(slot: GiftSlot, transform: Affine2D, out?: Affine2D): Affine2D;
|
|
23
|
+
/** AABB of a rotated stage-space rectangle, suitable for the backend scissor. */
|
|
24
|
+
export declare function getRotatedRectAABB(slot: GiftSlot, x: number, y: number, width: number, height: number): {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
w: number;
|
|
28
|
+
h: number;
|
|
29
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aligns GPU texture uploads with decoded video frames using `HTMLVideoElement.requestVideoFrameCallback`.
|
|
3
|
+
* Using only `currentTime` / rAF misses real decode timing under load and causes "high FPS counter but choppy video"
|
|
4
|
+
* when many `<video>` elements run together (especially with WebGPU texture copies).
|
|
5
|
+
*/
|
|
6
|
+
export declare class HtmlVideoUploadGate {
|
|
7
|
+
private readonly frameInfo;
|
|
8
|
+
private readonly pending;
|
|
9
|
+
private readonly lastHandle;
|
|
10
|
+
private readonly started;
|
|
11
|
+
private readonly firstUploaded;
|
|
12
|
+
private readonly forceTimeFallback;
|
|
13
|
+
private readonly lastArmAt;
|
|
14
|
+
private readonly lastCallbackAt;
|
|
15
|
+
private readonly lastProgressAt;
|
|
16
|
+
private readonly lastRecoveryAt;
|
|
17
|
+
private readonly stallLogged;
|
|
18
|
+
private readonly stagnationLogged;
|
|
19
|
+
/** When `requestVideoFrameCallback` is missing, fall back to `currentTime` changes (coarser than rVFC). */
|
|
20
|
+
private readonly lastTimeFallback;
|
|
21
|
+
/**
|
|
22
|
+
* Returns true when `updateTexture` should run for this slot: first time, or a new decoded frame was signaled.
|
|
23
|
+
*/
|
|
24
|
+
preUpload(slotId: string, video: HTMLVideoElement): boolean;
|
|
25
|
+
getFrameInfo(slotId: string): VideoFrameCallbackMetadata | undefined;
|
|
26
|
+
private ensureRvf;
|
|
27
|
+
private consumeTimeAdvance;
|
|
28
|
+
private markProgress;
|
|
29
|
+
private tryRecoverStalledPlayback;
|
|
30
|
+
private debugInfo;
|
|
31
|
+
private debugWarn;
|
|
32
|
+
private snapshot;
|
|
33
|
+
release(slotId: string, video: HTMLVideoElement | null | undefined): void;
|
|
34
|
+
private cleanup;
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface DecodedImageSource {
|
|
2
|
+
source: TexImageSource;
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
sourcePremultiplied: boolean;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
}
|
|
8
|
+
/** Decode a supported image gift source without retaining the encoded bytes. */
|
|
9
|
+
export declare function decodeImageSource(source: string | ArrayBuffer, signal?: AbortSignal): Promise<DecodedImageSource>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an orthographic projection matrix (column-major Float32Array).
|
|
3
|
+
* Maps (left..right, bottom..top, near..far) to clip space [-1,1].
|
|
4
|
+
* Y-down convention: top < bottom maps SVGA viewBox style.
|
|
5
|
+
*/
|
|
6
|
+
export declare function ortho(left: number, right: number, bottom: number, top: number, near: number, far: number): Float32Array;
|
|
7
|
+
export declare function lerp(a: number, b: number, t: number): number;
|
|
8
|
+
export declare function clamp(v: number, min: number, max: number): number;
|
|
9
|
+
export declare function generateId(): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SVGAWorkerMoviePayload } from '../parsers/svga-parser-worker-client';
|
|
2
|
+
type PersistKind = 'arrayBuffer' | 'text';
|
|
3
|
+
type MemoryKind = PersistKind | 'json' | 'svga-payload' | 'svga-asset';
|
|
4
|
+
export declare function configureResourceCache(opts?: {
|
|
5
|
+
maxConcurrent?: number;
|
|
6
|
+
maxMemoryBytes?: number;
|
|
7
|
+
maxPersistentBytes?: number;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function runResourceTaskLimited<T>(task: () => Promise<T>): Promise<T>;
|
|
10
|
+
export declare function fetchCachedArrayBuffer(url: string, signal?: AbortSignal): Promise<ArrayBuffer>;
|
|
11
|
+
export declare function fetchCachedText(url: string, signal?: AbortSignal): Promise<string>;
|
|
12
|
+
export declare function fetchCachedJSON<T>(url: string, signal?: AbortSignal): Promise<T>;
|
|
13
|
+
export declare function getCachedSVGAWorkerPayload(cacheKey: string): SVGAWorkerMoviePayload | null;
|
|
14
|
+
export declare function setCachedSVGAWorkerPayload(cacheKey: string, payload: SVGAWorkerMoviePayload): void;
|
|
15
|
+
export declare function getCachedMemoryValue<T>(cacheKey: string, kind: MemoryKind): T | null;
|
|
16
|
+
export declare function setCachedMemoryValue<T>(cacheKey: string, kind: MemoryKind, value: T, size: number): void;
|
|
17
|
+
export declare function getPersistedArrayBuffer(cacheKey: string): Promise<ArrayBuffer | null>;
|
|
18
|
+
export declare function setPersistedArrayBuffer(cacheKey: string, value: ArrayBuffer): Promise<void>;
|
|
19
|
+
export declare function deletePersistedValue(cacheKey: string): Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GPUBackend, ResolvedSVGASlotTexture, ResolvedVAPResource, SVGASlotContent, VAPConfig, VAPFrameObj, VAPMixState, VAPSlotContent, VAPSrcItem, VideoEntity } from '../types';
|
|
2
|
+
export declare function resolveSVGASlotTextures(backend: GPUBackend, entity: VideoEntity, slots: Record<string, SVGASlotContent> | undefined, options?: {
|
|
3
|
+
frameBudgetMs?: number;
|
|
4
|
+
checkActive?: () => void;
|
|
5
|
+
}): Promise<Map<string, ResolvedSVGASlotTexture>>;
|
|
6
|
+
export declare function getVAPSlotOverride(item: VAPSrcItem, slots: Record<string, VAPSlotContent> | undefined): VAPSlotContent | undefined;
|
|
7
|
+
export declare function resolveVAPResources(config: VAPConfig, slots: Record<string, VAPSlotContent> | undefined): Promise<Map<string, ResolvedVAPResource>>;
|
|
8
|
+
export declare function buildVAPFrameMap(config: VAPConfig): Map<number, VAPFrameObj[]>;
|
|
9
|
+
export declare function createVAPMixCanvasState(width: number, height: number): {
|
|
10
|
+
compositeCanvas: HTMLCanvasElement;
|
|
11
|
+
compositeCtx: CanvasRenderingContext2D;
|
|
12
|
+
rawCanvas: HTMLCanvasElement;
|
|
13
|
+
rawCtx: CanvasRenderingContext2D;
|
|
14
|
+
scratchCanvas: HTMLCanvasElement;
|
|
15
|
+
scratchCtx: CanvasRenderingContext2D;
|
|
16
|
+
};
|
|
17
|
+
export declare function compositeVAPFrame(state: VAPMixState, config: VAPConfig, source: CanvasImageSource, frameIndex: number, rgbPremultiplied?: boolean): HTMLCanvasElement;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal SVG path parser for SVGA clipPath strings.
|
|
3
|
+
* Converts an SVG path `d` attribute into an array of 2D polygon points
|
|
4
|
+
* by flattening curves into line segments.
|
|
5
|
+
*/
|
|
6
|
+
interface Point {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseSvgPath(d: string): Point[][];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SVGAParams, SVGAAudio, SVGASpriteTable, UVRect } from '../types';
|
|
2
|
+
import type { ClipMeshData } from './clip-mesh';
|
|
3
|
+
export interface SVGACachedAsset {
|
|
4
|
+
params: SVGAParams;
|
|
5
|
+
spriteTable: SVGASpriteTable;
|
|
6
|
+
audios: SVGAAudio[];
|
|
7
|
+
audioBlobs: Map<string, ArrayBuffer>;
|
|
8
|
+
atlasRects: Map<string, UVRect>;
|
|
9
|
+
atlasPages: SVGACachedAtlasPage[];
|
|
10
|
+
clipMeshes?: Map<string, ClipMeshData>;
|
|
11
|
+
}
|
|
12
|
+
export interface SVGACachedAtlasPage {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
imageBytes: ArrayBuffer;
|
|
16
|
+
}
|
|
17
|
+
export declare function serializeSVGACachedAsset(asset: SVGACachedAsset): ArrayBuffer;
|
|
18
|
+
export declare function serializeSVGACachedAssetCooperative(asset: SVGACachedAsset, frameBudgetMs?: number): Promise<ArrayBuffer>;
|
|
19
|
+
export declare function deserializeSVGACachedAsset(buffer: ArrayBuffer): SVGACachedAsset;
|
|
20
|
+
export declare function deserializeSVGACachedAssetCooperative(buffer: ArrayBuffer, frameBudgetMs?: number): Promise<SVGACachedAsset>;
|
|
21
|
+
export declare function loadSVGACachedAsset(sourceUrl: string, options?: {
|
|
22
|
+
frameBudgetMs?: number;
|
|
23
|
+
}): Promise<SVGACachedAsset | null>;
|
|
24
|
+
export declare function storeSVGACachedAsset(sourceUrl: string, asset: SVGACachedAsset, options?: {
|
|
25
|
+
frameBudgetMs?: number;
|
|
26
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVGA parser, image decode, atlas and clip workers share this gate so they
|
|
3
|
+
* do not create a multi-core CPU burst while real-time media is active.
|
|
4
|
+
*/
|
|
5
|
+
export declare function configureSVGAWorkerTaskConcurrency(maxConcurrent?: number): void;
|
|
6
|
+
export declare function runSVGAWorkerTask<T>(task: () => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
7
|
+
export declare function acquireSVGAWorkerTask(signal?: AbortSignal): Promise<() => void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ear-clipping polygon triangulation.
|
|
3
|
+
* Input: array of 2D polygon vertices (CCW or CW winding).
|
|
4
|
+
* Output: array of triangle index triples.
|
|
5
|
+
*/
|
|
6
|
+
interface Vec2 {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function triangulate(polygon: Vec2[]): number[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ResolvedVAPResource, VAPConfig, VAPFrameObj } from '../types';
|
|
2
|
+
export declare function buildVAPMixShaderSources(textureCount: number, positionSlotCount: number, webgl2: boolean): {
|
|
3
|
+
vertex: string;
|
|
4
|
+
fragment: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function buildVAPMixImagePositions(config: VAPConfig, frameMap: Map<number, VAPFrameObj[]>, textureUnitBySrcId: Map<string, number>, frameIndex: number, out?: Float32Array): Float32Array;
|
|
7
|
+
export declare class VAPMixComposer {
|
|
8
|
+
private readonly config;
|
|
9
|
+
private readonly frameMap;
|
|
10
|
+
readonly canvas: HTMLCanvasElement;
|
|
11
|
+
private readonly gl;
|
|
12
|
+
private readonly program;
|
|
13
|
+
private readonly vertexShader;
|
|
14
|
+
private readonly fragmentShader;
|
|
15
|
+
private readonly vertexBuffer;
|
|
16
|
+
private readonly videoTexture;
|
|
17
|
+
private readonly imagePosLoc;
|
|
18
|
+
private readonly imagePositionsScratch;
|
|
19
|
+
private readonly resourceEntries;
|
|
20
|
+
private readonly textureUnitBySrcId;
|
|
21
|
+
private videoTextureWidth;
|
|
22
|
+
private videoTextureHeight;
|
|
23
|
+
constructor(config: VAPConfig, frameMap: Map<number, VAPFrameObj[]>, resources: Map<string, ResolvedVAPResource>, width: number, height: number, rgbPremultiplied?: boolean);
|
|
24
|
+
render(source: TexImageSource, frameIndex: number): HTMLCanvasElement;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GiftSlot, VAPConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Atlas texel size for a WebCodecs frame: uniform downscale so the full frame matches
|
|
4
|
+
* on-screen footprint (same scale factor as gift renderers use for layout).
|
|
5
|
+
*/
|
|
6
|
+
export declare function wcAtlasUploadTexelSize(slot: GiftSlot, frameW: number, frameH: number, kind: 'alphaVideo' | 'vap', vapConfig: VAPConfig | undefined): {
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared mp4box demux + WebCodecs H.264 decode (used on main thread fallback or inside Worker).
|
|
3
|
+
*/
|
|
4
|
+
import type { DataStream, Sample } from 'mp4box';
|
|
5
|
+
/** Avoid decoding huge clips entirely into VideoFrame[] (memory). */
|
|
6
|
+
export declare const MAX_WEBCODECS_FRAMES = 800;
|
|
7
|
+
export declare function isH264Codec(codec: string): boolean;
|
|
8
|
+
export declare function stripAvccBoxWrapperIfPresent(recordOrBox: ArrayBuffer): ArrayBuffer;
|
|
9
|
+
export declare function avcDescriptionFromSampleEntry(entry: {
|
|
10
|
+
avcC?: {
|
|
11
|
+
write: (s: DataStream) => void;
|
|
12
|
+
};
|
|
13
|
+
}): Promise<ArrayBuffer | undefined>;
|
|
14
|
+
export type MP4BoxBuffer = ArrayBuffer & {
|
|
15
|
+
fileStart: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function collectMp4Samples(ab: ArrayBuffer): Promise<{
|
|
18
|
+
codec: string;
|
|
19
|
+
timescale: number;
|
|
20
|
+
durationSec: number;
|
|
21
|
+
videoWidth: number;
|
|
22
|
+
videoHeight: number;
|
|
23
|
+
samples: Sample[];
|
|
24
|
+
}>;
|
|
25
|
+
export declare function waitDecodeQueueDrained(decoder: VideoDecoder, label: string, maxWaitMs: number): Promise<void>;
|
|
26
|
+
export declare function flushDecoderOrThrow(decoder: VideoDecoder, meta: {
|
|
27
|
+
chunksDecoded: number;
|
|
28
|
+
outputsBeforeFlush: number;
|
|
29
|
+
queueBeforeFlush: number;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
export declare function decodeH264ToFrames(codec: string, description: ArrayBuffer | undefined, samples: Sample[], timescale: number): Promise<{
|
|
32
|
+
frames: VideoFrame[];
|
|
33
|
+
timesSec: Float64Array;
|
|
34
|
+
}>;
|
|
35
|
+
export declare function frameIndexForTime(timesSec: Float64Array, clipDurationSec: number, t: number): number;
|
|
36
|
+
export declare function computeClipDurationSeconds(metaDurationSec: number, timesSec: Float64Array, frames: VideoFrame[]): number;
|
|
37
|
+
/** Clip duration from demux metadata + presentation timeline only (no decoded frames). */
|
|
38
|
+
export declare function computeClipDurationSecondsFromTimes(metaDurationSec: number, timesSec: Float64Array): number;
|
|
39
|
+
/**
|
|
40
|
+
* Presentation order timestamps (sorted by CTS/DTS), one entry per sample — matches decoder output order after sort-by-timestamp.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildPresentationTimesSec(samples: Sample[], timescale: number): Float64Array;
|
|
43
|
+
/** Decode order: DTS (then stable index). */
|
|
44
|
+
export declare function sortSamplesDecodeOrder(samples: Sample[]): Sample[];
|
|
45
|
+
/** Map decoder output timestamp (µs) to presentation index using precomputed timeline. */
|
|
46
|
+
export declare function presentationIndexFromTimestampUs(presentationTimesSec: Float64Array, timestampUs: number): number;
|
|
47
|
+
export declare function findFirstSyncChunkIndexInDecodeOrder(decodeOrder: Sample[]): number;
|
|
48
|
+
/** Shared config resolution for H.264 WebCodecs (same logic as {@link decodeH264ToFrames}). */
|
|
49
|
+
export declare function resolveH264DecoderConfig(codec: string, description: ArrayBuffer | undefined): Promise<VideoDecoderConfig>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation-index → VideoFrame with bounded size.
|
|
3
|
+
* Eviction follows the playhead (see {@link setPlaybackHint}) so we keep a window around the
|
|
4
|
+
* current time — pure "evict global min" would leave only the last N decoded frames after a fast
|
|
5
|
+
* full decode, making middle-of-timeline playback return null.
|
|
6
|
+
*/
|
|
7
|
+
export declare class VideoFrameRing {
|
|
8
|
+
private readonly maxSize;
|
|
9
|
+
private readonly map;
|
|
10
|
+
/** Last known presentation index from the clock (updated from ensure / getFrameForTime). */
|
|
11
|
+
private playbackHint;
|
|
12
|
+
constructor(maxSize: number);
|
|
13
|
+
setPlaybackHint(idx: number): void;
|
|
14
|
+
minKey(): number | undefined;
|
|
15
|
+
set(idx: number, frame: VideoFrame): void;
|
|
16
|
+
private evictDownToCapacity;
|
|
17
|
+
private closeKey;
|
|
18
|
+
bestAtOrBefore(idx: number): VideoFrame | null;
|
|
19
|
+
/**
|
|
20
|
+
* Prefer latest frame at or before `idx`; if none (decode lag / gap), use nearest at or after;
|
|
21
|
+
* if still only lower indices exist, use the highest held (best effort).
|
|
22
|
+
*/
|
|
23
|
+
frameForDisplay(idx: number): VideoFrame | null;
|
|
24
|
+
clear(): void;
|
|
25
|
+
get size(): number;
|
|
26
|
+
}
|
|
27
|
+
export interface WebCodecsMp4CreateOptions {
|
|
28
|
+
/**
|
|
29
|
+
* When true (default), demux+decode run in a Dedicated Worker and VideoFrames are transferred to the main thread.
|
|
30
|
+
* Set false to force main-thread decode (tests, environments without Workers).
|
|
31
|
+
*/
|
|
32
|
+
useWorker?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Max presentation frames retained on the main thread (ring). Older indices are closed when exceeded.
|
|
35
|
+
* Default {@link DEFAULT_MAX_DECODED_FRAMES}.
|
|
36
|
+
*/
|
|
37
|
+
maxDecodedFrames?: number;
|
|
38
|
+
/**
|
|
39
|
+
* How many frames ahead of the playhead the worker tries to decode after demux / on each ensure.
|
|
40
|
+
* Default {@link DEFAULT_DECODE_AHEAD_FRAMES}.
|
|
41
|
+
*/
|
|
42
|
+
decodeAheadFrames?: number;
|
|
43
|
+
}
|
|
44
|
+
export declare class WebCodecsMp4Playback {
|
|
45
|
+
readonly duration: number;
|
|
46
|
+
readonly videoWidth: number;
|
|
47
|
+
readonly videoHeight: number;
|
|
48
|
+
private readonly timesSec;
|
|
49
|
+
private readonly worker;
|
|
50
|
+
private readonly workerMsgId;
|
|
51
|
+
private readonly streaming;
|
|
52
|
+
private readonly ring;
|
|
53
|
+
private readonly frames;
|
|
54
|
+
private readonly maxDecodedFrames;
|
|
55
|
+
private readonly decodeAheadFrames;
|
|
56
|
+
private lastWrapped;
|
|
57
|
+
private lastEnsureNeedIdx;
|
|
58
|
+
private lastEnsureFreeSlots;
|
|
59
|
+
private destroyed;
|
|
60
|
+
private constructor();
|
|
61
|
+
/**
|
|
62
|
+
* Fetch MP4 (if URL) → demux + H.264 decode in Worker (default) or main thread.
|
|
63
|
+
* Call {@link destroy} when done.
|
|
64
|
+
*/
|
|
65
|
+
static create(source: string | ArrayBuffer, signal?: AbortSignal, opts?: WebCodecsMp4CreateOptions): Promise<WebCodecsMp4Playback>;
|
|
66
|
+
private static createViaWorker;
|
|
67
|
+
private static createOnMainThread;
|
|
68
|
+
ensureDecodedThrough(tSec: number): void;
|
|
69
|
+
getFrameForTime(tSec: number): VideoFrame | null;
|
|
70
|
+
destroy(): void;
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type MainToWorker = {
|
|
2
|
+
type: 'init';
|
|
3
|
+
id: number;
|
|
4
|
+
buffer: ArrayBuffer;
|
|
5
|
+
maxDecodedFrames: number;
|
|
6
|
+
decodeAheadFrames: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'ensure';
|
|
9
|
+
id: number;
|
|
10
|
+
needIdx: number;
|
|
11
|
+
freeSlots: number;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'rewind';
|
|
14
|
+
id: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'destroy';
|
|
17
|
+
id: number;
|
|
18
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SVGAMovieEntity } from '../types';
|
|
2
|
+
export interface WasmSVGAPayloadProfile {
|
|
3
|
+
inputBytes: number;
|
|
4
|
+
outputBytes: number;
|
|
5
|
+
prostDecodeMs: number;
|
|
6
|
+
metadataAndImagesMs: number;
|
|
7
|
+
frameTableMs: number;
|
|
8
|
+
finalizeMs: number;
|
|
9
|
+
payloadBuildMs: number;
|
|
10
|
+
rustTotalMs: number;
|
|
11
|
+
wasmBoundaryAndGlueMs: number;
|
|
12
|
+
jsCallTotalMs: number;
|
|
13
|
+
imageBytes: number;
|
|
14
|
+
imageCount: number;
|
|
15
|
+
spriteCount: number;
|
|
16
|
+
frameCount: number;
|
|
17
|
+
cellCount: number;
|
|
18
|
+
clipPathCount: number;
|
|
19
|
+
shapeCount: number;
|
|
20
|
+
shapeGroupCount: number;
|
|
21
|
+
shapeBytes: number;
|
|
22
|
+
writerInitialCapacity: number;
|
|
23
|
+
writerFinalCapacity: number;
|
|
24
|
+
}
|
|
25
|
+
export interface WasmSVGAPayloadDecodeResult {
|
|
26
|
+
payload: Uint8Array;
|
|
27
|
+
profile?: WasmSVGAPayloadProfile;
|
|
28
|
+
}
|
|
29
|
+
export declare function isWasmReady(): boolean;
|
|
30
|
+
export declare function initWasm(): Promise<boolean>;
|
|
31
|
+
export declare function wasmInflate(data: Uint8Array): Uint8Array | null;
|
|
32
|
+
export declare function wasmDecodeSVGA(data: Uint8Array): SVGAMovieEntity | null;
|
|
33
|
+
export declare function wasmDecodeSVGAPayload(data: Uint8Array): Uint8Array | null;
|
|
34
|
+
export declare function wasmDecodeSVGAPayloadProfiled(data: Uint8Array): WasmSVGAPayloadDecodeResult | null;
|
|
35
|
+
export declare function wasmBuildBatchVertices(frames: Float32Array, outVertices: Float32Array, outIndices: Uint32Array): number | null;
|
|
36
|
+
export declare function wasmTriangulateClipPolygons(vertices: Float64Array, polygonStarts: Uint32Array): Uint32Array | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ParserWorkerRequest {
|
|
2
|
+
type: 'parse-svga';
|
|
3
|
+
id: number;
|
|
4
|
+
data: ArrayBuffer;
|
|
5
|
+
profile?: boolean;
|
|
6
|
+
workerTimeSliceMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ParserWorkerResponse {
|
|
9
|
+
type: 'parse-svga-result';
|
|
10
|
+
id: number;
|
|
11
|
+
result?: unknown;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SVGAAtlasWorkerPageResult, SVGAAtlasWorkerRect } from './svga-atlas-worker';
|
|
2
|
+
export interface SVGAAtlasWorkerBuildOptions {
|
|
3
|
+
maxSize: number;
|
|
4
|
+
padding: number;
|
|
5
|
+
maxPages: number;
|
|
6
|
+
captureEncodedPng: boolean;
|
|
7
|
+
workerTimeSliceMs?: number;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export interface SVGAAtlasWorkerBuildResult {
|
|
11
|
+
pages: Array<SVGAAtlasWorkerPageResult & {
|
|
12
|
+
pngBytesPromise?: Promise<ArrayBuffer | null>;
|
|
13
|
+
}>;
|
|
14
|
+
rects: SVGAAtlasWorkerRect[];
|
|
15
|
+
}
|
|
16
|
+
/** The worker has consumed the transferred ImageBitmaps, so callers must rebuild the image set. */
|
|
17
|
+
export declare class SVGAAtlasWorkerBuildError extends Error {
|
|
18
|
+
constructor(message: string, options?: ErrorOptions);
|
|
19
|
+
}
|
|
20
|
+
export declare function canUseSVGAAtlasWorker(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Transfer ImageBitmaps to the atlas worker (consumes / closes them on success or failure).
|
|
23
|
+
* Caller must not use the bitmaps after calling.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildSVGAAtlasInWorker(images: Map<string, ImageBitmap>, options: SVGAAtlasWorkerBuildOptions): Promise<SVGAAtlasWorkerBuildResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface SVGAAtlasWorkerRequest {
|
|
2
|
+
type: 'build-svga-atlas';
|
|
3
|
+
id: number;
|
|
4
|
+
keys: string[];
|
|
5
|
+
bitmaps: ImageBitmap[];
|
|
6
|
+
maxSize: number;
|
|
7
|
+
padding: number;
|
|
8
|
+
maxPages: number;
|
|
9
|
+
captureEncodedPng: boolean;
|
|
10
|
+
workerTimeSliceMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface SVGAAtlasWorkerPageResult {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
bitmap: ImageBitmap;
|
|
16
|
+
}
|
|
17
|
+
export interface SVGAAtlasWorkerRect {
|
|
18
|
+
key: string;
|
|
19
|
+
textureIndex: number;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
w: number;
|
|
23
|
+
h: number;
|
|
24
|
+
}
|
|
25
|
+
export interface SVGAAtlasWorkerResponse {
|
|
26
|
+
type: 'build-svga-atlas-result';
|
|
27
|
+
id: number;
|
|
28
|
+
pages?: SVGAAtlasWorkerPageResult[];
|
|
29
|
+
rects?: SVGAAtlasWorkerRect[];
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SVGAAtlasWorkerPngResponse {
|
|
33
|
+
type: 'build-svga-atlas-png-result';
|
|
34
|
+
id: number;
|
|
35
|
+
pageIndex: number;
|
|
36
|
+
pngBytes: ArrayBuffer | null;
|
|
37
|
+
}
|