asciify-engine 1.0.68 → 1.0.70
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 +72 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +72 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1160,6 +1160,25 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
1160
1160
|
}
|
|
1161
1161
|
|
|
1162
1162
|
// src/core/simple-api.ts
|
|
1163
|
+
function getSourceDims(el) {
|
|
1164
|
+
if (el instanceof HTMLVideoElement) return { w: el.videoWidth, h: el.videoHeight };
|
|
1165
|
+
if (el instanceof HTMLImageElement) return { w: el.naturalWidth || el.width, h: el.naturalHeight || el.height };
|
|
1166
|
+
return { w: el.width, h: el.height };
|
|
1167
|
+
}
|
|
1168
|
+
function makeHiResCanvas(srcW, srcH, displayW, displayH) {
|
|
1169
|
+
if (srcW <= displayW && srcH <= displayH) return null;
|
|
1170
|
+
const MAX = 2048;
|
|
1171
|
+
const scale = Math.min(1, MAX / Math.max(srcW, srcH));
|
|
1172
|
+
const offW = Math.round(srcW * scale);
|
|
1173
|
+
const offH = Math.round(srcH * scale);
|
|
1174
|
+
if (offW <= displayW && offH <= displayH) return null;
|
|
1175
|
+
const offCanvas = document.createElement("canvas");
|
|
1176
|
+
offCanvas.width = offW;
|
|
1177
|
+
offCanvas.height = offH;
|
|
1178
|
+
const offCtx = offCanvas.getContext("2d");
|
|
1179
|
+
if (!offCtx) return null;
|
|
1180
|
+
return { offCanvas, offCtx, offW, offH };
|
|
1181
|
+
}
|
|
1163
1182
|
function sizeCanvasToContainer(canvas, container, aspect, srcW, srcH) {
|
|
1164
1183
|
const { width, height } = container.getBoundingClientRect();
|
|
1165
1184
|
if (!width || !height) return;
|
|
@@ -1186,7 +1205,7 @@ function sizeCanvasToContainer(canvas, container, aspect, srcW, srcH) {
|
|
|
1186
1205
|
canvas.style.width = cssW + "px";
|
|
1187
1206
|
canvas.style.height = cssH + "px";
|
|
1188
1207
|
}
|
|
1189
|
-
async function asciify(source, canvas, { fontSize
|
|
1208
|
+
async function asciify(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
|
|
1190
1209
|
let el;
|
|
1191
1210
|
if (typeof source === "string") {
|
|
1192
1211
|
const img = new Image();
|
|
@@ -1207,15 +1226,26 @@ async function asciify(source, canvas, { fontSize = 10, artStyle = "classic", op
|
|
|
1207
1226
|
el = source;
|
|
1208
1227
|
}
|
|
1209
1228
|
const preset = ART_STYLE_PRESETS[artStyle];
|
|
1210
|
-
const
|
|
1229
|
+
const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
|
|
1230
|
+
const merged = { ...DEFAULT_OPTIONS, ...preset, ...options, fontSize: resolvedFontSize };
|
|
1211
1231
|
const ctx = canvas.getContext("2d");
|
|
1212
1232
|
if (!ctx) throw new Error("Could not get 2d context from canvas");
|
|
1213
|
-
const {
|
|
1214
|
-
|
|
1233
|
+
const { w: srcW, h: srcH } = getSourceDims(el);
|
|
1234
|
+
const hires = makeHiResCanvas(srcW, srcH, canvas.width, canvas.height);
|
|
1235
|
+
if (hires) {
|
|
1236
|
+
const { frame } = imageToAsciiFrame(el, merged, hires.offW, hires.offH);
|
|
1237
|
+
renderFrameToCanvas(hires.offCtx, frame, merged, hires.offW, hires.offH);
|
|
1238
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1239
|
+
ctx.drawImage(hires.offCanvas, 0, 0, canvas.width, canvas.height);
|
|
1240
|
+
} else {
|
|
1241
|
+
const { frame } = imageToAsciiFrame(el, merged, canvas.width, canvas.height);
|
|
1242
|
+
renderFrameToCanvas(ctx, frame, merged, canvas.width, canvas.height);
|
|
1243
|
+
}
|
|
1215
1244
|
}
|
|
1216
|
-
async function asciifyGif(source, canvas, { fontSize
|
|
1245
|
+
async function asciifyGif(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
|
|
1217
1246
|
const buffer = typeof source === "string" ? await fetch(source).then((r) => r.arrayBuffer()) : source;
|
|
1218
|
-
const
|
|
1247
|
+
const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
|
|
1248
|
+
const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize: resolvedFontSize };
|
|
1219
1249
|
const ctx = canvas.getContext("2d");
|
|
1220
1250
|
if (!ctx) throw new Error("Could not get 2d context from canvas");
|
|
1221
1251
|
const { frames, fps } = await gifToAsciiFrames(buffer, merged, canvas.width, canvas.height);
|
|
@@ -1239,10 +1269,11 @@ async function asciifyGif(source, canvas, { fontSize = 10, artStyle = "classic",
|
|
|
1239
1269
|
cancelAnimationFrame(animId);
|
|
1240
1270
|
};
|
|
1241
1271
|
}
|
|
1242
|
-
async function asciifyVideo(source, canvas, { fontSize
|
|
1272
|
+
async function asciifyVideo(source, canvas, { fontSize, artStyle = "classic", options = {}, fitTo, preExtract = false, trim, onReady, onFrame } = {}) {
|
|
1243
1273
|
const trimStart = trim?.start ?? 0;
|
|
1244
1274
|
const trimEnd = trim?.end;
|
|
1245
|
-
const
|
|
1275
|
+
const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
|
|
1276
|
+
const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize: resolvedFontSize };
|
|
1246
1277
|
const ctx = canvas.getContext("2d");
|
|
1247
1278
|
if (!ctx) throw new Error("asciifyVideo: could not get 2d context from canvas.");
|
|
1248
1279
|
const container = typeof fitTo === "string" ? document.querySelector(fitTo) : fitTo instanceof HTMLElement ? fitTo : null;
|
|
@@ -1262,15 +1293,24 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1262
1293
|
video2 = source;
|
|
1263
1294
|
}
|
|
1264
1295
|
if (container) sizeCanvasToContainer(canvas, container, video2.videoWidth / video2.videoHeight, video2.videoWidth, video2.videoHeight);
|
|
1296
|
+
const hires2 = makeHiResCanvas(video2.videoWidth, video2.videoHeight, canvas.width, canvas.height);
|
|
1297
|
+
const renderW = hires2 ? hires2.offW : canvas.width;
|
|
1298
|
+
const renderH = hires2 ? hires2.offH : canvas.height;
|
|
1265
1299
|
const maxDur = trimEnd !== void 0 ? trimEnd - trimStart : 10;
|
|
1266
|
-
const { frames, fps } = await videoToAsciiFrames(video2, merged,
|
|
1300
|
+
const { frames, fps } = await videoToAsciiFrames(video2, merged, renderW, renderH, void 0, maxDur, void 0, trimStart);
|
|
1267
1301
|
let cancelled2 = false, animId2, i = 0, last = performance.now();
|
|
1268
1302
|
let firstFrame2 = true;
|
|
1269
1303
|
const interval = 1e3 / fps;
|
|
1270
1304
|
const tick2 = (now) => {
|
|
1271
1305
|
if (cancelled2) return;
|
|
1272
1306
|
if (now - last >= interval) {
|
|
1273
|
-
|
|
1307
|
+
if (hires2) {
|
|
1308
|
+
renderFrameToCanvas(hires2.offCtx, frames[i], merged, hires2.offW, hires2.offH);
|
|
1309
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1310
|
+
ctx.drawImage(hires2.offCanvas, 0, 0, canvas.width, canvas.height);
|
|
1311
|
+
} else {
|
|
1312
|
+
renderFrameToCanvas(ctx, frames[i], merged, canvas.width, canvas.height);
|
|
1313
|
+
}
|
|
1274
1314
|
i = (i + 1) % frames.length;
|
|
1275
1315
|
last = now;
|
|
1276
1316
|
if (firstFrame2) {
|
|
@@ -1348,6 +1388,7 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1348
1388
|
ro = new ResizeObserver(() => sizeCanvasToContainer(canvas, container, aspect, vw, vh));
|
|
1349
1389
|
ro.observe(container);
|
|
1350
1390
|
}
|
|
1391
|
+
const hires = makeHiResCanvas(video.videoWidth, video.videoHeight, canvas.width, canvas.height);
|
|
1351
1392
|
let cancelled = false;
|
|
1352
1393
|
let animId;
|
|
1353
1394
|
let firstFrame = true;
|
|
@@ -1357,14 +1398,28 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
|
|
|
1357
1398
|
if (video.readyState < 2 || canvas.width === 0 || canvas.height === 0) return;
|
|
1358
1399
|
if (trimStart > 0 && video.currentTime < trimStart) return;
|
|
1359
1400
|
if (trimEnd !== void 0 && video.currentTime >= trimEnd) return;
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1401
|
+
if (hires) {
|
|
1402
|
+
const { frame } = imageToAsciiFrame(video, merged, hires.offW, hires.offH);
|
|
1403
|
+
if (frame.length > 0) {
|
|
1404
|
+
renderFrameToCanvas(hires.offCtx, frame, merged, hires.offW, hires.offH, 0, null);
|
|
1405
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1406
|
+
ctx.drawImage(hires.offCanvas, 0, 0, canvas.width, canvas.height);
|
|
1407
|
+
if (firstFrame) {
|
|
1408
|
+
firstFrame = false;
|
|
1409
|
+
onReady?.(video);
|
|
1410
|
+
}
|
|
1411
|
+
onFrame?.();
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1414
|
+
const { frame } = imageToAsciiFrame(video, merged, canvas.width, canvas.height);
|
|
1415
|
+
if (frame.length > 0) {
|
|
1416
|
+
renderFrameToCanvas(ctx, frame, merged, canvas.width, canvas.height, 0, null);
|
|
1417
|
+
if (firstFrame) {
|
|
1418
|
+
firstFrame = false;
|
|
1419
|
+
onReady?.(video);
|
|
1420
|
+
}
|
|
1421
|
+
onFrame?.();
|
|
1366
1422
|
}
|
|
1367
|
-
onFrame?.();
|
|
1368
1423
|
}
|
|
1369
1424
|
};
|
|
1370
1425
|
animId = requestAnimationFrame(tick);
|