| |
| export async function rasterizeSVGCanvas(svg: string, width: number, height: number, backgroundColor?: string): Promise<HTMLCanvasElement> { |
| |
| const canvas = document.createElement("canvas"); |
| canvas.width = width; |
| canvas.height = height; |
| const context = canvas.getContext("2d", { willReadFrequently: true }); |
| if (!context) throw new Error("Can't create 2D context from canvas during SVG rasterization"); |
|
|
| |
| if (backgroundColor) { |
| context.fillStyle = backgroundColor; |
| context.fillRect(0, 0, width, height); |
| } |
|
|
| |
| const svgBlob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" }); |
| const url = URL.createObjectURL(svgBlob); |
|
|
| |
| const image = new Image(); |
| image.src = url; |
| await new Promise<void>((resolve) => { |
| image.onload = () => resolve(); |
| }); |
|
|
| |
| context?.drawImage(image, 0, 0, width, height); |
|
|
| |
| URL.revokeObjectURL(url); |
|
|
| return canvas; |
| } |
|
|
| |
| export async function rasterizeSVG(svg: string, width: number, height: number, mime: string, backgroundColor?: string): Promise<Blob> { |
| if (!width || !height) throw new Error("Width and height must be nonzero when given to rasterizeSVG()"); |
|
|
| const canvas = await rasterizeSVGCanvas(svg, width, height, backgroundColor); |
|
|
| |
| const blob = await new Promise<Blob | undefined>((resolve) => { |
| canvas.toBlob((blob) => { |
| resolve(blob || undefined); |
| }, mime); |
| }); |
|
|
| if (!blob) throw new Error("Converting canvas to blob data failed in rasterizeSVG()"); |
|
|
| return blob; |
| } |
|
|
| |
| export async function extractPixelData(imageData: ImageBitmapSource): Promise<ImageData> { |
| const canvasContext = await imageToCanvasContext(imageData); |
| const width = canvasContext.canvas.width; |
| const height = canvasContext.canvas.height; |
|
|
| return canvasContext.getImageData(0, 0, width, height); |
| } |
|
|
| export async function imageToCanvasContext(imageData: ImageBitmapSource): Promise<CanvasRenderingContext2D> { |
| |
| let svgImageData; |
| if (imageData instanceof File && imageData.type === "image/svg+xml") { |
| const svgSource = await imageData.text(); |
| const svgElement = new DOMParser().parseFromString(svgSource, "image/svg+xml").querySelector("svg"); |
| if (!svgElement) throw new Error("Error reading SVG file"); |
|
|
| let bounds = svgElement.viewBox.baseVal; |
|
|
| |
| if (bounds.width === 0 || bounds.height === 0) { |
| |
| const toRemove = document.body.insertAdjacentElement("beforeend", svgElement); |
| bounds = svgElement.getBBox(); |
| toRemove?.remove(); |
| } |
|
|
| svgImageData = await rasterizeSVGCanvas(svgSource, bounds.width, bounds.height); |
| } |
|
|
| |
| const image = await createImageBitmap(svgImageData || imageData); |
|
|
| let { width, height } = image; |
| width = Math.floor(width); |
| height = Math.floor(height); |
|
|
| |
| const canvas = document.createElement("canvas"); |
| canvas.width = width; |
| canvas.height = height; |
|
|
| const context = canvas.getContext("2d"); |
| if (!context) throw new Error("Could not create canvas context"); |
| context.drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height); |
|
|
| return context; |
| } |
|
|