我正在编写一个非常小的C ++程序来帮助我激活精灵。我想把它从photoshop复制到剪贴板,在我的程序中操作它,然后用变换覆盖剪贴板。
但问题是我不确定如何从photoshop读取初始剪贴板。
我可以使用GetClipboardData(CF_DIB)
加载剪贴板,并获得有效的句柄,但我不知道如何使用该句柄。我尝试过使用能够从内存中加载位图文件的SFML Image::LoadFromMemory(handle, GlobalSize(handle))
,但这似乎不起作用。
我是否需要实际解析整个格式?在这种情况下,我会看到什么样的格式结构?是否有可能以任何方式快速破坏数据,使其看起来像位图文件?使用Windows API简单地将其保存到文件是否更容易/可能? (然后我可以用SFML加载该文件进行编辑,那样)
对于我来说,这只是一个快速而又脏的工具,可以在photoshop中保存大量繁琐的工作,因此效率或稳健性并不重要。
答案 0 :(得分:5)
从维基百科学习位图结构,然后将其写入文件,然后写出像素。
我在Windows 8.1上使用Paint测试了下面的内容。我用油漆打开了一个图像,然后按下Ctrl + C复制到剪贴板..然后我运行了以下代码并将剪贴板图像复制到桌面:
#include <iostream>
#include <fstream>
#include <windows.h>
typedef struct
{
std::uint32_t biSize;
std::int32_t biWidth;
std::int32_t biHeight;
std::uint16_t biPlanes;
std::uint16_t biBitCount;
std::uint32_t biCompression;
std::uint32_t biSizeImage;
std::int32_t biXPelsPerMeter;
std::int32_t biYPelsPerMeter;
std::uint32_t biClrUsed;
std::uint32_t biClrImportant;
} DIB;
typedef struct
{
std::uint16_t type;
std::uint32_t bfSize;
std::uint32_t reserved;
std::uint32_t offset;
} HEADER;
typedef struct
{
HEADER header;
DIB dib;
} BMP;
int main()
{
std::cout<<"Format Bitmap: "<<IsClipboardFormatAvailable(CF_BITMAP)<<"\n";
std::cout<<"Format DIB: "<<IsClipboardFormatAvailable(CF_DIB)<<"\n";
std::cout<<"Format DIBv5: "<<IsClipboardFormatAvailable(CF_DIBV5)<<"\n";
if (IsClipboardFormatAvailable(CF_BITMAP) || IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_DIBV5))
{
if (OpenClipboard(NULL))
{
HANDLE hClipboard = GetClipboardData(CF_DIB);
if (!hClipboard)
{
hClipboard = GetClipboardData(CF_DIBV5);
}
if (hClipboard != NULL && hClipboard != INVALID_HANDLE_VALUE)
{
void* dib = GlobalLock(hClipboard);
if (dib)
{
DIB *info = reinterpret_cast<DIB*>(dib);
BMP bmp = {0};
bmp.header.type = 0x4D42;
bmp.header.offset = 54;
bmp.header.bfSize = info->biSizeImage + bmp.header.offset;
bmp.dib = *info;
std::cout<<"Type: "<<std::hex<<bmp.header.type<<std::dec<<"\n";
std::cout<<"bfSize: "<<bmp.header.bfSize<<"\n";
std::cout<<"Reserved: "<<bmp.header.reserved<<"\n";
std::cout<<"Offset: "<<bmp.header.offset<<"\n";
std::cout<<"biSize: "<<bmp.dib.biSize<<"\n";
std::cout<<"Width: "<<bmp.dib.biWidth<<"\n";
std::cout<<"Height: "<<bmp.dib.biHeight<<"\n";
std::cout<<"Planes: "<<bmp.dib.biPlanes<<"\n";
std::cout<<"Bits: "<<bmp.dib.biBitCount<<"\n";
std::cout<<"Compression: "<<bmp.dib.biCompression<<"\n";
std::cout<<"Size: "<<bmp.dib.biSizeImage<<"\n";
std::cout<<"X-res: "<<bmp.dib.biXPelsPerMeter<<"\n";
std::cout<<"Y-res: "<<bmp.dib.biYPelsPerMeter<<"\n";
std::cout<<"ClrUsed: "<<bmp.dib.biClrUsed<<"\n";
std::cout<<"ClrImportant: "<<bmp.dib.biClrImportant<<"\n";
std::ofstream file("C:/Users/Brandon/Desktop/Test.bmp", std::ios::out | std::ios::binary);
if (file)
{
file.write(reinterpret_cast<char*>(&bmp.header.type), sizeof(bmp.header.type));
file.write(reinterpret_cast<char*>(&bmp.header.bfSize), sizeof(bmp.header.bfSize));
file.write(reinterpret_cast<char*>(&bmp.header.reserved), sizeof(bmp.header.reserved));
file.write(reinterpret_cast<char*>(&bmp.header.offset), sizeof(bmp.header.offset));
file.write(reinterpret_cast<char*>(&bmp.dib.biSize), sizeof(bmp.dib.biSize));
file.write(reinterpret_cast<char*>(&bmp.dib.biWidth), sizeof(bmp.dib.biWidth));
file.write(reinterpret_cast<char*>(&bmp.dib.biHeight), sizeof(bmp.dib.biHeight));
file.write(reinterpret_cast<char*>(&bmp.dib.biPlanes), sizeof(bmp.dib.biPlanes));
file.write(reinterpret_cast<char*>(&bmp.dib.biBitCount), sizeof(bmp.dib.biBitCount));
file.write(reinterpret_cast<char*>(&bmp.dib.biCompression), sizeof(bmp.dib.biCompression));
file.write(reinterpret_cast<char*>(&bmp.dib.biSizeImage), sizeof(bmp.dib.biSizeImage));
file.write(reinterpret_cast<char*>(&bmp.dib.biXPelsPerMeter), sizeof(bmp.dib.biXPelsPerMeter));
file.write(reinterpret_cast<char*>(&bmp.dib.biYPelsPerMeter), sizeof(bmp.dib.biYPelsPerMeter));
file.write(reinterpret_cast<char*>(&bmp.dib.biClrUsed), sizeof(bmp.dib.biClrUsed));
file.write(reinterpret_cast<char*>(&bmp.dib.biClrImportant), sizeof(bmp.dib.biClrImportant));
file.write(reinterpret_cast<char*>(info + 1), bmp.dib.biSizeImage);
}
GlobalUnlock(dib);
}
}
CloseClipboard();
}
}
return 0;
}
答案 1 :(得分:1)
我最初不打算发布答案,毕竟您已经有了足够好的答案。但我想无论如何我都是被迫这样做的,此外,这是您搜索GetClipboardData CF_DIB
时弹出的主要问题,所以不妨尝试提出一个更完整的解决方案。
不幸的是,剪贴板格式是一个雷区。 GDI 位图是一个更大的雷区。 CF_DIB
为您提供了一个“打包的 DIB”,如果您真的想用它做任何有意义的事情,您确实需要在某种程度上解析它。这是布局(伪代码):
struct PACKED_DIB {
struct BITMAPINFO {
BITMAPINFOHEADER bih; // 40 bytes
DWORD optional_RGB_bitmaks[]; // (variable size)
DWORD optional_color_table[]; // (variable size)
}
BYTE pixel_data_array[]; // (variable size)
}
结构的总大小由 GlobalSize()
给出。任何进一步处理所需的关键信息位是从 BITMAPINFO
结构的开头到像素数据数组的开头的偏移量(以字节为单位)。如果可选位掩码和颜色表不存在,则此偏移量是常数 40
(sizeof(BITMAPINFOHEADER)
)。 是否是这种情况完全取决于应用程序如何将位图放入剪贴板。大多数应用程序都这样做,因为这是最简单的方法。
此代码计算偏移量:
// Returns the offset, in bytes, from the start of the BITMAPINFO, to the start of the pixel data array, for a packed DIB.
static INT GetPixelDataOffsetForPackedDIB(const BITMAPINFOHEADER *BitmapInfoHeader)
{
INT OffsetExtra = 0;
if (BitmapInfoHeader->biSize == sizeof(BITMAPINFOHEADER) /* 40 */)
{
// This is the common BITMAPINFOHEADER type. In this case, there may be bit masks following the BITMAPINFOHEADER
// and before the actual pixel bits (does not apply if bitmap has <= 8 bpp)
if (BitmapInfoHeader->biBitCount > 8)
{
if (BitmapInfoHeader->biCompression == BI_BITFIELDS)
{
OffsetExtra += 3 * sizeof(RGBQUAD);
}
else if (BitmapInfoHeader->biCompression == 6 /* BI_ALPHABITFIELDS */)
{
// Not widely supported, but technically a valid DIB format.
// You *can* get this in the clipboard, although neither GDI nor stb_image will like it.
OffsetExtra += 4 * sizeof(RGBQUAD);
}
}
}
if (BitmapInfoHeader->biClrUsed > 0)
{
// We have no choice but to trust this value.
OffsetExtra += BitmapInfoHeader->biClrUsed * sizeof(RGBQUAD);
}
else
{
// In this case, the color table contains the maximum number for the current bit count (0 if > 8bpp)
if (BitmapInfoHeader->biBitCount <= 8)
{
// 1bpp: 2
// 4bpp: 16
// 8bpp: 256
OffsetExtra += sizeof(RGBQUAD) << BitmapInfoHeader->biBitCount;
}
}
return BitmapInfoHeader->biSize + OffsetExtra;
}
下面的程序演示了您可以使用此偏移量做的几件事:
sf::Image::loadFromMemory
) 从内存加载HBITMAP
(以便在 GDI 中使用)HBITMAP
#include <sdkddkver.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#if DEMO_SFML
#include <SFML/Graphics.hpp>
#endif
static BOOL OpenClipboard_ButTryABitHarder(HWND ClipboardOwner);
static INT GetPixelDataOffsetForPackedDIB(const BITMAPINFOHEADER *BitmapInfoHeader);
static void PutBitmapInClipboard_AsDIB(HBITMAP hBitmap);
static void PutBitmapInClipboard_From32bppTopDownRGBAData(INT Width, INT Height, const void *Data32bppRGBA);
int wmain(int argc, wchar_t *argv[])
{
if (!OpenClipboard_ButTryABitHarder(NULL))
{
// Could not open clipboard. This usually indicates that another application is permanently blocking it.
return 1;
}
HGLOBAL ClipboardDataHandle = (HGLOBAL)GetClipboardData(CF_DIB);
if (!ClipboardDataHandle)
{
// Clipboard object is not a DIB, and is not auto-convertible to DIB
CloseClipboard();
return 0;
}
BITMAPINFOHEADER *BitmapInfoHeader = (BITMAPINFOHEADER *)GlobalLock(ClipboardDataHandle);
assert(BitmapInfoHeader); // This can theoretically fail if mapping the HGLOBAL into local address space fails. Very pathological, just act as if it wasn't a bitmap in the clipboard.
SIZE_T ClipboardDataSize = GlobalSize(ClipboardDataHandle);
assert(ClipboardDataSize >= sizeof(BITMAPINFOHEADER)); // Malformed data. While older DIB formats exist (e.g. BITMAPCOREHEADER), they are not valid data for CF_DIB; it mandates a BITMAPINFO struct. If this fails, just act as if it wasn't a bitmap in the clipboard.
INT PixelDataOffset = GetPixelDataOffsetForPackedDIB(BitmapInfoHeader);
// ============================================================================================================
// ============================================================================================================
//
// Example 1: Write it to a .bmp file
//
// The clipboard contains a packed DIB, whose start address coincides with BitmapInfoHeader, and whose total size is ClipboardDataSize.
// By definition, we can jam the whole DIB memory into a BMP file as-is, except that we need to prepend a BITMAPFILEHEADER struct.
// The tricky part is that for BITMAPFILEHEADER.bfOffBits, which must be calculated using the information in BITMAPINFOHEADER.
// The BMP file layout:
// @offset 0: BITMAPFILEHEADER
// @offset 14 (sizeof(BITMAPFILEHEADER)): BITMAPINFOHEADER
// @offset 14 + BitmapInfoHeader->biSize: Optional bit masks and color table
// @offset 14 + DIBPixelDataOffset: pixel bits
// @offset 14 + ClipboardDataSize: EOF
size_t TotalBitmapFileSize = sizeof(BITMAPFILEHEADER) + ClipboardDataSize;
wprintf(L"BITMAPINFOHEADER size: %u\r\n", BitmapInfoHeader->biSize);
wprintf(L"Format: %hubpp, Compression %u\r\n", BitmapInfoHeader->biBitCount, BitmapInfoHeader->biCompression);
wprintf(L"Pixel data offset within DIB: %u\r\n", PixelDataOffset);
wprintf(L"Total DIB size: %zu\r\n", ClipboardDataSize);
wprintf(L"Total bitmap file size: %zu\r\n", TotalBitmapFileSize);
BITMAPFILEHEADER BitmapFileHeader = {};
BitmapFileHeader.bfType = 0x4D42;
BitmapFileHeader.bfSize = (DWORD)TotalBitmapFileSize; // Will fail if bitmap size is nonstandard >4GB
BitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + PixelDataOffset;
HANDLE FileHandle = CreateFileW(L"test.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (FileHandle != INVALID_HANDLE_VALUE)
{
DWORD dummy = 0;
BOOL Success = true;
Success &= WriteFile(FileHandle, &BitmapFileHeader, sizeof(BITMAPFILEHEADER), &dummy, NULL);
Success &= WriteFile(FileHandle, BitmapInfoHeader, (DWORD)ClipboardDataSize, &dummy, NULL);
Success &= CloseHandle(FileHandle);
if (Success)
{
wprintf(L"File saved.\r\n");
}
}
#if DEMO_SFML
// ============================================================================================================
// ============================================================================================================
//
// Example 2: Load it from memory in SFML
//
// SFML expects a whole bitmap file, including its BITMAPFILEHEADER, in memory.
// So this is similar to Example 1, except in memory.
BYTE *BitmapFileContents = (BYTE *)malloc(TotalBitmapFileSize);
assert(BitmapFileContents);
memcpy(BitmapFileContents, &BitmapFileHeader, sizeof(BITMAPFILEHEADER));
// Append DIB
memcpy(BitmapFileContents + sizeof(BITMAPFILEHEADER), BitmapInfoHeader, ClipboardDataSize);
sf::Image image;
image.loadFromMemory(BitmapFileContents, TotalBitmapFileSize);
// The memory can be freed once the image has been loaded in SFML.
free(BitmapFileContents);
// Manipulate it:
image.flipHorizontally();
// Put it back in the clipboard:
PutBitmapInClipboard_From32bppTopDownRGBAData(image.getSize().x, image.getSize().y, image.getPixelsPtr());
#else
// ============================================================================================================
// ============================================================================================================
//
// Example 3: Convert to HBITMAP for GDI
//
BYTE *PixelDataFromClipboard = (BYTE *)BitmapInfoHeader + PixelDataOffset;
// This will only work if the DIB format is supported by GDI. Not all formats are supported.
BYTE *PixelDataNew;
HBITMAP hBitmap = CreateDIBSection(NULL, (BITMAPINFO *)BitmapInfoHeader, DIB_RGB_COLORS, (void **)&PixelDataNew, NULL, 0);
assert(hBitmap);
// Need to copy the data from the clipboard to the new DIBSection.
BITMAP BitmapDesc = {};
GetObjectW(hBitmap, sizeof(BitmapDesc), &BitmapDesc);
SIZE_T PixelDataBytesToCopy = (SIZE_T)BitmapDesc.bmHeight * BitmapDesc.bmWidthBytes;
SIZE_T PixelDataBytesAvailable = ClipboardDataSize - PixelDataOffset;
if (PixelDataBytesAvailable < PixelDataBytesToCopy)
{
// Malformed data; doesn't contain enough pixels.
PixelDataBytesToCopy = PixelDataBytesAvailable;
}
memcpy(PixelDataNew, PixelDataFromClipboard, PixelDataBytesToCopy);
// NOTE: While it is possible to create a DIB section without copying the pixel data, in general you'd want to
// copy it anyway because the clipboard needs to be closed asap.
// Draw something on it.
PixelDataNew[7] = 0;
PixelDataNew[11] = 100;
HDC hdc = CreateCompatibleDC(NULL);
assert(hdc);
SelectObject(hdc, hBitmap);
RECT rc = { 0, 0, BitmapDesc.bmWidth / 2, BitmapDesc.bmHeight / 2 };
HBRUSH brush = CreateSolidBrush(RGB(250, 100, 0));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
DeleteDC(hdc);
// ============================================================================================================
// ============================================================================================================
//
// Copy it back to the clipboard.
//
PutBitmapInClipboard_AsDIB(hBitmap);
#endif // DEMO_SFML
GlobalUnlock(ClipboardDataHandle);
CloseClipboard();
return 0;
}
static BOOL OpenClipboard_ButTryABitHarder(HWND hWnd)
{
for (int i = 0; i < 20; ++i)
{
// This can fail if the clipboard is currently being accessed by another application.
if (OpenClipboard(hWnd)) return true;
Sleep(10);
}
return false;
}
// Returns the offset, in bytes, from the start of the BITMAPINFO, to the start of the pixel data array, for a packed DIB.
static INT GetPixelDataOffsetForPackedDIB(const BITMAPINFOHEADER *BitmapInfoHeader)
{
INT OffsetExtra = 0;
if (BitmapInfoHeader->biSize == sizeof(BITMAPINFOHEADER) /* 40 */)
{
// This is the common BITMAPINFOHEADER type. In this case, there may be bit masks following the BITMAPINFOHEADER
// and before the actual pixel bits (does not apply if bitmap has <= 8 bpp)
if (BitmapInfoHeader->biBitCount > 8)
{
if (BitmapInfoHeader->biCompression == BI_BITFIELDS)
{
OffsetExtra += 3 * sizeof(RGBQUAD);
}
else if (BitmapInfoHeader->biCompression == 6 /* BI_ALPHABITFIELDS */)
{
// Not widely supported, but valid.
OffsetExtra += 4 * sizeof(RGBQUAD);
}
}
}
if (BitmapInfoHeader->biClrUsed > 0)
{
// We have no choice but to trust this value.
OffsetExtra += BitmapInfoHeader->biClrUsed * sizeof(RGBQUAD);
}
else
{
// In this case, the color table contains the maximum number for the current bit count (0 if > 8bpp)
if (BitmapInfoHeader->biBitCount <= 8)
{
// 1bpp: 2
// 4bpp: 16
// 8bpp: 256
OffsetExtra += sizeof(RGBQUAD) << BitmapInfoHeader->biBitCount;
}
}
return BitmapInfoHeader->biSize + OffsetExtra;
}
// Helper function for interaction with libraries like stb_image.
// Data will be copied, so you can do what you want with it after this function returns.
static void PutBitmapInClipboard_From32bppTopDownRGBAData(INT Width, INT Height, const void *Data32bppRGBA)
{
// Nomenclature: Data at offset 0 is R top left corner, offset 1 is G top left corner, etc.
// This is pretty much the opposite of what a HBITMAP normally does.
assert(Width > 0);
assert(Height > 0);
assert(Data32bppRGBA);
// GDI won't help us here if we want to preserve the alpha channel. It doesn't support BI_ALPHABITFIELDS, and
// we can't use BI_RGB directly because BI_RGB actually means BGRA in reality.
// That means, unfortunately it's not going to be a simple memcpy :(
DWORD PixelDataSize = 4/*32bpp*/ * Width * Height;
// We need BI_BITFIELDS for RGB color masks here.
size_t TotalSize = sizeof(BITMAPINFOHEADER) + PixelDataSize;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, TotalSize);
assert(hGlobal);
void *mem = GlobalLock(hGlobal);
assert(mem);
BITMAPINFOHEADER *bih = (BITMAPINFOHEADER *)mem;
bih->biSize = sizeof(BITMAPINFOHEADER);
bih->biWidth = Width;
bih->biHeight = -Height; // Negative height means top-down bitmap
bih->biPlanes = 1;
bih->biBitCount = 32;
bih->biCompression = BI_RGB;
bih->biSizeImage = PixelDataSize;
BYTE *PixelData = (BYTE *)mem + sizeof(BITMAPINFOHEADER);
DWORD NumPixels = Width * Height;
for (DWORD i = 0; i < NumPixels; ++i)
{
// Convert RGBA to BGRA
DWORD tmp = ((DWORD *)Data32bppRGBA)[i];
DWORD tmp2 = tmp & 0xff00ff00; // assumes LE
tmp2 |= (tmp >> 16) & 0xff;
tmp2 |= (tmp & 0xff) << 16;
((DWORD *)PixelData)[i] = tmp2;
}
GlobalUnlock(hGlobal);
EmptyClipboard();
SetClipboardData(CF_DIB, hGlobal);
// The hGlobal now belongs to the clipboard. Do not free it.
}
// Bitmap will be copied, so you can do what you want with it after this function returns.
static void PutBitmapInClipboard_AsDIB(HBITMAP hBitmap)
{
// Need this to get the bitmap dimensions.
BITMAP desc = {};
int tmp = GetObjectW(hBitmap, sizeof(desc), &desc);
assert(tmp != 0);
// We need to build this structure in a GMEM_MOVEABLE global memory block:
// BITMAPINFOHEADER (40 bytes)
// PixelData (4 * Width * Height bytes)
// We're enforcing 32bpp BI_RGB, so no bitmasks and no color table.
// NOTE: SetClipboardData(CF_DIB) insists on the size 40 version of BITMAPINFOHEADER, otherwise it will misinterpret the data.
DWORD PixelDataSize = 4/*32bpp*/ * desc.bmWidth * desc.bmHeight; // Correct alignment happens implicitly.
assert(desc.bmWidth > 0);
assert(desc.bmHeight > 0);
size_t TotalSize = sizeof(BITMAPINFOHEADER) + PixelDataSize;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, TotalSize);
assert(hGlobal);
void *mem = GlobalLock(hGlobal);
assert(mem);
BITMAPINFOHEADER *bih = (BITMAPINFOHEADER *)mem;
bih->biSize = sizeof(BITMAPINFOHEADER);
bih->biWidth = desc.bmWidth;
bih->biHeight = desc.bmHeight;
bih->biPlanes = 1;
bih->biBitCount = 32;
bih->biCompression = BI_RGB;
bih->biSizeImage = PixelDataSize;
HDC hdc = CreateCompatibleDC(NULL);
assert(hdc);
HGDIOBJ old = SelectObject(hdc, hBitmap);
assert(old != nullptr); // This can fail if the hBitmap is still selected into a different DC.
void *PixelData = (BYTE *)mem + sizeof(BITMAPINFOHEADER);
// Pathologial "bug": If the bitmap is a DDB that originally belonged to a device with a different palette, that palette is lost. The caller would need to give us the correct HDC, but this is already insane enough as it is.
tmp = GetDIBits(hdc, hBitmap, 0, desc.bmHeight, PixelData, (BITMAPINFO *)bih, DIB_RGB_COLORS);
assert(tmp != 0);
// NOTE: This will correctly preserve the alpha channel if possible, but it's up to the receiving application to handle it.
DeleteDC(hdc);
GlobalUnlock(hGlobal);
EmptyClipboard();
SetClipboardData(CF_DIB, hGlobal);
// The hGlobal now belongs to the clipboard. Do not free it.
}
我打算将此代码主要用于生产,因为我自己需要它,如果有人发现问题,我很乐意听到。
一些补充说明供参考:
%zu
在较旧的 CRT 中不起作用,谁知道CF_DIB
真正想说的是“这是一个压缩的 DIB”。没有官方保证它会是一个普通的 BITMAPINFOHEADER
,即 biSize == 40
,尽管很可能是这种情况。BITMAPFINO
documentation 说明结构的长度确实是可变的,需要考虑 BITMAPINFOHEADER.biClrUsed
,但没有提及 BI_BITFIELDS
。BITMAPINFOHEADER
对此有更多详细信息,但没有提及 BI_ALPHABITFIELDS
或仅当多态 BITMAPINFOHEADER
结构实际上是一个普通的 BITMAPINFOHEADER
时才存在位掩码的事实(即biSize == 40
)。更高版本(如 BITMAPV5HEADER
)无条件地包含位掩码。GetDIBPixelDataOffset
中所做的非常相似(显然我无法在此处逐字贴出)。它不假设 biSize == 40
。较新版本的 Paint 使用 COleServerItem
进行剪贴板处理。length
参数来自 GlobalSize
。请注意,它也不假定 biSize == 40
。SetClipboardData
调用 CF_BITMAP
,则它需要一个 DDB(如果您传递给它一个实际上是 DIB 的 HBITMAP
,它将默默地失败) .隐式转换为 DIB 的 CF_BITMAP
使用 BI_BITFIELDS
,这也适用于屏幕截图(至少如果原始 DDB 与屏幕 DC 兼容)。CF_BITMAP
将 DDB 放入剪贴板,则不会复制该位图(至少最初不会)。如果任何程序对其进行操作,所有访问剪贴板的程序都会看到被操作的位图。但是,只要有任何应用程序将其作为 CF_DIB
请求,Windows 就会应用一堆魔法,而这不再正确,位图现在是一个副本。这不适用于作为 CF_DIB
放入剪贴板的位图,这些位图立即不受其他程序的操作影响。 CF_DIB
似乎没有令人不快的暗示和意外,而且似乎也被大多数应用程序使用。虽然您可以在将原始位图放入剪贴板时尝试保留其格式,但我选择对传出数据使用固定的全能格式,因为这已经够疯狂了。SetClipboardData
暗示 CF_DIB
不适用于 Windows Store 应用程序,但我无法确认该声明。再说一次,关于 NULL 所有者的段落也是不正确的。PutBitmapInClipboard_AsDIB
和 PutBitmapInClipboard_From32bppTopDownRGBAData
都会保留 alpha 通道,尽管演示的 GDI 绘图函数不支持 alpha 并且会破坏它(SFML 会很好地处理它)。将 alpha 通道放入 MSB 并使用 BI_RGB
似乎是在 DIB 中存储 alpha 的事实上的标准。