Форум программистов, компьютерный форум CyberForum.ru

Скриншот, работа с bmp - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 4.92
Vlad1slav
21 / 21 / 5
Регистрация: 16.09.2009
Сообщений: 111
21.02.2012, 02:27     Скриншот, работа с bmp #1
Возникла необходимость создания скриншота неактивого окна. В результате долгих поисков, по гуглу я наткнулся на следующий рабочий код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//Вход с функции ScreenCapture()
inline int GetFilePointer(HANDLE FileHandle){
    return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT);
}
 
bool SaveBMPFile(wchar_t *filename, HBITMAP bitmap, HDC bitmapDC, int width, int height){
    bool Success=0;
    HDC SurfDC=NULL;
    HBITMAP OffscrBmp=NULL;
    HDC OffscrDC=NULL;
    LPBITMAPINFO lpbi=NULL;
    LPVOID lpvBits=NULL;
    HANDLE BmpFile=INVALID_HANDLE_VALUE;
    BITMAPFILEHEADER bmfh;
    if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL)
        return 0;
    if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL)
        return 0;
    HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp);
    BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY);
    if ((lpbi = (LPBITMAPINFO)(new char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)])) == NULL) 
        return 0;
    ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    SelectObject(OffscrDC, OldBmp);
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS))
        return 0;
    if ((lpvBits = new char[lpbi->bmiHeader.biSizeImage]) == NULL)
        return 0;
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS))
        return 0;
    if ((BmpFile = CreateFile(filename,
                        GENERIC_WRITE,
                        0, NULL,
                        CREATE_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL)) == INVALID_HANDLE_VALUE)
        return 0;
    DWORD Written;
    bmfh.bfType = 19778;
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
        return 0;
    if (Written < sizeof(bmfh)) 
        return 0; 
    if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL)) 
        return 0;
    if (Written < sizeof(BITMAPINFOHEADER)) 
        return 0;
    int PalEntries;
    if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) 
        PalEntries = 3;
    else PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ?
                      (int)(1 << lpbi->bmiHeader.biBitCount) : 0;
    if(lpbi->bmiHeader.biClrUsed) 
    PalEntries = lpbi->bmiHeader.biClrUsed;
    if(PalEntries){
    if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL)) 
        return 0;
        if (Written < PalEntries * sizeof(RGBQUAD)) 
            return 0;
    }
    bmfh.bfOffBits = GetFilePointer(BmpFile);
    if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL)) 
        return 0;
    if (Written < lpbi->bmiHeader.biSizeImage) 
        return 0;
    bmfh.bfSize = GetFilePointer(BmpFile);
    SetFilePointer(BmpFile, 0, 0, FILE_BEGIN);
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
        return 0;
    if (Written < sizeof(bmfh)) 
        return 0;
    CloseHandle(BmpFile);
    return 1;
}
 
bool ScreenCapture(int x, int y, int width, int height, wchar_t *filename){
    HDC hdc=GetWindowDC(hChild);
    HDC hDc = CreateCompatibleDC(hdc);    
    HBITMAP hBmp = CreateCompatibleBitmap(hdc, width, height);   
    SelectObject(hDc, hBmp);   
    BitBlt(hDc, 0, 0, width, height, hdc, x, y, SRCCOPY);  
    bool ret = SaveBMPFile(filename, hBmp, hDc, width, height); 
    DeleteObject(hBmp);  
    ReleaseDC(hChild, hdc);
    return ret;
}
Результат: http://www.cyberforum.ru/attachment....1&d=1329776576 Всё великолепно работает)

