@taole/giftstage 0.1.28 → 0.1.29

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.
Files changed (74) hide show
  1. package/README.md +89 -36
  2. package/dist/core/audio-manager.d.ts +19 -0
  3. package/dist/core/gift-stage.d.ts +117 -0
  4. package/dist/core/index.d.ts +4 -0
  5. package/dist/core/post-animator.d.ts +43 -0
  6. package/dist/core/render-manager.d.ts +99 -0
  7. package/dist/core/webcodecs-video-atlas.d.ts +30 -0
  8. package/dist/gift-stage.cjs.js +225 -225
  9. package/dist/gift-stage.es.js +2559 -2062
  10. package/dist/gpu/detect.d.ts +2 -0
  11. package/dist/gpu/gl-utils.d.ts +10 -0
  12. package/dist/gpu/index.d.ts +3 -0
  13. package/dist/gpu/webgl1-backend.d.ts +43 -0
  14. package/dist/gpu/webgl2-backend.d.ts +69 -0
  15. package/dist/gpu/webgpu-backend.d.ts +155 -0
  16. package/dist/index.d.ts +27 -0
  17. package/dist/parsers/atlas-builder.d.ts +37 -0
  18. package/dist/parsers/atlas-packing.d.ts +31 -0
  19. package/dist/parsers/index.d.ts +7 -0
  20. package/dist/parsers/inflate-js.d.ts +37 -0
  21. package/dist/parsers/svga-image-decode-worker-client.d.ts +14 -0
  22. package/dist/parsers/svga-parser-worker-client.d.ts +31 -0
  23. package/dist/parsers/svga-parser.d.ts +49 -0
  24. package/dist/parsers/svga-proto-js.d.ts +7 -0
  25. package/dist/parsers/svga-sprite-table-binary.d.ts +36 -0
  26. package/dist/parsers/svga-sprite-table.d.ts +59 -0
  27. package/dist/parsers/svga-worker-payload-binary.d.ts +10 -0
  28. package/dist/parsers/vap-config-parser.d.ts +20 -0
  29. package/dist/renderers/alpha-video-renderer.d.ts +48 -0
  30. package/dist/renderers/image-renderer.d.ts +15 -0
  31. package/dist/renderers/index.d.ts +4 -0
  32. package/dist/renderers/svga-batch-renderer.d.ts +113 -0
  33. package/dist/renderers/vap-renderer.d.ts +58 -0
  34. package/dist/renderers/vertex-builder.d.ts +5 -0
  35. package/dist/shaders/index.d.ts +49 -0
  36. package/dist/types/index.d.ts +699 -0
  37. package/dist/utils/abort.d.ts +3 -0
  38. package/dist/utils/atlas-constants.d.ts +9 -0
  39. package/dist/utils/buffer-pool.d.ts +14 -0
  40. package/dist/utils/canvas-dpr.d.ts +37 -0
  41. package/dist/utils/clip-mesh.d.ts +6 -0
  42. package/dist/utils/detect.d.ts +7 -0
  43. package/dist/utils/easing.d.ts +7 -0
  44. package/dist/utils/frame-budget.d.ts +36 -0
  45. package/dist/utils/gift-object-fit.d.ts +14 -0
  46. package/dist/utils/gift-position.d.ts +5 -0
  47. package/dist/utils/gift-size.d.ts +11 -0
  48. package/dist/utils/gift-transform.d.ts +29 -0
  49. package/dist/utils/html-video-upload-gate.d.ts +35 -0
  50. package/dist/utils/image-source.d.ts +9 -0
  51. package/dist/utils/math.d.ts +9 -0
  52. package/dist/utils/resource-cache.d.ts +20 -0
  53. package/dist/utils/slot-content.d.ts +17 -0
  54. package/dist/utils/svg-path-parser.d.ts +11 -0
  55. package/dist/utils/svga-binary-cache.d.ts +26 -0
  56. package/dist/utils/svga-work-scheduler.d.ts +7 -0
  57. package/dist/utils/triangulate.d.ts +11 -0
  58. package/dist/utils/utf8.d.ts +2 -0
  59. package/dist/utils/vap-mix-composer.d.ts +26 -0
  60. package/dist/utils/wc-atlas-upload-size.d.ts +9 -0
  61. package/dist/video/mp4box-loader.d.ts +2 -0
  62. package/dist/video/webcodecs-mp4-core.d.ts +49 -0
  63. package/dist/video/webcodecs-mp4-playback.d.ts +71 -0
  64. package/dist/video/webcodecs-mp4.worker.d.ts +18 -0
  65. package/dist/wasm/wasm-bridge.d.ts +36 -0
  66. package/dist/workers/parser-worker.d.ts +13 -0
  67. package/dist/workers/svga-atlas-worker-client.d.ts +25 -0
  68. package/dist/workers/svga-atlas-worker.d.ts +37 -0
  69. package/dist/workers/svga-clip-worker-client.d.ts +6 -0
  70. package/dist/workers/svga-clip-worker.d.ts +17 -0
  71. package/dist/workers/svga-image-decode-worker.d.ts +19 -0
  72. package/dist/workers/vertex-worker.d.ts +15 -0
  73. package/dist/workers/worker-scheduler.d.ts +19 -0
  74. package/package.json +7 -5
