asciify-engine 1.0.69 → 1.0.71

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 CHANGED
@@ -1162,9 +1162,19 @@ 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 computeRenderDims(srcW, srcH) {
1171
+ const MAX = 2048;
1172
+ const scale = Math.min(1, MAX / Math.max(srcW, srcH));
1173
+ return { renderW: Math.round(srcW * scale), renderH: Math.round(srcH * scale) };
1174
+ }
1165
1175
  function sizeCanvasToContainer(canvas, container, aspect, srcW, srcH) {
1166
1176
  const { width, height } = container.getBoundingClientRect();
1167
- if (!width || !height) return;
1177
+ if (!width || !height) return { renderW: 0, renderH: 0, dpr: 1 };
1168
1178
  let cssW = width, cssH = cssW / aspect;
1169
1179
  if (cssH > height) {
1170
1180
  cssH = height;
@@ -1172,21 +1182,21 @@ function sizeCanvasToContainer(canvas, container, aspect, srcW, srcH) {
1172
1182
  }
1173
1183
  cssW = Math.round(cssW);
1174
1184
  cssH = Math.round(cssH);
1175
- const MAX_BUF = 2048;
1176
- let bufW, bufH;
1177
- if (srcW && srcH && (srcW > cssW || srcH > cssH)) {
1178
- const scale = Math.min(1, MAX_BUF / Math.max(srcW, srcH));
1179
- bufW = Math.round(srcW * scale);
1180
- bufH = Math.round(srcH * scale);
1185
+ let renderW, renderH;
1186
+ if (srcW && srcH) {
1187
+ ({ renderW, renderH } = computeRenderDims(srcW, srcH));
1181
1188
  } else {
1182
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1183
- bufW = Math.round(cssW * dpr);
1184
- bufH = Math.round(cssH * dpr);
1185
- }
1186
- canvas.width = bufW;
1187
- canvas.height = bufH;
1189
+ renderW = cssW;
1190
+ renderH = cssH;
1191
+ }
1192
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1193
+ const MAX_PX = 8e6;
1194
+ const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1195
+ canvas.width = Math.round(renderW * cappedDpr);
1196
+ canvas.height = Math.round(renderH * cappedDpr);
1188
1197
  canvas.style.width = cssW + "px";
1189
1198
  canvas.style.height = cssH + "px";
1199
+ return { renderW, renderH, dpr: cappedDpr };
1190
1200
  }
1191
1201
  async function asciify(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
1192
1202
  let el;
@@ -1213,8 +1223,20 @@ async function asciify(source, canvas, { fontSize, artStyle = "classic", options
1213
1223
  const merged = { ...DEFAULT_OPTIONS, ...preset, ...options, fontSize: resolvedFontSize };
1214
1224
  const ctx = canvas.getContext("2d");
1215
1225
  if (!ctx) throw new Error("Could not get 2d context from canvas");
1216
- const { frame } = imageToAsciiFrame(el, merged, canvas.width, canvas.height);
1217
- renderFrameToCanvas(ctx, frame, merged, canvas.width, canvas.height);
1226
+ const { w: srcW, h: srcH } = getSourceDims(el);
1227
+ const { renderW, renderH } = computeRenderDims(srcW, srcH);
1228
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1229
+ const MAX_PX = 8e6;
1230
+ const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1231
+ if (canvas.width < renderW || canvas.height < renderH) {
1232
+ canvas.width = Math.round(renderW * cappedDpr);
1233
+ canvas.height = Math.round(renderH * cappedDpr);
1234
+ }
1235
+ const { frame } = imageToAsciiFrame(el, merged, renderW, renderH);
1236
+ ctx.save();
1237
+ ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1238
+ renderFrameToCanvas(ctx, frame, merged, renderW, renderH);
1239
+ ctx.restore();
1218
1240
  }
1219
1241
  async function asciifyGif(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
1220
1242
  const buffer = typeof source === "string" ? await fetch(source).then((r) => r.arrayBuffer()) : source;
@@ -1267,15 +1289,26 @@ async function asciifyVideo(source, canvas, { fontSize, artStyle = "classic", op
1267
1289
  video2 = source;
1268
1290
  }
1269
1291
  if (container) sizeCanvasToContainer(canvas, container, video2.videoWidth / video2.videoHeight, video2.videoWidth, video2.videoHeight);
1292
+ const { renderW: renderW2, renderH: renderH2 } = computeRenderDims(video2.videoWidth, video2.videoHeight);
1293
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1294
+ const MAX_PX = 8e6;
1295
+ const cappedDpr = renderW2 * dpr * renderH2 * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW2 * renderH2)) : dpr;
1296
+ if (canvas.width < Math.round(renderW2 * cappedDpr)) {
1297
+ canvas.width = Math.round(renderW2 * cappedDpr);
1298
+ canvas.height = Math.round(renderH2 * cappedDpr);
1299
+ }
1270
1300
  const maxDur = trimEnd !== void 0 ? trimEnd - trimStart : 10;
1271
- const { frames, fps } = await videoToAsciiFrames(video2, merged, canvas.width, canvas.height, void 0, maxDur, void 0, trimStart);
1301
+ const { frames, fps } = await videoToAsciiFrames(video2, merged, renderW2, renderH2, void 0, maxDur, void 0, trimStart);
1272
1302
  let cancelled2 = false, animId2, i = 0, last = performance.now();
1273
1303
  let firstFrame2 = true;
1274
1304
  const interval = 1e3 / fps;
1275
1305
  const tick2 = (now) => {
1276
1306
  if (cancelled2) return;
1277
1307
  if (now - last >= interval) {
1278
- renderFrameToCanvas(ctx, frames[i], merged, canvas.width, canvas.height);
1308
+ ctx.save();
1309
+ ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1310
+ renderFrameToCanvas(ctx, frames[i], merged, renderW2, renderH2);
1311
+ ctx.restore();
1279
1312
  i = (i + 1) % frames.length;
1280
1313
  last = now;
1281
1314
  if (firstFrame2) {
@@ -1346,12 +1379,28 @@ async function asciifyVideo(source, canvas, { fontSize, artStyle = "classic", op
1346
1379
  video.addEventListener("timeupdate", timeupdateHandler);
1347
1380
  }
1348
1381
  let ro = null;
1382
+ const { renderW, renderH } = computeRenderDims(video.videoWidth, video.videoHeight);
1349
1383
  if (container) {
1350
1384
  const aspect = video.videoWidth / video.videoHeight;
1351
1385
  const vw = video.videoWidth, vh = video.videoHeight;
1352
- sizeCanvasToContainer(canvas, container, aspect, vw, vh);
1353
- ro = new ResizeObserver(() => sizeCanvasToContainer(canvas, container, aspect, vw, vh));
1386
+ const sizing = sizeCanvasToContainer(canvas, container, aspect, vw, vh);
1387
+ const sCtx = canvas.getContext("2d");
1388
+ if (sCtx) sCtx.setTransform(sizing.dpr, 0, 0, sizing.dpr, 0, 0);
1389
+ ro = new ResizeObserver(() => {
1390
+ const s = sizeCanvasToContainer(canvas, container, aspect, vw, vh);
1391
+ const rCtx = canvas.getContext("2d");
1392
+ if (rCtx) rCtx.setTransform(s.dpr, 0, 0, s.dpr, 0, 0);
1393
+ });
1354
1394
  ro.observe(container);
1395
+ } else {
1396
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1397
+ const MAX_PX = 8e6;
1398
+ const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1399
+ if (canvas.width < Math.round(renderW * cappedDpr)) {
1400
+ canvas.width = Math.round(renderW * cappedDpr);
1401
+ canvas.height = Math.round(renderH * cappedDpr);
1402
+ }
1403
+ ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1355
1404
  }
1356
1405
  let cancelled = false;
1357
1406
  let animId;
@@ -1362,9 +1411,9 @@ async function asciifyVideo(source, canvas, { fontSize, artStyle = "classic", op
1362
1411
  if (video.readyState < 2 || canvas.width === 0 || canvas.height === 0) return;
1363
1412
  if (trimStart > 0 && video.currentTime < trimStart) return;
1364
1413
  if (trimEnd !== void 0 && video.currentTime >= trimEnd) return;
1365
- const { frame } = imageToAsciiFrame(video, merged, canvas.width, canvas.height);
1414
+ const { frame } = imageToAsciiFrame(video, merged, renderW, renderH);
1366
1415
  if (frame.length > 0) {
1367
- renderFrameToCanvas(ctx, frame, merged, canvas.width, canvas.height, 0, null);
1416
+ renderFrameToCanvas(ctx, frame, merged, renderW, renderH, 0, null);
1368
1417
  if (firstFrame) {
1369
1418
  firstFrame = false;
1370
1419
  onReady?.(video);