collageify
Version:
accepts images, creates a collage, and downloads it as a file, get the dominant colors in the uploaded image
1 lines • 5.73 kB
Source Map (JSON)
{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["export interface CollageOptions {\r\n images: (File | string)[];\r\n rows?: number;\r\n cols?: number;\r\n width?: number;\r\n height?: number;\r\n padding?: number;\r\n backgroundColor?: string;\r\n}\r\n\r\nexport async function createCollage({\r\n images,\r\n rows = 2,\r\n cols = 2,\r\n width = 800,\r\n height = 800,\r\n padding = 10,\r\n backgroundColor = '#ffffff',\r\n}: CollageOptions): Promise<HTMLCanvasElement> {\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) throw new Error('Canvas not supported');\r\n\r\n canvas.width = width;\r\n canvas.height = height;\r\n\r\n ctx.fillStyle = backgroundColor;\r\n ctx.fillRect(0, 0, width, height);\r\n\r\n const imageElements = await Promise.all(\r\n images.map(src => loadImage(src))\r\n );\r\n\r\n const cellWidth = (width - padding * (cols + 1)) / cols;\r\n const cellHeight = (height - padding * (rows + 1)) / rows;\r\n\r\n imageElements.forEach((img, index) => {\r\n const row = Math.floor(index / cols);\r\n const col = index % cols;\r\n const x = padding + col * (cellWidth + padding);\r\n const y = padding + row * (cellHeight + padding);\r\n ctx.drawImage(img, x, y, cellWidth, cellHeight);\r\n });\r\n\r\n return canvas;\r\n}\r\n\r\nfunction loadImage(src: File | string): Promise<HTMLImageElement> {\r\n return new Promise((resolve, reject) => {\r\n const img = new Image();\r\n img.crossOrigin = 'anonymous';\r\n img.onload = () => resolve(img);\r\n img.onerror = reject;\r\n if (typeof src === 'string') {\r\n img.src = src;\r\n } else {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n img.src = reader.result as string;\r\n };\r\n reader.onerror = reject;\r\n reader.readAsDataURL(src);\r\n }\r\n });\r\n}\r\n\r\nexport function downloadCanvas(canvas: HTMLCanvasElement, filename = 'collage.png') {\r\n const link = document.createElement('a');\r\n link.download = filename;\r\n link.href = canvas.toDataURL('image/png');\r\n link.click();\r\n}\r\n\r\nexport async function getDominantColorsFromImage(\r\n file: File,\r\n colorCount = 5\r\n): Promise<string[]> {\r\n const image = await loadImage(file);\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n\r\n if (!ctx) throw new Error(\"Canvas is not supported\");\r\n\r\n const width = (canvas.width = image.width);\r\n const height = (canvas.height = image.height);\r\n ctx.drawImage(image, 0, 0, width, height);\r\n\r\n const imageData = ctx.getImageData(0, 0, width, height);\r\n const colorMap: Record<string, number> = {};\r\n\r\n for (let i = 0; i < imageData.data.length; i += 4) {\r\n const r = imageData.data[i];\r\n const g = imageData.data[i + 1];\r\n const b = imageData.data[i + 2];\r\n const a = imageData.data[i + 3];\r\n if (a < 128) continue; // Ignore transparent pixels\r\n\r\n const hex = rgbToHex(r, g, b);\r\n colorMap[hex] = (colorMap[hex] || 0) + 1;\r\n }\r\n\r\n const sortedColors = Object.entries(colorMap)\r\n .sort((a, b) => b[1] - a[1]) // Sort by frequency\r\n .slice(0, colorCount) // Top N\r\n .map(([hex]) => hex);\r\n\r\n return sortedColors;\r\n}\r\n\r\nfunction rgbToHex(r: number, g: number, b: number): string {\r\n return (\r\n '#' +\r\n [r, g, b]\r\n .map((x) => x.toString(16).padStart(2, '0'))\r\n .join('')\r\n .toUpperCase()\r\n );\r\n}\r\n"],"mappings":"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","names":[]}