package/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  - `SVGA`
6
6
  - `VAP / VAPX`
7
- - 透明视频 `AlphaVideo`
7
+ - 透明视频 `AlphaVideo`
8
+ - 静态图片 `Image`
8
9
 
9
10
  底层支持:
10
11
 
@@ -16,7 +17,7 @@
16
17
 
17
18
  - 直播间礼物
18
19
  - 大批同屏动画
19
- - 需要统一接入 `SVGA / VAP / 透明视频` 的业务场景
20
+ - 需要统一接入 `SVGA / VAP / 透明视频 / 图片` 的业务场景
20
21
 
21
22
  ## npm 包信息
22
23
 
@@ -36,14 +37,15 @@ const { GiftStage } = require('@taole/giftstage');
36
37
 
37
38
  ## 特性
38
39
 
39
- - 统一 API:`addGift()` 即可播放三类礼物
40
+ - 统一 API:`addGift()` 即可播放四类礼物
40
41
  - `SVGA` 支持 Worker 解析、atlas 缓存、slot 替换
41
42
  - `VAP / AlphaVideo` 支持实验性的 `WebCodecs` 和稳定的 `HTMLVideoElement` 双路径
42
43
  - 同源视频播放实例、纹理、资源复用
43
44
  - 内存 LRU 缓存,默认 `128MB`
44
45
  - `IndexedDB` 磁盘 LRU 缓存,默认 `2048MB`
45
- - `x / y` 支持百分比定位
46
- - `width / height` 支持按礼物原始尺寸自动补全
46
+ - `x / y` 支持百分比定位
47
+ - `width / height` 支持按礼物原始尺寸自动补全
48
+ - 四类礼物共用位移、缩放、透明度、旋转、层级和销毁生命周期
47
49
 
48
50
  ## 安装
49
51
 
@@ -191,8 +193,9 @@ Promise<GiftHandle>;
191
193
 
192
194
  - `gift.id`
193
195
  - `gift.type`
194
- - `gift.pause()`
195
- - `gift.resume()`
196
+ - `gift.pause()`
197
+ - `gift.resume()`
198
+ - 暂停期间礼物播放时钟、图片 `duration` 和后置动画都会冻结
196
199
  - `gift.destroy()`
197
200
  - 立即移除当前礼物
198
201
  - `gift.animate(stepOrSteps?)`
@@ -237,7 +240,7 @@ Promise<GiftHandle>;
237
240
 
238
241
  ### 通用参数
239
242
 
240
- - `type: 'svga' | 'vap' | 'alphaVideo'`
243
+ - `type: 'svga' | 'vap' | 'alphaVideo' | 'image'`
241
244
  - `source: string | ArrayBuffer`
