@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/public-server.js
CHANGED
|
@@ -5117,12 +5117,12 @@ var require_common = __commonJS({
|
|
|
5117
5117
|
createDebug.skips = [];
|
|
5118
5118
|
createDebug.formatters = {};
|
|
5119
5119
|
function selectColor(namespace) {
|
|
5120
|
-
let
|
|
5120
|
+
let hash2 = 0;
|
|
5121
5121
|
for (let i = 0; i < namespace.length; i++) {
|
|
5122
|
-
|
|
5123
|
-
|
|
5122
|
+
hash2 = (hash2 << 5) - hash2 + namespace.charCodeAt(i);
|
|
5123
|
+
hash2 |= 0;
|
|
5124
5124
|
}
|
|
5125
|
-
return createDebug.colors[Math.abs(
|
|
5125
|
+
return createDebug.colors[Math.abs(hash2) % createDebug.colors.length];
|
|
5126
5126
|
}
|
|
5127
5127
|
createDebug.selectColor = selectColor;
|
|
5128
5128
|
function createDebug(namespace) {
|
|
@@ -54350,25 +54350,25 @@ var require_data = __commonJS({
|
|
|
54350
54350
|
var notmodified_1 = __importDefault2(require_notmodified());
|
|
54351
54351
|
var debug6 = (0, debug_1.default)("get-uri:data");
|
|
54352
54352
|
var DataReadable = class extends stream_1.Readable {
|
|
54353
|
-
constructor(
|
|
54353
|
+
constructor(hash2, buf) {
|
|
54354
54354
|
super();
|
|
54355
54355
|
this.push(buf);
|
|
54356
54356
|
this.push(null);
|
|
54357
|
-
this.hash =
|
|
54357
|
+
this.hash = hash2;
|
|
54358
54358
|
}
|
|
54359
54359
|
};
|
|
54360
54360
|
var data = async ({ href: uri }, { cache } = {}) => {
|
|
54361
54361
|
const shasum = (0, crypto_1.createHash)("sha1");
|
|
54362
54362
|
shasum.update(uri);
|
|
54363
|
-
const
|
|
54364
|
-
debug6('generated SHA1 hash for "data:" URI: %o',
|
|
54365
|
-
if (cache?.hash ===
|
|
54366
|
-
debug6("got matching cache SHA1 hash: %o",
|
|
54363
|
+
const hash2 = shasum.digest("hex");
|
|
54364
|
+
debug6('generated SHA1 hash for "data:" URI: %o', hash2);
|
|
54365
|
+
if (cache?.hash === hash2) {
|
|
54366
|
+
debug6("got matching cache SHA1 hash: %o", hash2);
|
|
54367
54367
|
throw new notmodified_1.default();
|
|
54368
54368
|
} else {
|
|
54369
54369
|
debug6('creating Readable stream from "data:" URI buffer');
|
|
54370
54370
|
const { buffer } = (0, data_uri_to_buffer_1.dataUriToBuffer)(uri);
|
|
54371
|
-
return new DataReadable(
|
|
54371
|
+
return new DataReadable(hash2, Buffer.from(buffer));
|
|
54372
54372
|
}
|
|
54373
54373
|
};
|
|
54374
54374
|
exports.data = data;
|
|
@@ -75363,14 +75363,14 @@ var require_dist10 = __commonJS({
|
|
|
75363
75363
|
(0, quickjs_emscripten_1.getQuickJS)(),
|
|
75364
75364
|
this.loadPacFile()
|
|
75365
75365
|
]);
|
|
75366
|
-
const
|
|
75367
|
-
if (this.resolver && this.resolverHash ===
|
|
75366
|
+
const hash2 = crypto3.createHash("sha1").update(code).digest("hex");
|
|
75367
|
+
if (this.resolver && this.resolverHash === hash2) {
|
|
75368
75368
|
debug6("Same sha1 hash for code - contents have not changed, reusing previous proxy resolver");
|
|
75369
75369
|
return this.resolver;
|
|
75370
75370
|
}
|
|
75371
75371
|
debug6("Creating new proxy resolver instance");
|
|
75372
75372
|
this.resolver = (0, pac_resolver_1.createPacResolver)(qjs, code, this.opts);
|
|
75373
|
-
this.resolverHash =
|
|
75373
|
+
this.resolverHash = hash2;
|
|
75374
75374
|
return this.resolver;
|
|
75375
75375
|
} catch (err) {
|
|
75376
75376
|
if (this.resolver && err.code === "ENOTMODIFIED") {
|
|
@@ -92210,6 +92210,7 @@ import {
|
|
|
92210
92210
|
mkdirSync as mkdirSync10,
|
|
92211
92211
|
rmSync as rmSync3,
|
|
92212
92212
|
readFileSync as readFileSync9,
|
|
92213
|
+
readdirSync as readdirSync6,
|
|
92213
92214
|
writeFileSync as writeFileSync4,
|
|
92214
92215
|
copyFileSync as copyFileSync2,
|
|
92215
92216
|
appendFileSync
|
|
@@ -101457,6 +101458,8 @@ var DEFAULT_CONFIG = {
|
|
|
101457
101458
|
ffmpegEncodeTimeout: 6e5,
|
|
101458
101459
|
ffmpegProcessTimeout: 3e5,
|
|
101459
101460
|
ffmpegStreamingTimeout: 6e5,
|
|
101461
|
+
hdr: false,
|
|
101462
|
+
hdrAutoDetect: true,
|
|
101460
101463
|
audioGain: 1.35,
|
|
101461
101464
|
frameDataUriCacheLimit: 256,
|
|
101462
101465
|
playerReadyTimeout: 45e3,
|
|
@@ -101513,6 +101516,12 @@ function resolveConfig(overrides) {
|
|
|
101513
101516
|
"FFMPEG_STREAMING_TIMEOUT_MS",
|
|
101514
101517
|
DEFAULT_CONFIG.ffmpegStreamingTimeout
|
|
101515
101518
|
),
|
|
101519
|
+
hdr: (() => {
|
|
101520
|
+
const raw2 = env2("PRODUCER_HDR_TRANSFER");
|
|
101521
|
+
if (raw2 === "hlg" || raw2 === "pq") return { transfer: raw2 };
|
|
101522
|
+
return void 0;
|
|
101523
|
+
})(),
|
|
101524
|
+
hdrAutoDetect: envBool("PRODUCER_HDR_AUTO_DETECT", DEFAULT_CONFIG.hdrAutoDetect),
|
|
101516
101525
|
audioGain: envNum("PRODUCER_AUDIO_GAIN", DEFAULT_CONFIG.audioGain),
|
|
101517
101526
|
frameDataUriCacheLimit: Math.max(
|
|
101518
101527
|
32,
|
|
@@ -101709,7 +101718,8 @@ function buildChromeArgs(options, config2) {
|
|
|
101709
101718
|
"--font-render-hinting=none",
|
|
101710
101719
|
"--force-color-profile=srgb",
|
|
101711
101720
|
`--window-size=${options.width},${options.height}`,
|
|
101712
|
-
//
|
|
101721
|
+
// Prevent Chrome from throttling background tabs/timers — critical when the
|
|
101722
|
+
// page is offscreen during headless capture
|
|
101713
101723
|
"--disable-background-timer-throttling",
|
|
101714
101724
|
"--disable-backgrounding-occluded-windows",
|
|
101715
101725
|
"--disable-renderer-backgrounding",
|
|
@@ -102423,6 +102433,7 @@ var mediaRules = [
|
|
|
102423
102433
|
const timedTagPositions = [];
|
|
102424
102434
|
for (const tag of tags) {
|
|
102425
102435
|
if (tag.name === "video" || tag.name === "audio") continue;
|
|
102436
|
+
if (readAttr(tag.raw, "data-composition-id")) continue;
|
|
102426
102437
|
if (readAttr(tag.raw, "data-start")) {
|
|
102427
102438
|
timedTagPositions.push({
|
|
102428
102439
|
name: tag.name,
|
|
@@ -103529,7 +103540,8 @@ function lintHyperframeHtml(html, options = {}) {
|
|
|
103529
103540
|
}
|
|
103530
103541
|
|
|
103531
103542
|
// ../core/src/compiler/rewriteSubCompPaths.ts
|
|
103532
|
-
import {
|
|
103543
|
+
import { posix } from "path";
|
|
103544
|
+
var { join: join4, resolve: resolve6, dirname: dirname4 } = posix;
|
|
103533
103545
|
var PATH_ATTRS = ["src", "href"];
|
|
103534
103546
|
var CSS_URL_RE = /\burl\(\s*(["']?)([^)"']+)\1\s*\)/g;
|
|
103535
103547
|
function isAbsoluteOrSpecial(val) {
|
|
@@ -103696,6 +103708,83 @@ async function pageScreenshotCapture(page, options) {
|
|
|
103696
103708
|
});
|
|
103697
103709
|
return Buffer.from(result.data, "base64");
|
|
103698
103710
|
}
|
|
103711
|
+
var TRANSPARENT_BG_STYLE_ID = "__hf_transparent_bg__";
|
|
103712
|
+
async function initTransparentBackground(page) {
|
|
103713
|
+
const client = await getCdpSession(page);
|
|
103714
|
+
await client.send("Emulation.setDefaultBackgroundColorOverride", {
|
|
103715
|
+
color: { r: 0, g: 0, b: 0, a: 0 }
|
|
103716
|
+
});
|
|
103717
|
+
await page.evaluate((styleId) => {
|
|
103718
|
+
if (document.getElementById(styleId)) return;
|
|
103719
|
+
const style = document.createElement("style");
|
|
103720
|
+
style.id = styleId;
|
|
103721
|
+
style.textContent = "html,body,[data-composition-id]{background:transparent !important;background-color:transparent !important;background-image:none !important;}";
|
|
103722
|
+
document.head.appendChild(style);
|
|
103723
|
+
}, TRANSPARENT_BG_STYLE_ID);
|
|
103724
|
+
}
|
|
103725
|
+
async function captureAlphaPng(page, width, height) {
|
|
103726
|
+
const client = await getCdpSession(page);
|
|
103727
|
+
const result = await client.send("Page.captureScreenshot", {
|
|
103728
|
+
format: "png",
|
|
103729
|
+
fromSurface: true,
|
|
103730
|
+
captureBeyondViewport: false,
|
|
103731
|
+
optimizeForSpeed: false,
|
|
103732
|
+
// must be false to preserve alpha
|
|
103733
|
+
clip: { x: 0, y: 0, width, height, scale: 1 }
|
|
103734
|
+
});
|
|
103735
|
+
return Buffer.from(result.data, "base64");
|
|
103736
|
+
}
|
|
103737
|
+
var DOM_LAYER_MASK_STYLE_ID = "__hf_dom_layer_mask__";
|
|
103738
|
+
async function applyDomLayerMask(page, showIds, extraHideIds) {
|
|
103739
|
+
await page.evaluate(
|
|
103740
|
+
(args) => {
|
|
103741
|
+
const existing = document.getElementById(args.styleId);
|
|
103742
|
+
if (existing) existing.remove();
|
|
103743
|
+
const showSelectors = [];
|
|
103744
|
+
for (const id of args.show) {
|
|
103745
|
+
const escaped = CSS.escape(id);
|
|
103746
|
+
showSelectors.push(`#${escaped}`, `#${escaped} *`);
|
|
103747
|
+
const renderEscaped = CSS.escape(`__render_frame_${id}__`);
|
|
103748
|
+
showSelectors.push(`#${renderEscaped}`, `#${renderEscaped} *`);
|
|
103749
|
+
}
|
|
103750
|
+
const massHideRule = "body *{visibility:hidden !important;}";
|
|
103751
|
+
const showRule = showSelectors.length === 0 ? "" : `${showSelectors.join(",")}{visibility:visible !important;}`;
|
|
103752
|
+
const style = document.createElement("style");
|
|
103753
|
+
style.id = args.styleId;
|
|
103754
|
+
style.textContent = `${massHideRule}
|
|
103755
|
+
${showRule}`;
|
|
103756
|
+
document.head.appendChild(style);
|
|
103757
|
+
for (const id of args.hide) {
|
|
103758
|
+
const el = document.getElementById(id);
|
|
103759
|
+
if (el) {
|
|
103760
|
+
el.style.setProperty("visibility", "hidden", "important");
|
|
103761
|
+
}
|
|
103762
|
+
const img = document.getElementById(`__render_frame_${id}__`);
|
|
103763
|
+
if (img) {
|
|
103764
|
+
img.style.setProperty("visibility", "hidden", "important");
|
|
103765
|
+
}
|
|
103766
|
+
}
|
|
103767
|
+
},
|
|
103768
|
+
{ show: showIds, hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID }
|
|
103769
|
+
);
|
|
103770
|
+
}
|
|
103771
|
+
async function removeDomLayerMask(page, extraHideIds) {
|
|
103772
|
+
await page.evaluate(
|
|
103773
|
+
(args) => {
|
|
103774
|
+
const style = document.getElementById(args.styleId);
|
|
103775
|
+
if (style) style.remove();
|
|
103776
|
+
for (const id of args.hide) {
|
|
103777
|
+
const el = document.getElementById(id);
|
|
103778
|
+
if (el) {
|
|
103779
|
+
el.style.removeProperty("visibility");
|
|
103780
|
+
}
|
|
103781
|
+
const img = document.getElementById(`__render_frame_${id}__`);
|
|
103782
|
+
if (img) img.style.removeProperty("visibility");
|
|
103783
|
+
}
|
|
103784
|
+
},
|
|
103785
|
+
{ hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID }
|
|
103786
|
+
);
|
|
103787
|
+
}
|
|
103699
103788
|
async function injectVideoFramesBatch(page, updates) {
|
|
103700
103789
|
if (updates.length === 0) return;
|
|
103701
103790
|
await page.evaluate(
|
|
@@ -103717,16 +103806,7 @@ async function injectVideoFramesBatch(page, updates) {
|
|
|
103717
103806
|
video.parentNode?.insertBefore(img, video.nextSibling);
|
|
103718
103807
|
}
|
|
103719
103808
|
if (!img) continue;
|
|
103720
|
-
|
|
103721
|
-
img.style.position = computedStyle.position;
|
|
103722
|
-
img.style.width = computedStyle.width;
|
|
103723
|
-
img.style.height = computedStyle.height;
|
|
103724
|
-
img.style.top = computedStyle.top;
|
|
103725
|
-
img.style.left = computedStyle.left;
|
|
103726
|
-
img.style.right = computedStyle.right;
|
|
103727
|
-
img.style.bottom = computedStyle.bottom;
|
|
103728
|
-
img.style.inset = computedStyle.inset;
|
|
103729
|
-
} else {
|
|
103809
|
+
{
|
|
103730
103810
|
const videoRect = video.getBoundingClientRect();
|
|
103731
103811
|
const offsetLeft = Number.isFinite(video.offsetLeft) ? video.offsetLeft : 0;
|
|
103732
103812
|
const offsetTop = Number.isFinite(video.offsetTop) ? video.offsetTop : 0;
|
|
@@ -103745,6 +103825,7 @@ async function injectVideoFramesBatch(page, updates) {
|
|
|
103745
103825
|
img.style.objectPosition = computedStyle.objectPosition;
|
|
103746
103826
|
img.style.zIndex = computedStyle.zIndex;
|
|
103747
103827
|
for (const property of visualProperties) {
|
|
103828
|
+
if (property === "opacity") continue;
|
|
103748
103829
|
if (sourceIsStatic && (property === "top" || property === "left" || property === "right" || property === "bottom" || property === "inset")) {
|
|
103749
103830
|
continue;
|
|
103750
103831
|
}
|
|
@@ -103777,14 +103858,22 @@ async function syncVideoFrameVisibility(page, activeVideoIds) {
|
|
|
103777
103858
|
const active = new Set(ids);
|
|
103778
103859
|
const videos = Array.from(document.querySelectorAll("video[data-start]"));
|
|
103779
103860
|
for (const video of videos) {
|
|
103780
|
-
if (active.has(video.id)) continue;
|
|
103781
|
-
video.style.removeProperty("display");
|
|
103782
|
-
video.style.setProperty("visibility", "hidden", "important");
|
|
103783
|
-
video.style.setProperty("opacity", "0", "important");
|
|
103784
|
-
video.style.setProperty("pointer-events", "none", "important");
|
|
103785
103861
|
const img = video.nextElementSibling;
|
|
103786
|
-
|
|
103787
|
-
|
|
103862
|
+
const hasImg = img && img.classList.contains("__render_frame__");
|
|
103863
|
+
if (active.has(video.id)) {
|
|
103864
|
+
video.style.setProperty("visibility", "hidden", "important");
|
|
103865
|
+
video.style.setProperty("pointer-events", "none", "important");
|
|
103866
|
+
if (hasImg) {
|
|
103867
|
+
img.style.visibility = "visible";
|
|
103868
|
+
}
|
|
103869
|
+
} else {
|
|
103870
|
+
video.style.removeProperty("display");
|
|
103871
|
+
video.style.setProperty("visibility", "hidden", "important");
|
|
103872
|
+
video.style.setProperty("opacity", "0", "important");
|
|
103873
|
+
video.style.setProperty("pointer-events", "none", "important");
|
|
103874
|
+
if (hasImg) {
|
|
103875
|
+
img.style.visibility = "hidden";
|
|
103876
|
+
}
|
|
103788
103877
|
}
|
|
103789
103878
|
}
|
|
103790
103879
|
}, activeVideoIds);
|
|
@@ -104241,7 +104330,7 @@ var ENCODER_PRESETS = {
|
|
|
104241
104330
|
standard: { preset: "medium", quality: 18, codec: "h264" },
|
|
104242
104331
|
high: { preset: "slow", quality: 15, codec: "h264" }
|
|
104243
104332
|
};
|
|
104244
|
-
function getEncoderPreset(quality, format3 = "mp4") {
|
|
104333
|
+
function getEncoderPreset(quality, format3 = "mp4", hdr) {
|
|
104245
104334
|
const base = ENCODER_PRESETS[quality];
|
|
104246
104335
|
if (format3 === "webm") {
|
|
104247
104336
|
return {
|
|
@@ -104259,6 +104348,15 @@ function getEncoderPreset(quality, format3 = "mp4") {
|
|
|
104259
104348
|
pixelFormat: "yuva444p10le"
|
|
104260
104349
|
};
|
|
104261
104350
|
}
|
|
104351
|
+
if (hdr) {
|
|
104352
|
+
return {
|
|
104353
|
+
preset: base.preset === "ultrafast" ? "fast" : base.preset,
|
|
104354
|
+
quality: base.quality,
|
|
104355
|
+
codec: "h265",
|
|
104356
|
+
pixelFormat: "yuv420p10le",
|
|
104357
|
+
hdr
|
|
104358
|
+
};
|
|
104359
|
+
}
|
|
104262
104360
|
return { ...base, pixelFormat: "yuv420p" };
|
|
104263
104361
|
}
|
|
104264
104362
|
function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
@@ -104316,6 +104414,9 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
104316
104414
|
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
104317
104415
|
}
|
|
104318
104416
|
}
|
|
104417
|
+
if (codec === "h265") {
|
|
104418
|
+
args.push("-tag:v", "hvc1");
|
|
104419
|
+
}
|
|
104319
104420
|
} else if (codec === "vp9") {
|
|
104320
104421
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
104321
104422
|
args.push("-deadline", preset === "ultrafast" ? "realtime" : "good");
|
|
@@ -104622,31 +104723,83 @@ async function applyFaststart(inputPath, outputPath, signal, config2) {
|
|
|
104622
104723
|
import { spawn as spawn6 } from "child_process";
|
|
104623
104724
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, statSync as statSync4 } from "fs";
|
|
104624
104725
|
import { dirname as dirname6 } from "path";
|
|
104726
|
+
|
|
104727
|
+
// ../engine/src/utils/hdr.ts
|
|
104728
|
+
function isHdrColorSpace(cs) {
|
|
104729
|
+
if (!cs) return false;
|
|
104730
|
+
return cs.colorPrimaries.includes("bt2020") || cs.colorSpace.includes("bt2020") || cs.colorTransfer === "smpte2084" || cs.colorTransfer === "arib-std-b67";
|
|
104731
|
+
}
|
|
104732
|
+
function detectTransfer(cs) {
|
|
104733
|
+
if (cs?.colorTransfer === "smpte2084") return "pq";
|
|
104734
|
+
return "hlg";
|
|
104735
|
+
}
|
|
104736
|
+
var DEFAULT_HDR10_MASTERING = {
|
|
104737
|
+
masterDisplay: "G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1)",
|
|
104738
|
+
maxCll: "1000,400"
|
|
104739
|
+
};
|
|
104740
|
+
function getHdrEncoderColorParams(transfer, mastering = DEFAULT_HDR10_MASTERING) {
|
|
104741
|
+
const colorTrc = transfer === "pq" ? "smpte2084" : "arib-std-b67";
|
|
104742
|
+
const tagging = `colorprim=bt2020:transfer=${colorTrc}:colormatrix=bt2020nc`;
|
|
104743
|
+
const metadata = `master-display=${mastering.masterDisplay}:max-cll=${mastering.maxCll}`;
|
|
104744
|
+
return {
|
|
104745
|
+
colorPrimaries: "bt2020",
|
|
104746
|
+
colorTrc,
|
|
104747
|
+
colorspace: "bt2020nc",
|
|
104748
|
+
pixelFormat: "yuv420p10le",
|
|
104749
|
+
x265ColorParams: `${tagging}:${metadata}`,
|
|
104750
|
+
mastering
|
|
104751
|
+
};
|
|
104752
|
+
}
|
|
104753
|
+
function analyzeCompositionHdr(colorSpaces) {
|
|
104754
|
+
let hasPq = false;
|
|
104755
|
+
let hasHdr = false;
|
|
104756
|
+
for (const cs of colorSpaces) {
|
|
104757
|
+
if (!isHdrColorSpace(cs)) continue;
|
|
104758
|
+
hasHdr = true;
|
|
104759
|
+
if (cs?.colorTransfer === "smpte2084") hasPq = true;
|
|
104760
|
+
}
|
|
104761
|
+
if (!hasHdr) return { hasHdr: false, dominantTransfer: null };
|
|
104762
|
+
const dominantTransfer = hasPq ? "pq" : "hlg";
|
|
104763
|
+
return { hasHdr: true, dominantTransfer };
|
|
104764
|
+
}
|
|
104765
|
+
|
|
104766
|
+
// ../engine/src/services/streamingEncoder.ts
|
|
104625
104767
|
function createFrameReorderBuffer(startFrame, endFrame) {
|
|
104626
|
-
let
|
|
104627
|
-
|
|
104628
|
-
const
|
|
104629
|
-
|
|
104630
|
-
|
|
104631
|
-
|
|
104632
|
-
|
|
104633
|
-
|
|
104768
|
+
let cursor = startFrame;
|
|
104769
|
+
const pending = /* @__PURE__ */ new Map();
|
|
104770
|
+
const enqueueAt = (frame, resolve13) => {
|
|
104771
|
+
const list = pending.get(frame);
|
|
104772
|
+
if (list === void 0) {
|
|
104773
|
+
pending.set(frame, [resolve13]);
|
|
104774
|
+
} else {
|
|
104775
|
+
list.push(resolve13);
|
|
104634
104776
|
}
|
|
104635
104777
|
};
|
|
104636
|
-
|
|
104637
|
-
|
|
104638
|
-
|
|
104639
|
-
|
|
104640
|
-
|
|
104641
|
-
|
|
104642
|
-
|
|
104643
|
-
|
|
104644
|
-
|
|
104645
|
-
|
|
104646
|
-
|
|
104647
|
-
|
|
104648
|
-
|
|
104778
|
+
const flushAt = (frame) => {
|
|
104779
|
+
const list = pending.get(frame);
|
|
104780
|
+
if (list === void 0) return;
|
|
104781
|
+
pending.delete(frame);
|
|
104782
|
+
for (const resolve13 of list) resolve13();
|
|
104783
|
+
};
|
|
104784
|
+
const waitForFrame = (frame) => new Promise((resolve13) => {
|
|
104785
|
+
if (frame === cursor) {
|
|
104786
|
+
resolve13();
|
|
104787
|
+
return;
|
|
104788
|
+
}
|
|
104789
|
+
enqueueAt(frame, resolve13);
|
|
104790
|
+
});
|
|
104791
|
+
const advanceTo = (frame) => {
|
|
104792
|
+
cursor = frame;
|
|
104793
|
+
flushAt(frame);
|
|
104649
104794
|
};
|
|
104795
|
+
const waitForAllDone = () => new Promise((resolve13) => {
|
|
104796
|
+
if (cursor >= endFrame) {
|
|
104797
|
+
resolve13();
|
|
104798
|
+
return;
|
|
104799
|
+
}
|
|
104800
|
+
enqueueAt(endFrame, resolve13);
|
|
104801
|
+
});
|
|
104802
|
+
return { waitForFrame, advanceTo, waitForAllDone };
|
|
104650
104803
|
}
|
|
104651
104804
|
function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
104652
104805
|
const {
|
|
@@ -104659,19 +104812,36 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
104659
104812
|
useGpu = false,
|
|
104660
104813
|
imageFormat = "jpeg"
|
|
104661
104814
|
} = options;
|
|
104662
|
-
const
|
|
104663
|
-
|
|
104664
|
-
|
|
104665
|
-
"
|
|
104666
|
-
|
|
104667
|
-
|
|
104668
|
-
|
|
104669
|
-
|
|
104670
|
-
|
|
104671
|
-
|
|
104672
|
-
|
|
104673
|
-
|
|
104674
|
-
|
|
104815
|
+
const args = [];
|
|
104816
|
+
if (options.rawInputFormat) {
|
|
104817
|
+
const hdrTransfer = options.hdr?.transfer;
|
|
104818
|
+
const inputColorTrc = hdrTransfer === "pq" ? "smpte2084" : hdrTransfer === "hlg" ? "arib-std-b67" : void 0;
|
|
104819
|
+
args.push(
|
|
104820
|
+
"-f",
|
|
104821
|
+
"rawvideo",
|
|
104822
|
+
"-pix_fmt",
|
|
104823
|
+
options.rawInputFormat,
|
|
104824
|
+
"-s",
|
|
104825
|
+
`${options.width}x${options.height}`,
|
|
104826
|
+
"-framerate",
|
|
104827
|
+
String(fps)
|
|
104828
|
+
);
|
|
104829
|
+
if (inputColorTrc) {
|
|
104830
|
+
args.push(
|
|
104831
|
+
"-color_primaries",
|
|
104832
|
+
"bt2020",
|
|
104833
|
+
"-color_trc",
|
|
104834
|
+
inputColorTrc,
|
|
104835
|
+
"-colorspace",
|
|
104836
|
+
"bt2020nc"
|
|
104837
|
+
);
|
|
104838
|
+
}
|
|
104839
|
+
args.push("-i", "-");
|
|
104840
|
+
} else {
|
|
104841
|
+
const inputCodec = imageFormat === "png" ? "png" : "mjpeg";
|
|
104842
|
+
args.push("-f", "image2pipe", "-vcodec", inputCodec, "-framerate", String(fps), "-i", "-");
|
|
104843
|
+
}
|
|
104844
|
+
args.push("-r", String(fps));
|
|
104675
104845
|
const shouldUseGpu = useGpu && gpuEncoder !== null;
|
|
104676
104846
|
if (codec === "h264" || codec === "h265") {
|
|
104677
104847
|
if (shouldUseGpu) {
|
|
@@ -104709,12 +104879,15 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
104709
104879
|
if (bitrate) args.push("-b:v", bitrate);
|
|
104710
104880
|
else args.push("-crf", String(quality));
|
|
104711
104881
|
const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
|
|
104712
|
-
const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
104882
|
+
const colorParams = options.rawInputFormat && options.hdr ? getHdrEncoderColorParams(options.hdr.transfer).x265ColorParams : "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
104713
104883
|
if (preset === "ultrafast") {
|
|
104714
104884
|
args.push(xParamsFlag, `aq-mode=3:${colorParams}`);
|
|
104715
104885
|
} else {
|
|
104716
104886
|
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
104717
104887
|
}
|
|
104888
|
+
if (codec === "h265") {
|
|
104889
|
+
args.push("-tag:v", "hvc1");
|
|
104890
|
+
}
|
|
104718
104891
|
}
|
|
104719
104892
|
} else if (codec === "vp9") {
|
|
104720
104893
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
@@ -104730,17 +104903,31 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
104730
104903
|
return [...args, "-y", outputPath];
|
|
104731
104904
|
}
|
|
104732
104905
|
if (codec === "h264" || codec === "h265") {
|
|
104733
|
-
|
|
104734
|
-
|
|
104735
|
-
|
|
104736
|
-
|
|
104737
|
-
|
|
104738
|
-
|
|
104739
|
-
|
|
104740
|
-
|
|
104741
|
-
|
|
104742
|
-
|
|
104743
|
-
|
|
104906
|
+
if (options.rawInputFormat && options.hdr) {
|
|
104907
|
+
args.push(
|
|
104908
|
+
"-colorspace:v",
|
|
104909
|
+
"bt2020nc",
|
|
104910
|
+
"-color_primaries:v",
|
|
104911
|
+
"bt2020",
|
|
104912
|
+
"-color_trc:v",
|
|
104913
|
+
options.hdr.transfer === "pq" ? "smpte2084" : "arib-std-b67",
|
|
104914
|
+
"-color_range",
|
|
104915
|
+
"tv"
|
|
104916
|
+
);
|
|
104917
|
+
} else {
|
|
104918
|
+
args.push(
|
|
104919
|
+
"-colorspace:v",
|
|
104920
|
+
"bt709",
|
|
104921
|
+
"-color_primaries:v",
|
|
104922
|
+
"bt709",
|
|
104923
|
+
"-color_trc:v",
|
|
104924
|
+
"bt709",
|
|
104925
|
+
"-color_range",
|
|
104926
|
+
"tv"
|
|
104927
|
+
);
|
|
104928
|
+
}
|
|
104929
|
+
if (options.rawInputFormat) {
|
|
104930
|
+
} else if (gpuEncoder === "vaapi") {
|
|
104744
104931
|
const vfIdx = args.indexOf("-vf");
|
|
104745
104932
|
if (vfIdx !== -1) {
|
|
104746
104933
|
args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
|
|
@@ -104810,14 +104997,16 @@ Process error: ${err.message}`;
|
|
|
104810
104997
|
if (exitStatus !== "running" || !ffmpeg.stdin || ffmpeg.stdin.destroyed) {
|
|
104811
104998
|
return false;
|
|
104812
104999
|
}
|
|
104813
|
-
|
|
105000
|
+
const copy = Buffer.from(buffer);
|
|
105001
|
+
return ffmpeg.stdin.write(copy);
|
|
104814
105002
|
},
|
|
104815
105003
|
close: async () => {
|
|
104816
105004
|
clearTimeout(timer2);
|
|
104817
105005
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
104818
|
-
|
|
105006
|
+
const stdin = ffmpeg.stdin;
|
|
105007
|
+
if (stdin && !stdin.destroyed) {
|
|
104819
105008
|
await new Promise((resolve13) => {
|
|
104820
|
-
|
|
105009
|
+
stdin.end(() => resolve13());
|
|
104821
105010
|
});
|
|
104822
105011
|
}
|
|
104823
105012
|
await exitPromise;
|
|
@@ -104921,6 +105110,10 @@ async function extractVideoMetadata(filePath) {
|
|
|
104921
105110
|
const avgFps = parseFrameRate(videoStream.avg_frame_rate);
|
|
104922
105111
|
const fps = avgFps || rFps;
|
|
104923
105112
|
const isVFR = rFps > 0 && avgFps > 0 && Math.abs(rFps - avgFps) / Math.max(rFps, avgFps) > 0.1;
|
|
105113
|
+
const colorTransfer = videoStream.color_transfer || "";
|
|
105114
|
+
const colorPrimaries = videoStream.color_primaries || "";
|
|
105115
|
+
const colorSpaceVal = videoStream.color_space || "";
|
|
105116
|
+
const hasColorInfo = !!(colorTransfer || colorPrimaries || colorSpaceVal);
|
|
104924
105117
|
return {
|
|
104925
105118
|
durationSeconds: output2.format.duration ? parseFloat(output2.format.duration) : 0,
|
|
104926
105119
|
width: videoStream.width || 0,
|
|
@@ -104928,7 +105121,8 @@ async function extractVideoMetadata(filePath) {
|
|
|
104928
105121
|
fps,
|
|
104929
105122
|
videoCodec: videoStream.codec_name || "unknown",
|
|
104930
105123
|
hasAudio: output2.streams.some((s) => s.codec_type === "audio"),
|
|
104931
|
-
isVFR
|
|
105124
|
+
isVFR,
|
|
105125
|
+
colorSpace: hasColorInfo ? { colorTransfer, colorPrimaries, colorSpace: colorSpaceVal } : null
|
|
104932
105126
|
};
|
|
104933
105127
|
})();
|
|
104934
105128
|
videoMetadataCache.set(filePath, probePromise);
|
|
@@ -105033,10 +105227,10 @@ import { finished } from "stream/promises";
|
|
|
105033
105227
|
var downloadPathCache = /* @__PURE__ */ new Map();
|
|
105034
105228
|
var inFlightDownloads = /* @__PURE__ */ new Map();
|
|
105035
105229
|
function getFilenameFromUrl(url) {
|
|
105036
|
-
const
|
|
105230
|
+
const hash2 = createHash("md5").update(url).digest("hex").slice(0, 12);
|
|
105037
105231
|
const urlObj = new URL(url);
|
|
105038
105232
|
const ext = extname2(urlObj.pathname) || ".mp4";
|
|
105039
|
-
return `download_${
|
|
105233
|
+
return `download_${hash2}${ext}`;
|
|
105040
105234
|
}
|
|
105041
105235
|
async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
105042
105236
|
const cachedPath = downloadPathCache.get(url);
|
|
@@ -105136,18 +105330,20 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
105136
105330
|
const metadata = await extractVideoMetadata(videoPath);
|
|
105137
105331
|
const framePattern = `frame_%05d.${format3}`;
|
|
105138
105332
|
const outputPattern = join8(videoOutputDir, framePattern);
|
|
105139
|
-
const
|
|
105140
|
-
|
|
105141
|
-
|
|
105142
|
-
|
|
105143
|
-
|
|
105144
|
-
|
|
105145
|
-
|
|
105146
|
-
|
|
105147
|
-
|
|
105148
|
-
"
|
|
105149
|
-
|
|
105150
|
-
|
|
105333
|
+
const isHdr = isHdrColorSpace(metadata.colorSpace);
|
|
105334
|
+
const isMacOS = process.platform === "darwin";
|
|
105335
|
+
const args = [];
|
|
105336
|
+
if (isHdr && isMacOS) {
|
|
105337
|
+
args.push("-hwaccel", "videotoolbox");
|
|
105338
|
+
}
|
|
105339
|
+
args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
|
|
105340
|
+
const vfFilters = [];
|
|
105341
|
+
if (isHdr && isMacOS) {
|
|
105342
|
+
vfFilters.push("format=nv12");
|
|
105343
|
+
}
|
|
105344
|
+
vfFilters.push(`fps=${fps}`);
|
|
105345
|
+
args.push("-vf", vfFilters.join(","));
|
|
105346
|
+
args.push("-q:v", format3 === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
|
|
105151
105347
|
if (format3 === "png") args.push("-compression_level", "6");
|
|
105152
105348
|
args.push("-y", outputPattern);
|
|
105153
105349
|
return new Promise((resolve13, reject) => {
|
|
@@ -105207,30 +105403,100 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
105207
105403
|
});
|
|
105208
105404
|
});
|
|
105209
105405
|
}
|
|
105406
|
+
async function convertSdrToHdr(inputPath, outputPath, signal, config2) {
|
|
105407
|
+
const timeout2 = config2?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
105408
|
+
const args = [
|
|
105409
|
+
"-i",
|
|
105410
|
+
inputPath,
|
|
105411
|
+
"-vf",
|
|
105412
|
+
"colorspace=all=bt2020:iall=bt709:range=tv",
|
|
105413
|
+
"-color_primaries",
|
|
105414
|
+
"bt2020",
|
|
105415
|
+
"-color_trc",
|
|
105416
|
+
"arib-std-b67",
|
|
105417
|
+
"-colorspace",
|
|
105418
|
+
"bt2020nc",
|
|
105419
|
+
"-c:v",
|
|
105420
|
+
"libx264",
|
|
105421
|
+
"-preset",
|
|
105422
|
+
"fast",
|
|
105423
|
+
"-crf",
|
|
105424
|
+
"16",
|
|
105425
|
+
"-c:a",
|
|
105426
|
+
"copy",
|
|
105427
|
+
"-y",
|
|
105428
|
+
outputPath
|
|
105429
|
+
];
|
|
105430
|
+
const result = await runFfmpeg(args, { signal, timeout: timeout2 });
|
|
105431
|
+
if (!result.success) {
|
|
105432
|
+
throw new Error(
|
|
105433
|
+
`SDR\u2192HDR conversion failed (exit ${result.exitCode}): ${result.stderr.slice(-300)}`
|
|
105434
|
+
);
|
|
105435
|
+
}
|
|
105436
|
+
}
|
|
105210
105437
|
async function extractAllVideoFrames(videos, baseDir, options, signal, config2, compiledDir) {
|
|
105211
105438
|
const startTime = Date.now();
|
|
105212
105439
|
const extracted = [];
|
|
105213
105440
|
const errors = [];
|
|
105214
105441
|
let totalFramesExtracted = 0;
|
|
105442
|
+
const resolvedVideos = [];
|
|
105443
|
+
for (const video of videos) {
|
|
105444
|
+
if (signal?.aborted) break;
|
|
105445
|
+
try {
|
|
105446
|
+
let videoPath = video.src;
|
|
105447
|
+
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
105448
|
+
const fromCompiled = compiledDir ? join8(compiledDir, videoPath) : null;
|
|
105449
|
+
videoPath = fromCompiled && existsSync8(fromCompiled) ? fromCompiled : join8(baseDir, videoPath);
|
|
105450
|
+
}
|
|
105451
|
+
if (isHttpUrl(videoPath)) {
|
|
105452
|
+
const downloadDir = join8(options.outputDir, "_downloads");
|
|
105453
|
+
mkdirSync5(downloadDir, { recursive: true });
|
|
105454
|
+
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
105455
|
+
}
|
|
105456
|
+
if (!existsSync8(videoPath)) {
|
|
105457
|
+
errors.push({ videoId: video.id, error: `Video file not found: ${videoPath}` });
|
|
105458
|
+
continue;
|
|
105459
|
+
}
|
|
105460
|
+
resolvedVideos.push({ video, videoPath });
|
|
105461
|
+
} catch (err) {
|
|
105462
|
+
errors.push({ videoId: video.id, error: err instanceof Error ? err.message : String(err) });
|
|
105463
|
+
}
|
|
105464
|
+
}
|
|
105465
|
+
const videoColorSpaces = await Promise.all(
|
|
105466
|
+
resolvedVideos.map(async ({ videoPath }) => {
|
|
105467
|
+
const metadata = await extractVideoMetadata(videoPath);
|
|
105468
|
+
return metadata.colorSpace;
|
|
105469
|
+
})
|
|
105470
|
+
);
|
|
105471
|
+
const hasAnyHdr = videoColorSpaces.some(isHdrColorSpace);
|
|
105472
|
+
if (hasAnyHdr) {
|
|
105473
|
+
const convertDir = join8(options.outputDir, "_hdr_normalized");
|
|
105474
|
+
mkdirSync5(convertDir, { recursive: true });
|
|
105475
|
+
for (let i = 0; i < resolvedVideos.length; i++) {
|
|
105476
|
+
if (signal?.aborted) break;
|
|
105477
|
+
const cs = videoColorSpaces[i] ?? null;
|
|
105478
|
+
if (!isHdrColorSpace(cs)) {
|
|
105479
|
+
const entry = resolvedVideos[i];
|
|
105480
|
+
if (!entry) continue;
|
|
105481
|
+
const convertedPath = join8(convertDir, `${entry.video.id}_hdr.mp4`);
|
|
105482
|
+
try {
|
|
105483
|
+
await convertSdrToHdr(entry.videoPath, convertedPath, signal, config2);
|
|
105484
|
+
entry.videoPath = convertedPath;
|
|
105485
|
+
} catch (err) {
|
|
105486
|
+
errors.push({
|
|
105487
|
+
videoId: entry.video.id,
|
|
105488
|
+
error: `SDR\u2192HDR conversion failed: ${err instanceof Error ? err.message : String(err)}`
|
|
105489
|
+
});
|
|
105490
|
+
}
|
|
105491
|
+
}
|
|
105492
|
+
}
|
|
105493
|
+
}
|
|
105215
105494
|
const results = await Promise.all(
|
|
105216
|
-
|
|
105495
|
+
resolvedVideos.map(async ({ video, videoPath }) => {
|
|
105217
105496
|
if (signal?.aborted) {
|
|
105218
105497
|
throw new Error("Video frame extraction cancelled");
|
|
105219
105498
|
}
|
|
105220
105499
|
try {
|
|
105221
|
-
let videoPath = video.src;
|
|
105222
|
-
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
105223
|
-
const fromCompiled = compiledDir ? join8(compiledDir, videoPath) : null;
|
|
105224
|
-
videoPath = fromCompiled && existsSync8(fromCompiled) ? fromCompiled : join8(baseDir, videoPath);
|
|
105225
|
-
}
|
|
105226
|
-
if (isHttpUrl(videoPath)) {
|
|
105227
|
-
const downloadDir = join8(options.outputDir, "_downloads");
|
|
105228
|
-
mkdirSync5(downloadDir, { recursive: true });
|
|
105229
|
-
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
105230
|
-
}
|
|
105231
|
-
if (!existsSync8(videoPath)) {
|
|
105232
|
-
return { error: { videoId: video.id, error: `Video file not found: ${videoPath}` } };
|
|
105233
|
-
}
|
|
105234
105500
|
let videoDuration = video.end - video.start;
|
|
105235
105501
|
if (!Number.isFinite(videoDuration) || videoDuration <= 0) {
|
|
105236
105502
|
const metadata = await extractVideoMetadata(videoPath);
|
|
@@ -105465,6 +105731,142 @@ function createVideoFrameInjector(frameLookup, config2) {
|
|
|
105465
105731
|
}
|
|
105466
105732
|
};
|
|
105467
105733
|
}
|
|
105734
|
+
async function queryElementStacking(page, nativeHdrVideoIds) {
|
|
105735
|
+
const hdrIds = Array.from(nativeHdrVideoIds);
|
|
105736
|
+
return page.evaluate((hdrIdList) => {
|
|
105737
|
+
const hdrSet = new Set(hdrIdList);
|
|
105738
|
+
const elements = document.querySelectorAll("[data-start]");
|
|
105739
|
+
const results = [];
|
|
105740
|
+
function getEffectiveZIndex(node) {
|
|
105741
|
+
let current = node;
|
|
105742
|
+
while (current) {
|
|
105743
|
+
const cs = window.getComputedStyle(current);
|
|
105744
|
+
const pos = cs.position;
|
|
105745
|
+
const z = parseInt(cs.zIndex);
|
|
105746
|
+
if (!Number.isNaN(z) && pos !== "static") return z;
|
|
105747
|
+
current = current.parentElement;
|
|
105748
|
+
}
|
|
105749
|
+
return 0;
|
|
105750
|
+
}
|
|
105751
|
+
function getEffectiveBorderRadius(node) {
|
|
105752
|
+
function resolveRadius(value, el) {
|
|
105753
|
+
if (value.includes("%")) {
|
|
105754
|
+
const pct = parseFloat(value) / 100;
|
|
105755
|
+
const htmlEl = el;
|
|
105756
|
+
const w = htmlEl.offsetWidth || 0;
|
|
105757
|
+
const h = htmlEl.offsetHeight || 0;
|
|
105758
|
+
return pct * Math.min(w, h);
|
|
105759
|
+
}
|
|
105760
|
+
return parseFloat(value) || 0;
|
|
105761
|
+
}
|
|
105762
|
+
const selfCs = window.getComputedStyle(node);
|
|
105763
|
+
const selfRadii = [
|
|
105764
|
+
resolveRadius(selfCs.borderTopLeftRadius, node),
|
|
105765
|
+
resolveRadius(selfCs.borderTopRightRadius, node),
|
|
105766
|
+
resolveRadius(selfCs.borderBottomRightRadius, node),
|
|
105767
|
+
resolveRadius(selfCs.borderBottomLeftRadius, node)
|
|
105768
|
+
];
|
|
105769
|
+
if (selfRadii[0] > 0 || selfRadii[1] > 0 || selfRadii[2] > 0 || selfRadii[3] > 0) {
|
|
105770
|
+
return selfRadii;
|
|
105771
|
+
}
|
|
105772
|
+
let current = node.parentElement;
|
|
105773
|
+
while (current) {
|
|
105774
|
+
const cs = window.getComputedStyle(current);
|
|
105775
|
+
if (cs.overflow !== "visible") {
|
|
105776
|
+
const tl = resolveRadius(cs.borderTopLeftRadius, current);
|
|
105777
|
+
const tr = resolveRadius(cs.borderTopRightRadius, current);
|
|
105778
|
+
const brr = resolveRadius(cs.borderBottomRightRadius, current);
|
|
105779
|
+
const bl = resolveRadius(cs.borderBottomLeftRadius, current);
|
|
105780
|
+
if (tl > 0 || tr > 0 || brr > 0 || bl > 0) {
|
|
105781
|
+
return [tl, tr, brr, bl];
|
|
105782
|
+
}
|
|
105783
|
+
}
|
|
105784
|
+
current = current.parentElement;
|
|
105785
|
+
}
|
|
105786
|
+
return [0, 0, 0, 0];
|
|
105787
|
+
}
|
|
105788
|
+
function getEffectiveOpacity(node) {
|
|
105789
|
+
let opacity = 1;
|
|
105790
|
+
let current = node;
|
|
105791
|
+
while (current) {
|
|
105792
|
+
const cs = window.getComputedStyle(current);
|
|
105793
|
+
const val = parseFloat(cs.opacity);
|
|
105794
|
+
opacity *= Number.isNaN(val) ? 1 : val;
|
|
105795
|
+
current = current.parentElement;
|
|
105796
|
+
}
|
|
105797
|
+
return opacity;
|
|
105798
|
+
}
|
|
105799
|
+
function getViewportMatrix(node) {
|
|
105800
|
+
const chain = [];
|
|
105801
|
+
let current = node;
|
|
105802
|
+
while (current instanceof HTMLElement) {
|
|
105803
|
+
chain.push(current);
|
|
105804
|
+
const next = current.offsetParent ?? current.parentElement;
|
|
105805
|
+
if (next === current) break;
|
|
105806
|
+
current = next;
|
|
105807
|
+
}
|
|
105808
|
+
let mat = new DOMMatrix();
|
|
105809
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
105810
|
+
const htmlEl = chain[i];
|
|
105811
|
+
if (!htmlEl) continue;
|
|
105812
|
+
mat = mat.translate(htmlEl.offsetLeft, htmlEl.offsetTop);
|
|
105813
|
+
const cs = window.getComputedStyle(htmlEl);
|
|
105814
|
+
if (cs.transform && cs.transform !== "none") {
|
|
105815
|
+
const origin = cs.transformOrigin.split(" ");
|
|
105816
|
+
const ox = resolveLength(origin[0] ?? "0", htmlEl.offsetWidth);
|
|
105817
|
+
const oy = resolveLength(origin[1] ?? "0", htmlEl.offsetHeight);
|
|
105818
|
+
try {
|
|
105819
|
+
const t = new DOMMatrix(cs.transform);
|
|
105820
|
+
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)) {
|
|
105821
|
+
mat = mat.translate(ox, oy).multiply(t).translate(-ox, -oy);
|
|
105822
|
+
}
|
|
105823
|
+
} catch {
|
|
105824
|
+
}
|
|
105825
|
+
}
|
|
105826
|
+
}
|
|
105827
|
+
return mat.toString();
|
|
105828
|
+
}
|
|
105829
|
+
function resolveLength(value, basis) {
|
|
105830
|
+
if (value.endsWith("%")) {
|
|
105831
|
+
const pct = parseFloat(value) / 100;
|
|
105832
|
+
return Number.isFinite(pct) ? pct * basis : 0;
|
|
105833
|
+
}
|
|
105834
|
+
const n = parseFloat(value);
|
|
105835
|
+
return Number.isFinite(n) ? n : 0;
|
|
105836
|
+
}
|
|
105837
|
+
for (const el of elements) {
|
|
105838
|
+
const id = el.id;
|
|
105839
|
+
if (!id) continue;
|
|
105840
|
+
const rect = el.getBoundingClientRect();
|
|
105841
|
+
const style = window.getComputedStyle(el);
|
|
105842
|
+
const zIndex = getEffectiveZIndex(el);
|
|
105843
|
+
const isHdrEl = hdrSet.has(id);
|
|
105844
|
+
const opacityStartNode = isHdrEl ? el.parentElement : el;
|
|
105845
|
+
const opacity = opacityStartNode ? getEffectiveOpacity(opacityStartNode) : 1;
|
|
105846
|
+
const visible = style.visibility !== "hidden" && style.display !== "none" && rect.width > 0 && rect.height > 0;
|
|
105847
|
+
const htmlEl = el instanceof HTMLElement ? el : null;
|
|
105848
|
+
results.push({
|
|
105849
|
+
id,
|
|
105850
|
+
zIndex,
|
|
105851
|
+
x: Math.round(rect.x),
|
|
105852
|
+
y: Math.round(rect.y),
|
|
105853
|
+
width: Math.round(rect.width),
|
|
105854
|
+
height: Math.round(rect.height),
|
|
105855
|
+
layoutWidth: htmlEl?.offsetWidth || Math.round(rect.width),
|
|
105856
|
+
layoutHeight: htmlEl?.offsetHeight || Math.round(rect.height),
|
|
105857
|
+
opacity,
|
|
105858
|
+
visible,
|
|
105859
|
+
isHdr: hdrSet.has(id),
|
|
105860
|
+
// For HDR elements, use the full accumulated viewport matrix so the
|
|
105861
|
+
// affine blit can apply rotation/scale/translate properly. For DOM
|
|
105862
|
+
// elements, the element-level transform is sufficient for reference.
|
|
105863
|
+
transform: isHdrEl ? getViewportMatrix(el) : style.transform || "none",
|
|
105864
|
+
borderRadius: isHdrEl ? getEffectiveBorderRadius(el) : [0, 0, 0, 0]
|
|
105865
|
+
});
|
|
105866
|
+
}
|
|
105867
|
+
return results;
|
|
105868
|
+
}, hdrIds);
|
|
105869
|
+
}
|
|
105468
105870
|
|
|
105469
105871
|
// ../engine/src/services/audioMixer.ts
|
|
105470
105872
|
import { existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync2 } from "fs";
|
|
@@ -105951,6 +106353,1016 @@ async function mergeWorkerFrames(workDir, tasks, outputDir) {
|
|
|
105951
106353
|
return totalFrames;
|
|
105952
106354
|
}
|
|
105953
106355
|
|
|
106356
|
+
// ../engine/src/utils/alphaBlit.ts
|
|
106357
|
+
import { inflateSync } from "zlib";
|
|
106358
|
+
function paeth(a, b, c) {
|
|
106359
|
+
const p = a + b - c;
|
|
106360
|
+
const pa = Math.abs(p - a);
|
|
106361
|
+
const pb = Math.abs(p - b);
|
|
106362
|
+
const pc = Math.abs(p - c);
|
|
106363
|
+
if (pa <= pb && pa <= pc) return a;
|
|
106364
|
+
if (pb <= pc) return b;
|
|
106365
|
+
return c;
|
|
106366
|
+
}
|
|
106367
|
+
function decodePngRaw(buf, caller) {
|
|
106368
|
+
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) {
|
|
106369
|
+
throw new Error(`${caller}: not a PNG file`);
|
|
106370
|
+
}
|
|
106371
|
+
let pos = 8;
|
|
106372
|
+
let width = 0;
|
|
106373
|
+
let height = 0;
|
|
106374
|
+
let bitDepth = 0;
|
|
106375
|
+
let colorType = 0;
|
|
106376
|
+
let interlace = 0;
|
|
106377
|
+
let sawIhdr = false;
|
|
106378
|
+
const idatChunks = [];
|
|
106379
|
+
while (pos + 12 <= buf.length) {
|
|
106380
|
+
const chunkLen = buf.readUInt32BE(pos);
|
|
106381
|
+
const chunkType = buf.toString("ascii", pos + 4, pos + 8);
|
|
106382
|
+
const chunkData = buf.subarray(pos + 8, pos + 8 + chunkLen);
|
|
106383
|
+
if (chunkType === "IHDR") {
|
|
106384
|
+
width = chunkData.readUInt32BE(0);
|
|
106385
|
+
height = chunkData.readUInt32BE(4);
|
|
106386
|
+
bitDepth = chunkData[8] ?? 0;
|
|
106387
|
+
colorType = chunkData[9] ?? 0;
|
|
106388
|
+
interlace = chunkData[12] ?? 0;
|
|
106389
|
+
sawIhdr = true;
|
|
106390
|
+
} else if (chunkType === "IDAT") {
|
|
106391
|
+
idatChunks.push(Buffer.from(chunkData));
|
|
106392
|
+
} else if (chunkType === "IEND") {
|
|
106393
|
+
break;
|
|
106394
|
+
}
|
|
106395
|
+
pos += 12 + chunkLen;
|
|
106396
|
+
}
|
|
106397
|
+
if (!sawIhdr) {
|
|
106398
|
+
throw new Error(`${caller}: PNG missing IHDR chunk`);
|
|
106399
|
+
}
|
|
106400
|
+
if (colorType !== 2 && colorType !== 6) {
|
|
106401
|
+
throw new Error(`${caller}: unsupported color type ${colorType} (expected 2=RGB or 6=RGBA)`);
|
|
106402
|
+
}
|
|
106403
|
+
if (interlace !== 0) {
|
|
106404
|
+
throw new Error(
|
|
106405
|
+
`${caller}: Adam7-interlaced PNGs are not supported (interlace method ${interlace})`
|
|
106406
|
+
);
|
|
106407
|
+
}
|
|
106408
|
+
const channels = colorType === 6 ? 4 : 3;
|
|
106409
|
+
const bpp = channels * (bitDepth / 8);
|
|
106410
|
+
const stride = width * bpp;
|
|
106411
|
+
const compressed = Buffer.concat(idatChunks);
|
|
106412
|
+
const decompressed = inflateSync(compressed);
|
|
106413
|
+
const rawPixels = Buffer.allocUnsafe(height * stride);
|
|
106414
|
+
const prevRow = new Uint8Array(stride);
|
|
106415
|
+
const currRow = new Uint8Array(stride);
|
|
106416
|
+
let srcPos = 0;
|
|
106417
|
+
for (let y = 0; y < height; y++) {
|
|
106418
|
+
const filterType = decompressed[srcPos++] ?? 0;
|
|
106419
|
+
const rawRow = decompressed.subarray(srcPos, srcPos + stride);
|
|
106420
|
+
srcPos += stride;
|
|
106421
|
+
switch (filterType) {
|
|
106422
|
+
case 0:
|
|
106423
|
+
currRow.set(rawRow);
|
|
106424
|
+
break;
|
|
106425
|
+
case 1:
|
|
106426
|
+
for (let x = 0; x < stride; x++) {
|
|
106427
|
+
currRow[x] = (rawRow[x] ?? 0) + (x >= bpp ? currRow[x - bpp] ?? 0 : 0) & 255;
|
|
106428
|
+
}
|
|
106429
|
+
break;
|
|
106430
|
+
case 2:
|
|
106431
|
+
for (let x = 0; x < stride; x++) {
|
|
106432
|
+
currRow[x] = (rawRow[x] ?? 0) + (prevRow[x] ?? 0) & 255;
|
|
106433
|
+
}
|
|
106434
|
+
break;
|
|
106435
|
+
case 3:
|
|
106436
|
+
for (let x = 0; x < stride; x++) {
|
|
106437
|
+
const left2 = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
106438
|
+
const up = prevRow[x] ?? 0;
|
|
106439
|
+
currRow[x] = (rawRow[x] ?? 0) + Math.floor((left2 + up) / 2) & 255;
|
|
106440
|
+
}
|
|
106441
|
+
break;
|
|
106442
|
+
case 4:
|
|
106443
|
+
for (let x = 0; x < stride; x++) {
|
|
106444
|
+
const left2 = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
106445
|
+
const up = prevRow[x] ?? 0;
|
|
106446
|
+
const upLeft = x >= bpp ? prevRow[x - bpp] ?? 0 : 0;
|
|
106447
|
+
currRow[x] = (rawRow[x] ?? 0) + paeth(left2, up, upLeft) & 255;
|
|
106448
|
+
}
|
|
106449
|
+
break;
|
|
106450
|
+
default:
|
|
106451
|
+
throw new Error(`${caller}: unknown filter type ${filterType} at row ${y}`);
|
|
106452
|
+
}
|
|
106453
|
+
rawPixels.set(currRow, y * stride);
|
|
106454
|
+
prevRow.set(currRow);
|
|
106455
|
+
}
|
|
106456
|
+
return { width, height, bitDepth, colorType, rawPixels };
|
|
106457
|
+
}
|
|
106458
|
+
function decodePng(buf) {
|
|
106459
|
+
const { width, height, bitDepth, colorType, rawPixels } = decodePngRaw(buf, "decodePng");
|
|
106460
|
+
if (bitDepth !== 8) {
|
|
106461
|
+
throw new Error(`decodePng: unsupported bit depth ${bitDepth} (expected 8)`);
|
|
106462
|
+
}
|
|
106463
|
+
const output2 = new Uint8Array(width * height * 4);
|
|
106464
|
+
if (colorType === 6) {
|
|
106465
|
+
output2.set(rawPixels);
|
|
106466
|
+
} else {
|
|
106467
|
+
for (let i = 0; i < width * height; i++) {
|
|
106468
|
+
output2[i * 4 + 0] = rawPixels[i * 3 + 0] ?? 0;
|
|
106469
|
+
output2[i * 4 + 1] = rawPixels[i * 3 + 1] ?? 0;
|
|
106470
|
+
output2[i * 4 + 2] = rawPixels[i * 3 + 2] ?? 0;
|
|
106471
|
+
output2[i * 4 + 3] = 255;
|
|
106472
|
+
}
|
|
106473
|
+
}
|
|
106474
|
+
return { width, height, data: output2 };
|
|
106475
|
+
}
|
|
106476
|
+
function decodePngToRgb48le(buf) {
|
|
106477
|
+
const { width, height, bitDepth, colorType, rawPixels } = decodePngRaw(buf, "decodePngToRgb48le");
|
|
106478
|
+
if (bitDepth !== 16) {
|
|
106479
|
+
throw new Error(`decodePngToRgb48le: unsupported bit depth ${bitDepth} (expected 16)`);
|
|
106480
|
+
}
|
|
106481
|
+
const bpp = colorType === 6 ? 8 : 6;
|
|
106482
|
+
const output2 = Buffer.allocUnsafe(width * height * 6);
|
|
106483
|
+
for (let y = 0; y < height; y++) {
|
|
106484
|
+
const dstBase = y * width * 6;
|
|
106485
|
+
const srcRowBase = y * width * bpp;
|
|
106486
|
+
for (let x = 0; x < width; x++) {
|
|
106487
|
+
const srcBase = srcRowBase + x * bpp;
|
|
106488
|
+
output2[dstBase + x * 6 + 0] = rawPixels[srcBase + 1] ?? 0;
|
|
106489
|
+
output2[dstBase + x * 6 + 1] = rawPixels[srcBase + 0] ?? 0;
|
|
106490
|
+
output2[dstBase + x * 6 + 2] = rawPixels[srcBase + 3] ?? 0;
|
|
106491
|
+
output2[dstBase + x * 6 + 3] = rawPixels[srcBase + 2] ?? 0;
|
|
106492
|
+
output2[dstBase + x * 6 + 4] = rawPixels[srcBase + 5] ?? 0;
|
|
106493
|
+
output2[dstBase + x * 6 + 5] = rawPixels[srcBase + 4] ?? 0;
|
|
106494
|
+
}
|
|
106495
|
+
}
|
|
106496
|
+
return { width, height, data: output2 };
|
|
106497
|
+
}
|
|
106498
|
+
function buildSrgbToHdrLut(transfer) {
|
|
106499
|
+
const lut = new Uint16Array(256);
|
|
106500
|
+
const hlgA = 0.17883277;
|
|
106501
|
+
const hlgB = 1 - 4 * hlgA;
|
|
106502
|
+
const hlgC = 0.5 - hlgA * Math.log(4 * hlgA);
|
|
106503
|
+
const pqM1 = 0.1593017578125;
|
|
106504
|
+
const pqM2 = 78.84375;
|
|
106505
|
+
const pqC1 = 0.8359375;
|
|
106506
|
+
const pqC2 = 18.8515625;
|
|
106507
|
+
const pqC3 = 18.6875;
|
|
106508
|
+
const pqMaxNits = 1e4;
|
|
106509
|
+
const sdrNits = 203;
|
|
106510
|
+
for (let i = 0; i < 256; i++) {
|
|
106511
|
+
const v = i / 255;
|
|
106512
|
+
const linear = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
106513
|
+
let signal;
|
|
106514
|
+
if (transfer === "hlg") {
|
|
106515
|
+
signal = linear <= 1 / 12 ? Math.sqrt(3 * linear) : hlgA * Math.log(12 * linear - hlgB) + hlgC;
|
|
106516
|
+
} else {
|
|
106517
|
+
const Lp = Math.max(0, linear * sdrNits / pqMaxNits);
|
|
106518
|
+
const Lm1 = Math.pow(Lp, pqM1);
|
|
106519
|
+
signal = Math.pow((pqC1 + pqC2 * Lm1) / (1 + pqC3 * Lm1), pqM2);
|
|
106520
|
+
}
|
|
106521
|
+
lut[i] = Math.min(65535, Math.round(signal * 65535));
|
|
106522
|
+
}
|
|
106523
|
+
return lut;
|
|
106524
|
+
}
|
|
106525
|
+
var SRGB_TO_HLG = buildSrgbToHdrLut("hlg");
|
|
106526
|
+
var SRGB_TO_PQ = buildSrgbToHdrLut("pq");
|
|
106527
|
+
function getSrgbToHdrLut(transfer) {
|
|
106528
|
+
return transfer === "pq" ? SRGB_TO_PQ : SRGB_TO_HLG;
|
|
106529
|
+
}
|
|
106530
|
+
function blitRgba8OverRgb48le(domRgba, canvas, width, height, transfer = "hlg") {
|
|
106531
|
+
const pixelCount = width * height;
|
|
106532
|
+
const lut = getSrgbToHdrLut(transfer);
|
|
106533
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
106534
|
+
const da = domRgba[i * 4 + 3] ?? 0;
|
|
106535
|
+
if (da === 0) {
|
|
106536
|
+
continue;
|
|
106537
|
+
} else if (da === 255) {
|
|
106538
|
+
const r16 = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
106539
|
+
const g16 = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
106540
|
+
const b16 = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
106541
|
+
canvas.writeUInt16LE(r16, i * 6);
|
|
106542
|
+
canvas.writeUInt16LE(g16, i * 6 + 2);
|
|
106543
|
+
canvas.writeUInt16LE(b16, i * 6 + 4);
|
|
106544
|
+
} else {
|
|
106545
|
+
const alpha = da / 255;
|
|
106546
|
+
const invAlpha = 1 - alpha;
|
|
106547
|
+
const hdrR = (canvas[i * 6 + 0] ?? 0) | (canvas[i * 6 + 1] ?? 0) << 8;
|
|
106548
|
+
const hdrG = (canvas[i * 6 + 2] ?? 0) | (canvas[i * 6 + 3] ?? 0) << 8;
|
|
106549
|
+
const hdrB = (canvas[i * 6 + 4] ?? 0) | (canvas[i * 6 + 5] ?? 0) << 8;
|
|
106550
|
+
const domR = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
106551
|
+
const domG = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
106552
|
+
const domB = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
106553
|
+
canvas.writeUInt16LE(Math.round(domR * alpha + hdrR * invAlpha), i * 6);
|
|
106554
|
+
canvas.writeUInt16LE(Math.round(domG * alpha + hdrG * invAlpha), i * 6 + 2);
|
|
106555
|
+
canvas.writeUInt16LE(Math.round(domB * alpha + hdrB * invAlpha), i * 6 + 4);
|
|
106556
|
+
}
|
|
106557
|
+
}
|
|
106558
|
+
}
|
|
106559
|
+
function cornerAlpha(px, py, cx, cy, r) {
|
|
106560
|
+
const dx = px - cx;
|
|
106561
|
+
const dy = py - cy;
|
|
106562
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
106563
|
+
if (dist > r + 0.5) return 0;
|
|
106564
|
+
if (dist > r - 0.5) return r + 0.5 - dist;
|
|
106565
|
+
return 1;
|
|
106566
|
+
}
|
|
106567
|
+
function roundedRectAlpha(px, py, w, h, radii) {
|
|
106568
|
+
const [tl, tr, br, bl] = radii;
|
|
106569
|
+
if (px < tl && py < tl) return cornerAlpha(px, py, tl, tl, tl);
|
|
106570
|
+
if (px >= w - tr && py < tr) return cornerAlpha(px, py, w - tr, tr, tr);
|
|
106571
|
+
if (px >= w - br && py >= h - br) return cornerAlpha(px, py, w - br, h - br, br);
|
|
106572
|
+
if (px < bl && py >= h - bl) return cornerAlpha(px, py, bl, h - bl, bl);
|
|
106573
|
+
return 1;
|
|
106574
|
+
}
|
|
106575
|
+
function blitRgb48leRegion(canvas, source2, dx, dy, sw, sh, canvasWidth, canvasHeight, opacity, borderRadius) {
|
|
106576
|
+
if (sw <= 0 || sh <= 0) return;
|
|
106577
|
+
const op = opacity ?? 1;
|
|
106578
|
+
const x0 = Math.max(0, dx);
|
|
106579
|
+
const y0 = Math.max(0, dy);
|
|
106580
|
+
const x1 = Math.min(canvasWidth, dx + sw);
|
|
106581
|
+
const y1 = Math.min(canvasHeight, dy + sh);
|
|
106582
|
+
if (x0 >= x1 || y0 >= y1) return;
|
|
106583
|
+
const clippedW = x1 - x0;
|
|
106584
|
+
const srcOffsetX = x0 - dx;
|
|
106585
|
+
const srcOffsetY = y0 - dy;
|
|
106586
|
+
const hasMask = borderRadius !== void 0;
|
|
106587
|
+
if (op >= 0.999 && !hasMask) {
|
|
106588
|
+
for (let y = 0; y < y1 - y0; y++) {
|
|
106589
|
+
const srcRowOff = ((srcOffsetY + y) * sw + srcOffsetX) * 6;
|
|
106590
|
+
const dstRowOff = ((y0 + y) * canvasWidth + x0) * 6;
|
|
106591
|
+
source2.copy(canvas, dstRowOff, srcRowOff, srcRowOff + clippedW * 6);
|
|
106592
|
+
}
|
|
106593
|
+
} else {
|
|
106594
|
+
for (let y = 0; y < y1 - y0; y++) {
|
|
106595
|
+
for (let x = 0; x < clippedW; x++) {
|
|
106596
|
+
let effectiveOp = op;
|
|
106597
|
+
if (hasMask) {
|
|
106598
|
+
const ma = roundedRectAlpha(srcOffsetX + x, srcOffsetY + y, sw, sh, borderRadius);
|
|
106599
|
+
if (ma <= 0) continue;
|
|
106600
|
+
effectiveOp *= ma;
|
|
106601
|
+
}
|
|
106602
|
+
const srcOff = ((srcOffsetY + y) * sw + srcOffsetX + x) * 6;
|
|
106603
|
+
const dstOff = ((y0 + y) * canvasWidth + x0 + x) * 6;
|
|
106604
|
+
if (effectiveOp >= 0.999) {
|
|
106605
|
+
source2.copy(canvas, dstOff, srcOff, srcOff + 6);
|
|
106606
|
+
} else {
|
|
106607
|
+
const invEff = 1 - effectiveOp;
|
|
106608
|
+
const sr = source2.readUInt16LE(srcOff);
|
|
106609
|
+
const sg = source2.readUInt16LE(srcOff + 2);
|
|
106610
|
+
const sb = source2.readUInt16LE(srcOff + 4);
|
|
106611
|
+
const dr = canvas.readUInt16LE(dstOff);
|
|
106612
|
+
const dg = canvas.readUInt16LE(dstOff + 2);
|
|
106613
|
+
const db = canvas.readUInt16LE(dstOff + 4);
|
|
106614
|
+
canvas.writeUInt16LE(Math.round(sr * effectiveOp + dr * invEff), dstOff);
|
|
106615
|
+
canvas.writeUInt16LE(Math.round(sg * effectiveOp + dg * invEff), dstOff + 2);
|
|
106616
|
+
canvas.writeUInt16LE(Math.round(sb * effectiveOp + db * invEff), dstOff + 4);
|
|
106617
|
+
}
|
|
106618
|
+
}
|
|
106619
|
+
}
|
|
106620
|
+
}
|
|
106621
|
+
}
|
|
106622
|
+
function blitRgb48leAffine(canvas, source2, matrix, srcW, srcH, canvasW, canvasH, opacity, borderRadius) {
|
|
106623
|
+
const a = matrix[0];
|
|
106624
|
+
const b = matrix[1];
|
|
106625
|
+
const c = matrix[2];
|
|
106626
|
+
const d = matrix[3];
|
|
106627
|
+
const tx = matrix[4];
|
|
106628
|
+
const ty = matrix[5];
|
|
106629
|
+
if (a === void 0 || b === void 0 || c === void 0 || d === void 0 || tx === void 0 || ty === void 0)
|
|
106630
|
+
return;
|
|
106631
|
+
const det = a * d - b * c;
|
|
106632
|
+
if (Math.abs(det) < 1e-10) return;
|
|
106633
|
+
const invA = d / det;
|
|
106634
|
+
const invB = -b / det;
|
|
106635
|
+
const invC = -c / det;
|
|
106636
|
+
const invD = a / det;
|
|
106637
|
+
const invTx = -(invA * tx + invC * ty);
|
|
106638
|
+
const invTy = -(invB * tx + invD * ty);
|
|
106639
|
+
const op = opacity ?? 1;
|
|
106640
|
+
const hasMask = borderRadius !== void 0;
|
|
106641
|
+
const corners = [
|
|
106642
|
+
[tx, ty],
|
|
106643
|
+
[a * srcW + tx, b * srcW + ty],
|
|
106644
|
+
[c * srcH + tx, d * srcH + ty],
|
|
106645
|
+
[a * srcW + c * srcH + tx, b * srcW + d * srcH + ty]
|
|
106646
|
+
];
|
|
106647
|
+
let minX = canvasW, maxX = 0, minY = canvasH, maxY = 0;
|
|
106648
|
+
for (const corner of corners) {
|
|
106649
|
+
const cx = corner[0] ?? 0;
|
|
106650
|
+
const cy = corner[1] ?? 0;
|
|
106651
|
+
if (cx < minX) minX = cx;
|
|
106652
|
+
if (cx > maxX) maxX = cx;
|
|
106653
|
+
if (cy < minY) minY = cy;
|
|
106654
|
+
if (cy > maxY) maxY = cy;
|
|
106655
|
+
}
|
|
106656
|
+
const startX = Math.max(0, Math.floor(minX));
|
|
106657
|
+
const endX = Math.min(canvasW, Math.ceil(maxX));
|
|
106658
|
+
const startY = Math.max(0, Math.floor(minY));
|
|
106659
|
+
const endY = Math.min(canvasH, Math.ceil(maxY));
|
|
106660
|
+
for (let dy = startY; dy < endY; dy++) {
|
|
106661
|
+
for (let dx = startX; dx < endX; dx++) {
|
|
106662
|
+
const sx = invA * dx + invC * dy + invTx;
|
|
106663
|
+
const sy = invB * dx + invD * dy + invTy;
|
|
106664
|
+
if (sx < 0 || sy < 0 || sx >= srcW || sy >= srcH) continue;
|
|
106665
|
+
let effectiveOp = op;
|
|
106666
|
+
if (hasMask) {
|
|
106667
|
+
const ma = roundedRectAlpha(sx, sy, srcW, srcH, borderRadius);
|
|
106668
|
+
if (ma <= 0) continue;
|
|
106669
|
+
effectiveOp *= ma;
|
|
106670
|
+
}
|
|
106671
|
+
const x0 = Math.floor(sx);
|
|
106672
|
+
const y0 = Math.floor(sy);
|
|
106673
|
+
const fx = sx - x0;
|
|
106674
|
+
const fy = sy - y0;
|
|
106675
|
+
const x1 = Math.min(x0 + 1, srcW - 1);
|
|
106676
|
+
const y1 = Math.min(y0 + 1, srcH - 1);
|
|
106677
|
+
const off00 = (y0 * srcW + x0) * 6;
|
|
106678
|
+
const off10 = (y0 * srcW + x1) * 6;
|
|
106679
|
+
const off01 = (y1 * srcW + x0) * 6;
|
|
106680
|
+
const off11 = (y1 * srcW + x1) * 6;
|
|
106681
|
+
const w00 = (1 - fx) * (1 - fy);
|
|
106682
|
+
const w10 = fx * (1 - fy);
|
|
106683
|
+
const w01 = (1 - fx) * fy;
|
|
106684
|
+
const w11 = fx * fy;
|
|
106685
|
+
const sr = source2.readUInt16LE(off00) * w00 + source2.readUInt16LE(off10) * w10 + source2.readUInt16LE(off01) * w01 + source2.readUInt16LE(off11) * w11;
|
|
106686
|
+
const sg = source2.readUInt16LE(off00 + 2) * w00 + source2.readUInt16LE(off10 + 2) * w10 + source2.readUInt16LE(off01 + 2) * w01 + source2.readUInt16LE(off11 + 2) * w11;
|
|
106687
|
+
const sb = source2.readUInt16LE(off00 + 4) * w00 + source2.readUInt16LE(off10 + 4) * w10 + source2.readUInt16LE(off01 + 4) * w01 + source2.readUInt16LE(off11 + 4) * w11;
|
|
106688
|
+
const dstOff = (dy * canvasW + dx) * 6;
|
|
106689
|
+
if (effectiveOp >= 0.999) {
|
|
106690
|
+
canvas.writeUInt16LE(Math.round(sr), dstOff);
|
|
106691
|
+
canvas.writeUInt16LE(Math.round(sg), dstOff + 2);
|
|
106692
|
+
canvas.writeUInt16LE(Math.round(sb), dstOff + 4);
|
|
106693
|
+
} else {
|
|
106694
|
+
const invEff = 1 - effectiveOp;
|
|
106695
|
+
const dr = canvas.readUInt16LE(dstOff);
|
|
106696
|
+
const dg = canvas.readUInt16LE(dstOff + 2);
|
|
106697
|
+
const db = canvas.readUInt16LE(dstOff + 4);
|
|
106698
|
+
canvas.writeUInt16LE(Math.round(sr * effectiveOp + dr * invEff), dstOff);
|
|
106699
|
+
canvas.writeUInt16LE(Math.round(sg * effectiveOp + dg * invEff), dstOff + 2);
|
|
106700
|
+
canvas.writeUInt16LE(Math.round(sb * effectiveOp + db * invEff), dstOff + 4);
|
|
106701
|
+
}
|
|
106702
|
+
}
|
|
106703
|
+
}
|
|
106704
|
+
}
|
|
106705
|
+
function parseTransformMatrix(css) {
|
|
106706
|
+
if (!css || css === "none") return null;
|
|
106707
|
+
const match2 = css.match(
|
|
106708
|
+
/^matrix\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,)]+)\s*\)$/
|
|
106709
|
+
);
|
|
106710
|
+
if (!match2) return null;
|
|
106711
|
+
const values = match2.slice(1, 7).map(Number);
|
|
106712
|
+
if (!values.every(Number.isFinite)) return null;
|
|
106713
|
+
return values;
|
|
106714
|
+
}
|
|
106715
|
+
|
|
106716
|
+
// ../engine/src/utils/layerCompositor.ts
|
|
106717
|
+
function groupIntoLayers(elements) {
|
|
106718
|
+
const sorted = [...elements].sort((a, b) => a.zIndex - b.zIndex);
|
|
106719
|
+
const layers = [];
|
|
106720
|
+
for (const el of sorted) {
|
|
106721
|
+
if (el.isHdr) {
|
|
106722
|
+
layers.push({ type: "hdr", element: el });
|
|
106723
|
+
} else {
|
|
106724
|
+
const last2 = layers[layers.length - 1];
|
|
106725
|
+
if (last2 && last2.type === "dom") {
|
|
106726
|
+
last2.elementIds.push(el.id);
|
|
106727
|
+
} else {
|
|
106728
|
+
layers.push({ type: "dom", elementIds: [el.id] });
|
|
106729
|
+
}
|
|
106730
|
+
}
|
|
106731
|
+
}
|
|
106732
|
+
return layers;
|
|
106733
|
+
}
|
|
106734
|
+
|
|
106735
|
+
// ../engine/src/utils/shaderTransitions.ts
|
|
106736
|
+
var PQ_M1 = 0.1593017578125;
|
|
106737
|
+
var PQ_M2 = 78.84375;
|
|
106738
|
+
var PQ_C1 = 0.8359375;
|
|
106739
|
+
var PQ_C2 = 18.8515625;
|
|
106740
|
+
var PQ_C3 = 18.6875;
|
|
106741
|
+
function pqEotf(signal) {
|
|
106742
|
+
const sp = Math.pow(Math.max(0, signal), 1 / PQ_M2);
|
|
106743
|
+
const num = Math.max(sp - PQ_C1, 0);
|
|
106744
|
+
const den = PQ_C2 - PQ_C3 * sp;
|
|
106745
|
+
return den > 0 ? Math.pow(num / den, 1 / PQ_M1) : 0;
|
|
106746
|
+
}
|
|
106747
|
+
function pqOetf(linear) {
|
|
106748
|
+
const lp = Math.pow(Math.max(0, linear), PQ_M1);
|
|
106749
|
+
return Math.pow((PQ_C1 + PQ_C2 * lp) / (1 + PQ_C3 * lp), PQ_M2);
|
|
106750
|
+
}
|
|
106751
|
+
function hlgEotf(signal) {
|
|
106752
|
+
const a = 0.17883277;
|
|
106753
|
+
const b = 1 - 4 * a;
|
|
106754
|
+
const c = 0.5 - a * Math.log(4 * a);
|
|
106755
|
+
if (signal <= 0.5) {
|
|
106756
|
+
return signal * signal / 3;
|
|
106757
|
+
}
|
|
106758
|
+
return (Math.exp((signal - c) / a) + b) / 12;
|
|
106759
|
+
}
|
|
106760
|
+
function hlgOetf(linear) {
|
|
106761
|
+
const a = 0.17883277;
|
|
106762
|
+
const b = 1 - 4 * a;
|
|
106763
|
+
const c = 0.5 - a * Math.log(4 * a);
|
|
106764
|
+
if (linear <= 1 / 12) {
|
|
106765
|
+
return Math.sqrt(3 * linear);
|
|
106766
|
+
}
|
|
106767
|
+
return a * Math.log(12 * linear - b) + c;
|
|
106768
|
+
}
|
|
106769
|
+
function buildLut(fn) {
|
|
106770
|
+
const lut = new Uint16Array(65536);
|
|
106771
|
+
for (let i = 0; i < 65536; i++) {
|
|
106772
|
+
lut[i] = Math.round(fn(i / 65535) * 65535);
|
|
106773
|
+
}
|
|
106774
|
+
return lut;
|
|
106775
|
+
}
|
|
106776
|
+
var HLG_OOTF_LW = 1e3;
|
|
106777
|
+
var HLG_OOTF_GAMMA = 1.2 * Math.pow(1.111, Math.log2(HLG_OOTF_LW / 1e3));
|
|
106778
|
+
function hlgSceneToPqDisplay(sceneLinear) {
|
|
106779
|
+
const displayNits = HLG_OOTF_LW * Math.pow(Math.max(0, sceneLinear), HLG_OOTF_GAMMA);
|
|
106780
|
+
return displayNits / 1e4;
|
|
106781
|
+
}
|
|
106782
|
+
function pqDisplayToHlgScene(displayNormalized) {
|
|
106783
|
+
const displayNits = displayNormalized * 1e4;
|
|
106784
|
+
return Math.pow(Math.max(0, displayNits / HLG_OOTF_LW), 1 / HLG_OOTF_GAMMA);
|
|
106785
|
+
}
|
|
106786
|
+
var hlgToPqLut = null;
|
|
106787
|
+
var pqToHlgLut = null;
|
|
106788
|
+
function getHlgToPqLut() {
|
|
106789
|
+
if (!hlgToPqLut) hlgToPqLut = buildLut((v) => pqOetf(hlgSceneToPqDisplay(hlgEotf(v))));
|
|
106790
|
+
return hlgToPqLut;
|
|
106791
|
+
}
|
|
106792
|
+
function getPqToHlgLut() {
|
|
106793
|
+
if (!pqToHlgLut) pqToHlgLut = buildLut((v) => hlgOetf(pqDisplayToHlgScene(pqEotf(v))));
|
|
106794
|
+
return pqToHlgLut;
|
|
106795
|
+
}
|
|
106796
|
+
function convertTransfer(buf, from2, to) {
|
|
106797
|
+
if (from2 === to) return;
|
|
106798
|
+
const lut = from2 === "hlg" ? getHlgToPqLut() : getPqToHlgLut();
|
|
106799
|
+
const len = buf.length / 2;
|
|
106800
|
+
for (let i = 0; i < len; i++) {
|
|
106801
|
+
const off = i * 2;
|
|
106802
|
+
buf.writeUInt16LE(lut[buf.readUInt16LE(off)] ?? 0, off);
|
|
106803
|
+
}
|
|
106804
|
+
}
|
|
106805
|
+
function sampleRgb48le(buf, u, v, w, h) {
|
|
106806
|
+
const uc = Math.max(0, Math.min(1, u));
|
|
106807
|
+
const vc = Math.max(0, Math.min(1, v));
|
|
106808
|
+
const sx = uc * (w - 1);
|
|
106809
|
+
const sy = vc * (h - 1);
|
|
106810
|
+
const x0 = Math.floor(sx);
|
|
106811
|
+
const y0 = Math.floor(sy);
|
|
106812
|
+
const x1 = Math.min(x0 + 1, w - 1);
|
|
106813
|
+
const y1 = Math.min(y0 + 1, h - 1);
|
|
106814
|
+
const fx = sx - x0;
|
|
106815
|
+
const fy = sy - y0;
|
|
106816
|
+
const w00 = (1 - fx) * (1 - fy);
|
|
106817
|
+
const w10 = fx * (1 - fy);
|
|
106818
|
+
const w01 = (1 - fx) * fy;
|
|
106819
|
+
const w11 = fx * fy;
|
|
106820
|
+
const off00 = (y0 * w + x0) * 6;
|
|
106821
|
+
const off10 = (y0 * w + x1) * 6;
|
|
106822
|
+
const off01 = (y1 * w + x0) * 6;
|
|
106823
|
+
const off11 = (y1 * w + x1) * 6;
|
|
106824
|
+
const r = Math.round(
|
|
106825
|
+
buf.readUInt16LE(off00) * w00 + buf.readUInt16LE(off10) * w10 + buf.readUInt16LE(off01) * w01 + buf.readUInt16LE(off11) * w11
|
|
106826
|
+
);
|
|
106827
|
+
const g = Math.round(
|
|
106828
|
+
buf.readUInt16LE(off00 + 2) * w00 + buf.readUInt16LE(off10 + 2) * w10 + buf.readUInt16LE(off01 + 2) * w01 + buf.readUInt16LE(off11 + 2) * w11
|
|
106829
|
+
);
|
|
106830
|
+
const b = Math.round(
|
|
106831
|
+
buf.readUInt16LE(off00 + 4) * w00 + buf.readUInt16LE(off10 + 4) * w10 + buf.readUInt16LE(off01 + 4) * w01 + buf.readUInt16LE(off11 + 4) * w11
|
|
106832
|
+
);
|
|
106833
|
+
return [r, g, b];
|
|
106834
|
+
}
|
|
106835
|
+
function mix16(a, b, t) {
|
|
106836
|
+
return Math.round(a * (1 - t) + b * t);
|
|
106837
|
+
}
|
|
106838
|
+
function clamp16(v) {
|
|
106839
|
+
return Math.max(0, Math.min(65535, v));
|
|
106840
|
+
}
|
|
106841
|
+
function smoothstep(edge0, edge1, x) {
|
|
106842
|
+
const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
|
|
106843
|
+
return t * t * (3 - 2 * t);
|
|
106844
|
+
}
|
|
106845
|
+
function hash(x, y) {
|
|
106846
|
+
return (Math.sin(x * 127.1 + y * 311.7) * 43758.5453 % 1 + 1) % 1;
|
|
106847
|
+
}
|
|
106848
|
+
function vnoise(px, py) {
|
|
106849
|
+
const ix = Math.floor(px);
|
|
106850
|
+
const iy = Math.floor(py);
|
|
106851
|
+
let fx = px - ix;
|
|
106852
|
+
let fy = py - iy;
|
|
106853
|
+
fx = fx * fx * fx * (fx * (fx * 6 - 15) + 10);
|
|
106854
|
+
fy = fy * fy * fy * (fy * (fy * 6 - 15) + 10);
|
|
106855
|
+
const h00 = hash(ix, iy);
|
|
106856
|
+
const h10 = hash(ix + 1, iy);
|
|
106857
|
+
const h01 = hash(ix, iy + 1);
|
|
106858
|
+
const h11 = hash(ix + 1, iy + 1);
|
|
106859
|
+
return h00 * (1 - fx) * (1 - fy) + h10 * fx * (1 - fy) + h01 * (1 - fx) * fy + h11 * fx * fy;
|
|
106860
|
+
}
|
|
106861
|
+
var ROT_A = 0.8;
|
|
106862
|
+
var ROT_B = 0.6;
|
|
106863
|
+
function fbm(px, py) {
|
|
106864
|
+
let value = 0;
|
|
106865
|
+
let amplitude = 0.5;
|
|
106866
|
+
let x = px;
|
|
106867
|
+
let y = py;
|
|
106868
|
+
for (let i = 0; i < 5; i++) {
|
|
106869
|
+
value += amplitude * vnoise(x, y);
|
|
106870
|
+
const nx = ROT_A * x - ROT_B * y;
|
|
106871
|
+
const ny = ROT_B * x + ROT_A * y;
|
|
106872
|
+
x = nx * 2.02;
|
|
106873
|
+
y = ny * 2.02;
|
|
106874
|
+
amplitude *= 0.5;
|
|
106875
|
+
}
|
|
106876
|
+
return value;
|
|
106877
|
+
}
|
|
106878
|
+
var TRANSITIONS = {};
|
|
106879
|
+
var crossfade = (from2, to, out, w, h, p) => {
|
|
106880
|
+
const inv = 1 - p;
|
|
106881
|
+
for (let i = 0; i < w * h; i++) {
|
|
106882
|
+
const o = i * 6;
|
|
106883
|
+
out.writeUInt16LE(Math.round(from2.readUInt16LE(o) * inv + to.readUInt16LE(o) * p), o);
|
|
106884
|
+
out.writeUInt16LE(
|
|
106885
|
+
Math.round(from2.readUInt16LE(o + 2) * inv + to.readUInt16LE(o + 2) * p),
|
|
106886
|
+
o + 2
|
|
106887
|
+
);
|
|
106888
|
+
out.writeUInt16LE(
|
|
106889
|
+
Math.round(from2.readUInt16LE(o + 4) * inv + to.readUInt16LE(o + 4) * p),
|
|
106890
|
+
o + 4
|
|
106891
|
+
);
|
|
106892
|
+
}
|
|
106893
|
+
};
|
|
106894
|
+
TRANSITIONS["crossfade"] = crossfade;
|
|
106895
|
+
var flashThroughWhite = (from2, to, out, w, h, p) => {
|
|
106896
|
+
const toWhite = smoothstep(0, 0.45, p);
|
|
106897
|
+
const fromWhite = 1 - smoothstep(0.5, 1, p);
|
|
106898
|
+
const blend = smoothstep(0.35, 0.65, p);
|
|
106899
|
+
for (let i = 0; i < w * h; i++) {
|
|
106900
|
+
const o = i * 6;
|
|
106901
|
+
const fromR = mix16(from2.readUInt16LE(o), 65535, toWhite);
|
|
106902
|
+
const fromG = mix16(from2.readUInt16LE(o + 2), 65535, toWhite);
|
|
106903
|
+
const fromB = mix16(from2.readUInt16LE(o + 4), 65535, toWhite);
|
|
106904
|
+
const toR = mix16(to.readUInt16LE(o), 65535, fromWhite);
|
|
106905
|
+
const toG = mix16(to.readUInt16LE(o + 2), 65535, fromWhite);
|
|
106906
|
+
const toB = mix16(to.readUInt16LE(o + 4), 65535, fromWhite);
|
|
106907
|
+
out.writeUInt16LE(mix16(fromR, toR, blend), o);
|
|
106908
|
+
out.writeUInt16LE(mix16(fromG, toG, blend), o + 2);
|
|
106909
|
+
out.writeUInt16LE(mix16(fromB, toB, blend), o + 4);
|
|
106910
|
+
}
|
|
106911
|
+
};
|
|
106912
|
+
TRANSITIONS["flash-through-white"] = flashThroughWhite;
|
|
106913
|
+
var chromaticSplit = (from2, to, out, w, h, p) => {
|
|
106914
|
+
for (let i = 0; i < w * h; i++) {
|
|
106915
|
+
const ux = i % w / w;
|
|
106916
|
+
const uy = Math.floor(i / w) / h;
|
|
106917
|
+
const o = i * 6;
|
|
106918
|
+
const cx = ux - 0.5;
|
|
106919
|
+
const cy = uy - 0.5;
|
|
106920
|
+
const fromShift = p * 0.06;
|
|
106921
|
+
const fr = sampleRgb48le(from2, ux + cx * fromShift, uy + cy * fromShift, w, h)[0];
|
|
106922
|
+
const fg = sampleRgb48le(from2, ux, uy, w, h)[1];
|
|
106923
|
+
const fb = sampleRgb48le(from2, ux - cx * fromShift, uy - cy * fromShift, w, h)[2];
|
|
106924
|
+
const toShift = (1 - p) * 0.06;
|
|
106925
|
+
const tr = sampleRgb48le(to, ux - cx * toShift, uy - cy * toShift, w, h)[0];
|
|
106926
|
+
const tg = sampleRgb48le(to, ux, uy, w, h)[1];
|
|
106927
|
+
const tb = sampleRgb48le(to, ux + cx * toShift, uy + cy * toShift, w, h)[2];
|
|
106928
|
+
out.writeUInt16LE(clamp16(mix16(fr, tr, p)), o);
|
|
106929
|
+
out.writeUInt16LE(clamp16(mix16(fg, tg, p)), o + 2);
|
|
106930
|
+
out.writeUInt16LE(clamp16(mix16(fb, tb, p)), o + 4);
|
|
106931
|
+
}
|
|
106932
|
+
};
|
|
106933
|
+
TRANSITIONS["chromatic-split"] = chromaticSplit;
|
|
106934
|
+
var sdfIris = (from2, to, out, w, h, p) => {
|
|
106935
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
106936
|
+
for (let i = 0; i < w * h; i++) {
|
|
106937
|
+
const ux = i % w / w;
|
|
106938
|
+
const uy = Math.floor(i / w) / h;
|
|
106939
|
+
const o = i * 6;
|
|
106940
|
+
const ax = (ux - 0.5) * (w / h);
|
|
106941
|
+
const ay = uy - 0.5;
|
|
106942
|
+
const d = Math.sqrt(ax * ax + ay * ay);
|
|
106943
|
+
const radius = p * 1.2;
|
|
106944
|
+
const fw = 3e-3;
|
|
106945
|
+
const edge = smoothstep(radius + fw, radius - fw, d);
|
|
106946
|
+
const ring1 = Math.exp(-Math.abs(d - radius) * 25);
|
|
106947
|
+
const ring2 = Math.exp(-Math.abs(d - radius + 0.04) * 20) * 0.5;
|
|
106948
|
+
const ring3 = Math.exp(-Math.abs(d - radius + 0.08) * 15) * 0.25;
|
|
106949
|
+
const glow = (ring1 + ring2 + ring3) * p * (1 - p) * 4;
|
|
106950
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, ux, uy, w, h);
|
|
106951
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
106952
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, edge) + accentBright[0] * glow * 0.6), o);
|
|
106953
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, edge) + accentBright[1] * glow * 0.6), o + 2);
|
|
106954
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, edge) + accentBright[2] * glow * 0.6), o + 4);
|
|
106955
|
+
}
|
|
106956
|
+
};
|
|
106957
|
+
TRANSITIONS["sdf-iris"] = sdfIris;
|
|
106958
|
+
function glitchRand(x, y) {
|
|
106959
|
+
return (Math.sin(x * 12.9898 + y * 78.233) * 43758.5453 % 1 + 1) % 1;
|
|
106960
|
+
}
|
|
106961
|
+
var glitch = (from2, to, out, w, h, p) => {
|
|
106962
|
+
const intensity = p * (1 - p) * 4;
|
|
106963
|
+
for (let i = 0; i < w * h; i++) {
|
|
106964
|
+
const ux = i % w / w;
|
|
106965
|
+
const uy = Math.floor(i / w) / h;
|
|
106966
|
+
const o = i * 6;
|
|
106967
|
+
const lineY = Math.floor(uy * 60) / 60;
|
|
106968
|
+
const lineDisp = (glitchRand(lineY, Math.floor(p * 17)) - 0.5) * 0.18 * intensity;
|
|
106969
|
+
const blockX = Math.floor(ux * 12);
|
|
106970
|
+
const blockY = Math.floor(uy * 8);
|
|
106971
|
+
const progressStep = Math.floor(p * 11);
|
|
106972
|
+
const br = glitchRand(blockX + progressStep, blockY + progressStep);
|
|
106973
|
+
const ba = (br >= 0.83 ? 1 : 0) * intensity;
|
|
106974
|
+
const bdx = (glitchRand(blockX * 2.1, blockY * 2.1) - 0.5) * 0.35 * ba;
|
|
106975
|
+
const bdy = (glitchRand(blockX * 3.7, blockY * 3.7) - 0.5) * 0.35 * ba;
|
|
106976
|
+
const uvx = Math.max(0, Math.min(1, ux + lineDisp + bdx));
|
|
106977
|
+
const uvy = Math.max(0, Math.min(1, uy + bdy));
|
|
106978
|
+
const shift = intensity * 0.035;
|
|
106979
|
+
const r = sampleRgb48le(from2, uvx + shift, uvy, w, h)[0];
|
|
106980
|
+
const g = sampleRgb48le(from2, uvx, uvy, w, h)[1];
|
|
106981
|
+
const b = sampleRgb48le(from2, uvx - shift, uvy, w, h)[2];
|
|
106982
|
+
let cr = r / 65535;
|
|
106983
|
+
let cg = g / 65535;
|
|
106984
|
+
let cb = b / 65535;
|
|
106985
|
+
const scanline = (uy * h * 0.5 % 1 + 1) % 1 >= 0.5 ? 0.05 * intensity : 0;
|
|
106986
|
+
cr -= scanline;
|
|
106987
|
+
cg -= scanline;
|
|
106988
|
+
cb -= scanline;
|
|
106989
|
+
const flicker = 1 + (glitchRand(Math.floor(p * 23), 0) - 0.5) * 0.3 * intensity;
|
|
106990
|
+
cr *= flicker;
|
|
106991
|
+
cg *= flicker;
|
|
106992
|
+
cb *= flicker;
|
|
106993
|
+
const levels = 256 - (256 - 8) * (intensity * 0.5);
|
|
106994
|
+
cr = Math.floor(cr * levels) / levels;
|
|
106995
|
+
cg = Math.floor(cg * levels) / levels;
|
|
106996
|
+
cb = Math.floor(cb * levels) / levels;
|
|
106997
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
106998
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cr * 65535), toR, p)), o);
|
|
106999
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cg * 65535), toG, p)), o + 2);
|
|
107000
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(cb * 65535), toB, p)), o + 4);
|
|
107001
|
+
}
|
|
107002
|
+
};
|
|
107003
|
+
TRANSITIONS["glitch"] = glitch;
|
|
107004
|
+
function aces(x) {
|
|
107005
|
+
return Math.max(0, Math.min(1, x * (2.51 * x + 0.03) / (x * (2.43 * x + 0.59) + 0.14)));
|
|
107006
|
+
}
|
|
107007
|
+
var lightLeak = (from2, to, out, w, h, p) => {
|
|
107008
|
+
const accent = [5e4 / 65535, 25e3 / 65535, 5e3 / 65535];
|
|
107009
|
+
const accentBright = [65535 / 65535, 55e3 / 65535, 35e3 / 65535];
|
|
107010
|
+
const lpx = 1.3;
|
|
107011
|
+
const lpy = -0.2;
|
|
107012
|
+
for (let i = 0; i < w * h; i++) {
|
|
107013
|
+
const ux = i % w / w;
|
|
107014
|
+
const uy = Math.floor(i / w) / h;
|
|
107015
|
+
const o = i * 6;
|
|
107016
|
+
const dx = ux - lpx;
|
|
107017
|
+
const dy = uy - lpy;
|
|
107018
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
107019
|
+
const leak = Math.max(0, Math.min(1, Math.exp(-dist * 1.8) * p * 4));
|
|
107020
|
+
const warmR = accent[0] + (accentBright[0] - accent[0]) * dist * 0.7;
|
|
107021
|
+
const warmG = accent[1] + (accentBright[1] - accent[1]) * dist * 0.7;
|
|
107022
|
+
const warmB = accent[2] + (accentBright[2] - accent[2]) * dist * 0.7;
|
|
107023
|
+
const flare = Math.exp(-Math.abs(uy - (-0.2 + ux * 0.3)) * 15) * leak * 0.3;
|
|
107024
|
+
const [fr, fg, fb] = sampleRgb48le(from2, ux, uy, w, h);
|
|
107025
|
+
const fromR = fr / 65535;
|
|
107026
|
+
const fromG = fg / 65535;
|
|
107027
|
+
const fromB = fb / 65535;
|
|
107028
|
+
const overR = aces(fromR + warmR * leak * 3 + accentBright[0] * flare);
|
|
107029
|
+
const overG = aces(fromG + warmG * leak * 3 + accentBright[1] * flare);
|
|
107030
|
+
const overB = aces(fromB + warmB * leak * 3 + accentBright[2] * flare);
|
|
107031
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
107032
|
+
const blend = smoothstep(0.15, 0.85, p);
|
|
107033
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overR * 65535), toR, blend)), o);
|
|
107034
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overG * 65535), toG, blend)), o + 2);
|
|
107035
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(overB * 65535), toB, blend)), o + 4);
|
|
107036
|
+
}
|
|
107037
|
+
};
|
|
107038
|
+
TRANSITIONS["light-leak"] = lightLeak;
|
|
107039
|
+
var crossWarpMorph = (from2, to, out, w, h, p) => {
|
|
107040
|
+
for (let i = 0; i < w * h; i++) {
|
|
107041
|
+
const ux = i % w / w;
|
|
107042
|
+
const uy = Math.floor(i / w) / h;
|
|
107043
|
+
const o = i * 6;
|
|
107044
|
+
const dispX = fbm(ux * 3, uy * 3) - 0.5;
|
|
107045
|
+
const dispY = fbm(ux * 3 + 7.3, uy * 3 + 3.7) - 0.5;
|
|
107046
|
+
const fromUx = Math.max(0, Math.min(1, ux + dispX * p * 0.5));
|
|
107047
|
+
const fromUy = Math.max(0, Math.min(1, uy + dispY * p * 0.5));
|
|
107048
|
+
const toUx = Math.max(0, Math.min(1, ux - dispX * (1 - p) * 0.5));
|
|
107049
|
+
const toUy = Math.max(0, Math.min(1, uy - dispY * (1 - p) * 0.5));
|
|
107050
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
107051
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
107052
|
+
const n = fbm(ux * 4 + 3.1, uy * 4 + 1.7);
|
|
107053
|
+
const blend = smoothstep(0.4, 0.6, n + p * 1.2 - 0.6);
|
|
107054
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, blend)), o);
|
|
107055
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, blend)), o + 2);
|
|
107056
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, blend)), o + 4);
|
|
107057
|
+
}
|
|
107058
|
+
};
|
|
107059
|
+
TRANSITIONS["cross-warp-morph"] = crossWarpMorph;
|
|
107060
|
+
var whipPan = (from2, to, out, w, h, p) => {
|
|
107061
|
+
const fromOff = p * 1.5;
|
|
107062
|
+
const toOff = (1 - p) * 1.5;
|
|
107063
|
+
for (let i = 0; i < w * h; i++) {
|
|
107064
|
+
const ux = i % w / w;
|
|
107065
|
+
const uy = Math.floor(i / w) / h;
|
|
107066
|
+
const o = i * 6;
|
|
107067
|
+
let fromR = 0, fromG = 0, fromB = 0;
|
|
107068
|
+
for (let s = 0; s < 10; s++) {
|
|
107069
|
+
const f = s / 10;
|
|
107070
|
+
const fuv = Math.max(0, Math.min(1, ux + fromOff + p * 0.08 * f));
|
|
107071
|
+
const [r, g, b] = sampleRgb48le(from2, fuv, uy, w, h);
|
|
107072
|
+
fromR += r;
|
|
107073
|
+
fromG += g;
|
|
107074
|
+
fromB += b;
|
|
107075
|
+
}
|
|
107076
|
+
fromR /= 10;
|
|
107077
|
+
fromG /= 10;
|
|
107078
|
+
fromB /= 10;
|
|
107079
|
+
let toR = 0, toG = 0, toB = 0;
|
|
107080
|
+
for (let s = 0; s < 10; s++) {
|
|
107081
|
+
const f = s / 10;
|
|
107082
|
+
const tuv = Math.max(0, Math.min(1, ux - toOff - (1 - p) * 0.08 * f));
|
|
107083
|
+
const [r, g, b] = sampleRgb48le(to, tuv, uy, w, h);
|
|
107084
|
+
toR += r;
|
|
107085
|
+
toG += g;
|
|
107086
|
+
toB += b;
|
|
107087
|
+
}
|
|
107088
|
+
toR /= 10;
|
|
107089
|
+
toG /= 10;
|
|
107090
|
+
toB /= 10;
|
|
107091
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromR), Math.round(toR), p)), o);
|
|
107092
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromG), Math.round(toG), p)), o + 2);
|
|
107093
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromB), Math.round(toB), p)), o + 4);
|
|
107094
|
+
}
|
|
107095
|
+
};
|
|
107096
|
+
TRANSITIONS["whip-pan"] = whipPan;
|
|
107097
|
+
var cinematicZoom = (from2, to, out, w, h, p) => {
|
|
107098
|
+
const fromS = p * 0.08;
|
|
107099
|
+
const toS = (1 - p) * 0.06;
|
|
107100
|
+
for (let i = 0; i < w * h; i++) {
|
|
107101
|
+
const ux = i % w / w;
|
|
107102
|
+
const uy = Math.floor(i / w) / h;
|
|
107103
|
+
const o = i * 6;
|
|
107104
|
+
const dx = ux - 0.5;
|
|
107105
|
+
const dy = uy - 0.5;
|
|
107106
|
+
let fr = 0, fg = 0, fb = 0;
|
|
107107
|
+
for (let s = 0; s < 12; s++) {
|
|
107108
|
+
const f = s / 12;
|
|
107109
|
+
const rr = sampleRgb48le(
|
|
107110
|
+
from2,
|
|
107111
|
+
ux - dx * fromS * 1.06 * f,
|
|
107112
|
+
uy - dy * fromS * 1.06 * f,
|
|
107113
|
+
w,
|
|
107114
|
+
h
|
|
107115
|
+
)[0];
|
|
107116
|
+
const gg = sampleRgb48le(from2, ux - dx * fromS * f, uy - dy * fromS * f, w, h)[1];
|
|
107117
|
+
const bb = sampleRgb48le(
|
|
107118
|
+
from2,
|
|
107119
|
+
ux - dx * fromS * 0.94 * f,
|
|
107120
|
+
uy - dy * fromS * 0.94 * f,
|
|
107121
|
+
w,
|
|
107122
|
+
h
|
|
107123
|
+
)[2];
|
|
107124
|
+
fr += rr;
|
|
107125
|
+
fg += gg;
|
|
107126
|
+
fb += bb;
|
|
107127
|
+
}
|
|
107128
|
+
fr /= 12;
|
|
107129
|
+
fg /= 12;
|
|
107130
|
+
fb /= 12;
|
|
107131
|
+
let tr = 0, tg = 0, tb = 0;
|
|
107132
|
+
for (let s = 0; s < 12; s++) {
|
|
107133
|
+
const f = s / 12;
|
|
107134
|
+
const rr = sampleRgb48le(to, ux + dx * toS * 1.06 * f, uy + dy * toS * 1.06 * f, w, h)[0];
|
|
107135
|
+
const gg = sampleRgb48le(to, ux + dx * toS * f, uy + dy * toS * f, w, h)[1];
|
|
107136
|
+
const bb = sampleRgb48le(to, ux + dx * toS * 0.94 * f, uy + dy * toS * 0.94 * f, w, h)[2];
|
|
107137
|
+
tr += rr;
|
|
107138
|
+
tg += gg;
|
|
107139
|
+
tb += bb;
|
|
107140
|
+
}
|
|
107141
|
+
tr /= 12;
|
|
107142
|
+
tg /= 12;
|
|
107143
|
+
tb /= 12;
|
|
107144
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fr), Math.round(tr), p)), o);
|
|
107145
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fg), Math.round(tg), p)), o + 2);
|
|
107146
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fb), Math.round(tb), p)), o + 4);
|
|
107147
|
+
}
|
|
107148
|
+
};
|
|
107149
|
+
TRANSITIONS["cinematic-zoom"] = cinematicZoom;
|
|
107150
|
+
var gravitationalLens = (from2, to, out, w, h, p) => {
|
|
107151
|
+
for (let i = 0; i < w * h; i++) {
|
|
107152
|
+
const ux = i % w / w;
|
|
107153
|
+
const uy = Math.floor(i / w) / h;
|
|
107154
|
+
const o = i * 6;
|
|
107155
|
+
const uvx = ux - 0.5;
|
|
107156
|
+
const uvy = uy - 0.5;
|
|
107157
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
107158
|
+
const pull = p * 2;
|
|
107159
|
+
const warpStr = pull * 0.3 / (dist + 0.1);
|
|
107160
|
+
const warpedX = Math.max(0, Math.min(1, ux - uvx * warpStr));
|
|
107161
|
+
const warpedY = Math.max(0, Math.min(1, uy - uvy * warpStr));
|
|
107162
|
+
const [, ag] = sampleRgb48le(from2, warpedX, warpedY, w, h);
|
|
107163
|
+
const horizon = smoothstep(0, 0.3, dist / (1 - p * 0.85 + 1e-3));
|
|
107164
|
+
const shift = pull * 0.02 / (dist + 0.2);
|
|
107165
|
+
const rSampX = Math.max(0, Math.min(1, ux - uvx * (warpStr + shift)));
|
|
107166
|
+
const rSampY = Math.max(0, Math.min(1, uy - uvy * (warpStr + shift)));
|
|
107167
|
+
const bSampX = Math.max(0, Math.min(1, ux - uvx * (warpStr - shift)));
|
|
107168
|
+
const bSampY = Math.max(0, Math.min(1, uy - uvy * (warpStr - shift)));
|
|
107169
|
+
const ar = sampleRgb48le(from2, rSampX, rSampY, w, h)[0];
|
|
107170
|
+
const ab = sampleRgb48le(from2, bSampX, bSampY, w, h)[2];
|
|
107171
|
+
const lensedR = Math.round(ar * horizon);
|
|
107172
|
+
const lensedG = Math.round(ag * horizon);
|
|
107173
|
+
const lensedB = Math.round(ab * horizon);
|
|
107174
|
+
const [toR, toG, toB] = sampleRgb48le(to, ux, uy, w, h);
|
|
107175
|
+
const blend = smoothstep(0.3, 0.9, p);
|
|
107176
|
+
out.writeUInt16LE(clamp16(mix16(lensedR, toR, blend)), o);
|
|
107177
|
+
out.writeUInt16LE(clamp16(mix16(lensedG, toG, blend)), o + 2);
|
|
107178
|
+
out.writeUInt16LE(clamp16(mix16(lensedB, toB, blend)), o + 4);
|
|
107179
|
+
}
|
|
107180
|
+
};
|
|
107181
|
+
TRANSITIONS["gravitational-lens"] = gravitationalLens;
|
|
107182
|
+
var rippleWaves = (from2, to, out, w, h, p) => {
|
|
107183
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107184
|
+
for (let i = 0; i < w * h; i++) {
|
|
107185
|
+
const ux = i % w / w;
|
|
107186
|
+
const uy = Math.floor(i / w) / h;
|
|
107187
|
+
const o = i * 6;
|
|
107188
|
+
const uvx = ux - 0.5;
|
|
107189
|
+
const uvy = uy - 0.5;
|
|
107190
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
107191
|
+
const nux = uvx + 1e-3;
|
|
107192
|
+
const nuy = uvy + 1e-3;
|
|
107193
|
+
const nlen = Math.sqrt(nux * nux + nuy * nuy);
|
|
107194
|
+
const dirx = nux / nlen;
|
|
107195
|
+
const diry = nuy / nlen;
|
|
107196
|
+
const fromAmp = p * 0.04;
|
|
107197
|
+
const fw1 = Math.exp(Math.sin(dist * 25 - p * 12) - 1);
|
|
107198
|
+
const fw2 = Math.exp(Math.sin(dist * 50 - p * 18) - 1) * 0.5;
|
|
107199
|
+
const fromUx = Math.max(0, Math.min(1, ux + dirx * (fw1 + fw2) * fromAmp));
|
|
107200
|
+
const fromUy = Math.max(0, Math.min(1, uy + diry * (fw1 + fw2) * fromAmp));
|
|
107201
|
+
const toAmp = (1 - p) * 0.04;
|
|
107202
|
+
const tw1 = Math.exp(Math.sin(dist * 25 + p * 12) - 1);
|
|
107203
|
+
const tw2 = Math.exp(Math.sin(dist * 50 + p * 18) - 1) * 0.5;
|
|
107204
|
+
const toUx = Math.max(0, Math.min(1, ux - dirx * (tw1 + tw2) * toAmp));
|
|
107205
|
+
const toUy = Math.max(0, Math.min(1, uy - diry * (tw1 + tw2) * toAmp));
|
|
107206
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
107207
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
107208
|
+
const peak = fw1 * p;
|
|
107209
|
+
const tintR = accentBright[0] * peak * 0.1;
|
|
107210
|
+
const tintG = accentBright[1] * peak * 0.1;
|
|
107211
|
+
const tintB = accentBright[2] * peak * 0.1;
|
|
107212
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromR + tintR), toR, p)), o);
|
|
107213
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromG + tintG), toG, p)), o + 2);
|
|
107214
|
+
out.writeUInt16LE(clamp16(mix16(Math.round(fromB + tintB), toB, p)), o + 4);
|
|
107215
|
+
}
|
|
107216
|
+
};
|
|
107217
|
+
TRANSITIONS["ripple-waves"] = rippleWaves;
|
|
107218
|
+
var swirlVortex = (from2, to, out, w, h, p) => {
|
|
107219
|
+
for (let i = 0; i < w * h; i++) {
|
|
107220
|
+
const ux = i % w / w;
|
|
107221
|
+
const uy = Math.floor(i / w) / h;
|
|
107222
|
+
const o = i * 6;
|
|
107223
|
+
const uvx = ux - 0.5;
|
|
107224
|
+
const uvy = uy - 0.5;
|
|
107225
|
+
const dist = Math.sqrt(uvx * uvx + uvy * uvy);
|
|
107226
|
+
const warp = fbm(ux * 4, uy * 4) * 0.5;
|
|
107227
|
+
const fromAng = p * (1 - dist) * 10 + warp * p * 3;
|
|
107228
|
+
const fs8 = Math.sin(fromAng);
|
|
107229
|
+
const fc = Math.cos(fromAng);
|
|
107230
|
+
const fromUx = Math.max(0, Math.min(1, uvx * fc - uvy * fs8 + 0.5));
|
|
107231
|
+
const fromUy = Math.max(0, Math.min(1, uvx * fs8 + uvy * fc + 0.5));
|
|
107232
|
+
const toAng = -(1 - p) * (1 - dist) * 10 - warp * (1 - p) * 3;
|
|
107233
|
+
const ts = Math.sin(toAng);
|
|
107234
|
+
const tc = Math.cos(toAng);
|
|
107235
|
+
const toUx = Math.max(0, Math.min(1, uvx * tc - uvy * ts + 0.5));
|
|
107236
|
+
const toUy = Math.max(0, Math.min(1, uvx * ts + uvy * tc + 0.5));
|
|
107237
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, fromUy, w, h);
|
|
107238
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, toUy, w, h);
|
|
107239
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, p)), o);
|
|
107240
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, p)), o + 2);
|
|
107241
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, p)), o + 4);
|
|
107242
|
+
}
|
|
107243
|
+
};
|
|
107244
|
+
TRANSITIONS["swirl-vortex"] = swirlVortex;
|
|
107245
|
+
var thermalDistortion = (from2, to, out, w, h, p) => {
|
|
107246
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107247
|
+
for (let i = 0; i < w * h; i++) {
|
|
107248
|
+
const ux = i % w / w;
|
|
107249
|
+
const uy = Math.floor(i / w) / h;
|
|
107250
|
+
const o = i * 6;
|
|
107251
|
+
const heat = p * 1.5;
|
|
107252
|
+
const yFade = smoothstep(1, 0, uy);
|
|
107253
|
+
const shimmer = Math.sin(uy * 40 + fbm(ux * 6, uy * 6) * 8) * fbm(ux * 3 + 0, uy * 3 + p * 2);
|
|
107254
|
+
const dispX = shimmer * heat * 0.03 * yFade;
|
|
107255
|
+
const fromUx = Math.max(0, Math.min(1, ux + dispX));
|
|
107256
|
+
const [fromR, fromG, fromB] = sampleRgb48le(from2, fromUx, uy, w, h);
|
|
107257
|
+
const invShimmer = Math.sin(uy * 40 + fbm(ux * 6 + 3, uy * 6 + 3) * 8) * fbm(ux * 3 + 3, uy * 3 + p * 2);
|
|
107258
|
+
const dispX2 = invShimmer * (1 - p) * 0.03 * yFade;
|
|
107259
|
+
const toUx = Math.max(0, Math.min(1, ux + dispX2));
|
|
107260
|
+
const [toR, toG, toB] = sampleRgb48le(to, toUx, uy, w, h);
|
|
107261
|
+
const haze = heat * yFade * 0.15 * (1 - p);
|
|
107262
|
+
out.writeUInt16LE(clamp16(mix16(fromR, toR, p) + Math.round(accentBright[0] * haze)), o);
|
|
107263
|
+
out.writeUInt16LE(clamp16(mix16(fromG, toG, p) + Math.round(accentBright[1] * haze)), o + 2);
|
|
107264
|
+
out.writeUInt16LE(clamp16(mix16(fromB, toB, p) + Math.round(accentBright[2] * haze)), o + 4);
|
|
107265
|
+
}
|
|
107266
|
+
};
|
|
107267
|
+
TRANSITIONS["thermal-distortion"] = thermalDistortion;
|
|
107268
|
+
var domainWarp = (from2, to, out, w, h, p) => {
|
|
107269
|
+
const accentDark = [25e3, 8e3, 2e3];
|
|
107270
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107271
|
+
for (let i = 0; i < w * h; i++) {
|
|
107272
|
+
const ux = i % w / w;
|
|
107273
|
+
const uy = Math.floor(i / w) / h;
|
|
107274
|
+
const o = i * 6;
|
|
107275
|
+
const qx = fbm(ux * 3, uy * 3);
|
|
107276
|
+
const qy = fbm(ux * 3 + 5.2, uy * 3 + 1.3);
|
|
107277
|
+
const rx = fbm(ux * 3 + qx * 4 + 1.7, uy * 3 + qy * 4 + 9.2);
|
|
107278
|
+
const ry = fbm(ux * 3 + qx * 4 + 8.3, uy * 3 + qy * 4 + 2.8);
|
|
107279
|
+
const n = fbm(ux * 3 + rx * 2, uy * 3 + ry * 2);
|
|
107280
|
+
const warpDirX = (qx - 0.5) * 0.4;
|
|
107281
|
+
const warpDirY = (qy - 0.5) * 0.4;
|
|
107282
|
+
const aUx = Math.max(0, Math.min(1, ux + warpDirX * p));
|
|
107283
|
+
const aUy = Math.max(0, Math.min(1, uy + warpDirY * p));
|
|
107284
|
+
const bUx = Math.max(0, Math.min(1, ux - warpDirX * (1 - p)));
|
|
107285
|
+
const bUy = Math.max(0, Math.min(1, uy - warpDirY * (1 - p)));
|
|
107286
|
+
const [aR, aG, aB] = sampleRgb48le(from2, aUx, aUy, w, h);
|
|
107287
|
+
const [bR, bG, bB] = sampleRgb48le(to, bUx, bUy, w, h);
|
|
107288
|
+
const e = smoothstep(p - 0.08, p + 0.08, n);
|
|
107289
|
+
const ed = Math.abs(n - p);
|
|
107290
|
+
const pStep = p >= 1 ? 1 : 0;
|
|
107291
|
+
const em = smoothstep(0.1, 0, ed) * (1 - pStep);
|
|
107292
|
+
const ecBlend = smoothstep(0, 0.1, ed);
|
|
107293
|
+
const ecR = accentDark[0] + (accentBright[0] - accentDark[0]) * (1 - ecBlend);
|
|
107294
|
+
const ecG = accentDark[1] + (accentBright[1] - accentDark[1]) * (1 - ecBlend);
|
|
107295
|
+
const ecB = accentDark[2] + (accentBright[2] - accentDark[2]) * (1 - ecBlend);
|
|
107296
|
+
out.writeUInt16LE(clamp16(mix16(bR, aR, e) + Math.round(ecR * em * 2)), o);
|
|
107297
|
+
out.writeUInt16LE(clamp16(mix16(bG, aG, e) + Math.round(ecG * em * 2)), o + 2);
|
|
107298
|
+
out.writeUInt16LE(clamp16(mix16(bB, aB, e) + Math.round(ecB * em * 2)), o + 4);
|
|
107299
|
+
}
|
|
107300
|
+
};
|
|
107301
|
+
TRANSITIONS["domain-warp"] = domainWarp;
|
|
107302
|
+
function ridged(px, py) {
|
|
107303
|
+
let value = 0;
|
|
107304
|
+
let amplitude = 0.5;
|
|
107305
|
+
let x = px;
|
|
107306
|
+
let y = py;
|
|
107307
|
+
for (let i = 0; i < 5; i++) {
|
|
107308
|
+
value += amplitude * Math.abs(vnoise(x, y) * 2 - 1);
|
|
107309
|
+
const nx = ROT_A * x - ROT_B * y;
|
|
107310
|
+
const ny = ROT_B * x + ROT_A * y;
|
|
107311
|
+
x = nx * 2.02;
|
|
107312
|
+
y = ny * 2.02;
|
|
107313
|
+
amplitude *= 0.5;
|
|
107314
|
+
}
|
|
107315
|
+
return value;
|
|
107316
|
+
}
|
|
107317
|
+
var ridgedBurn = (from2, to, out, w, h, p) => {
|
|
107318
|
+
const accent = [5e4, 25e3, 5e3];
|
|
107319
|
+
const accentDark = [25e3, 8e3, 2e3];
|
|
107320
|
+
const accentBright = [65535, 55e3, 35e3];
|
|
107321
|
+
for (let i = 0; i < w * h; i++) {
|
|
107322
|
+
const ux = i % w / w;
|
|
107323
|
+
const uy = Math.floor(i / w) / h;
|
|
107324
|
+
const o = i * 6;
|
|
107325
|
+
const [aR, aG, aB] = sampleRgb48le(from2, ux, uy, w, h);
|
|
107326
|
+
const [bR, bG, bB] = sampleRgb48le(to, ux, uy, w, h);
|
|
107327
|
+
const n = ridged(ux * 4, uy * 4);
|
|
107328
|
+
const e = smoothstep(p - 0.04, p + 0.04, n);
|
|
107329
|
+
const heat = smoothstep(0.12, 0, Math.abs(n - p));
|
|
107330
|
+
const pStep = p >= 1 ? 1 : 0;
|
|
107331
|
+
const heatMasked = heat * (1 - pStep);
|
|
107332
|
+
let burnR = accentDark[0] + (accent[0] - accentDark[0]) * smoothstep(0, 0.25, heatMasked);
|
|
107333
|
+
let burnG = accentDark[1] + (accent[1] - accentDark[1]) * smoothstep(0, 0.25, heatMasked);
|
|
107334
|
+
let burnB = accentDark[2] + (accent[2] - accentDark[2]) * smoothstep(0, 0.25, heatMasked);
|
|
107335
|
+
const blend2 = smoothstep(0.25, 0.5, heatMasked);
|
|
107336
|
+
burnR = burnR + (accentBright[0] - burnR) * blend2;
|
|
107337
|
+
burnG = burnG + (accentBright[1] - burnG) * blend2;
|
|
107338
|
+
burnB = burnB + (accentBright[2] - burnB) * blend2;
|
|
107339
|
+
const blend3 = smoothstep(0.5, 1, heatMasked);
|
|
107340
|
+
burnR = burnR + (65535 - burnR) * blend3;
|
|
107341
|
+
burnG = burnG + (65535 - burnG) * blend3;
|
|
107342
|
+
burnB = burnB + (65535 - burnB) * blend3;
|
|
107343
|
+
const sparks = (vnoise(ux * 80, uy * 80) >= 0.92 ? 1 : 0) * heatMasked * 3;
|
|
107344
|
+
out.writeUInt16LE(
|
|
107345
|
+
clamp16(
|
|
107346
|
+
mix16(bR, aR, e) + Math.round(burnR * heatMasked * 3.5) + Math.round(accentBright[0] * sparks)
|
|
107347
|
+
),
|
|
107348
|
+
o
|
|
107349
|
+
);
|
|
107350
|
+
out.writeUInt16LE(
|
|
107351
|
+
clamp16(
|
|
107352
|
+
mix16(bG, aG, e) + Math.round(burnG * heatMasked * 3.5) + Math.round(accentBright[1] * sparks)
|
|
107353
|
+
),
|
|
107354
|
+
o + 2
|
|
107355
|
+
);
|
|
107356
|
+
out.writeUInt16LE(
|
|
107357
|
+
clamp16(
|
|
107358
|
+
mix16(bB, aB, e) + Math.round(burnB * heatMasked * 3.5) + Math.round(accentBright[2] * sparks)
|
|
107359
|
+
),
|
|
107360
|
+
o + 4
|
|
107361
|
+
);
|
|
107362
|
+
}
|
|
107363
|
+
};
|
|
107364
|
+
TRANSITIONS["ridged-burn"] = ridgedBurn;
|
|
107365
|
+
|
|
105954
107366
|
// src/services/renderOrchestrator.ts
|
|
105955
107367
|
import { join as join15, dirname as dirname10, resolve as resolve10 } from "path";
|
|
105956
107368
|
import { randomUUID } from "crypto";
|
|
@@ -106271,6 +107683,10 @@ var RENDER_MODE_SCRIPT = `(function() {
|
|
|
106271
107683
|
}
|
|
106272
107684
|
waitForPlayer();
|
|
106273
107685
|
})();`;
|
|
107686
|
+
var HF_EARLY_STUB = `(function() {
|
|
107687
|
+
if (typeof window === "undefined") return;
|
|
107688
|
+
if (!window.__hf) window.__hf = {};
|
|
107689
|
+
})();`;
|
|
106274
107690
|
var HF_BRIDGE_SCRIPT = `(function() {
|
|
106275
107691
|
var __realSetInterval =
|
|
106276
107692
|
window.__HF_VIRTUAL_TIME__ && typeof window.__HF_VIRTUAL_TIME__.originalSetInterval === "function"
|
|
@@ -106316,20 +107732,24 @@ var HF_BRIDGE_SCRIPT = `(function() {
|
|
|
106316
107732
|
if (!p || typeof p.renderSeek !== "function" || typeof p.getDuration !== "function") {
|
|
106317
107733
|
return false;
|
|
106318
107734
|
}
|
|
106319
|
-
window.__hf
|
|
106320
|
-
|
|
107735
|
+
var hf = window.__hf || {};
|
|
107736
|
+
Object.defineProperty(hf, "duration", {
|
|
107737
|
+
configurable: true,
|
|
107738
|
+
enumerable: true,
|
|
107739
|
+
get: function() {
|
|
106321
107740
|
var d = p.getDuration();
|
|
106322
107741
|
return d > 0 ? d : getDeclaredDuration();
|
|
106323
107742
|
},
|
|
106324
|
-
|
|
106325
|
-
|
|
106326
|
-
|
|
106327
|
-
|
|
106328
|
-
|
|
106329
|
-
|
|
106330
|
-
|
|
106331
|
-
|
|
107743
|
+
});
|
|
107744
|
+
hf.seek = function(t) {
|
|
107745
|
+
p.renderSeek(t);
|
|
107746
|
+
var nextTimeMs = (Math.max(0, Number(t) || 0)) * 1000;
|
|
107747
|
+
if (window.__HF_VIRTUAL_TIME__ && typeof window.__HF_VIRTUAL_TIME__.seekToTime === "function") {
|
|
107748
|
+
window.__HF_VIRTUAL_TIME__.seekToTime(nextTimeMs);
|
|
107749
|
+
}
|
|
107750
|
+
seekSameOriginChildFrames(window, nextTimeMs);
|
|
106332
107751
|
};
|
|
107752
|
+
window.__hf = hf;
|
|
106333
107753
|
return true;
|
|
106334
107754
|
}
|
|
106335
107755
|
if (bridge()) return;
|
|
@@ -106411,7 +107831,7 @@ ${headTags}`);
|
|
|
106411
107831
|
}
|
|
106412
107832
|
function createFileServer2(options) {
|
|
106413
107833
|
const { projectDir, compiledDir, port = 0, stripEmbeddedRuntime = true } = options;
|
|
106414
|
-
const preHeadScripts = options.preHeadScripts ?? [];
|
|
107834
|
+
const preHeadScripts = [HF_EARLY_STUB, ...options.preHeadScripts ?? []];
|
|
106415
107835
|
const headScripts = options.headScripts ?? [getVerifiedHyperframeRuntimeSource()];
|
|
106416
107836
|
const bodyScripts = options.bodyScripts ?? [RENDER_MODE_SCRIPT, HF_BRIDGE_SCRIPT];
|
|
106417
107837
|
const app = new Hono2();
|
|
@@ -107671,6 +109091,24 @@ async function safeCleanup(label, fn, log = defaultLogger) {
|
|
|
107671
109091
|
});
|
|
107672
109092
|
}
|
|
107673
109093
|
}
|
|
109094
|
+
var frameDirMaxIndexCache = /* @__PURE__ */ new Map();
|
|
109095
|
+
var FRAME_FILENAME_RE = /^frame_(\d+)\.png$/;
|
|
109096
|
+
function getMaxFrameIndex(frameDir) {
|
|
109097
|
+
const cached = frameDirMaxIndexCache.get(frameDir);
|
|
109098
|
+
if (cached !== void 0) return cached;
|
|
109099
|
+
let max = 0;
|
|
109100
|
+
try {
|
|
109101
|
+
for (const name of readdirSync6(frameDir)) {
|
|
109102
|
+
const m = FRAME_FILENAME_RE.exec(name);
|
|
109103
|
+
if (!m) continue;
|
|
109104
|
+
const n = Number(m[1]);
|
|
109105
|
+
if (Number.isFinite(n) && n > max) max = n;
|
|
109106
|
+
}
|
|
109107
|
+
} catch {
|
|
109108
|
+
}
|
|
109109
|
+
frameDirMaxIndexCache.set(frameDir, max);
|
|
109110
|
+
return max;
|
|
109111
|
+
}
|
|
107674
109112
|
var RenderCancelledError = class extends Error {
|
|
107675
109113
|
reason;
|
|
107676
109114
|
constructor(message = "render_cancelled", reason = "aborted") {
|
|
@@ -107772,6 +109210,63 @@ function applyRenderModeHints(cfg, compiled, log = defaultLogger) {
|
|
|
107772
109210
|
reasons: compiled.renderModeHints.reasons.map((reason) => reason.message)
|
|
107773
109211
|
});
|
|
107774
109212
|
}
|
|
109213
|
+
function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, width, height, log, sourceTransfer, targetTransfer) {
|
|
109214
|
+
const frameDir = hdrFrameDirs.get(el.id);
|
|
109215
|
+
const startTime = hdrStartTimes.get(el.id);
|
|
109216
|
+
if (!frameDir || startTime === void 0) {
|
|
109217
|
+
return;
|
|
109218
|
+
}
|
|
109219
|
+
const videoFrameIndex = Math.round((time - startTime) * fps) + 1;
|
|
109220
|
+
if (videoFrameIndex < 1) return;
|
|
109221
|
+
const maxIndex = getMaxFrameIndex(frameDir);
|
|
109222
|
+
const effectiveIndex = maxIndex > 0 ? Math.min(videoFrameIndex, maxIndex) : videoFrameIndex;
|
|
109223
|
+
const framePath = join15(frameDir, `frame_${String(effectiveIndex).padStart(4, "0")}.png`);
|
|
109224
|
+
if (!existsSync15(framePath)) {
|
|
109225
|
+
return;
|
|
109226
|
+
}
|
|
109227
|
+
try {
|
|
109228
|
+
const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(readFileSync9(framePath));
|
|
109229
|
+
if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
|
|
109230
|
+
convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
|
|
109231
|
+
}
|
|
109232
|
+
const viewportMatrix = parseTransformMatrix(el.transform);
|
|
109233
|
+
const br = el.borderRadius;
|
|
109234
|
+
const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0;
|
|
109235
|
+
const borderRadiusParam = hasBorderRadius ? br : void 0;
|
|
109236
|
+
if (viewportMatrix) {
|
|
109237
|
+
blitRgb48leAffine(
|
|
109238
|
+
canvas,
|
|
109239
|
+
hdrRgb,
|
|
109240
|
+
viewportMatrix,
|
|
109241
|
+
srcW,
|
|
109242
|
+
srcH,
|
|
109243
|
+
width,
|
|
109244
|
+
height,
|
|
109245
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
109246
|
+
borderRadiusParam
|
|
109247
|
+
);
|
|
109248
|
+
} else {
|
|
109249
|
+
blitRgb48leRegion(
|
|
109250
|
+
canvas,
|
|
109251
|
+
hdrRgb,
|
|
109252
|
+
el.x,
|
|
109253
|
+
el.y,
|
|
109254
|
+
srcW,
|
|
109255
|
+
srcH,
|
|
109256
|
+
width,
|
|
109257
|
+
height,
|
|
109258
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
109259
|
+
borderRadiusParam
|
|
109260
|
+
);
|
|
109261
|
+
}
|
|
109262
|
+
} catch (err) {
|
|
109263
|
+
if (log) {
|
|
109264
|
+
log.debug(`HDR blit failed for ${el.id}`, {
|
|
109265
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109266
|
+
});
|
|
109267
|
+
}
|
|
109268
|
+
}
|
|
109269
|
+
}
|
|
107775
109270
|
function createRenderJob(config2) {
|
|
107776
109271
|
return {
|
|
107777
109272
|
id: randomUUID(),
|
|
@@ -108042,6 +109537,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108042
109537
|
perfStages.browserProbeMs = Date.now() - probeStart;
|
|
108043
109538
|
job.duration = composition.duration;
|
|
108044
109539
|
job.totalFrames = Math.ceil(composition.duration * job.config.fps);
|
|
109540
|
+
const totalFrames = job.totalFrames;
|
|
108045
109541
|
if (job.duration <= 0) {
|
|
108046
109542
|
const diagnostics = [];
|
|
108047
109543
|
try {
|
|
@@ -108070,7 +109566,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108070
109566
|
}
|
|
108071
109567
|
}
|
|
108072
109568
|
}
|
|
108073
|
-
} catch {
|
|
109569
|
+
} catch (err) {
|
|
109570
|
+
log.warn("Failed to gather browser diagnostics for zero-duration composition", {
|
|
109571
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109572
|
+
});
|
|
108074
109573
|
diagnostics.push("(Could not gather browser diagnostics \u2014 page may have crashed)");
|
|
108075
109574
|
}
|
|
108076
109575
|
const hint = diagnostics.length > 0 ? "\n\nDiagnostics:\n - " + diagnostics.join("\n - ") : "\n\nCheck that GSAP timelines are registered on window.__timelines.";
|
|
@@ -108094,8 +109593,28 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108094
109593
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
|
|
108095
109594
|
let frameLookup = null;
|
|
108096
109595
|
const compiledDir = join15(workDir, "compiled");
|
|
109596
|
+
let extractionResult = null;
|
|
109597
|
+
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
109598
|
+
const videoTransfers = /* @__PURE__ */ new Map();
|
|
109599
|
+
if (job.config.hdr && composition.videos.length > 0) {
|
|
109600
|
+
await Promise.all(
|
|
109601
|
+
composition.videos.map(async (v) => {
|
|
109602
|
+
let videoPath = v.src;
|
|
109603
|
+
if (!videoPath.startsWith("/")) {
|
|
109604
|
+
const fromCompiled = existsSync15(join15(compiledDir, videoPath)) ? join15(compiledDir, videoPath) : join15(projectDir, videoPath);
|
|
109605
|
+
videoPath = fromCompiled;
|
|
109606
|
+
}
|
|
109607
|
+
if (!existsSync15(videoPath)) return;
|
|
109608
|
+
const meta = await extractVideoMetadata(videoPath);
|
|
109609
|
+
if (isHdrColorSpace(meta.colorSpace)) {
|
|
109610
|
+
nativeHdrVideoIds.add(v.id);
|
|
109611
|
+
videoTransfers.set(v.id, detectTransfer(meta.colorSpace));
|
|
109612
|
+
}
|
|
109613
|
+
})
|
|
109614
|
+
);
|
|
109615
|
+
}
|
|
108097
109616
|
if (composition.videos.length > 0) {
|
|
108098
|
-
|
|
109617
|
+
extractionResult = await extractAllVideoFrames(
|
|
108099
109618
|
composition.videos,
|
|
108100
109619
|
projectDir,
|
|
108101
109620
|
{ fps: job.config.fps, outputDir: join15(workDir, "video-frames") },
|
|
@@ -108130,6 +109649,29 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108130
109649
|
} else {
|
|
108131
109650
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
108132
109651
|
}
|
|
109652
|
+
let effectiveHdr;
|
|
109653
|
+
if (job.config.hdr && frameLookup) {
|
|
109654
|
+
const colorSpaces = (extractionResult?.extracted ?? []).map((ext) => ext.metadata.colorSpace);
|
|
109655
|
+
const info = analyzeCompositionHdr(colorSpaces);
|
|
109656
|
+
if (info.hasHdr && info.dominantTransfer) {
|
|
109657
|
+
effectiveHdr = { transfer: info.dominantTransfer };
|
|
109658
|
+
}
|
|
109659
|
+
}
|
|
109660
|
+
if (job.config.hdr && !effectiveHdr && nativeHdrVideoIds.size > 0) {
|
|
109661
|
+
const firstTransfer = videoTransfers.values().next().value;
|
|
109662
|
+
if (firstTransfer) {
|
|
109663
|
+
effectiveHdr = { transfer: firstTransfer };
|
|
109664
|
+
}
|
|
109665
|
+
}
|
|
109666
|
+
if (effectiveHdr && outputFormat !== "mp4") {
|
|
109667
|
+
log.info(`[Render] HDR source detected but format is ${outputFormat} \u2014 using SDR`);
|
|
109668
|
+
effectiveHdr = void 0;
|
|
109669
|
+
}
|
|
109670
|
+
if (effectiveHdr) {
|
|
109671
|
+
log.info(
|
|
109672
|
+
`[Render] HDR source detected \u2014 output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
109673
|
+
);
|
|
109674
|
+
}
|
|
108133
109675
|
const stage3Start = Date.now();
|
|
108134
109676
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
108135
109677
|
const audioOutputPath = join15(workDir, "audio.aac");
|
|
@@ -108171,222 +109713,655 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108171
109713
|
format: needsAlpha ? "png" : "jpeg",
|
|
108172
109714
|
quality: needsAlpha ? void 0 : job.config.quality === "draft" ? 80 : 95
|
|
108173
109715
|
};
|
|
108174
|
-
const workerCount = calculateOptimalWorkers(
|
|
109716
|
+
const workerCount = calculateOptimalWorkers(totalFrames, job.config.workers, cfg);
|
|
108175
109717
|
const FORMAT_EXT = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
108176
109718
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
108177
109719
|
const videoOnlyPath = join15(workDir, `video-only${videoExt}`);
|
|
108178
|
-
const
|
|
108179
|
-
const
|
|
108180
|
-
const
|
|
108181
|
-
const baseEncoderOpts = {
|
|
108182
|
-
fps: job.config.fps,
|
|
108183
|
-
width,
|
|
108184
|
-
height,
|
|
108185
|
-
codec: preset.codec,
|
|
108186
|
-
preset: preset.preset,
|
|
108187
|
-
quality: effectiveQuality,
|
|
108188
|
-
bitrate: effectiveBitrate,
|
|
108189
|
-
pixelFormat: preset.pixelFormat,
|
|
108190
|
-
useGpu: job.config.useGpu
|
|
108191
|
-
};
|
|
109720
|
+
const hasHdrContent = effectiveHdr && nativeHdrVideoIds.size > 0;
|
|
109721
|
+
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
109722
|
+
const preset = getEncoderPreset(job.config.quality, outputFormat, encoderHdr);
|
|
108192
109723
|
job.framesRendered = 0;
|
|
108193
|
-
|
|
108194
|
-
|
|
108195
|
-
|
|
109724
|
+
if (hasHdrContent) {
|
|
109725
|
+
log.info("[Render] HDR layered composite: z-ordered DOM + native HLG video layers");
|
|
109726
|
+
const hdrVideoIds = composition.videos.filter((v) => nativeHdrVideoIds.has(v.id)).map((v) => v.id);
|
|
109727
|
+
const hdrVideoSrcPaths = /* @__PURE__ */ new Map();
|
|
109728
|
+
for (const v of composition.videos) {
|
|
109729
|
+
if (!hdrVideoIds.includes(v.id)) continue;
|
|
109730
|
+
let srcPath = v.src;
|
|
109731
|
+
if (!srcPath.startsWith("/")) {
|
|
109732
|
+
const fromCompiled = join15(compiledDir, srcPath);
|
|
109733
|
+
srcPath = existsSync15(fromCompiled) ? fromCompiled : join15(projectDir, srcPath);
|
|
109734
|
+
}
|
|
109735
|
+
hdrVideoSrcPaths.set(v.id, srcPath);
|
|
109736
|
+
}
|
|
109737
|
+
if (!fileServer) throw new Error("fileServer must be initialized before HDR compositing");
|
|
109738
|
+
const domSession = await createCaptureSession(
|
|
109739
|
+
fileServer.url,
|
|
109740
|
+
framesDir,
|
|
109741
|
+
captureOptions,
|
|
109742
|
+
createVideoFrameInjector(frameLookup),
|
|
109743
|
+
cfg
|
|
109744
|
+
);
|
|
109745
|
+
await initializeSession(domSession);
|
|
109746
|
+
assertNotAborted();
|
|
109747
|
+
lastBrowserConsole = domSession.browserConsoleBuffer;
|
|
109748
|
+
await initTransparentBackground(domSession.page);
|
|
109749
|
+
const transitionMeta = await domSession.page.evaluate(() => {
|
|
109750
|
+
return window.__hf?.transitions ?? [];
|
|
109751
|
+
});
|
|
109752
|
+
const sceneElements = await domSession.page.evaluate(() => {
|
|
109753
|
+
const scenes = document.querySelectorAll(".scene");
|
|
109754
|
+
const map2 = {};
|
|
109755
|
+
for (const scene of scenes) {
|
|
109756
|
+
const els = scene.querySelectorAll("[data-start]");
|
|
109757
|
+
map2[scene.id] = Array.from(els).map((e) => e.id);
|
|
109758
|
+
}
|
|
109759
|
+
return map2;
|
|
109760
|
+
});
|
|
109761
|
+
const transitionRanges = transitionMeta.map((t) => ({
|
|
109762
|
+
...t,
|
|
109763
|
+
startFrame: Math.floor(t.time * job.config.fps),
|
|
109764
|
+
endFrame: Math.ceil((t.time + t.duration) * job.config.fps)
|
|
109765
|
+
}));
|
|
109766
|
+
if (transitionRanges.length > 0) {
|
|
109767
|
+
log.info("[Render] Detected shader transitions for HDR compositing", {
|
|
109768
|
+
count: transitionRanges.length,
|
|
109769
|
+
transitions: transitionRanges.map((t) => ({
|
|
109770
|
+
shader: t.shader,
|
|
109771
|
+
from: t.fromScene,
|
|
109772
|
+
to: t.toScene,
|
|
109773
|
+
frames: `${t.startFrame}-${t.endFrame}`
|
|
109774
|
+
}))
|
|
109775
|
+
});
|
|
109776
|
+
}
|
|
109777
|
+
const hdrEncoder = await spawnStreamingEncoder(
|
|
108196
109778
|
videoOnlyPath,
|
|
108197
109779
|
{
|
|
108198
|
-
|
|
108199
|
-
|
|
109780
|
+
fps: job.config.fps,
|
|
109781
|
+
width,
|
|
109782
|
+
height,
|
|
109783
|
+
codec: preset.codec,
|
|
109784
|
+
preset: preset.preset,
|
|
109785
|
+
quality: preset.quality,
|
|
109786
|
+
pixelFormat: preset.pixelFormat,
|
|
109787
|
+
hdr: preset.hdr,
|
|
109788
|
+
rawInputFormat: "rgb48le"
|
|
108200
109789
|
},
|
|
108201
|
-
abortSignal
|
|
109790
|
+
abortSignal,
|
|
109791
|
+
{ ffmpegStreamingTimeout: 36e5 }
|
|
108202
109792
|
);
|
|
108203
109793
|
assertNotAborted();
|
|
108204
|
-
|
|
108205
|
-
|
|
108206
|
-
const
|
|
108207
|
-
|
|
108208
|
-
|
|
108209
|
-
|
|
108210
|
-
|
|
108211
|
-
|
|
108212
|
-
|
|
108213
|
-
|
|
109794
|
+
const hdrExtractionDims = /* @__PURE__ */ new Map();
|
|
109795
|
+
const hdrVideoStartTimes = /* @__PURE__ */ new Map();
|
|
109796
|
+
for (const v of composition.videos) {
|
|
109797
|
+
if (hdrVideoIds.includes(v.id)) {
|
|
109798
|
+
hdrVideoStartTimes.set(v.id, v.start);
|
|
109799
|
+
}
|
|
109800
|
+
}
|
|
109801
|
+
const uniqueStartTimes = [...new Set(hdrVideoStartTimes.values())].sort((a, b) => a - b);
|
|
109802
|
+
for (const seekTime of uniqueStartTimes) {
|
|
109803
|
+
await domSession.page.evaluate((t) => {
|
|
109804
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109805
|
+
}, seekTime);
|
|
109806
|
+
if (domSession.onBeforeCapture) {
|
|
109807
|
+
await domSession.onBeforeCapture(domSession.page, seekTime);
|
|
109808
|
+
}
|
|
109809
|
+
const stacking = await queryElementStacking(domSession.page, nativeHdrVideoIds);
|
|
109810
|
+
for (const el of stacking) {
|
|
109811
|
+
if (el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0 && !hdrExtractionDims.has(el.id)) {
|
|
109812
|
+
hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
109813
|
+
}
|
|
109814
|
+
}
|
|
109815
|
+
}
|
|
109816
|
+
const hdrFrameDirs = /* @__PURE__ */ new Map();
|
|
109817
|
+
for (const [videoId, srcPath] of hdrVideoSrcPaths) {
|
|
109818
|
+
const video = composition.videos.find((v) => v.id === videoId);
|
|
109819
|
+
if (!video) continue;
|
|
109820
|
+
const frameDir = join15(framesDir, `hdr_${videoId}`);
|
|
109821
|
+
mkdirSync10(frameDir, { recursive: true });
|
|
109822
|
+
const duration = video.end - video.start;
|
|
109823
|
+
const dims = hdrExtractionDims.get(videoId) ?? { width, height };
|
|
109824
|
+
const ffmpegArgs = [
|
|
109825
|
+
"-ss",
|
|
109826
|
+
String(video.mediaStart),
|
|
109827
|
+
"-i",
|
|
109828
|
+
srcPath,
|
|
109829
|
+
"-t",
|
|
109830
|
+
String(duration),
|
|
109831
|
+
"-r",
|
|
109832
|
+
String(job.config.fps),
|
|
109833
|
+
"-vf",
|
|
109834
|
+
`scale=${dims.width}:${dims.height}:force_original_aspect_ratio=increase,crop=${dims.width}:${dims.height}`,
|
|
109835
|
+
"-pix_fmt",
|
|
109836
|
+
"rgb48le",
|
|
109837
|
+
"-c:v",
|
|
109838
|
+
"png",
|
|
109839
|
+
"-y",
|
|
109840
|
+
join15(frameDir, "frame_%04d.png")
|
|
109841
|
+
];
|
|
109842
|
+
const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
|
|
109843
|
+
if (!result.success) {
|
|
109844
|
+
log.warn("HDR frame pre-extraction failed; loop will fill with black", {
|
|
109845
|
+
videoId,
|
|
109846
|
+
srcPath,
|
|
109847
|
+
stderr: result.stderr.slice(-400)
|
|
109848
|
+
});
|
|
109849
|
+
}
|
|
109850
|
+
hdrFrameDirs.set(videoId, frameDir);
|
|
109851
|
+
}
|
|
109852
|
+
assertNotAborted();
|
|
109853
|
+
try {
|
|
109854
|
+
let countNonZeroAlpha2 = function(rgba) {
|
|
109855
|
+
let n = 0;
|
|
109856
|
+
for (let p = 3; p < rgba.length; p += 4) {
|
|
109857
|
+
if (rgba[p] !== 0) n++;
|
|
109858
|
+
}
|
|
109859
|
+
return n;
|
|
109860
|
+
}, countNonZeroRgb482 = function(buf) {
|
|
109861
|
+
let n = 0;
|
|
109862
|
+
for (let p = 0; p < buf.length; p += 6) {
|
|
109863
|
+
if (buf[p] !== 0 || buf[p + 1] !== 0 || buf[p + 2] !== 0) n++;
|
|
109864
|
+
}
|
|
109865
|
+
return n;
|
|
108214
109866
|
};
|
|
108215
|
-
|
|
108216
|
-
|
|
108217
|
-
|
|
108218
|
-
|
|
108219
|
-
|
|
108220
|
-
()
|
|
108221
|
-
|
|
108222
|
-
|
|
108223
|
-
|
|
108224
|
-
|
|
108225
|
-
|
|
108226
|
-
|
|
108227
|
-
|
|
108228
|
-
|
|
108229
|
-
|
|
108230
|
-
|
|
108231
|
-
|
|
108232
|
-
|
|
109867
|
+
var countNonZeroAlpha = countNonZeroAlpha2, countNonZeroRgb48 = countNonZeroRgb482;
|
|
109868
|
+
const beforeCaptureHook = domSession.onBeforeCapture;
|
|
109869
|
+
const cleanedUpVideos = /* @__PURE__ */ new Set();
|
|
109870
|
+
const hdrVideoEndTimes = /* @__PURE__ */ new Map();
|
|
109871
|
+
for (const v of composition.videos) {
|
|
109872
|
+
if (hdrFrameDirs.has(v.id)) {
|
|
109873
|
+
hdrVideoEndTimes.set(v.id, v.end);
|
|
109874
|
+
}
|
|
109875
|
+
}
|
|
109876
|
+
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
109877
|
+
const debugDumpDir = debugDumpEnabled ? join15(framesDir, "debug-composite") : null;
|
|
109878
|
+
if (debugDumpDir && !existsSync15(debugDumpDir)) {
|
|
109879
|
+
mkdirSync10(debugDumpDir, { recursive: true });
|
|
109880
|
+
}
|
|
109881
|
+
async function compositeToBuffer(canvas, time, fullStacking, elementFilter, debugFrameIndex = -1) {
|
|
109882
|
+
const filteredStacking = elementFilter ? fullStacking.filter((e) => elementFilter.has(e.id)) : fullStacking;
|
|
109883
|
+
const layers = groupIntoLayers(filteredStacking);
|
|
109884
|
+
const shouldLog = debugDumpEnabled && debugFrameIndex >= 0;
|
|
109885
|
+
if (shouldLog) {
|
|
109886
|
+
log.info("[diag] compositeToBuffer plan", {
|
|
109887
|
+
frame: debugFrameIndex,
|
|
109888
|
+
time: time.toFixed(3),
|
|
109889
|
+
filterSize: elementFilter?.size,
|
|
109890
|
+
fullStackingCount: fullStacking.length,
|
|
109891
|
+
filteredCount: filteredStacking.length,
|
|
109892
|
+
layerCount: layers.length,
|
|
109893
|
+
layers: layers.map(
|
|
109894
|
+
(l) => l.type === "hdr" ? {
|
|
109895
|
+
type: "hdr",
|
|
109896
|
+
id: l.element.id,
|
|
109897
|
+
z: l.element.zIndex,
|
|
109898
|
+
visible: l.element.visible,
|
|
109899
|
+
opacity: l.element.opacity,
|
|
109900
|
+
bounds: `${Math.round(l.element.x)},${Math.round(l.element.y)} ${Math.round(l.element.width)}x${Math.round(l.element.height)}`
|
|
109901
|
+
} : { type: "dom", ids: l.elementIds }
|
|
109902
|
+
)
|
|
109903
|
+
});
|
|
109904
|
+
}
|
|
109905
|
+
for (let layerIdx = 0; layerIdx < layers.length; layerIdx++) {
|
|
109906
|
+
const layer = layers[layerIdx];
|
|
109907
|
+
if (layer.type === "hdr") {
|
|
109908
|
+
const before2 = shouldLog ? countNonZeroRgb482(canvas) : 0;
|
|
109909
|
+
blitHdrVideoLayer(
|
|
109910
|
+
canvas,
|
|
109911
|
+
layer.element,
|
|
109912
|
+
time,
|
|
109913
|
+
job.config.fps,
|
|
109914
|
+
hdrFrameDirs,
|
|
109915
|
+
hdrVideoStartTimes,
|
|
109916
|
+
width,
|
|
109917
|
+
height,
|
|
109918
|
+
log,
|
|
109919
|
+
videoTransfers.get(layer.element.id),
|
|
109920
|
+
effectiveHdr?.transfer
|
|
108233
109921
|
);
|
|
109922
|
+
if (shouldLog) {
|
|
109923
|
+
const after2 = countNonZeroRgb482(canvas);
|
|
109924
|
+
const frameDir = hdrFrameDirs.get(layer.element.id);
|
|
109925
|
+
const startTime = hdrVideoStartTimes.get(layer.element.id) ?? 0;
|
|
109926
|
+
const localTime = time - startTime;
|
|
109927
|
+
const frameNum = Math.floor(localTime * job.config.fps) + 1;
|
|
109928
|
+
const expectedFrame = frameDir ? join15(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
|
|
109929
|
+
log.info("[diag] hdr layer blit", {
|
|
109930
|
+
frame: debugFrameIndex,
|
|
109931
|
+
layerIdx,
|
|
109932
|
+
id: layer.element.id,
|
|
109933
|
+
pixelsAdded: after2 - before2,
|
|
109934
|
+
totalNonZero: after2,
|
|
109935
|
+
startTime,
|
|
109936
|
+
localTime: localTime.toFixed(3),
|
|
109937
|
+
hdrFrameNum: frameNum,
|
|
109938
|
+
expectedFrame,
|
|
109939
|
+
expectedFrameExists: expectedFrame ? existsSync15(expectedFrame) : false
|
|
109940
|
+
});
|
|
109941
|
+
}
|
|
109942
|
+
} else {
|
|
109943
|
+
const allElementIds = fullStacking.map((e) => e.id);
|
|
109944
|
+
const layerIds = new Set(layer.elementIds);
|
|
109945
|
+
const hideIds = allElementIds.filter((id) => !layerIds.has(id));
|
|
109946
|
+
await domSession.page.evaluate((t) => {
|
|
109947
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
109948
|
+
}, time);
|
|
109949
|
+
if (beforeCaptureHook) {
|
|
109950
|
+
await beforeCaptureHook(domSession.page, time);
|
|
109951
|
+
}
|
|
109952
|
+
await applyDomLayerMask(domSession.page, layer.elementIds, hideIds);
|
|
109953
|
+
const domPng = await captureAlphaPng(domSession.page, width, height);
|
|
109954
|
+
await removeDomLayerMask(domSession.page, hideIds);
|
|
109955
|
+
try {
|
|
109956
|
+
const { data: domRgba } = decodePng(domPng);
|
|
109957
|
+
if (!effectiveHdr) {
|
|
109958
|
+
throw new Error(
|
|
109959
|
+
"Invariant violation: effectiveHdr is undefined inside HDR layer branch"
|
|
109960
|
+
);
|
|
109961
|
+
}
|
|
109962
|
+
const before2 = shouldLog ? countNonZeroRgb482(canvas) : 0;
|
|
109963
|
+
const alphaPixels = shouldLog ? countNonZeroAlpha2(domRgba) : 0;
|
|
109964
|
+
blitRgba8OverRgb48le(domRgba, canvas, width, height, effectiveHdr.transfer);
|
|
109965
|
+
if (shouldLog && debugDumpDir) {
|
|
109966
|
+
const after2 = countNonZeroRgb482(canvas);
|
|
109967
|
+
const dumpName = `frame_${String(debugFrameIndex).padStart(4, "0")}_layer_${String(layerIdx).padStart(2, "0")}_dom.png`;
|
|
109968
|
+
const dumpPath = join15(debugDumpDir, dumpName);
|
|
109969
|
+
writeFileSync4(dumpPath, domPng);
|
|
109970
|
+
log.info("[diag] dom layer blit", {
|
|
109971
|
+
frame: debugFrameIndex,
|
|
109972
|
+
layerIdx,
|
|
109973
|
+
layerIds: layer.elementIds,
|
|
109974
|
+
hideCount: hideIds.length,
|
|
109975
|
+
pngBytes: domPng.length,
|
|
109976
|
+
alphaPixels,
|
|
109977
|
+
pixelsAdded: after2 - before2,
|
|
109978
|
+
totalNonZero: after2,
|
|
109979
|
+
dumpPath
|
|
109980
|
+
});
|
|
109981
|
+
}
|
|
109982
|
+
} catch (err) {
|
|
109983
|
+
log.warn("DOM layer decode/blit failed; skipping overlay", {
|
|
109984
|
+
layerIds: layer.elementIds,
|
|
109985
|
+
error: err instanceof Error ? err.message : String(err)
|
|
109986
|
+
});
|
|
109987
|
+
}
|
|
108234
109988
|
}
|
|
108235
|
-
},
|
|
108236
|
-
onFrameBuffer,
|
|
108237
|
-
cfg
|
|
108238
|
-
);
|
|
108239
|
-
if (probeSession) {
|
|
108240
|
-
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
108241
|
-
await closeCaptureSession(probeSession);
|
|
108242
|
-
probeSession = null;
|
|
108243
|
-
}
|
|
108244
|
-
} else {
|
|
108245
|
-
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
108246
|
-
const session = probeSession ?? await createCaptureSession(
|
|
108247
|
-
fileServer.url,
|
|
108248
|
-
framesDir,
|
|
108249
|
-
captureOptions,
|
|
108250
|
-
videoInjector,
|
|
108251
|
-
cfg
|
|
108252
|
-
);
|
|
108253
|
-
if (probeSession) {
|
|
108254
|
-
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
108255
|
-
probeSession = null;
|
|
108256
|
-
}
|
|
108257
|
-
try {
|
|
108258
|
-
if (!session.isInitialized) {
|
|
108259
|
-
await initializeSession(session);
|
|
108260
109989
|
}
|
|
109990
|
+
if (shouldLog && debugDumpDir) {
|
|
109991
|
+
const finalNonZero = countNonZeroRgb482(canvas);
|
|
109992
|
+
log.info("[diag] compositeToBuffer end", {
|
|
109993
|
+
frame: debugFrameIndex,
|
|
109994
|
+
finalNonZeroPixels: finalNonZero,
|
|
109995
|
+
totalPixels: width * height,
|
|
109996
|
+
coverage: (finalNonZero / (width * height) * 100).toFixed(1) + "%"
|
|
109997
|
+
});
|
|
109998
|
+
}
|
|
109999
|
+
}
|
|
110000
|
+
const bufSize = width * height * 6;
|
|
110001
|
+
const hasTransitions = transitionRanges.length > 0;
|
|
110002
|
+
const transBufferA = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
110003
|
+
const transBufferB = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
110004
|
+
const transOutput = hasTransitions ? Buffer.alloc(bufSize) : null;
|
|
110005
|
+
const normalCanvas = Buffer.alloc(bufSize);
|
|
110006
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
108261
110007
|
assertNotAborted();
|
|
108262
|
-
|
|
108263
|
-
|
|
108264
|
-
|
|
108265
|
-
|
|
108266
|
-
|
|
108267
|
-
await
|
|
108268
|
-
|
|
108269
|
-
|
|
108270
|
-
|
|
108271
|
-
|
|
108272
|
-
|
|
110008
|
+
const time = i / job.config.fps;
|
|
110009
|
+
await domSession.page.evaluate((t) => {
|
|
110010
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
110011
|
+
}, time);
|
|
110012
|
+
if (beforeCaptureHook) {
|
|
110013
|
+
await beforeCaptureHook(domSession.page, time);
|
|
110014
|
+
}
|
|
110015
|
+
const stackingInfo = await queryElementStacking(domSession.page, nativeHdrVideoIds);
|
|
110016
|
+
const activeTransition = transitionRanges.find(
|
|
110017
|
+
(t) => i >= t.startFrame && i <= t.endFrame
|
|
110018
|
+
);
|
|
110019
|
+
if (i % 30 === 0) {
|
|
110020
|
+
const hdrEl = stackingInfo.find((e) => e.isHdr);
|
|
110021
|
+
log.debug("[Render] HDR layer composite frame", {
|
|
110022
|
+
frame: i,
|
|
110023
|
+
time: time.toFixed(2),
|
|
110024
|
+
hdrElement: hdrEl ? { z: hdrEl.zIndex, visible: hdrEl.visible, width: hdrEl.width } : null,
|
|
110025
|
+
stackingCount: stackingInfo.length,
|
|
110026
|
+
activeTransition: activeTransition?.shader
|
|
110027
|
+
});
|
|
110028
|
+
}
|
|
110029
|
+
if (activeTransition && transBufferA && transBufferB && transOutput) {
|
|
110030
|
+
const progress = activeTransition.endFrame === activeTransition.startFrame ? 1 : (i - activeTransition.startFrame) / (activeTransition.endFrame - activeTransition.startFrame);
|
|
110031
|
+
const sceneAIds = new Set(sceneElements[activeTransition.fromScene] ?? []);
|
|
110032
|
+
const sceneBIds = new Set(sceneElements[activeTransition.toScene] ?? []);
|
|
110033
|
+
transBufferA.fill(0);
|
|
110034
|
+
transBufferB.fill(0);
|
|
110035
|
+
for (const [sceneBuf, sceneIds] of [
|
|
110036
|
+
[transBufferA, sceneAIds],
|
|
110037
|
+
[transBufferB, sceneBIds]
|
|
110038
|
+
]) {
|
|
110039
|
+
await domSession.page.evaluate((t) => {
|
|
110040
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t);
|
|
110041
|
+
}, time);
|
|
110042
|
+
if (beforeCaptureHook) {
|
|
110043
|
+
await beforeCaptureHook(domSession.page, time);
|
|
110044
|
+
}
|
|
110045
|
+
for (const el of stackingInfo) {
|
|
110046
|
+
if (!el.isHdr || !sceneIds.has(el.id)) continue;
|
|
110047
|
+
blitHdrVideoLayer(
|
|
110048
|
+
sceneBuf,
|
|
110049
|
+
el,
|
|
110050
|
+
time,
|
|
110051
|
+
job.config.fps,
|
|
110052
|
+
hdrFrameDirs,
|
|
110053
|
+
hdrVideoStartTimes,
|
|
110054
|
+
width,
|
|
110055
|
+
height,
|
|
110056
|
+
log,
|
|
110057
|
+
videoTransfers.get(el.id),
|
|
110058
|
+
effectiveHdr?.transfer
|
|
110059
|
+
);
|
|
110060
|
+
}
|
|
110061
|
+
const showIds = Array.from(sceneIds);
|
|
110062
|
+
const hideIds = stackingInfo.map((e) => e.id).filter((id) => !sceneIds.has(id) || nativeHdrVideoIds.has(id));
|
|
110063
|
+
await applyDomLayerMask(domSession.page, showIds, hideIds);
|
|
110064
|
+
const domPng = await captureAlphaPng(domSession.page, width, height);
|
|
110065
|
+
await removeDomLayerMask(domSession.page, hideIds);
|
|
110066
|
+
try {
|
|
110067
|
+
const { data: domRgba } = decodePng(domPng);
|
|
110068
|
+
if (!effectiveHdr) {
|
|
110069
|
+
throw new Error(
|
|
110070
|
+
"Invariant violation: effectiveHdr is undefined inside hasHdrVideo branch"
|
|
110071
|
+
);
|
|
110072
|
+
}
|
|
110073
|
+
blitRgba8OverRgb48le(
|
|
110074
|
+
domRgba,
|
|
110075
|
+
sceneBuf,
|
|
110076
|
+
width,
|
|
110077
|
+
height,
|
|
110078
|
+
effectiveHdr.transfer
|
|
110079
|
+
);
|
|
110080
|
+
} catch (err) {
|
|
110081
|
+
log.warn("DOM layer decode/blit failed; skipping overlay for transition scene", {
|
|
110082
|
+
frameIndex: i,
|
|
110083
|
+
sceneIds: Array.from(sceneIds),
|
|
110084
|
+
error: err instanceof Error ? err.message : String(err)
|
|
110085
|
+
});
|
|
110086
|
+
}
|
|
110087
|
+
}
|
|
110088
|
+
const transitionFn = TRANSITIONS[activeTransition.shader] ?? crossfade;
|
|
110089
|
+
transitionFn(transBufferA, transBufferB, transOutput, width, height, progress);
|
|
110090
|
+
hdrEncoder.writeFrame(transOutput);
|
|
110091
|
+
} else {
|
|
110092
|
+
normalCanvas.fill(0);
|
|
110093
|
+
await compositeToBuffer(normalCanvas, time, stackingInfo, void 0, i);
|
|
110094
|
+
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
110095
|
+
const previewPath = join15(
|
|
110096
|
+
debugDumpDir,
|
|
110097
|
+
`frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`
|
|
110098
|
+
);
|
|
110099
|
+
writeFileSync4(previewPath, normalCanvas);
|
|
110100
|
+
}
|
|
110101
|
+
hdrEncoder.writeFrame(normalCanvas);
|
|
110102
|
+
}
|
|
110103
|
+
if (process.env.KEEP_TEMP !== "1") {
|
|
110104
|
+
for (const [videoId, endTime] of hdrVideoEndTimes) {
|
|
110105
|
+
if (time > endTime && !cleanedUpVideos.has(videoId)) {
|
|
110106
|
+
const stillNeeded = activeTransition && (sceneElements[activeTransition.fromScene]?.includes(videoId) || sceneElements[activeTransition.toScene]?.includes(videoId));
|
|
110107
|
+
if (!stillNeeded) {
|
|
110108
|
+
const frameDir = hdrFrameDirs.get(videoId);
|
|
110109
|
+
if (frameDir) {
|
|
110110
|
+
try {
|
|
110111
|
+
rmSync3(frameDir, { recursive: true, force: true });
|
|
110112
|
+
} catch (err) {
|
|
110113
|
+
log.warn("Failed to clean up HDR frame directory", {
|
|
110114
|
+
videoId,
|
|
110115
|
+
frameDir,
|
|
110116
|
+
error: err instanceof Error ? err.message : String(err)
|
|
110117
|
+
});
|
|
110118
|
+
}
|
|
110119
|
+
}
|
|
110120
|
+
cleanedUpVideos.add(videoId);
|
|
110121
|
+
}
|
|
110122
|
+
}
|
|
110123
|
+
}
|
|
110124
|
+
}
|
|
110125
|
+
job.framesRendered = i + 1;
|
|
110126
|
+
if ((i + 1) % 10 === 0 || i + 1 === totalFrames) {
|
|
110127
|
+
const frameProgress = (i + 1) / totalFrames;
|
|
108273
110128
|
updateJobStatus(
|
|
108274
110129
|
job,
|
|
108275
110130
|
"rendering",
|
|
108276
|
-
`
|
|
108277
|
-
Math.round(
|
|
110131
|
+
`HDR composite frame ${i + 1}/${job.totalFrames}`,
|
|
110132
|
+
Math.round(25 + frameProgress * 55),
|
|
108278
110133
|
onProgress
|
|
108279
110134
|
);
|
|
108280
110135
|
}
|
|
108281
|
-
} finally {
|
|
108282
|
-
lastBrowserConsole = session.browserConsoleBuffer;
|
|
108283
|
-
await closeCaptureSession(session);
|
|
108284
110136
|
}
|
|
110137
|
+
} finally {
|
|
110138
|
+
lastBrowserConsole = domSession.browserConsoleBuffer;
|
|
110139
|
+
await closeCaptureSession(domSession);
|
|
108285
110140
|
}
|
|
108286
|
-
const
|
|
110141
|
+
const hdrEncodeResult = await hdrEncoder.close();
|
|
108287
110142
|
assertNotAborted();
|
|
108288
|
-
if (!
|
|
108289
|
-
throw new Error(`
|
|
110143
|
+
if (!hdrEncodeResult.success) {
|
|
110144
|
+
throw new Error(`HDR encode failed: ${hdrEncodeResult.error}`);
|
|
108290
110145
|
}
|
|
108291
110146
|
perfStages.captureMs = Date.now() - stage4Start;
|
|
108292
|
-
perfStages.encodeMs =
|
|
110147
|
+
perfStages.encodeMs = hdrEncodeResult.durationMs;
|
|
108293
110148
|
} else {
|
|
108294
|
-
|
|
108295
|
-
|
|
108296
|
-
await
|
|
108297
|
-
|
|
108298
|
-
|
|
108299
|
-
|
|
108300
|
-
|
|
108301
|
-
|
|
108302
|
-
|
|
108303
|
-
|
|
108304
|
-
|
|
108305
|
-
|
|
108306
|
-
|
|
108307
|
-
|
|
110149
|
+
let streamingEncoder = null;
|
|
110150
|
+
if (enableStreamingEncode) {
|
|
110151
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
110152
|
+
videoOnlyPath,
|
|
110153
|
+
{
|
|
110154
|
+
fps: job.config.fps,
|
|
110155
|
+
width,
|
|
110156
|
+
height,
|
|
110157
|
+
codec: preset.codec,
|
|
110158
|
+
preset: preset.preset,
|
|
110159
|
+
quality: preset.quality,
|
|
110160
|
+
pixelFormat: preset.pixelFormat,
|
|
110161
|
+
useGpu: job.config.useGpu,
|
|
110162
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
110163
|
+
hdr: preset.hdr
|
|
110164
|
+
},
|
|
110165
|
+
abortSignal
|
|
110166
|
+
);
|
|
110167
|
+
assertNotAborted();
|
|
110168
|
+
}
|
|
110169
|
+
if (enableStreamingEncode && streamingEncoder) {
|
|
110170
|
+
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
110171
|
+
const currentEncoder = streamingEncoder;
|
|
110172
|
+
if (workerCount > 1) {
|
|
110173
|
+
const tasks = distributeFrames(job.totalFrames, workerCount, workDir);
|
|
110174
|
+
const onFrameBuffer = async (frameIndex, buffer) => {
|
|
110175
|
+
await reorderBuffer.waitForFrame(frameIndex);
|
|
110176
|
+
currentEncoder.writeFrame(buffer);
|
|
110177
|
+
reorderBuffer.advanceTo(frameIndex + 1);
|
|
110178
|
+
};
|
|
110179
|
+
await executeParallelCapture(
|
|
110180
|
+
fileServer.url,
|
|
110181
|
+
workDir,
|
|
110182
|
+
tasks,
|
|
110183
|
+
captureOptions,
|
|
110184
|
+
() => createVideoFrameInjector(frameLookup),
|
|
110185
|
+
abortSignal,
|
|
110186
|
+
(progress) => {
|
|
110187
|
+
job.framesRendered = progress.capturedFrames;
|
|
110188
|
+
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
110189
|
+
const progressPct = 25 + frameProgress * 55;
|
|
110190
|
+
if (progress.capturedFrames % 30 === 0 || progress.capturedFrames === progress.totalFrames) {
|
|
110191
|
+
updateJobStatus(
|
|
110192
|
+
job,
|
|
110193
|
+
"rendering",
|
|
110194
|
+
`Streaming frame ${progress.capturedFrames}/${progress.totalFrames} (${workerCount} workers)`,
|
|
110195
|
+
Math.round(progressPct),
|
|
110196
|
+
onProgress
|
|
110197
|
+
);
|
|
110198
|
+
}
|
|
110199
|
+
},
|
|
110200
|
+
onFrameBuffer,
|
|
110201
|
+
cfg
|
|
110202
|
+
);
|
|
110203
|
+
if (probeSession) {
|
|
110204
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
110205
|
+
await closeCaptureSession(probeSession);
|
|
110206
|
+
probeSession = null;
|
|
110207
|
+
}
|
|
110208
|
+
} else {
|
|
110209
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
110210
|
+
const session = probeSession ?? await createCaptureSession(
|
|
110211
|
+
fileServer.url,
|
|
110212
|
+
framesDir,
|
|
110213
|
+
captureOptions,
|
|
110214
|
+
videoInjector,
|
|
110215
|
+
cfg
|
|
110216
|
+
);
|
|
110217
|
+
if (probeSession) {
|
|
110218
|
+
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
110219
|
+
probeSession = null;
|
|
110220
|
+
}
|
|
110221
|
+
try {
|
|
110222
|
+
if (!session.isInitialized) {
|
|
110223
|
+
await initializeSession(session);
|
|
110224
|
+
}
|
|
110225
|
+
assertNotAborted();
|
|
110226
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110227
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
110228
|
+
assertNotAborted();
|
|
110229
|
+
const time = i / job.config.fps;
|
|
110230
|
+
const { buffer } = await captureFrameToBuffer(session, i, time);
|
|
110231
|
+
await reorderBuffer.waitForFrame(i);
|
|
110232
|
+
currentEncoder.writeFrame(buffer);
|
|
110233
|
+
reorderBuffer.advanceTo(i + 1);
|
|
110234
|
+
job.framesRendered = i + 1;
|
|
110235
|
+
const frameProgress = (i + 1) / totalFrames;
|
|
110236
|
+
const progress = 25 + frameProgress * 55;
|
|
108308
110237
|
updateJobStatus(
|
|
108309
110238
|
job,
|
|
108310
110239
|
"rendering",
|
|
108311
|
-
`
|
|
108312
|
-
Math.round(
|
|
110240
|
+
`Streaming frame ${i + 1}/${job.totalFrames}`,
|
|
110241
|
+
Math.round(progress),
|
|
108313
110242
|
onProgress
|
|
108314
110243
|
);
|
|
108315
110244
|
}
|
|
108316
|
-
}
|
|
108317
|
-
|
|
108318
|
-
|
|
108319
|
-
|
|
108320
|
-
await mergeWorkerFrames(workDir, tasks, framesDir);
|
|
108321
|
-
if (probeSession) {
|
|
108322
|
-
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
108323
|
-
await closeCaptureSession(probeSession);
|
|
108324
|
-
probeSession = null;
|
|
110245
|
+
} finally {
|
|
110246
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110247
|
+
await closeCaptureSession(session);
|
|
110248
|
+
}
|
|
108325
110249
|
}
|
|
108326
|
-
|
|
108327
|
-
|
|
108328
|
-
|
|
108329
|
-
|
|
108330
|
-
framesDir,
|
|
108331
|
-
captureOptions,
|
|
108332
|
-
videoInjector,
|
|
108333
|
-
cfg
|
|
108334
|
-
);
|
|
108335
|
-
if (probeSession) {
|
|
108336
|
-
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
108337
|
-
probeSession = null;
|
|
110250
|
+
const encodeResult = await currentEncoder.close();
|
|
110251
|
+
assertNotAborted();
|
|
110252
|
+
if (!encodeResult.success) {
|
|
110253
|
+
throw new Error(`Streaming encode failed: ${encodeResult.error}`);
|
|
108338
110254
|
}
|
|
108339
|
-
|
|
108340
|
-
|
|
108341
|
-
|
|
110255
|
+
perfStages.captureMs = Date.now() - stage4Start;
|
|
110256
|
+
perfStages.encodeMs = encodeResult.durationMs;
|
|
110257
|
+
} else {
|
|
110258
|
+
if (workerCount > 1) {
|
|
110259
|
+
const tasks = distributeFrames(job.totalFrames, workerCount, workDir);
|
|
110260
|
+
await executeParallelCapture(
|
|
110261
|
+
fileServer.url,
|
|
110262
|
+
workDir,
|
|
110263
|
+
tasks,
|
|
110264
|
+
captureOptions,
|
|
110265
|
+
() => createVideoFrameInjector(frameLookup),
|
|
110266
|
+
abortSignal,
|
|
110267
|
+
(progress) => {
|
|
110268
|
+
job.framesRendered = progress.capturedFrames;
|
|
110269
|
+
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
110270
|
+
const progressPct = 25 + frameProgress * 45;
|
|
110271
|
+
if (progress.capturedFrames % 30 === 0 || progress.capturedFrames === progress.totalFrames) {
|
|
110272
|
+
updateJobStatus(
|
|
110273
|
+
job,
|
|
110274
|
+
"rendering",
|
|
110275
|
+
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${workerCount} workers)`,
|
|
110276
|
+
Math.round(progressPct),
|
|
110277
|
+
onProgress
|
|
110278
|
+
);
|
|
110279
|
+
}
|
|
110280
|
+
},
|
|
110281
|
+
void 0,
|
|
110282
|
+
cfg
|
|
110283
|
+
);
|
|
110284
|
+
await mergeWorkerFrames(workDir, tasks, framesDir);
|
|
110285
|
+
if (probeSession) {
|
|
110286
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
110287
|
+
await closeCaptureSession(probeSession);
|
|
110288
|
+
probeSession = null;
|
|
108342
110289
|
}
|
|
108343
|
-
|
|
108344
|
-
|
|
108345
|
-
|
|
108346
|
-
|
|
108347
|
-
|
|
108348
|
-
|
|
108349
|
-
|
|
108350
|
-
|
|
108351
|
-
|
|
108352
|
-
|
|
108353
|
-
|
|
108354
|
-
|
|
108355
|
-
`Capturing frame ${i + 1}/${job.totalFrames}`,
|
|
108356
|
-
Math.round(progress),
|
|
108357
|
-
onProgress
|
|
108358
|
-
);
|
|
110290
|
+
} else {
|
|
110291
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
110292
|
+
const session = probeSession ?? await createCaptureSession(
|
|
110293
|
+
fileServer.url,
|
|
110294
|
+
framesDir,
|
|
110295
|
+
captureOptions,
|
|
110296
|
+
videoInjector,
|
|
110297
|
+
cfg
|
|
110298
|
+
);
|
|
110299
|
+
if (probeSession) {
|
|
110300
|
+
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
110301
|
+
probeSession = null;
|
|
108359
110302
|
}
|
|
108360
|
-
|
|
108361
|
-
|
|
108362
|
-
|
|
110303
|
+
try {
|
|
110304
|
+
if (!session.isInitialized) {
|
|
110305
|
+
await initializeSession(session);
|
|
110306
|
+
}
|
|
110307
|
+
assertNotAborted();
|
|
110308
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110309
|
+
for (let i = 0; i < job.totalFrames; i++) {
|
|
110310
|
+
assertNotAborted();
|
|
110311
|
+
const time = i / job.config.fps;
|
|
110312
|
+
await captureFrame(session, i, time);
|
|
110313
|
+
job.framesRendered = i + 1;
|
|
110314
|
+
const frameProgress = (i + 1) / job.totalFrames;
|
|
110315
|
+
const progress = 25 + frameProgress * 45;
|
|
110316
|
+
updateJobStatus(
|
|
110317
|
+
job,
|
|
110318
|
+
"rendering",
|
|
110319
|
+
`Capturing frame ${i + 1}/${job.totalFrames}`,
|
|
110320
|
+
Math.round(progress),
|
|
110321
|
+
onProgress
|
|
110322
|
+
);
|
|
110323
|
+
}
|
|
110324
|
+
} finally {
|
|
110325
|
+
lastBrowserConsole = session.browserConsoleBuffer;
|
|
110326
|
+
await closeCaptureSession(session);
|
|
110327
|
+
}
|
|
110328
|
+
}
|
|
110329
|
+
perfStages.captureMs = Date.now() - stage4Start;
|
|
110330
|
+
const stage5Start = Date.now();
|
|
110331
|
+
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
|
110332
|
+
const frameExt = needsAlpha ? "png" : "jpg";
|
|
110333
|
+
const framePattern = `frame_%06d.${frameExt}`;
|
|
110334
|
+
const encoderOpts = {
|
|
110335
|
+
fps: job.config.fps,
|
|
110336
|
+
width,
|
|
110337
|
+
height,
|
|
110338
|
+
codec: preset.codec,
|
|
110339
|
+
preset: preset.preset,
|
|
110340
|
+
quality: preset.quality,
|
|
110341
|
+
pixelFormat: preset.pixelFormat,
|
|
110342
|
+
useGpu: job.config.useGpu,
|
|
110343
|
+
hdr: preset.hdr
|
|
110344
|
+
};
|
|
110345
|
+
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
110346
|
+
framesDir,
|
|
110347
|
+
framePattern,
|
|
110348
|
+
videoOnlyPath,
|
|
110349
|
+
encoderOpts,
|
|
110350
|
+
chunkedEncodeSize,
|
|
110351
|
+
abortSignal
|
|
110352
|
+
) : await encodeFramesFromDir(
|
|
110353
|
+
framesDir,
|
|
110354
|
+
framePattern,
|
|
110355
|
+
videoOnlyPath,
|
|
110356
|
+
encoderOpts,
|
|
110357
|
+
abortSignal
|
|
110358
|
+
);
|
|
110359
|
+
assertNotAborted();
|
|
110360
|
+
if (!encodeResult.success) {
|
|
110361
|
+
throw new Error(`Encoding failed: ${encodeResult.error}`);
|
|
108363
110362
|
}
|
|
110363
|
+
perfStages.encodeMs = Date.now() - stage5Start;
|
|
108364
110364
|
}
|
|
108365
|
-
perfStages.captureMs = Date.now() - stage4Start;
|
|
108366
|
-
const stage5Start = Date.now();
|
|
108367
|
-
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
|
108368
|
-
const frameExt = needsAlpha ? "png" : "jpg";
|
|
108369
|
-
const framePattern = `frame_%06d.${frameExt}`;
|
|
108370
|
-
const encoderOpts = baseEncoderOpts;
|
|
108371
|
-
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
108372
|
-
framesDir,
|
|
108373
|
-
framePattern,
|
|
108374
|
-
videoOnlyPath,
|
|
108375
|
-
encoderOpts,
|
|
108376
|
-
chunkedEncodeSize,
|
|
108377
|
-
abortSignal
|
|
108378
|
-
) : await encodeFramesFromDir(
|
|
108379
|
-
framesDir,
|
|
108380
|
-
framePattern,
|
|
108381
|
-
videoOnlyPath,
|
|
108382
|
-
encoderOpts,
|
|
108383
|
-
abortSignal
|
|
108384
|
-
);
|
|
108385
|
-
assertNotAborted();
|
|
108386
|
-
if (!encodeResult.success) {
|
|
108387
|
-
throw new Error(`Encoding failed: ${encodeResult.error}`);
|
|
108388
|
-
}
|
|
108389
|
-
perfStages.encodeMs = Date.now() - stage5Start;
|
|
108390
110365
|
}
|
|
108391
110366
|
if (probeSession !== null) {
|
|
108392
110367
|
const remainingProbeSession = probeSession;
|
|
@@ -108430,12 +110405,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108430
110405
|
chunkedEncode: enableChunkedEncode,
|
|
108431
110406
|
chunkSizeFrames: enableChunkedEncode ? chunkedEncodeSize : null,
|
|
108432
110407
|
compositionDurationSeconds: composition.duration,
|
|
108433
|
-
totalFrames
|
|
110408
|
+
totalFrames,
|
|
108434
110409
|
resolution: { width, height },
|
|
108435
110410
|
videoCount: composition.videos.length,
|
|
108436
110411
|
audioCount: composition.audios.length,
|
|
108437
110412
|
stages: perfStages,
|
|
108438
|
-
captureAvgMs:
|
|
110413
|
+
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0
|
|
108439
110414
|
};
|
|
108440
110415
|
job.perfSummary = perfSummary;
|
|
108441
110416
|
if (job.config.debug) {
|
|
@@ -108453,6 +110428,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
108453
110428
|
const debugOutput = join15(workDir, `output${videoExt}`);
|
|
108454
110429
|
copyFileSync2(outputPath, debugOutput);
|
|
108455
110430
|
}
|
|
110431
|
+
} else if (process.env.KEEP_TEMP === "1") {
|
|
110432
|
+
log.info("KEEP_TEMP=1 \u2014 leaving workDir on disk for inspection", { workDir });
|
|
108456
110433
|
} else {
|
|
108457
110434
|
await safeCleanup(
|
|
108458
110435
|
"remove workDir",
|