diff --git a/src/data/blob.ts b/src/data/blob.ts index c0aa66ee7..2fc44ec6c 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -210,9 +210,7 @@ export const loadLibraryFromBlob = async ( return parseLibraryJSON(await parseFileContents(blob), defaultStatus); }; -export const canvasToBlob = async ( - canvas: HTMLCanvasElement, -): Promise => { +const _canvasToBlob = async (canvas: HTMLCanvasElement): Promise => { return new Promise((resolve, reject) => { try { canvas.toBlob((blob) => { @@ -232,6 +230,80 @@ export const canvasToBlob = async ( }); }; +export const canvasToBlob = async ( + canvas: HTMLCanvasElement, +): Promise => { + const chunkSize = 8000; // Adjust the chunk size according to your requirements + + const chunkBlobs: Blob[] = []; // Array to hold the generated image chunk Blobs + + console.log("canvas", canvas.width, canvas.height); + + // Split the canvas into chunks and generate the image chunks + for (let x = 0; x < canvas.width; x += chunkSize) { + for (let y = 0; y < canvas.height; y += chunkSize) { + const chunkCanvas = document.createElement("canvas"); + chunkCanvas.width = chunkSize; + chunkCanvas.height = chunkSize; + const chunkContext = chunkCanvas.getContext("2d"); + + if (!chunkContext) { + throw new Error("Could not get context"); + } + + // Copy the portion of the main canvas into the chunk canvas + chunkContext.drawImage( + canvas, + x, + y, + chunkSize, + chunkSize, + 0, + 0, + chunkSize, + chunkSize, + ); + + console.log(x, y, chunkSize); + + // Convert the chunk canvas to a Blob + const blob = await _canvasToBlob(chunkCanvas); + chunkBlobs.push(blob); + + chunkCanvas.remove(); + } + } + + // Convert each Blob into an ArrayBuffer and concatenate them + const arrayBuffers = await Promise.all( + chunkBlobs.map((blob) => { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onloadend = () => { + if (reader.result instanceof ArrayBuffer) { + resolve(reader.result); + } else { + throw new Error("Failed to read ArrayBuffer"); + } + }; + reader.readAsArrayBuffer(blob); + }); + }), + ); + const totalLength = arrayBuffers.reduce( + (length, buffer) => length + buffer.byteLength, + 0, + ); + const concatenatedBuffer = new Uint8Array(totalLength); + let offset = 0; + for (const buffer of arrayBuffers) { + concatenatedBuffer.set(new Uint8Array(buffer), offset); + offset += buffer.byteLength; + } + + return new Blob([concatenatedBuffer], { type: "image/png" }); +}; + /** generates SHA-1 digest from supplied file (if not supported, falls back to a 40-char base64 random id) */ export const generateIdFromFile = async (file: File): Promise => { diff --git a/src/data/index.ts b/src/data/index.ts index 20ba75ebe..2061388fb 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -74,8 +74,11 @@ export const exportCanvas = async ( tempCanvas.style.display = "none"; document.body.appendChild(tempCanvas); + console.log("tempCanvas", tempCanvas.width, tempCanvas.height); + if (type === "png") { let blob = await canvasToBlob(tempCanvas); + console.log("final blob size", blob.size); tempCanvas.remove(); if (appState.exportEmbedScene) { blob = await (