Решил я написать свой код, по аналогии с этим, убирая ненужные излишки, ну и пользуясь более рациональными на мой взгляд функциями... Потратил часов 10 в общей сложности чтобы разобраться как работать с битмапами. Получилось вот:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void func(HWND hwnd, int x, int y, int cx, int cy, char* fname) {
    HDC hdc=GetDC(hwnd);
    HDC hcdc=CreateCompatibleDC(hdc);
    HBITMAP hbmp=CreateCompatibleBitmap(hdc, cx, cy);
    SelectObject(hcdc, hbmp);
    BitBlt(hcdc, 0, 0, cx, cy, hdc, x, y,  SRCCOPY);
 
    LPBITMAPINFO lpbi=(LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
 
    ZeroMemory(lpbi, sizeof(BITMAPINFO));
    lpbi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    GetDIBits(hcdc, hbmp, 0, cy, NULL, lpbi, DIB_RGB_COLORS);
    
    LPVOID buf=malloc(lpbi->bmiHeader.biSizeImage);
    ZeroMemory(buf, lpbi->bmiHeader.biSizeImage);
    GetDIBits(hcdc, hbmp, 0, cy, buf, lpbi, DIB_RGB_COLORS);
 
    int PalEntries;
    if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) 
        PalEntries = 3;
    else PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ? (int)(1 << lpbi->bmiHeader.biBitCount) : 0;
    if(lpbi->bmiHeader.biClrUsed) 
        PalEntries = lpbi->bmiHeader.biClrUsed;
    PalEntries*=sizeof(RGBQUAD);
 
    LPBITMAPFILEHEADER lpbmpfh=new BITMAPFILEHEADER;
    lpbmpfh->bfType=0x4d42;
    lpbmpfh->bfReserved1=lpbmpfh->bfReserved2=0;
    lpbmpfh->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PalEntries;
    lpbmpfh->bfSize=lpbmpfh->bfOffBits+lpbi->bmiHeader.biSizeImage;
 
    FILE* f=fopen(fname, "w");
 
    fwrite(lpbmpfh, sizeof(BITMAPFILEHEADER), 1, f);
    fwrite(lpbi, sizeof(BITMAPINFOHEADER), 1, f);
    fwrite((BITMAPINFOHEADER*)lpbi+1, PalEntries, 1, f);
    fwrite(buf, lpbi->bmiHeader.biSizeImage, 1, f);
 
    fclose(f);
    delete lpbmpfh;
    delete buf;
    delete lpbi;
    DeleteObject(hbmp);
    ReleaseDC(hwnd, hdc);
    DeleteDC(hcdc);
}
А вот и результат: http://www.cyberforum.ru/attachment....1&d=1329776567 Уже 100 раз перечитал свой код, сравнивал разными способами, дебажил и всё такое... Заголовки bmpщников абсолютно идентичные получаются, но последние пару сотен тысяч байт файла "искажаются". Между программами только 1 разница: первая прога - оконная, вторая - консольная, но помойму это не повод для таких ошибок.

Помогите пожалуйста, в чём может быть проблемма?
Миниатюры
Скриншот, работа с bmp   Скриншот, работа с bmp  
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
21.02.2012, 02:27     Скриншот, работа с bmp
Посмотрите здесь:

Работа с файлами BMP формата C++
C++ Работа с BMP файлом.
C++ Работа с bmp изображением
C++ Работа с BMP-файлами (класс "8 битное BMP изображение)
Скриншот C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Vlad1slav
21 / 21 / 5
Регистрация: 16.09.2009
Сообщений: 111
21.02.2012, 02:49  [ТС]     Скриншот, работа с bmp #2
Попробывал перенести панель задач вниз рабочего стола: http://www.cyberforum.ru/attachment....1&d=1329777903
Похоже что проблема с цветами... в диапозоне 0x020202, 0х030303...0х111111
murderer
3175 / 1398 / 69
Регистрация: 06.10.2010
Сообщений: 3,017
21.02.2012, 09:29     Скриншот, работа с bmp #3
Проверь значение lpbi->bmiHeader.biSizeImage.
Vlad1slav
21 / 21 / 5
Регистрация: 16.09.2009
Сообщений: 111
21.02.2012, 11:08  [ТС]     Скриншот, работа с bmp #4
Структура вроде бы правильно заполнена... при разрешении экрана 1366*768 biBitCount==32(4 байта): http://www.cyberforum.ru/attachment....1&d=1329806659
Итого получаем размер рисунка 1366*768*4=4196352.
lpbmpfh->OffBits==66, lpbmpfh->biSize==4196352 вроде тоже правильно указан...
Я даже открывал оба файла и сравнивал оба заголовка, они абсолютно идентичные, а вот массивы, возвращаемый функцией GetDIBits разные(

Вот ещё друг протестил эти проги: http://www.cyberforum.ru/attachment....1&d=1329807540
Опять таки у него на рабочем столе много "искажающихся" пикселей и кроме того появилось ещё и смещение...: http://www.cyberforum.ru/attachment....1&d=1329807730
Миниатюры
Скриншот, работа с bmp   Скриншот, работа с bmp   Скриншот, работа с bmp  

murderer
3175 / 1398 / 69
Регистрация: 06.10.2010
Сообщений: 3,017
21.02.2012, 12:27     Скриншот, работа с bmp #5
Смещение может появиться если физический размер строки не кратен 4 байтам (формат bmp предполагает выравнивание).

Добавлено через 7 минут
biCompression=BI_BITFIELDS, значит палитра должна содержать битовые маски каналов. Попробуй biCompression=BI_RGB.
Vlad1slav
21 / 21 / 5
Регистрация: 16.09.2009
Сообщений: 111
21.02.2012, 13:17  [ТС]     Скриншот, работа с bmp #6
вывел на консоль содержимое буфферов с цветами пикселей(lpvBits и buf). Они тоже совпадают, т.е. на запись в файл подаются одинаковые массивы..., и ошибка всё таки в printf...

Цитата Сообщение от murderer Посмотреть сообщение
Смещение может появиться если физический размер строки не кратен 4 байтам (формат bmp предполагает выравнивание).

Добавлено через 7 минут
biCompression=BI_BITFIELDS, значит палитра должна содержать битовые маски каналов. Попробуй biCompression=BI_RGB.
Спасибо, однако, можно поподробнее? На каком этапе мне это значение установить?
Оно же вроде само у меня инициализируется при передаче lpbi в функцию GetDIBits()

Добавлено через 37 минут
Цитата Сообщение от Vlad1slav Посмотреть сообщение
Они тоже совпадают, т.е. на запись в файл подаются одинаковые массивы..., и ошибка всё таки в printf...
fwrite
murderer
3175 / 1398 / 69
Регистрация: 06.10.2010
Сообщений: 3,017
21.02.2012, 14:03     Скриншот, работа с bmp #7
Вот здесь должна быть не единичка
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+1, PalEntries, 1, f);
скорее всего
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+sizeof(BITMAPINFOHEADER), PalEntries, 1, f);
Vlad1slav
21 / 21 / 5
Регистрация: 16.09.2009
Сообщений: 111
21.02.2012, 18:10  [ТС]     Скриншот, работа с bmp #8
Цитата Сообщение от murderer Посмотреть сообщение
Вот здесь должна быть не единичка
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+1, PalEntries, 1, f);
скорее всего
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+sizeof(BITMAPINFOHEADER), PalEntries, 1, f);
Нет-нет) тут точно единица должна быть...
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+sizeof(BITMAPINFOHEADER), PalEntries, 1, f);
Учитывая, что структура BITMAPINFOHEADER занимает 40 байт, то в файл бы было записано 1600 байт... Раз уж на то пошло, то можно бы было использовать
C++
1
(char*)lpbi+sizeof(BITMAPINFOHEADER)
Но это тоже самое что и
C++
1
fwrite((BITMAPINFOHEADER*)lpbi+1, PalEntries, 1, f);
Сегодня на паре друг подкинул идею "о бредовой роботе функции fwrite", мол она пишет только текстовые файлы. Он был отчасти не прав, но подтолкнул меня к решению проблемы:
C++
1
2
    
