@hyperframes/producer 0.4.6 → 0.4.8
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/index.js +2282 -305
- package/dist/index.js.map +4 -4
- package/dist/public-server.js +2282 -305
- package/dist/public-server.js.map +4 -4
- package/dist/services/fileServer.d.ts +17 -2
- package/dist/services/fileServer.d.ts.map +1 -1
- package/dist/services/renderOrchestrator.d.ts +4 -11
- package/dist/services/renderOrchestrator.d.ts.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -5116,12 +5116,12 @@ var require_common = __commonJS({
|
|
|
5116
5116
|
createDebug.skips = [];
|
|
5117
5117
|
createDebug.formatters = {};
|
|
5118
5118
|
function selectColor(namespace) {
|
|
5119
|
-
let
|
|
5119
|
+
let hash2 = 0;
|
|
5120
5120
|
for (let i = 0; i < namespace.length; i++) {
|
|
5121
|
-
|
|
5122
|
-
|
|
5121
|
+
hash2 = (hash2 << 5) - hash2 + namespace.charCodeAt(i);
|
|
5122
|
+
hash2 |= 0;
|
|
5123
5123
|
}
|
|
5124
|
-
return createDebug.colors[Math.abs(
|
|
5124
|
+
return createDebug.colors[Math.abs(hash2) % createDebug.colors.length];
|
|
5125
5125
|
}
|
|
5126
5126
|
createDebug.selectColor = selectColor;
|
|
5127
5127
|
function createDebug(namespace) {
|
|
@@ -54349,25 +54349,25 @@ var require_data = __commonJS({
|
|
|
54349
54349
|
var notmodified_1 = __importDefault2(require_notmodified());
|
|
54350
54350
|
var debug6 = (0, debug_1.default)("get-uri:data");
|
|
54351
54351
|
var DataReadable = class extends stream_1.Readable {
|
|
54352
|
-
constructor(
|
|
54352
|
+
constructor(hash2, buf) {
|
|
54353
54353
|
super();
|
|
54354
54354
|
this.push(buf);
|
|
54355
54355
|
this.push(null);
|
|
54356
|
-
this.hash =
|
|
54356
|
+
this.hash = hash2;
|
|
54357
54357
|
}
|
|
54358
54358
|
};
|
|
54359
54359
|
var data = async ({ href: uri }, { cache } = {}) => {
|
|
54360
54360
|
const shasum = (0, crypto_1.createHash)("sha1");
|
|
54361
54361
|
shasum.update(uri);
|
|
54362
|
-
const
|
|
54363
|
-
debug6('generated SHA1 hash for "data:" URI: %o',
|
|
54364
|
-
if (cache?.hash ===
|
|
54365
|
-
debug6("got matching cache SHA1 hash: %o",
|
|
54362
|
+
const hash2 = shasum.digest("hex");
|
|
54363
|
+
debug6('generated SHA1 hash for "data:" URI: %o', hash2);
|
|
54364
|
+
if (cache?.hash === hash2) {
|
|
54365
|
+
debug6("got matching cache SHA1 hash: %o", hash2);
|
|
54366
54366
|
throw new notmodified_1.default();
|
|
54367
54367
|
} else {
|
|
54368
54368
|
debug6('creating Readable stream from "data:" URI buffer');
|
|
54369
54369
|
const { buffer } = (0, data_uri_to_buffer_1.dataUriToBuffer)(uri);
|
|
54370
|
-
return new DataReadable(
|
|
54370
|
+
return new DataReadable(hash2, Buffer.from(buffer));
|
|
54371
54371
|
}
|
|
54372
54372
|
};
|
|
54373
54373
|
exports.data = data;
|
|
@@ -75362,14 +75362,14 @@ var require_dist10 = __commonJS({
|
|
|
75362
75362
|
(0, quickjs_emscripten_1.getQuickJS)(),
|
|
75363
75363
|
this.loadPacFile()
|
|
75364
75364
|
]);
|
|
75365
|
-
const
|
|
75366
|
-
if (this.resolver && this.resolverHash ===
|
|
75365
|
+
const hash2 = crypto3.createHash("sha1").update(code).digest("hex");
|
|
75366
|
+
if (this.resolver && this.resolverHash === hash2) {
|
|
75367
75367
|
debug6("Same sha1 hash for code - contents have not changed, reusing previous proxy resolver");
|
|
75368
75368
|
return this.resolver;
|
|
75369
75369
|
}
|
|
75370
75370
|
debug6("Creating new proxy resolver instance");
|
|
75371
75371
|
this.resolver = (0, pac_resolver_1.createPacResolver)(qjs, code, this.opts);
|
|
75372
|
-
this.resolverHash =
|
|
75372
|
+
this.resolverHash = hash2;
|
|
75373
75373
|
return this.resolver;
|
|
75374
75374
|
} catch (err) {
|
|
75375
75375
|
if (this.resolver && err.code === "ENOTMODIFIED") {
|
|
@@ -89421,6 +89421,7 @@ import {
|
|
|
89421
89421
|
mkdirSync as mkdirSync10,
|
|
89422
89422
|
rmSync as rmSync3,
|
|
89423
89423
|
readFileSync as readFileSync9,
|
|
89424
|
+
readdirSync as readdirSync6,
|
|
89424
89425
|
writeFileSync as writeFileSync4,
|
|
89425
89426
|
copyFileSync as copyFileSync2,
|
|
89426
89427
|
appendFileSync
|
|
@@ -98668,6 +98669,8 @@ var DEFAULT_CONFIG = {
|
|
|
98668
98669
|
ffmpegEncodeTimeout: 6e5,
|
|
98669
98670
|
ffmpegProcessTimeout: 3e5,
|
|
98670
98671
|
ffmpegStreamingTimeout: 6e5,
|
|
98672
|
+
hdr: false,
|
|
98673
|
+
hdrAutoDetect: true,
|
|
98671
98674
|
audioGain: 1.35,
|
|
98672
98675
|
frameDataUriCacheLimit: 256,
|
|
98673
98676
|
playerReadyTimeout: 45e3,
|
|
@@ -98724,6 +98727,12 @@ function resolveConfig(overrides) {
|
|
|
98724
98727
|
"FFMPEG_STREAMING_TIMEOUT_MS",
|
|
98725
98728
|
DEFAULT_CONFIG.ffmpegStreamingTimeout
|
|
98726
98729
|
),
|
|
98730
|
+
hdr: (() => {
|
|
98731
|
+
const raw2 = env2("PRODUCER_HDR_TRANSFER");
|
|
98732
|
+
if (raw2 === "hlg" || raw2 === "pq") return { transfer: raw2 };
|
|
98733
|
+
return void 0;
|
|
98734
|
+
})(),
|
|
98735
|
+
hdrAutoDetect: envBool("PRODUCER_HDR_AUTO_DETECT", DEFAULT_CONFIG.hdrAutoDetect),
|
|
98727
98736
|
audioGain: envNum("PRODUCER_AUDIO_GAIN", DEFAULT_CONFIG.audioGain),
|
|
98728
98737
|
frameDataUriCacheLimit: Math.max(
|
|
98729
98738
|
32,
|
|
@@ -98920,7 +98929,8 @@ function buildChromeArgs(options, config2) {
|
|
|
98920
98929
|
"--font-render-hinting=none",
|
|
98921
98930
|
"--force-color-profile=srgb",
|
|
98922
98931
|
`--window-size=${options.width},${options.height}`,
|
|
98923
|
-
//
|
|
98932
|
+
// Prevent Chrome from throttling background tabs/timers — critical when the
|
|
98933
|
+
// page is offscreen during headless capture
|
|
98924
98934
|
"--disable-background-timer-throttling",
|
|
98925
98935
|
"--disable-backgrounding-occluded-windows",
|
|
98926
98936
|
"--disable-renderer-backgrounding",
|
|
@@ -99634,6 +99644,7 @@ var mediaRules = [
|
|
|
99634
99644
|
const timedTagPositions = [];
|
|
99635
99645
|
for (const tag of tags) {
|
|
99636
99646
|
if (tag.name === "video" || tag.name === "audio") continue;
|
|
99647
|
+
if (readAttr(tag.raw, "data-composition-id")) continue;
|
|
99637
99648
|
if (readAttr(tag.raw, "data-start")) {
|
|
99638
99649
|
timedTagPositions.push({
|
|
99639
99650
|
name: tag.name,
|
|
@@ -100740,7 +100751,8 @@ function lintHyperframeHtml(html, options = {}) {
|
|
|
100740
100751
|
}
|
|
100741
100752
|
|
|
100742
100753
|
// ../core/src/compiler/rewriteSubCompPaths.ts
|
|
100743
|
-
import {
|
|
100754
|
+
import { posix } from "path";
|
|
100755
|
+
var { join: join4, resolve: resolve6, dirname: dirname4 } = posix;
|
|
100744
100756
|
var PATH_ATTRS = ["src", "href"];
|
|
100745
100757
|
var CSS_URL_RE = /\burl\(\s*(["']?)([^)"']+)\1\s*\)/g;
|
|
100746
100758
|
function isAbsoluteOrSpecial(val) {
|
|
@@ -100907,6 +100919,83 @@ async function pageScreenshotCapture(page, options) {
|
|
|
100907
100919
|
});
|
|
100908
100920
|
return Buffer.from(result.data, "base64");
|
|
100909
100921
|
}
|
|
100922
|
+
var TRANSPARENT_BG_STYLE_ID = "__hf_transparent_bg__";
|
|
100923
|
+
async function initTransparentBackground(page) {
|
|
100924
|
+
const client = await getCdpSession(page);
|
|
100925
|
+
await client.send("Emulation.setDefaultBackgroundColorOverride", {
|
|
100926
|
+
color: { r: 0, g: 0, b: 0, a: 0 }
|
|
100927
|
+
});
|
|
100928
|
+
await page.evaluate((styleId) => {
|
|
100929
|
+
if (document.getElementById(styleId)) return;
|
|
100930
|
+
const style = document.createElement("style");
|
|
100931
|
+
style.id = styleId;
|
|
100932
|
+
style.textContent = "html,body,[data-composition-id]{background:transparent !important;background-color:transparent !important;background-image:none !important;}";
|
|
100933
|
+
document.head.appendChild(style);
|
|
100934
|
+
}, TRANSPARENT_BG_STYLE_ID);
|
|
100935
|
+
}
|
|
100936
|
+
async function captureAlphaPng(page, width, height) {
|
|
100937
|
+
const client = await getCdpSession(page);
|
|
100938
|
+
const result = await client.send("Page.captureScreenshot", {
|
|
100939
|
+
format: "png",
|
|
100940
|
+
fromSurface: true,
|
|
100941
|
+
captureBeyondViewport: false,
|
|
100942
|
+
optimizeForSpeed: false,
|
|
100943
|
+
// must be false to preserve alpha
|
|
100944
|
+
clip: { x: 0, y: 0, width, height, scale: 1 }
|
|
100945
|
+
});
|
|
100946
|
+
return Buffer.from(result.data, "base64");
|
|
100947
|
+
}
|
|
100948
|
+
var DOM_LAYER_MASK_STYLE_ID = "__hf_dom_layer_mask__";
|
|
100949
|
+
async function applyDomLayerMask(page, showIds, extraHideIds) {
|
|
100950
|
+
await page.evaluate(
|
|
100951
|
+
(args) => {
|
|
100952
|
+
const existing = document.getElementById(args.styleId);
|
|
100953
|
+
if (existing) existing.remove();
|
|
100954
|
+
const showSelectors = [];
|
|
100955
|
+
for (const id of args.show) {
|
|
100956
|
+
const escaped = CSS.escape(id);
|
|
100957
|
+
showSelectors.push(`#${escaped}`, `#${escaped} *`);
|
|
100958
|
+
const renderEscaped = CSS.escape(`__render_frame_${id}__`);
|
|
100959
|
+
showSelectors.push(`#${renderEscaped}`, `#${renderEscaped} *`);
|
|
100960
|
+
}
|
|
100961
|
+
const massHideRule = "body *{visibility:hidden !important;}";
|
|
100962
|
+
const showRule = showSelectors.length === 0 ? "" : `${showSelectors.join(",")}{visibility:visible !important;}`;
|
|
100963
|
+
const style = document.createElement("style");
|
|
100964
|
+
style.id = args.styleId;
|
|
100965
|
+
style.textContent = `${massHideRule}
|
|
100966
|
+
${showRule}`;
|
|
100967
|
+
document.head.appendChild(style);
|
|
100968
|
+
for (const id of args.hide) {
|
|
100969
|
+
const el = document.getElementById(id);
|
|
100970
|
+
if (el) {
|
|
100971
|
+
el.style.setProperty("visibility", "hidden", "important");
|
|
100972
|
+
}
|
|
100973
|
+
const img = document.getElementById(`__render_frame_${id}__`);
|
|
100974
|
+
if (img) {
|
|
100975
|
+
img.style.setProperty("visibility", "hidden", "important");
|
|
100976
|
+
}
|
|
100977
|
+
}
|
|
100978
|
+
},
|
|
100979
|
+
{ show: showIds, hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID }
|
|
100980
|
+
);
|
|
100981
|
+
}
|
|
100982
|
+
async function removeDomLayerMask(page, extraHideIds) {
|
|
100983
|
+
await page.evaluate(
|
|
100984
|
+
(args) => {
|
|
100985
|
+
const style = document.getElementById(args.styleId);
|
|
100986
|
+
if (style) style.remove();
|
|
100987
|
+
for (const id of args.hide) {
|
|
100988
|
+
const el = document.getElementById(id);
|
|
100989
|
+
if (el) {
|
|
100990
|
+
el.style.removeProperty("visibility");
|
|
100991
|
+
}
|
|
100992
|
+
const img = document.getElementById(`__render_frame_${id}__`);
|
|
100993
|
+
if (img) img.style.removeProperty("visibility");
|
|
100994
|
+
}
|
|
100995
|
+
},
|
|
100996
|
+
{ hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID }
|
|
100997
|
+
);
|
|
100998
|
+
}
|
|
100910
100999
|
async function injectVideoFramesBatch(page, updates) {
|
|
100911
101000
|
if (updates.length === 0) return;
|
|
100912
101001
|
await page.evaluate(
|
|
@@ -100928,16 +101017,7 @@ async function injectVideoFramesBatch(page, updates) {
|
|
|
100928
101017
|
video.parentNode?.insertBefore(img, video.nextSibling);
|
|
100929
101018
|
}
|
|
100930
101019
|
if (!img) continue;
|
|
100931
|
-
|
|
100932
|
-
img.style.position = computedStyle.position;
|
|
100933
|
-
img.style.width = computedStyle.width;
|
|
100934
|
-
img.style.height = computedStyle.height;
|
|
100935
|
-
img.style.top = computedStyle.top;
|
|
100936
|
-
img.style.left = computedStyle.left;
|
|
100937
|
-
img.style.right = computedStyle.right;
|
|
100938
|
-
img.style.bottom = computedStyle.bottom;
|
|
100939
|
-
img.style.inset = computedStyle.inset;
|
|
100940
|
-
} else {
|
|
101020
|
+
{
|
|
100941
101021
|
const videoRect = video.getBoundingClientRect();
|
|
100942
101022
|
const offsetLeft = Number.isFinite(video.offsetLeft) ? video.offsetLeft : 0;
|
|
100943
101023
|
const offsetTop = Number.isFinite(video.offsetTop) ? video.offsetTop : 0;
|
|
@@ -100956,6 +101036,7 @@ async function injectVideoFramesBatch(page, updates) {
|
|
|
100956
101036
|
img.style.objectPosition = computedStyle.objectPosition;
|
|
100957
101037
|
img.style.zIndex = computedStyle.zIndex;
|
|
100958
101038
|
for (const property of visualProperties) {
|
|
101039
|
+
if (property === "opacity") continue;
|
|
100959
101040
|
if (sourceIsStatic && (property === "top" || property === "left" || property === "right" || property === "bottom" || property === "inset")) {
|
|
100960
101041
|
continue;
|
|
100961
101042
|
}
|
|
@@ -100988,14 +101069,22 @@ async function syncVideoFrameVisibility(page, activeVideoIds) {
|
|
|
100988
101069
|
const active = new Set(ids);
|
|
100989
101070
|
const videos = Array.from(document.querySelectorAll("video[data-start]"));
|
|
100990
101071
|
for (const video of videos) {
|
|
100991
|
-
if (active.has(video.id)) continue;
|
|
100992
|
-
video.style.removeProperty("display");
|
|
100993
|
-
video.style.setProperty("visibility", "hidden", "important");
|
|
100994
|
-
video.style.setProperty("opacity", "0", "important");
|
|
100995
|
-
video.style.setProperty("pointer-events", "none", "important");
|
|
100996
101072
|
const img = video.nextElementSibling;
|
|
100997
|
-
|
|
100998
|
-
|
|
101073
|
+
const hasImg = img && img.classList.contains("__render_frame__");
|
|
101074
|
+
if (active.has(video.id)) {
|
|
101075
|
+
video.style.setProperty("visibility", "hidden", "important");
|
|
101076
|
+
video.style.setProperty("pointer-events", "none", "important");
|
|
101077
|
+
if (hasImg) {
|
|
101078
|
+
img.style.visibility = "visible";
|
|
101079
|
+
}
|
|
101080
|
+
} else {
|
|
101081
|
+
video.style.removeProperty("display");
|
|
101082
|
+
video.style.setProperty("visibility", "hidden", "important");
|
|
101083
|
+
video.style.setProperty("opacity", "0", "important");
|
|
101084
|
+
video.style.setProperty("pointer-events", "none", "important");
|
|
101085
|
+
if (hasImg) {
|
|
101086
|
+
img.style.visibility = "hidden";
|
|
101087
|
+
}
|
|
100999
101088
|
}
|
|
101000
101089
|
}
|
|
101001
101090
|
}, activeVideoIds);
|
|
@@ -101452,7 +101541,7 @@ var ENCODER_PRESETS = {
|
|
|
101452
101541
|
standard: { preset: "medium", quality: 18, codec: "h264" },
|
|
101453
101542
|
high: { preset: "slow", quality: 15, codec: "h264" }
|
|
101454
101543
|
};
|
|
101455
|
-
function getEncoderPreset(quality, format3 = "mp4") {
|
|
101544
|
+
function getEncoderPreset(quality, format3 = "mp4", hdr) {
|
|
101456
101545
|
const base = ENCODER_PRESETS[quality];
|
|
101457
101546
|
if (format3 === "webm") {
|
|
101458
101547
|
return {
|
|
@@ -101470,6 +101559,15 @@ function getEncoderPreset(quality, format3 = "mp4") {
|
|
|
101470
101559
|
pixelFormat: "yuva444p10le"
|
|
101471
101560
|
};
|
|
101472
101561
|
}
|
|
101562
|
+
if (hdr) {
|
|
101563
|
+
return {
|
|
101564
|
+
preset: base.preset === "ultrafast" ? "fast" : base.preset,
|
|
101565
|
+
quality: base.quality,
|
|
101566
|
+
codec: "h265",
|
|
101567
|
+
pixelFormat: "yuv420p10le",
|
|
101568
|
+
hdr
|
|
101569
|
+
};
|
|
101570
|
+
}
|
|
101473
101571
|
return { ...base, pixelFormat: "yuv420p" };
|
|
101474
101572
|
}
|
|
101475
101573
|
function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
@@ -101527,6 +101625,9 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
101527
101625
|
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
101528
101626
|
}
|
|
101529
101627
|
}
|
|
101628
|
+
if (codec === "h265") {
|
|
101629
|
+
args.push("-tag:v", "hvc1");
|
|
101630
|
+
}
|
|
101530
101631
|
} else if (codec === "vp9") {
|
|
101531
101632
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
101532
101633
|
args.push("-deadline", preset === "ultrafast" ? "realtime" : "good");
|
|
@@ -101833,31 +101934,83 @@ async function applyFaststart(inputPath, outputPath, signal, config2) {
|
|
|
101833
101934
|
import { spawn as spawn6 } from "child_process";
|
|
101834
101935
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, statSync as statSync4 } from "fs";
|
|
101835
101936
|
import { dirname as dirname6 } from "path";
|
|
101937
|
+
|
|
101938
|
+
// ../engine/src/utils/hdr.ts
|
|
101939
|
+
function isHdrColorSpace(cs) {
|
|
101940
|
+
if (!cs) return false;
|
|
101941
|
+
return cs.colorPrimaries.includes("bt2020") || cs.colorSpace.includes("bt2020") || cs.colorTransfer === "smpte2084" || cs.colorTransfer === "arib-std-b67";
|
|
101942
|
+
}
|
|
101943
|
+
function detectTransfer(cs) {
|
|
101944
|
+
if (cs?.colorTransfer === "smpte2084") return "pq";
|
|
101945
|
+
return "hlg";
|
|
101946
|
+
}
|
|
101947
|
+
var DEFAULT_HDR10_MASTERING = {
|
|
101948
|
+
masterDisplay: "G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1)",
|
|
101949
|
+
maxCll: "1000,400"
|
|
101950
|
+
};
|
|
101951
|
+
function getHdrEncoderColorParams(transfer, mastering = DEFAULT_HDR10_MASTERING) {
|
|
101952
|
+
const colorTrc = transfer === "pq" ? "smpte2084" : "arib-std-b67";
|
|
101953
|
+
const tagging = `colorprim=bt2020:transfer=${colorTrc}:colormatrix=bt2020nc`;
|
|
101954
|
+
const metadata = `master-display=${mastering.masterDisplay}:max-cll=${mastering.maxCll}`;
|
|
101955
|
+
return {
|
|
101956
|
+
colorPrimaries: "bt2020",
|
|
101957
|
+
colorTrc,
|
|
101958
|
+
colorspace: "bt2020nc",
|
|
101959
|
+
pixelFormat: "yuv420p10le",
|
|
101960
|
+
x265ColorParams: `${tagging}:${metadata}`,
|
|
101961
|
+
mastering
|
|
101962
|
+
};
|
|
101963
|
+
}
|
|
101964
|
+
function analyzeCompositionHdr(colorSpaces) {
|
|
101965
|
+
let hasPq = false;
|
|
101966
|
+
let hasHdr = false;
|
|
101967
|
+
for (const cs of colorSpaces) {
|
|
101968
|
+
if (!isHdrColorSpace(cs)) continue;
|
|
101969
|
+
hasHdr = true;
|
|
101970
|
+
if (cs?.colorTransfer === "smpte2084") hasPq = true;
|
|
101971
|
+
}
|
|
101972
|
+
if (!hasHdr) return { hasHdr: false, dominantTransfer: null };
|
|
101973
|
+
const dominantTransfer = hasPq ? "pq" : "hlg";
|
|
101974
|
+
return { hasHdr: true, dominantTransfer };
|
|
101975
|
+
}
|
|
101976
|
+
|
|
101977
|
+
// ../engine/src/services/streamingEncoder.ts
|
|
101836
101978
|
function createFrameReorderBuffer(startFrame, endFrame) {
|
|
101837
|
-
let
|
|
101838
|
-
|
|
101839
|
-
const
|
|
101840
|
-
|
|
101841
|
-
|
|
101842
|
-
|
|
101843
|
-
|
|
101844
|
-
|
|
101979
|
+
let cursor = startFrame;
|
|
101980
|
+
const pending = /* @__PURE__ */ new Map();
|
|
101981
|
+
const enqueueAt = (frame, resolve13) => {
|
|
101982
|
+
const list = pending.get(frame);
|
|
101983
|
+
if (list === void 0) {
|
|
101984
|
+
pending.set(frame, [resolve13]);
|
|
101985
|
+
} else {
|
|
101986
|
+
list.push(resolve13);
|
|
101845
101987
|
}
|
|
101846
101988
|
};
|
|
101847
|
-
|
|
101848
|
-
|
|
101849
|
-
|
|
101850
|
-
|
|
101851
|
-
|
|
101852
|
-
|
|
101853
|
-
|
|
101854
|
-
|
|
101855
|
-
|
|
101856
|
-
|
|
101857
|
-
|
|
101858
|
-
|
|
101859
|
-
|
|
101989
|
+
const flushAt = (frame) => {
|
|
101990
|
+
const list = pending.get(frame);
|
|
101991
|
+
if (list === void 0) return;
|
|
101992
|
+
pending.delete(frame);
|
|
101993
|
+
for (const resolve13 of list) resolve13();
|
|
101994
|
+
};
|
|
101995
|
+
const waitForFrame = (frame) => new Promise((resolve13) => {
|
|
101996
|
+
if (frame === cursor) {
|
|
101997
|
+
resolve13();
|
|
101998
|
+
return;
|
|
101999
|
+
}
|
|
102000
|
+
enqueueAt(frame, resolve13);
|
|
102001
|
+
});
|
|
102002
|
+
const advanceTo = (frame) => {
|
|
102003
|
+
cursor = frame;
|
|
102004
|
+
flushAt(frame);
|
|
101860
102005
|
};
|
|
102006
|
+
const waitForAllDone = () => new Promise((resolve13) => {
|
|
102007
|
+
if (cursor >= endFrame) {
|
|
102008
|
+
resolve13();
|
|
102009
|
+
return;
|
|
102010
|
+
}
|
|
102011
|
+
enqueueAt(endFrame, resolve13);
|
|
102012
|
+
});
|
|
102013
|
+
return { waitForFrame, advanceTo, waitForAllDone };
|
|
101861
102014
|
}
|
|
101862
102015
|
function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
101863
102016
|
const {
|
|
@@ -101870,19 +102023,36 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
101870
102023
|
useGpu = false,
|
|
101871
102024
|
imageFormat = "jpeg"
|
|
101872
102025
|
} = options;
|
|
101873
|
-
const
|
|
101874
|
-
|
|
101875
|
-
|
|
101876
|
-
"
|
|
101877
|
-
|
|
101878
|
-
|
|
101879
|
-
|
|
101880
|
-
|
|
101881
|
-
|
|
101882
|
-
|
|
101883
|
-
|
|
101884
|
-
|
|
101885
|
-
|
|
102026
|
+
const args = [];
|
|
102027
|
+
if (options.rawInputFormat) {
|
|
102028
|
+
const hdrTransfer = options.hdr?.transfer;
|
|
102029
|
+
const inputColorTrc = hdrTransfer === "pq" ? "smpte2084" : hdrTransfer === "hlg" ? "arib-std-b67" : void 0;
|
|
102030
|
+
args.push(
|
|
102031
|
+
"-f",
|
|
102032
|
+
"rawvideo",
|
|
102033
|
+
"-pix_fmt",
|
|
102034
|
+
options.rawInputFormat,
|
|
102035
|
+
"-s",
|
|
102036
|
+
`${options.width}x${options.height}`,
|
|
102037
|
+
"-framerate",
|
|
102038
|
+
String(fps)
|
|
102039
|
+
);
|
|
102040
|
+
if (inputColorTrc) {
|
|
102041
|
+
args.push(
|
|
102042
|
+
"-color_primaries",
|
|
102043
|
+
"bt2020",
|
|
102044
|
+
"-color_trc",
|
|
102045
|
+
inputColorTrc,
|
|
102046
|
+
"-colorspace",
|
|
102047
|
+
"bt2020nc"
|
|
102048
|
+
);
|
|
102049
|
+
}
|
|
102050
|
+
args.push("-i", "-");
|
|
102051
|
+
} else {
|
|
102052
|
+
const inputCodec = imageFormat === "png" ? "png" : "mjpeg";
|
|
102053
|
+
args.push("-f", "image2pipe", "-vcodec", inputCodec, "-framerate", String(fps), "-i", "-");
|
|
102054
|
+
}
|
|
102055
|
+
args.push("-r", String(fps));
|
|
101886
102056
|
const shouldUseGpu = useGpu && gpuEncoder !== null;
|
|
101887
102057
|
if (codec === "h264" || codec === "h265") {
|
|
101888
102058
|
if (shouldUseGpu) {
|
|
@@ -101920,12 +102090,15 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
101920
102090
|
if (bitrate) args.push("-b:v", bitrate);
|
|
101921
102091
|
else args.push("-crf", String(quality));
|
|
101922
102092
|
const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
|
|
101923
|
-
const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
102093
|
+
const colorParams = options.rawInputFormat && options.hdr ? getHdrEncoderColorParams(options.hdr.transfer).x265ColorParams : "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
101924
102094
|
if (preset === "ultrafast") {
|
|
101925
102095
|
args.push(xParamsFlag, `aq-mode=3:${colorParams}`);
|
|
101926
102096
|
} else {
|
|
101927
102097
|
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
101928
102098
|
}
|
|
102099
|
+
if (codec === "h265") {
|
|
102100
|
+
args.push("-tag:v", "hvc1");
|
|
102101
|
+
}
|
|
101929
102102
|
}
|
|
101930
102103
|
} else if (codec === "vp9") {
|
|
101931
102104
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
@@ -101941,17 +102114,31 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
101941
102114
|
return [...args, "-y", outputPath];
|
|
101942
102115
|
}
|
|
101943
102116
|
if (codec === "h264" || codec === "h265") {
|
|
101944
|
-
|
|
101945
|
-
|
|
101946
|
-
|
|
101947
|
-
|
|
101948
|
-
|
|
101949
|
-
|
|
101950
|
-
|
|
101951
|
-
|
|
101952
|
-
|
|
101953
|
-
|
|
101954
|
-
|
|
102117
|
+
if (options.rawInputFormat && options.hdr) {
|
|
102118
|
+
args.push(
|
|
102119
|
+
"-colorspace:v",
|
|
102120
|
+
"bt2020nc",
|
|
102121
|
+
"-color_primaries:v",
|
|
102122
|
+
"bt2020",
|
|
102123
|
+
"-color_trc:v",
|
|
102124
|
+
options.hdr.transfer === "pq" ? "smpte2084" : "arib-std-b67",
|
|
102125
|
+
"-color_range",
|
|
102126
|
+
"tv"
|
|
102127
|
+
);
|
|
102128
|
+
} else {
|
|
102129
|
+
args.push(
|
|
102130
|
+
"-colorspace:v",
|
|
102131
|
+
"bt709",
|
|
102132
|
+
"-color_primaries:v",
|
|
102133
|
+
"bt709",
|
|
102134
|
+
"-color_trc:v",
|
|
102135
|
+
"bt709",
|
|
102136
|
+
"-color_range",
|
|
102137
|
+
"tv"
|
|
102138
|
+
);
|
|
102139
|
+
}
|
|
102140
|
+
if (options.rawInputFormat) {
|
|
102141
|
+
} else if (gpuEncoder === "vaapi") {
|
|
101955
102142
|
const vfIdx = args.indexOf("-vf");
|
|
101956
102143
|
if (vfIdx !== -1) {
|
|
101957
102144
|
args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
|
|
@@ -102021,14 +102208,16 @@ Process error: ${err.message}`;
|
|
|
102021
102208
|
if (exitStatus !== "running" || !ffmpeg.stdin || ffmpeg.stdin.destroyed) {
|
|
102022
102209
|
return false;
|
|
102023
102210
|
}
|
|
102024
|
-
|
|
102211
|
+
const copy = Buffer.from(buffer);
|
|
102212
|
+
return ffmpeg.stdin.write(copy);
|
|
102025
102213
|
},
|
|
102026
102214
|
close: async () => {
|
|
102027
102215
|
clearTimeout(timer2);
|
|
102028
102216
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
102029
|
-
|
|
102217
|
+
const stdin = ffmpeg.stdin;
|
|
102218
|
+
if (stdin && !stdin.destroyed) {
|
|
102030
102219
|
await new Promise((resolve13) => {
|
|
102031
|
-
|
|
102220
|
+
stdin.end(() => resolve13());
|
|
102032
102221
|
});
|
|
102033
102222
|
}
|
|
102034
102223
|
await exitPromise;
|
|
@@ -102132,6 +102321,10 @@ async function extractVideoMetadata(filePath) {
|
|
|
102132
102321
|
const avgFps = parseFrameRate(videoStream.avg_frame_rate);
|
|
102133
102322
|
const fps = avgFps || rFps;
|
|
102134
102323
|
const isVFR = rFps > 0 && avgFps > 0 && Math.abs(rFps - avgFps) / Math.max(rFps, avgFps) > 0.1;
|
|
102324
|
+
const colorTransfer = videoStream.color_transfer || "";
|
|
102325
|
+
const colorPrimaries = videoStream.color_primaries || "";
|
|
102326
|
+
const colorSpaceVal = videoStream.color_space || "";
|
|
102327
|
+
const hasColorInfo = !!(colorTransfer || colorPrimaries || colorSpaceVal);
|
|
102135
102328
|
return {
|
|
102136
102329
|
durationSeconds: output2.format.duration ? parseFloat(output2.format.duration) : 0,
|
|
102137
102330
|
width: videoStream.width || 0,
|
|
@@ -102139,7 +102332,8 @@ async function extractVideoMetadata(filePath) {
|
|
|
102139
102332
|
fps,
|
|
102140
102333
|
videoCodec: videoStream.codec_name || "unknown",
|
|
102141
102334
|
hasAudio: output2.streams.some((s) => s.codec_type === "audio"),
|
|
102142
|
-
isVFR
|
|
102335
|
+
isVFR,
|
|
102336
|
+
colorSpace: hasColorInfo ? { colorTransfer, colorPrimaries, colorSpace: colorSpaceVal } : null
|
|
102143
102337
|
};
|
|
102144
102338
|
})();
|
|
102145
102339
|
videoMetadataCache.set(filePath, probePromise);
|
|
@@ -102244,10 +102438,10 @@ import { finished } from "stream/promises";
|
|
|
102244
102438
|
var downloadPathCache = /* @__PURE__ */ new Map();
|
|
102245
102439
|
var inFlightDownloads = /* @__PURE__ */ new Map();
|
|
102246
102440
|
function getFilenameFromUrl(url) {
|
|
102247
|
-
const
|
|
102441
|
+
const hash2 = createHash("md5").update(url).digest("hex").slice(0, 12);
|
|
102248
102442
|
const urlObj = new URL(url);
|
|
102249
102443
|
const ext = extname2(urlObj.pathname) || ".mp4";
|
|
102250
|
-
return `download_${
|
|
102444
|
+
return `download_${hash2}${ext}`;
|
|
102251
102445
|
}
|
|
102252
102446
|
async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
102253
102447
|
const cachedPath = downloadPathCache.get(url);
|
|
@@ -102347,18 +102541,20 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
102347
102541
|
const metadata = await extractVideoMetadata(videoPath);
|
|
102348
102542
|
const framePattern = `frame_%05d.${format3}`;
|
|
102349
102543
|
const outputPattern = join8(videoOutputDir, framePattern);
|
|
102350
|
-
const
|
|
102351
|
-
|
|
102352
|
-
|
|
102353
|
-
|
|
102354
|
-
|
|
102355
|
-
|
|
102356
|
-
|
|
102357
|
-
|
|
102358
|
-
|
|
102359
|
-
"
|
|
102360
|
-
|
|
102361
|
-
|
|
102544
|
+
const isHdr = isHdrColorSpace(metadata.colorSpace);
|
|
102545
|
+
const isMacOS = process.platform === "darwin";
|
|
102546
|
+
const args = [];
|
|
102547
|
+
if (isHdr && isMacOS) {
|
|
102548
|
+
args.push("-hwaccel", "videotoolbox");
|
|
102549
|
+
}
|
|
102550
|
+
args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
|
|
102551
|
+
const vfFilters = [];
|
|
102552
|
+
if (isHdr && isMacOS) {
|
|
102553
|
+
vfFilters.push("format=nv12");
|
|
102554
|
+
}
|
|
102555
|
+
vfFilters.push(`fps=${fps}`);
|
|
102556
|
+
args.push("-vf", vfFilters.join(","));
|
|
102557
|
+
args.push("-q:v", format3 === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
|
|
102362
102558
|
if (format3 === "png") args.push("-compression_level", "6");
|
|
102363
102559
|
args.push("-y", outputPattern);
|
|
102364
102560
|
return new Promise((resolve13, reject) => {
|
|
@@ -102418,30 +102614,100 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
102418
102614
|
});
|
|
102419
102615
|
});
|
|
102420
102616
|
}
|
|
102617
|
+
async function convertSdrToHdr(inputPath, outputPath, signal, config2) {
|
|
102618
|
+
const timeout2 = config2?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
102619
|
+
const args = [
|
|
102620
|
+
"-i",
|
|
102621
|
+
inputPath,
|
|
102622
|
+
"-vf",
|
|
102623
|
+
"colorspace=all=bt2020:iall=bt709:range=tv",
|
|
102624
|
+
"-color_primaries",
|
|
102625
|
+
"bt2020",
|
|
102626
|
+
"-color_trc",
|
|
102627
|
+
"arib-std-b67",
|
|
102628
|
+
"-colorspace",
|
|
102629
|
+
"bt2020nc",
|
|
102630
|
+
"-c:v",
|
|
102631
|
+
"libx264",
|
|
102632
|
+
"-preset",
|
|
102633
|
+
"fast",
|
|
102634
|
+
"-crf",
|
|
102635
|
+
"16",
|
|
102636
|
+
"-c:a",
|
|
102637
|
+
"copy",
|
|
102638
|
+
"-y",
|
|
102639
|
+
outputPath
|
|
102640
|
+
];
|
|
102641
|
+
const result = await runFfmpeg(args, { signal, timeout: timeout2 });
|
|
102642
|
+
if (!result.success) {
|
|
102643
|
+
throw new Error(
|
|
102644
|
+
`SDR\u2192HDR conversion failed (exit ${result.exitCode}): ${result.stderr.slice(-300)}`
|
|
102645
|
+
);
|
|
102646
|
+
}
|
|
102647
|
+
}
|
|
102421
102648
|
async function extractAllVideoFrames(videos, baseDir, options, signal, config2, compiledDir) {
|
|
102422
102649
|
const startTime = Date.now();
|
|
102423
102650
|
const extracted = [];
|
|
102424
102651
|
const errors = [];
|
|
102425
102652
|
let totalFramesExtracted = 0;
|
|
102653
|
+
const resolvedVideos = [];
|
|
102654
|
+
for (const video of videos) {
|
|
102655
|
+
if (signal?.aborted) break;
|
|
102656
|
+
try {
|
|
102657
|
+
let videoPath = video.src;
|
|
102658
|
+
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
102659
|
+
const fromCompiled = compiledDir ? join8(compiledDir, videoPath) : null;
|
|
102660
|
+
videoPath = fromCompiled && existsSync8(fromCompiled) ? fromCompiled : join8(baseDir, videoPath);
|
|
102661
|
+
}
|
|
102662
|
+
if (isHttpUrl(videoPath)) {
|
|
102663
|
+
const downloadDir = join8(options.outputDir, "_downloads");
|
|
102664
|
+
mkdirSync5(downloadDir, { recursive: true });
|
|
102665
|
+
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
102666
|
+
}
|
|
102667
|
+
if (!existsSync8(videoPath)) {
|
|
102668
|
+
errors.push({ videoId: video.id, error: `Video file not found: ${videoPath}` });
|
|
102669
|
+
continue;
|
|
102670
|
+
}
|
|
102671
|
+
resolvedVideos.push({ video, videoPath });
|
|
102672
|
+
} catch (err) {
|
|
102673
|
+
errors.push({ videoId: video.id, error: err instanceof Error ? err.message : String(err) });
|
|
102674
|
+
}
|
|
102675
|
+
}
|
|
102676
|
+
const videoColorSpaces = await Promise.all(
|
|
102677
|
+
resolvedVideos.map(async ({ videoPath }) => {
|
|
102678
|
+
const metadata = await extractVideoMetadata(videoPath);
|
|
102679
|
+
return metadata.colorSpace;
|
|
102680
|
+
})
|
|
102681
|
+
);
|
|
102682
|
+
const hasAnyHdr = videoColorSpaces.some(isHdrColorSpace);
|
|
102683
|
+
if (hasAnyHdr) {
|
|
102684
|
+
const convertDir = join8(options.outputDir, "_hdr_normalized");
|
|
102685
|
+
mkdirSync5(convertDir, { recursive: true });
|
|
102686
|
+
for (let i = 0; i < resolvedVideos.length; i++) {
|
|
102687
|
+
if (signal?.aborted) break;
|
|
102688
|
+
const cs = videoColorSpaces[i] ?? null;
|
|
102689
|
+
if (!isHdrColorSpace(cs)) {
|
|
102690
|
+
const entry = resolvedVideos[i];
|
|
102691
|
+
if (!entry) continue;
|
|
102692
|
+
const convertedPath = join8(convertDir, `${entry.video.id}_hdr.mp4`);
|
|
102693
|
+
try {
|
|
102694
|
+
await convertSdrToHdr(entry.videoPath, convertedPath, signal, config2);
|
|
102695
|
+
entry.videoPath = convertedPath;
|
|
102696
|
+
} catch (err) {
|
|
102697
|
+
errors.push({
|
|
102698
|
+
videoId: entry.video.id,
|
|
102699
|
+
error: `SDR\u2192HDR conversion failed: ${err instanceof Error ? err.message : String(err)}`
|
|
102700
|
+
});
|
|
102701
|
+
}
|
|
102702
|
+
}
|
|
102703
|
+
}
|
|
102704
|
+
}
|
|
102426
102705
|
const results = await Promise.all(
|
|
102427
|
-
|
|
102706
|
+
resolvedVideos.map(async ({ video, videoPath }) => {
|
|
102428
102707
|
if (signal?.aborted) {
|
|
102429
102708
|
throw new Error("Video frame extraction cancelled");
|
|
102430
102709
|
}
|
|
102431
102710
|
try {
|
|
102432
|
-
let videoPath = video.src;
|
|
102433
|
-
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
102434
|
-
const fromCompiled = compiledDir ? join8(compiledDir, videoPath) : null;
|
|
102435
|
-
videoPath = fromCompiled && existsSync8(fromCompiled) ? fromCompiled : join8(baseDir, videoPath);
|
|
102436
|
-
}
|
|
102437
|
-
if (isHttpUrl(videoPath)) {
|
|
102438
|
-
const downloadDir = join8(options.outputDir, "_downloads");
|
|
102439
|
-
mkdirSync5(downloadDir, { recursive: true });
|
|
102440
|
-
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
102441
|
-
}
|
|
102442
|
-
if (!existsSync8(videoPath)) {
|
|
102443
|
-
return { error: { videoId: video.id, error: `Video file not found: ${videoPath}` } };
|
|
102444
|
-
}
|
|
102445
102711
|
let videoDuration = video.end - video.start;
|
|
102446
102712
|
if (!Number.isFinite(videoDuration) || videoDuration <= 0) {
|
|
102447
102713
|
const metadata = await extractVideoMetadata(videoPath);
|
|
@@ -102676,6 +102942,142 @@ function createVideoFrameInjector(frameLookup, config2) {
|
|
|
102676
102942
|
}
|
|
102677
102943
|
};
|
|
102678
102944
|
}
|
|
102945
|
+
async function queryElementStacking(page, nativeHdrVideoIds) {
|
|
102946
|
+
const hdrIds = Array.from(nativeHdrVideoIds);
|
|
102947
|
+
return page.evaluate((hdrIdList) => {
|
|
102948
|
+
const hdrSet = new Set(hdrIdList);
|
|
102949
|
+
const elements = document.querySelectorAll("[data-start]");
|
|
102950
|
+
const results = [];
|
|
102951
|
+
function getEffectiveZIndex(node) {
|
|
102952
|
+
let current = node;
|
|
102953
|
+
while (current) {
|
|
102954
|
+
const cs = window.getComputedStyle(current);
|
|
102955
|
+
const pos = cs.position;
|
|
102956
|
+
const z = parseInt(cs.zIndex);
|
|
102957
|
+
if (!Number.isNaN(z) && pos !== "static") return z;
|
|
102958
|
+
current = current.parentElement;
|
|
102959
|
+
}
|
|
102960
|
+
return 0;
|
|
102961
|
+
}
|
|
102962
|
+
function getEffectiveBorderRadius(node) {
|
|
102963
|
+
function resolveRadius(value, el) {
|
|
102964
|
+
if (value.includes("%")) {
|
|
102965
|
+
const pct = parseFloat(value) / 100;
|
|
102966
|
+
const htmlEl = el;
|
|
102967
|
+
const w = htmlEl.offsetWidth || 0;
|
|
102968
|
+
const h = htmlEl.offsetHeight || 0;
|
|
102969
|
+
return pct * Math.min(w, h);
|
|
102970
|
+
}
|
|
102971
|
+
return parseFloat(value) || 0;
|
|
102972
|
+
}
|
|
102973
|
+
const selfCs = window.getComputedStyle(node);
|
|
102974
|
+
const selfRadii = [
|
|
102975
|
+
resolveRadius(selfCs.borderTopLeftRadius, node),
|
|
102976
|
+
resolveRadius(selfCs.borderTopRightRadius, node),
|
|
102977
|
+
resolveRadius(selfCs.borderBottomRightRadius, node),
|
|
102978
|
+
resolveRadius(selfCs.borderBottomLeftRadius, node)
|
|
102979
|
+
];
|
|
102980
|
+
if (selfRadii[0] > 0 || selfRadii[1] > 0 || selfRadii[2] > 0 || selfRadii[3] > 0) {
|
|
102981
|
+
return selfRadii;
|
|
102982
|
+
}
|
|
102983
|
+
let current = node.parentElement;
|
|
102984
|
+
while (current) {
|
|
102985
|
+
const cs = window.getComputedStyle(current);
|
|
102986
|
+
if (cs.overflow !== "visible") {
|
|
102987
|
+
const tl = resolveRadius(cs.borderTopLeftRadius, current);
|
|
102988
|
+
const tr = resolveRadius(cs.borderTopRightRadius, current);
|
|
102989
|
+
const brr = resolveRadius(cs.borderBottomRightRadius, current);
|
|
102990
|
+
const bl = resolveRadius(cs.borderBottomLeftRadius, current);
|
|
102991
|
+
if (tl > 0 || tr > 0 || brr > 0 || bl > 0) {
|
|
102992
|
+
return [tl, tr, brr, bl];
|
|
102993
|
+
}
|
|
102994
|
+
}
|
|
102995
|
+
current = current.parentElement;
|
|
102996
|
+
}
|
|
102997
|
+
return [0, 0, 0, 0];
|
|
102998
|
+
}
|
|
102999
|
+
function getEffectiveOpacity(node) {
|
|
103000
|
+
let opacity = 1;
|
|
103001
|
+
let current = node;
|
|
103002
|
+
while (current) {
|
|
103003
|
+
const cs = window.getComputedStyle(current);
|
|
103004
|
+
const val = parseFloat(cs.opacity);
|
|
103005
|
+
opacity *= Number.isNaN(val) ? 1 : val;
|
|
103006
|
+
current = current.parentElement;
|
|
103007
|
+
}
|
|
103008
|
+
return opacity;
|
|
103009
|
+
}
|
|
103010
|
+
function getViewportMatrix(node) {
|
|
103011
|
+
const chain = [];
|
|
103012
|
+
let current = node;
|
|
103013
|
+
while (current instanceof HTMLElement) {
|
|
103014
|
+
chain.push(current);
|
|
103015
|
+
const next = current.offsetParent ?? current.parentElement;
|
|
103016
|
+
if (next === current) break;
|
|
103017
|
+
current = next;
|
|
103018
|
+
}
|
|
103019
|
+
let mat = new DOMMatrix();
|
|
103020
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
103021
|
+
const htmlEl = chain[i];
|
|
103022
|
+
if (!htmlEl) continue;
|
|
103023
|
+
mat = mat.translate(htmlEl.offsetLeft, htmlEl.offsetTop);
|
|
103024
|
+
const cs = window.getComputedStyle(htmlEl);
|
|
103025
|
+
if (cs.transform && cs.transform !== "none") {
|
|
103026
|
+
const origin = cs.transformOrigin.split(" ");
|
|
103027
|
+
const ox = resolveLength(origin[0] ?? "0", htmlEl.offsetWidth);
|
|
103028
|
+
const oy = resolveLength(origin[1] ?? "0", htmlEl.offsetHeight);
|
|
103029
|
+
try {
|
|
103030
|
+
const t = new DOMMatrix(cs.transform);
|
|
103031
|
+
if (Number.isFinite(t.a) && Number.isFinite(t.b) && Number.isFinite(t.c) && Number.isFinite(t.d) && Number.isFinite(t.e) && Number.isFinite(t.f)) {
|
|
103032
|
+
mat = mat.translate(ox, oy).multiply(t).translate(-ox, -oy);
|
|
103033
|
+
}
|
|
103034
|
+
} catch {
|
|
103035
|
+
}
|
|
103036
|
+
}
|
|
103037
|
+
}
|
|
103038
|
+
return mat.toString();
|
|
103039
|
+
}
|
|
103040
|
+
function resolveLength(value, basis) {
|
|
103041
|
+
if (value.endsWith("%")) {
|
|
103042
|
+
const pct = parseFloat(value) / 100;
|
|
103043
|
+
return Number.isFinite(pct) ? pct * basis : 0;
|
|
103044
|
+
}
|
|
103045
|
+
const n = parseFloat(value);
|
|
103046
|
+
return Number.isFinite(n) ? n : 0;
|
|
103047
|
+
}
|
|
103048
|
+
for (const el of elements) {
|
|
103049
|
+
const id = el.id;
|
|
103050
|
+
if (!id) continue;
|
|
103051
|
+
const rect = el.getBoundingClientRect();
|
|
103052
|
+
const style = window.getComputedStyle(el);
|
|
103053
|
+
const zIndex = getEffectiveZIndex(el);
|
|
103054
|
+
const isHdrEl = hdrSet.has(id);
|
|
103055
|
+
const opacityStartNode = isHdrEl ? el.parentElement : el;
|
|
103056
|
+
const opacity = opacityStartNode ? getEffectiveOpacity(opacityStartNode) : 1;
|
|
103057
|
+
const visible = style.visibility !== "hidden" && style.display !== "none" && rect.width > 0 && rect.height > 0;
|
|
103058
|
+
const htmlEl = el instanceof HTMLElement ? el : null;
|
|
103059
|
+
results.push({
|
|
103060
|
+
id,
|
|
103061
|
+
zIndex,
|
|
103062
|
+
x: Math.round(rect.x),
|
|
103063
|
+
y: Math.round(rect.y),
|
|
103064
|
+
width: Math.round(rect.width),
|
|
103065
|
+
height: Math.round(rect.height),
|
|
103066
|
+
layoutWidth: htmlEl?.offsetWidth || Math.round(rect.width),
|
|
103067
|
+
layoutHeight: htmlEl?.offsetHeight || Math.round(rect.height),
|
|
103068
|
+
opacity,
|
|
103069
|
+
visible,
|
|
103070
|
+
isHdr: hdrSet.has(id),
|
|
103071
|
+
// For HDR elements, use the full accumulated viewport matrix so the
|
|
103072
|
+
// affine blit can apply rotation/scale/translate properly. For DOM
|
|
103073
|
+
// elements, the element-level transform is sufficient for reference.
|
|
103074
|
+
transform: isHdrEl ? getViewportMatrix(el) : style.transform || "none",
|
|
103075
|
+
borderRadius: isHdrEl ? getEffectiveBorderRadius(el) : [0, 0, 0, 0]
|
|
103076
|
+
});
|
|
103077
|
+
}
|
|
103078
|
+
return results;
|
|
103079
|
+
}, hdrIds);
|
|
103080
|
+
}
|
|
102679
103081
|
|
|
102680
103082
|
// ../engine/src/services/audioMixer.ts
|
|
102681
103083
|
import { existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync2 } from "fs";
|
|
@@ -105786,6 +106188,1016 @@ var serve = (options, listeningListener) => {
|
|
|
105786
106188
|
return server;
|
|
105787
106189
|
};
|
|
105788
106190
|
|
|
106191
|
+
// ../engine/src/utils/alphaBlit.ts
|
|
106192
|
+
import { inflateSync } from "zlib";
|
|
106193
|
+
function paeth(a, b, c) {
|
|
106194
|
+
const p = a + b - c;
|
|
106195
|
+
const pa = Math.abs(p - a);
|
|
106196
|
+
const pb = Math.abs(p - b);
|
|
106197
|
+
const pc = Math.abs(p - c);
|
|
106198
|
+
if (pa <= pb && pa <= pc) return a;
|
|
106199
|
+
if (pb <= pc) return b;
|
|
106200
|
+
return c;
|
|
106201
|
+
}
|
|
106202
|
+
function decodePngRaw(buf, caller) {
|
|
106203
|
+
if (buf[0] !== 137 || buf[1] !== 80 || buf[2] !== 78 || buf[3] !== 71 || buf[4] !== 13 || buf[5] !== 10 || buf[6] !== 26 || buf[7] !== 10) {
|
|
106204
|
+
throw new Error(`${caller}: not a PNG file`);
|
|
106205
|
+
}
|
|
106206
|
+
let pos = 8;
|
|
106207
|
+
let width = 0;
|
|
106208
|
+
let height = 0;
|
|
106209
|
+
let bitDepth = 0;
|
|
106210
|
+
let colorType = 0;
|
|
106211
|
+
let interlace = 0;
|
|
106212
|
+
let sawIhdr = false;
|
|
106213
|
+
const idatChunks = [];
|
|
106214
|
+
while (pos + 12 <= buf.length) {
|
|
106215
|
+
const chunkLen = buf.readUInt32BE(pos);
|
|
106216
|
+
const chunkType = buf.toString("ascii", pos + 4, pos + 8);
|
|
106217
|
+
const chunkData = buf.subarray(pos + 8, pos + 8 + chunkLen);
|
|
106218
|
+
if (chunkType === "IHDR") {
|
|
106219
|
+
width = chunkData.readUInt32BE(0);
|
|
106220
|
+
height = chunkData.readUInt32BE(4);
|
|
106221
|
+
bitDepth = chunkData[8] ?? 0;
|
|
106222
|
+
colorType = chunkData[9] ?? 0;
|
|
106223
|
+
interlace = chunkData[12] ?? 0;
|
|
106224
|
+
sawIhdr = true;
|
|
106225
|
+
} else if (chunkType === "IDAT") {
|
|
106226
|
+
idatChunks.push(Buffer.from(chunkData));
|
|
106227
|
+
} else if (chunkType === "IEND") {
|
|
106228
|
+
break;
|
|
106229
|
+
}
|
|
106230
|
+
pos += 12 + chunkLen;
|
|
106231
|
+
}
|
|
106232
|
+
if (!sawIhdr) {
|
|
106233
|
+
throw new Error(`${caller}: PNG missing IHDR chunk`);
|
|
106234
|
+
}
|
|
106235
|
+
if (colorType !== 2 && colorType !== 6) {
|
|
106236
|
+
throw new Error(`${caller}: unsupported color type ${colorType} (expected 2=RGB or 6=RGBA)`);
|
|
106237
|
+
}
|
|
106238
|
+
if (interlace !== 0) {
|
|
106239
|
+
throw new Error(
|
|
106240
|
+
`${caller}: Adam7-interlaced PNGs are not supported (interlace method ${interlace})`
|
|
106241
|
+
);
|
|
106242
|
+
}
|
|
106243
|
+
const channels = colorType === 6 ? 4 : 3;
|
|
106244
|
+
const bpp = channels * (bitDepth / 8);
|
|
106245
|
+
const stride = width * bpp;
|
|
106246
|
+
const compressed = Buffer.concat(idatChunks);
|
|
106247
|
+
const decompressed = inflateSync(compressed);
|
|
106248
|
+
const rawPixels = Buffer.allocUnsafe(height * stride);
|
|
106249
|
+
const prevRow = new Uint8Array(stride);
|
|
106250
|
+
const currRow = new Uint8Array(stride);
|
|
106251
|
+
let srcPos = 0;
|
|
106252
|
+
for (let y = 0; y < height; y++) {
|
|
106253
|
+
const filterType = decompressed[srcPos++] ?? 0;
|
|
106254
|
+
const rawRow = decompressed.subarray(srcPos, srcPos + stride);
|
|
106255
|
+
srcPos += stride;
|
|
106256
|
+
switch (filterType) {
|
|
106257
|
+
case 0:
|
|
106258
|
+
currRow.set(rawRow);
|
|
106259
|
+
break;
|
|
106260
|
+
case 1:
|
|
106261
|
+
for (let x = 0; x < stride; x++) {
|
|
106262
|
+
currRow[x] = (rawRow[x] ?? 0) + (x >= bpp ? currRow[x - bpp] ?? 0 : 0) & 255;
|
|
106263
|
+
}
|
|
106264
|
+
break;
|
|
106265
|
+
case 2:
|
|
106266
|
+
for (let x = 0; x < stride; x++) {
|
|
106267
|
+
currRow[x] = (rawRow[x] ?? 0) + (prevRow[x] ?? 0) & 255;
|
|
106268
|
+
}
|
|
106269
|
+
break;
|
|
106270
|
+
case 3:
|
|
106271
|
+
for (let x = 0; x < stride; x++) {
|
|
106272
|
+
const left2 = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
106273
|
+
const up = prevRow[x] ?? 0;
|
|
106274
|
+
currRow[x] = (rawRow[x] ?? 0) + Math.floor((left2 + up) / 2) & 255;
|
|
106275
|
+
}
|
|
106276
|
+
break;
|
|
106277
|
+
case 4:
|
|
106278
|
+
for (let x = 0; x < stride; x++) {
|
|
106279
|
+
const left2 = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
106280
|
+
const up = prevRow[x] ?? 0;
|
|
106281
|
+
const upLeft = x >= bpp ? prevRow[x - bpp] ?? 0 : 0;
|
|
106282
|
+
currRow[x] = (rawRow[x] ?? 0) + paeth(left2, up, upLeft) & 255;
|
|
106283
|
+
}
|
|
106284
|
+
break;
|
|
106285
|
+
default:
|
|
106286
|
+
throw new Error(`${caller}: unknown filter type ${filterType} at row ${y}`);
|
|
106287
|
+
}
|
|
106288
|
+
rawPixels.set(currRow, y * stride);
|
|
106289
|
+
prevRow.set(currRow);
|
|
106290
|
+
}
|
|
106291
|
+
return { width, height, bitDepth, colorType, rawPixels };
|
|
106292
|
+
}
|
|
106293
|
+
function decodePng(buf) {
|
|
106294
|
+
const { width, height, bitDepth, colorType, rawPixels } = decodePngRaw(buf, "decodePng");
|
|
106295
|
+
if (bitDepth !== 8) {
|
|
106296
|
+
throw new Error(`decodePng: unsupported bit depth ${bitDepth} (expected 8)`);
|
|
106297
|
+
}
|
|
106298
|
+
const output2 = new Uint8Array(width * height * 4);
|
|
106299
|
+
if (colorType === 6) {
|
|
106300
|
+
output2.set(rawPixels);
|
|
106301
|
+
} else {
|
|
106302
|
+
for (let i = 0; i < width * height; i++) {
|
|
106303
|
+
output2[i * 4 + 0] = rawPixels[i * 3 + 0] ?? 0;
|
|
106304
|
+
output2[i * 4 + 1] = rawPixels[i * 3 + 1] ?? 0;
|
|
106305
|
+
output2[i * 4 + 2] = rawPixels[i * 3 + 2] ?? 0;
|
|
106306
|
+
output2[i * 4 + 3] = 255;
|
|
106307
|
+
}
|
|
106308
|
+
}
|
|
106309
|
+
return { width, height, data: output2 };
|
|
106310
|
+
}
|
|
106311
|
+
function decodePngToRgb48le(buf) {
|
|
106312
|
+
const { width, height, bitDepth, colorType, rawPixels } = decodePngRaw(buf, "decodePngToRgb48le");
|
|
106313
|
+
if (bitDepth !== 16) {
|
|
106314
|
+
throw new Error(`decodePngToRgb48le: unsupported bit depth ${bitDepth} (expected 16)`);
|
|
106315
|
+
}
|
|
106316
|
+
const bpp = colorType === 6 ? 8 : 6;
|
|
106317
|
+
const output2 = Buffer.allocUnsafe(width * height * 6);
|
|
106318
|
+
for (let y = 0; y < height; y++) {
|
|
106319
|
+
const dstBase = y * width * 6;
|
|
106320
|
+
const srcRowBase = y * width * bpp;
|
|
106321
|
+
for (let x = 0; x < width; x++) {
|
|
106322
|
+
const srcBase = srcRowBase + x * bpp;
|
|
106323
|
+
output2[dstBase + x * 6 + 0] = rawPixels[srcBase + 1] ?? 0;
|
|
106324
|
+
output2[dstBase + x * 6 + 1] = rawPixels[srcBase + 0] ?? 0;
|
|
106325
|
+
output2[dstBase + x * 6 + 2] = rawPixels[srcBase + 3] ?? 0;
|
|
106326
|
+
output2[dstBase + x * 6 + 3] = rawPixels[srcBase + 2] ?? 0;
|
|
106327
|
+
output2[dstBase + x * 6 + 4] = rawPixels[srcBase + 5] ?? 0;
|
|
106328
|
+
output2[dstBase + x * 6 + 5] = rawPixels[srcBase + 4] ?? 0;
|
|
106329
|
+
}
|
|
106330
|
+
}
|
|
106331
|
+
return { width, height, data: output2 };
|
|
106332
|
+
}
|
|
106333
|
+
function buildSrgbToHdrLut(transfer) {
|
|
106334
|
+
const lut = new Uint16Array(256);
|
|
106335
|
+
const hlgA = 0.17883277;
|
|
106336
|
+
const hlgB = 1 - 4 * hlgA;
|
|
106337
|
+
const hlgC = 0.5 - hlgA * Math.log(4 * hlgA);
|
|
106338
|
+
const pqM1 = 0.1593017578125;
|
|
106339
|
+
const pqM2 = 78.84375;
|
|
106340
|
+
const pqC1 = 0.8359375;
|
|
106341
|
+
const pqC2 = 18.8515625;
|
|
106342
|
+
const pqC3 = 18.6875;
|
|
106343
|
+
const pqMaxNits = 1e4;
|
|
106344
|
+
const sdrNits = 203;
|
|
106345
|
+
for (let i = 0; i < 256; i++) {
|
|
106346
|
+
const v = i / 255;
|
|
106347
|
+
const linear = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
106348
|
+
let signal;
|
|
106349
|
+
if (transfer === "hlg") {
|
|
106350
|
+
signal = linear <= 1 / 12 ? Math.sqrt(3 * linear) : hlgA * Math.log(12 * linear - hlgB) + hlgC;
|
|
106351
|
+
} else {
|
|
106352
|
+
const Lp = Math.max(0, linear * sdrNits / pqMaxNits);
|
|
106353
|
+
const Lm1 = Math.pow(Lp, pqM1);
|
|
106354
|
+
signal = Math.pow((pqC1 + pqC2 * Lm1) / (1 + pqC3 * Lm1), pqM2);
|
|
106355
|
+
}
|
|
106356
|
+
lut[i] = Math.min(65535, Math.round(signal * 65535));
|
|
106357
|
+
}
|
|
106358
|
+
return lut;
|
|
106359
|
+
}
|
|
106360
|
+
var SRGB_TO_HLG = buildSrgbToHdrLut("hlg");
|
|
106361
|
+
var SRGB_TO_PQ = buildSrgbToHdrLut("pq");
|
|
106362
|
+
function getSrgbToHdrLut(transfer) {
|
|
106363
|
+
return transfer === "pq" ? SRGB_TO_PQ : SRGB_TO_HLG;
|
|
106364
|
+
}
|
|
106365
|
+
function blitRgba8OverRgb48le(domRgba, canvas, width, height, transfer = "hlg") {
|
|
106366
|
+
const pixelCount = width * height;
|
|
106367
|
+
const lut = getSrgbToHdrLut(transfer);
|
|
106368
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
106369
|
+
const da = domRgba[i * 4 + 3] ?? 0;
|
|
106370
|
+
if (da === 0) {
|
|
106371
|
+
continue;
|
|
106372
|
+
} else if (da === 255) {
|
|
106373
|
+
const r16 = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
106374
|
+
const g16 = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
106375
|
+
const b16 = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
106376
|
+
canvas.writeUInt16LE(r16, i * 6);
|
|
106377
|
+
canvas.writeUInt16LE(g16, i * 6 + 2);
|
|
106378
|
+
canvas.writeUInt16LE(b16, i * 6 + 4);
|
|
106379
|
+
} else {
|
|
106380
|
+
const alpha = da / 255;
|
|
106381
|
+
const invAlpha = 1 - alpha;
|
|
106382
|
+
const hdrR = (canvas[i * 6 + 0] ?? 0) | (canvas[i * 6 + 1] ?? 0) << 8;
|
|
106383
|
+
const hdrG = (canvas[i * 6 + 2] ?? 0) | (canvas[i * 6 + 3] ?? 0) << 8;
|
|
106384
|
+
const hdrB = (canvas[i * 6 + 4] ?? 0) | (canvas[i * 6 + 5] ?? 0) << 8;
|
|
106385
|
+
const domR = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
106386
|
+
const domG = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
106387
|
+
const domB = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
106388
|
+
canvas.writeUInt16LE(Math.round(domR * alpha + hdrR * invAlpha), i * 6);
|
|
106389
|
+
canvas.writeUInt16LE(Math.round(domG * alpha + hdrG * invAlpha), i * 6 + 2);
|
|
106390
|
+
canvas.writeUInt16LE(Math.round(domB * alpha + hdrB * invAlpha), i * 6 + 4);
|
|
106391
|
+
}
|
|
106392
|
+
}
|
|
106393
|
+
}
|
|
106394
|
+
function cornerAlpha(px, py, cx, cy, r) {
|
|
106395
|
+
const dx = px - cx;
|
|
106396
|
+
const dy = py - cy;
|
|
106397
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
106398
|
+
if (dist > r + 0.5) return 0;
|
|
106399
|
+
if (dist > r - 0.5) return r + 0.5 - dist;
|
|
106400
|
+
return 1;
|
|
106401
|
+
}
|
|
106402
|
+
function roundedRectAlpha(px, py, w, h, radii) {
|
|
106403
|
+
const [tl, tr, br, bl] = radii;
|
|
106404
|
+
if (px < tl && py < tl) return cornerAlpha(px, py, tl, tl, tl);
|
|
106405
|
+
if (px >= w - tr && py < tr) return cornerAlpha(px, py, w - tr, tr, tr);
|
|
106406
|
+
if (px >= w - br && py >= h - br) return cornerAlpha(px, py, w - br, h - br, br);
|
|
106407
|
+
if (px < bl && py >= h - bl) return cornerAlpha(px, py, bl, h - bl, bl);
|
|
106408
|
+
return 1;
|
|
106409
|
+
}
|
|
106410
|
+
function blitRgb48leRegion(canvas, source2, dx, dy, sw, sh, canvasWidth, canvasHeight, opacity, borderRadius) {
|
|
106411
|
+
if (sw <= 0 || sh <= 0) return;
|
|
106412
|
+
const op = opacity ?? 1;
|
|
106413
|
+
const x0 = Math.max(0, dx);
|
|
106414
|
+
const y0 = Math.max(0, dy);
|
|
106415
|
+
const x1 = Math.min(canvasWidth, dx + sw);
|
|
106416
|
+
const y1 = Math.min(canvasHeight, dy + sh);
|
|
106417
|
+
if (x0 >= x1 || y0 >= y1) return;
|
|
106418
|
+
const clippedW = x1 - x0;
|
|
106419
|
+
const srcOffsetX = x0 - dx;
|
|
106420
|
+
const srcOffsetY = y0 - dy;
|
|
106421
|
+
const hasMask = borderRadius !== void 0;
|
|
106422
|
+
if (op >= 0.999 && !hasMask) {
|
|
106423
|
+
for (let y = 0; y < y1 - y0; y++) {
|
|
106424
|
+
const srcRowOff = ((srcOffsetY + y) * sw + srcOffsetX) * 6;
|
|
106425
|
+
const dstRowOff = ((y0 + y) * canvasWidth + x0) * 6;
|
|
106426
|
+
source2.copy(canvas, dstRowOff, srcRowOff, srcRowOff + clippedW * 6);
|
|
106427
|
+
}
|
|
106428
|
+
} else {
|
|
106429
|
+
for (let y = 0; y < y1 - y0; y++) {
|
|
106430
|
+
for (let x = 0; x < clippedW; x++) {
|
|
106431
|
+
let effectiveOp = op;
|
|
106432
|
+
if (hasMask) {
|
|
106433
|
+
const ma = roundedRectAlpha(srcOffsetX + x, srcOffsetY + y, sw, sh, borderRadius);
|
|
106434
|
+
if (ma <= 0) continue;
|
|
106435
|
+
effectiveOp *= ma;
|
|
106436
|
+
}
|
|
106437
|
+
const srcOff = ((srcOffsetY + y) * sw + srcOffsetX + x) * 6;
|
|
106438
|
+
const dstOff = ((y0 + y) * canvasWidth + x0 + x) * 6;
|
|
106439
|
+
if (effectiveOp >= 0.999) {
|
|
106440
|
+
source2.copy(canvas, dstOff, srcOff, srcOff + 6);
|
|
106441
|
+
} else {
|
|
106442
|
+
const invEff = 1 - effectiveOp;
|
|
106443
|
+
const sr = source2.readUInt16LE(srcOff);
|
|
106444
|
+
const sg = source2.readUInt16LE(srcOff + 2);
|
|
106445
|
+
const sb = source2.readUInt16LE(srcOff + 4);
|
|
106446
|
+
const dr = canvas.readUInt16LE(dstOff);
|
|
106447
|
+
const dg = canvas.readUInt16LE(dstOff + 2);
|
|
106448
|
+
const db = canvas.readUInt16LE(dstOff + 4);
|
|
106449
|
+
canvas.writeUInt16LE(Math.round(sr * effectiveOp + dr * invEff), dstOff);
|
|
106450
|
+
canvas.writeUInt16LE(Math.round(sg * effectiveOp + dg * invEff), dstOff + 2);
|
|
106451
|
+
canvas.writeUInt16LE(Math.round(sb * effectiveOp + db * invEff), dstOff + 4);
|
|
106452
|
+
}
|
|
106453
|
+
}
|
|
106454
|
+
}
|
|
106455
|
+
}
|
|
106456
|
+
}
|
|
106457
|
+
function blitRgb48leAffine(canvas, source2, matrix, srcW, srcH, canvasW, canvasH, opacity, borderRadius) {
|
|
106458
|
+
const a = matrix[0];
|
|
106459
|
+
const b = matrix[1];
|
|
106460
|
+
const c = matrix[2];
|
|
106461
|
+
const d = matrix[3];
|
|
106462
|
+
const tx = matrix[4];
|
|
106463
|
+
const ty = matrix[5];
|
|
106464
|
+
if (a === void 0 || b === void 0 || c === void 0 || d === void 0 || tx === void 0 || ty === void 0)
|
|
106465
|
+
return;
|
|
106466
|
+
const det = a * d - b * c;
|
|
106467
|
+
if (Math.abs(det) < 1e-10) return;
|
|
106468
|
+
const invA = d / det;
|
|
106469
|
+
const invB = -b / det;
|
|
106470
|
+
const invC = -c / det;
|
|
106471
|
+
const invD = a / det;
|
|
106472
|
+
const invTx = -(invA * tx + invC * ty);
|
|
106473
|
+
const invTy = -(invB * tx + invD * ty);
|
|
106474
|
+
const op = opacity ?? 1;
|
|
106475
|
+
const hasMask = borderRadius !== void 0;
|
|
106476
|
+
const corners = [
|
|
106477
|
+
[tx, ty],
|
|
106478
|
+
[a * srcW + tx, b * srcW + ty],
|
|
106479
|
+
[c * srcH + tx, d * srcH + ty],
|
|
106480
|
+
[a * srcW + c * srcH + tx, b * srcW + d * srcH + ty]
|
|
106481
|
+
];
|
|
106482
|
+
let minX = canvasW, maxX = 0, minY = canvasH, maxY = 0;
|
|
106483
|
+
for (const corner of corners) {
|
|
106484
|
+
const cx = corner[0] ?? 0;
|
|
106485
|
+
const cy = corner[1] ?? 0;
|
|
106486
|
+
if (cx < minX) minX = cx;
|
|
106487
|
+
if (cx > maxX) maxX = cx;
|
|
106488
|
+
if (cy < minY) minY = cy;
|
|
106489
|
+
if (cy > maxY) maxY = cy;
|
|
106490
|
+
}
|
|
106491
|
+
const startX = Math.max(0, Math.floor(minX));
|
|
106492
|
+
const endX = Math.min(canvasW, Math.ceil(maxX));
|
|
106493
|
+
const startY = Math.max(0, Math.floor(minY));
|
|
106494
|
+
const endY = Math.min(canvasH, Math.ceil(maxY));
|
|
106495
|
+
for (let dy = startY; dy < endY; dy++) {
|
|
106496
|
+
for (let dx = startX; dx < endX; dx++) {
|
|
106497
|
+
const sx = invA * dx + invC * dy + invTx;
|
|
106498
|
+
const sy = invB * dx + invD * dy + invTy;
|
|
106499
|
+
if (sx < 0 || sy < 0 || sx >= srcW || sy >= srcH) continue;
|
|
106500
|
+
let effectiveOp = op;
|
|
106501
|
+
if (hasMask) {
|
|
106502
|
+
const ma = roundedRectAlpha(sx, sy, srcW, srcH, borderRadius);
|
|
106503
|
+
if (ma <= 0) continue;
|
|
106504
|
+
effectiveOp *= ma;
|
|
106505
|
+
}
|
|
106506
|
+
const x0 = Math.floor(sx);
|
|
106507
|
+
const y0 = Math.floor(sy);
|
|
106508
|
+
const fx = sx - x0;
|
|
106509
|
+
const fy = sy - y0;
|
|
106510
|
+
const x1 = Math.min(x0 + 1, srcW - 1);
|
|
106511
|
+
const y1 = Math.min(y0 + 1, srcH - 1);
|
|
106512
|
+
const off00 = (y0 * srcW + x0) * 6;
|
|
106513
|
+
const off10 = (y0 * srcW + x1) * 6;
|
|
106514
|
+
const off01 = (y1 * srcW + x0) * 6;
|
|
106515
|
+
const off11 = (y1 * srcW + x1) * 6;
|
|
106516
|
+
const w00 = (1 - fx) * (1 - fy);
|
|
106517
|
+
const w10 = fx * (1 - fy);
|
|
106518
|
+
const w01 = (1 - fx) * fy;
|
|
106519
|
+
const w11 = fx * fy;
|
|
106520
|
+
const sr = source2.readUInt16LE(off00) * w00 + source2.readUInt16LE(off10) * w10 + source2.readUInt16LE(off01) * w01 + source2.readUInt16LE(off11) * w11;
|
|
106521
|
+
const sg = source2.readUInt16LE(off00 + 2) * w00 + source2.readUInt16LE(off10 + 2) * w10 + source2.readUInt16LE(off01 + 2) * w01 + source2.readUInt16LE(off11 + 2) * w11;
|
|
106522
|
+
const sb = source2.readUInt16LE(off00 + 4) * w00 + source2.readUInt16LE(off10 + 4) * w10 + source2.readUInt16LE(off01 + 4) * w01 + source2.readUInt16LE(off11 + 4) * w11;
|
|
106523
|
+
const dstOff = (dy * canvasW + dx) * 6;
|
|
106524
|
+
if (effectiveOp >= 0.999) {
|
|
106525
|
+
canvas.writeUInt16LE(Math.round(sr), dstOff);
|
|
106526
|
+
canvas.writeUInt16LE(Math.round(sg), dstOff + 2);
|
|
106527
|
+
canvas.writeUInt16LE(Math.round(sb), dstOff + 4);
|
|
106528
|
+
} else {
|
|
106529
|
+
const invEff = 1 - effectiveOp;
|
|
106530
|
+
const dr = canvas.readUInt16LE(dstOff);
|
|
106531
|
+
const dg = canvas.readUInt16LE(dstOff + 2);
|
|
106532
|
+
const db = canvas.readUInt16LE(dstOff + 4);
|
|
106533
|
+
canvas.writeUInt16LE(Math.round(sr * effectiveOp + dr * invEff), dstOff);
|
|
106534
|
+
canvas.writeUInt16LE(Math.round(sg * effectiveOp + dg * invEff), dstOff + 2);
|
|
106535
|
+
canvas.writeUInt16LE(Math.round(sb * effectiveOp + db * invEff), dstOff + 4);
|
|
106536
|
+
}
|
|
106537
|
+
}
|
|
106538
|
+
}
|
|
106539
|
+
}
|
|
106540
|
+
function parseTransformMatrix(css) {
|
|
106541
|
+
if (!css || css === "none") return null;
|
|
106542
|
+
const match2 = css.match(
|
|
106543
|
+
/^matrix\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,)]+)\s*\)$/
|
|
106544
|
+
);
|
|
106545
|
+
if (!match2) return null;
|
|
106546
|
+
const values = match2.slice(1, 7).map(Number);
|
|
106547
|
+
if (!values.every(Number.isFinite)) return null;
|
|
106548
|
+
return values;
|
|
106549
|
+
}
|
|
106550
|
+
|
|
106551
|
+
// ../engine/src/utils/layerCompositor.ts
|
|
106552
|
+
function groupIntoLayers(elements) {
|
|
106553
|
+
const sorted = [...elements].sort((a, b) => a.zIndex - b.zIndex);
|
|
106554
|
+
const layers = [];
|
|
106555
|
+
for (const el of sorted) {
|
|
106556
|
+
if (el.isHdr) {
|
|
106557
|
+
layers.push({ type: "hdr", element: el });
|
|
106558
|
+
} else {
|
|
106559
|
+
const last2 = layers[layers.length - 1];
|
|
106560
|
+
if (last2 && last2.type === "dom") {
|
|
106561
|
+
last2.elementIds.push(el.id);
|
|
106562
|
+
} else {
|
|
106563
|
+
layers.push({ type: "dom", elementIds: [el.id] });
|
|
106564
|
+
}
|
|
106565
|
+
}
|
|
106566
|
+
}
|
|
106567
|
+
return layers;
|
|
106568
|
+
}
|
|
106569
|
+
|
|
106570
|
+
// ../engine/src/utils/shaderTransitions.ts
|
|
106571
|
+
var PQ_M1 = 0.1593017578125;
|
|
106572
|
+
var PQ_M2 = 78.84375;
|
|
106573
|
+
var PQ_C1 = 0.8359375;
|
|
106574
|
+
var PQ_C2 = 18.8515625;
|
|
106575
|
+
var PQ_C3 = 18.6875;
|
|
106576
|
+
function pqEotf(signal) {
|
|
106577
|
+
const sp = Math.pow(Math.max(0, signal), 1 / PQ_M2);
|
|
106578
|
+
const num = Math.max(sp - PQ_C1, 0);
|
|
106579
|
+
const den = PQ_C2 - PQ_C3 * sp;
|
|
106580
|
+
return den > 0 ? Math.pow(num / den, 1 / PQ_M1) : 0;
|
|
106581
|
+
}
|
|
106582
|
+
function pqOetf(linear) {
|
|
106583
|
+
const lp = Math.pow(Math.max(0, linear), PQ_M1);
|
|
106584
|
+
return Math.pow((PQ_C1 + PQ_C2 * lp) / (1 + PQ_C3 * lp), PQ_M2);
|
|
106585
|
+
}
|
|
106586
|
+
function hlgEotf(signal) {
|
|
106587
|
+
const a = 0.17883277;
|
|
106588
|
+
const b = 1 - 4 * a;
|
|
106589
|
+
const c = 0.5 - a * Math.log(4 * a);
|
|
106590
|
+
if (signal <= 0.5) {
|
|
106591
|
+
return signal * signal / 3;
|
|
106592
|
+
}
|
|
106593
|
+
return (Math.exp((signal - c) / a) + b) / 12;
|
|
106594
|
+
}
|
|
106595
|
+
function hlgOetf(linear) {
|
|
106596
|
+
const a = 0.17883277;
|
|
106597
|
+
const b = 1 - 4 * a;
|
|
106598
|
+
const c = 0.5 - a * Math.log(4 * a);
|
|
106599
|
+
if (linear <= 1 / 12) {
|
|
106600
|
+
return Math.sqrt(3 * linear);
|
|
106601
|
+
}
|
|
106602
|
+
return a * Math.log(12 * linear - b) + c;
|
|
106603
|
+
}
|
|
106604
|
+
function buildLut(fn) {
|
|
106605
|
+
const lut = new Uint16Array(65536);
|
|
106606
|
+
for (let i = 0; i < 65536; i++) {
|
|
106607
|
+
lut[i] = Math.round(fn(i / 65535) * 65535);
|
|
106608
|
+
}
|
|
106609
|
+
return lut;
|
|
106610
|
+
}
|
|
106611
|
+
var HLG_OOTF_LW = 1e3;
|
|
106612
|
+
var HLG_OOTF_GAMMA = 1.2 * Math.pow(1.111, Math.log2(HLG_OOTF_LW / 1e3));
|
|
106613
|
+
function hlgSceneToPqDisplay(sceneLinear) {
|
|
106614
|
+
const displayNits = HLG_OOTF_LW * Math.pow(Math.max(0, sceneLinear), HLG_OOTF_GAMMA);
|
|
106615
|
+
return displayNits / 1e4;
|
|
106616
|
+
}
|
|
106617
|
+
function pqDisplayToHlgScene(displayNormalized) {
|
|
106618
|
+
const displayNits = displayNormalized * 1e4;
|
|
106619
|
+
return Math.pow(Math.max(0, displayNits / HLG_OOTF_LW), 1 / HLG_OOTF_GAMMA);
|
|
106620
|
+
}
|
|
106621
|
+
var hlgToPqLut = null;
|
|
106622
|
+
var pqToHlgLut = null;
|
|
106623
|
+
function getHlgToPqLut() {
|
|
106624
|
+
if (!hlgToPqLut) hlgToPqLut = buildLut((v) => pqOetf(hlgSceneToPqDisplay(hlgEotf(v))));
|
|
106625
|
+
return hlgToPqLut;
|
|
106626
|
+
}
|
|
106627
|
+
function getPqToHlgLut() {
|
|
106628
|
+
if (!pqToHlgLut) pqToHlgLut = buildLut((v) => hlgOetf(pqDisplayToHlgScene(pqEotf(v))));
|
|
106629
|
+
return pqToHlgLut;
|
|
106630
|
+
}
|
|
106631
|
+
function convertTransfer(buf, from2, to) {
|
|
106632
|
+
if (from2 === to) return;
|
|
106633
|
+
const lut = from2 === "hlg" ? getHlgToPqLut() : getPqToHlgLut();
|
|
106634
|
+
const len = buf.length / 2;
|
|
106635
|
+
for (let i = 0; i < len; i++) {
|
|
106636
|
+
const off = i * 2;
|
|
106637
|
+
buf.writeUInt16LE(lut[buf.readUInt16LE(off)] ?? 0, off);
|
|
106638
|
+
}
|
|
106639
|
+
}
|
|
106640
|
+
function sampleRgb48le(buf, u, v, w, h) {
|
|
106641
|
+
const uc = Math.max(0, Math.min(1, u));
|
|
106642
|
+
const vc = Math.max(0, Math.min(1, v));
|
|
106643
|
+
const sx = uc * (w - 1);
|
|
106644
|
+
const sy = vc * (h - 1);
|
|
106645
|
+
const x0 = Math.floor(sx);
|
|
106646
|
+
const y0 = Math.floor(sy);
|
|
106647
|
+
const x1 = Math.min(x0 + 1, w - 1);
|
|
106648
|
+
const y1 = Math.min(y0 + 1, h - 1);
|
|
106649
|
+
const fx = sx - x0;
|
|
106650
|
+
const fy = sy - y0;
|
|
106651
|
+
const w00 = (1 - fx) * (1 - fy);
|
|
106652
|
+
const w10 = fx * (1 - fy);
|
|
106653
|
+
const w01 = (1 - fx) * fy;
|
|
106654
|
+
const w11 = fx * fy;
|
|
106655
|
+
const off00 = (y0 * w + x0) * 6;
|
|
106656
|
+
const off10 = (y0 * w + x1) * 6;
|
|
106657
|
+
const off01 = (y1 * w + x0) * 6;
|
|
106658
|
+
const off11 = (y1 * w + x1) * 6;
|
|
106659
|
+
const r = Math.round(
|
|
106660
|
+
buf.readUInt16LE(off00) * w00 + buf.readUInt16LE(off10) * w10 + buf.readUInt16LE(off01) * w01 + buf.readUInt16LE(off11) * w11
|
|
106661
|
+
);
|
|
106662
|
+
const g = Math.round(
|
|
106663
|
+
buf.readUInt16LE(off00 + 2) * w00 + buf.readUInt16LE(off10 + 2) * w10 + buf.readUInt16LE(off01 + 2) * w01 + buf.readUInt16LE(off11 + 2) * w11
|
|
106664
|
+
);
|
|
106665
|
+
const b = Math.round(
|
|
106666
|
+
buf.readUInt16LE(off00 + 4) * w00 + buf.readUInt16LE(off10 + 4) * w10 + buf.readUInt16LE(off01 + 4) * w01 + buf.readUInt16LE(off11 + 4) * w11
|
|
106667
|
+
);
|
|
106668
|
+
return [r, g, b];
|
|
106669
|
+
}
|
|
106670
|
+
function mix16(a, b, t) {
|
|
106671
|
+
return Math.round(a * (1 - t) + b * t);
|
|
106672
|
+
}
|
|
106673
|
+
function clamp16(v) {
|
|
106674
|
+
return Math.max(0, Math.min(65535, v));
|
|
106675
|
+
}
|
|
106676
|
+
function smoothstep(edge0, edge1, x) {
|
|
106677
|
+
const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
|
|
106678
|
+
return t * t * (3 - 2 * t);
|
|
106679
|
+
}
|
|
106680
|
+
function hash(x, y) {
|
|
106681
|
+
return (Math.sin(x * 127.1 + y * 311.7) * 43758.5453 % 1 + 1) % 1;
|
|
106682
|
+
}
|
|
106683
|
+
function vnoise(px, py) {
|
|
106684
|
+
const ix = Math.floor(px);
|
|
106685
|
+
const iy = Math.floor(py);
|
|
106686
|
+
let fx = px - ix;
|
|
106687
|
+
let fy = py - iy;
|
|
106688
|
+
fx = fx * fx * fx * (fx * (fx * 6 - 15) + 10);
|
|
106689
|
+
fy = fy * fy * fy * (fy * (fy * 6 - 15) + 10);
|
|
106690
|
+
const h00 = hash(ix, iy);
|
|
106691
|
+
const h10 = hash(ix + 1, iy);
|
|
106692
|
+
const h01 = hash(ix, iy + 1);
|
|
106693
|
+
const h11 = hash(ix + 1, iy + 1);
|
|
106694
|
+
return h00 * (1 - fx) * (1 - fy) + h10 * fx * (1 - fy) + h01 * (1 - fx) * fy + h11 * fx * fy;
|
|
106695
|
+
}
|
|
106696
|
+
var ROT_A = 0.8;
|
|
106697
|
+
var ROT_B = 0.6;
|
|
106698
|
+
function fbm(px, py) {
|
|
106699
|
+
let value = 0;
|
|
106700
|
+
let amplitude = 0.5;
|
|
106701
|
+
let x = px;
|
|
106702
|
+
let y = py;
|
|
106703
|
+
for (let i = 0; i < 5; i++) {
|
|
106704
|
+
value += amplitude * vnoise(x, y);
|
|
106705
|
+
const nx = ROT_A * x - ROT_B * y;
|
|
106706
|
+
const ny = ROT_B * x + ROT_A * y;
|
|
106707
|
+
x = nx * 2.02;
|
|
106708
|
+
y = ny * 2.02;
|
|
106709
|
+
amplitude *= 0.5;
|
|
106710
|
+
}
|
|
106711
|
+
return value;
|
|
106712
|
+
}
|
|
106713
|
+
var TRANSITIONS = {};
|
|
106714
|
+
var crossfade = (from2, to, out, w, h, p) => {
|
|
106715
|
+
const inv = 1 - p;
|
|
106716
|
+
for (let i = 0; i < w * h; i++) {
|
|
106717
|
+
const o = i * 6;
|
|
106718
|
+
out.writeUInt16LE(Math.round(from2.readUInt16LE(o) * inv + to.readUInt16LE(o) * p), o);
|
|
106719
|
+
out.writeUInt16LE(
|
|
106720
|
+
Math.round(from2.readUInt16LE(o + 2) * inv + to.readUInt16LE(o + 2) * p),
|
|
106721
|
+
o + 2
|
|
106722
|
+
);
|
|
106723
|
+
out.writeUInt16LE(
|
|
106724
|
+
Math.round(from2.readUInt16LE(o + 4) * inv + to.readUInt16LE(o + 4) * p),
|
|
106725
|
+
o + 4
|
|
106726
|
+
);
|
|
106727
|
+
}
|
|
106728
|
+
};
|
|
106729
|
+
TRANSITIONS["crossfade"] = crossfade;
|
|
106730
|
+
var flashThroughWhite = (from2, to, out, w, h, p) => {
|
|
106731
|
+
const toWhite = smoothstep(0, 0.45, p);
|
|
106732
|
+
const fromWhite = 1 - smoothstep(0.5, 1, p);
|
|
106733
|
+
const blend = smoothstep(0.35, 0.65, p);
|
|
106734
|
+
for (let i = 0; i < w * h; i++) {
|
|
106735
|
+
const o = i * 6;
|
|
106736
|
+
const fromR = mix16(from2.readUInt16LE(o), 65535, toWhite);
|
|
106737
|
+
const fromG = mix16(from2.readUInt16LE(o + 2), 65535, toWhite);
|
|
106738
|
+
const fromB = mix16(from2.readUInt16LE(o + 4), 65535, toWhite);
|
|
106739
|
+
const toR = mix16(to.readUInt16LE(o), 65535, fromWhite);
|
|
106740
|
+
const toG = mix16(to.readUInt16LE(o + 2), 65535, fromWhite);
|
|
106741
|
+
const toB = mix16(to.readUInt16LE(o + 4), 65535, fromWhite);
|
|
106742
|
+
out.writeUInt16LE(mix16(fromR, toR, blend), o);
|
|
106743
|
+
out.writeUInt16LE(mix16(fromG, toG, blend), o + 2);
|
|
106744
|
+
out.writeUInt16LE(mix16(fromB, toB, blend), o + 4);
|
|
106745
|
+
}
|
|
106746
|
+
};
|
|
106747
|
+
TRANSITIONS["flash-through-white"] = flashThroughWhite;
|
|
106748
|
+
var chromaticSplit = (from2, to, out, w, h, p) => {
|
|
106749
|
+
for (let i = 0; i < w * h; i++) {
|
|
106750
|
+
const ux = i % w / w;
|
|
106751
|
+
const uy = Math.floor(i / w) / h;
|
|
106752
|
+
const o = i * 6;
|
|
106753
|
+
const cx = ux - 0.5;
|
|
106754
|
+
const cy = uy - 0.5;
|
|
106755
|
+
const fromShift = p * 0.06;
|
|
106756
|
+
const fr = sampleRgb48le(from2, ux + cx * fromShift, uy + cy * fromShift, w, h)[0];
|
|
106757
|
+
const fg = sampleRgb48le(from2, ux, uy, w, h)[1];
|
|
106758
|
+
const fb = sampleRgb48le(from2, ux - cx * fromShift, uy - cy * fromShift, w, h)[2];
|
|
106759
|
+
const toShift = (1 - p) * 0.06;
|
|
106760
|
+
const tr = sampleRgb48le(to, ux - cx * toShift, uy - cy * toShift, w, h)[0];
|
|
106761
|
+
const tg = sampleRgb48le(to, ux, uy, w, h)[1];
|
|
106762
|
+
const tb = sampleRgb48le(to, ux + cx * toShift, uy + cy * toShift, w, h)[2];
|
|
106763
|
+
out.writeUInt16LE(clamp16(mix16(fr, tr, p)), o);
|
|
106764
|
+
out.writeUInt16LE(clamp16(mix16(fg, tg, p)), o + 2);
|
|
106765
|
+
out.writeUInt16LE(clamp16(mix16(fb, tb, p)), o + 4);
|
|
106766
|
+
}
|
|
106767
|
+
};
|
|
106768
|
+
TRANSITIONS["chromatic-split"] = chromaticSplit;
|
|
106769
|
+
var sdfIris = (from2, to, out, w, h, p) => {
|
|
106770
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
106771
|
+
for (let i = 0; i < w * h; i++) {
|
|
106772
|
+
const ux = i % w / w;
|
|
106773
|
+
const uy = Math.floor(i / w) / h;
|
|
106774
|
+
const o = i * 6;
|
|
106775
|
+
const ax = (ux - 0.5) * (w / h);
|
|
106776
|
+
const ay = uy - 0.5;
|
|
106777
|
+
const d = Math.sqrt(ax * ax + ay * ay);
|
|
106778
|
+
const radius = p * 1.2;
|
|
106779
|
+
const fw = 3e-3;
|
|
106780
|
+
const edge = smoothstep(radius + fw, radius - fw, d);
|
|
106781
|
+
const ring1 = Math.exp(-Math.abs(d - radius) * 25);
|
|
106782
|
+
const ring2 = Math.exp(-Math.abs(d - radius + 0.04) * 20) * 0.5;
|
|
106783
|
+
const ring3 = Math.exp(-Math.abs(d - radius + 0.08) * 15) * 0.25;
|
|
106784
|
+
const glow = (ring1 + ring2 + ring3) * p * (1 - p) * 4;
|
|
106785
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, ux, uy, w, h);
|
|
106786
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
106787
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, edge) + accentBright[0] * glow * 0.6), o);
|
|
106788
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, edge) + accentBright[1] * glow * 0.6), o + 2);
|
|
106789
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, edge) + accentBright[2] * glow * 0.6), o + 4);
|
|
106790
|
+
}
|
|
106791
|
+
};
|
|
106792
|
+
TRANSITIONS["sdf-iris"] = sdfIris;
|
|
106793
|
+
function glitchRand(x, y) {
|
|
106794
|
+
return (Math.sin(x * 12.9898 + y * 78.233) * 43758.5453 % 1 + 1) % 1;
|
|
106795
|
+
}
|
|
106796
|
+
var glitch = (from2, to, out, w, h, p) => {
|
|
106797
|
+
const intensity = p * (1 - p) * 4;
|
|
106798
|
+
for (let i = 0; i < w * h; i++) {
|
|
106799
|
+
const ux = i % w / w;
|
|
106800
|
+
const uy = Math.floor(i / w) / h;
|
|
106801
|
+
const o = i * 6;
|
|
106802
|
+
const lineY = Math.floor(uy * 60) / 60;
|
|
106803
|
+
const lineDisp = (glitchRand(lineY, Math.floor(p * 17)) - 0.5) * 0.18 * intensity;
|
|
106804
|
+
const blockX = Math.floor(ux * 12);
|
|
106805
|
+
const blockY = Math.floor(uy * 8);
|
|
106806
|
+
const progressStep = Math.floor(p * 11);
|
|
106807
|
+
const br = glitchRand(blockX + progressStep, blockY + progressStep);
|
|
106808
|
+
const ba = (br >= 0.83 ? 1 : 0) * intensity;
|
|
106809
|
+
const bdx = (glitchRand(blockX * 2.1, blockY * 2.1) - 0.5) * 0.35 * ba;
|
|
106810
|
+
const bdy = (glitchRand(blockX * 3.7, blockY * 3.7) - 0.5) * 0.35 * ba;
|
|
106811
|
+
const uvx = Math.max(0, Math.min(1, ux + lineDisp + bdx));
|
|
106812
|
+
const uvy = Math.max(0, Math.min(1, uy + bdy));
|
|
106813
|
+
const shift = intensity * 0.035;
|
|
106814
|
+
const r = sampleRgb48le(from2, uvx + shift, uvy, w, h)[0];
|
|
106815
|
+
const g = sampleRgb48le(from2, uvx, uvy, w, h)[1];
|
|
106816
|
+
const b = sampleRgb48le(from2, uvx - shift, uvy, w, h)[2];
|
|
106817
|
+
let cr = r / 65535;
|
|
106818
|
+
let cg = g / 65535;
|
|
106819
|
+
let cb = b / 65535;
|
|
106820
|
+
const scanline = (uy * h * 0.5 % 1 + 1) % 1 >= 0.5 ? 0.05 * intensity : 0;
|
|
106821
|
+
cr -= scanline;
|
|
106822
|
+
cg -= scanline;
|
|
106823
|
+
cb -= scanline;
|
|
106824
|
+
const flicker = 1 + (glitchRand(Math.floor(p * 23), 0) - 0.5) * 0.3 * intensity;
|
|
106825
|
+
cr *= flicker;
|
|
106826
|
+
cg *= flicker;
|
|
106827
|
+
cb *= flicker;
|
|
106828
|
+
const levels = 256 - (256 - 8) * (intensity * 0.5);
|
|
106829
|
+
cr = Math.floor(cr * levels) / levels;
|
|
106830
|
+
cg = Math.floor(cg * levels) / levels;
|
|
106831
|
+
cb = Math.floor(cb * levels) / levels;
|
|
106832
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
106833
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cr * 65535), toR, p)), o);
|
|
106834
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cg * 65535), toG, p)), o + 2);
|
|
106835
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cb * 65535), toB, p)), o + 4);
|
|
106836
|
+
}
|
|
106837
|
+
};
|
|
106838
|
+
TRANSITIONS["glitch"] = glitch;
|
|
106839
|
+
function aces(x) {
|
|
106840
|
+
return Math.max(0, Math.min(1, x * (2.51 * x + 0.03) / (x * (2.43 * x + 0.59) + 0.14)));
|
|
106841
|
+
}
|
|
106842
|
+
var lightLeak = (from2, to, out, w, h, p) => {
|
|
106843
|
+
const accent = [5e4 / 65535, 25e3 / 65535, 5e3 / 65535];
|
|
106844
|
+
const accentBright = [65535 / 65535, 55e3 / 65535, 35e3 / 65535];
|
|
106845
|
+
const lpx = 1.3;
|
|
106846
|
+
const lpy = -0.2;
|
|
106847
|
+
for (let i = 0; i < w * h; i++) {
|
|
106848
|
+
const ux = i % w / w;
|
|
106849
|
+
const uy = Math.floor(i / w) / h;
|
|
106850
|
+
const o = i * 6;
|
|
106851
|
+
const dx = ux - lpx;
|
|
106852
|
+
const dy = uy - lpy;
|
|
106853
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
106854
|
+
const leak = Math.max(0, Math.min(1, Math.exp(-dist * 1.8) * p * 4));
|
|
106855
|
+
const warmR = accent[0] + (accentBright[0] - accent[0]) * dist * 0.7;
|
|
106856
|
+
const warmG = accent[1] + (accentBright[1] - accent[1]) * dist * 0.7;
|
|
106857
|
+
const warmB = accent[2] + (accentBright[2] - accent[2]) * dist * 0.7;
|
|
106858
|
+
const flare = Math.exp(-Math.abs(uy - (-0.2 + ux * 0.3)) * 15) * leak * 0.3;
|
|
106859
|
+
const [fr, fg, fb] = sampleRgb48le(from2, ux, uy, w, h);
|
|
106860
|
+
const fromR = fr / 65535;
|
|
106861
|
+
const fromG = fg / 65535;
|
|
106862
|
+
const fromB = fb / 65535;
|
|
106863
|
+
const overR = aces(fromR + warmR * leak * 3 + accentBright[0] * flare);
|
|
106864
|
+
const overG = aces(fromG + warmG * leak * 3 + accentBright[1] * flare);
|
|
106865
|
+
const overB = aces(fromB + warmB * leak * 3 + accentBright[2] * flare);
|
|
106866
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
106867
|
+
const blend = smoothstep(0.15, 0.85, p);
|
|
106868
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overR * 65535), toR, blend)), o);
|
|
106869
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overG * 65535), toG, blend)), o + 2);
|
|
106870
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overB * 65535), toB, blend)), o + 4);
|
|
106871
|
+
}
|
|
106872
|
+
};
|
|
106873
|
+
TRANSITIONS["light-leak"] = lightLeak;
|
|
106874
|
+
var crossWarpMorph = (from2, to, out, w, h, p) => {
|
|
106875
|
+
for (let i = 0; i < w * h; i++) {
|
|
106876
|
+
const ux = i % w / w;
|
|
106877
|
+
const uy = Math.floor(i / w) / h;
|
|
106878
|
+
const o = i * 6;
|
|
106879
|
+
const dispX = fbm(ux * 3, uy * 3) - 0.5;
|
|
106880
|
+
const dispY = fbm(ux * 3 + 7.3, uy * 3 + 3.7) - 0.5;
|
|
106881
|
+
const fromUx = Math.max(0, Math.min(1, ux + dispX * p * 0.5));
|
|
106882
|
+
const fromUy = Math.max(0, Math.min(1, uy + dispY * p * 0.5));
|
|
106883
|
+
const toUx = Math.max(0, Math.min(1, ux - dispX * (1 - p) * 0.5));
|
|
106884
|
+
const toUy = Math.max(0, Math.min(1, uy - dispY * (1 - p) * 0.5));
|
|
106885
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
106886
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
106887
|
+
const n = fbm(ux * 4 + 3.1, uy * 4 + 1.7);
|
|
106888
|
+
const blend = smoothstep(0.4, 0.6, n + p * 1.2 - 0.6);
|
|
106889
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, blend)), o);
|
|
106890
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, blend)), o + 2);
|
|
106891
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, blend)), o + 4);
|
|
106892
|
+
}
|
|
106893
|
+
};
|
|
106894
|
+
TRANSITIONS["cross-warp-morph"] = crossWarpMorph;
|
|
106895
|
+
var whipPan = (from2, to, out, w, h, p) => {
|
|
106896
|
+
const fromOff = p * 1.5;
|
|
106897
|
+
const toOff = (1 - p) * 1.5;
|
|
106898
|
+
for (let i = 0; i < w * h; i++) {
|
|
106899
|
+
const ux = i % w / w;
|
|
106900
|
+
const uy = Math.floor(i / w) / h;
|
|
106901
|
+
const o = i * 6;
|
|
106902
|
+
let fromR = 0, fromG = 0, fromB = 0;
|
|
106903
|
+
for (let s = 0; s < 10; s++) {
|
|
106904
|
+
const f = s / 10;
|
|
106905
|
+
const fuv = Math.max(0, Math.min(1, ux + fromOff + p * 0.08 * f));
|
|
106906
|
+
const [r, g, b] = sampleRgb48le(from2, fuv, uy, w, h);
|
|
106907
|
+
fromR += r;
|
|
106908
|
+
fromG += g;
|
|
106909
|
+
fromB += b;
|
|
106910
|
+
}
|
|
106911
|
+
fromR /= 10;
|
|
106912
|
+
fromG /= 10;
|
|
106913
|
+
fromB /= 10;
|
|
106914
|
+
let toR = 0, toG = 0, toB = 0;
|
|
106915
|
+
for (let s = 0; s < 10; s++) {
|
|
106916
|
+
const f = s / 10;
|
|
106917
|
+
const tuv = Math.max(0, Math.min(1, ux - toOff - (1 - p) * 0.08 * f));
|
|
106918
|
+
const [r, g, b] = sampleRgb48le(to, tuv, uy, w, h);
|
|
106919
|
+
toR += r;
|
|
106920
|
+
toG += g;
|
|
106921
|
+
toB += b;
|
|
106922
|
+
}
|
|
106923
|
+
toR /= 10;
|
|
106924
|
+
toG /= 10;
|
|
106925
|
+
toB /= 10;
|
|
106926
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromR), Math.round(toR), p)), o);
|
|
106927
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromG), Math.round(toG), p)), o + 2);
|
|
106928
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromB), Math.round(toB), p)), o + 4);
|
|
106929
|
+
}
|
|
106930
|
+
};
|
|
106931
|
+
TRANSITIONS["whip-pan"] = whipPan;
|
|
106932
|
+
var cinematicZoom = (from2, to, out, w, h, p) => {
|
|
106933
|
+
const fromS = p * 0.08;
|
|
106934
|
+
const toS = (1 - p) * 0.06;
|
|
106935
|
+
for (let i = 0; i < w * h; i++) {
|
|
106936
|
+
const ux = i % w / w;
|
|
106937
|
+
const uy = Math.floor(i / w) / h;
|
|
106938
|
+
const o = i * 6;
|
|
106939
|
+
const dx = ux - 0.5;
|
|
106940
|
+
const dy = uy - 0.5;
|
|
106941
|
+
let fr = 0, fg = 0, fb = 0;
|
|
106942
|
+
for (let s = 0; s < 12; s++) {
|
|
106943
|
+
const f = s / 12;
|
|
106944
|
+
const rr = sampleRgb48le(
|
|
106945
|
+
from2,
|
|
106946
|
+
ux - dx * fromS * 1.06 * f,
|
|
106947
|
+
uy - dy * fromS * 1.06 * f,
|
|
106948
|
+
w,
|
|
106949
|
+
h
|
|
106950
|
+
)[0];
|
|
106951
|
+
const gg = sampleRgb48le(from2, ux - dx * fromS * f, uy - dy * fromS * f, w, h)[1];
|
|
106952
|
+
const bb = sampleRgb48le(
|
|
106953
|
+
from2,
|
|
106954
|
+
ux - dx * fromS * 0.94 * f,
|
|
106955
|
+
uy - dy * fromS * 0.94 * f,
|
|
106956
|
+
w,
|
|
106957
|
+
h
|
|
106958
|
+
)[2];
|
|
106959
|
+
fr += rr;
|
|
106960
|
+
fg += gg;
|
|
106961
|
+
fb += bb;
|
|
106962
|
+
}
|
|
106963
|
+
fr /= 12;
|
|
106964
|
+
fg /= 12;
|
|
106965
|
+
fb /= 12;
|
|
106966
|
+
let tr = 0, tg = 0, tb = 0;
|
|
106967
|
+
for (let s = 0; s < 12; s++) {
|
|
106968
|
+
const f = s / 12;
|
|
106969
|
+
const rr = sampleRgb48le(to, ux + dx * toS * 1.06 * f, uy + dy * toS * 1.06 * f, w, h)[0];
|
|
106970
|
+
const gg = sampleRgb48le(to, ux + dx * toS * f, uy + dy * toS * f, w, h)[1];
|
|
106971
|
+
const bb = sampleRgb48le(to, ux + dx * toS * 0.94 * f, uy + dy * toS * 0.94 * f, w, h)[2];
|
|
106972
|
+
tr += rr;
|
|
106973
|
+
tg += gg;
|
|
106974
|
+
tb += bb;
|
|
106975
|
+
}
|
|
106976
|
+
tr /= 12;
|
|
106977
|
+
tg /= 12;
|
|
106978
|
+
tb /= 12;
|
|
106979
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fr), Math.round(tr), p)), o);
|
|
106980
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fg), Math.round(tg), p)), o + 2);
|
|
106981
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fb), Math.round(tb), p)), o + 4);
|
|
106982
|
+
}
|
|
106983
|
+
};
|
|
106984
|
+
TRANSITIONS["cinematic-zoom"] = cinematicZoom;
|
|
106985
|
+
var gravitationalLens = (from2, to, out, w, h, p) => {
|
|
106986
|
+
for (let i = 0; i < w * h; i++) {
|
|
106987
|
+
const ux = i % w / w;
|
|
106988
|
+
const uy = Math.floor(i / w) / h;
|
|
106989
|
+
const o = i * 6;
|
|
106990
|
+
const uvx = ux - 0.5;
|
|
106991
|
+
const uvy = uy - 0.5;
|
|
106992
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
106993
|
+
const pull = p * 2;
|
|
106994
|
+
const warpStr = pull * 0.3 / (dist + 0.1);
|
|
106995
|
+
const warpedX = Math.max(0, Math.min(1, ux - uvx * warpStr));
|
|
106996
|
+
const warpedY = Math.max(0, Math.min(1, uy - uvy * warpStr));
|
|
106997
|
+
const [, ag] = sampleRgb48le(from2, warpedX, warpedY, w, h);
|
|
106998
|
+
const horizon = smoothstep(0, 0.3, dist / (1 - p * 0.85 + 1e-3));
|
|
106999
|
+
const shift = pull * 0.02 / (dist + 0.2);
|
|
107000
|
+
const rSampX = Math.max(0, Math.min(1, ux - uvx * (warpStr + shift)));
|
|
107001
|
+
const rSampY = Math.max(0, Math.min(1, uy - uvy * (warpStr + shift)));
|
|
107002
|
+
const bSampX = Math.max(0, Math.min(1, ux - uvx * (warpStr - shift)));
|
|
107003
|
+
const bSampY = Math.max(0, Math.min(1, uy - uvy * (warpStr - shift)));
|
|
107004
|
+
const ar = sampleRgb48le(from2, rSampX, rSampY, w, h)[0];
|
|
107005
|
+
const ab = sampleRgb48le(from2, bSampX, bSampY, w, h)[2];
|
|
107006
|
+
const lensedR = Math.round(ar * horizon);
|
|
107007
|
+
const lensedG = Math.round(ag * horizon);
|
|
107008
|
+
const lensedB = Math.round(ab * horizon);
|
|
107009
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
107010
|
+
const blend = smoothstep(0.3, 0.9, p);
|
|
107011
|
+
out.writeUInt16LE(clamp16(mix16(lensedR, toR, blend)), o);
|
|
107012
|
+
out.writeUInt16LE(clamp16(mix16(lensedG, toG, blend)), o + 2);
|
|
107013
|
+
out.writeUInt16LE(clamp16(mix16(lensedB, toB, blend)), o + 4);
|
|
107014
|
+
}
|
|
107015
|
+
};
|
|
107016
|
+
TRANSITIONS["gravitational-lens"] = gravitationalLens;
|
|
107017
|
+
var rippleWaves = (from2, to, out, w, h, p) => {
|
|
107018
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107019
|
+
for (let i = 0; i < w * h; i++) {
|
|
107020
|
+
const ux = i % w / w;
|
|
107021
|
+
const uy = Math.floor(i / w) / h;
|
|
107022
|
+
const o = i * 6;
|
|
107023
|
+
const uvx = ux - 0.5;
|
|
107024
|
+
const uvy = uy - 0.5;
|
|
107025
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
107026
|
+
const nux = uvx + 1e-3;
|
|
107027
|
+
const nuy = uvy + 1e-3;
|
|
107028
|
+
const nlen = Math.sqrt(nux * nux + nuy * nuy);
|
|
107029
|
+
const dirx = nux / nlen;
|
|
107030
|
+
const diry = nuy / nlen;
|
|
107031
|
+
const fromAmp = p * 0.04;
|
|
107032
|
+
const fw1 = Math.exp(Math.sin(dist * 25 - p * 12) - 1);
|
|
107033
|
+
const fw2 = Math.exp(Math.sin(dist * 50 - p * 18) - 1) * 0.5;
|
|
107034
|
+
const fromUx = Math.max(0, Math.min(1, ux + dirx * (fw1 + fw2) * fromAmp));
|
|
107035
|
+
const fromUy = Math.max(0, Math.min(1, uy + diry * (fw1 + fw2) * fromAmp));
|
|
107036
|
+
const toAmp = (1 - p) * 0.04;
|
|
107037
|
+
const tw1 = Math.exp(Math.sin(dist * 25 + p * 12) - 1);
|
|
107038
|
+
const tw2 = Math.exp(Math.sin(dist * 50 + p * 18) - 1) * 0.5;
|
|
107039
|
+
const toUx = Math.max(0, Math.min(1, ux - dirx * (tw1 + tw2) * toAmp));
|
|
107040
|
+
const toUy = Math.max(0, Math.min(1, uy - diry * (tw1 + tw2) * toAmp));
|
|
107041
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
107042
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
107043
|
+
const peak = fw1 * p;
|
|
107044
|
+
const tintR = accentBright[0] * peak * 0.1;
|
|
107045
|
+
const tintG = accentBright[1] * peak * 0.1;
|
|
107046
|
+
const tintB = accentBright[2] * peak * 0.1;
|
|
107047
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromR + tintR), toR, p)), o);
|
|
107048
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromG + tintG), toG, p)), o + 2);
|
|
107049
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromB + tintB), toB, p)), o + 4);
|
|
107050
|
+
}
|
|
107051
|
+
};
|
|
107052
|
+
TRANSITIONS["ripple-waves"] = rippleWaves;
|
|
107053
|
+
var swirlVortex = (from2, to, out, w, h, p) => {
|
|
107054
|
+
for (let i = 0; i < w * h; i++) {
|
|
107055
|
+
const ux = i % w / w;
|
|
107056
|
+
const uy = Math.floor(i / w) / h;
|
|
107057
|
+
const o = i * 6;
|
|
107058
|
+
const uvx = ux - 0.5;
|
|
107059
|
+
const uvy = uy - 0.5;
|
|
107060
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
107061
|
+
const warp = fbm(ux * 4, uy * 4) * 0.5;
|
|
107062
|
+
const fromAng = p * (1 - dist) * 10 + warp * p * 3;
|
|
107063
|
+
const fs8 = Math.sin(fromAng);
|
|
107064
|
+
const fc = Math.cos(fromAng);
|
|
107065
|
+
const fromUx = Math.max(0, Math.min(1, uvx * fc - uvy * fs8 + 0.5));
|
|
107066
|
+
const fromUy = Math.max(0, Math.min(1, uvx * fs8 + uvy * fc + 0.5));
|
|
107067
|
+
const toAng = -(1 - p) * (1 - dist) * 10 - warp * (1 - p) * 3;
|
|
107068
|
+
const ts = Math.sin(toAng);
|
|
107069
|
+
const tc = Math.cos(toAng);
|
|
107070
|
+
const toUx = Math.max(0, Math.min(1, uvx * tc - uvy * ts + 0.5));
|
|
107071
|
+
const toUy = Math.max(0, Math.min(1, uvx * ts + uvy * tc + 0.5));
|
|
107072
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
107073
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
107074
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, p)), o);
|
|
107075
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, p)), o + 2);
|
|
107076
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, p)), o + 4);
|
|
107077
|
+
}
|
|
107078
|
+
};
|
|
107079
|
+
TRANSITIONS["swirl-vortex"] = swirlVortex;
|
|
107080
|
+
var thermalDistortion = (from2, to, out, w, h, p) => {
|
|
107081
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107082
|
+
for (let i = 0; i < w * h; i++) {
|
|
107083
|
+
const ux = i % w / w;
|
|
107084
|
+
const uy = Math.floor(i / w) / h;
|
|
107085
|
+
const o = i * 6;
|
|
107086
|
+
const heat = p * 1.5;
|
|
107087
|
+
const yFade = smoothstep(1, 0, uy);
|
|
107088
|
+
const shimmer = Math.sin(uy * 40 + fbm(ux * 6, uy * 6) * 8) * fbm(ux * 3 + 0, uy * 3 + p * 2);
|
|
107089
|
+
const dispX = shimmer * heat * 0.03 * yFade;
|
|
107090
|
+
const fromUx = Math.max(0, Math.min(1, ux + dispX));
|
|
107091
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, uy, w, h);
|
|
107092
|
+
const invShimmer = Math.sin(uy * 40 + fbm(ux * 6 + 3, uy * 6 + 3) * 8) * fbm(ux * 3 + 3, uy * 3 + p * 2);
|
|
107093
|
+
const dispX2 = invShimmer * (1 - p) * 0.03 * yFade;
|
|
107094
|
+
const toUx = Math.max(0, Math.min(1, ux + dispX2));
|
|
107095
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, uy, w, h);
|
|
107096
|
+
const haze = heat * yFade * 0.15 * (1 - p);
|
|
107097
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, p) + Math.round(accentBright[0] * haze)), o);
|
|
107098
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, p) + Math.round(accentBright[1] * haze)), o + 2);
|
|
107099
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, p) + Math.round(accentBright[2] * haze)), o + 4);
|
|
107100
|
+
}
|
|
107101
|
+
};
|
|
107102
|
+
TRANSITIONS["thermal-distortion"] = thermalDistortion;
|
|
107103
|
+
var domainWarp = (from2, to, out, w, h, p) => {
|
|
107104
|
+
const accentDark = [25e3, 8e3, 2e3];
|
|
107105
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107106
|
+
for (let i = 0; i < w * h; i++) {
|
|
107107
|
+
const ux = i % w / w;
|
|
107108
|
+
const uy = Math.floor(i / w) / h;
|
|
107109
|
+
const o = i * 6;
|
|
107110
|
+
const qx = fbm(ux * 3, uy * 3);
|
|
107111
|
+
const qy = fbm(ux * 3 + 5.2, uy * 3 + 1.3);
|
|
107112
|
+
const rx = fbm(ux * 3 + qx * 4 + 1.7, uy * 3 + qy * 4 + 9.2);
|
|
107113
|
+
const ry = fbm(ux * 3 + qx * 4 + 8.3, uy * 3 + qy * 4 + 2.8);
|
|
107114
|
+
const n = fbm(ux * 3 + rx * 2, uy * 3 + ry * 2);
|
|
107115
|
+
const warpDirX = (qx - 0.5) * 0.4;
|
|
107116
|
+
const warpDirY = (qy - 0.5) * 0.4;
|
|
107117
|
+
const aUx = Math.max(0, Math.min(1, ux + warpDirX * p));
|
|
107118
|
+
const aUy = Math.max(0, Math.min(1, uy + warpDirY * p));
|
|
107119
|
+
const bUx = Math.max(0, Math.min(1, ux - warpDirX * (1 - p)));
|
|
107120
|
+
const bUy = Math.max(0, Math.min(1, uy - warpDirY * (1 - p)));
|
|
107121
|
+
const [aR, aG, aB] = sampleRgb48le(from2, aUx, aUy, w, h);
|
|
107122
|
+
const [bR, bG, bB] = sampleRgb48le(to, bUx, bUy, w, h);
|
|
107123
|
+
const e = smoothstep(p - 0.08, p + 0.08, n);
|
|
107124
|
+
const ed = Math.abs(n - p);
|
|
107125
|
+
const pStep = p >= 1 ? 1 : 0;
|
|
107126
|
+
const em = smoothstep(0.1, 0, ed) * (1 - pStep);
|
|
107127
|
+
const ecBlend = smoothstep(0, 0.1, ed);
|
|
107128
|
+
const ecR = accentDark[0] + (accentBright[0] - accentDark[0]) * (1 - ecBlend);
|
|
107129
|
+
const ecG = accentDark[1] + (accentBright[1] - accentDark[1]) * (1 - ecBlend);
|
|
107130
|
+
const ecB = accentDark[2] + (accentBright[2] - accentDark[2]) * (1 - ecBlend);
|
|
107131
|
+
out.writeUInt16LE(clamp16(mix16(bR, aR, e) + Math.round(ecR * em * 2)), o);
|
|
107132
|
+
out.writeUInt16LE(clamp16(mix16(bG, aG, e) + Math.round(ecG * em * 2)), o + 2);
|
|
107133
|
+
out.writeUInt16LE(clamp16(mix16(bB, aB, e) + Math.round(ecB * em * 2)), o + 4);
|
|
107134
|
+
}
|
|
107135
|
+
};
|
|
107136
|
+
TRANSITIONS["domain-warp"] = domainWarp;
|
|
107137
|
+
function ridged(px, py) {
|
|
107138
|
+
let value = 0;
|
|
107139
|
+
let amplitude = 0.5;
|
|
107140
|
+
let x = px;
|
|
107141
|
+
let y = py;
|
|
107142
|
+
for (let i = 0; i < 5; i++) {
|
|
107143
|
+
value += amplitude * Math.abs(vnoise(x, y) * 2 - 1);
|
|
107144
|
+
const nx = ROT_A * x - ROT_B * y;
|
|
107145
|
+
const ny = ROT_B * x + ROT_A * y;
|
|
107146
|
+
x = nx * 2.02;
|
|
107147
|
+
y = ny * 2.02;
|
|
107148
|
+
amplitude *= 0.5;
|
|
107149
|
+
}
|
|
107150
|
+
return value;
|
|
107151
|
+
}
|
|
107152
|
+
var ridgedBurn = (from2, to, out, w, h, p) => {
|
|
107153
|
+
const accent = [5e4, 25e3, 5e3];
|
|
107154
|
+
const accentDark = [25e3, 8e3, 2e3];
|
|
107155
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107156
|
+
for (let i = 0; i < w * h; i++) {
|
|
107157
|
+
const ux = i % w / w;
|
|
107158
|
+
const uy = Math.floor(i / w) / h;
|
|
107159
|
+
const o = i * 6;
|
|
107160
|
+
const [aR, aG, aB] = sampleRgb48le(from2, ux, uy, w, h);
|
|
107161
|
+
const [bR, bG, bB] = sampleRgb48le(to, ux, uy, w, h);
|
|
107162
|
+
const n = ridged(ux * 4, uy * 4);
|
|
107163
|
+
const e = smoothstep(p - 0.04, p + 0.04, n);
|
|
107164
|
+
const heat = smoothstep(0.12, 0, Math.abs(n - p));
|
|
107165
|
+
const pStep = p >= 1 ? 1 : 0;
|
|
107166
|
+
const heatMasked = heat * (1 - pStep);
|
|
107167
|
+
let burnR = accentDark[0] + (accent[0] - accentDark[0]) * smoothstep(0, 0.25, heatMasked);
|
|
107168
|
+
let burnG = accentDark[1] + (accent[1] - accentDark[1]) * smoothstep(0, 0.25, heatMasked);
|
|
107169
|
+
let burnB = accentDark[2] + (accent[2] - accentDark[2]) * smoothstep(0, 0.25, heatMasked);
|
|
107170
|
+
const blend2 = smoothstep(0.25, 0.5, heatMasked);
|
|
107171
|
+
burnR = burnR + (accentBright[0] - burnR) * blend2;
|
|
107172
|
+
burnG = burnG + (accentBright[1] - burnG) * blend2;
|
|
107173
|
+
burnB = burnB + (accentBright[2] - burnB) * blend2;
|
|
107174
|
+
const blend3 = smoothstep(0.5, 1, heatMasked);
|
|
107175
|
+
burnR = burnR + (65535 - burnR) * blend3;
|
|
107176
|
+
burnG = burnG + (65535 - burnG) * blend3;
|
|
107177
|
+
burnB = burnB + (65535 - burnB) * blend3;
|
|
107178
|
+
const sparks = (vnoise(ux * 80, uy * 80) >= 0.92 ? 1 : 0) * heatMasked * 3;
|
|
107179
|
+
out.writeUInt16LE(
|
|
107180
|
+
clamp16(
|
|
107181
|
+
mix16(bR, aR, e) + Math.round(burnR * heatMasked * 3.5) + Math.round(accentBright[0] * sparks)
|
|
107182
|
+
),
|
|
107183
|
+
o
|
|
107184
|
+
);
|
|
107185
|
+
out.writeUInt16LE(
|
|
107186
|
+
clamp16(
|
|
107187
|
+
mix16(bG, aG, e) + Math.round(burnG * heatMasked * 3.5) + Math.round(accentBright[1] * sparks)
|
|
107188
|
+
),
|
|
107189
|
+
o + 2
|
|
107190
|
+
);
|
|
107191
|
+
out.writeUInt16LE(
|
|
107192
|
+
clamp16(
|
|
107193
|
+
mix16(bB, aB, e) + Math.round(burnB * heatMasked * 3.5) + Math.round(accentBright[2] * sparks)
|
|
107194
|
+
),
|
|
107195
|
+
o + 4
|
|
107196
|
+
);
|
|
107197
|
+
}
|
|
107198
|
+
};
|
|
107199
|
+
TRANSITIONS["ridged-burn"] = ridgedBurn;
|
|
107200
|
+
|
|
105789
107201
|
// src/services/renderOrchestrator.ts
|
|
105790
107202
|
import { join as join15, dirname as dirname10, resolve as resolve10 } from "path";
|
|
105791
107203
|
import { randomUUID } from "crypto";
|
|
@@ -106106,6 +107518,10 @@ var RENDER_MODE_SCRIPT = `(function() {
|
|
|
106106
107518
|
}
|
|
106107
107519
|
waitForPlayer();
|
|
106108
107520
|
})();`;
|
|
107521
|
+
var HF_EARLY_STUB = `(function() {
|
|
107522
|
+
if (typeof window === "undefined") return;
|
|
107523
|
+
if (!window.__hf) window.__hf = {};
|
|
107524
|
+
})();`;
|
|
106109
107525
|
var HF_BRIDGE_SCRIPT = `(function() {
|
|
106110
107526
|
var __realSetInterval =
|
|
106111
107527
|
window.__HF_VIRTUAL_TIME__ && typeof window.__HF_VIRTUAL_TIME__.originalSetInterval === "function"
|
|
@@ -106151,20 +107567,24 @@ var HF_BRIDGE_SCRIPT = `(function() {
|
|
|
106151
107567
|
if (!p || typeof p.renderSeek !== "function" || typeof p.getDuration !== "function") {
|
|
106152
107568
|
return false;
|
|
106153
107569
|
}
|
|
106154
|
-
window.__hf
|
|
106155
|
-
|
|
107570
|
+
var hf = window.__hf || {};
|
|
107571
|
+
Object.defineProperty(hf, "duration", {
|
|
107572
|
+
configurable: true,
|
|
107573
|
+
enumerable: true,
|
|
107574
|
+
get: function() {
|
|
106156
107575
|
var d = p.getDuration();
|
|
106157
107576
|
return d > 0 ? d : getDeclaredDuration();
|
|
106158
107577
|
},
|
|
106159
|
-
|
|
106160
|
-
|
|
106161
|
-
|
|
106162
|
-
|
|
106163
|
-
|
|
106164
|
-
|
|
106165
|
-
|
|
106166
|
-
|
|
107578
|
+
});
|
|
107579
|
+
hf.seek = function(t) {
|
|
107580
|
+
p.renderSeek(t);
|
|
107581
|
+
var nextTimeMs = (Math.max(0, Number(t) || 0)) * 1000;
|
|
107582
|
+
if (window.__HF_VIRTUAL_TIME__ && typeof window.__HF_VIRTUAL_TIME__.seekToTime === "function") {
|
|
107583
|
+
window.__HF_VIRTUAL_TIME__.seekToTime(nextTimeMs);
|
|
107584
|
+
}
|
|
107585
|
+
seekSameOriginChildFrames(window, nextTimeMs);
|
|
106167
107586
|
};
|
|
107587
|
+
window.__hf = hf;
|
|
106168
107588
|
return true;
|
|
106169
107589
|
}
|
|
106170
107590
|
if (bridge()) return;
|
|
@@ -106246,7 +107666,7 @@ ${headTags}`);
|
|
|
106246
107666
|
}
|
|
106247
107667
|
function createFileServer2(options) {
|
|
106248
107668
|
const { projectDir, compiledDir, port = 0, stripEmbeddedRuntime = true } = options;
|
|
106249
|
-
const preHeadScripts = options.preHeadScripts ?? [];
|
|
107669
|
+
const preHeadScripts = [HF_EARLY_STUB, ...options.preHeadScripts ?? []];
|
|
106250
107670
|
const headScripts = options.headScripts ?? [getVerifiedHyperframeRuntimeSource()];
|
|
106251
107671
|
const bodyScripts = options.bodyScripts ?? [RENDER_MODE_SCRIPT, HF_BRIDGE_SCRIPT];
|
|
106252
107672
|
const app = new Hono2();
|
|
@@ -107506,6 +108926,24 @@ async function safeCleanup(label, fn, log = defaultLogger) {
|
|
|
107506
108926
|
});
|
|
107507
108927
|
}
|
|
107508
108928
|
}
|
|
108929
|
+
var frameDirMaxIndexCache = /* @__PURE__ */ new Map();
|
|
108930
|
+
var FRAME_FILENAME_RE = /^frame_(\d+)\.png$/;
|
|
108931
|
+
function getMaxFrameIndex(frameDir) {
|
|
108932
|
+
const cached = frameDirMaxIndexCache.get(frameDir);
|
|
108933
|
+
if (cached !== void 0) return cached;
|
|
108934
|
+
let max = 0;
|
|
108935
|
+
try {
|
|
108936
|
+
for (const name of readdirSync6(frameDir)) {
|
|
108937
|
+
const m = FRAME_FILENAME_RE.exec(name);
|
|
108938
|
+
if (!m) continue;
|
|
108939
|
+
const n = Number(m[1]);
|
|
108940
|
+
if (Number.isFinite(n) && n > max) max = n;
|
|
108941
|
+
}
|
|
108942
|
+
} catch {
|
|
108943
|
+
}
|
|
108944
|
+
frameDirMaxIndexCache.set(frameDir, max);
|
|
108945
|
+
return max;
|
|
108946
|
+
}
|
|
107509
108947
|
var RenderCancelledError = class extends Error {
|
|
107510
108948
|
reason;
|
|
107511
108949
|
constructor(message = "render_cancelled", reason = "aborted") {
|
|
@@ -107607,6 +109045,63 @@ function applyRenderModeHints(cfg, compiled, log = defaultLogger) {
|
|
|
107607
109045
|
reasons: compiled.renderModeHints.reasons.map((reason) => reason.message)
|
|
107608
109046
|
});
|
|
107609
109047
|
}
|
|
109048
|
+
function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, width, height, log, sourceTransfer, targetTransfer) {
|
|
109049
|
+
const frameDir = hdrFrameDirs.get(el.id);
|
|
109050
|
+
const startTime = hdrStartTimes.get(el.id);
|
|
109051
|
+
if (!frameDir || startTime === void 0) {
|
|
109052
|
+
return;
|
|
109053
|
+
}
|
|
109054
|
+
const videoFrameIndex = Math.round((time - startTime) * fps) + 1;
|
|
109055
|
+
if (videoFrameIndex < 1) return;
|
|
109056
|
+
const maxIndex = getMaxFrameIndex(frameDir);
|
|
109057
|
+
const effectiveIndex = maxIndex > 0 ? Math.min(videoFrameIndex, maxIndex) : videoFrameIndex;
|
|
109058
|
+
const framePath = join15(frameDir, `frame_${String(effectiveIndex).padStart(4, "0")}.png`);
|
|
109059
|
+
if (!existsSync15(framePath)) {
|
|
109060
|
+
return;
|
|
109061
|
+
}
|
|
109062
|
+
try {
|
|
109063
|
+
const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(readFileSync9(framePath));
|
|
109064
|
+
if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
|
|
109065
|
+
convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
|
|
109066
|
+
}
|
|
109067
|
+
const viewportMatrix = parseTransformMatrix(el.transform);
|
|
109068
|
+
const br = el.borderRadius;
|
|
109069
|
+
const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0;
|
|
109070
|
+
const borderRadiusParam = hasBorderRadius ? br : void 0;
|
|
109071
|
+
if (viewportMatrix) {
|
|
109072
|
+
blitRgb48leAffine(
|
|
109073
|
+
canvas,
|
|
109074
|
+
hdrRgb,
|
|
109075
|
+
viewportMatrix,
|
|
109076
|
+
srcW,
|
|
109077
|
+
srcH,
|
|
109078
|
+
width,
|
|
109079
|
+
height,
|
|
109080
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
109081
|
+
borderRadiusParam
|
|
109082
|
+
);
|
|
109083
|
+
} else {
|
|
109084
|
+
blitRgb48leRegion(
|
|
109085
|
+
canvas,
|
|
109086
|
+
hdrRgb,
|
|
109087
|
+
el.x,
|
|
109088
|
+
el.y,
|
|
109089
|
+
srcW,
|
|
109090
|
+
srcH,
|
|
109091
|
+
width,
|
|
109092
|
+
height,
|
|
109093
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
109094
|
+
borderRadiusParam
|
|
109095
|
+
);
|
|
109096
|
+
}
|
|
109097
|
+
} catch (err) {
|
|
109098
|
+
if (log) {
|
|
109099
|
+
log.debug(`HDR blit failed for ${el.id}`, {
|
|
109100
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109101
|
+
});
|
|
109102
|
+
}
|
|
109103
|
+
}
|
|
109104
|
+
}
|
|
107610
109105
|
function createRenderJob(config2) {
|
|
107611
109106
|
return {
|
|
107612
109107
|
id: randomUUID(),
|
|
@@ -107877,6 +109372,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
107877
109372
|
perfStages.browserProbeMs = Date.now() - probeStart;
|
|
107878
109373
|
job.duration = composition.duration;
|
|
107879
109374
|
job.totalFrames = Math.ceil(composition.duration * job.config.fps);
|
|
109375
|
+
const totalFrames = job.totalFrames;
|
|
107880
109376
|
if (job.duration <= 0) {
|
|
107881
109377
|
const diagnostics = [];
|
|
107882
109378
|
try {
|
|
@@ -107905,7 +109401,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
107905
109401
|
}
|
|
107906
109402
|
}
|
|
107907
109403
|
}
|
|
107908
|
-
} catch {
|
|
109404
|
+
} catch (err) {
|
|
109405
|
+
log.warn("Failed to gather browser diagnostics for zero-duration composition", {
|
|
109406
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109407
|
+
});
|
|
107909
109408
|
diagnostics.push("(Could not gather browser diagnostics \u2014 page may have crashed)");
|
|
107910
109409
|
}
|
|
107911
109410
|
const hint = diagnostics.length > 0 ? "\n\nDiagnostics:\n - " + diagnostics.join("\n - ") : "\n\nCheck that GSAP timelines are registered on window.__timelines.";
|
|
@@ -107929,8 +109428,28 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
107929
109428
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
|
|
107930
109429
|
let frameLookup = null;
|
|
107931
109430
|
const compiledDir = join15(workDir, "compiled");
|
|
109431
|
+
let extractionResult = null;
|
|
109432
|
+
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
109433
|
+
const videoTransfers = /* @__PURE__ */ new Map();
|
|
109434
|
+
if (job.config.hdr && composition.videos.length > 0) {
|
|
109435
|
+
await Promise.all(
|
|
109436
|
+
composition.videos.map(async (v) => {
|
|
109437
|
+
let videoPath = v.src;
|
|
109438
|
+
if (!videoPath.startsWith("/")) {
|
|
109439
|
+
const fromCompiled = existsSync15(join15(compiledDir, videoPath)) ? join15(compiledDir, videoPath) : join15(projectDir, videoPath);
|
|
109440
|
+
videoPath = fromCompiled;
|
|
109441
|
+
}
|
|
109442
|
+
if (!existsSync15(videoPath)) return;
|
|
109443
|
+
const meta = await extractVideoMetadata(videoPath);
|
|
109444
|
+
if (isHdrColorSpace(meta.colorSpace)) {
|
|
109445
|
+
nativeHdrVideoIds.add(v.id);
|
|
109446
|
+
videoTransfers.set(v.id, detectTransfer(meta.colorSpace));
|
|
109447
|
+
}
|
|
109448
|
+
})
|
|
109449
|
+
);
|
|
109450
|
+
}
|
|
107932
109451
|
if (composition.videos.length > 0) {
|
|
107933
|
-
|
|
109452
|
+
extractionResult = await extractAllVideoFrames(
|
|
107934
109453
|
composition.videos,
|
|
107935
109454
|
projectDir,
|
|
107936
109455
|
{ fps: job.config.fps, outputDir: join15(workDir, "video-frames") },
|
|
@@ -107965,6 +109484,29 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
107965
109484
|
} else {
|
|
107966
109485
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
107967
109486
|
}
|
|
109487
|
+
let effectiveHdr;
|
|
109488
|
+
if (job.config.hdr && frameLookup) {
|
|
109489
|
+
const colorSpaces = (extractionResult?.extracted ?? []).map((ext) => ext.metadata.colorSpace);
|
|
109490
|
+
const info = analyzeCompositionHdr(colorSpaces);
|
|
109491
|
+
if (info.hasHdr && info.dominantTransfer) {
|
|
109492
|
+
effectiveHdr = { transfer: info.dominantTransfer };
|
|
109493
|
+
}
|
|
109494
|
+
}
|
|
109495
|
+
if (job.config.hdr && !effectiveHdr && nativeHdrVideoIds.size > 0) {
|
|
109496
|
+
const firstTransfer = videoTransfers.values().next().value;
|
|
109497
|
+
if (firstTransfer) {
|
|
109498
|
+
effectiveHdr = { transfer: firstTransfer };
|
|
109499
|
+
}
|
|
109500
|
+
}
|
|
109501
|
+
if (effectiveHdr && outputFormat !== "mp4") {
|
|
109502
|
+
log.info(`[Render] HDR source detected but format is ${outputFormat} \u2014 using SDR`);
|
|
109503
|
+
effectiveHdr = void 0;
|
|
109504
|
+
}
|
|
109505
|
+
if (effectiveHdr) {
|
|
109506
|
+
log.info(
|
|
109507
|
+
`[Render] HDR source detected \u2014 output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
109508
|
+
);
|
|
109509
|
+
}
|
|
107968
109510
|
const stage3Start = Date.now();
|
|
107969
109511
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
107970
109512
|
const audioOutputPath = join15(workDir, "audio.aac");
|
|
@@ -108006,222 +109548,655 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108006
109548
|
format: needsAlpha ? "png" : "jpeg",
|
|
108007
109549
|
quality: needsAlpha ? void 0 : job.config.quality === "draft" ? 80 : 95
|
|
108008
109550
|
};
|
|
108009
|
-
const workerCount = calculateOptimalWorkers(
|
|
109551
|
+
const workerCount = calculateOptimalWorkers(totalFrames, job.config.workers, cfg);
|
|
108010
109552
|
const FORMAT_EXT = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
108011
109553
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
108012
109554
|
const videoOnlyPath = join15(workDir, `video-only${videoExt}`);
|
|
108013
|
-
const
|
|
108014
|
-
const
|
|
108015
|
-
const
|
|
108016
|
-
const baseEncoderOpts = {
|
|
108017
|
-
fps: job.config.fps,
|
|
108018
|
-
width,
|
|
108019
|
-
height,
|
|
108020
|
-
codec: preset.codec,
|
|
108021
|
-
preset: preset.preset,
|
|
108022
|
-
quality: effectiveQuality,
|
|
108023
|
-
bitrate: effectiveBitrate,
|
|
108024
|
-
pixelFormat: preset.pixelFormat,
|
|
108025
|
-
useGpu: job.config.useGpu
|
|
108026
|
-
};
|
|
109555
|
+
const hasHdrContent = effectiveHdr && nativeHdrVideoIds.size > 0;
|
|
109556
|
+
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
109557
|
+
const preset = getEncoderPreset(job.config.quality, outputFormat, encoderHdr);
|
|
108027
109558
|
job.framesRendered = 0;
|
|
108028
|
-
|
|
108029
|
-
|
|
108030
|
-
|
|
109559
|
+
if (hasHdrContent) {
|
|
109560
|
+
log.info("[Render] HDR layered composite: z-ordered DOM + native HLG video layers");
|
|
109561
|
+
const hdrVideoIds = composition.videos.filter((v) => nativeHdrVideoIds.has(v.id)).map((v) => v.id);
|
|
109562
|
+
const hdrVideoSrcPaths = /* @__PURE__ */ new Map();
|
|
109563
|
+
for (const v of composition.videos) {
|
|
109564
|
+
if (!hdrVideoIds.includes(v.id)) continue;
|
|
109565
|
+
let srcPath = v.src;
|
|
109566
|
+
if (!srcPath.startsWith("/")) {
|
|
109567
|
+
const fromCompiled = join15(compiledDir, srcPath);
|
|
109568
|
+
srcPath = existsSync15(fromCompiled) ? fromCompiled : join15(projectDir, srcPath);
|
|
109569
|
+
}
|
|
109570
|
+
hdrVideoSrcPaths.set(v.id, srcPath);
|
|
109571
|
+
}
|
|
109572
|
+
if (!fileServer) throw new Error("fileServer must be initialized before HDR compositing");
|
|
109573
|
+
const domSession = await createCaptureSession(
|
|
109574
|
+
fileServer.url,
|
|
109575
|
+
framesDir,
|
|
109576
|
+
captureOptions,
|
|
109577
|
+
createVideoFrameInjector(frameLookup),
|
|
109578
|
+
cfg
|
|
109579
|
+
);
|
|
109580
|
+
await initializeSession(domSession);
|
|
109581
|
+
assertNotAborted();
|
|
109582
|
+
lastBrowserConsole = domSession.browserConsoleBuffer;
|
|
109583
|
+
await initTransparentBackground(domSession.page);
|
|
109584
|
+
const transitionMeta = await domSession.page.evaluate(() => {
|
|
109585
|
+
return window.__hf?.transitions ?? [];
|
|
109586
|
+
});
|
|
109587
|
+
const sceneElements = await domSession.page.evaluate(() => {
|
|
109588
|
+
const scenes = document.querySelectorAll(".scene");
|
|
109589
|
+
const map2 = {};
|
|
109590
|
+
for (const scene of scenes) {
|
|
109591
|
+
const els = scene.querySelectorAll("[data-start]");
|
|
109592
|
+
map2[scene.id] = Array.from(els).map((e) => e.id);
|
|
109593
|
+
}
|
|
109594
|
+
return map2;
|
|
109595
|
+
});
|
|
109596
|
+
const transitionRanges = transitionMeta.map((t) => ({
|
|
109597
|
+
...t,
|
|
109598
|
+
startFrame: Math.floor(t.time * job.config.fps),
|
|
109599
|
+
endFrame: Math.ceil((t.time + t.duration) * job.config.fps)
|
|
109600
|
+
}));
|
|
109601
|
+
if (transitionRanges.length > 0) {
|
|
109602
|
+
log.info("[Render] Detected shader transitions for HDR compositing", {
|
|
109603
|
+
count: transitionRanges.length,
|
|
109604
|
+
transitions: transitionRanges.map((t) => ({
|
|
109605
|
+
shader: t.shader,
|
|
109606
|
+
from: t.fromScene,
|
|
109607
|
+
to: t.toScene,
|
|
109608
|
+
frames: `${t.startFrame}-${t.endFrame}`
|
|
109609
|
+
}))
|
|
109610
|
+
});
|
|
109611
|
+
}
|
|
109612
|
+
const hdrEncoder = await spawnStreamingEncoder(
|
|
108031
109613
|
videoOnlyPath,
|
|
108032
109614
|
{
|
|
108033
|
-
|
|
108034
|
-
|
|
109615
|
+
fps: job.config.fps,
|
|
109616
|
+
width,
|
|
109617
|
+
height,
|
|
109618
|
+
codec: preset.codec,
|
|
109619
|
+
preset: preset.preset,
|
|
109620
|
+
quality: preset.quality,
|
|
109621
|
+
pixelFormat: preset.pixelFormat,
|
|
109622
|
+
hdr: preset.hdr,
|
|
109623
|
+
rawInputFormat: "rgb48le"
|
|
108035
109624
|
},
|
|
108036
|
-
abortSignal
|
|
109625
|
+
abortSignal,
|
|
109626
|
+
{ ffmpegStreamingTimeout: 36e5 }
|
|
108037
109627
|
);
|
|
108038
109628
|
assertNotAborted();
|
|
108039
|
-
|
|
108040
|
-
|
|
108041
|
-
const
|
|
108042
|
-
|
|
108043
|
-
|
|
108044
|
-
|
|
108045
|
-
|
|
108046
|
-
|
|
108047
|
-
|
|
108048
|
-
|
|
109629
|
+
const hdrExtractionDims = /* @__PURE__ */ new Map();
|
|
109630
|
+
const hdrVideoStartTimes = /* @__PURE__ */ new Map();
|
|
109631
|
+
for (const v of composition.videos) {
|
|
109632
|
+
if (hdrVideoIds.includes(v.id)) {
|
|
109633
|
+
hdrVideoStartTimes.set(v.id, v.start);
|
|
109634
|
+
}
|
|
109635
|
+
}
|
|
109636
|
+
const uniqueStartTimes = [...new Set(hdrVideoStartTimes.values())].sort((a, b) => a - b);
|
|
109637
|
+
for (const seekTime of uniqueStartTimes) {
|
|
109638
|
+
await domSession.page.evaluate((t) => {
|
|
109639
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109640
|
+
}, seekTime);
|
|
109641
|
+
if (domSession.onBeforeCapture) {
|
|
109642
|
+
await domSession.onBeforeCapture(domSession.page, seekTime);
|
|
109643
|
+
}
|
|
109644
|
+
const stacking = await queryElementStacking(domSession.page, nativeHdrVideoIds);
|
|
109645
|
+
for (const el of stacking) {
|
|
109646
|
+
if (el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0 && !hdrExtractionDims.has(el.id)) {
|
|
109647
|
+
hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
109648
|
+
}
|
|
109649
|
+
}
|
|
109650
|
+
}
|
|
109651
|
+
const hdrFrameDirs = /* @__PURE__ */ new Map();
|
|
109652
|
+
for (const [videoId, srcPath] of hdrVideoSrcPaths) {
|
|
109653
|
+
const video = composition.videos.find((v) => v.id === videoId);
|
|
109654
|
+
if (!video) continue;
|
|
109655
|
+
const frameDir = join15(framesDir, `hdr_${videoId}`);
|
|
109656
|
+
mkdirSync10(frameDir, { recursive: true });
|
|
109657
|
+
const duration = video.end - video.start;
|
|
109658
|
+
const dims = hdrExtractionDims.get(videoId) ?? { width, height };
|
|
109659
|
+
const ffmpegArgs = [
|
|
109660
|
+
"-ss",
|
|
109661
|
+
String(video.mediaStart),
|
|
109662
|
+
"-i",
|
|
109663
|
+
srcPath,
|
|
109664
|
+
"-t",
|
|
109665
|
+
String(duration),
|
|
109666
|
+
"-r",
|
|
109667
|
+
String(job.config.fps),
|
|
109668
|
+
"-vf",
|
|
109669
|
+
`scale=${dims.width}:${dims.height}:force_original_aspect_ratio=increase,crop=${dims.width}:${dims.height}`,
|
|
109670
|
+
"-pix_fmt",
|
|
109671
|
+
"rgb48le",
|
|
109672
|
+
"-c:v",
|
|
109673
|
+
"png",
|
|
109674
|
+
"-y",
|
|
109675
|
+
join15(frameDir, "frame_%04d.png")
|
|
109676
|
+
];
|
|
109677
|
+
const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
|
|
109678
|
+
if (!result.success) {
|
|
109679
|
+
log.warn("HDR frame pre-extraction failed; loop will fill with black", {
|
|
109680
|
+
videoId,
|
|
109681
|
+
srcPath,
|
|
109682
|
+
stderr: result.stderr.slice(-400)
|
|
109683
|
+
});
|
|
109684
|
+
}
|
|
109685
|
+
hdrFrameDirs.set(videoId, frameDir);
|
|
109686
|
+
}
|
|
109687
|
+
assertNotAborted();
|
|
109688
|
+
try {
|
|
109689
|
+
let countNonZeroAlpha2 = function(rgba) {
|
|
109690
|
+
let n = 0;
|
|
109691
|
+
for (let p = 3; p < rgba.length; p += 4) {
|
|
109692
|
+
if (rgba[p] !== 0) n++;
|
|
109693
|
+
}
|
|
109694
|
+
return n;
|
|
109695
|
+
}, countNonZeroRgb482 = function(buf) {
|
|
109696
|
+
let n = 0;
|
|
109697
|
+
for (let p = 0; p < buf.length; p += 6) {
|
|
109698
|
+
if (buf[p] !== 0 || buf[p + 1] !== 0 || buf[p + 2] !== 0) n++;
|
|
109699
|
+
}
|
|
109700
|
+
return n;
|
|
108049
109701
|
};
|
|
108050
|
-
|
|
108051
|
-
|
|
108052
|
-
|
|
108053
|
-
|
|
108054
|
-
|
|
108055
|
-
()
|
|
108056
|
-
|
|
108057
|
-
|
|
108058
|
-
|
|
108059
|
-
|
|
108060
|
-
|
|
108061
|
-
|
|
108062
|
-
|
|
108063
|
-
|
|
108064
|
-
|
|
108065
|
-
|
|
108066
|
-
|
|
108067
|
-
|
|
109702
|
+
var countNonZeroAlpha = countNonZeroAlpha2, countNonZeroRgb48 = countNonZeroRgb482;
|
|
109703
|
+
const beforeCaptureHook = domSession.onBeforeCapture;
|
|
109704
|
+
const cleanedUpVideos = /* @__PURE__ */ new Set();
|
|
109705
|
+
const hdrVideoEndTimes = /* @__PURE__ */ new Map();
|
|
109706
|
+
for (const v of composition.videos) {
|
|
109707
|
+
if (hdrFrameDirs.has(v.id)) {
|
|
109708
|
+
hdrVideoEndTimes.set(v.id, v.end);
|
|
109709
|
+
}
|
|
109710
|
+
}
|
|
109711
|
+
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
109712
|
+
const debugDumpDir = debugDumpEnabled ? join15(framesDir, "debug-composite") : null;
|
|
109713
|
+
if (debugDumpDir && !existsSync15(debugDumpDir)) {
|
|
109714
|
+
mkdirSync10(debugDumpDir, { recursive: true });
|
|
109715
|
+
}
|
|
109716
|
+
async function compositeToBuffer(canvas, time, fullStacking, elementFilter, debugFrameIndex = -1) {
|
|
109717
|
+
const filteredStacking = elementFilter ? fullStacking.filter((e) => elementFilter.has(e.id)) : fullStacking;
|
|
109718
|
+
const layers = groupIntoLayers(filteredStacking);
|
|
109719
|
+
const shouldLog = debugDumpEnabled && debugFrameIndex >= 0;
|
|
109720
|
+
if (shouldLog) {
|
|
109721
|
+
log.info("[diag] compositeToBuffer plan", {
|
|
109722
|
+
frame: debugFrameIndex,
|
|
109723
|
+
time: time.toFixed(3),
|
|
109724
|
+
filterSize: elementFilter?.size,
|
|
109725
|
+
fullStackingCount: fullStacking.length,
|
|
109726
|
+
filteredCount: filteredStacking.length,
|
|
109727
|
+
layerCount: layers.length,
|
|
109728
|
+
layers: layers.map(
|
|
109729
|
+
(l) => l.type === "hdr" ? {
|
|
109730
|
+
type: "hdr",
|
|
109731
|
+
id: l.element.id,
|
|
109732
|
+
z: l.element.zIndex,
|
|
109733
|
+
visible: l.element.visible,
|
|
109734
|
+
opacity: l.element.opacity,
|
|
109735
|
+
bounds: `${Math.round(l.element.x)},${Math.round(l.element.y)} ${Math.round(l.element.width)}x${Math.round(l.element.height)}`
|
|
109736
|
+
} : { type: "dom", ids: l.elementIds }
|
|
109737
|
+
)
|
|
109738
|
+
});
|
|
109739
|
+
}
|
|
109740
|
+
for (let layerIdx = 0; layerIdx < layers.length; layerIdx++) {
|
|
109741
|
+
const layer = layers[layerIdx];
|
|
109742
|
+
if (layer.type === "hdr") {
|
|
109743
|
+
const before2 = shouldLog ? countNonZeroRgb482(canvas) : 0;
|
|
109744
|
+
blitHdrVideoLayer(
|
|
109745
|
+
canvas,
|
|
109746
|
+
layer.element,
|
|
109747
|
+
time,
|
|
109748
|
+
job.config.fps,
|
|
109749
|
+
hdrFrameDirs,
|
|
109750
|
+
hdrVideoStartTimes,
|
|
109751
|
+
width,
|
|
109752
|
+
height,
|
|
109753
|
+
log,
|
|
109754
|
+
videoTransfers.get(layer.element.id),
|
|
109755
|
+
effectiveHdr?.transfer
|
|
108068
109756
|
);
|
|
109757
|
+
if (shouldLog) {
|
|
109758
|
+
const after2 = countNonZeroRgb482(canvas);
|
|
109759
|
+
const frameDir = hdrFrameDirs.get(layer.element.id);
|
|
109760
|
+
const startTime = hdrVideoStartTimes.get(layer.element.id) ?? 0;
|
|
109761
|
+
const localTime = time - startTime;
|
|
109762
|
+
const frameNum = Math.floor(localTime * job.config.fps) + 1;
|
|
109763
|
+
const expectedFrame = frameDir ? join15(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
|
|
109764
|
+
log.info("[diag] hdr layer blit", {
|
|
109765
|
+
frame: debugFrameIndex,
|
|
109766
|
+
layerIdx,
|
|
109767
|
+
id: layer.element.id,
|
|
109768
|
+
pixelsAdded: after2 - before2,
|
|
109769
|
+
totalNonZero: after2,
|
|
109770
|
+
startTime,
|
|
109771
|
+
localTime: localTime.toFixed(3),
|
|
109772
|
+
hdrFrameNum: frameNum,
|
|
109773
|
+
expectedFrame,
|
|
109774
|
+
expectedFrameExists: expectedFrame ? existsSync15(expectedFrame) : false
|
|
109775
|
+
});
|
|
109776
|
+
}
|
|
109777
|
+
} else {
|
|
109778
|
+
const allElementIds = fullStacking.map((e) => e.id);
|
|
109779
|
+
const layerIds = new Set(layer.elementIds);
|
|
109780
|
+
const hideIds = allElementIds.filter((id) => !layerIds.has(id));
|
|
109781
|
+
await domSession.page.evaluate((t) => {
|
|
109782
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109783
|
+
}, time);
|
|
109784
|
+
if (beforeCaptureHook) {
|
|
109785
|
+
await beforeCaptureHook(domSession.page, time);
|
|
109786
|
+
}
|
|
109787
|
+
await applyDomLayerMask(domSession.page, layer.elementIds, hideIds);
|
|
109788
|
+
const domPng = await captureAlphaPng(domSession.page, width, height);
|
|
109789
|
+
await removeDomLayerMask(domSession.page, hideIds);
|
|
109790
|
+
try {
|
|
109791
|
+
const { data: domRgba } = decodePng(domPng);
|
|
109792
|
+
if (!effectiveHdr) {
|
|
109793
|
+
throw new Error(
|
|
109794
|
+
"Invariant violation: effectiveHdr is undefined inside HDR layer branch"
|
|
109795
|
+
);
|
|
109796
|
+
}
|
|
109797
|
+
const before2 = shouldLog ? countNonZeroRgb482(canvas) : 0;
|
|
109798
|
+
const alphaPixels = shouldLog ? countNonZeroAlpha2(domRgba) : 0;
|
|
109799
|
+
blitRgba8OverRgb48le(domRgba, canvas, width, height, effectiveHdr.transfer);
|
|
109800
|
+
if (shouldLog && debugDumpDir) {
|
|
109801
|
+
const after2 = countNonZeroRgb482(canvas);
|
|
109802
|
+
const dumpName = `frame_${String(debugFrameIndex).padStart(4, "0")}_layer_${String(layerIdx).padStart(2, "0")}_dom.png`;
|
|
109803
|
+
const dumpPath = join15(debugDumpDir, dumpName);
|
|
109804
|
+
writeFileSync4(dumpPath, domPng);
|
|
109805
|
+
log.info("[diag] dom layer blit", {
|
|
109806
|
+
frame: debugFrameIndex,
|
|
109807
|
+
layerIdx,
|
|
109808
|
+
layerIds: layer.elementIds,
|
|
109809
|
+
hideCount: hideIds.length,
|
|
109810
|
+
pngBytes: domPng.length,
|
|
109811
|
+
alphaPixels,
|
|
109812
|
+
pixelsAdded: after2 - before2,
|
|
109813
|
+
totalNonZero: after2,
|
|
109814
|
+
dumpPath
|
|
109815
|
+
});
|
|
109816
|
+
}
|
|
109817
|
+
} catch (err) {
|
|
109818
|
+
log.warn("DOM layer decode/blit failed; skipping overlay", {
|
|
109819
|
+
layerIds: layer.elementIds,
|
|
109820
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109821
|
+
});
|
|
109822
|
+
}
|
|
108069
109823
|
}
|
|
108070
|
-
},
|
|
108071
|
-
onFrameBuffer,
|
|
108072
|
-
cfg
|
|
108073
|
-
);
|
|
108074
|
-
if (probeSession) {
|
|
108075
|
-
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
108076
|
-
await closeCaptureSession(probeSession);
|
|
108077
|
-
probeSession = null;
|
|
108078
|
-
}
|
|
108079
|
-
} else {
|
|
108080
|
-
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
108081
|
-
const session = probeSession ?? await createCaptureSession(
|
|
108082
|
-
fileServer.url,
|
|
108083
|
-
framesDir,
|
|
108084
|
-
captureOptions,
|
|
108085
|
-
videoInjector,
|
|
108086
|
-
cfg
|
|
108087
|
-
);
|
|
108088
|
-
if (probeSession) {
|
|
108089
|
-
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
108090
|
-
probeSession = null;
|
|
108091
|
-
}
|
|
108092
|
-
try {
|
|
108093
|
-
if (!session.isInitialized) {
|
|
108094
|
-
await initializeSession(session);
|
|
108095
109824
|
}
|
|
109825
|
+
if (shouldLog && debugDumpDir) {
|
|
109826
|
+
const finalNonZero = countNonZeroRgb482(canvas);
|
|
109827
|
+
log.info("[diag] compositeToBuffer end", {
|
|
109828
|
+
frame: debugFrameIndex,
|
|
109829
|
+
finalNonZeroPixels: finalNonZero,
|
|
109830
|
+
totalPixels: width * height,
|
|
109831
|
+
coverage: (finalNonZero / (width * height) * 100).toFixed(1) + "%"
|
|
109832
|
+
});
|
|
109833
|
+
}
|
|
109834
|
+
}
|
|
109835
|
+
const bufSize = width * height * 6;
|
|
109836
|
+
const hasTransitions = transitionRanges.length > 0;
|
|
109837
|
+
const transBufferA = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
109838
|
+
const transBufferB = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
109839
|
+
const transOutput = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
109840
|
+
const normalCanvas = Buffer.alloc(bufSize);
|
|
109841
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
108096
109842
|
assertNotAborted();
|
|
108097
|
-
|
|
108098
|
-
|
|
108099
|
-
|
|
108100
|
-
|
|
108101
|
-
|
|
108102
|
-
await
|
|
108103
|
-
|
|
108104
|
-
|
|
108105
|
-
|
|
108106
|
-
|
|
108107
|
-
|
|
109843
|
+
const time = i / job.config.fps;
|
|
109844
|
+
await domSession.page.evaluate((t) => {
|
|
109845
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109846
|
+
}, time);
|
|
109847
|
+
if (beforeCaptureHook) {
|
|
109848
|
+
await beforeCaptureHook(domSession.page, time);
|
|
109849
|
+
}
|
|
109850
|
+
const stackingInfo = await queryElementStacking(domSession.page, nativeHdrVideoIds);
|
|
109851
|
+
const activeTransition = transitionRanges.find(
|
|
109852
|
+
(t) => i >= t.startFrame && i <= t.endFrame
|
|
109853
|
+
);
|
|
109854
|
+
if (i % 30 === 0) {
|
|
109855
|
+
const hdrEl = stackingInfo.find((e) => e.isHdr);
|
|
109856
|
+
log.debug("[Render] HDR layer composite frame", {
|
|
109857
|
+
frame: i,
|
|
109858
|
+
time: time.toFixed(2),
|
|
109859
|
+
hdrElement: hdrEl ? { z: hdrEl.zIndex, visible: hdrEl.visible, width: hdrEl.width } : null,
|
|
109860
|
+
stackingCount: stackingInfo.length,
|
|
109861
|
+
activeTransition: activeTransition?.shader
|
|
109862
|
+
});
|
|
109863
|
+
}
|
|
109864
|
+
if (activeTransition && transBufferA && transBufferB && transOutput) {
|
|
109865
|
+
const progress = activeTransition.endFrame === activeTransition.startFrame ? 1 : (i - activeTransition.startFrame) / (activeTransition.endFrame - activeTransition.startFrame);
|
|
109866
|
+
const sceneAIds = new Set(sceneElements[activeTransition.fromScene] ?? []);
|
|
109867
|
+
const sceneBIds = new Set(sceneElements[activeTransition.toScene] ?? []);
|
|
109868
|
+
transBufferA.fill(0);
|
|
109869
|
+
transBufferB.fill(0);
|
|
109870
|
+
for (const [sceneBuf, sceneIds] of [
|
|
109871
|
+
[transBufferA, sceneAIds],
|
|
109872
|
+
[transBufferB, sceneBIds]
|
|
109873
|
+
]) {
|
|
109874
|
+
await domSession.page.evaluate((t) => {
|
|
109875
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109876
|
+
}, time);
|
|
109877
|
+
if (beforeCaptureHook) {
|
|
109878
|
+
await beforeCaptureHook(domSession.page, time);
|
|
109879
|
+
}
|
|
109880
|
+
for (const el of stackingInfo) {
|
|
109881
|
+
if (!el.isHdr || !sceneIds.has(el.id)) continue;
|
|
109882
|
+
blitHdrVideoLayer(
|
|
109883
|
+
sceneBuf,
|
|
109884
|
+
el,
|
|
109885
|
+
time,
|
|
109886
|
+
job.config.fps,
|
|
109887
|
+
hdrFrameDirs,
|
|
109888
|
+
hdrVideoStartTimes,
|
|
109889
|
+
width,
|
|
109890
|
+
height,
|
|
109891
|
+
log,
|
|
109892
|
+
videoTransfers.get(el.id),
|
|
109893
|
+
effectiveHdr?.transfer
|
|
109894
|
+
);
|
|
109895
|
+
}
|
|
109896
|
+
const showIds = Array.from(sceneIds);
|
|
109897
|
+
const hideIds = stackingInfo.map((e) => e.id).filter((id) => !sceneIds.has(id) || nativeHdrVideoIds.has(id));
|
|
109898
|
+
await applyDomLayerMask(domSession.page, showIds, hideIds);
|
|
109899
|
+
const domPng = await captureAlphaPng(domSession.page, width, height);
|
|
109900
|
+
await removeDomLayerMask(domSession.page, hideIds);
|
|
109901
|
+
try {
|
|
109902
|
+
const { data: domRgba } = decodePng(domPng);
|
|
109903
|
+
if (!effectiveHdr) {
|
|
109904
|
+
throw new Error(
|
|
109905
|
+
"Invariant violation: effectiveHdr is undefined inside hasHdrVideo branch"
|
|
109906
|
+
);
|
|
109907
|
+
}
|
|
109908
|
+
blitRgba8OverRgb48le(
|
|
109909
|
+
domRgba,
|
|
109910
|
+
sceneBuf,
|
|
109911
|
+
width,
|
|
109912
|
+
height,
|
|
109913
|
+
effectiveHdr.transfer
|
|
109914
|
+
);
|
|
109915
|
+
} catch (err) {
|
|
109916
|
+
log.warn("DOM layer decode/blit failed; skipping overlay for transition scene", {
|
|
109917
|
+
frameIndex: i,
|
|
109918
|
+
sceneIds: Array.from(sceneIds),
|
|
109919
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109920
|
+
});
|
|
109921
|
+
}
|
|
109922
|
+
}
|
|
109923
|
+
const transitionFn = TRANSITIONS[activeTransition.shader] ?? crossfade;
|
|
109924
|
+
transitionFn(transBufferA, transBufferB, transOutput, width, height, progress);
|
|
109925
|
+
hdrEncoder.writeFrame(transOutput);
|
|
109926
|
+
} else {
|
|
109927
|
+
normalCanvas.fill(0);
|
|
109928
|
+
await compositeToBuffer(normalCanvas, time, stackingInfo, void 0, i);
|
|
109929
|
+
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
109930
|
+
const previewPath = join15(
|
|
109931
|
+
debugDumpDir,
|
|
109932
|
+
`frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`
|
|
109933
|
+
);
|
|
109934
|
+
writeFileSync4(previewPath, normalCanvas);
|
|
109935
|
+
}
|
|
109936
|
+
hdrEncoder.writeFrame(normalCanvas);
|
|
109937
|
+
}
|
|
109938
|
+
if (process.env.KEEP_TEMP !== "1") {
|
|
109939
|
+
for (const [videoId, endTime] of hdrVideoEndTimes) {
|
|
109940
|
+
if (time > endTime && !cleanedUpVideos.has(videoId)) {
|
|
109941
|
+
const stillNeeded = activeTransition && (sceneElements[activeTransition.fromScene]?.includes(videoId) || sceneElements[activeTransition.toScene]?.includes(videoId));
|
|
109942
|
+
if (!stillNeeded) {
|
|
109943
|
+
const frameDir = hdrFrameDirs.get(videoId);
|
|
109944
|
+
if (frameDir) {
|
|
109945
|
+
try {
|
|
109946
|
+
rmSync3(frameDir, { recursive: true, force: true });
|
|
109947
|
+
} catch (err) {
|
|
109948
|
+
log.warn("Failed to clean up HDR frame directory", {
|
|
109949
|
+
videoId,
|
|
109950
|
+
frameDir,
|
|
109951
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109952
|
+
});
|
|
109953
|
+
}
|
|
109954
|
+
}
|
|
109955
|
+
cleanedUpVideos.add(videoId);
|
|
109956
|
+
}
|
|
109957
|
+
}
|
|
109958
|
+
}
|
|
109959
|
+
}
|
|
109960
|
+
job.framesRendered = i + 1;
|
|
109961
|
+
if ((i + 1) % 10 === 0 || i + 1 === totalFrames) {
|
|
109962
|
+
const frameProgress = (i + 1) / totalFrames;
|
|
108108
109963
|
updateJobStatus(
|
|
108109
109964
|
job,
|
|
108110
109965
|
"rendering",
|
|
108111
|
-
`
|
|
108112
|
-
Math.round(
|
|
109966
|
+
`HDR composite frame ${i + 1}/${job.totalFrames}`,
|
|
109967
|
+
Math.round(25 + frameProgress * 55),
|
|
108113
109968
|
onProgress
|
|
108114
109969
|
);
|
|
108115
109970
|
}
|
|
108116
|
-
} finally {
|
|
108117
|
-
lastBrowserConsole = session.browserConsoleBuffer;
|
|
108118
|
-
await closeCaptureSession(session);
|
|
108119
109971
|
}
|
|
109972
|
+
} finally {
|
|
109973
|
+
lastBrowserConsole = domSession.browserConsoleBuffer;
|
|
109974
|
+
await closeCaptureSession(domSession);
|
|
108120
109975
|
}
|
|
108121
|
-
const
|
|
109976
|
+
const hdrEncodeResult = await hdrEncoder.close();
|
|
108122
109977
|
assertNotAborted();
|
|
108123
|
-
if (!
|
|
108124
|
-
throw new Error(`
|
|
109978
|
+
if (!hdrEncodeResult.success) {
|
|
109979
|
+
throw new Error(`HDR encode failed: ${hdrEncodeResult.error}`);
|
|
108125
109980
|
}
|
|
108126
109981
|
perfStages.captureMs = Date.now() - stage4Start;
|
|
108127
|
-
perfStages.encodeMs =
|
|
109982
|
+
perfStages.encodeMs = hdrEncodeResult.durationMs;
|
|
108128
109983
|
} else {
|
|
108129
|
-
|
|
108130
|
-
|
|
108131
|
-
await
|
|
108132
|
-
|
|
108133
|
-
|
|
108134
|
-
|
|
108135
|
-
|
|
108136
|
-
|
|
108137
|
-
|
|
108138
|
-
|
|
108139
|
-
|
|
108140
|
-
|
|
108141
|
-
|
|
108142
|
-
|
|
109984
|
+
let streamingEncoder = null;
|
|
109985
|
+
if (enableStreamingEncode) {
|
|
109986
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
109987
|
+
videoOnlyPath,
|
|
109988
|
+
{
|
|
109989
|
+
fps: job.config.fps,
|
|
109990
|
+
width,
|
|
109991
|
+
height,
|
|
109992
|
+
codec: preset.codec,
|
|
109993
|
+
preset: preset.preset,
|
|
109994
|
+
quality: preset.quality,
|
|
109995
|
+
pixelFormat: preset.pixelFormat,
|
|
109996
|
+
useGpu: job.config.useGpu,
|
|
109997
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
109998
|
+
hdr: preset.hdr
|
|
109999
|
+
},
|
|
110000
|
+
abortSignal
|
|
110001
|
+
);
|
|
110002
|
+
assertNotAborted();
|
|
110003
|
+
}
|
|
110004
|
+
if (enableStreamingEncode && streamingEncoder) {
|
|
110005
|
+
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
110006
|
+
const currentEncoder = streamingEncoder;
|
|
110007
|
+
if (workerCount > 1) {
|
|
110008
|
+
const tasks = distributeFrames(job.totalFrames, workerCount, workDir);
|
|
110009
|
+
const onFrameBuffer = async (frameIndex, buffer) => {
|
|
110010
|
+
await reorderBuffer.waitForFrame(frameIndex);
|
|
110011
|
+
currentEncoder.writeFrame(buffer);
|
|
110012
|
+
reorderBuffer.advanceTo(frameIndex + 1);
|
|
110013
|
+
};
|
|
110014
|
+
await executeParallelCapture(
|
|
110015
|
+
fileServer.url,
|
|
110016
|
+
workDir,
|
|
110017
|
+
tasks,
|
|
110018
|
+
captureOptions,
|
|
110019
|
+
() => createVideoFrameInjector(frameLookup),
|
|
110020
|
+
abortSignal,
|
|
110021
|
+
(progress) => {
|
|
110022
|
+
job.framesRendered = progress.capturedFrames;
|
|
110023
|
+
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
110024
|
+
const progressPct = 25 + frameProgress * 55;
|
|
110025
|
+
if (progress.capturedFrames % 30 === 0 || progress.capturedFrames === progress.totalFrames) {
|
|
110026
|
+
updateJobStatus(
|
|
110027
|
+
job,
|
|
110028
|
+
"rendering",
|
|
110029
|
+
`Streaming frame ${progress.capturedFrames}/${progress.totalFrames} (${workerCount} workers)`,
|
|
110030
|
+
Math.round(progressPct),
|
|
110031
|
+
onProgress
|
|
110032
|
+
);
|
|
110033
|
+
}
|
|
110034
|
+
},
|
|
110035
|
+
onFrameBuffer,
|
|
110036
|
+
cfg
|
|
110037
|
+
);
|
|
110038
|
+
if (probeSession) {
|
|
110039
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
110040
|
+
await closeCaptureSession(probeSession);
|
|
110041
|
+
probeSession = null;
|
|
110042
|
+
}
|
|
110043
|
+
} else {
|
|
110044
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
110045
|
+
const session = probeSession ?? await createCaptureSession(
|
|
110046
|
+
fileServer.url,
|
|
110047
|
+
framesDir,
|
|
110048
|
+
captureOptions,
|
|
110049
|
+
videoInjector,
|
|
110050
|
+
cfg
|
|
110051
|
+
);
|
|
110052
|
+
if (probeSession) {
|
|
110053
|
+
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
110054
|
+
probeSession = null;
|
|
110055
|
+
}
|
|
110056
|
+
try {
|
|
110057
|
+
if (!session.isInitialized) {
|
|
110058
|
+
await initializeSession(session);
|
|
110059
|
+
}
|
|
110060
|
+
assertNotAborted();
|
|
110061
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110062
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
110063
|
+
assertNotAborted();
|
|
110064
|
+
const time = i / job.config.fps;
|
|
110065
|
+
const { buffer } = await captureFrameToBuffer(session, i, time);
|
|
110066
|
+
await reorderBuffer.waitForFrame(i);
|
|
110067
|
+
currentEncoder.writeFrame(buffer);
|
|
110068
|
+
reorderBuffer.advanceTo(i + 1);
|
|
110069
|
+
job.framesRendered = i + 1;
|
|
110070
|
+
const frameProgress = (i + 1) / totalFrames;
|
|
110071
|
+
const progress = 25 + frameProgress * 55;
|
|
108143
110072
|
updateJobStatus(
|
|
108144
110073
|
job,
|
|
108145
110074
|
"rendering",
|
|
108146
|
-
`
|
|
108147
|
-
Math.round(
|
|
110075
|
+
`Streaming frame ${i + 1}/${job.totalFrames}`,
|
|
110076
|
+
Math.round(progress),
|
|
108148
110077
|
onProgress
|
|
108149
110078
|
);
|
|
108150
110079
|
}
|
|
108151
|
-
}
|
|
108152
|
-
|
|
108153
|
-
|
|
108154
|
-
|
|
108155
|
-
await mergeWorkerFrames(workDir, tasks, framesDir);
|
|
108156
|
-
if (probeSession) {
|
|
108157
|
-
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
108158
|
-
await closeCaptureSession(probeSession);
|
|
108159
|
-
probeSession = null;
|
|
110080
|
+
} finally {
|
|
110081
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110082
|
+
await closeCaptureSession(session);
|
|
110083
|
+
}
|
|
108160
110084
|
}
|
|
108161
|
-
|
|
108162
|
-
|
|
108163
|
-
|
|
108164
|
-
|
|
108165
|
-
framesDir,
|
|
108166
|
-
captureOptions,
|
|
108167
|
-
videoInjector,
|
|
108168
|
-
cfg
|
|
108169
|
-
);
|
|
108170
|
-
if (probeSession) {
|
|
108171
|
-
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
108172
|
-
probeSession = null;
|
|
110085
|
+
const encodeResult = await currentEncoder.close();
|
|
110086
|
+
assertNotAborted();
|
|
110087
|
+
if (!encodeResult.success) {
|
|
110088
|
+
throw new Error(`Streaming encode failed: ${encodeResult.error}`);
|
|
108173
110089
|
}
|
|
108174
|
-
|
|
108175
|
-
|
|
108176
|
-
|
|
110090
|
+
perfStages.captureMs = Date.now() - stage4Start;
|
|
110091
|
+
perfStages.encodeMs = encodeResult.durationMs;
|
|
110092
|
+
} else {
|
|
110093
|
+
if (workerCount > 1) {
|
|
110094
|
+
const tasks = distributeFrames(job.totalFrames, workerCount, workDir);
|
|
110095
|
+
await executeParallelCapture(
|
|
110096
|
+
fileServer.url,
|
|
110097
|
+
workDir,
|
|
110098
|
+
tasks,
|
|
110099
|
+
captureOptions,
|
|
110100
|
+
() => createVideoFrameInjector(frameLookup),
|
|
110101
|
+
abortSignal,
|
|
110102
|
+
(progress) => {
|
|
110103
|
+
job.framesRendered = progress.capturedFrames;
|
|
110104
|
+
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
110105
|
+
const progressPct = 25 + frameProgress * 45;
|
|
110106
|
+
if (progress.capturedFrames % 30 === 0 || progress.capturedFrames === progress.totalFrames) {
|
|
110107
|
+
updateJobStatus(
|
|
110108
|
+
job,
|
|
110109
|
+
"rendering",
|
|
110110
|
+
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${workerCount} workers)`,
|
|
110111
|
+
Math.round(progressPct),
|
|
110112
|
+
onProgress
|
|
110113
|
+
);
|
|
110114
|
+
}
|
|
110115
|
+
},
|
|
110116
|
+
void 0,
|
|
110117
|
+
cfg
|
|
110118
|
+
);
|
|
110119
|
+
await mergeWorkerFrames(workDir, tasks, framesDir);
|
|
110120
|
+
if (probeSession) {
|
|
110121
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
110122
|
+
await closeCaptureSession(probeSession);
|
|
110123
|
+
probeSession = null;
|
|
108177
110124
|
}
|
|
108178
|
-
|
|
108179
|
-
|
|
108180
|
-
|
|
108181
|
-
|
|
108182
|
-
|
|
108183
|
-
|
|
108184
|
-
|
|
108185
|
-
|
|
108186
|
-
|
|
108187
|
-
|
|
108188
|
-
|
|
108189
|
-
|
|
108190
|
-
`Capturing frame ${i + 1}/${job.totalFrames}`,
|
|
108191
|
-
Math.round(progress),
|
|
108192
|
-
onProgress
|
|
108193
|
-
);
|
|
110125
|
+
} else {
|
|
110126
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
110127
|
+
const session = probeSession ?? await createCaptureSession(
|
|
110128
|
+
fileServer.url,
|
|
110129
|
+
framesDir,
|
|
110130
|
+
captureOptions,
|
|
110131
|
+
videoInjector,
|
|
110132
|
+
cfg
|
|
110133
|
+
);
|
|
110134
|
+
if (probeSession) {
|
|
110135
|
+
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
110136
|
+
probeSession = null;
|
|
108194
110137
|
}
|
|
108195
|
-
|
|
108196
|
-
|
|
108197
|
-
|
|
110138
|
+
try {
|
|
110139
|
+
if (!session.isInitialized) {
|
|
110140
|
+
await initializeSession(session);
|
|
110141
|
+
}
|
|
110142
|
+
assertNotAborted();
|
|
110143
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110144
|
+
for (let i = 0; i < job.totalFrames; i++) {
|
|
110145
|
+
assertNotAborted();
|
|
110146
|
+
const time = i / job.config.fps;
|
|
110147
|
+
await captureFrame(session, i, time);
|
|
110148
|
+
job.framesRendered = i + 1;
|
|
110149
|
+
const frameProgress = (i + 1) / job.totalFrames;
|
|
110150
|
+
const progress = 25 + frameProgress * 45;
|
|
110151
|
+
updateJobStatus(
|
|
110152
|
+
job,
|
|
110153
|
+
"rendering",
|
|
110154
|
+
`Capturing frame ${i + 1}/${job.totalFrames}`,
|
|
110155
|
+
Math.round(progress),
|
|
110156
|
+
onProgress
|
|
110157
|
+
);
|
|
110158
|
+
}
|
|
110159
|
+
} finally {
|
|
110160
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110161
|
+
await closeCaptureSession(session);
|
|
110162
|
+
}
|
|
110163
|
+
}
|
|
110164
|
+
perfStages.captureMs = Date.now() - stage4Start;
|
|
110165
|
+
const stage5Start = Date.now();
|
|
110166
|
+
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
|
110167
|
+
const frameExt = needsAlpha ? "png" : "jpg";
|
|
110168
|
+
const framePattern = `frame_%06d.${frameExt}`;
|
|
110169
|
+
const encoderOpts = {
|
|
110170
|
+
fps: job.config.fps,
|
|
110171
|
+
width,
|
|
110172
|
+
height,
|
|
110173
|
+
codec: preset.codec,
|
|
110174
|
+
preset: preset.preset,
|
|
110175
|
+
quality: preset.quality,
|
|
110176
|
+
pixelFormat: preset.pixelFormat,
|
|
110177
|
+
useGpu: job.config.useGpu,
|
|
110178
|
+
hdr: preset.hdr
|
|
110179
|
+
};
|
|
110180
|
+
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
110181
|
+
framesDir,
|
|
110182
|
+
framePattern,
|
|
110183
|
+
videoOnlyPath,
|
|
110184
|
+
encoderOpts,
|
|
110185
|
+
chunkedEncodeSize,
|
|
110186
|
+
abortSignal
|
|
110187
|
+
) : await encodeFramesFromDir(
|
|
110188
|
+
framesDir,
|
|
110189
|
+
framePattern,
|
|
110190
|
+
videoOnlyPath,
|
|
110191
|
+
encoderOpts,
|
|
110192
|
+
abortSignal
|
|
110193
|
+
);
|
|
110194
|
+
assertNotAborted();
|
|
110195
|
+
if (!encodeResult.success) {
|
|
110196
|
+
throw new Error(`Encoding failed: ${encodeResult.error}`);
|
|
108198
110197
|
}
|
|
110198
|
+
perfStages.encodeMs = Date.now() - stage5Start;
|
|
108199
110199
|
}
|
|
108200
|
-
perfStages.captureMs = Date.now() - stage4Start;
|
|
108201
|
-
const stage5Start = Date.now();
|
|
108202
|
-
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
|
108203
|
-
const frameExt = needsAlpha ? "png" : "jpg";
|
|
108204
|
-
const framePattern = `frame_%06d.${frameExt}`;
|
|
108205
|
-
const encoderOpts = baseEncoderOpts;
|
|
108206
|
-
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
108207
|
-
framesDir,
|
|
108208
|
-
framePattern,
|
|
108209
|
-
videoOnlyPath,
|
|
108210
|
-
encoderOpts,
|
|
108211
|
-
chunkedEncodeSize,
|
|
108212
|
-
abortSignal
|
|
108213
|
-
) : await encodeFramesFromDir(
|
|
108214
|
-
framesDir,
|
|
108215
|
-
framePattern,
|
|
108216
|
-
videoOnlyPath,
|
|
108217
|
-
encoderOpts,
|
|
108218
|
-
abortSignal
|
|
108219
|
-
);
|
|
108220
|
-
assertNotAborted();
|
|
108221
|
-
if (!encodeResult.success) {
|
|
108222
|
-
throw new Error(`Encoding failed: ${encodeResult.error}`);
|
|
108223
|
-
}
|
|
108224
|
-
perfStages.encodeMs = Date.now() - stage5Start;
|
|
108225
110200
|
}
|
|
108226
110201
|
if (probeSession !== null) {
|
|
108227
110202
|
const remainingProbeSession = probeSession;
|
|
@@ -108265,12 +110240,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108265
110240
|
chunkedEncode: enableChunkedEncode,
|
|
108266
110241
|
chunkSizeFrames: enableChunkedEncode ? chunkedEncodeSize : null,
|
|
108267
110242
|
compositionDurationSeconds: composition.duration,
|
|
108268
|
-
totalFrames
|
|
110243
|
+
totalFrames,
|
|
108269
110244
|
resolution: { width, height },
|
|
108270
110245
|
videoCount: composition.videos.length,
|
|
108271
110246
|
audioCount: composition.audios.length,
|
|
108272
110247
|
stages: perfStages,
|
|
108273
|
-
captureAvgMs:
|
|
110248
|
+
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0
|
|
108274
110249
|
};
|
|
108275
110250
|
job.perfSummary = perfSummary;
|
|
108276
110251
|
if (job.config.debug) {
|
|
@@ -108288,6 +110263,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108288
110263
|
const debugOutput = join15(workDir, `output${videoExt}`);
|
|
108289
110264
|
copyFileSync2(outputPath, debugOutput);
|
|
108290
110265
|
}
|
|
110266
|
+
} else if (process.env.KEEP_TEMP === "1") {
|
|
110267
|
+
log.info("KEEP_TEMP=1 \u2014 leaving workDir on disk for inspection", { workDir });
|
|
108291
110268
|
} else {
|
|
108292
110269
|
await safeCleanup(
|
|
108293
110270
|
"remove workDir",
|