242
245
  - `x: number | \`${number}%\``
243
246
  - `y: number | \`${number}%\``
@@ -245,25 +248,37 @@ Promise<GiftHandle>;
245
248
  - `width?: number`
246
249
  - `height?: number`
247
250
  - `useOriginalSize?: boolean`
248
- - `objectFit?: 'contain' | 'cover' | 'fill'`
251
+ - `objectFit?: 'contain' | 'cover'`
249
252
  - `loop?: number`
250
253
  - `0` 表示无限循环
251
254
  - `opacity?: number`
252
255
  - 全局透明度,范围 `0 ~ 1`,默认 `1`
253
- - `videoRGBAlphaMode?: 'straight' | 'premultiplied'`
254
- - 仅用于 `vap` / `alphaVideo` 分离 Alpha 视频源,默认 `premultiplied`
255
- - `straight`:RGB 区域尚未乘独立 Alpha,播放器负责预乘
256
- - `premultiplied`:RGB 区域在制作阶段已经乘过独立 Alpha,播放器不会再次相乘
257
- - 该参数只描述输入视频;最终画布仍统一输出预乘 Alpha
258
- - `clearsAfterStop?: boolean`
256
+ - `rotation?: number`
257
+ - 初始旋转弧度;屏幕坐标中正值为顺时针
258
+ - `transformOrigin?: { x: number | \`${number}%\`; y: number | \`${number}%\` }`
259
+ - 相对未缩放礼物框左上角,默认 `50% / 50%`
260
+ - 数值使用 CSS 像素;允许负值或超过礼物宽高,用于绕框外点公转
261
+ - `videoRGBAlphaMode?: 'straight' | 'premultiplied'`
262
+ - 仅用于 `vap` / `alphaVideo` 分离 Alpha 视频源,默认 `premultiplied`
263
+ - `straight`:RGB 区域尚未乘独立 Alpha,播放器负责预乘
264
+ - `premultiplied`:RGB 区域在制作阶段已经乘过独立 Alpha,播放器不会再次相乘
265
+ - 该参数只描述输入视频;最终画布仍统一输出预乘 Alpha
266
+ - `clearsAfterStop?: boolean`
259
267
  - 播放结束后,是否自动移除礼物
260
268
  - 默认 `true`,传 `false` 时会停留最后一帧,方便后续继续调用 `gift.animate(...).start()`
261
- - `mute?: boolean`
262
- - `onComplete?: (gift) => void`
269
+ - `mute?: boolean`
270
+ - `onComplete?: (gift) => void`
271
+
272
+ 图片专用参数:
273
+
274
+ - `duration?: number`
275
+ - 单位毫秒;正数到期后触发 `onComplete`,并遵循 `clearsAfterStop`
276
+ - 不传或传非正数时持续显示到 `destroy()`;加载完成后 `afterGiftComplete` 可立即继续,但不会自动触发 `onComplete`
277
+ - `image` 忽略 `loop` 和 `mute`
263
278
 
264
279
  层级规则:
265
280
 
266
- - `zIndex` 在 `SVGA / VAP / AlphaVideo` 三种礼物间通用
281
+ - `zIndex` 在 `SVGA / VAP / AlphaVideo / Image` 四种礼物间通用
267
282
  - 数值越大,渲染越靠上
268
283
  - 同层级下,后 `addGift()` 的礼物会覆盖先添加的礼物
269
284
 
@@ -353,7 +368,7 @@ transform: translate(-50%, -50%);
353
368
 
354
369
  如果是数值,则继续按原来的像素坐标语义处理。
355
370
 
356
- ## 三类礼物示例
371
+ ## 四类礼物示例
357
372
 
358
373
  ### 播放 SVGA
359
374
 
