From 29b84d7d8e95f0fd6e70c45173d5b92575cb8990 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 20 Aug 2022 02:37:54 -0500 Subject: [PATCH] encode dibs for windows clipboard --- src/pixie/fileformats/bmp.nim | 70 +++++++++++++++------------- tests/fileformats/bmp/knight.24.bmp | Bin 890 -> 918 bytes tests/fileformats/bmp/knight.32.bmp | Bin 890 -> 918 bytes tests/fileformats/bmp/rgb.24.bmp | Bin 921722 -> 921750 bytes tests/test_bmp.nim | 12 ++++- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/pixie/fileformats/bmp.nim b/src/pixie/fileformats/bmp.nim index 02294cf..86f23c6 100644 --- a/src/pixie/fileformats/bmp.nim +++ b/src/pixie/fileformats/bmp.nim @@ -6,7 +6,9 @@ import bitops, chroma, flatty/binny, pixie/common, pixie/images # https://stackoverflow.com/questions/61788908/windows-clipboard-getclipboarddata-for-cf-dibv5-causes-the-image-on-the-clip # https://stackoverflow.com/questions/44177115/copying-from-and-to-clipboard-loses-image-transparency/46424800#46424800 -const bmpSignature* = "BM" +const + bmpSignature* = "BM" + LCS_sRGB = 0x73524742 template failInvalid() = raise newException(PixieError, "Invalid BMP buffer, unable to load") @@ -17,8 +19,7 @@ proc colorMaskShift(color, mask: uint32): uint8 {.inline.} = proc decodeDib*( data: pointer, len: int, lpBitmapInfo = false ): Image {.raises: [PixieError].} = - ## Decodes DIB data into an Image. - + ## Decodes DIB data into an image. if len < 40: failInvalid() @@ -216,8 +217,7 @@ proc decodeDib*( result.flipVertical() proc decodeBmp*(data: string): Image {.raises: [PixieError].} = - ## Decodes bitmap data into an Image. - + ## Decodes bitmap data into an image. if data.len < 14: failInvalid() @@ -231,7 +231,6 @@ proc decodeBmpDimensions*( data: string ): ImageDimensions {.raises: [PixieError].} = ## Decodes the BMP dimensions. - if data.len < 26: failInvalid() @@ -242,42 +241,47 @@ proc decodeBmpDimensions*( result.width = data.readInt32(18).int result.height = abs(data.readInt32(22)).int -proc encodeBmp*(image: Image): string {.raises: [].} = - ## Encodes an image into the BMP file format. +proc encodeDib*(image: Image): string {.raises: [].} = + ## Encodes an image into a DIB. - # 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. + # BITMAPINFO containing BITMAPV5HEADER + result.addUint32(124) # 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 + 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.addUint32(LCS_sRGB) # Color space + result.setLen(result.len + 64) # Unused + result.addUint32(0) # BITMAPINFO bmiColors 0 + result.addUint32(0) # BITMAPINFO bmiColors 1 + result.addUint32(0) # BITMAPINFO bmiColors 2 for y in 0 ..< image.height: for x in 0 ..< image.width: let rgba = image[x, image.height - y - 1].rgba() - result.addUint8(rgba.r) - result.addUint8(rgba.g) - result.addUint8(rgba.b) - result.addUint8(rgba.a) + result.addUint32(cast[uint32](rgba)) - result.writeUInt32(2, result.len.uint32) +proc encodeBmp*(image: Image): string {.raises: [].} = + ## Encodes an image into the BMP file format. + + # 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(14 + 12 + 124) # The offset to the pixel array + + # DIB + result.add(encodeDib(image)) + + result.writeUint32(2, result.len.uint32) diff --git a/tests/fileformats/bmp/knight.24.bmp b/tests/fileformats/bmp/knight.24.bmp index ed23acc5f88fcb2c22cadb4fd14485fd826b817c..1bcaca38f765dcb01b1797ad01f45808c41b578e 100644 GIT binary patch delta 40 rcmeyxHjSO%$#)tv0~kyLk~I_g%~_n>gNi2xYEDd$nwY@3u!&@$+wD`0Su~uWX?o>bC&STJcWsY1`DS#0|0``2owMS diff --git a/tests/fileformats/bmp/knight.32.bmp b/tests/fileformats/bmp/knight.32.bmp index ebce0d18b205344051665f5613b9d690da4751a9..f5a3336c5848ce7d4de090f9f310bae6f5a8f250 100644 GIT binary patch delta 40 rcmeyxHjSO%$#)tv0~kyLk~I_g%~_n>gNi2xYEDd$nwY@3u!&@$+wD`0Su~uWX?o>bC&STJcWsY1`DS#0|0``2owMS diff --git a/tests/fileformats/bmp/rgb.24.bmp b/tests/fileformats/bmp/rgb.24.bmp index 02b40352d3847d531bb0d16772273bcccc26ac8f..3bca2e499e38036189f997065d42c2edec964be5 100644 GIT binary patch delta 85 zcmeyhz+&1$3w|fxX##uqqUZ^wU(>3mbrCFQGx`4V0|4L$8vg(Q diff --git a/tests/test_bmp.nim b/tests/test_bmp.nim index 157d6b6..41b583b 100644 --- a/tests/test_bmp.nim +++ b/tests/test_bmp.nim @@ -1,4 +1,4 @@ -import os, pixie/fileformats/bmp +import os, pixie, pixie/fileformats/bmp # block: # var image = newImage(4, 2) @@ -52,3 +52,13 @@ block: #image.writeFile(file.replace("bmpsuite", "output") & ".png") doAssert image.width == dimensions.width doAssert image.height == dimensions.height + +block: + let image = newImage(100, 100) + image.fill(color(1, 0, 0, 1)) + + let + encoded = encodeDib(image) + decoded = decodeDib(encoded.cstring, encoded.len, true) + + doAssert image.data == decoded.data