位图信息在返回正确的像素颜色之前会定期返回一系列相同的错误值

问题描述 投票:0回答:1

更新:

事实证明,这个问题其实比我想象的要严重得多。我在

main()
中编写了一些额外内容,以便使用
screenCap()
循环并调用捕获中心像素数组中的值来多次运行
for
函数。当我运行这个时,所有其他被计算的捕获都返回 c0c0c。诚然,我可以这样做,以便该函数再次运行以获得正确的值,因为它运行得很快,或者至少对于我的目的来说足够快,但是,我宁愿避免任何可能的复杂情况,例如速度或内存,如果这可能是一个问题的话。这些 c0c0c 值在这里似乎确实是持久的,而不是任何其他值。


您好,如果我描述的问题非常明显,我们深表歉意。我是 Windows 编程和使用 GDI+ 的新手。

我正在编写一个程序,该程序读取屏幕上的 n*n 区域并将其获取的像素发送到 2D 矢量以供以后计算。我一直在计算向量,以便可以将它与图像版本交叉引用,以确保它获得正确的数据,主要只是为了调试目的。它通常有效,但每隔一段时间,我就会得到相同的奇数序列:一个充满 c0c0c 的向量,一个充满 c0c0c 的向量,一个充满 0 的向量,最后是一个有些随机值的向量。之后,运行程序将返回位图的正确值。

下面是我的代码的简化版本,没有任何图像保存或其他内容:

#include <windows.h>
#include <iostream>
#include <vector>
#include <iomanip>
#include <gdiplus.h>
#include <gdiplusheaders.h>

using namespace Gdiplus;
using std::cout;

BITMAPINFOHEADER createBitmapHeader(int w, int h) { 
    //Manually creating the header for the bitmap.
    BITMAPINFOHEADER bi = { 0 };

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = w;
    bi.biHeight = -h;
    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;
    return bi;
}

