canvu-react 0.4.70 → 0.4.72

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.
@@ -22,6 +22,8 @@ type VectorViewportAssetUploadRequest = {
22
22
  file: File;
23
23
  /** High-level bucket for routing image vs PDF upload behavior. */
24
24
  kind: VectorViewportAssetKind;
25
+ /** Optional lifecycle signal for cancelling in-flight uploads. */
26
+ signal?: AbortSignal;
25
27
  };
26
28
  /**
27
29
  * Persisted metadata returned by a custom asset upload.
@@ -179,6 +181,37 @@ type RasterImageCanvasRenderRequest = {
179
181
  cameraZoom: number;
180
182
  devicePixelRatio: number;
181
183
  };
184
+ /**
185
+ * Bitmap-like source returned by `imageCanvasRendering.resolveCanvasSource`.
186
+ *
187
+ * Apps can render into an offscreen canvas, create an `ImageBitmap`, or wrap any
188
+ * other canvas-compatible source and let Canvu copy it into the visible canvas
189
+ * without changing the underlying SVG image URL.
190
+ */
191
+ type RasterImageCanvasSource = {
192
+ width: number;
193
+ height: number;
194
+ draw: (context: CanvasRenderingContext2D) => void;
195
+ close?: () => void;
196
+ };
197
+ /**
198
+ * Input passed to `imageCanvasRendering.resolveCanvasSource`.
199
+ *
200
+ * Use this for custom async rendering pipelines such as PDF page rasterization.
201
+ * Canvu keeps the previous canvas visible while this request resolves and aborts
202
+ * stale requests when the item leaves the viewport or a sharper target is queued.
203
+ */
204
+ type RasterImageCanvasSourceRequest = RasterImageCanvasRenderRequest & {
205
+ targetHref: string;
206
+ sourceKey: string;
207
+ signal: AbortSignal;
208
+ };
209
+ /**
210
+ * Maps an image item plus requested bitmap size to a custom canvas source.
211
+ *
212
+ * Return `null`/`undefined` to let Canvu fetch and decode the resolved URL.
213
+ */
214
+ type RasterImageCanvasSourceResolver = (request: RasterImageCanvasSourceRequest) => RasterImageCanvasSource | null | undefined | Promise<RasterImageCanvasSource | null | undefined>;
182
215
  /**
183
216
  * Resolved raster source for a zoom-aware canvas redraw.
184
217
  *
@@ -209,6 +242,7 @@ type RasterImageCanvasRenderTargetResolver = (request: RasterImageCanvasRenderRe
209
242
  type RasterImageCanvasRenderingOptions = {
210
243
  resolveSourceSize?: RasterImageCanvasSourceSizeResolver;
211
244
  resolveRenderTarget?: RasterImageCanvasRenderTargetResolver;
245
+ resolveCanvasSource?: RasterImageCanvasSourceResolver;
212
246
  devicePixelRatio?: number;
213
247
  pixelHeadroom?: number;
214
248
  maxPixelCount?: number;
@@ -216,4 +250,4 @@ type RasterImageCanvasRenderingOptions = {
216
250
  upscaleRedrawRatio?: number;
217
251
  };
218
252
 
219
- export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSourceSizeRequest as e, RasterImageCanvasSourceSizeResolver as f, VectorViewportAssetHydrationRequest as g, VectorViewportAssetResolveRequest as h, VectorViewportAssetResolveResult as i, VectorViewportAssetUploadRequest as j, VectorViewportAssetUploadResult as k };
253
+ export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSource as e, RasterImageCanvasSourceRequest as f, RasterImageCanvasSourceResolver as g, RasterImageCanvasSourceSizeRequest as h, RasterImageCanvasSourceSizeResolver as i, VectorViewportAssetHydrationRequest as j, VectorViewportAssetResolveRequest as k, VectorViewportAssetResolveResult as l, VectorViewportAssetUploadRequest as m, VectorViewportAssetUploadResult as n };
@@ -22,6 +22,8 @@ type VectorViewportAssetUploadRequest = {
22
22
  file: File;
23
23
  /** High-level bucket for routing image vs PDF upload behavior. */
24
24
  kind: VectorViewportAssetKind;
25
+ /** Optional lifecycle signal for cancelling in-flight uploads. */
26
+ signal?: AbortSignal;
25
27
  };
