feat: improve RenPy conversion and support MIST runtime
This commit is contained in:
1 parent
f7f7f5b1b3
commit
f9eb5c1212
44 files changed
+2822
-174
No files matched your search
@@ -0,0 +1,54 @@
|
||||
/** Ren'Py uses the mask movie's red channel as opacity, not its alpha channel. */
|
||||
export function applyMovieMask(picture: Uint8ClampedArray, mask: Uint8ClampedArray) {
|
||||
if (picture.length !== mask.length) throw new Error("Размеры кадров маски не совпадают");
|
||||
for (let i = 3; i < picture.length; i += 4) picture[i] = Math.round(picture[i] * mask[i - 3] / 255);
|
||||
}
|
||||
|
||||
export function movieDisplayable(source: string, mask: string | null, loop = true): HTMLElement {
|
||||
const video = document.createElement("video");
|
||||
video.src = source;
|
||||
video.autoplay = true;
|
||||
video.loop = loop;
|
||||
video.muted = true;
|
||||
video.playsInline = true;
|
||||
if (!mask) return video;
|
||||
const alpha = document.createElement("video");
|
||||
alpha.src = mask;
|
||||
alpha.loop = loop;
|
||||
alpha.muted = true;
|
||||
alpha.playsInline = true;
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.className = "pg-masked-movie";
|
||||
const maskCanvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
||||
const maskCtx = maskCanvas.getContext("2d", { willReadFrequently: true });
|
||||
if (!ctx || !maskCtx) throw new Error("Не удалось создать поверхность для видео с маской");
|
||||
let last = -1;
|
||||
let failed = false;
|
||||
const draw = () => {
|
||||
if (failed) return;
|
||||
if (!canvas.isConnected) { video.pause(); alpha.pause(); return; }
|
||||
if (video.readyState >= 2 && alpha.readyState >= 2 && video.videoWidth && video.currentTime !== last) {
|
||||
if (Math.abs(alpha.currentTime - video.currentTime) > 0.06) alpha.currentTime = video.currentTime;
|
||||
const w = video.videoWidth, h = video.videoHeight;
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = maskCanvas.width = w;
|
||||
canvas.height = maskCanvas.height = h;
|
||||
}
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.drawImage(video, 0, 0, w, h);
|
||||
maskCtx.drawImage(alpha, 0, 0, w, h);
|
||||
const pixels = ctx.getImageData(0, 0, w, h);
|
||||
applyMovieMask(pixels.data, maskCtx.getImageData(0, 0, w, h).data);
|
||||
ctx.putImageData(pixels, 0, 0);
|
||||
last = video.currentTime;
|
||||
}
|
||||
if (video.ended && !loop) { video.pause(); alpha.pause(); return; }
|
||||
requestAnimationFrame(draw);
|
||||
};
|
||||
video.onerror = alpha.onerror = () => { failed = true; canvas.dataset.error = "Не удалось загрузить видео или его маску"; video.pause(); alpha.pause(); };
|
||||
void video.play().catch(() => {});
|
||||
void alpha.play().catch(() => {});
|
||||
requestAnimationFrame(draw);
|
||||
return canvas;
|
||||
}
|
||||
Reference in new issue
Block a user