Skip to content

Commit 61368e2

Browse files
authored
Merge pull request #501 from treeform/guzba
encode dibs for windows clipboard
2 parents 5f4c15c + 29b84d7 commit 61368e2

5 files changed

Lines changed: 48 additions & 34 deletions

File tree

src/pixie/fileformats/bmp.nim

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import bitops, chroma, flatty/binny, pixie/common, pixie/images
66
# https://stackoverflow.com/questions/61788908/windows-clipboard-getclipboarddata-for-cf-dibv5-causes-the-image-on-the-clip
77
# https://stackoverflow.com/questions/44177115/copying-from-and-to-clipboard-loses-image-transparency/46424800#46424800
88

9-
const bmpSignature* = "BM"
9+
const
10+
bmpSignature* = "BM"
11+
LCS_sRGB = 0x73524742
1012

1113
template failInvalid() =
1214
raise newException(PixieError, "Invalid BMP buffer, unable to load")
@@ -17,8 +19,7 @@ proc colorMaskShift(color, mask: uint32): uint8 {.inline.} =
1719
proc decodeDib*(
1820
data: pointer, len: int, lpBitmapInfo = false
1921
): Image {.raises: [PixieError].} =
20-
## Decodes DIB data into an Image.
21-
22+
## Decodes DIB data into an image.
2223
if len < 40:
2324
failInvalid()
2425

@@ -216,8 +217,7 @@ proc decodeDib*(
216217
result.flipVertical()
217218

218219
proc decodeBmp*(data: string): Image {.raises: [PixieError].} =
219-
## Decodes bitmap data into an Image.
220-
220+
## Decodes bitmap data into an image.
221221
if data.len < 14:
222222
failInvalid()
223223

@@ -231,7 +231,6 @@ proc decodeBmpDimensions*(
231231
data: string
232232
): ImageDimensions {.raises: [PixieError].} =
233233
## Decodes the BMP dimensions.
234-
235234
if data.len < 26:
236235
failInvalid()
237236

@@ -242,42 +241,47 @@ proc decodeBmpDimensions*(
242241
result.width = data.readInt32(18).int
243242
result.height = abs(data.readInt32(22)).int
244243

245-
proc encodeBmp*(image: Image): string {.raises: [].} =
246-
## Encodes an image into the BMP file format.
244+
proc encodeDib*(image: Image): string {.raises: [].} =
245+
## Encodes an image into a DIB.
247246

248-
# BMP Header
249-
result.add("BM") # The header field used to identify the BMP
250-
result.addUint32(0) # The size of the BMP file in bytes.
251-
result.addUint16(0) # Reserved.
252-
result.addUint16(0) # Reserved.
253-
result.addUint32(122) # The offset to the pixel array.
254-
255-
# DIB Header
256-
result.addUint32(108) # Size of this header
257-
result.addInt32(image.width.int32) # Signed integer.
258-
result.addInt32(image.height.int32) # Signed integer.
259-
result.addUint16(1) # Must be 1 (color planes).
260-
result.addUint16(32) # Bits per pixels, only support RGBA.
247+
# BITMAPINFO containing BITMAPV5HEADER
248+
result.addUint32(124) # Size of this header
249+
result.addInt32(image.width.int32) # Signed integer
250+
result.addInt32(image.height.int32) # Signed integer
251+
result.addUint16(1) # Must be 1 (color planes)
252+
result.addUint16(32) # Bits per pixels, only support RGBA
261253
result.addUint32(3) # BI_BITFIELDS, no pixel array compression used
262254
result.addUint32(32) # Size of the raw bitmap data (including padding)
263255
result.addUint32(2835) # Print resolution of the image
264256
result.addUint32(2835) # Print resolution of the image
265257
result.addUint32(0) # Number of colors in the palette
266258
result.addUint32(0) # 0 means all colors are important
267-
result.addUint32(uint32(0x000000FF)) # Red channel.
268-
result.addUint32(uint32(0x0000FF00)) # Green channel.
269-
result.addUint32(uint32(0x00FF0000)) # Blue channel.
270-
result.addUint32(uint32(0xFF000000)) # Alpha channel.
271-
result.add("Win ") # little-endian.
272-
for i in 0 ..< 48:
273-
result.addUint8(0) # Unused
259+
result.addUint32(uint32(0x000000FF)) # Red channel
260+
result.addUint32(uint32(0x0000FF00)) # Green channel
261+
result.addUint32(uint32(0x00FF0000)) # Blue channel
262+
result.addUint32(uint32(0xFF000000)) # Alpha channel
263+
result.addUint32(LCS_sRGB) # Color space
264+
result.setLen(result.len + 64) # Unused
265+
result.addUint32(0) # BITMAPINFO bmiColors 0
266+
result.addUint32(0) # BITMAPINFO bmiColors 1
267+
result.addUint32(0) # BITMAPINFO bmiColors 2
274268

275269
for y in 0 ..< image.height:
276270
for x in 0 ..< image.width:
277271
let rgba = image[x, image.height - y - 1].rgba()
278-
result.addUint8(rgba.r)
279-
result.addUint8(rgba.g)
280-
result.addUint8(rgba.b)
281-
result.addUint8(rgba.a)
272+
result.addUint32(cast[uint32](rgba))
273+
274+
proc encodeBmp*(image: Image): string {.raises: [].} =
275+
## Encodes an image into the BMP file format.
276+
277+
# BMP Header
278+
result.add("BM") # The header field used to identify the BMP
279+
result.addUint32(0) # The size of the BMP file in bytes
280+
result.addUint16(0) # Reserved
281+
result.addUint16(0) # Reserved
282+
result.addUint32(14 + 12 + 124) # The offset to the pixel array
283+
284+
# DIB
285+
result.add(encodeDib(image))
282286

283-
result.writeUInt32(2, result.len.uint32)
287+
result.writeUint32(2, result.len.uint32)
28 Bytes
Binary file not shown.
28 Bytes
Binary file not shown.

tests/fileformats/bmp/rgb.24.bmp

28 Bytes
Binary file not shown.

tests/test_bmp.nim

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import os, pixie/fileformats/bmp
1+
import os, pixie, pixie/fileformats/bmp
22

33
# block:
44
# var image = newImage(4, 2)
@@ -52,3 +52,13 @@ block:
5252
#image.writeFile(file.replace("bmpsuite", "output") & ".png")
5353
doAssert image.width == dimensions.width
5454
doAssert image.height == dimensions.height
55+
56+
block:
57+
let image = newImage(100, 100)
58+
image.fill(color(1, 0, 0, 1))
59+
60+
let
61+
encoded = encodeDib(image)
62+
decoded = decodeDib(encoded.cstring, encoded.len, true)
63+
64+
doAssert image.data == decoded.data

0 commit comments

Comments
 (0)