Захват Изображения С Экрана

  • Автор темы Guest
  • Дата начала
G

Guest

#1
Здравствуйте. Мне необходимо получить битмап экрана в Linux'е и Windows'е... Подскажите пожалуйста, как это можно сделать. Библиотеки для работы с графикой (которые нашел, по крайней мере), обеспечивают доступ лишь к созданной ими же экранной поверхности. Надеюсь на вашу помощь :)
 

lazybiz

Well-known member
03.11.2010
1 339
0
#2
Для Windows: GetDC( NULL ) == DC всего экрана
Для Linux (не уверен что будет работать с Xorg): читать /dev/fb0
 
G

Guest

#3
С обращением к видеопамяти в Линуксе вроде бы почти разобрался. А вот с Win API беда...
Ниже - немного исправленный мною кусок чужого кода. Вероятно, именно поэтому не рабочий...
По задумке должен сохранять скрин экрана по пути strFileName
Если это не слишком нагло, помогите пожалуйста его поправить, за пару часов разобраться не смог, а полноценно изучать Win API времени нет
C++:
#include <windows.h>
int main()
{
HWND window;
//Получаем прямоугольную область экрана
RECT windowRect;
//GetWindowRect(window, &windowRect);
GetClientRect(window, &windowRect);


//Размеры битмэпа
int bitmap_dx = windowRect.right-windowRect.left;
int bitmap_dy = windowRect.bottom-windowRect.top;

BITMAPINFOHEADER bmpInfoHeader;
BITMAPFILEHEADER bmpFileHeader;
BITMAP* pBitmap;

bmpFileHeader.bfType = 0x4d42;
bmpFileHeader.bfSize = 0;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

bmpInfoHeader.biSize = sizeof(bmpInfoHeader);
bmpInfoHeader.biWidth = bitmap_dx;
bmpInfoHeader.biHeight = bitmap_dy;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 24;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biSizeImage = bitmap_dx*bitmap_dy*(24/8);
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;

BITMAPINFO info;
info.bmiHeader = bmpInfoHeader;

BYTE* memory;
HDC winDC = GetWindowDC(window);
HDC bmpDC = CreateCompatibleDC(winDC);
//Создаем битмэп
HBITMAP bitmap = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&memory, NULL, 0);
SelectObject(bmpDC, bitmap);//Выбираем в контекст битмэп
BitBlt(bmpDC, 0, 0, bitmap_dx, bitmap_dy, winDC, 0, 0, SRCCOPY);
ReleaseDC(window, winDC);

OPENFILENAME ofn;//Указатель на структуру с данными инициализации диалогового окна
char strFilter[] = "Файлы данных *.bmp\0";
char strFileName[MAX_PATH] = "[путь к файлу]";
memset(&ofn, 0, sizeof(OPENFILENAME));//Обнуление ofn
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = window;
ofn.lpstrFilter = strFilter;
ofn.lpstrFile = strFileName;//Буфер для имени файла
ofn.nMaxFile = MAX_PATH;//Размер файла
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
strcpy(strFileName, ofn.lpstrFile);
GetSaveFileName(&ofn); //MessageBox(hwnd,"Невозможно сохранить файл", "О программе...",MB_ICONINFORMATION);

HANDLE hFile = CreateFile(
ofn.lpstrFile, GENERIC_WRITE, 
0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
DWORD dwWritten = 0;
WriteFile(hFile, &bmpFileHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &bmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, memory, bmpInfoHeader.biSizeImage, &dwWritten, NULL);
CloseHandle(hFile);
return 0;
}
 

lazybiz

Well-known member
03.11.2010
1 339
0
#4
Вот простой пример того, как снять скрин и записать его черно-белое изображение в файл сырого формата:
C++:
#include <windows.h>
#include <stdio.h>

static int		w, h;
static void *	p;

void dump_ss()
{
int				i;
unsigned char *	pp = (unsigned char *)p;
FILE *			fp;

fp = fopen( "ss.raw", "wb+" );
for ( i = 0; i < w * h; i++ ) {
fputc( pp[i * 4], fp );
}
fclose( fp );
}

