Guard palette array indexing

This commit is contained in:
lucast committed 2026-10-06 02:45:08 +02:00
1 parent 8f1cc8d4a7
commit a6485e5c73
1 file changed
+16 -13
@@ -73,16 +73,19 @@ export function extractPalette(image: HTMLImageElement, size = 48): AlbumPalette
const pixels = context.getImageData(0, 0, size, size).data
const bins = new Map<number, { count: number; r: number; g: number; b: number }>()
for (let i = 0; i < pixels.length; i += 4) {
if (pixels[i + 3] < 96) continue
const r = pixels[i] >> 3
const g = pixels[i + 1] >> 3
const b = pixels[i + 2] >> 3
const red = pixels[i]!
const green = pixels[i + 1]!
const blue = pixels[i + 2]!
if (pixels[i + 3]! < 96) continue
const r = red >> 3
const g = green >> 3
const b = blue >> 3
const key = (r << 10) | (g << 5) | b
const bin = bins.get(key) ?? { count: 0, r: 0, g: 0, b: 0 }
bin.count++
bin.r += pixels[i]
bin.g += pixels[i + 1]
bin.b += pixels[i + 2]
bin.r += red
bin.g += green
bin.b += blue
bins.set(key, bin)
}
const entries = [...bins.values()].map((bin) => ({
@@ -99,7 +102,7 @@ export function extractPalette(image: HTMLImageElement, size = 48): AlbumPalette
}
const centers: RGB[] = [entries.reduce((best, entry) => entry.count > best.count ? entry : best).color]
while (centers.length < 4) {
let candidate = entries[0]
let candidate = entries[0]!
let bestScore = -1
for (const entry of entries) {
const nearestDistance = Math.min(...centers.map((center) => distance(entry.color, center)))
@@ -119,30 +122,30 @@ export function extractPalette(image: HTMLImageElement, size = 48): AlbumPalette
let nearest = 0
let nearestDistance = Infinity
for (let i = 0; i < centers.length; i++) {
const nextDistance = distance(entry.color, centers[i])
const nextDistance = distance(entry.color, centers[i]!)
if (nextDistance < nearestDistance) {
nearest = i
nearestDistance = nextDistance
}
}
const cluster = clusters[nearest]
const cluster = clusters[nearest]!
cluster.count += entry.count
cluster.red += entry.color[0] * entry.count
cluster.green += entry.color[1] * entry.count
cluster.blue += entry.color[2] * entry.count
}
for (let i = 0; i < clusters.length; i++) {
const cluster = clusters[i]
const cluster = clusters[i]!
if (cluster.count) centers[i] = [cluster.red / cluster.count, cluster.green / cluster.count, cluster.blue / cluster.count]
}
clusterSupport = clusters.map((cluster) => cluster.count)
}
const dominantIndex = clusterSupport.reduce(
(best, count, index) => count > clusterSupport[best] ? index : best,
(best, count, index) => count > clusterSupport[best]! ? index : best,
0,
)
const dominant = centers[dominantIndex]
const dominant = centers[dominantIndex]!
const distinctCenters = centers.filter((center) => distance(center, dominant) > 0.002)
const candidates = distinctCenters.length ? distinctCenters : centers
const accent = candidates.reduce((best, color) => {