|
|
|
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; |
|
} |
|
|