@@ -378,15 +393,15 @@ await stage.addGift({
378
393
  config: 'https://example.com/demo.json',
379
394
  x: 20,
380
395
  y: 20,
381
- width: 400,
382
- height: 220,
383
- // 默认 premultiplied;RGB 尚未乘独立 Alpha 的视频源需显式传 straight
384
- videoRGBAlphaMode: 'premultiplied',
385
- loop: 0,
386
- });
396
+ width: 400,
397
+ height: 220,
398
+ // 默认 premultiplied;RGB 尚未乘独立 Alpha 的视频源需显式传 straight
399
+ videoRGBAlphaMode: 'premultiplied',
400
+ loop: 0,
401
+ });
387
402
  ```
388
403
 
389
- ### 播放透明视频
404
+ ### 播放透明视频
390
405
 
391
406
  ```ts
392
407
  await stage.addGift({
@@ -394,19 +409,36 @@ await stage.addGift({
394
409
  source: 'https://example.com/demo.mp4',
395
410
  x: 20,
396
411
  y: 20,
397
- width: 400,
398
- height: 220,
399
- // 默认 premultiplied;未预乘的视频源改为 straight
400
- videoRGBAlphaMode: 'premultiplied',
401
- loop: 0,
412
+ width: 400,
413
+ height: 220,
414
+ // 默认 premultiplied;未预乘的视频源改为 straight
415
+ videoRGBAlphaMode: 'premultiplied',
416
+ loop: 0,
402
417
  });
403
- ```
418
+ ```
419
+
420
+ ### 播放图片
421
+
422
+ `source` 首版支持 URL 和 `ArrayBuffer`。URL 图片会按地址共享 GPU 纹理并引用计数;图片按静态帧处理,不保证 GIF / WebP 动画播放。跨域 URL 必须允许匿名 CORS 读取。
423
+
424
+ ```ts
425
+ const avatar = await stage.addGift({
426
+ type: 'image',
427
+ source: 'https://example.com/avatar.png',
428
+ x: '50%',
429
+ y: '50%',
430
+ width: 100,
431
+ height: 100,
432
+ objectFit: 'cover',
433
+ duration: 5000,
434
+ });
435
+ ```
404
436
 
405
437
  ## 动画控制
406
438
 
407
439
  ### 链式动画
408
440
 
409
- `addGift()` 返回的 `GiftHandle` 支持 `animate()`。一次 `to()` 是组合动画,同一步里可以同时移动、缩放和改变透明度;多个 `to()` / `then()` 会按顺序播放:
441
+ `addGift()` 返回的 `GiftHandle` 支持 `animate()`。一次 `to()` 是组合动画,同一步里可以同时移动、缩放、改变透明度和旋转;多个 `to()` / `then()` 会按顺序播放。`rotationTo` 直接按数值线性插值,不做最短角度归一化,因此可以明确表达多圈旋转:
410
442
 
411
443
  ```ts
412
444
  const gift = await stage.addGift({
@@ -423,8 +455,9 @@ await gift
423
455
  .animate()
424
456
  .to({
425
457
  flyTo: { x: 200, y: 240 },
426
- scaleTo: 0.8,
427
- opacity: 0.6,
458
+ scaleTo: 0.8,
459
+ opacity: 0.6,
460
+ rotationTo: Math.PI * 4,
428
461
  duration: 500,
429
462
  })
430
463
  .then({
@@ -442,7 +475,27 @@ await gift
442
475
  console.log('chain complete', g.id);
443
476
  })
444
477
  .start();
445
- ```
478
+ ```
479
+
480
+ ### 绕外部中心旋转
481
+
482
+ 下面的礼物初始位于舞台中心右侧 200px,旋转中心通过框外坐标指回舞台中心:
483
+
484
+ ```ts
485
+ const orbitGift = await stage.addGift({
486
+ type: 'image',
487
+ source: 'https://example.com/gift.png',
488
+ x: container.clientWidth / 2 + 150,
489
+ y: '50%',
490
+ width: 100,
491
+ height: 100,
492
+ transformOrigin: { x: -150, y: '50%' },
493
+ });
494
+
495
+ await orbitGift
496
+ .animate({ rotationTo: Math.PI * 2, duration: 1200 })
497
+ .start();
498
+ ```
446
499
 
447
500
  也可以传入单步或数组:
448
501
 
@@ -0,0 +1,19 @@
1
+ import type { GiftSlot, GiftHandle } from '../types';
2
+ export declare class AudioManager {
3
+ private ctx;
4
+ private decodedCache;
5
+ private activeSources;
6
+ private globalMute;
7
+ private gainNode;
8
+ private ensureContext;
9
+ decodeAudio(key: string, data: ArrayBuffer): Promise<void>;
10
+ /**
11
+ * Frame-synced playback: start audio on startFrame, stop on endFrame.
12
+ * Matches SVGAPlayer-Web-master Canvas/renderer.js playAudio() pattern.
13
+ */
14
+ tickFrame(slot: GiftSlot, currentFrame: number, _handle: GiftHandle): void;
15
+ private startAudio;
16
+ stopAllForGift(giftId: string): void;
17
+ setMute(mute: boolean): void;
18
+ destroy(): void;
19
+ }
@@ -0,0 +1,117 @@
1
+ import type { GiftStageOptions, AddGiftOptions, GiftHandle, BackendType } from '../types';
2
+ export declare function resolveResourcePipelineConcurrency(explicit: number | undefined, hardwareConcurrency?: number | undefined): number;
3
+ export declare class GiftStage {
4
+ private static readonly SVGA_ATLAS_MAX_SIZE;
5
+ private static readonly SVGA_ATLAS_PADDING;
6
+ private static readonly DEFAULT_SHARED_VIDEO_PLAYBACK_WINDOW_MS;
7
+ private static readonly DEFAULT_RESOURCE_CACHE_MEMORY_BYTES;
8
+ private static readonly DEFAULT_RESOURCE_CACHE_PERSISTENT_BYTES;
9
+ private readonly sharedSVGAEntities;
10
+ private readonly pendingSharedSVGAEntities;
11
+ private readonly sharedVideoSources;
12
+ private readonly sharedHtmlVideos;
13
+ private readonly sharedVideoTextures;
14
+ private readonly pendingSharedHtmlVideos;
15
+ private readonly sharedImageTextures;
16
+ private readonly pendingSharedImageTextures;
17
+ private readonly sharedWebCodecsPlaybacks;
18
+ private readonly pendingSharedWebCodecsPlaybacks;
19
+ private readonly sharedWebCodecsSurfaces;
20
+ private readonly completedPlaybackSlots;
21
+ private readonly playbackCompleteWaiters;
22
+ private canvas;
23
+ private backend;
24
+ private manager;
25
+ private options;
26
+ /** Resolves when WebGPU/WebGL backend + WASM are ready; safe to `await` before `addGift`. */
27
+ readonly ready: Promise<void>;
28
+ private destroyed;
29
+ private resizeObserver;
30
+ private nextRenderOrder;
31
+ private nextSharedWebCodecsPlaybackId;
32
+ private nextSharedWebCodecsSurfaceId;
33
+ private readonly onWindowLayout;
34
+ constructor(options: GiftStageOptions);
35
+ private backingStoreDpr;
36
+ private syncCanvasSize;
37
+ private resourcePipelineConcurrency;
38
+ private init;
39
+ private svgaParseOptions;
40
+ private svgaAtlasFrameBudgetMs;
41
+ private svgaClipFrameBudgetMs;
42
+ private isSlotLoadActive;
43
+ private assertSlotLoadActive;
44
+ private isLoadCanceled;
45
+ private currentCanvasCssSize;
46
+ private resolveGiftIntrinsicSize;
47
+ private normalizeAddGiftOptions;
48
+ private resolveAnimationStepTarget;
49
+ private runGiftAnimationStep;
50
+ private createGiftAnimationChain;
51
+ addGift(options: AddGiftOptions): Promise<GiftHandle>;
52
+ private loadSVGA;
53
+ private acquireSharedSVGAEntity;
54
+ private waitForPendingSharedSVGAEntity;
55
+ private buildSharedSVGAEntity;
56
+ private releaseSharedSVGAEntity;
57
+ private createCachedSVGAAtlasTextures;
58
+ private deleteSVGAAtlasTextures;
59
+ private deleteSVGAAtlasResultTextures;
60
+ private finishLoadSVGA;
61
+ private svgaAtlasMaxSize;
62
+ private buildSVGAAtlasIfNeeded;
63
+ private svgaAtlasMaxPages;
64
+ private shouldTryWebCodecs;
65
+ /** DevTools: filter by `[GiftStage]` to see WebCodecs vs &lt;video&gt; per gift. */
66
+ private logPlaybackBackend;
67
+ private tryWebCodecsPlayback;
68
+ private acquireWebCodecsPlayback;
69
+ private acquireSharedWebCodecsPlayback;
70
+ private createPendingSharedWebCodecsPlayback;
71
+ private waitForPendingSharedWebCodecsPlayback;
72
+ private releaseSharedWebCodecsPlayback;
73
+ private attachWebCodecsPlayback;
74
+ private releaseAttachedWebCodecsPlayback;
75
+ private acquireSharedWebCodecsSurface;
76
+ private retainSharedWebCodecsSurface;
77
+ private createSharedWebCodecsSurface;
78
+ private releaseSharedWebCodecsSurface;
79
+ private attachSharedWebCodecsSurface;
80
+ private loadVAP;
81
+ private createVAPMixResourceTextures;
82
+ private loadAlphaVideo;
83
+ /**
84
+ * Some mobile WebKit builds ignore or partially apply `video.loop`; `ended` still fires once.
85
+ * Drive repeat / infinite playback explicitly from `ended` (aligns with SVGA `loop`: 0 = ∞, default 1).
86
+ */
87
+ private bindVideoLoopOnEnded;
88
+ private createVideo;
89
+ private waitVideoMetadata;
90
+ private probeVideoNaturalSize;
91
+ private acquireVideoPlaybackSource;
92
+ private normalizeSharedVideoSourceKey;
93
+ private fetchSharedVideoSource;
94
+ private releaseSharedVideoSource;
95
+ private tryAcquireSharedHtmlVideo;
96
+ private acquireSharedVideoTexture;
97
+ private acquireImageTexture;
98
+ private releaseSharedImageTexture;
99
+ private releaseSharedVideoTexture;
100
+ private releaseSharedHtmlVideo;
101
+ /**
102
+ * After `play()`, wait until at least one frame is presented so WebGPU
103
+ * `copyExternalImageToTexture(<video>)` can import (avoids “no back resource” / early return).
104
+ */
105
+ private waitForFirstVideoFrame;
106
+ private waitVideoReady;
107
+ removeGift(id: string): void;
108
+ removeAll(): void;
109
+ pause(): void;
110
+ resume(): void;
111
+ get activeCount(): number;
112
+ get backendType(): BackendType;
113
+ destroy(): void;
114
+ private markGiftPlaybackComplete;
115
+ private resolveGiftPlaybackWaiters;
116
+ private waitForGiftPlaybackComplete;
117
+ }
@@ -0,0 +1,4 @@
1
+ export { GiftStage } from './gift-stage';
2
+ export { RenderManager } from './render-manager';
3
+ export { AudioManager } from './audio-manager';
4
+ export { PostAnimator } from './post-animator';
@@ -0,0 +1,43 @@
1
+ import type { GiftSlot } from '../types';
2
+ export interface PostAnimationTarget {
3
+ targetX: number;
4
+ targetY: number;
5
+ targetScale: number;
6
+ targetOpacity: number;
7
+ targetRotation?: number;
8
+ duration: number;
9
+ easing: (t: number) => number;
10
+ onComplete?: () => void;
11
+ }
12
+ export declare class PostAnimator {
13
+ private animations;
14
+ private completedScratch;
15
+ private individuallyPaused;
16
+ private globallyPaused;
17
+ startFly(slot: GiftSlot, targetX: number, targetY: number, targetScale: number, duration: number, easing: (t: number) => number, onComplete?: () => void, targetOpacity?: number, targetRotation?: number): void;
18
+ startTween(slot: GiftSlot, targetX: number, targetY: number, targetScale: number, duration: number, easing: (t: number) => number, onComplete?: () => void, onCancel?: () => void, targetOpacity?: number, targetRotation?: number): void;
19
+ startTweenPromise(slot: GiftSlot, targetX: number, targetY: number, targetScale: number, duration: number, easing: (t: number) => number, onComplete?: () => void, targetOpacity?: number, targetRotation?: number): Promise<void>;
20
+ startSequence(slot: GiftSlot, targets: PostAnimationTarget[]): Promise<void>;
21
+ update(now: number): void;
22
+ /**
23
+ * Get the current animated position/scale for a gift.
24
+ * Returns null if no active animation.
25
+ */
26
+ getAnimatedState(giftId: string, now: number): {
27
+ x: number;
28
+ y: number;
29
+ scale: number;
30
+ opacity: number;
31
+ rotation: number;
32
+ } | null;
33
+ applyAnimatedState(slot: GiftSlot, now: number): boolean;
34
+ hasAnimation(giftId: string): boolean;
35
+ hasActiveAnimations(): boolean;
36
+ pauseForGift(giftId: string, now?: number): void;
37
+ resumeForGift(giftId: string, now?: number): void;
38
+ pauseAll(now?: number): void;
39
+ resumeAll(now?: number): void;
40
+ cancelForGift(giftId: string): void;
41
+ releaseGift(giftId: string): void;
42
+ destroy(): void;
43
+ }
@@ -0,0 +1,99 @@
1
+ import type { GPUBackend, GiftSlot, GiftHandle, GPUTextureHandle } from '../types';
2
+ import { type SVGAClipMeshMap } from '../renderers/svga-batch-renderer';
3
+ import { PostAnimator } from './post-animator';
4
+ import { AudioManager } from './audio-manager';
5
+ import { type WebCodecsAtlasRect } from './webcodecs-video-atlas';
6
+ export declare class RenderManager {
7
+ private backend;
8
+ private svgaBatch;
9
+ private vapRenderer;
10
+ private alphaVideoRenderer;
11
+ private imageRenderer;
12
+ readonly postAnimator: PostAnimator;
13
+ readonly audioManager: AudioManager;
14
+ private slots;
15
+ private handles;
16
+ private rafId;
17
+ private lastTime;
18
+ private running;
19
+ private forceNextDraw;
20
+ private readonly onAnimationFrame;
21
+ private readonly finishedSlotIdsScratch;
22
+ private readonly videoRunMapScratch;
23
+ /** Shared WebCodecs RGBA atlas (optional); multiple slots may reference `handle` with distinct `wcAtlasRect`. */
24
+ private wcAtlas;
25
+ /** One-shot logs for WebCodecs clock debugging (filter console by `[GiftStage:WC]`). */
26
+ private readonly wcDbgTickOnce;
27
+ private readonly advancedSharedWebCodecsScratch;
28
+ private readonly activeSharedWebCodecsScratch;
29
+ constructor(backend: GPUBackend);
30
+ addSlot(slot: GiftSlot, handle: GiftHandle): void;
31
+ releaseSlotResources(slot: GiftSlot): void;
32
+ removeSlot(id: string): void;
33
+ getSlot(id: string): GiftSlot | undefined;
34
+ get activeCount(): number;
35
+ getSlotIds(): string[];
36
+ /**
37
+ * Reserve a sub-rectangle in the shared WebCodecs atlas (when `webCodecsVideoAtlas` is configured).
38
+ * Returns null if disabled, full, or size mismatch while the atlas is still in use.
39
+ */
40
+ tryAllocateWebCodecsAtlas(slotId: string, frameW: number, frameH: number, cfg: {
41
+ width: number;
42
+ height: number;
43
+ } | undefined): {
44
+ handle: GPUTextureHandle;
45
+ rect: WebCodecsAtlasRect;
46
+ } | null;
47
+ releaseWebCodecsAtlasAllocation(slotId: string): void;
48
+ /** Re-apply CSS-space options when canvas backing store or layout changes (skips active fly animations). */
49
+ syncSlotLayoutFromOptions(): void;
50
+ private start;
51
+ private stop;
52
+ private clearFrameOnce;
53
+ pause(): void;
54
+ resume(): void;
55
+ prewarmSVGAClipMeshes(entity: NonNullable<GiftSlot['videoEntity']>, options?: {
56
+ frameBudgetMs?: number;
57
+ workerTimeSliceMs?: number;
58
+ useWorker?: boolean;
59
+ checkActive?: () => void;
60
+ signal?: AbortSignal;
61
+ }): Promise<number>;
62
+ hydrateSVGAClipMeshes(meshes: SVGAClipMeshMap | null | undefined): void;
63
+ snapshotSVGAClipMeshes(entity: NonNullable<GiftSlot['videoEntity']>): SVGAClipMeshMap;
64
+ private renderLoop;
65
+ /**
66
+ * Fixed-step frame advancement matching Unity SvgaLitePlayer.Update()
67
+ */
68
+ /**
69
+ * Advance WebCodecs playback clock (mirrors HTMLVideoElement time + `ended` loop semantics).
70
+ */
71
+ private tickSharedWebCodecs;
72
+ private tickWebCodecs;
73
+ private wcDbgLogTickOnce;
74
+ /** Same completion path as `GiftStage.bindVideoLoopOnEnded` for &lt;video&gt;. */
75
+ private onWebCodecsPlaybackComplete;
76
+ private tickSVGA;
77
+ private onGiftPlayComplete;
78
+ private tickImage;
79
+ private removeFinished;
80
+ /** Reused scratch to avoid a per-frame array allocation; consumed synchronously by getRenderLayers. */
81
+ private orderedSlotsScratch;
82
+ private getRenderOrderedSlots;
83
+ private getRenderLayers;
84
+ /**
85
+ * Multiple copies of the same gift are often added together before each copy
86
+ * flies to a different target. While their visual state is exactly identical,
87
+ * drawing every copy only increases overdraw and changes translucent pixels.
88
+ * Keep the top-most copy until a transform or frame diverges.
89
+ */
90
+ private collapseCoincidentVisualDuplicates;
91
+ private getCoincidentVisualKey;
92
+ private getSVGASlotTextureKey;
93
+ private drawLayer;
94
+ private drawSlotRun;
95
+ /** Batch keys are cached on the slot and only rebuilt when their inputs change. */
96
+ private getBatchKey;
97
+ private deleteSVGAAtlasTextures;
98
+ destroy(): void;
99
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Row-packs decoded WebCodecs frames into one RGBA atlas to share a single texture bind across many gifts.
3
+ * Falls back to per-gift textures when allocation fails (returns null).
4
+ */
5
+ export interface WebCodecsAtlasRect {
6
+ x: number;
7
+ y: number;
8
+ w: number;
9
+ h: number;
10
+ }
11
+ export declare class WebCodecsVideoAtlasAllocator {
12
+ private readonly atlasW;
13
+ private readonly atlasH;
14
+ /** Current row: y offset and row height. */
15
+ private rowY;
16
+ private rowH;
17
+ private rowX;
18
+ private readonly slots;
19
+ constructor(atlasW: number, atlasH: number);
20
+ get size(): {
21
+ w: number;
22
+ h: number;
23
+ };
24
+ /**
25
+ * Reserve a `w×h` rectangle (texels). Returns null if it does not fit.
26
+ */
27
+ tryAllocate(slotId: string, w: number, h: number): WebCodecsAtlasRect | null;
28
+ release(slotId: string): void;
29
+ getRect(slotId: string): WebCodecsAtlasRect | undefined;
30
+ }