asciify-engine 1.0.49 → 1.0.51
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.cjs +34 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +34 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -615,6 +615,10 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
// src/core/renderer.ts
|
|
618
|
+
function resolveInvert(invert) {
|
|
619
|
+
if (invert !== "auto") return invert;
|
|
620
|
+
return typeof window !== "undefined" && !window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
621
|
+
}
|
|
618
622
|
function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
|
|
619
623
|
const srcWidth = source instanceof HTMLVideoElement ? source.videoWidth : source.width;
|
|
620
624
|
const srcHeight = source instanceof HTMLVideoElement ? source.videoHeight : source.height;
|
|
@@ -648,6 +652,7 @@ function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
|
|
|
648
652
|
normRange = hi > lo ? hi - lo : 255;
|
|
649
653
|
}
|
|
650
654
|
const frame = [];
|
|
655
|
+
const invertVal = resolveInvert(options.invert);
|
|
651
656
|
const ck = options.chromaKey;
|
|
652
657
|
const ckEnabled = ck != null && ck !== false;
|
|
653
658
|
const ckHeuristicGreen = ck === true;
|
|
@@ -687,22 +692,22 @@ function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
|
|
|
687
692
|
const lum = options.normalize ? (rawLum - normMin) / normRange * 255 : rawLum;
|
|
688
693
|
const adjustedLum = adjustLuminance(lum, options.brightness, options.contrast);
|
|
689
694
|
const ditheredLum = applyDither(adjustedLum, x, y, options.ditherStrength);
|
|
690
|
-
const char = options.customText ? customTextToChar(ditheredLum, options.customText, x, y, cols,
|
|
695
|
+
const char = options.customText ? customTextToChar(ditheredLum, options.customText, x, y, cols, invertVal) : luminanceToChar(ditheredLum, options.charset, invertVal);
|
|
691
696
|
row.push({ char, r, g, b, a });
|
|
692
697
|
}
|
|
693
698
|
frame.push(row);
|
|
694
699
|
}
|
|
695
700
|
return { frame, cols, rows };
|
|
696
701
|
}
|
|
697
|
-
async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress) {
|
|
698
|
-
const duration = Math.min(video.duration, maxDuration);
|
|
702
|
+
async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress, startTime = 0) {
|
|
703
|
+
const duration = Math.min(video.duration - startTime, maxDuration);
|
|
699
704
|
const totalFrames = Math.ceil(duration * targetFps);
|
|
700
705
|
const frames = [];
|
|
701
706
|
let cols = 0;
|
|
702
707
|
let rows = 0;
|
|
703
708
|
for (let i = 0; i < totalFrames; i++) {
|
|
704
|
-
const time = i / targetFps;
|
|
705
|
-
if (time > duration) break;
|
|
709
|
+
const time = startTime + i / targetFps;
|
|
710
|
+
if (time > startTime + duration) break;
|
|
706
711
|
video.currentTime = time;
|
|
707
712
|
await new Promise((resolve) => {
|
|
708
713
|
const handler = () => {
|
|
@@ -846,7 +851,7 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
846
851
|
const hoverStrength = options.hoverStrength;
|
|
847
852
|
const hoverEffect = options.hoverEffect;
|
|
848
853
|
const hoverRadiusFactor = effectiveHoverRadius;
|
|
849
|
-
const isInverted = options.invert;
|
|
854
|
+
const isInverted = resolveInvert(options.invert);
|
|
850
855
|
const colorMode = options.colorMode;
|
|
851
856
|
const TWO_PI = Math.PI * 2;
|
|
852
857
|
const invCols = 1 / cols;
|
|
@@ -1089,7 +1094,9 @@ async function asciifyGif(source, canvas, { fontSize = 10, artStyle = "classic",
|
|
|
1089
1094
|
cancelAnimationFrame(animId);
|
|
1090
1095
|
};
|
|
1091
1096
|
}
|
|
1092
|
-
async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic", options = {}, fitTo, preExtract = false, onReady, onFrame } = {}) {
|
|
1097
|
+
async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic", options = {}, fitTo, preExtract = false, trim, onReady, onFrame } = {}) {
|
|
1098
|
+
const trimStart = trim?.start ?? 0;
|
|
1099
|
+
const trimEnd = trim?.end;
|
|
1093
1100
|
const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize };
|
|
1094
1101
|
const ctx = canvas.getContext("2d");
|
|
1095
1102
|
if (!ctx) throw new Error("asciifyVideo: could not get 2d context from canvas.");
|
|
@@ -1110,8 +1117,8 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1110
1117
|
video2 = source;
|
|
1111
1118
|
}
|
|
1112
1119
|
if (container) sizeCanvasToContainer(canvas, container, video2.videoWidth / video2.videoHeight);
|
|
1113
|
-
|
|
1114
|
-
const { frames, fps } = await videoToAsciiFrames(video2, merged, canvas.width, canvas.height);
|
|
1120
|
+
const maxDur = trimEnd !== void 0 ? trimEnd - trimStart : 10;
|
|
1121
|
+
const { frames, fps } = await videoToAsciiFrames(video2, merged, canvas.width, canvas.height, void 0, maxDur, void 0, trimStart);
|
|
1115
1122
|
let cancelled2 = false, animId2, i = 0, last = performance.now();
|
|
1116
1123
|
let firstFrame2 = true;
|
|
1117
1124
|
const interval = 1e3 / fps;
|
|
@@ -1167,6 +1174,23 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1167
1174
|
if (video.paused) await video.play().catch(() => {
|
|
1168
1175
|
});
|
|
1169
1176
|
}
|
|
1177
|
+
if (trimStart > 0) {
|
|
1178
|
+
video.currentTime = trimStart;
|
|
1179
|
+
await new Promise((resolve) => {
|
|
1180
|
+
const h = () => {
|
|
1181
|
+
video.removeEventListener("seeked", h);
|
|
1182
|
+
resolve();
|
|
1183
|
+
};
|
|
1184
|
+
video.addEventListener("seeked", h);
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
let timeupdateHandler = null;
|
|
1188
|
+
if (trimEnd !== void 0) {
|
|
1189
|
+
timeupdateHandler = () => {
|
|
1190
|
+
if (video.currentTime >= trimEnd) video.currentTime = trimStart;
|
|
1191
|
+
};
|
|
1192
|
+
video.addEventListener("timeupdate", timeupdateHandler);
|
|
1193
|
+
}
|
|
1170
1194
|
let ro = null;
|
|
1171
1195
|
if (container) {
|
|
1172
1196
|
const aspect = video.videoWidth / video.videoHeight;
|
|
@@ -1196,6 +1220,7 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1196
1220
|
cancelled = true;
|
|
1197
1221
|
cancelAnimationFrame(animId);
|
|
1198
1222
|
ro?.disconnect();
|
|
1223
|
+
if (timeupdateHandler) video.removeEventListener("timeupdate", timeupdateHandler);
|
|
1199
1224
|
if (ownedVideo) {
|
|
1200
1225
|
video.pause();
|
|
1201
1226
|
video.src = "";
|