HBITMAP screenCap(HWND hWnd, unsigned int x, unsigned int y, int gridSize) {
    //x and y here are the centre pixel of the bitmap. 
    //gridSize tells how big a square the bitmap is supposed to be
    if (gridSize % 2 == 0) {
        gridSize++; //this just makes sure that gridSize is odd
    }

    HDC hwinDC = GetDC(hWnd);
    HDC hwinCDC = CreateCompatibleDC(hwinDC);
    SetStretchBltMode(hwinCDC, COLORONCOLOR);
    //These give the coords of the bottom-left corner of the bitmap
    int capx = x - (gridSize - 1) / 2; 
    int capy = y - (gridSize - 1) / 2; 
    

    HBITMAP hbwin = CreateCompatibleBitmap(hwinDC, gridSize, gridSize);
    BITMAPINFOHEADER bi = createBitmapHeader(gridSize, gridSize);
    SelectObject(hwinCDC, hbwin);

    //this makes the buffer for the bitmap data to go into
    DWORD dwBmpSize = ((gridSize * bi.biBitCount + 31) / 32) * 4 * gridSize;
    HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
    int* lpbitmap = (int*)GlobalLock(hDIB);
    //creates the vector for the buffer to be sent to
    std::vector<std::vector<int>> refGrid;
    refGrid.resize(gridSize);
    for (int i = 0; i < refGrid.size(); i++) {
        refGrid[i].resize(gridSize);
    }
    
    //stretchBlt the data to hwinCDC so that GetDIBits can read it
    StretchBlt(hwinCDC, 0, 0, gridSize, gridSize, hwinDC, capx, capy, gridSize, gridSize, SRCCOPY);
    //and send that to lpbitmap
    GetDIBits(hwinCDC, hbwin, 0, gridSize, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

    for (int i = 0; i < refGrid.size(); i++) {
        for (int j = 0; j < refGrid.size(); j++) {
            refGrid.at(i).at(j) = lpbitmap[i * refGrid.size() + j] - 0xff000000; 
            //this sends everything to the vector refGrid
            //subtracting 0xff000000 gets rid of the alpha value, which I don't need
        }
    }

    //now we cout the vector. I could just cout the lpbitmap, but this is more 
    //readable and is better for the calculations I'll need to do later
    for (int i = 0; i < refGrid.size(); i++) {
        for (int j = 0; j < refGrid.size(); j++) {
            std::cout << std::hex << refGrid[i][j] << " ";
        }
        std::cout << "\n";
    }
    
    //and finally, get rid of the DCs and return hbwin
    DeleteDC(hwinCDC);
    ReleaseDC(hWnd, hwinDC);
    return hbwin;
}

//the main call
int main() {

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    HWND hWnd = GetDesktopWindow();
    HBITMAP hBmp = screenCap(hWnd, 960, 100, 5);
        
    GdiplusShutdown(gdiplusToken);
    return 0;
}

我主要在 5x5 网格中工作。这些第一个向量似乎在我捕获的任何东西中都是恒定的:

第一次和第二次运行:

c0c0c c0c0c c0c0c c0c0c c0c0c
c0c0c c0c0c c0c0c c0c0c c0c0c
c0c0c c0c0c c0c0c c0c0c c0c0c
c0c0c c0c0c c0c0c c0c0c c0c0c
c0c0c c0c0c c0c0c c0c0c c0c0c

第三次运行:

0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

然后,第四次运行对于不同的捕获有一些不同的值。

对于全白图像(所有像素均为 ffffff),我得到:

f1f1f1 f1f1f1 f1f1f1 f1f1f1 f1f1f1
efefef efefef efefef efefef efefef
ebebeb ebebeb ebebeb ebebeb ebebeb
e5e5e5 e5e5e5 e5e5e5 e5e5e5 e5e5e5
dfdfdf dfdfdf dfdfdf dfdfdf dfdfdf

但是对于具有位图的图像,请说:

ffffff ed1c24 ffffff ffffff ffffff
ffffff ed1c24 ffffff ffffff ffffff
ed1c24 ed1c24 ed1c24 ed1c24 ed1c24
ffffff ed1c24 ffffff ffffff ffffff
ffffff ed1c24 ffffff ffffff ffffff

我得到:

f1f1f1 e01a22 f1f1f1 f1f1f1 f1f1f1
efefef de1a22 efefef efefef efefef
da1a21 da1a21 da1a21 da1a21 da1a21
e5e5e5 d51920 e5e5e5 e5e5e5 e5e5e5
dfdfdf cf181f dfdfdf dfdfdf dfdfdf

超过这四个值的任何运行都会返回正确的 RGB 值。我还应该注意到,使用

getPixel()
执行相同操作但速度较慢的程序会返回类似的值。

我环顾四周,几乎没有发现任何相关内容。我确实注意到,这个循环似乎是在程序窗口(由

main()
创建的窗口)位于最左上角时开始的,并在我关闭并再次运行程序时继续向右下方。我怀疑这是窗口的错,但我对 Windows GUI 的内部工作原理也了解不够,无法排除这种可能性。对于前面提到的
getPixel()
版本也是如此,这是除计算值之外的唯一公共链接。如果有人能告诉我为什么会出现这些值,以及如何补救,我将不胜感激

c++ windows bitmap gdi+
1个回答
0
投票

你的代码有点乱,我不太能理解(部分是因为我不太了解GDI+;我只了解一点GDI)。看起来您想要捕获屏幕的矩形区域并将其保存到 BITMAP,然后将像素内容输出到数组。
以下是代码中需要改进的一些领域:

HBITMAP screenCap(HWND hWnd, unsigned int x, unsigned int y, int gridSize)
ES.106:不要尝试使用无符号来避免负值。
为什么使用
GlobalAlloc
而不是
new
unique_ptr
vector

与一维
std::vector<std::vector<int>>
相比,
std::vector<int>
消耗更多空间且访问性能更差。
您不需要拉伸图像,因此应该使用
BitBlt
而不是
StretchBlt
。这提高了性能和可读性。

除此之外,你的程序似乎可以工作。但是,您如何知道您的程序捕获了屏幕的哪一部分?在我看来,这可能是您的程序似乎“失败”的原因:您的程序工作正常,但您想要捕获的区域与程序实际捕获的区域不一致。

c0c0c
是控制台窗口的颜色。

以下程序将捕获屏幕上指定区域的像素,输出它们,将捕获区域绘制到屏幕左上角,并将捕获区域反转。

#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <cassert>

HBITMAP screenCap(HWND hWnd, int x,int y,int width,int height) {
    assert(x >= 0 && y >= 0);
    assert(width > 0 && height > 0);
    //capture screen
    HDC screenDC = GetDC(hWnd);
    HDC memDC = CreateCompatibleDC(screenDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(screenDC, width, height);
    SelectObject(memDC, hBitmap);
    BitBlt(memDC, 0, 0, width, height, screenDC, x, y, SRCCOPY);
    ReleaseDC(hWnd, screenDC); screenDC = nullptr;
    //Store pixels in an array
    std::vector<RGBQUAD> buffer(width * height);
    GetBitmapBits(hBitmap, width * height * 4, buffer.data());// GetDIBits should be used here, but GetBitmapBits is easy to use, and also works.
    DeleteDC(memDC); memDC = nullptr;
    // output as string
    std::string out; 
    out.reserve(width * height * 7);
    const char c[17] = "0123456789abcdef";
    for (int i = 0; i < height; ++i) {
        for (int j = 0; j < width; ++j) {
            //Convert pixels to hexadecimal string
            out += c[buffer[i * width + j].rgbRed >> 4];
            out += c[buffer[i * width + j].rgbRed & 0x0f];
            out += c[buffer[i * width + j].rgbGreen >> 4];
            out += c[buffer[i * width + j].rgbGreen & 0x0f];
            out += c[buffer[i * width + j].rgbBlue >> 4];
            out += c[buffer[i * width + j].rgbBlue & 0x0f];
            out += '\t';
        }
        out.back() = '\n';
    }
    std::cout << out;
    return hBitmap;
}
int main() {
    int x = 960;
    int y = 100;
    int width = 10;
    int height = 10;
    HBITMAP hBitmap= screenCap(NULL, x,y,width,height);
    //Show image and show captured area
    HDC screenDC = GetDC(NULL);
    HDC memDC = CreateCompatibleDC(screenDC);
    SelectObject(memDC, hBitmap);
    while (true) {
        BitBlt(screenDC, 0, 0, width, height, memDC, 0, 0, SRCCOPY);    // Draw the captured content to the top-left corner of the screen
        BitBlt(screenDC, x, y, width, height, memDC, 0, 0, NOTSRCCOPY); // Highlight the captured area
        Sleep(100);
    }
    ReleaseDC(NULL, screenDC);
    DeleteDC(memDC);
    DeleteObject(hBitmap);
}

附注我的英语不是很好,所以请原谅任何语法错误。

© www.soinside.com 2019 - 2024. All rights reserved.