src/pixie/fileformats/bmp.nim:77, src/pixie/fileformats/bmp.nim:80, and src/pixie/fileformats/bmp.nim:130 only synthesize a default palette size for 8-bit BMPs. Valid 1-bit and 4-bit indexed BMPs are allowed by the format to leave clrUsed at 0, but this decoder leaves the palette empty and then indexes it during decode. Verified locally with a valid 1-bit BMP repro, which crashed with IndexDefect.
# Run: nim r --path:src pocs/bmp_1bit_default_palette_crash.nim
import pixie
proc addLe16(data: var string, value: int) =
data.add(char(value and 0xff))
data.add(char((value shr 8) and 0xff))
proc addLe32(data: var string, value: int) =
data.add(char(value and 0xff))
data.add(char((value shr 8) and 0xff))
data.add(char((value shr 16) and 0xff))
data.add(char((value shr 24) and 0xff))
let pixelDataOffset = 14 + 40 + 8
let fileSize = pixelDataOffset + 4
var payload = "BM"
payload.addLe32(fileSize)
payload.addLe16(0)
payload.addLe16(0)
payload.addLe32(pixelDataOffset)
payload.addLe32(40) # BITMAPINFOHEADER
payload.addLe32(1) # width
payload.addLe32(1) # height
payload.addLe16(1) # planes
payload.addLe16(1) # 1-bit indexed BMP
payload.addLe32(0) # BI_RGB
payload.addLe32(4) # padded pixel data size
payload.addLe32(0)
payload.addLe32(0)
payload.addLe32(0) # clrUsed = 0, meaning default palette size should apply
payload.addLe32(0)
# Valid 1-bit palette: black and white.
payload.add("\x00\x00\x00\x00")
payload.add("\xff\xff\xff\x00")
# One white pixel, padded to a 4-byte row.
payload.add("\x80\x00\x00\x00")
echo "parsing valid 1-bit BMP with clrUsed == 0"
discard decodeImage(payload)