如何将 RGBA 图像复制到 Windows' 剪贴板
How to copy an RGBA image to Windows' Clipboard
如何将 32 位(每像素)RGBA 图像复制到 Windows' 剪贴板?经过大量试验后,我已经达到了这个功能,但我的图像数据根本没有“粘贴”。它也没有出现在剪贴板的历史记录中。
稍微编辑它以使用 CF_DIB
和 BITMAPINFOHEADER
header 已在该历史记录中生成一个“复制”条目,并在粘贴时生成正确大小的图像,尽管粘贴了png
在 CF_DIB
的背后导致程序以非常有趣和 non-benign 的方式出现故障。
我的目标是将带有 alpha 通道的图像复制到剪贴板,并且在 hand-off 期间不让颜色与此 alpha 相乘。我做错了什么..?
bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
// this section is my code for creating a png file
StreamWrite stream = StreamWrite::asBufferCreate();
in.savePng(stream);
uint64 bufSize = 0;
char * buf = stream._takeBuffer(bufSize, false);
// "buf" <-- contains the PNG payload
// "bufSize" <-- is the size of this payload
// beyond this point, it's just standard windows' stuff that doesn't rely on my code
BITMAPV5HEADER header;
header.bV5Size = sizeof(BITMAPV5HEADER);
header.bV5Width = in.getX(); // <-- size of the bitmap in pixels, width and height
header.bV5Height = in.getY();
header.bV5Planes = 1;
header.bV5BitCount = 0;
header.bV5Compression = BI_PNG;
header.bV5SizeImage = bufSize;
header.bV5XPelsPerMeter = 0;
header.bV5YPelsPerMeter = 0;
header.bV5ClrUsed = 0;
header.bV5ClrImportant = 0;
header.bV5RedMask = 0xFF000000;
header.bV5GreenMask = 0x00FF0000;
header.bV5BlueMask = 0x0000FF00;
header.bV5AlphaMask = 0x000000FF;
header.bV5CSType = LCS_sRGB;
header.bV5Endpoints; // ignored
header.bV5GammaRed = 0;
header.bV5GammaGreen = 0;
header.bV5GammaBlue = 0;
header.bV5Intent = 0;
header.bV5ProfileData = 0;
header.bV5ProfileSize = 0;
header.bV5Reserved = 0;
HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER) + bufSize);
if (gift == NULL)
return false;
HWND win = window.getWindowHandle();
if (!OpenClipboard(win)) {
GlobalFree(gift);
return false;
}
EmptyClipboard();
void * giftLocked = GlobalLock(gift);
if (giftLocked) {
memcpy(giftLocked, &header, sizeof(BITMAPV5HEADER));
memcpy((char*)giftLocked + sizeof(BITMAPV5HEADER), buf, bufSize);
}
GlobalUnlock(gift);
SetClipboardData(CF_DIBV5, gift);
CloseClipboard();
return true;
}
至少根据我的经验,尝试使用 BITMAPV5HEADER
传输 png 数据几乎是完全失败的,除非您基本上计划将其严格用作内部格式。
一种至少对相当数量的应用程序有效的策略是注册 PNG
剪贴板格式,并将 PNG 文件的内容放入剪贴板(没有其他 header).代码看起来像这样:
bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
// this section is my code for creating a png file
StreamWrite stream = StreamWrite::asBufferCreate();
in.savePng(stream);
uint64 bufSize = 0;
char * buf = stream._takeBuffer(bufSize, false);
// "buf" <-- contains the PNG payload
// "bufSize" <-- is the size of this payload
HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, bufSize);
if (gift == NULL)
return false;
HWND win = window.getWindowHandle();
if (!OpenClipboard(win)) {
GlobalFree(gift);
return false;
}
EmptyClipboard();
auto fmt = RegisterClipboardFormat("PNG"); // or `L"PNG", as applicable
void * giftLocked = GlobalLock(gift);
if (giftLocked) {
memcpy((char*)giftLocked, buf, bufSize);
}
GlobalUnlock(gift);
SetClipboardData(fmt, gift);
CloseClipboard();
return true;
}
我使用过这样的代码,并成功地将内容粘贴到至少 LibreOffice Write 和 Calc、MS Word 和 Paint.Net 的最新版本中。
如果您告诉它复制位图,这也是 Chrome(例如)将作为第一种(首选)格式生成的格式。
另一方面,FireFox 生成了一大堆格式,但不是这个。它会产生一个 CF_DIBV5,但至少如果没记错的话,它有 pre-multiplied alpha(或者它可能完全失去 alpha——我不太记得了。不会像你那样保留它'无论如何我都想要)。
Gimp 将接受 32 位 RGB 格式 DIB,在 left-over 字节中带有 alpha,并使用该 alpha。无论好坏,据我所知,这是关于 只有 的东西,它可以将某些东西粘贴到 Gimp 中并保留其 alpha(不是 pre-multiplied) .
备注
随着版本的更新,它们支持的格式可能会发生变化,所以即使(例如)我上次尝试时 PNG 不能与 Gimp 一起使用,但现在可能可以了。
您可以将相同的数据以不同的格式添加到剪贴板。您希望从“最佳”格式(最忠实地保留数据的格式)开始,然后逐步降低到最差的格式。所以当你做一个副本时,你可能想要做 PNG,然后是带有 alpha 通道的 RGB,然后是 CF_BITMAP(这将是 pre-multiply alpha,但总比没有好)。
如何将 32 位(每像素)RGBA 图像复制到 Windows' 剪贴板?经过大量试验后,我已经达到了这个功能,但我的图像数据根本没有“粘贴”。它也没有出现在剪贴板的历史记录中。
稍微编辑它以使用 CF_DIB
和 BITMAPINFOHEADER
header 已在该历史记录中生成一个“复制”条目,并在粘贴时生成正确大小的图像,尽管粘贴了png
在 CF_DIB
的背后导致程序以非常有趣和 non-benign 的方式出现故障。
我的目标是将带有 alpha 通道的图像复制到剪贴板,并且在 hand-off 期间不让颜色与此 alpha 相乘。我做错了什么..?
bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
// this section is my code for creating a png file
StreamWrite stream = StreamWrite::asBufferCreate();
in.savePng(stream);
uint64 bufSize = 0;
char * buf = stream._takeBuffer(bufSize, false);
// "buf" <-- contains the PNG payload
// "bufSize" <-- is the size of this payload
// beyond this point, it's just standard windows' stuff that doesn't rely on my code
BITMAPV5HEADER header;
header.bV5Size = sizeof(BITMAPV5HEADER);
header.bV5Width = in.getX(); // <-- size of the bitmap in pixels, width and height
header.bV5Height = in.getY();
header.bV5Planes = 1;
header.bV5BitCount = 0;
header.bV5Compression = BI_PNG;
header.bV5SizeImage = bufSize;
header.bV5XPelsPerMeter = 0;
header.bV5YPelsPerMeter = 0;
header.bV5ClrUsed = 0;
header.bV5ClrImportant = 0;
header.bV5RedMask = 0xFF000000;
header.bV5GreenMask = 0x00FF0000;
header.bV5BlueMask = 0x0000FF00;
header.bV5AlphaMask = 0x000000FF;
header.bV5CSType = LCS_sRGB;
header.bV5Endpoints; // ignored
header.bV5GammaRed = 0;
header.bV5GammaGreen = 0;
header.bV5GammaBlue = 0;
header.bV5Intent = 0;
header.bV5ProfileData = 0;
header.bV5ProfileSize = 0;
header.bV5Reserved = 0;
HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER) + bufSize);
if (gift == NULL)
return false;
HWND win = window.getWindowHandle();
if (!OpenClipboard(win)) {
GlobalFree(gift);
return false;
}
EmptyClipboard();
void * giftLocked = GlobalLock(gift);
if (giftLocked) {
memcpy(giftLocked, &header, sizeof(BITMAPV5HEADER));
memcpy((char*)giftLocked + sizeof(BITMAPV5HEADER), buf, bufSize);
}
GlobalUnlock(gift);
SetClipboardData(CF_DIBV5, gift);
CloseClipboard();
return true;
}
至少根据我的经验,尝试使用 BITMAPV5HEADER
传输 png 数据几乎是完全失败的,除非您基本上计划将其严格用作内部格式。
一种至少对相当数量的应用程序有效的策略是注册 PNG
剪贴板格式,并将 PNG 文件的内容放入剪贴板(没有其他 header).代码看起来像这样:
bool copyBitmapIntoClipboard(Window & window, const Bitmap & in) {
// this section is my code for creating a png file
StreamWrite stream = StreamWrite::asBufferCreate();
in.savePng(stream);
uint64 bufSize = 0;
char * buf = stream._takeBuffer(bufSize, false);
// "buf" <-- contains the PNG payload
// "bufSize" <-- is the size of this payload
HGLOBAL gift = GlobalAlloc(GMEM_MOVEABLE, bufSize);
if (gift == NULL)
return false;
HWND win = window.getWindowHandle();
if (!OpenClipboard(win)) {
GlobalFree(gift);
return false;
}
EmptyClipboard();
auto fmt = RegisterClipboardFormat("PNG"); // or `L"PNG", as applicable
void * giftLocked = GlobalLock(gift);
if (giftLocked) {
memcpy((char*)giftLocked, buf, bufSize);
}
GlobalUnlock(gift);
SetClipboardData(fmt, gift);
CloseClipboard();
return true;
}
我使用过这样的代码,并成功地将内容粘贴到至少 LibreOffice Write 和 Calc、MS Word 和 Paint.Net 的最新版本中。
如果您告诉它复制位图,这也是 Chrome(例如)将作为第一种(首选)格式生成的格式。
另一方面,FireFox 生成了一大堆格式,但不是这个。它会产生一个 CF_DIBV5,但至少如果没记错的话,它有 pre-multiplied alpha(或者它可能完全失去 alpha——我不太记得了。不会像你那样保留它'无论如何我都想要)。
Gimp 将接受 32 位 RGB 格式 DIB,在 left-over 字节中带有 alpha,并使用该 alpha。无论好坏,据我所知,这是关于 只有 的东西,它可以将某些东西粘贴到 Gimp 中并保留其 alpha(不是 pre-multiplied) .
备注
随着版本的更新,它们支持的格式可能会发生变化,所以即使(例如)我上次尝试时 PNG 不能与 Gimp 一起使用,但现在可能可以了。
您可以将相同的数据以不同的格式添加到剪贴板。您希望从“最佳”格式(最忠实地保留数据的格式)开始,然后逐步降低到最差的格式。所以当你做一个副本时,你可能想要做 PNG,然后是带有 alpha 通道的 RGB,然后是 CF_BITMAP(这将是 pre-multiply alpha,但总比没有好)。