3

我正在尝试创建一个等效于 windows API GetPixel() 函数的函数,但我想创建我的屏幕的位图,然后读取该缓冲区。

这就是我所得到的(主要是从谷歌搜索中复制粘贴),当我运行它时它只打印出 0。我想我大部分都做对了,我的问题是我不知道如何读取 BYTE 变量。

所以我的问题是,我需要做什么才能让它用我的 for 循环打印出一些随机颜色(R、G 或 B)?

#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>

using namespace std;

int main() {

    HDC hdc,hdcMem;

    hdc = GetDC(NULL);
    hdcMem = CreateCompatibleDC(hdc); 

    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);

    BITMAPINFO MyBMInfo = {0};
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); 
    // Get the BITMAPINFO structure from the bitmap
    if(0 == GetDIBits(hdcMem, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
        cout << "error" << endl;
    }

    // create the bitmap buffer
    BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];

    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
    MyBMInfo.bmiHeader.biBitCount = 32;  
    MyBMInfo.bmiHeader.biCompression = BI_RGB;  
    MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight); 

    // get the actual bitmap buffer
    if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
        cout << "error2" << endl;
    }

    for(int i = 0; i < 100; i++) {
        cout << (int)lpPixels[i] << endl;
    }

    return 0;
}
  • Windows 7的
  • C::B 13.12(控制台应用程序)
  • 编译器:mingw32-gcc
  • 库 gdi32 链接
4

3 回答 3

6

按照约定,我正在使用工作代码片段添加一个新答案(我添加了丢失的 lpPixels 清理)。请参阅我之前的回答和@enhzflep 所做的讨论。

#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;

HBITMAP GetScreenBmp( HDC hdc) {
    // Get screen dimensions
    int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

    // Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
    HDC hCaptureDC  = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
    HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap); 
    BOOL bOK = BitBlt(hCaptureDC,0,0,nScreenWidth, nScreenHeight, hdc,0,0,SRCCOPY|CAPTUREBLT); 

    SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
    DeleteDC(hCaptureDC);
    return hBitmap;
}

int main() {
    HDC hdc = GetDC(0);

    HBITMAP hBitmap = GetScreenBmp(hdc);

    BITMAPINFO MyBMInfo = {0};
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); 

    // Get the BITMAPINFO structure from the bitmap
    if(0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
        cout << "error" << endl;
    }

    // create the bitmap buffer
    BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];

    // Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
    // necessary to read the color table - you might not want this.
    MyBMInfo.bmiHeader.biCompression = BI_RGB;  

    // get the actual bitmap buffer
    if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
        cout << "error2" << endl;
    }

    for(int i = 0; i < 100; i++) {
        cout << (int)lpPixels[i];
    }

    DeleteObject(hBitmap);
    ReleaseDC(NULL, hdc);
    delete[] lpPixels;
    return 0;
}
于 2014-10-07T14:27:49.130 回答
3

基本上,您需要绘制一些像素才能返回 0 以外的结果。

目前,main 中的第 4 行代码创建了一个空(空白,0 初始化)图像。然后,您可以通过第一次调用来获取有关此图像大小的信息GetDIBits。然后,您第二次调用GetDIBits.

要修复,只需将一个位图文件从磁盘加载到您的hBitmap并将该位图选择到您的hdcMem.

即,改变

HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);

到这样的事情。

HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, "xpButton.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
HBITMAP old = (HBITMAP) SelectObject(hdcMem, hBitmap);

(确保使用有效的 bmp 文件名。我的文件名与 .cpp 文件位于同一文件夹中,因为这是通过 IDE 运行时的“当前”目录。如果您希望通过资源管理器运行,请放置另一个副本bmp 与您的 exe 位于同一文件夹中)

这是我使用的 bmp(上传到 SO 后已转换为 png):

在此处输入图像描述

这是循环的前 10 次迭代。

255
5
253
0
255
5
253
0
255
5

请注意,0,0 处的像素的颜色为:rgb(253,5,255),它是一个 8 位图像,因此没有 alpha 通道,因此它的值为 0。像素存储为 [BGRA], [BGRA] , [BGRA] 等等等等。我将把它留给你来修复你程序的(不存在的)清理部分。Windows 将取消分配您在此处使用的内存,但您绝对不应该养成不释放已分配的任何内存的习惯。:)

于 2014-10-07T11:23:32.683 回答
2

您的代码似乎有点混乱。我猜太多的片段:)。不过,您已经很接近了:第一个 GetDIBits() 调用是为了获取填充位图的属性,正如您的代码中的注释所暗示的那样。您为此使用了不必要的 MemDC - 这可能来自想要对屏幕执行 BitBlt 的片段。

然后,您可以使用填充的结构通过第二个 GetDIBits() 调用来获取实际的位图像素,但是您所做的是再次用硬编码值替换属性,从而使第一个 GetDIBits() 调用无用。

所以:删除 MemDC - 你不需要它 - 并在第一次调用 GetDIBits() 时用 hdc 替换 hdcMem,然后在第一次调用 GetDIBits 后删除所有覆盖 bmiHeader 成员的语句,你应该得到你的像素。

哦,当然不要忘记在 dc 和位图上调用 ReleaseDC()/DeleteObject() 并删除 [] 缓冲区 :)

于 2014-10-07T11:08:32.527 回答