26
28
  /**
27
29
  * Persisted metadata returned by a custom asset upload.
@@ -179,6 +181,37 @@ type RasterImageCanvasRenderRequest = {
179
181
  cameraZoom: number;
180
182
  devicePixelRatio: number;
181
183
  };
184
+ /**
185
+ * Bitmap-like source returned by `imageCanvasRendering.resolveCanvasSource`.
186
+ *
187
+ * Apps can render into an offscreen canvas, create an `ImageBitmap`, or wrap any
188
+ * other canvas-compatible source and let Canvu copy it into the visible canvas
189
+ * without changing the underlying SVG image URL.
190
+ */
191
+ type RasterImageCanvasSource = {
192
+ width: number;
193
+ height: number;
194
+ draw: (context: CanvasRenderingContext2D) => void;
195
+ close?: () => void;
196
+ };
197
+ /**
198
+ * Input passed to `imageCanvasRendering.resolveCanvasSource`.
199
+ *
200
+ * Use this for custom async rendering pipelines such as PDF page rasterization.
201
+ * Canvu keeps the previous canvas visible while this request resolves and aborts
202
+ * stale requests when the item leaves the viewport or a sharper target is queued.
203
+ */
204
+ type RasterImageCanvasSourceRequest = RasterImageCanvasRenderRequest & {
205
+ targetHref: string;
206
+ sourceKey: string;
207
+ signal: AbortSignal;
208
+ };
209
+ /**
210
+ * Maps an image item plus requested bitmap size to a custom canvas source.
211
+ *
212
+ * Return `null`/`undefined` to let Canvu fetch and decode the resolved URL.
213
+ */
214
+ type RasterImageCanvasSourceResolver = (request: RasterImageCanvasSourceRequest) => RasterImageCanvasSource | null | undefined | Promise<RasterImageCanvasSource | null | undefined>;
182
215
  /**
183
216
  * Resolved raster source for a zoom-aware canvas redraw.
184
217
  *
@@ -209,6 +242,7 @@ type RasterImageCanvasRenderTargetResolver = (request: RasterImageCanvasRenderRe
209
242
  type RasterImageCanvasRenderingOptions = {
210
243
  resolveSourceSize?: RasterImageCanvasSourceSizeResolver;
211
244
  resolveRenderTarget?: RasterImageCanvasRenderTargetResolver;
245
+ resolveCanvasSource?: RasterImageCanvasSourceResolver;
212
246
  devicePixelRatio?: number;
213
247
  pixelHeadroom?: number;
214
248
  maxPixelCount?: number;
@@ -216,4 +250,4 @@ type RasterImageCanvasRenderingOptions = {
216
250
  upscaleRedrawRatio?: number;
217
251
  };
218
252
 
219
- export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSourceSizeRequest as e, RasterImageCanvasSourceSizeResolver as f, VectorViewportAssetHydrationRequest as g, VectorViewportAssetResolveRequest as h, VectorViewportAssetResolveResult as i, VectorViewportAssetUploadRequest as j, VectorViewportAssetUploadResult as k };
253
+ export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSource as e, RasterImageCanvasSourceRequest as f, RasterImageCanvasSourceResolver as g, RasterImageCanvasSourceSizeRequest as h, RasterImageCanvasSourceSizeResolver as i, VectorViewportAssetHydrationRequest as j, VectorViewportAssetResolveRequest as k, VectorViewportAssetResolveResult as l, VectorViewportAssetUploadRequest as m, VectorViewportAssetUploadResult as n };
package/dist/react.cjs CHANGED
@@ -1208,7 +1208,8 @@ function getPdfJs() {
1208
1208
  }
1209
1209
  return pdfjsPromise;
1210
1210
  }
1211
- async function renderPageToCanvas(page, scale) {
1211
+ async function renderPageToCanvas(page, scale, signal) {
1212
+ throwIfAborted(signal);
1212
1213
  const raw = page.getViewport({ scale: 1 });
1213
1214
  const adjustedScale = Math.round(raw.width * scale) / raw.width;
1214
1215
  const viewport = page.getViewport({ scale: adjustedScale });
@@ -1221,22 +1222,48 @@ async function renderPageToCanvas(page, scale) {
1221
1222
  if (!ctx) throw new Error("Canvas 2D context unavailable");
1222
1223
  ctx.imageSmoothingEnabled = true;
1223
1224
  ctx.imageSmoothingQuality = "high";
1224
- await page.render({ canvas, viewport }).promise;
1225
+ const renderTask = page.render({ canvas, viewport });
1226
+ if (signal) {
1227
+ const abortRender = () => {
1228
+ renderTask.cancel();
1229
+ };
1230
+ signal.addEventListener("abort", abortRender, { once: true });
1231
+ try {
1232
+ await renderTask.promise;
1233
+ } catch (error) {
1234
+ if (signal.aborted) throw createAbortError();
1235
+ throw error;
1236
+ } finally {
1237
+ signal.removeEventListener("abort", abortRender);
1238
+ }
1239
+ } else {
1240
+ await renderTask.promise;
1241
+ }
1242
+ throwIfAborted(signal);
1225
1243
  return { canvas, width: w, height: h };
1226
1244
  }
1245
+ function createAbortError() {
1246
+ return new DOMException("Aborted", "AbortError");
1247
+ }
1248
+ function throwIfAborted(signal) {
1249
+ if (signal?.aborted) {
1250
+ throw createAbortError();
1251
+ }
1252
+ }
1227
1253
  function normalizePdfPageNumbers(pageNumbers, pageCount) {
1228
1254
  if (!pageNumbers || pageNumbers.length === 0) {
1229
1255
  return Array.from({ length: pageCount }, (_, index) => index + 1);
1230
1256
  }
1231
1257
  return [...new Set(pageNumbers)].filter((pageNumber) => pageNumber >= 1 && pageNumber <= pageCount).sort((left, right) => left - right);
1232
1258
  }
1233
- async function runWithConcurrency(items, concurrency, execute) {
1259
+ async function runWithConcurrency(items, concurrency, execute, signal) {
1234
1260
  const results = new Array(items.length);
1235
1261
  let nextIndex = 0;
1236
1262
  const workerCount = Math.max(1, Math.min(concurrency, items.length));
1237
1263
  await Promise.all(
1238
1264
  Array.from({ length: workerCount }, async () => {
1239
1265
  while (nextIndex < items.length) {
1266
+ throwIfAborted(signal);
1240
1267
  const currentIndex = nextIndex;
1241
1268
  nextIndex += 1;
1242
1269
  const item = items[currentIndex];
@@ -1244,6 +1271,7 @@ async function runWithConcurrency(items, concurrency, execute) {
1244
1271
  continue;
1245
1272
  }
1246
1273
  results[currentIndex] = await execute(item);
1274
+ throwIfAborted(signal);
1247
1275
  }
1248
1276
  })
1249
1277
  );
@@ -1253,9 +1281,49 @@ async function loadPdfToStore(file, store, options) {
1253
1281
  const scale = options?.scale ?? 1.5;
1254
1282
  const pageConcurrency = options?.pageConcurrency ?? 2;
1255
1283
  const storeThumbnails = options?.storeThumbnails ?? false;
1284
+ const signal = options?.signal;
1285
+ throwIfAborted(signal);
1256
1286
  const pdfjs = await getPdfJs();
1287
+ throwIfAborted(signal);
1257
1288
  const arrayBuffer = await file.arrayBuffer();
1258
- const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
1289
+ throwIfAborted(signal);
1290
+ const loadingTask = pdfjs.getDocument({ data: arrayBuffer });
1291
+ if (signal) {
1292
+ const abortLoading = () => {
1293
+ void loadingTask.destroy();
1294
+ };
1295
+ signal.addEventListener("abort", abortLoading, { once: true });
1296
+ try {
1297
+ const pdf2 = await loadingTask.promise;
1298
+ signal.removeEventListener("abort", abortLoading);
1299
+ return await loadPdfDocumentToStore(pdf2, store, {
1300
+ scale,
1301
+ pageNumbers: options?.pageNumbers,
1302
+ pageConcurrency,
1303
+ storeThumbnails,
1304
+ signal,
1305
+ onPageStored: options?.onPageStored
1306
+ });
1307
+ } catch (error) {
1308
+ if (signal.aborted) throw createAbortError();
1309
+ throw error;
1310
+ } finally {
1311
+ signal.removeEventListener("abort", abortLoading);
1312
+ }
1313
+ }
1314
+ const pdf = await loadingTask.promise;
1315
+ return await loadPdfDocumentToStore(pdf, store, {
1316
+ scale,
1317
+ pageNumbers: options?.pageNumbers,
1318
+ pageConcurrency,
1319
+ storeThumbnails,
1320
+ onPageStored: options?.onPageStored
1321
+ });
1322
+ }
1323
+ async function loadPdfDocumentToStore(pdf, store, options) {
1324
+ const { pageConcurrency, scale, signal } = options;
1325
+ const storeThumbnails = options.storeThumbnails ?? false;
1326
+ throwIfAborted(signal);
1259
1327
  const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
1260
1328
  const bufferedResults = /* @__PURE__ */ new Map();
