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