int main()
{
HDC					hdc;
HBITMAP				dib;
BITMAPINFOHEADER	bi;

w = GetSystemMetrics( SM_CXSCREEN );
h = GetSystemMetrics( SM_CYSCREEN );

bi.biSize			= sizeof( BITMAPINFOHEADER );
bi.biWidth			= w;
bi.biHeight			= -h;
bi.biPlanes			= 1;
bi.biBitCount		= 32;
bi.biCompression	= BI_RGB;
hdc = CreateCompatibleDC( NULL );
dib = CreateDIBSection( hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &p, NULL, 0 );
SelectObject( hdc, dib );

BitBlt( hdc, 0, 0, w, h, GetDC( NULL ), 0, 0, SRCCOPY );

dump_ss();

return 0;
}
И не говори потом что ничего не получается. Включаешь мозг - и все становится понятно.
 
G

Guest

#5
Ок, спасибо за помощь, постараюсь разобраться.
 

lazybiz

Well-known member
03.11.2010
1 339
0
#6
Да, совсем забыл. Вот скрин того что получается в файле.
 

Вложения

  • 43.8 КБ Просмотры: 81
G

Guest

#7
Что-то понял... Попробовал написать сохранение в bmp, но на выходе черная картинка нужного размера...
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">код</div></div><div class="sp-body"><div class="sp-content">
C++:
#include <windows.h>
#include <stdio.h>

static int		w, h;
static void *	p;
static char * name="screen.bmp";
void SaveBitmap(char *name,HBITMAP bitmap)
{
HDC hdcScreen; // Контекст устройства экрана
HWND hwndScreen; // Дескриптор окна рабочего стола
int i; // Количество выбираемых линий
BYTE *bits; // Указатель на массив битов изображения
BITMAPINFOHEADER *info; // Информационная структура изображения
HANDLE fp; // Дескриптор файла
BITMAPFILEHEADER finfo; // Заголовок файла изображения
long ctsize,imgsize,pixels;// Различные размерности для записи файла
DWORD dw; // Код возврата
char *ptr; // Указатель используемый для записи открытого BM

// Получаем дескриптор рабочего стола
hwndScreen=GetDesktopWindow();
// Получаем контекст устройства рабочего стола
hdcScreen = GetDC( hwndScreen );

// Выделяем память под структуру с информацией о изображении
info = (BITMAPINFOHEADER*)GlobalAlloc(GMEM_FIXED,sizeof(BITMAPFILEHEADER)+(1024*sizeof(RGBQUAD)));
info->biSize = sizeof(BITMAPINFOHEADER);
info->biBitCount = 0;

// Заполняем структуру информацией о изображении
i = GetDIBits( hdcScreen, // дескриптор контекста устройства
bitmap, // дескриптор изображения
0, // первая строка развертки, которая должна быть выбрана
0, // число копируемых линий
NULL, // адрес массива для битов изображения
(BITMAPINFO*)info, // адрес структуры для данных изображения
DIB_RGB_COLORS // RGB или индекс палитры
);
// Определяем количество бит на пиксель.
if( (info->biBitCount!=4) &&
(info->biBitCount!=8) &&
(info->biBitCount!=24) )
info->biBitCount=24;
// Указываем что изображение не сжато
info->biCompression=BI_RGB;

// Получаем количество пикселей
pixels=info->biWidth*info->biHeight;
switch(info->biBitCount)
{
case 4:ctsize=16;imgsize=pixels/2;break;
case 8:ctsize=256;imgsize=pixels;break;
case 16:ctsize=0;imgsize=pixels*2;break;
case 24:ctsize=0;imgsize=pixels*3;break;
case 32:ctsize=0;imgsize=pixels*4;break;
default:
ReleaseDC( hwndScreen,hdcScreen );
}

// Инициализируем массив для битов изображения
bits = (unsigned char*)GlobalAlloc(GMEM_FIXED,imgsize);

//Заполняем массив битов изображения
i = GetDIBits( hdcScreen, // дескриптор контекста устройства
bitmap, // дескриптор изображения
0, // первая выбираемая линия для изображения назначения
info->biHeight, // количество выбираемых линий
bits, // адрес массива битов изображения
(BITMAPINFO*)info,// адрес структуры с данными изображения
DIB_RGB_COLORS // RGB или индекс палитры
);

// Формируем заголовок файла
ptr = (char *)&finfo.bfType;
ptr[0]='B';
ptr[1]='M';

//Определяем сдвиг от начала структуры BITMAPFILEHEADER до массива бит
finfo.bfOffBits=sizeof(BITMAPFILEHEADER)+
sizeof(BITMAPINFOHEADER)+
(sizeof(RGBQUAD)*ctsize);
// Размер файла изображения в байтах
finfo.bfSize=finfo.bfOffBits+imgsize;
finfo.bfReserved1=0;
finfo.bfReserved2=0;

//Создаем файл для изображения
fp = CreateFile(name, // указатель с именем файла
GENERIC_WRITE, // режим доступа
0, // режим использования
NULL, // указатель на параметры безопасности
CREATE_ALWAYS, // метод создания
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,// атрибуты файла
NULL
);
// Записываем структуру заголовка файла
WriteFile(fp,&finfo,sizeof(BITMAPFILEHEADER),&dw,NULL);
// Записываем структуру информации изображения
WriteFile(fp,info,sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*ctsize,&dw,NULL);
// Записываем биты изображения
WriteFile(fp,bits,imgsize,&dw,NULL);
// Закрываем файл
CloseHandle(fp);
//Освобождаем память с информацией изображения
GlobalFree(info);
// Освобождаем память с битами изображения
GlobalFree(bits);
// Освобождаем контекст устройства
ReleaseDC( hwndScreen,hdcScreen );
} 