1261
1329
  let nextEmitIndex = 0;
@@ -1270,7 +1338,9 @@ async function loadPdfToStore(file, store, options) {
1270
1338
  if (!bufferedResult) break;
1271
1339
  bufferedResults.delete(nextPageNumber);
1272
1340
  nextEmitIndex += 1;
1341
+ throwIfAborted(signal);
1273
1342
  await options?.onPageStored?.(bufferedResult);
1343
+ throwIfAborted(signal);
1274
1344
  }
1275
1345
  };
1276
1346
  const nextChain = emitChain.then(run, run);
@@ -1282,11 +1352,20 @@ async function loadPdfToStore(file, store, options) {
1282
1352
  pageNumbers,
1283
1353
  pageConcurrency,
1284
1354
  async (pageNumber) => {
1355
+ throwIfAborted(signal);
1285
1356
  const page = await pdf.getPage(pageNumber);
1286
- const { canvas, width, height } = await renderPageToCanvas(page, scale);
1357
+ throwIfAborted(signal);
1358
+ const { canvas, width, height } = await renderPageToCanvas(
1359
+ page,
1360
+ scale,
1361
+ signal
1362
+ );
1363
+ throwIfAborted(signal);
1287
1364
  const mime = "image/png";
1288
1365
  const pageBlob = await encodeCanvasToBlob(canvas, { mimeType: mime });
1366
+ throwIfAborted(signal);
1289
1367
  const blobId = await store.storeOriginal(pageBlob);
1368
+ throwIfAborted(signal);
1290
1369
  const thumbnailBlobId = storeThumbnails ? await (async () => {
1291
1370
  const thumbScale = Math.min(1, 256 / Math.max(width, height));
1292
1371
  const tw = Math.max(1, Math.round(width * thumbScale));
@@ -1303,6 +1382,7 @@ async function loadPdfToStore(file, store, options) {
1303
1382
  const thumbBlob = await encodeCanvasToBlob(thumbCanvas, {
1304
1383
  mimeType: mime
1305
1384
  });
1385
+ throwIfAborted(signal);
1306
1386
  return await store.storeThumbnail(thumbBlob);
1307
1387
  })() : "";
1308
1388
  const pageResult = {
@@ -1314,7 +1394,8 @@ async function loadPdfToStore(file, store, options) {
1314
1394
  };
1315
1395
  await queuePageEmission(pageResult);
1316
1396
  return pageResult;
1317
- }
1397
+ },
1398
+ signal
1318
1399
  );
1319
1400
  }
