Add bmp file format.

This commit is contained in:
treeform 2020-11-20 09:14:33 -08:00
parent cb50a1cb86
commit f8ea6c3717
5 changed files with 103 additions and 1 deletions

View file

@ -0,0 +1,67 @@
import ../images, flatty/binny, flatty/hexPrint, chroma, sequtils
# See: https://en.wikipedia.org/wiki/BMP_file_format
proc decodeBmp*(data: string): Image =
## Decodes bitmap data into an Image.
# BMP Header
doAssert data[0..1] == "BM"
let
width = data.readInt32(0x12).int
height = data.readInt32(0x16).int
# TODO: Handle masks.
var
offset = data.readUInt32(0xA).int
result = newImage(width, height)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
var rgba: ColorRGBA
rgba.r = data.readUint8(offset+0)
rgba.g = data.readUint8(offset+1)
rgba.b = data.readUint8(offset+2)
rgba.a = data.readUint8(offset+3)
result[x, result.height - y - 1] = rgba
offset += 4
proc encodeBmp*(image: Image): string =
## Encodes an image into bitmap data.
# BMP Header
result.add("BM") # The header field used to identify the BMP
result.addUint32(0) # The size of the BMP file in bytes.
result.addUint16(0) # Reserved.
result.addUint16(0) # Reserved.
result.addUint32(122) # The offset to the pixel array.
# DIB Header
result.addUint32(108) # Size of this header
result.addInt32(image.width.int32) # Signed integer.
result.addInt32(image.height.int32) # Signed integer.
result.addUint16(1) # Must be 1 (color planes).
result.addUint16(32) # Bits per pixels, only support RGBA.
result.addUint32(3) # BI_BITFIELDS, no pixel array compression used
result.addUint32(32) # Size of the raw bitmap data (including padding)
result.addUint32(2835) # Print resolution of the image
result.addUint32(2835) # Print resolution of the image
result.addUint32(0) # Number of colors in the palette
result.addUint32(0) # 0 means all colors are important
result.addUint32(uint32(0x000000FF)) # Red channel.
result.addUint32(uint32(0x0000FF00)) # Green channel.
result.addUint32(uint32(0x00FF0000)) # Blue channel.
result.addUint32(uint32(0xFF000000)) # Alpha channel.
result.add("Win ") # little-endian.
for i in 0 ..< 48:
result.addUint8(0) # Unused
for y in 0 ..< image.height:
for x in 0 ..< image.width:
let rgba = image[x, image.height - y - 1]
result.addUint8(rgba.r)
result.addUint8(rgba.g)
result.addUint8(rgba.b)
result.addUint8(rgba.a)
result.writeUInt32(2, result.len.uint32)

View file

@ -296,4 +296,8 @@ proc draw*(
## Thoughts
## single draw function that takes a matrix
## if matrix is simple integer translation -> fast pass
## if blend mode is copy -> even faster path
## if matrix is a simple flip -> fast path
## if blend mode is copy -> fast path
##
## Helper function that takes x,y
## Helper function that takes x,y and rotation.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

31
tests/testbmp.nim Normal file
View file

@ -0,0 +1,31 @@
import pixie, pixie/fileformats/bmp, chroma, flatty/hexPrint
block:
var image = newImage(4, 2)
image[0, 0] = rgba(0, 0, 255, 255)
image[1, 0] = rgba(0, 255, 0, 255)
image[2, 0] = rgba(255, 0, 0, 255)
image[3, 0] = rgba(255, 255, 255, 255)
image[0, 1] = rgba(0, 0, 255, 127)
image[1, 1] = rgba(0, 255, 0, 127)
image[2, 1] = rgba(255, 0, 0, 127)
image[3, 1] = rgba(255, 255, 255, 127)
writeFile("images/bmp/test4x2.bmp", encodeBmp(image))
var image2 = decodeBmp(encodeBmp(image))
doAssert image2.width == image.width
doAssert image2.height == image.height
doAssert image2.data == image.data
block:
var image = newImage(16, 16)
image.fill(rgba(255, 0, 0, 127))
writeFile("images/bmp/test16x16.bmp", encodeBmp(image))
var image2 = decodeBmp(encodeBmp(image))
doAssert image2.width == image.width
doAssert image2.height == image.height
doAssert image2.data == image.data