diff --git a/src/pixie/fileformats/bmp.nim b/src/pixie/fileformats/bmp.nim index 9853d7f..a45523b 100644 --- a/src/pixie/fileformats/bmp.nim +++ b/src/pixie/fileformats/bmp.nim @@ -1,9 +1,12 @@ -import chroma, flatty/binny, pixie/common, pixie/images +import bitops, chroma, flatty/binny, pixie/common, pixie/images # See: https://en.wikipedia.org/wiki/BMP_file_format const bmpSignature* = "BM" +proc colorMaskShift(color: uint32, mask: uint32): uint8 = + ((color and mask) shr (mask.firstSetBit() - 1)).uint8 + proc decodeBmp*(data: string): Image {.raises: [PixieError].} = ## Decodes bitmap data into an Image. @@ -16,8 +19,21 @@ proc decodeBmp*(data: string): Image {.raises: [PixieError].} = height = data.readInt32(22).int bits = data.readUint16(28).int compression = data.readUint32(30).int + dibHeader = data.readInt32(14).int + var offset = data.readUInt32(10).int + # Default channels if header does not contain them: + redChan = 0x00FF0000.uint32 + greenChan = 0x0000FF00.uint32 + blueChan = 0x000000FF.uint32 + alphaChan = 0xFF000000.uint32 + + if dibHeader == 108: + redChan = data.readUInt32(54) + greenChan = data.readUInt32(58) + blueChan = data.readUInt32(62) + alphaChan = data.readUInt32(66) if bits notin [32, 24]: raise newException(PixieError, "Unsupported BMP data format") @@ -35,10 +51,11 @@ proc decodeBmp*(data: string): Image {.raises: [PixieError].} = for x in 0 ..< result.width: var rgba: ColorRGBA if bits == 32: - rgba.r = data.readUint8(offset + 0) - rgba.g = data.readUint8(offset + 1) - rgba.b = data.readUint8(offset + 2) - rgba.a = data.readUint8(offset + 3) + let color = data.readUint32(offset) + rgba.r = color.colorMaskShift(redChan) + rgba.g = color.colorMaskShift(greenChan) + rgba.b = color.colorMaskShift(blueChan) + rgba.a = color.colorMaskShift(alphaChan) offset += 4 elif bits == 24: rgba.r = data.readUint8(offset + 2) diff --git a/tests/test_bmp.nim b/tests/test_bmp.nim index 78b8c5d..d5716dc 100644 --- a/tests/test_bmp.nim +++ b/tests/test_bmp.nim @@ -36,3 +36,10 @@ block: "tests/fileformats/bmp/knight." & $bits & ".master.bmp" )) writeFile("tests/fileformats/bmp/knight." & $bits & ".bmp", encodeBmp(image)) + + +block: + let image = decodeBmp(readFile( + "tests/fileformats/bmp/rgb.24.master.bmp" + )) + writeFile("tests/fileformats/bmp/rgb.24.bmp", encodeBmp(image))