1320
1401
 
@@ -1655,9 +1736,22 @@ function finalizeIngestedItem(item, context, uploadResult, decorateItem) {
1655
1736
  const itemWithAssetData = applyAssetUploadResultToItem(item, uploadResult);
1656
1737
  return decorateItem ? decorateItem(itemWithAssetData, context) : itemWithAssetData;
1657
1738
  }
1658
- async function uploadAssetIfNeeded(assetStore, file, kind) {
1739
+ function createAbortError2() {
1740
+ return new DOMException("Aborted", "AbortError");
1741
+ }
1742
+ function throwIfAborted2(signal) {
1743
+ if (signal?.aborted) {
1744
+ throw createAbortError2();
1745
+ }
1746
+ }
1747
+ function isAbortError(error) {
1748
+ return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
1749
+ }
1750
+ async function uploadAssetIfNeeded(assetStore, file, kind, signal) {
1751
+ throwIfAborted2(signal);
1659
1752
  if (!assetStore) return null;
1660
- const result = await assetStore.upload({ file, kind });
1753
+ const result = await assetStore.upload({ file, kind, signal });
1754
+ throwIfAborted2(signal);
1661
1755
  return result ?? null;
1662
1756
  }
1663
1757
  async function ingestAssetFilesToSceneItems(options) {
@@ -1671,6 +1765,7 @@ async function ingestAssetFilesToSceneItems(options) {
1671
1765
  gapWorld = 16,
1672
1766
  pdfScale = 1.15,
1673
1767
  pdfPageConcurrency = 2,
1768
+ signal,
1674
1769
  decorateItem,
1675
1770
  onItemsReady,
1676
1771
  onError
@@ -1679,6 +1774,7 @@ async function ingestAssetFilesToSceneItems(options) {
1679
1774
  const errors = [];
1680
1775
  let occupiedBottomY = existingItems.length > 0 ? Math.max(...existingItems.map((item) => item.bounds.y + item.bounds.height)) : null;
1681
1776
  for (const file of files) {
1777
+ throwIfAborted2(signal);
1682
1778
  const kind = getAssetKindForFile(file);
1683
1779
  if (!kind) {
1684
1780
  const error = {
@@ -1691,14 +1787,23 @@ async function ingestAssetFilesToSceneItems(options) {
1691
1787
  }
1692
1788
  try {
1693
1789
  if (kind === "pdf") {
1694
- const uploadResultPromise = uploadAssetIfNeeded(assetStore, file, kind);
1790
+ const uploadResultPromise = uploadAssetIfNeeded(
1791
+ assetStore,
1792
+ file,
1793
+ kind,
1794
+ signal
1795
+ );
1796
+ void uploadResultPromise.catch(() => void 0);
1695
1797
  await loadPdfToStore(file, imageStore, {
1696
1798
  scale: pdfScale,
1697
1799
  pageConcurrency: pdfPageConcurrency,
1698
1800
  storeThumbnails: false,
1801
+ signal,
1699
1802
  onPageStored: async (page) => {
1803
+ throwIfAborted2(signal);
1700
1804
  const uploadResult2 = await uploadResultPromise;
1701
1805
  const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
1806
+ throwIfAborted2(signal);
1702
1807
  const naturalTopY2 = worldCenter.y - page.height / 2;
1703
1808
  const stackedTopY2 = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
1704
1809
  const bounds2 = {
@@ -1738,15 +1843,18 @@ async function ingestAssetFilesToSceneItems(options) {
1738
1843
  );
1739
1844
  items.push(item2);
1740
1845
  occupiedBottomY = bounds2.y + page.height;
1846
+ throwIfAborted2(signal);
1741
1847
  onItemsReady?.([item2], { file, kind });
1742
1848
  }
1743
1849
  });
1744
1850
  continue;
1745
1851
  }
1852
+ throwIfAborted2(signal);
1746
1853
  const [uploadResult, storedImage] = await Promise.all([
1747
- uploadAssetIfNeeded(assetStore, file, kind),
1854
+ uploadAssetIfNeeded(assetStore, file, kind, signal),
1748
1855
  loadImageToStore(file, imageStore)
1749
1856
  ]);
1857
+ throwIfAborted2(signal);
1750
1858
  const { blobId, thumbnailBlobId, width, height } = storedImage;
1751
1859
  const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
1752
1860
  const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
@@ -1784,9 +1892,13 @@ async function ingestAssetFilesToSceneItems(options) {
1784
1892
  decorateItem
1785
1893
  );
1786
1894
  items.push(item);
1895
+ throwIfAborted2(signal);
1787
1896
  onItemsReady?.([item], { file, kind });
1788
1897
  occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
1789
1898
  } catch (error) {
1899
+ if (isAbortError(error)) {
1900
+ throw error;
1901
+ }
1790
1902
  const fileError = {
1791
1903
  file,
1792
1904
  kind,
@@ -6896,6 +7008,7 @@ function resolveRasterImageCanvasRenderingOptions(options) {
6896
7008
  return {
6897
7009
  resolveSourceSize: options.resolveSourceSize,
6898
7010
  resolveRenderTarget: options.resolveRenderTarget,
7011
+ resolveCanvasSource: options.resolveCanvasSource,
6899
7012
  devicePixelRatio: toPositiveFiniteNumber(
6900
7013
  options.devicePixelRatio,
6901
7014
  fallbackDevicePixelRatio
@@ -7160,6 +7273,36 @@ function loadImageElement(href, signal) {
7160
7273
  image.src = href;
7161
7274
  });
7162
7275
  }
7276
+ function normalizeDecodedRasterImageSource(source) {
7277
+ if (!source) return null;
7278
+ const width = Math.max(1, Math.round(source.width));
7279
+ const height = Math.max(1, Math.round(source.height));
7280
+ if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
7281
+ return {
7282
+ width,
7283
+ height,
7284
+ draw: source.draw,
7285
+ close: source.close ?? (() => {
7286
+ })
7287
+ };
7288
+ }
7289
+ async function resolveDecodedRasterImageSource({
7290
+ options,
7291
+ request,
7292
+ width,
7293
+ height,
7294
+ signal
7295
+ }) {
7296
+ if (options?.resolveCanvasSource) {
7297
+ const source = await options.resolveCanvasSource({
7298
+ ...request,
7299
+ signal
7300
+ });
7301
+ const decoded = normalizeDecodedRasterImageSource(source);
7302
+ if (decoded) return decoded;
7303
+ }
7304
+ return decodeRasterImage(request.targetHref, width, height, signal);
7305
+ }
7163
7306
  var SvgVectorRenderer = class {
7164
7307
  container;
7165
7308
  scene;
@@ -7392,7 +7535,20 @@ var SvgVectorRenderer = class {
7392
7535
  }
7393
7536
  const request = {
7394
7537
  itemHref: item.imageRasterHref,
7395
- target
7538
+ target,
7539
+ sourceRequest: {
7540
+ item,
7541
+ href: item.imageRasterHref,
7542
+ intrinsicSize: item.imageIntrinsicSize,
7543
+ sourceSize,
7544
+ contentRect: target.contentRect,
7545
+ targetSize: target.targetSize,
7546
+ viewportSize,
7547
+ cameraZoom: this.camera.zoom,
7548
+ devicePixelRatio: options.devicePixelRatio,
7549
+ targetHref: target.href,
7550
+ sourceKey: target.sourceKey
7551
+ }
7396
7552
  };
7397
7553
  if (rasterCanvas.abortController) {
7398
7554
  if (rasterCanvas.loadingItemHref !== item.imageRasterHref) {
@@ -7482,7 +7638,13 @@ var SvgVectorRenderer = class {
7482
7638
  rasterCanvas.loadingWidth = width;
7483
7639
  rasterCanvas.loadingHeight = height;
7484
7640
  rasterCanvas.queuedTarget = null;
7485
- decodeRasterImage(target.href, width, height, abortController.signal).then((decoded) => {
7641
+ resolveDecodedRasterImageSource({
7642
+ options: this.rasterImageCanvasRendering,
7643
+ request: request.sourceRequest,
7644
+ width,
7645
+ height,
7646
+ signal: abortController.signal
7647
+ }).then((decoded) => {
7486
7648
  if (abortController.signal.aborted || rasterCanvas.loadSequence !== sequence) {
7487
7649
  decoded.close();
7488
7650
  return;
@@ -8391,6 +8553,7 @@ function shallowEqualStringArray(a, b) {
8391
8553
  }
8392
8554
 
8393
8555
  // src/react/stroke-input.ts
8556
+ var MAX_INTERPOLATED_POINTS_PER_SAMPLE = 192;
8394
8557
  function getPointerEventSamples(event) {
8395
8558
  if (typeof event.getCoalescedEvents !== "function") {
8396
8559
  return [event];
@@ -8414,9 +8577,9 @@ function appendInterpolatedStrokePoint(points, nextPoint, maxStepWorld) {
8414
8577
  const distanceSquared = deltaX * deltaX + deltaY * deltaY;
8415
8578
  if (distanceSquared < 1e-12) return points;
8416
8579
  const distance = Math.sqrt(distanceSquared);
8417
- const stepCount = Math.max(
8418
- 1,
8419
- Math.ceil(distance / resolveMaxStepWorld(maxStepWorld))
8580
+ const stepCount = Math.min(
8581
+ MAX_INTERPOLATED_POINTS_PER_SAMPLE,
8582
+ Math.max(1, Math.ceil(distance / resolveMaxStepWorld(maxStepWorld)))
8420
8583
  );
8421
8584
  if (stepCount === 1) return [...points, nextPoint];
8422
8585
  const interpolatedPoints = Array.from({ length: stepCount }, (_, index) => {