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