feat: working export with pngjs
This commit is contained in:
parent
2b19d53549
commit
d5ac76d4ea
@ -44,6 +44,7 @@
|
||||
"png-chunk-text": "1.0.0",
|
||||
"png-chunks-encode": "1.0.0",
|
||||
"png-chunks-extract": "1.0.0",
|
||||
"pngjs": "7.0.0",
|
||||
"points-on-curve": "0.2.0",
|
||||
"pwacompat": "2.0.17",
|
||||
"react": "18.2.0",
|
||||
@ -74,6 +75,7 @@
|
||||
"@types/lodash.throttle": "4.1.7",
|
||||
"@types/pako": "1.0.3",
|
||||
"@types/pica": "5.1.3",
|
||||
"@types/pngjs": "6.0.1",
|
||||
"@types/react": "18.0.15",
|
||||
"@types/react-dom": "18.0.6",
|
||||
"@types/resize-observer-browser": "0.1.7",
|
||||
|
111
src/data/blob.ts
111
src/data/blob.ts
@ -13,6 +13,7 @@ import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem";
|
||||
import { isValidExcalidrawData, isValidLibrary } from "./json";
|
||||
import { restore, restoreLibraryItems } from "./restore";
|
||||
import { ImportedLibraryData } from "./types";
|
||||
import { PNG } from "pngjs/browser";
|
||||
|
||||
const parseFileContents = async (blob: Blob | File) => {
|
||||
let contents: string;
|
||||
@ -233,75 +234,71 @@ const _canvasToBlob = async (canvas: HTMLCanvasElement): Promise<Blob> => {
|
||||
export const canvasToBlob = async (
|
||||
canvas: HTMLCanvasElement,
|
||||
): Promise<Blob> => {
|
||||
const chunkSize = 8000; // Adjust the chunk size according to your requirements
|
||||
const tileWidth = 1000;
|
||||
const tileHeight = 1000;
|
||||
const tileDataArray: Uint8ClampedArray[][] = []; // Two-dimensional array to store tile data
|
||||
|
||||
const chunkBlobs: Blob[] = []; // Array to hold the generated image chunk Blobs
|
||||
const { width: canvasWidth, height: canvasHeight } = canvas;
|
||||
|
||||
console.log("canvas", canvas.width, canvas.height);
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
// 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 (!ctx) {
|
||||
throw new Error("No canvas context");
|
||||
}
|
||||
|
||||
if (!chunkContext) {
|
||||
throw new Error("Could not get context");
|
||||
}
|
||||
// Function to process each tile
|
||||
function processTile(tileX: number, tileY: number) {
|
||||
// Calculate the starting and ending coordinates for the tile
|
||||
const startX = tileX * tileWidth;
|
||||
const startY = tileY * tileHeight;
|
||||
const endX = Math.min(startX + tileWidth, canvasWidth);
|
||||
const endY = Math.min(startY + tileHeight, canvasHeight);
|
||||
|
||||
// Copy the portion of the main canvas into the chunk canvas
|
||||
chunkContext.drawImage(
|
||||
canvas,
|
||||
x,
|
||||
y,
|
||||
chunkSize,
|
||||
chunkSize,
|
||||
0,
|
||||
0,
|
||||
chunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
// Get the image data for the tile directly from the main canvas
|
||||
const imageData = ctx!.getImageData(
|
||||
startX,
|
||||
startY,
|
||||
endX - startX,
|
||||
endY - startY,
|
||||
).data;
|
||||
|
||||
console.log(x, y, chunkSize);
|
||||
// Store the tile data in the two-dimensional array
|
||||
tileDataArray[tileY] = tileDataArray[tileY] || [];
|
||||
tileDataArray[tileY][tileX] = imageData;
|
||||
}
|
||||
|
||||
// Convert the chunk canvas to a Blob
|
||||
const blob = await _canvasToBlob(chunkCanvas);
|
||||
chunkBlobs.push(blob);
|
||||
|
||||
chunkCanvas.remove();
|
||||
// Iterate over the tiles and process each one
|
||||
for (let tileY = 0; tileY < canvasHeight / tileHeight; tileY++) {
|
||||
for (let tileX = 0; tileX < canvasWidth / tileWidth; tileX++) {
|
||||
processTile(tileX, tileY);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert each Blob into an ArrayBuffer and concatenate them
|
||||
const arrayBuffers = await Promise.all(
|
||||
chunkBlobs.map((blob) => {
|
||||
return new Promise<ArrayBuffer>((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;
|
||||
// Create a new PNG image with the final dimensions
|
||||
const finalImage = new PNG({ width: canvasWidth, height: canvasHeight });
|
||||
|
||||
// Merge the tiles into the final image
|
||||
for (let tileY = 0; tileY < canvasHeight / tileHeight; tileY++) {
|
||||
for (let tileX = 0; tileX < canvasWidth / tileWidth; tileX++) {
|
||||
const imageData = tileDataArray[tileY][tileX];
|
||||
const destX = tileX * tileWidth;
|
||||
const destY = tileY * tileHeight;
|
||||
for (let y = 0; y < tileHeight; y++) {
|
||||
for (let x = 0; x < tileWidth; x++) {
|
||||
const index = (y * tileWidth + x) * 4;
|
||||
const destIndex = ((destY + y) * canvasWidth + destX + x) * 4;
|
||||
finalImage.data[destIndex] = imageData[index];
|
||||
finalImage.data[destIndex + 1] = imageData[index + 1];
|
||||
finalImage.data[destIndex + 2] = imageData[index + 2];
|
||||
finalImage.data[destIndex + 3] = imageData[index + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Blob([concatenatedBuffer], { type: "image/png" });
|
||||
const buffer = PNG.sync.write(finalImage);
|
||||
|
||||
return new Blob([buffer], { type: "image/png" });
|
||||
};
|
||||
|
||||
/** generates SHA-1 digest from supplied file (if not supported, falls back
|
||||
|
4
src/global.d.ts
vendored
4
src/global.d.ts
vendored
@ -120,3 +120,7 @@ declare module "image-blob-reduce" {
|
||||
const reduce: ImageBlobReduce.ImageBlobReduceStatic;
|
||||
export = reduce;
|
||||
}
|
||||
|
||||
declare module "pngjs/browser" {
|
||||
export { PNG } from "pngjs";
|
||||
}
|
||||
|
12
yarn.lock
12
yarn.lock
@ -2714,6 +2714,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/pica/-/pica-5.1.3.tgz#5ef64529a1f83f7d6586a8bf75a8a00be32aca02"
|
||||
integrity sha512-13SEyETRE5psd9bE0AmN+0M1tannde2fwHfLVaVIljkbL9V0OfFvKwCicyeDvVYLkmjQWEydbAlsDsmjrdyTOg==
|
||||
|
||||
"@types/pngjs@6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.1.tgz#c711ec3fbbf077fed274ecccaf85dd4673130072"
|
||||
integrity sha512-J39njbdW1U/6YyVXvC9+1iflZghP8jgRf2ndYghdJb5xL49LYDB+1EuAxfbuJ2IBbWIL3AjHPQhgaTxT3YaYeg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/prettier@^2.1.5":
|
||||
version "2.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0"
|
||||
@ -8227,6 +8234,11 @@ png-chunks-extract@1.0.0:
|
||||
dependencies:
|
||||
crc-32 "^0.3.0"
|
||||
|
||||
pngjs@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26"
|
||||
integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==
|
||||
|
||||
points-on-curve@0.2.0, points-on-curve@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-0.2.0.tgz#7dbb98c43791859434284761330fa893cb81b4d1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user