C++ считывание пикселей с помощью GetDIBits()

Я пытаюсь создать функцию, которая эквивалентна функции Windows API GetPixel(), но я хочу создать растровое изображение моего экрана и затем прочитать этот буфер.

Это то, что у меня есть (в основном, копия, вставленная из поисков Google), когда я запускаю ее, она выводит только 0. Я думаю, что у меня есть большая часть этого права, и что моя проблема в том, что я не знаю, как читать переменную BYTE.

Итак, мой вопрос: что мне нужно сделать, чтобы он распечатывал некоторые случайные цвета (R,G или B) с помощью цикла for?

#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 связана

3 ответа

Решение

По договоренности, я добавляю новый ответ с рабочим фрагментом кода (я добавил недостающую очистку 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;
}

По сути, вам нужно нарисовать несколько пикселей, чтобы получить результат, отличный от 0.

В настоящее время 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. Mine находится в той же папке, что и файл.cpp, поскольку это "текущий" каталог при запуске через IDE. Если вы хотите запустить через проводник, поместите другую копию bmp в той же папке, что и ваш exe)

Вот bmp, который я использовал (который был преобразован в png после загрузки в SO):

введите описание изображения здесь

И вот первые 10 итераций в цикле.

255
5
253
0
255
5
253
0
255
5

Обратите внимание, что пиксель в точке 0,0 имеет цвет: rgb(253,5255), и это 8-битное изображение, поэтому альфа-канал отсутствует, следовательно, он имеет значение 0. Пиксели сохраняются как [BGRA], [BGRA], [BGRA] и т. Д., И т. Д. Я оставлю это вам, чтобы исправить (несуществующий) раздел очистки вашей программы. Windows освободит память, которую вы использовали здесь, но вы абсолютно не должны привыкать не освобождать память, которую вы распределили.:)

Ваш код кажется немного запутанным. Слишком много фрагментов, я думаю:). Тем не менее, вы достаточно близки: первый вызов GetDIBits() предназначен для того, чтобы получить заполненные свойства растрового изображения, как предполагает комментарий в вашем коде. Вы используете ненужный MemDC для этого - который, вероятно, из фрагмента, который хочет сделать BitBlt с экраном.

Затем вы можете использовать заполненную структуру для получения фактических растровых пикселей со вторым вызовом GetDIBits(), но вы снова заменяете свойства жестко закодированными значениями, делая первый вызов GetDIBits() бесполезным.

Итак: удалите MemDC - он вам не нужен - и замените hdcMem на hdc при первом вызове GetDIBits(), затем удалите все операторы, которые перезаписывают члены bmiHeader после первого вызова GetDIBits, и вы должны получить свои пиксели.

О, и, конечно же, не забудьте вызвать ReleaseDC()/DeleteObject() для dc и bitmap и удалить [] буфер:)

Другие вопросы по тегам