C++ 如何使用 16 色(4 位)位图在内存中获取屏幕截图输出

标签 c++ image winapi screenshot

我正在尝试获取具有 16 色位图的图像。
我在互联网帖子上找不到解决方案。

这里是简单的实现。

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

LPBITMAPINFO ConstructBitmapInfo(int nBits, int nX, int nY);
VOID RocketBuffer(LPVOID lpData);

int _tmain(int argc, _TCHAR* argv[])
{
    HWND hDesktop = NULL;
    HDC hCurrenDC = NULL;
    INT nX, nY;
    LPBITMAPINFO lpBmpInfo;
    HBITMAP hBmpScreen;
    LPVOID lpBmpBuffer = NULL;

    if(NULL == (hDesktop = GetDesktopWindow()))
        return GetLastError();

    if (NULL == (hCurrenDC = GetDC(hDesktop)))
        return GetLastError();

    nX = GetSystemMetrics(SM_CXSCREEN);
    nY = GetSystemMetrics(SM_CYSCREEN);

    HDC hOrgMemDC = NULL;
    hOrgMemDC = CreateCompatibleDC(hCurrenDC);
    lpBmpInfo = ConstructBitmapInfo(4, nX, nY);
    hBmpScreen = CreateDIBSection(hCurrenDC, lpBmpInfo, DIB_RGB_COLORS, &lpBmpBuffer, NULL, 0);

    SelectObject(hOrgMemDC, hBmpScreen);

    if(!BitBlt(hOrgMemDC, 0, 0, nX, nY, hCurrenDC, 0, 0, SRCCOPY))
    {
        return GetLastError();
    }

    LPBYTE lpWriteData = new BYTE[lpBmpInfo->bmiHeader.biSizeImage * 2];

    memcpy(lpWriteData, lpBmpBuffer, lpBmpInfo->bmiHeader.biSizeImage);
    RocketBuffer(lpBmpBuffer);

    delete[] lpWriteData;

    return 0;
}

LPBITMAPINFO ConstructBitmapInfo(int nBits, int nX, int nY)
{   
    int color_num = nBits <= 8 ? 1 << nBits : 0;

    int nBISize = sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
    BITMAPINFO  *lpbmi = (BITMAPINFO *) new BYTE[nBISize];

    BITMAPINFOHEADER    *lpbmih = &(lpbmi->bmiHeader);
    lpbmih->biSize = sizeof(BITMAPINFOHEADER);
    lpbmih->biWidth = nX;
    lpbmih->biHeight = nY;
    lpbmih->biPlanes = 1;
    lpbmih->biBitCount = nBits;
    lpbmih->biCompression = BI_RGB;
    lpbmih->biXPelsPerMeter = 0;
    lpbmih->biYPelsPerMeter = 0;
    lpbmih->biClrUsed = 0;
    lpbmih->biClrImportant = 0;
    lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;

    if (nBits >= 16)
        return lpbmi;

    HDC hDC = GetDC(NULL);
    HBITMAP hBmp = CreateCompatibleBitmap(hDC, 1, 1); 
    GetDIBits(hDC, hBmp, 0, 0, NULL, lpbmi, DIB_RGB_COLORS);
    ReleaseDC(NULL, hDC);
    DeleteObject(hBmp);

    return lpbmi;
}

VOID RocketBuffer(LPVOID lpData)
{
    // ...
}

我无法将 lpWriteData 输出为 * .bmp 文件。它不是 BMP 格式。
因此,函数RocketBuffer (LPVOID lpData)将无法正确执行其功能。

问题是如何用16色位图在内存中获取屏幕截图输出。

32 位彩色图像
32 Bit Color Image

我想要这样的 4 位图像。
4 位彩色图像
4 Bit Color Image

最佳答案

16 色 4 位图像包含 16 种颜色的颜色表。颜色表大小为16 * 4 字节。 BITMAPINFO 应足够大,以便通过 GetDIBits 接收调色板

下面的代码适用于以下位图:

  • 1 位位图(单色)//bpp = 1
  • 4 位位图(16 色)//bpp = 4
  • 8 位位图(256 色)//bpp = 8

.

#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>

int main()
{
    //4-bit bitmap: bpp = 4
    //valid values with this method bpp = 1, 4, 8
    WORD bpp = 4;

    //color table:
    int colorsize = (1 << bpp) * sizeof(RGBQUAD);

    int width = GetSystemMetrics(SM_CXFULLSCREEN);
    int height = GetSystemMetrics(SM_CYFULLSCREEN);

    HDC hdc = GetDC(HWND_DESKTOP);
    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
    HDC memdc = CreateCompatibleDC(hdc);
    HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
    BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, CAPTUREBLT | SRCCOPY);
    SelectObject(memdc, oldbmp);

    //size in bytes for pixel data:
    DWORD size = ((width * bpp + 31) / 32) * 4 * height;

    std::vector<BYTE> bi_memory;
    bi_memory.resize(sizeof(BITMAPINFOHEADER) + colorsize, 0);
    BITMAPINFO* bi = (BITMAPINFO*)&bi_memory[0];
    bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi->bmiHeader.biWidth = width;
    bi->bmiHeader.biHeight = height;
    bi->bmiHeader.biPlanes = 1;
    bi->bmiHeader.biBitCount = bpp;
    bi->bmiHeader.biCompression = BI_RGB;
    bi->bmiHeader.biClrUsed = 16;

    std::vector<BYTE> pixels(size + colorsize);
    GetDIBits(hdc, hbitmap, 0, height, &pixels[0], bi, DIB_RGB_COLORS);

    std::ofstream fout(TEXT("c:\\test\\_4bit.bmp"), std::ios::binary);
    if(fout)
    {
        //bitmap file header 
        //(54 = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))
        BITMAPFILEHEADER filehdr = { 'MB', 54 + colorsize + size, 0, 0, 54 };
        fout.write((char*)&filehdr, sizeof(BITMAPFILEHEADER));

        //bitmap info header
        fout.write((char*)&bi->bmiHeader, sizeof(BITMAPINFOHEADER));

        //color table
        fout.write((char*)bi->bmiColors, colorsize);

        //pixel data
        fout.write((char*)pixels.data(), pixels.size());
    }

    //cleanup:
    DeleteObject(memdc);
    DeleteObject(hbitmap);
    ReleaseDC(HWND_DESKTOP, hdc);

    return 0;
}

关于C++ 如何使用 16 色(4 位)位图在内存中获取屏幕截图输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50794015/

相关文章:

c++ - 打印所有可能的最长递减子序列

C++将函数指针一行放入映射中

c++ - ImageList 和 BltBit - ting

c - 从编辑控件获取 Unicode 字符串

c++ - 是否可以在 C++ 中强制传递依赖对象的常量

c++ - OpenCV 3.1 升级导致 Linux 上的链接器错误

html - 图片显示不正确

android - 我如何创建一个包含固定大小图像的 RecyclerView 来包装内容并且图像以裁剪为中心

css - Chrome 不按比例缩放

winapi - 鼠标状态 winapi