@editframe/elements 0.37.3-beta → 0.38.1
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/EF_FRAMEGEN.js +17 -14
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_RENDERING.js.map +1 -1
- package/dist/canvas/EFCanvas.d.ts +9 -2
- package/dist/canvas/EFCanvas.js +14 -4
- package/dist/canvas/EFCanvas.js.map +1 -1
- package/dist/canvas/EFCanvasItem.d.ts +2 -2
- package/dist/canvas/overlays/SelectionOverlay.d.ts +10 -2
- package/dist/canvas/overlays/SelectionOverlay.js +5 -12
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
- package/dist/canvas/overlays/overlayState.js.map +1 -1
- package/dist/canvas/selection/SelectionController.js.map +1 -1
- package/dist/elements/EFAudio.d.ts +1 -11
- package/dist/elements/EFAudio.js +2 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +5 -9
- package/dist/elements/EFCaptions.js +34 -11
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +10 -8
- package/dist/elements/EFImage.js +117 -32
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +2 -2
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +15 -92
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js +10 -11
- package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
- package/dist/elements/EFMedia/{AssetIdMediaEngine.js → FileMediaEngine.js} +44 -24
- package/dist/elements/EFMedia/FileMediaEngine.js.map +1 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +14 -13
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +12 -7
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/shared/timeoutUtils.js +44 -0
- package/dist/elements/EFMedia/shared/timeoutUtils.js.map +1 -0
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +4 -4
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +14 -8
- package/dist/elements/EFMedia.js +52 -19
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +2 -2
- package/dist/elements/EFPanZoom.js +1 -1
- package/dist/elements/EFPanZoom.js.map +1 -1
- package/dist/elements/EFSourceMixin.js +16 -8
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +5 -8
- package/dist/elements/EFSurface.js +4 -43
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +33 -8
- package/dist/elements/EFTemporal.js +92 -40
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +3 -0
- package/dist/elements/EFText.js +54 -21
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.js +8 -4
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +26 -43
- package/dist/elements/EFTimegroup.js +295 -314
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +44 -42
- package/dist/elements/EFVideo.js +259 -172
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +3 -8
- package/dist/elements/EFWaveform.js +18 -13
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.js.map +1 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/TargetController.d.ts +0 -3
- package/dist/elements/TargetController.js +12 -35
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/cloneFactoryRegistry.d.ts +14 -0
- package/dist/elements/cloneFactoryRegistry.js +15 -0
- package/dist/elements/cloneFactoryRegistry.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +8 -6
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/setupTemporalHierarchy.js +62 -0
- package/dist/elements/setupTemporalHierarchy.js.map +1 -0
- package/dist/elements/updateAnimations.js +62 -87
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +3 -2
- package/dist/getRenderInfo.js +20 -4
- package/dist/getRenderInfo.js.map +1 -1
- package/dist/gui/ContextMixin.js +68 -12
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +1 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +2 -2
- package/dist/gui/EFActiveRootTemporal.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +2 -2
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +2 -2
- package/dist/gui/EFDial.js +12 -9
- package/dist/gui/EFDial.js.map +1 -1
- package/dist/gui/EFFilmstrip.d.ts +2 -0
- package/dist/gui/EFFilmstrip.js +18 -10
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +28 -4
- package/dist/gui/EFFitScale.js +88 -26
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +2 -2
- package/dist/gui/EFFocusOverlay.js +3 -3
- package/dist/gui/EFFocusOverlay.js.map +1 -1
- package/dist/gui/EFOverlayItem.d.ts +2 -2
- package/dist/gui/EFOverlayLayer.d.ts +2 -2
- package/dist/gui/EFPause.d.ts +2 -2
- package/dist/gui/EFPause.js +1 -1
- package/dist/gui/EFPlay.d.ts +2 -2
- package/dist/gui/EFPlay.js +1 -1
- package/dist/gui/EFPreview.js +1 -1
- package/dist/gui/EFResizableBox.d.ts +2 -2
- package/dist/gui/EFResizableBox.js +5 -5
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +2 -2
- package/dist/gui/EFScrubber.js +8 -13
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +6 -2
- package/dist/gui/EFTimeDisplay.js +25 -7
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +2 -2
- package/dist/gui/EFTimelineRuler.js +3 -3
- package/dist/gui/EFTimelineRuler.js.map +1 -1
- package/dist/gui/EFToggleLoop.d.ts +2 -2
- package/dist/gui/EFToggleLoop.js +1 -1
- package/dist/gui/EFTogglePlay.d.ts +2 -2
- package/dist/gui/EFTogglePlay.js +1 -1
- package/dist/gui/EFTransformHandles.d.ts +2 -2
- package/dist/gui/EFTransformHandles.js +6 -6
- package/dist/gui/EFTransformHandles.js.map +1 -1
- package/dist/gui/EFWorkbench.d.ts +40 -36
- package/dist/gui/EFWorkbench.js +436 -822
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.js.map +1 -1
- package/dist/gui/PlaybackController.d.ts +3 -8
- package/dist/gui/PlaybackController.js +59 -56
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/TargetOrContextMixin.js +43 -6
- package/dist/gui/TargetOrContextMixin.js.map +1 -1
- package/dist/gui/ef-theme.css +136 -0
- package/dist/gui/hierarchy/EFHierarchy.d.ts +2 -2
- package/dist/gui/hierarchy/EFHierarchy.js +14 -24
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
- package/dist/gui/hierarchy/EFHierarchyItem.js +22 -10
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
- package/dist/gui/icons.js.map +1 -1
- package/dist/gui/previewSettingsContext.d.ts +18 -0
- package/dist/gui/previewSettingsContext.js.map +1 -1
- package/dist/gui/theme.js +34 -0
- package/dist/gui/theme.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +2 -2
- package/dist/gui/timeline/EFTimeline.js +70 -52
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/EFTimelineRow.d.ts +5 -3
- package/dist/gui/timeline/EFTimelineRow.js +55 -32
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
- package/dist/gui/timeline/TrimHandles.d.ts +23 -9
- package/dist/gui/timeline/TrimHandles.js +224 -51
- package/dist/gui/timeline/TrimHandles.js.map +1 -1
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
- package/dist/gui/timeline/timelineEditingContext.d.ts +34 -0
- package/dist/gui/timeline/timelineEditingContext.js +24 -0
- package/dist/gui/timeline/timelineEditingContext.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -1
- package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +2 -3
- package/dist/gui/timeline/tracks/CaptionsTrack.js +17 -75
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +52 -0
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js +596 -0
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TextTrack.d.ts +3 -2
- package/dist/gui/timeline/tracks/TextTrack.js +17 -43
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +3 -4
- package/dist/gui/timeline/tracks/TimegroupTrack.js +33 -23
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TrackItem.d.ts +7 -9
- package/dist/gui/timeline/tracks/TrackItem.js +18 -17
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
- package/dist/gui/timeline/tracks/VideoTrack.d.ts +3 -3
- package/dist/gui/timeline/tracks/VideoTrack.js +11 -14
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
- package/dist/gui/tree/EFTree.d.ts +2 -2
- package/dist/gui/tree/EFTree.js +8 -14
- package/dist/gui/tree/EFTree.js.map +1 -1
- package/dist/gui/tree/EFTreeItem.d.ts +2 -2
- package/dist/gui/tree/EFTreeItem.js +3 -3
- package/dist/gui/tree/EFTreeItem.js.map +1 -1
- package/dist/gui/tree/treeContext.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/preview/AdaptiveResolutionTracker.js +3 -3
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
- package/dist/preview/FrameController.d.ts +2 -17
- package/dist/preview/FrameController.js +40 -63
- package/dist/preview/FrameController.js.map +1 -1
- package/dist/preview/QualityUpgradeScheduler.d.ts +76 -0
- package/dist/preview/QualityUpgradeScheduler.js +158 -0
- package/dist/preview/QualityUpgradeScheduler.js.map +1 -0
- package/dist/preview/RenderContext.d.ts +119 -1
- package/dist/preview/RenderContext.js +21 -3
- package/dist/preview/RenderContext.js.map +1 -1
- package/dist/preview/RenderProfiler.js.map +1 -1
- package/dist/preview/RenderStats.js +85 -0
- package/dist/preview/RenderStats.js.map +1 -0
- package/dist/preview/encoding/canvasEncoder.js +2 -52
- package/dist/preview/encoding/canvasEncoder.js.map +1 -1
- package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
- package/dist/preview/encoding/workerEncoder.js.map +1 -1
- package/dist/preview/logger.js.map +1 -1
- package/dist/preview/previewSettings.d.ts +34 -0
- package/dist/preview/previewSettings.js +29 -17
- package/dist/preview/previewSettings.js.map +1 -1
- package/dist/preview/previewTypes.js +4 -4
- package/dist/preview/previewTypes.js.map +1 -1
- package/dist/preview/renderElementToCanvas.d.ts +44 -0
- package/dist/preview/renderElementToCanvas.js +72 -0
- package/dist/preview/renderElementToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +134 -32
- package/dist/preview/renderTimegroupToCanvas.js +321 -146
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.types.d.ts +51 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +20 -35
- package/dist/preview/renderTimegroupToVideo.js +94 -106
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/renderTimegroupToVideo.types.d.ts +42 -0
- package/dist/preview/renderVideoToVideo.js +286 -0
- package/dist/preview/renderVideoToVideo.js.map +1 -0
- package/dist/preview/renderers.d.ts +56 -0
- package/dist/preview/renderers.js +13 -1
- package/dist/preview/renderers.js.map +1 -1
- package/dist/preview/rendering/ScaleConfig.js +74 -0
- package/dist/preview/rendering/ScaleConfig.js.map +1 -0
- package/dist/preview/rendering/inlineImages.d.ts +13 -0
- package/dist/preview/rendering/inlineImages.js +7 -44
- package/dist/preview/rendering/inlineImages.js.map +1 -1
- package/dist/preview/rendering/loadImage.d.ts +8 -0
- package/dist/preview/rendering/loadImage.js +22 -0
- package/dist/preview/rendering/loadImage.js.map +1 -0
- package/dist/preview/rendering/renderToImageNative.js +3 -3
- package/dist/preview/rendering/renderToImageNative.js.map +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
- package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
- package/dist/preview/statsTrackingStrategy.js +1 -101
- package/dist/preview/statsTrackingStrategy.js.map +1 -1
- package/dist/preview/workers/WorkerPool.js +0 -1
- package/dist/preview/workers/WorkerPool.js.map +1 -1
- package/dist/preview/workers/encoderWorkerInline.js +21 -54
- package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
- package/dist/render/EFRenderAPI.d.ts +2 -1
- package/dist/render/EFRenderAPI.js +12 -36
- package/dist/render/EFRenderAPI.js.map +1 -1
- package/dist/render/getRenderData.js +4 -4
- package/dist/render/getRenderData.js.map +1 -1
- package/dist/style.css +114 -163
- package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
- package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
- package/dist/transcoding/types/index.d.ts +1 -1
- package/dist/transcoding/utils/UrlGenerator.js +10 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -0
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +23 -1
- package/dist/utils/frameTime.js.map +1 -1
- package/package.json +45 -8
- package/scripts/build-css.js +8 -1
- package/test/setup.ts +0 -1
- package/test/useAssetMSW.ts +50 -0
- package/test/visualRegressionUtils.ts +23 -9
- package/tsdown.config.ts +6 -1
- package/dist/_virtual/rolldown_runtime.js +0 -27
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
- package/dist/elements/EFThumbnailStrip.d.ts +0 -167
- package/dist/elements/EFThumbnailStrip.js +0 -731
- package/dist/elements/EFThumbnailStrip.js.map +0 -1
- package/dist/elements/SessionThumbnailCache.js +0 -154
- package/dist/elements/SessionThumbnailCache.js.map +0 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
- package/dist/node_modules/react/cjs/react.development.js +0 -1521
- package/dist/node_modules/react/cjs/react.development.js.map +0 -1
- package/dist/node_modules/react/index.js +0 -13
- package/dist/node_modules/react/index.js.map +0 -1
- package/dist/node_modules/react/jsx-runtime.js +0 -13
- package/dist/node_modules/react/jsx-runtime.js.map +0 -1
- package/dist/preview/encoding/types.d.ts +0 -1
- package/dist/preview/renderTimegroupPreview.js +0 -686
- package/dist/preview/renderTimegroupPreview.js.map +0 -1
- package/dist/preview/rendering/renderToImage.d.ts +0 -2
- package/dist/preview/rendering/renderToImage.js +0 -95
- package/dist/preview/rendering/renderToImage.js.map +0 -1
- package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
- package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
- package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
- package/dist/preview/rendering/svgSerializer.js +0 -43
- package/dist/preview/rendering/svgSerializer.js.map +0 -1
- package/dist/preview/rendering/types.d.ts +0 -2
- package/dist/preview/thumbnailCacheSettings.js +0 -52
- package/dist/preview/thumbnailCacheSettings.js.map +0 -1
- package/dist/sandbox/PlaybackControls.d.ts +0 -1
- package/dist/sandbox/PlaybackControls.js +0 -10
- package/dist/sandbox/PlaybackControls.js.map +0 -1
- package/dist/sandbox/ScenarioRunner.d.ts +0 -1
- package/dist/sandbox/ScenarioRunner.js +0 -1
- package/dist/sandbox/defineSandbox.d.ts +0 -1
- package/dist/sandbox/index.d.ts +0 -3
- package/dist/sandbox/index.js +0 -2
- package/test/EFVideo.framegen.browsertest.ts +0 -80
- package/test/thumbnail-performance-test.html +0 -116
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { FrameController } from "./FrameController.js";
|
|
2
|
+
import { updateAnimations } from "../elements/updateAnimations.js";
|
|
2
3
|
import { logger } from "./logger.js";
|
|
3
|
-
import { DEFAULT_BLOCKING_TIMEOUT_MS, DEFAULT_HEIGHT, DEFAULT_THUMBNAIL_SCALE, DEFAULT_WIDTH, createPreviewContainer, isVisibleAtTime } from "./previewTypes.js";
|
|
4
|
-
import { buildCloneStructure, collectDocumentStyles, overrideRootCloneStyles, removeHiddenNodesForSerialization, restoreHiddenNodes } from "./renderTimegroupPreview.js";
|
|
5
|
-
import { getEffectiveRenderMode } from "./renderers.js";
|
|
6
4
|
import { RenderContext } from "./RenderContext.js";
|
|
5
|
+
import { DEFAULT_BLOCKING_TIMEOUT_MS, DEFAULT_CAPTURE_SCALE, DEFAULT_HEIGHT, DEFAULT_WIDTH, isVisibleAtTime } from "./previewTypes.js";
|
|
6
|
+
import { captureTimelineToDataUri } from "./rendering/serializeTimelineDirect.js";
|
|
7
|
+
import { getRenderMode, isNativeCanvasApiAvailable } from "./previewSettings.js";
|
|
8
|
+
import { getEffectiveRenderMode, isCanvas, isImage } from "./renderers.js";
|
|
7
9
|
import { defaultProfiler } from "./RenderProfiler.js";
|
|
10
|
+
import { loadImageFromDataUri } from "./rendering/loadImage.js";
|
|
8
11
|
import { createDprCanvas, renderToImageNative } from "./rendering/renderToImageNative.js";
|
|
9
|
-
import { clearInlineImageCache } from "./rendering/inlineImages.js";
|
|
10
|
-
import { loadImageFromDataUri, renderToImage } from "./rendering/renderToImage.js";
|
|
12
|
+
import { clearInlineImageCache, getInlineImageCacheSize } from "./rendering/inlineImages.js";
|
|
11
13
|
|
|
12
14
|
//#region src/preview/renderTimegroupToCanvas.ts
|
|
13
15
|
/** Number of rows to sample when checking canvas content */
|
|
@@ -40,6 +42,20 @@ const renderState = {
|
|
|
40
42
|
}
|
|
41
43
|
};
|
|
42
44
|
/**
|
|
45
|
+
* Get the current render state for testing and debugging.
|
|
46
|
+
* @returns The module-level render state object
|
|
47
|
+
*/
|
|
48
|
+
function getRenderState() {
|
|
49
|
+
return renderState;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get cache metrics for monitoring performance.
|
|
53
|
+
* @returns Object with cache hit/miss/eviction counts
|
|
54
|
+
*/
|
|
55
|
+
function getCacheMetrics() {
|
|
56
|
+
return { ...renderState.metrics };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
43
59
|
* Reset cache metrics to zero.
|
|
44
60
|
*/
|
|
45
61
|
function resetCacheMetrics() {
|
|
@@ -57,37 +73,34 @@ function resetRenderState() {
|
|
|
57
73
|
resetCacheMetrics();
|
|
58
74
|
}
|
|
59
75
|
/**
|
|
60
|
-
*
|
|
76
|
+
* DEBUG: Capture a single thumbnail at the current time.
|
|
77
|
+
* Call from console: window.debugCaptureThumbnail()
|
|
61
78
|
*/
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
88
|
-
label.style.color = scaleColors[resolutionScale] || "#ffffff";
|
|
89
|
-
label.textContent = `Render: ${renderWidth}x${renderHeight} (${Math.round(resolutionScale * 100)}%)`;
|
|
90
|
-
}
|
|
79
|
+
if (typeof window !== "undefined") window.debugCaptureThumbnail = async function() {
|
|
80
|
+
const timegroup = document.querySelector("ef-timegroup");
|
|
81
|
+
if (!timegroup) {
|
|
82
|
+
console.error("No timegroup found");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const currentTime = timegroup.currentTimeMs ?? 0;
|
|
86
|
+
try {
|
|
87
|
+
const result = await captureTimegroupAtTime(timegroup, {
|
|
88
|
+
timeMs: currentTime,
|
|
89
|
+
scale: .25,
|
|
90
|
+
contentReadyMode: "blocking",
|
|
91
|
+
blockingTimeoutMs: 1e3
|
|
92
|
+
});
|
|
93
|
+
const img = document.createElement("img");
|
|
94
|
+
if (result instanceof HTMLCanvasElement) img.src = result.toDataURL();
|
|
95
|
+
else if (result instanceof HTMLImageElement) img.src = result.src;
|
|
96
|
+
img.style.cssText = "position:fixed;top:10px;right:10px;border:2px solid red;z-index:99999;";
|
|
97
|
+
document.body.appendChild(img);
|
|
98
|
+
return result;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error("[DEBUG] Capture failed:", err);
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
91
104
|
/**
|
|
92
105
|
* Wait for next animation frame (allows browser to complete layout)
|
|
93
106
|
*/
|
|
@@ -99,7 +112,7 @@ function waitForFrame() {
|
|
|
99
112
|
* Returns true if there's ANY non-transparent pixel.
|
|
100
113
|
*/
|
|
101
114
|
function canvasHasContent(canvas) {
|
|
102
|
-
const ctx = canvas.getContext("2d");
|
|
115
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
103
116
|
if (!ctx) return false;
|
|
104
117
|
try {
|
|
105
118
|
const width = canvas.width;
|
|
@@ -167,101 +180,192 @@ async function waitForVideoContent(timegroup, timeMs, maxWaitMs) {
|
|
|
167
180
|
/**
|
|
168
181
|
* Captures a frame from an already-seeked render clone.
|
|
169
182
|
* Used internally by captureBatch for efficiency (reuses one clone across all captures).
|
|
170
|
-
*
|
|
183
|
+
*
|
|
171
184
|
* @param renderClone - A render clone that has already been seeked to the target time
|
|
172
185
|
* @param renderContainer - The container holding the render clone (from createRenderClone)
|
|
173
186
|
* @param options - Capture options
|
|
174
|
-
* @returns Canvas with the rendered frame
|
|
187
|
+
* @returns Canvas or Image with the rendered frame (both are CanvasImageSource)
|
|
175
188
|
*/
|
|
176
|
-
async function captureFromClone(renderClone,
|
|
177
|
-
const { scale =
|
|
189
|
+
async function captureFromClone(renderClone, _renderContainer, options = {}) {
|
|
190
|
+
const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, originalTimegroup, timeMs: explicitTimeMs, canvasMode } = options;
|
|
191
|
+
const timeMs = explicitTimeMs ?? renderClone.currentTimeMs;
|
|
178
192
|
const sourceForDimensions = originalTimegroup ?? renderClone;
|
|
179
193
|
const width = sourceForDimensions.offsetWidth || DEFAULT_WIDTH;
|
|
180
194
|
const height = sourceForDimensions.offsetHeight || DEFAULT_HEIGHT;
|
|
181
|
-
const dpr = window.devicePixelRatio || 1;
|
|
182
|
-
const canvas = document.createElement("canvas");
|
|
183
|
-
canvas.width = Math.floor(width * scale * dpr);
|
|
184
|
-
canvas.height = Math.floor(height * scale * dpr);
|
|
185
|
-
canvas.style.width = `${Math.floor(width * scale)}px`;
|
|
186
|
-
canvas.style.height = `${Math.floor(height * scale)}px`;
|
|
187
|
-
const ctx = canvas.getContext("2d");
|
|
188
|
-
if (!ctx) throw new Error("Failed to get canvas 2d context");
|
|
189
|
-
const timeMs = renderClone.currentTimeMs;
|
|
190
195
|
if (contentReadyMode === "blocking") {
|
|
191
196
|
const result = await waitForVideoContent(renderClone, timeMs, blockingTimeoutMs);
|
|
192
197
|
if (!result.ready) throw new ContentNotReadyError(timeMs, blockingTimeoutMs, result.blankVideos);
|
|
193
198
|
}
|
|
199
|
+
const effectiveCanvasMode = (() => {
|
|
200
|
+
if (!canvasMode) return "foreignObject";
|
|
201
|
+
if (canvasMode === "native" && !isNativeCanvasApiAvailable()) {
|
|
202
|
+
logger.debug("[captureFromClone] Native canvas mode requested but not available, falling back to foreignObject");
|
|
203
|
+
return "foreignObject";
|
|
204
|
+
}
|
|
205
|
+
return canvasMode;
|
|
206
|
+
})();
|
|
194
207
|
const renderContext = new RenderContext();
|
|
195
208
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
width: ${width}px;
|
|
203
|
-
height: ${height}px;
|
|
204
|
-
pointer-events: none;
|
|
205
|
-
overflow: hidden;
|
|
206
|
-
`;
|
|
207
|
-
image = await renderToImageNative(renderContainer, width, height, { skipDprScaling: true });
|
|
209
|
+
if (effectiveCanvasMode === "native") {
|
|
210
|
+
const t0 = performance.now();
|
|
211
|
+
const canvas = await renderToImageNative(renderClone, width, height, { skipDprScaling: true });
|
|
212
|
+
const renderTime = performance.now() - t0;
|
|
213
|
+
logger.debug(`[captureFromClone] native render=${renderTime.toFixed(0)}ms (canvasScale=${scale})`);
|
|
214
|
+
return canvas;
|
|
208
215
|
} else {
|
|
209
216
|
const t0 = performance.now();
|
|
210
|
-
const
|
|
211
|
-
const buildTime = performance.now() - t0;
|
|
212
|
-
const bgSource = originalTimegroup ?? renderClone;
|
|
213
|
-
const previewContainer = createPreviewContainer({
|
|
214
|
-
width,
|
|
215
|
-
height,
|
|
216
|
-
background: getComputedStyle(bgSource).background || "#000"
|
|
217
|
-
});
|
|
218
|
-
const t1 = performance.now();
|
|
219
|
-
const styleEl = document.createElement("style");
|
|
220
|
-
styleEl.textContent = collectDocumentStyles();
|
|
221
|
-
const stylesTime = performance.now() - t1;
|
|
222
|
-
previewContainer.appendChild(styleEl);
|
|
223
|
-
previewContainer.appendChild(container$1);
|
|
224
|
-
overrideRootCloneStyles(syncState, true);
|
|
225
|
-
const t2 = performance.now();
|
|
226
|
-
image = await renderToImage(previewContainer, width, height, {
|
|
227
|
-
canvasScale: scale,
|
|
217
|
+
const dataUri = await captureTimelineToDataUri(renderClone, width, height, {
|
|
228
218
|
renderContext,
|
|
229
|
-
|
|
219
|
+
canvasScale: scale,
|
|
220
|
+
timeMs
|
|
230
221
|
});
|
|
231
|
-
const
|
|
232
|
-
|
|
222
|
+
const serializeTime = performance.now() - t0;
|
|
223
|
+
const t1 = performance.now();
|
|
224
|
+
const image = await loadImageFromDataUri(dataUri);
|
|
225
|
+
const loadTime = performance.now() - t1;
|
|
226
|
+
logger.debug(`[captureFromClone] foreignObject serialize=${serializeTime.toFixed(0)}ms, load=${loadTime.toFixed(0)}ms (canvasScale=${scale})`);
|
|
227
|
+
return image;
|
|
233
228
|
}
|
|
234
|
-
const srcWidth = image.width;
|
|
235
|
-
const srcHeight = image.height;
|
|
236
|
-
ctx.drawImage(image, 0, 0, srcWidth, srcHeight, 0, 0, canvas.width, canvas.height);
|
|
237
|
-
return canvas;
|
|
238
229
|
} finally {
|
|
239
230
|
renderContext.dispose();
|
|
240
231
|
}
|
|
241
232
|
}
|
|
242
233
|
/**
|
|
243
234
|
* Captures a single frame from a timegroup at a specific time.
|
|
244
|
-
*
|
|
235
|
+
*
|
|
245
236
|
* CLONE-TIMELINE ARCHITECTURE:
|
|
246
237
|
* Creates an independent render clone, seeks it to the target time, and captures.
|
|
247
238
|
* Prime-timeline is NEVER seeked - user can continue previewing/editing during capture.
|
|
248
|
-
*
|
|
239
|
+
*
|
|
249
240
|
* @param timegroup - The source timegroup
|
|
250
241
|
* @param options - Capture options including timeMs, scale, contentReadyMode
|
|
251
242
|
* @returns Canvas with the rendered frame
|
|
252
243
|
* @throws ContentNotReadyError if blocking mode times out waiting for video content
|
|
253
244
|
*/
|
|
254
245
|
async function captureTimegroupAtTime(timegroup, options) {
|
|
255
|
-
const { timeMs, scale =
|
|
246
|
+
const { timeMs, scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, canvasMode, skipClone = false } = options;
|
|
247
|
+
if (skipClone) {
|
|
248
|
+
const seekStart = performance.now();
|
|
249
|
+
await timegroup.seekForRender(timeMs);
|
|
250
|
+
const seekMs = performance.now() - seekStart;
|
|
251
|
+
const renderStart = performance.now();
|
|
252
|
+
const result = await captureFromClone(timegroup, timegroup.parentElement || document.body, {
|
|
253
|
+
scale,
|
|
254
|
+
contentReadyMode,
|
|
255
|
+
blockingTimeoutMs,
|
|
256
|
+
originalTimegroup: void 0,
|
|
257
|
+
canvasMode,
|
|
258
|
+
timeMs
|
|
259
|
+
});
|
|
260
|
+
const renderMs = performance.now() - renderStart;
|
|
261
|
+
if (typeof result === "object" && result !== null) result.__perfTiming = {
|
|
262
|
+
cloneMs: 0,
|
|
263
|
+
seekMs,
|
|
264
|
+
renderMs
|
|
265
|
+
};
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
const cloneStart = performance.now();
|
|
256
269
|
const { clone: renderClone, container: renderContainer, cleanup: cleanupRenderClone } = await timegroup.createRenderClone();
|
|
270
|
+
const cloneMs = performance.now() - cloneStart;
|
|
257
271
|
try {
|
|
272
|
+
const seekStart = performance.now();
|
|
258
273
|
await renderClone.seekForRender(timeMs);
|
|
259
|
-
|
|
274
|
+
const seekMs = performance.now() - seekStart;
|
|
275
|
+
const renderStart = performance.now();
|
|
276
|
+
const result = await captureFromClone(renderClone, renderContainer, {
|
|
260
277
|
scale,
|
|
261
278
|
contentReadyMode,
|
|
262
279
|
blockingTimeoutMs,
|
|
263
|
-
originalTimegroup: timegroup
|
|
280
|
+
originalTimegroup: timegroup,
|
|
281
|
+
canvasMode
|
|
264
282
|
});
|
|
283
|
+
const renderMs = performance.now() - renderStart;
|
|
284
|
+
if (typeof result === "object" && result !== null) result.__perfTiming = {
|
|
285
|
+
cloneMs,
|
|
286
|
+
seekMs,
|
|
287
|
+
renderMs
|
|
288
|
+
};
|
|
289
|
+
return result;
|
|
290
|
+
} finally {
|
|
291
|
+
cleanupRenderClone();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Generate thumbnails using an existing render clone and mutable queue.
|
|
296
|
+
* The queue can be modified while generation is in progress.
|
|
297
|
+
*
|
|
298
|
+
* @param renderClone - Pre-created render clone to use
|
|
299
|
+
* @param renderContainer - Container for the render clone
|
|
300
|
+
* @param queue - Mutable queue that provides timestamps
|
|
301
|
+
* @param options - Capture options (scale, contentReadyMode, etc.)
|
|
302
|
+
* @yields Objects with { timeMs, canvas } for each captured thumbnail
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* const queue = new MutableTimestampQueue();
|
|
307
|
+
* queue.reset([0, 100, 200]);
|
|
308
|
+
*
|
|
309
|
+
* for await (const { timeMs, canvas } of generateThumbnailsFromClone(clone, container, queue)) {
|
|
310
|
+
* cache.set(timeMs, canvas);
|
|
311
|
+
* // Queue can be modified here while generator continues
|
|
312
|
+
* }
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
async function* generateThumbnailsFromClone(renderClone, renderContainer, queue, options = {}) {
|
|
316
|
+
const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, signal } = options;
|
|
317
|
+
while (true) {
|
|
318
|
+
if (signal?.aborted) break;
|
|
319
|
+
const timeMs = queue.shift();
|
|
320
|
+
if (timeMs === void 0) break;
|
|
321
|
+
await renderClone.seekForRender(timeMs);
|
|
322
|
+
if (signal?.aborted) break;
|
|
323
|
+
yield {
|
|
324
|
+
timeMs,
|
|
325
|
+
canvas: await captureFromClone(renderClone, renderContainer, {
|
|
326
|
+
scale,
|
|
327
|
+
contentReadyMode,
|
|
328
|
+
blockingTimeoutMs,
|
|
329
|
+
timeMs
|
|
330
|
+
})
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Generate thumbnails for multiple timestamps efficiently using a single render clone.
|
|
336
|
+
* This avoids the overhead of creating/destroying a clone for each thumbnail.
|
|
337
|
+
*
|
|
338
|
+
* @param timegroup - The timegroup to capture
|
|
339
|
+
* @param timestamps - Array of timestamps to capture (in milliseconds)
|
|
340
|
+
* @param options - Capture options (scale, contentReadyMode, etc.)
|
|
341
|
+
* @param signal - Optional AbortSignal to cancel generation
|
|
342
|
+
* @yields Objects with { timeMs, canvas } for each captured thumbnail
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```ts
|
|
346
|
+
* for await (const { timeMs, canvas } of generateThumbnails(tg, [0, 100, 200])) {
|
|
347
|
+
* console.log(`Got thumbnail for ${timeMs}ms`);
|
|
348
|
+
* thumbnailCache.set(timeMs, canvas);
|
|
349
|
+
* }
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
async function* generateThumbnails(timegroup, timestamps, options = {}, signal) {
|
|
353
|
+
const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS } = options;
|
|
354
|
+
const { clone: renderClone, container: renderContainer, cleanup: cleanupRenderClone } = await timegroup.createRenderClone();
|
|
355
|
+
try {
|
|
356
|
+
for (const timeMs of timestamps) {
|
|
357
|
+
signal?.throwIfAborted();
|
|
358
|
+
await renderClone.seekForRender(timeMs);
|
|
359
|
+
yield {
|
|
360
|
+
timeMs,
|
|
361
|
+
canvas: await captureFromClone(renderClone, renderContainer, {
|
|
362
|
+
scale,
|
|
363
|
+
contentReadyMode,
|
|
364
|
+
blockingTimeoutMs,
|
|
365
|
+
originalTimegroup: timegroup
|
|
366
|
+
})
|
|
367
|
+
};
|
|
368
|
+
}
|
|
265
369
|
} finally {
|
|
266
370
|
cleanupRenderClone();
|
|
267
371
|
}
|
|
@@ -282,10 +386,10 @@ function toAbsoluteTime(timegroup, relativeTimeMs) {
|
|
|
282
386
|
}
|
|
283
387
|
/**
|
|
284
388
|
* Renders a timegroup preview to a canvas using SVG foreignObject.
|
|
285
|
-
*
|
|
389
|
+
*
|
|
286
390
|
* Captures the prime timeline's current visual state including DOM changes
|
|
287
391
|
* from frame tasks (SVG paths, canvas content, text updates, etc.).
|
|
288
|
-
*
|
|
392
|
+
*
|
|
289
393
|
* Optimized with:
|
|
290
394
|
* - Passive clone structure rebuilt each frame from prime's current state
|
|
291
395
|
* - Temporal bucketing for time-based culling
|
|
@@ -302,7 +406,7 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
302
406
|
let currentResolutionScale = options.resolutionScale ?? DEFAULT_RESOLUTION_SCALE;
|
|
303
407
|
const width = timegroup.offsetWidth || DEFAULT_WIDTH;
|
|
304
408
|
const height = timegroup.offsetHeight || DEFAULT_HEIGHT;
|
|
305
|
-
const dpr = window.devicePixelRatio || 1;
|
|
409
|
+
const dpr = (typeof window !== "undefined" ? window.devicePixelRatio : 1) || 1;
|
|
306
410
|
let renderWidth = Math.floor(width * currentResolutionScale);
|
|
307
411
|
let renderHeight = Math.floor(height * currentResolutionScale);
|
|
308
412
|
const canvas = createDprCanvas({
|
|
@@ -313,11 +417,7 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
313
417
|
fullHeight: height,
|
|
314
418
|
dpr
|
|
315
419
|
});
|
|
316
|
-
const wrapperContainer =
|
|
317
|
-
wrapperContainer.style.cssText = "position: relative; display: inline-block;";
|
|
318
|
-
const debugLabel = createDebugLabel();
|
|
319
|
-
wrapperContainer.appendChild(debugLabel);
|
|
320
|
-
wrapperContainer.appendChild(canvas);
|
|
420
|
+
const wrapperContainer = canvas;
|
|
321
421
|
const ctx = canvas.getContext("2d");
|
|
322
422
|
if (!ctx) throw new Error("Failed to get canvas 2d context");
|
|
323
423
|
let rendering = false;
|
|
@@ -325,16 +425,34 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
325
425
|
let disposed = false;
|
|
326
426
|
const renderContext = new RenderContext();
|
|
327
427
|
const frameController = new FrameController(timegroup);
|
|
328
|
-
const previewContainer = createPreviewContainer({
|
|
329
|
-
width: renderWidth,
|
|
330
|
-
height: renderHeight,
|
|
331
|
-
background: getComputedStyle(timegroup).background || "#000"
|
|
332
|
-
});
|
|
333
|
-
const styleEl = document.createElement("style");
|
|
334
|
-
styleEl.textContent = collectDocumentStyles();
|
|
335
|
-
previewContainer.appendChild(styleEl);
|
|
336
428
|
let hasLoggedScale = false;
|
|
337
429
|
let pendingResolutionScale = null;
|
|
430
|
+
const useNative = getRenderMode() === "native" && isNativeCanvasApiAvailable();
|
|
431
|
+
let captureCanvas = null;
|
|
432
|
+
let captureCtx = null;
|
|
433
|
+
let originalParent = null;
|
|
434
|
+
let originalNextSibling = null;
|
|
435
|
+
let savedClipPath = "";
|
|
436
|
+
let savedPointerEvents = "";
|
|
437
|
+
if (useNative) {
|
|
438
|
+
captureCanvas = document.createElement("canvas");
|
|
439
|
+
captureCanvas.setAttribute("layoutsubtree", "");
|
|
440
|
+
captureCanvas.layoutSubtree = true;
|
|
441
|
+
captureCanvas.width = renderWidth;
|
|
442
|
+
captureCanvas.height = renderHeight;
|
|
443
|
+
captureCanvas.style.cssText = `position:fixed;left:0;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;z-index:-9999;`;
|
|
444
|
+
originalParent = timegroup.parentNode;
|
|
445
|
+
originalNextSibling = timegroup.nextSibling;
|
|
446
|
+
savedClipPath = timegroup.style.clipPath;
|
|
447
|
+
savedPointerEvents = timegroup.style.pointerEvents;
|
|
448
|
+
timegroup.style.clipPath = "";
|
|
449
|
+
timegroup.style.pointerEvents = "";
|
|
450
|
+
captureCanvas.appendChild(timegroup);
|
|
451
|
+
document.body.appendChild(captureCanvas);
|
|
452
|
+
captureCtx = captureCanvas.getContext("2d");
|
|
453
|
+
captureCanvas.offsetHeight;
|
|
454
|
+
timegroup.offsetHeight;
|
|
455
|
+
}
|
|
338
456
|
/**
|
|
339
457
|
* Apply pending resolution scale changes.
|
|
340
458
|
* Called at the start of refresh() before rendering, so the old content
|
|
@@ -347,12 +465,10 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
347
465
|
currentResolutionScale = newScale;
|
|
348
466
|
renderWidth = Math.floor(width * currentResolutionScale);
|
|
349
467
|
renderHeight = Math.floor(height * currentResolutionScale);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
container.style.transformOrigin = "top left";
|
|
355
|
-
} else container.style.transform = "";
|
|
468
|
+
if (captureCanvas) {
|
|
469
|
+
captureCanvas.width = renderWidth;
|
|
470
|
+
captureCanvas.height = renderHeight;
|
|
471
|
+
}
|
|
356
472
|
};
|
|
357
473
|
/**
|
|
358
474
|
* Dynamically change resolution scale without rebuilding clone structure.
|
|
@@ -366,52 +482,104 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
366
482
|
lastTimeMs = -1;
|
|
367
483
|
};
|
|
368
484
|
const getResolutionScale = () => pendingResolutionScale ?? currentResolutionScale;
|
|
485
|
+
let frameCount = 0;
|
|
486
|
+
let totalFrameControllerMs = 0;
|
|
487
|
+
let totalCaptureMs = 0;
|
|
488
|
+
let totalCopyMs = 0;
|
|
489
|
+
let totalFrameMs = 0;
|
|
369
490
|
const refresh = async () => {
|
|
370
|
-
if (
|
|
491
|
+
if (disposed) return;
|
|
371
492
|
const sourceTimeMs = timegroup.currentTimeMs ?? 0;
|
|
372
493
|
const userTimeMs = timegroup.userTimeMs ?? 0;
|
|
373
494
|
if (Math.abs(sourceTimeMs - userTimeMs) > TIME_EPSILON_MS) return;
|
|
374
495
|
if (userTimeMs === lastTimeMs) return;
|
|
496
|
+
if (rendering) return;
|
|
375
497
|
lastTimeMs = userTimeMs;
|
|
376
498
|
rendering = true;
|
|
377
499
|
applyPendingResolutionChange();
|
|
378
500
|
if (!hasLoggedScale) {
|
|
379
501
|
hasLoggedScale = true;
|
|
380
|
-
const mode =
|
|
502
|
+
const mode = useNative ? "native" : "foreignObject";
|
|
381
503
|
logger.debug(`[renderTimegroupToCanvas] Resolution scale: ${currentResolutionScale} (${width}x${height} → ${renderWidth}x${renderHeight}), canvas buffer: ${canvas.width}x${canvas.height}, CSS size: ${canvas.style.width}x${canvas.style.height}, renderMode: ${mode}`);
|
|
382
504
|
}
|
|
383
505
|
try {
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
previewContainer.appendChild(container$1);
|
|
392
|
-
overrideRootCloneStyles(syncState);
|
|
393
|
-
const removedNodes = removeHiddenNodesForSerialization(syncState);
|
|
394
|
-
const t0 = performance.now();
|
|
395
|
-
const image = await renderToImage(previewContainer, renderWidth, renderHeight, {
|
|
396
|
-
canvasScale: currentResolutionScale,
|
|
397
|
-
renderContext,
|
|
398
|
-
sourceMap: syncState.canvasSourceMap
|
|
506
|
+
const tFrame = performance.now();
|
|
507
|
+
const tFC0 = performance.now();
|
|
508
|
+
await frameController.renderFrame(userTimeMs, {
|
|
509
|
+
waitForLitUpdate: false,
|
|
510
|
+
onAnimationsUpdate: (root) => {
|
|
511
|
+
updateAnimations(root);
|
|
512
|
+
}
|
|
399
513
|
});
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
514
|
+
const fcMs = performance.now() - tFC0;
|
|
515
|
+
const tCapture0 = performance.now();
|
|
516
|
+
if (useNative && captureCanvas && captureCtx) {
|
|
517
|
+
if (captureCanvas.width !== width || captureCanvas.height !== height) {
|
|
518
|
+
captureCtx.save();
|
|
519
|
+
captureCtx.scale(captureCanvas.width / width, captureCanvas.height / height);
|
|
520
|
+
captureCtx.drawElementImage(timegroup, 0, 0);
|
|
521
|
+
captureCtx.restore();
|
|
522
|
+
} else captureCtx.drawElementImage(timegroup, 0, 0);
|
|
523
|
+
const captureMs = performance.now() - tCapture0;
|
|
524
|
+
const tCopy0 = performance.now();
|
|
525
|
+
const targetWidth = Math.floor(renderWidth * scale * dpr);
|
|
526
|
+
const targetHeight = Math.floor(renderHeight * scale * dpr);
|
|
527
|
+
if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
|
|
528
|
+
canvas.width = targetWidth;
|
|
529
|
+
canvas.height = targetHeight;
|
|
530
|
+
} else ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
531
|
+
ctx.drawImage(captureCanvas, 0, 0, canvas.width, canvas.height);
|
|
532
|
+
const copyMs = performance.now() - tCopy0;
|
|
533
|
+
const frameMs = performance.now() - tFrame;
|
|
534
|
+
frameCount++;
|
|
535
|
+
totalFrameControllerMs += fcMs;
|
|
536
|
+
totalCaptureMs += captureMs;
|
|
537
|
+
totalCopyMs += copyMs;
|
|
538
|
+
totalFrameMs += frameMs;
|
|
539
|
+
defaultProfiler.incrementRenderCount();
|
|
540
|
+
if (defaultProfiler.shouldLogByFrameCount(60)) {
|
|
541
|
+
frameCount = 0;
|
|
542
|
+
totalFrameControllerMs = 0;
|
|
543
|
+
totalCaptureMs = 0;
|
|
544
|
+
totalCopyMs = 0;
|
|
545
|
+
totalFrameMs = 0;
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
const absoluteTimeMs = toAbsoluteTime(timegroup, userTimeMs);
|
|
549
|
+
const dataUri = await captureTimelineToDataUri(timegroup, width, height, {
|
|
550
|
+
renderContext,
|
|
551
|
+
canvasScale: currentResolutionScale,
|
|
552
|
+
timeMs: absoluteTimeMs
|
|
553
|
+
});
|
|
554
|
+
const captureMs = performance.now() - tCapture0;
|
|
555
|
+
const tCopy0 = performance.now();
|
|
556
|
+
const image = await loadImageFromDataUri(dataUri);
|
|
557
|
+
const copyMs = performance.now() - tCopy0;
|
|
558
|
+
const targetWidth = Math.floor(renderWidth * scale * dpr);
|
|
559
|
+
const targetHeight = Math.floor(renderHeight * scale * dpr);
|
|
560
|
+
if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
|
|
561
|
+
canvas.width = targetWidth;
|
|
562
|
+
canvas.height = targetHeight;
|
|
563
|
+
} else ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
564
|
+
ctx.save();
|
|
565
|
+
ctx.scale(dpr * scale, dpr * scale);
|
|
566
|
+
ctx.drawImage(image, 0, 0, renderWidth, renderHeight);
|
|
567
|
+
ctx.restore();
|
|
568
|
+
const frameMs = performance.now() - tFrame;
|
|
569
|
+
frameCount++;
|
|
570
|
+
totalFrameControllerMs += fcMs;
|
|
571
|
+
totalCaptureMs += captureMs;
|
|
572
|
+
totalCopyMs += copyMs;
|
|
573
|
+
totalFrameMs += frameMs;
|
|
574
|
+
defaultProfiler.incrementRenderCount();
|
|
575
|
+
if (defaultProfiler.shouldLogByFrameCount(60)) {
|
|
576
|
+
frameCount = 0;
|
|
577
|
+
totalFrameControllerMs = 0;
|
|
578
|
+
totalCaptureMs = 0;
|
|
579
|
+
totalCopyMs = 0;
|
|
580
|
+
totalFrameMs = 0;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
415
583
|
} catch (e) {
|
|
416
584
|
logger.error("Canvas preview render failed:", e);
|
|
417
585
|
} finally {
|
|
@@ -426,6 +594,13 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
426
594
|
disposed = true;
|
|
427
595
|
frameController.abort();
|
|
428
596
|
renderContext.dispose();
|
|
597
|
+
if (useNative && originalParent) {
|
|
598
|
+
timegroup.style.clipPath = savedClipPath;
|
|
599
|
+
timegroup.style.pointerEvents = savedPointerEvents;
|
|
600
|
+
if (originalNextSibling) originalParent.insertBefore(timegroup, originalNextSibling);
|
|
601
|
+
else originalParent.appendChild(timegroup);
|
|
602
|
+
captureCanvas?.remove();
|
|
603
|
+
}
|
|
429
604
|
};
|
|
430
605
|
refresh();
|
|
431
606
|
return {
|
|
@@ -439,5 +614,5 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
|
|
|
439
614
|
}
|
|
440
615
|
|
|
441
616
|
//#endregion
|
|
442
|
-
export { captureFromClone, captureTimegroupAtTime, renderTimegroupToCanvas, resetRenderState };
|
|
617
|
+
export { ContentNotReadyError, captureFromClone, captureTimegroupAtTime, clearInlineImageCache, generateThumbnails, generateThumbnailsFromClone, getCacheMetrics, getEffectiveRenderMode, getInlineImageCacheSize, getRenderState, isCanvas, isImage, loadImageFromDataUri, renderTimegroupToCanvas, resetCacheMetrics, resetRenderState, waitForVideoContent };
|
|
443
618
|
//# sourceMappingURL=renderTimegroupToCanvas.js.map
|