I am writing an extremely small C++ program to help me animate sprites. I’d like it to take data I copy to the clipboard from photoshop, manipulate it in my program, then overwrite the clipboard with the transform.
The problem though is that I’m not sure how to read the initial clipboard from photoshop.
I can load the clipboard with GetClipboardData(CF_DIB)
, and get a valid handle, but I’ve no idea how to use that handle. I’ve tried using SFML’s Image::LoadFromMemory(handle, GlobalSize(handle))
which is able to load bitmap files from memory, but that doesn’t seem to work.
Will I need to actually parse the entire format? What format structure would I be looking at in that case? Would there perhaps be any way I could quickly mangle the data so it might look like a bitmap file? Could it be easier/possible to simply save it to file using the windows API? (I could then load that file with SFML to edit, that way)
It’s just a quick and dirty tool for myself to save a lot of grunt work in photoshop, so efficiency or robustness aren’t important at all.
2
Answers
Learn the bitmap structure from Wikipedia and then write it out to a file and then write out the pixels..
I’ve tested the below with Paint on Windows 8.1. I opened an image with paint and then pressed Ctrl + C to copy to the clipboard.. then I ran the following code and it copied the clipboard image to the desktop:
I wasn’t going to post an answer initially, after all you already have a good enough answer. But I guess I was coerced into doing so anyway, besides, this is the primary question that pops when you search for
GetClipboardData CF_DIB
, so might as well try to present a more complete solution.Unfortunately, clipboard formats are a minefield. And GDI bitmaps are an even bigger minefield.
CF_DIB
gives you a "packed DIB", you do need to parse it to some extent if you actually want to do anything meaningful with it. This is the layout (pseudo code):The total size of the structure is given by
GlobalSize()
. The crucial bit of information that is required for any further processing is the offset, in bytes, from the start of theBITMAPINFO
structure to the start of the pixel data array. If the optional bitmasks and the color table are absent, this offset is constant40
(sizeof(BITMAPINFOHEADER)
). Whether this is the case depends entirely on how an application has put the bitmap into the clipboard. Most applications do this because it’s the simplest way.This code calculates that offset:
Below is a program that demonstrates several things you can do with this offset:
sf::Image::loadFromMemory
)HBITMAP
(so it can be used in GDI)HBITMAP
I intend this code to be mostly production-ready because I need it for myself, if anyone finds a problem I’d be happy to hear about it.
Some additional notes for reference:
%zu
doesn’t work in older CRTs, who knewCF_DIB
really wanted to say is "it’s a packed DIB". There is no official guarantee that it will be a plainBITMAPINFOHEADER
, i.e.biSize == 40
though, although it is likely that this is the case.BITMAPFINO
documentation explains that the structure is really variable in length, and thatBITMAPINFOHEADER.biClrUsed
needs to be taken into account, but it fails to mentionBI_BITFIELDS
.BITMAPINFOHEADER
has more details on this, but fails to mentionBI_ALPHABITFIELDS
or the fact that the bitmasks are only present if the polymorphicBITMAPINFOHEADER
struct is actually a plainBITMAPINFOHEADER
(i.e.biSize == 40
). Later versions, likeBITMAPV5HEADER
, include the bitmasks unconditionally.GetDIBPixelDataOffset
(obviously I can’t post that verbatim here). It does not assume thatbiSize == 40
. Newer versions of Paint useCOleServerItem
for clipboard handling.length
parameter comes fromGlobalSize
. Note that it also does not assume thatbiSize == 40
.SetClipboardData
is called withCF_BITMAP
, it requires a DDB (it will silently fail if you pass it aHBITMAP
that is really a DIB). ACF_BITMAP
that is implicitly converted to a DIB usesBI_BITFIELDS
, this also applies to screenshots (at least if the original DDB was compatible with the screen DC).CF_BITMAP
, that bitmap is not copied (at least not initially). If any program manipulates it, all programs accessing the clipboard will see the manipulated bitmap. However, as soon as any one application ever requests it as aCF_DIB
, Windows applies a bunch of magic, and that is no longer true, the bitmap is now a copy. This does not apply to bitmaps that were put in the clipboard asCF_DIB
, those immediately become immune to manipulations by other programs.CF_DIB
seems to have fewer unpleasant implications and surprises, and also seems to be used by most applications. While you could try to preserve the original bitmap’s format when putting it in the clipboard, I chose to use a fixed catch-all format for outgoing data because it’s already crazy enough.SetClipboardData
implies thatCF_DIB
doesn’t work with Windows Store apps, but I was unable to confirm that claim. Then again, the paragraph about a NULL owner is also incorrect.PutBitmapInClipboard_AsDIB
andPutBitmapInClipboard_From32bppTopDownRGBAData
do preserve the alpha channel if possible, although GDI drawing functions as demonstrated don’t support alpha and will destroy it (SFML will handle it just fine). Putting the alpha channel in the MSB and usingBI_RGB
seems to be the de-facto standard for storing alpha in DIBs.