Add bmp file format.
This commit is contained in:
parent
cb50a1cb86
commit
f8ea6c3717
5 changed files with 103 additions and 1 deletions
67
src/pixie/fileformats/bmp.nim
Normal file
67
src/pixie/fileformats/bmp.nim
Normal 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)
|
|
@ -296,4 +296,8 @@ proc draw*(
|
||||||
## Thoughts
|
## Thoughts
|
||||||
## single draw function that takes a matrix
|
## single draw function that takes a matrix
|
||||||
## if matrix is simple integer translation -> fast pass
|
## 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.
|
||||||
|
|
BIN
tests/images/bmp/test16x16.bmp
Normal file
BIN
tests/images/bmp/test16x16.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/bmp/test4x2.bmp
Normal file
BIN
tests/images/bmp/test4x2.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 B |
31
tests/testbmp.nim
Normal file
31
tests/testbmp.nim
Normal 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
|
Loading…
Reference in a new issue