Как сделать снимок экрана в буфер на C++?

343
14 февраля 2017, 19:19

Нужно сделать снимок экрана и записать его RGB-данные в буфер (BYTE*, unsigned char*, string - не важно). Не нужно сохранять файл, не нужны BMP-заголовки - просто массив RGB.

Как это сделать на C++ с минимально возможным временем выполнения и наименьшими затратами системных ресурсов?

PS: среда выполнения - Windows. Пробовал glReadPixels() - но в результате чёрный экран. GDI работает медленно и я так и не понял как с его помощью достать RGB. И курсор мыши GDI не захватывает

Answer 1

Отсюда:

/* Globals */
int ScreenX = GetDeviceCaps(GetDC(0), HORZRES);
int ScreenY = GetDeviceCaps(GetDC(0), VERTRES);
BYTE* ScreenData = new BYTE[3*ScreenX*ScreenY];
void ScreenCap() {
HDC hdc = GetDC(GetDesktopWindow());
HDC hdcMem = CreateCompatibleDC (hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, ScreenX, ScreenY);
BITMAPINFOHEADER bmi = {0};
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biPlanes = 1;
bmi.biBitCount = 24;
bmi.biWidth = ScreenX;
bmi.biHeight = -ScreenY;
bmi.biCompression = BI_RGB;
bmi.biSizeImage = ScreenX * ScreenY;
SelectObject(hdcMem, hBitmap);
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hdc, 0, 0, SRCCOPY);
GetDIBits(hdc, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdc);
}
inline int PosR(int x, int y) {
return ScreenData[3*((y*ScreenX)+x)+2];
}
inline int PosG(int x, int y) {
return ScreenData[3*((y*ScreenX)+x)+1];
}
inline int PosB(int x, int y) {
return ScreenData[3*((y*ScreenX)+x)];
}
Answer 2

Решил пока так, при помощи средств gdi32:

void CaptureScreen(){
// Определение контекстов
HDC ScreenDC = GetDC(0);
HDC MemoryDC = CreateCompatibleDC(ScreenDC);
// Фиксация размеров экрана
int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
// Создание и частичное заполнение структуры формата
BITMAPINFO BMI;
BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BMI.bmiHeader.biWidth = ScreenWidth;
BMI.bmiHeader.biHeight = -ScreenHeight; // Отрицательное значение высоты, чтобы изображение не было перевёрнутым
BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3; // Ширина * Высота * Количество_цветов_на_пиксель
BMI.bmiHeader.biCompression = BI_RGB;
BMI.bmiHeader.biBitCount  = 24;
BMI.bmiHeader.biPlanes = 1;
ScreenshotSize = BMI.bmiHeader.biSizeImage; // ScreenshotSize - глобальная переменная типа int, может меняться в ходе выполнения программы
unsigned char *ImageBuffer; // Указатель на блок данных BGR, управляемый HBITMAP (да, именно BGR - не RGB)
HBITMAP hBitmap = CreateDIBSection(ScreenDC,&BMI,DIB_RGB_COLORS,(void**)&ImageBuffer,0,0);
SelectObject(MemoryDC, hBitmap);
BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC , 0, 0, SRCCOPY);
// Контексты больше не нужны
DeleteDC(MemoryDC);
ReleaseDC(NULL, ScreenDC);
// Если требуется RGB вместо BGR - следующий цикл перевернёт нужные байты
for(int i = 0; i < ScreenshotSize; i += 3){
    unsigned char ColorValue = ImageBuffer[i];
    ImageBuffer[i] = ImageBuffer[i + 2];
    ImageBuffer[i + 2] = ColorValue;
}
// Используем ImageBuffer как нам хочется... после удаляем его хендлер (HBITMAP) для избежания утечки памяти
DeleteObject(hBitmap);
}

Быстрее, наверно, будет только DirectX или OpenGL, но с последним уже был неудачный опыт скриншота, и я пока не знаю как получить данные прямо из графического вывода

Answer 3

Можно попробовать через COLORREF и GetPixel, т.е получить дисплейный контекст через GetDC, а потом через GetPixel прочитать цвет пикселя.

HDC dc = GetDC();
COLORREF color = GetPixel(dc,x,y);
READ ALSO
Заменить подстроки из таблицы

Заменить подстроки из таблицы

Есть таблица ForgeRock, которая содержит поле nameВ поле ForgeRock

368
Wordpress + GIT + MySQL

Wordpress + GIT + MySQL

Добрый деньНесколько разработчиков используют GIT в работе с сайтом, который сделан на WordPress

274
Крестики-нолики 10 на 10

Крестики-нолики 10 на 10

Как написать функцию проверки isGameOver() 5ти одинаковых символов подряд? Те

379
Помогите выполнить задание [требует правки]

Помогите выполнить задание [требует правки]

Помогите пожалуйста выполнить это задание

300