int main()
{
HDC					hdc;
HBITMAP				dib;
BITMAPINFOHEADER	bi;

w = GetSystemMetrics( SM_CXSCREEN );
h = GetSystemMetrics( SM_CYSCREEN );

bi.biSize			= sizeof( BITMAPINFOHEADER );
bi.biWidth			= w;
bi.biHeight			= -h;
bi.biPlanes			= 1;
bi.biBitCount		= 32;
bi.biCompression	= BI_RGB;
hdc = CreateCompatibleDC( NULL );
dib = CreateDIBSection( hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, NULL, NULL, 0 );
SaveBitmap(name,dib);
return 0;
}
И вопрос по Вашему способу сохранения... Почему в файл пишется именно каждый четвертый байт? Судя по всему, за цвет каждого пикселя отвечают четыре байта, так? Тогда можно извлечь r,g,b составляющие для каждого пикселя?
 
R

rrrFer

#8
Не уверен что поможет, но:
http://developer.qt.nokia.com/doc/qt-4.8/qpixmap.html
А именно:
QPixmap QPixmap

grabWindow ( WId window , int x=0 , int y=0 , int width=-1 , int height=-1 ) [static]

Creates and returns a pixmap constructed by grabbing the contents of the given window restricted by QRect(x, y, width, height).

The arguments (x, y) specify the offset in the window, whereas (width, height) specify the area to be copied. If width is negative, the function copies everything to the right border of the window. If height is negative, the function copies everything to the bottom of the window.

The window system identifier (WId) can be retrieved using the QWidget::winId() function. The rationale for using a window identifier and not a QWidget, is to enable grabbing of windows that are not part of the application, window system frames, and so on.

The grabWindow() function grabs pixels from the screen, not from the window, i.e. if there is another window partially or entirely over the one you grab, you get pixels from the overlying window, too. The mouse cursor is generally not grabbed.

Note on X11 that if the given window doesn't have the same depth as the root window, and another window partially or entirely obscures the one you grab, you will not get pixels from the overlying window. The contents of the obscured areas in the pixmap will be undefined and uninitialized.

On Windows Vista and above grabbing a layered window, which is created by setting the Qt::WA_TranslucentBackground attribute, will not work. Instead grabbing the desktop widget should work.

