【问题标题】:What is the differences in structure between screenshot and copied part of bitmap?截图和复制的位图部分在结构上有什么区别?
【发布时间】:2019-12-09 11:54:24
【问题描述】:

我尝试使用问题here 的答案之一中的代码,当我在“paint”中打开位图文件后运行它时,选择其中的一部分并复制它,程序将复制的图像保存为需要,但是当我按 PrntScrn (并且屏幕截图出现在剪贴板中并在程序中标识为位图文件)时,程序将其保存为位图文件,但是当我尝试打开它时,我收到一条消息,指出不支持图像格式. 我想这是因为我保存图像的方式。 我的问题是两种类型的图像之间有什么区别,其中一种可以正常工作而另一种不能?

这是我的代码:

#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("Test2.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);
                        std::cout << "finished" << std::endl;
                    }

                    GlobalUnlock(dib);
                }
            }

            CloseClipboard();
        }
    }

    return 0;
}

【问题讨论】:

  • 不同之处在于,一个程序比另一个程序保存的图像具有更少的协议违规(或不那么致命)。 BMP 文件格式比您想象的要复杂得多。你可以帮自己一个忙,而不是试图重新发明轮子。 Windows 附带一个位图编解码器作为WIC 的一部分。使用它,你会得到一个结构正确的文件。

标签: c++ windows winapi clipboard


【解决方案1】:

如您所想,保存图片时出错。

让我们先分析一下你的代码。

这是截图的图像数据。

请注意:压缩:3 -> BI_BITFIELDS

BI_BITFIELDS:位图不压缩,颜色表 由三个 DWORD(在 [MS-DTYP] 第 2.2.9 节中定义)颜色组成 分别指定红色、绿色和蓝色分量的掩码, 每个像素。这在与每像素 16 位和 32 位一起使用时有效 位图。

然后我注意到截图保存为BI_BITFIELDS类型的情况很少。

包含官方 GDI 示例 -> Capturing an Image

部分代码:

 // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);

    BITMAPFILEHEADER   bmfHeader;    
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = bmpScreen.bmWidth;    
    bi.biHeight = bmpScreen.bmHeight;  
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

所以我建议你按照官方的例子来修改当前的代码。

最快的解决方案:加bmp.dib.biCompression = BI_RGB;

在哪里添加?

参考代码:

        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;
            bmp.dib.biCompression = BI_RGB;

            std::cout << "Type: " << std::hex << bmp.header.type << std::dec << "\n";

            ....

【讨论】:

  • Windows 剪贴板实际上使用 BI_BITFIELDS。请注意,即使 DIB5 已经将 ARGB 掩码作为其标头的一部分,BI_BITFIELDS 压缩意味着还有 3 个额外的 UInt32 值,其中 RGB 位掩码添加在标头后面。这没有任何意义,但这就是规范中描述的方式。所以实现 BI_BITFIELDS 支持很简单,包括这 3 个字段。
  • 请注意,“这在与每像素 16 位和 32 位位图一起使用时有效”有点欺骗性;因为有只有三个掩码,这是在谈论 32 位 RGB,不是 ARGB。
猜你喜欢
  • 2010-11-06
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多