It writes to my BITMAP.bmp file but when I try to view it in Windows Photo Viewer It says "Windows Photo Viewer can't open this picture because the file appears to be damaged, corrupted, or is too large." I know I probably should have put the functions into header files but I haven't ever really delved deeply enough into a project to really make header files so I forgot how. If anyone knows the size limit for pictures in Windows Photo Viewer I would be very thankful. And I got some of these functions from MSDN(unashamed). The error handling could be better but I don't have errorhandler.h. I am not completely sure how all of the functions work because as I said I used some code from MSDN.
I would be greatly appreciative of anyone that can help me. :)
#include <errorrep.h>
#include <windows.h>
#include <iostream>
using namespace std;
namespace Globals{
HBITMAP hBitmap;
HDC hScreen;
}
using namespace Globals;
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
LPSIZE lpSize;
LPVOID lpvBits;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2-x1;
h = y2-y1;
// copy screen to bitmap
hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
GetBitmapDimensionEx(hBitmap, lpSize);
GetBitmapBits(hBitmap, (LONG)lpSize, lpvBits);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
/*DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);*/
}
PBITMAPINFO CreateBitmapInfoStruct(/*HWND hwnd, */HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
//errhandler("GetObject", hwnd);
cout << "Error: CreateBitmapInfoStruct" << endl;
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CreateBMPFile(/*HWND hwnd, */LPTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
//errhandler("GlobalAlloc", hwnd);
cout << "!lpBits" << endl;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS))
{
//errhandler("GetDIBits", hwnd);
cout << "Error 1" << endl;
}
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
//errhandler("CreateFile", hwnd);
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL))
{
//errhandler("WriteFile", hwnd);
cout << "!WriteFile" << endl;
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)))
//errhandler("WriteFile", hwnd);
cout << "!WriteFile" << endl;
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
//errhandler("WriteFile", hwnd);
cout << "if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))" << endl;
// Close the .BMP file.
if (!CloseHandle(hf))
//errhandler("CloseHandle", hwnd);
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
int main() {
cout << "ScreenShot - Takes a screen shot\nScreen shot will be put in your clipboard"
"\nThere will be 10 seconds before it takes the screen shot\n" << endl;
string input;
do
{
cin >> input;
if(input == "ScreenShot")
{
/*for(int i=1; i<11; i++)
{
Sleep(1000);
cout << i << endl;
if(i == 10)
{
break;
}
}*/
GetScreenShot();
PBITMAPINFO pbmi = CreateBitmapInfoStruct(hBitmap);
CreateBMPFile("C:\\Users\\Owner\\Desktop\\BITMAP.bmp", pbmi, hBitmap, hScreen);
cout << "ScreenShot taken!" << endl;
cin.ignore(2);
Sleep(3000);
break;
}
else
{
cout << "Invalid command." << endl;
}
} while(true);
return 0;
}
Passing uninitialized values to GetBitmapDimensionEx and GetBitmapBits doesn't seems good, so delete them since they doesn't seem to be used.
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2-x1;
h = y2-y1;
// copy screen to bitmap
hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
/*DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);*/
}
Then, set hdr.bfType even where hf != INVALID_HANDLE_VALUE.
change
if (hf == INVALID_HANDLE_VALUE)
//errhandler("CreateFile", hwnd);
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
to
if (hf == INVALID_HANDLE_VALUE)
{
//errhandler("CreateFile", hwnd);
}
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
(add braces)
Using LPCTSTR instead of LPTSTR for type of pszFile, which is an argument of CreateBMPFile is also good
to avoid compiler warning when a string literal is passed.
Related
I am working on a project where I have to read the color of a pixel on screen with CreateCompatibleBitmap and Bitblt. Unfortunately this method is pretty slow, I am getting times of around 60 to 100 ms in a loop. I use this code:
HDC hdc = GetDC(NULL), 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;
SelectObject(hdcMem, hBitmap);
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hdc, 0, 0, SRCCOPY);
GetDIBits(hdc, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdc);
Most of the time (95%) is spent in the Bitblt function. I have read that it takes so long because Bitblt has to convert the color formats but I dont understand how I can avoid that...
I use Windows 11 and my screen res is FHD 1920x1080
Any suggestions how I could speed this program up?
You can use CreateDIBSection to create a kind of bitmap that gives you access to the pixel data rather than creating a regular "compatible" bitmap and then copying the pixel data into a buffer using GetDIBits but I ran a benchmark of doing it this way and the time saving is there but it is not substantial. I get about a 10% speed improvement, which is not much.
Code below, and I did write this fast so may have made a mistake. The point of the variable dummy in the following is so that everything does not get optimized away because there is not output.
#include <vector>
#include <iostream>
#include <Windows.h>
#include <chrono>
namespace c = std::chrono;
void get_screen_bytes1(void* ScreenData, int ScreenX, int ScreenY) {
HDC hdc = GetDC(NULL), 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;
SelectObject(hdcMem, hBitmap);
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hdc, 0, 0, SRCCOPY);
GetDIBits(hdc, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdc);
}
struct bitmap_info {
HBITMAP handle;
uint8_t* data;
};
bitmap_info get_screen_bytes2(int wd, int hgt) {
HDC hdc_scr = GetDC(NULL);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = wd;
bmi.bmiHeader.biHeight = -hgt;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
bitmap_info bi;
bi.handle = CreateDIBSection(hdc_scr, &bmi, DIB_RGB_COLORS, (void**)bi.data, NULL, NULL);
HDC hdc = CreateCompatibleDC(hdc_scr);
auto hbm_old = SelectObject(hdc, bi.handle);
BitBlt(hdc, 0, 0, wd, hgt, hdc_scr, 0, 0, SRCCOPY);
SelectObject(hdc, hbm_old);
DeleteDC(hdc);
ReleaseDC(NULL, hdc_scr);
return bi;
}
int main() {
constexpr auto scr_wd = 2560;
constexpr auto scr_hgt = 1440;
std::vector<uint8_t> buffer(scr_wd * scr_hgt * 3);
std::chrono::high_resolution_clock timer;
int dummy;
double sum = 0.0;
int n = 100;
for (int i = 0; i < n; ++i) {
auto start = timer.now();
get_screen_bytes1(buffer.data(), scr_wd, scr_hgt);
sum += c::duration_cast<c::microseconds>(timer.now() - start).count();
dummy += buffer[0];
}
double time1 = sum / n;
std::cout << "get_screen_bytes1 => " << time1 << "\n";
sum = 0.0;
for (int i = 0; i < n; ++i) {
auto start = timer.now();
auto bmp_info = get_screen_bytes2( scr_wd, scr_hgt);
sum += c::duration_cast<c::microseconds>(timer.now() - start).count();
dummy += bmp_info.data[0];
DeleteObject(bmp_info.handle);
}
double time2 = sum / n;
std::cout << "get_screen_bytes2 => " << time2 << "\n";
std::cout << dummy << "\n";
std::cout << "pcnt speed improvement => " << time2 / time1 << "\n";
}
I realized a program which makes a screenshot but the problem is that the image size is larger more than 6 MB.
I want to make a correction to minimize the image size.
this is my function
BOOL CALLBACK MonitorEnumProcCallback( HMONITOR hMonitor, HDC DevC,LPRECT lprcMonitor,LPARAM dwData)
{
const char*BmpName;
string BmpNameString;
BmpNameString="screen.jpeg";
BmpName= BmpNameString.c_str();
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
BOOL monitorInfo = GetMonitorInfo(hMonitor, &info);
if (monitorInfo) {
DWORD Width = info.rcMonitor.right - info.rcMonitor.left;
DWORD Height = info.rcMonitor.bottom - info.rcMonitor.top;
DWORD FileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 3 * Width * Height;
char *BmpFileData = (char*)GlobalAlloc(0x0040, FileSize);
PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
BFileHeader->bfType = 0x4D42; // BM
BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
BInfoHeader->biPlanes = 1;
BInfoHeader->biBitCount = 24;
BInfoHeader->biCompression = BI_RGB;
BInfoHeader->biHeight = Height;
BInfoHeader->biWidth = Width;
RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)];
RGBTRIPLE color;
HDC CaptureDC = CreateCompatibleDC(DevC);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC, Width, Height);
SelectObject(CaptureDC, CaptureBitmap);
BitBlt(CaptureDC, 0, 0, Width, Height, DevC, info.rcMonitor.left, info.rcMonitor.top, SRCCOPY | CAPTUREBLT);
GetDIBits(CaptureDC, CaptureBitmap, 0, Height, Image, (LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
DWORD Junk;
HANDLE FH = CreateFileA(BmpName, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
WriteFile(FH, BmpFileData, FileSize, &Junk, 0);
CloseHandle(FH);
GlobalFree(BmpFileData);
}
return TRUE;
}
data
If you don't mind, you can use the GDI + method below to get a relatively small size:
#include<windows.h>
#include <iostream>
#include <string.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
using namespace std;
#pragma comment(lib, "Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
BOOL CALLBACK MonitorEnumProcCallback(HMONITOR hMonitor, HDC DevC, LPRECT lprcMonitor, LPARAM dwData)
{
wstring BmpNameString = L"screen.jpeg";
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
BOOL monitorInfo = GetMonitorInfo(hMonitor, &info);
if (monitorInfo) {
DWORD Width = info.rcMonitor.right - info.rcMonitor.left;
DWORD Height = info.rcMonitor.bottom - info.rcMonitor.top;
//DWORD FileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 3 * Width * Height;
//char* BmpFileData = (char*)GlobalAlloc(0x0040, FileSize);
//PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
//PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
//BFileHeader->bfType = 0x4D42; // BM
//BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
//BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
//BInfoHeader->biPlanes = 1;
//BInfoHeader->biBitCount = 24;
//BInfoHeader->biCompression = BI_RLE8;
//BInfoHeader->biHeight = Height;
//BInfoHeader->biWidth = Width;
//RGBTRIPLE* Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)];
//RGBTRIPLE color;
HDC CaptureDC = CreateCompatibleDC(DevC);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC, Width, Height);
HGDIOBJ old_obj = SelectObject(CaptureDC, CaptureBitmap);
BitBlt(CaptureDC, 0, 0, Width, Height, DevC, info.rcMonitor.left, info.rcMonitor.top, SRCCOPY | CAPTUREBLT);
//GetDIBits(CaptureDC, CaptureBitmap, 0, Height, Image, (LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
/*DWORD Junk;
HANDLE FH = CreateFileA(BmpName, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
WriteFile(FH, BmpFileData, FileSize, &Junk, 0);
CloseHandle(FH);
GlobalFree(BmpFileData);*/
Gdiplus::Bitmap bitmap(CaptureBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/jpeg", &pngClsid);
bitmap.Save(BmpNameString.c_str(), &pngClsid, NULL);
SelectObject(CaptureDC, old_obj);
DeleteDC(CaptureDC);
ReleaseDC(NULL, DevC);
DeleteObject(CaptureBitmap);
}
return TRUE;
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HDC hdc = GetDC(NULL);
EnumDisplayMonitors(hdc,NULL, MonitorEnumProcCallback,NULL);
ReleaseDC(NULL,hdc);
GdiplusShutdown(gdiplusToken);
return 0;
}
im making an application that takes a screenshot of the monitor and then reads it with tesseract.
To get better performance id like to avoid saving the screenshot before sending it to tesseract.
I found some example and other related post and came to this code that uses "pixReadMemBm" but it always return a null ptr:
#define _CRT_SECURE_NO_WARNINGS // for strncopy and fopen
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
//include leptonica headers
#include <allheaders.h>
//include tesseract headers
#include <baseapi.h>
using namespace std;
TCHAR CurDir[MAX_PATH];
tesseract::TessBaseAPI *api;
void init_tesseract_ocr();
PIX* tesseract_preprocess(PIX* pixs);
string tesseract_ocr(PIX* image);
PIX* ScreenShot(int left, int top, int sizex, int sizey);
int main()
{
GetCurrentDirectory(MAX_PATH, CurDir);
init_tesseract_ocr();
PIX* sc = ScreenShot(0, 0, 500, 30);
//PIX* pp = tesseract_preprocess(sc);
//string text = tesseract_ocr(pp);
//cout << text << endl;
system("pause");
return 0;
}
PIX* ScreenShot(int left, int top, int sizex, int sizey)
{
// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
HBITMAP hBitmap;
hBitmap = CreateCompatibleBitmap(hScreenDC, sizex, sizey);
// get a new bitmap
SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, sizex, sizey, hScreenDC, left, top, SRCCOPY);
BITMAP bmpScreen;
GetObject(hBitmap, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
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;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
GetDIBits(hScreenDC, hBitmap, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*HANDLE hFile = CreateFile("UI.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Close the handle for the file that was created
CloseHandle(hFile);*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<unsigned char> buffer(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmpSize);
std::copy(reinterpret_cast<unsigned char*>(&bmfHeader), reinterpret_cast<unsigned char*>(&bmfHeader) + sizeof(BITMAPFILEHEADER), buffer.begin());
std::copy(reinterpret_cast<unsigned char*>(&bi), reinterpret_cast<unsigned char*>(&bi) + sizeof(BITMAPINFOHEADER), buffer.begin() + sizeof(BITMAPFILEHEADER));
std::copy(lpbitmap, lpbitmap + dwBmpSize, buffer.begin() + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
PIX *mypix = pixReadMemBmp(&buffer[0], buffer.size());
if (mypix == NULL)
{
cout << "mypix is NULL" << endl;
}
else
{
pixWriteImpliedFormat("preprocess.bmp", mypix, 0, 0);
}
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
DeleteObject(hBitmap);
GlobalUnlock(hDIB);
GlobalFree(hDIB);
/////////////////////////////////////
return mypix;
}
if I save the screenshot to a file (with the methode commented in the code) i get the expected result, so the problem is those 5 lines:
std::vector<unsigned char> buffer(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmpSize);
std::copy(reinterpret_cast<unsigned char*>(&bmfHeader), reinterpret_cast<unsigned char*>(&bmfHeader) + sizeof(BITMAPFILEHEADER), buffer.begin());
std::copy(reinterpret_cast<unsigned char*>(&bi), reinterpret_cast<unsigned char*>(&bi) + sizeof(BITMAPINFOHEADER), buffer.begin() + sizeof(BITMAPFILEHEADER));
std::copy(lpbitmap, lpbitmap + dwBmpSize, buffer.begin() + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
PIX *mypix = pixReadMemBmp(&buffer[0], buffer.size());
could someone explain to me what i did wrong?
Thanks !
I'm writing a remote controlling program with GUI.
Here's a part of the server code:
HDC desktopHdc = GetDC(NULL);
int bm_x = GetDeviceCaps(desktopHdc, HORZRES);
int bm_y = GetDeviceCaps(desktopHdc, VERTRES);
DWORD bmSize = ((bm_x * 32 + 31) / 32) * bm_y * 4;
HDC memHdc = CreateCompatibleDC(desktopHdc);
HBITMAP bitmap = CreateCompatibleBitmap(desktopHdc, bm_x, bm_y);
while(TRUE)
{
SelectObject(memHdc, (HGDIOBJ)bitmap);
HANDLE hDIB = GlobalAlloc(GHND, bmSize);
char* bmbits = (char*)GlobalLock(hDIB);
BitBlt(memHdc, 0, 0, bm_x, bm_y, desktopHdc, 0, 0, SRCCOPY);
GetBitmapBits(bitmap, bmSize, bmbits);
int bytes = send(secondarySocket, bmbits, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
GlobalUnlock(hDIB);
GlobalFree(hDIB);
Sleep(1000);
}
And here's some client code:
HDC windowHdc = GetDC(hwnd);
HDC memHdc = CreateCompatibleDC(windowHdc);
// bm_x and bm_y are sent by the server and received by the client
DWORD bmSize = ((bm_x * 32 + 31) / 32) * bm_y * 4;
while(TRUE)
{
HANDLE hDIB = GlobalAlloc(GHND, bmSize);
char* buf = (char*)GlobalLock(hDIB);
int bytes = recv(mainSocket, buf, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
else
{
buf[bytes] = '\0';
HBITMAP bmp = CreateCompatibleBitmap(memHdc, bm_x, bm_y);
SetBitmapBits(bmp, bmSize, buf);
SelectObject(memHdc, bmp);
BitBlt(windowHdc, 0, 0, bm_x, bm_y, memHdc, 0, 0, SRCCOPY); // should copy received bitmap to the window DC
}
GlobalUnlock(hDIB);
GlobalFree(hDIB);
}
I receive whole buffer in the client correctly. I make a bitmap from it. And then I just can't copy it to the window DC - nothing appears. How can I do that?
EDIT
Here's my new code:
DWORD thServerSend(LPVOID param)
{
HDC memHdc = CreateCompatibleDC(desktopHdc);
HBITMAP bitmap;
while(TRUE)
{
BITMAPINFO bmi;
ZeroMemory(&bmi,sizeof(BITMAPINFO));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=bm_x;
bmi.bmiHeader.biHeight=bm_y;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=32;
char* bmbits;
bitmap = CreateDIBSection(memHdc, &bmi, DIB_RGB_COLORS, (void**)&bmbits, NULL, 0);
HBITMAP restore = (HBITMAP)SelectObject(memHdc, (HGDIOBJ)bitmap);
BitBlt(memHdc, 0, 0, bm_x, bm_y, desktopHdc, 0, 0, SRCCOPY);
int bytes = send(secondarySocket, bmbits, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
std::cout << "bmsize: " << bmSize << " bytes: " << bytes << " bmbits: " << bmbits << std::endl; // for debugging
SelectObject(memHdc, restore);
DeleteObject(bitmap);
Sleep(1000);
}
DeleteDC(memHdc);
DeleteObject(bitmap);
shutdown(mainSocket, SD_BOTH);
shutdown(secondarySocket, SD_BOTH);
return 0;
}
DWORD thClient(LPVOID param) // window handle is passed through "param" parameter
{
HDC windowHdc = GetDC((HWND)param);
HDC memHdc = CreateCompatibleDC(windowHdc);
HBITMAP restore;
while(TRUE)
{
BITMAPINFO bmi;
ZeroMemory(&bmi,sizeof(BITMAPINFO));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=bm_x;
bmi.bmiHeader.biHeight=bm_y;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=32;
char* buffer;
HBITMAP bitmap = CreateDIBSection(windowHdc, &bmi, DIB_RGB_COLORS, (void**)&buffer, NULL, 0);
int bytes = recv(mainSocket, buffer, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "Disconnected.", "", 0);
break;
}
else
{
restore = (HBITMAP)SelectObject(memHdc,bitmap);
BitBlt(windowHdc, 0, 0, bm_x, bm_y, memHdc, 0, 0, SRCCOPY);
std::cout << "bmsize: " << bmSize << " bytes: " << bytes << " bmbits: " << buffer << std::endl; // for debugging
SelectObject(memHdc, restore);
}
}
DeleteDC(memHdc);
return 0;
}
EDIT 2
Problem solved:
That is, how I called the threads:
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thClient, (PVOID)&hwnd, 0, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thServerSend, (PVOID)hwnd, 0, NULL);
As you can see, I used reference to the window handle in the argument of the thread calling function. This secretly caused stupid errors. Nobody could solve it without full code.
Don't create a compatible bitmap.
Create a DIB section: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183494%28v=vs.85%29.aspx
BITMAPINFO bi;
ZeroMemory(&bi,sizeof(BITMAPINFO));
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth=Width;
bi.bmiHeader.biHeight=Height;
bi.bmiHeader.biPlanes=1;
bi.bmiHeader.biBitCount=32;
char* buffer;
HBITMAP bitmap=CreateDIBSection(dc,&bi,DIB_RGB_COLORS,&buffer,NULL,0);
// copy pixels to buffer
SelectObject(dc,bitmap);
BitBlt(....
I use a function which captures a screen using the BitBlt mehtod and can then return a HBITMAP.
int screenCapture() {
int width = 1000;
int height = 700;
HDC hdcTemp, hdc;
BYTE* bitPointer;
hdc = GetDC(HWND_DESKTOP);
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = width;
bitmap.bmiHeader.biHeight = -height;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap);
BitBlt(hdcTemp, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
ReleaseDC(HWND_DESKTOP, hdc);
DeleteDC(hdcTemp);
return (int)bitPointer[0];
}
Here, the function only returns the first value of the pixels array.
Actually, it works fine.
for (int i = 0; i >= 0; i++) {
cout << i << ": " << screenCapture() << endl;
}
But when I try to loop this, an error is generated after a few hundred rounds (a little over 900 for me), an Access violation reading location error.
I also noticed that if I reduced the values of width and height, then the error took longer to be called.
I am a true beginner and I do not know where the error may come, but it would look like a memory problem, right?
As was stated in comments, you are leaking your HBITMAP, and also the original HBITMAP that was already in the HDC before you called SelectObject(). Whenever you use SelectObject(), you must always restore the original value (you don't own it).
And don't forget to do error checking!
Try this:
int screenCapture()
{
int result = -1;
int width = 1000;
int height = 700;
HDC hdcTemp, hdc;
BYTE* bitPointer;
hdc = GetDC(HWND_DESKTOP);
if (hdc != NULL)
{
hdcTemp = CreateCompatibleDC(hdc);
if (hdcTemp != NULL)
{
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = width;
bitmap.bmiHeader.biHeight = -height;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)&bitPointer, NULL, NULL);
if (hBitmap != NULL)
{
HBITMAP hPrevBitmap = SelectObject(hdcTemp, hBitmap);
BitBlt(hdcTemp, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
result = (int) bitPointer[0];
SelectObject(hdcTemp, hPrevBitmap);
DeleteObject(hBitmap);
}
DeleteDC(hdcTemp);
}
ReleaseDC(HWND_DESKTOP, hdc);
}
return result;
}