Warning: In general, grabbing an area outside the screen is not safe. This depends on the underlying window system.

See also grabWidget() and Screenshot Example.
И даже, есть пример готовый: http://developer.qt.nokia.com/doc/qt-4.8/d...ff-ef489cb6b1ed
И вам должно быть приятно что не придется писать 2 версии программы...
Мне необходимо получить битмап экрана в Linux'е и Windows'е...
 
G

Guest

#9
Спасибо, посмотрю завтра. Но, так или иначе, с предыдущим способом уже просто интересно разобраться) Qt - более...высокоуровневая штука)
 

lazybiz

Well-known member
03.11.2010
1 339
0
#10
Читай. Еще. Раз. !. У меня написано совсем по-другому.

Почему в файл пишется именно каждый четвертый байт?
Чтобы не заморачиваться я брал только одну компоненту, поэтому у меня и получилось черно-белое изображение. А вообще там они все (R, G и B ).

Добавлено:
Каждый четвертый потому, что помимо RGB так есть еще и А, но в нашем случае она не используется.
 
G

Guest

#11
Вывести черно-белое изображение на экран, не сохраняя, попиксельно, получилось. Вопрос - при сохранении в bmp после структур FILEHEADER и INFOHEADER можно просто целиком записать получаемую в Вашем примере область памяти с указателем на начало p? Или все сложнее?
И почему biHeight=-h , а не просто h?
 

lazybiz

Well-known member
03.11.2010
1 339
0
#12
C++:
#include <windows.h>
#include <stdio.h>

int		w, h;
void *	p;

void dump_bmp()
{
int					i;
BITMAPFILEHEADER	bfh;
BITMAPINFOHEADER	bmi;
FILE *				fp;

memset( &bfh, 0, sizeof( BITMAPFILEHEADER ) );
memset( &bmi, 0, sizeof( BITMAPINFOHEADER ) );

bfh.bfType		= 0x4d42; // "BM"
bfh.bfSize		= sizeof( BITMAPFILEHEADER ) + w * h * 4;
bfh.bfOffBits	= sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );

bmi.biSize			= sizeof( BITMAPINFOHEADER );
bmi.biWidth			= w;
bmi.biHeight		= h;
bmi.biPlanes		= 1;
bmi.biBitCount		= 32;
bmi.biCompression	= BI_RGB;

fp = fopen( "ss.bmp", "wb+" );

fwrite( &bfh, sizeof( BITMAPFILEHEADER ), 1, fp );
fwrite( &bmi, sizeof( BITMAPINFOHEADER ), 1, fp );
fwrite( p, w * h * 4, 1, fp );

fclose( fp );
}

int main()
{
HDC					hdc;
HBITMAP				dib;
BITMAPINFOHEADER	bi;

w = GetSystemMetrics( SM_CXSCREEN );
h = GetSystemMetrics( SM_CYSCREEN );

bi.biSize			= sizeof( BITMAPINFOHEADER );
bi.biWidth			= w;
bi.biHeight			= h;
bi.biPlanes			= 1;
bi.biBitCount		= 32;
bi.biCompression	= BI_RGB;
hdc = CreateCompatibleDC( NULL );
dib = CreateDIBSection( hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&p, NULL, 0 );
SelectObject( hdc, dib );

BitBlt( hdc, 0, 0, w, h, GetDC( NULL ), 0, 0, SRCCOPY );

dump_bmp();

return 0;
}
Мне просто интересно, если ты учишься на по этой специальности, то как ты дальше будешь решать такие задачи?
 
G

Guest

#13
Благодарю.
P.S. Вчера хоть и с большим количеством косяков, но дошел почти до этого кода.
P.P.S. Учусь на специальности "Компьютерная безопасность" на втором курсе. Во втором семестре первого прошли паскаль. Сейчас изучаем Си, дошли по программе примерно до строк... ^ - для себя. И не могу сказать, что программирование для меня очень важно. Так, одно из интересных занятий, не более.