FILE* f=fopen(fname, "wb");
Помню когда то у меня возникал вопрос, какова разница между "w" и "wb". Вот я её и нащупал)

murderer, спасибо за попытки помочь найти ошибку)

Если кому то понадобиться программа для создания скриншотов, вот код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "stdafx.h"
#include <windows.h>
 
void MakeScreen(HWND hwnd, int x, int y, int cx, int cy, wchar_t* fname) {
    HDC hdc=GetDC(hwnd);
    HBITMAP hbmp=CreateCompatibleBitmap(hdc, cx, cy);
    HDC hcdc=CreateCompatibleDC(hdc);
    SelectObject(hcdc, hbmp);
    BitBlt(hcdc, 0, 0, cx, cy, hdc, x, y,  SRCCOPY);
 
    LPBITMAPINFO lpbi=(LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
 
    ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
    lpbi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    GetDIBits(hcdc, hbmp, 0, cy, NULL, lpbi, DIB_RGB_COLORS);
    
    LPVOID buf=malloc(lpbi->bmiHeader.biSizeImage);
    GetDIBits(hcdc, hbmp, 0, cy, buf, lpbi, DIB_RGB_COLORS);
 
    int PalEntries;
    if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) 
        PalEntries = 3;
    else PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ? (int)(1 << lpbi->bmiHeader.biBitCount) : 0;
    if(lpbi->bmiHeader.biClrUsed) 
        PalEntries = lpbi->bmiHeader.biClrUsed;
    PalEntries*=sizeof(RGBQUAD);
 
    LPBITMAPFILEHEADER lpbmpfh=new BITMAPFILEHEADER;
    lpbmpfh->bfType=0x4d42;
    lpbmpfh->bfReserved1=lpbmpfh->bfReserved2=0;
    lpbmpfh->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PalEntries;
    lpbmpfh->bfSize=lpbmpfh->bfOffBits+lpbi->bmiHeader.biSizeImage;
 
    FILE* f=_wfopen(fname, L"wb");
 
    fwrite(lpbmpfh, sizeof(BITMAPFILEHEADER), 1, f);
    fwrite(lpbi, sizeof(BITMAPINFOHEADER), 1, f);
    if (PalEntries) 
        fwrite((BITMAPINFOHEADER*)lpbi+1, PalEntries, 1, f);
    fwrite(buf, lpbi->bmiHeader.biSizeImage, 1, f);
    
    fclose(f);
    delete lpbmpfh;
    delete buf;
    delete lpbi;
    DeleteObject(hbmp);
    ReleaseDC(hwnd, hdc);
    DeleteDC(hcdc);
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    //HWND окна для скрина, координаты левого верхнего угла, ширина, высота области для скрина, имя файла
    MakeScreen(NULL, 0, 0, 1366, 768, L"C:\\Screen.bmp");
    return 0;
}
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
02.12.2014, 20:41     Скриншот, работа с bmp
Еще ссылки по теме:

Работа с bmp файлом. Считывание значение пикселей в массив C++
C++ Работа с BMP изображаниями
Работа с bmp файлом не получается найти ошибку C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
noname_club
 Аватар для noname_club
100 / 88 / 9
Регистрация: 01.05.2013
Сообщений: 563
02.12.2014, 20:41     Скриншот, работа с bmp #9
[некропост]
функция GetDIBits

http://msdn.microsoft.com/en-us/library/ms787796.aspx << тонкость про порядок следования линий пикселей в массиве битов

если высота у картинки в BITMAPINFOHEADER будет отрицательная то линии картинки будут следовать в обратном порядке
[/некропост]
Yandex
Объявления
02.12.2014, 20:41     Скриншот, работа с bmp
Ответ Создать тему
Опции темы

Текущее время: 17:52. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru