Given just a HBITMAP, how to draw to it? - c++

I'm an absolute beginner at this but have managed to blunder my way to 93% of where I want to be. Need help for the final 7%.
I've manually created a bitmap like so:
BITMAPINFO bmpInfo = { 0 };
BITMAPINFOHEADER bmpInfoHeader = { 0 };
BITMAP ImageBitmap;
void *bits;
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biBitCount = 32;
bmpInfoHeader.biClrImportant = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biHeight = -IMAGE_DISPLAY_HEIGHT;
bmpInfoHeader.biWidth = IMAGE_DISPLAY_WIDTH;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biSizeImage = IMAGE_DISPLAY_WIDTH * IMAGE_DISPLAY_HEIGHT * 4;
ZeroMemory(&bmpInfo, sizeof(bmpInfo));
bmpInfo.bmiHeader = bmpInfoHeader;
bmpInfo.bmiColors->rgbBlue = 0;
bmpInfo.bmiColors->rgbGreen = 0;
bmpInfo.bmiColors->rgbRed = 0;
bmpInfo.bmiColors->rgbReserved = 0;
g_hImageBitmap = CreateDIBSection(hDC, &bmpInfo, DIB_RGB_COLORS, &bits, NULL, 0);
GetObject(g_hImageBitmap, sizeof(BITMAP), &ImageBitmap);
for (i = 0; i < IMAGE_DISPLAY_WIDTH; i++) {
for (j = 0; j < IMAGE_DISPLAY_HEIGHT; j++) {
((unsigned char *)bits)[j*IMAGE_DISPLAY_WIDTH * 4 + i * 4] = 255; // Blue
((unsigned char *)bits)[j*IMAGE_DISPLAY_WIDTH * 4 + i * 4 + 1] = 255; // Green
((unsigned char *)bits)[j*IMAGE_DISPLAY_WIDTH * 4 + i * 4 + 2] = 255; // Red
((unsigned char *)bits)[j*IMAGE_DISPLAY_WIDTH * 4 + i * 4 + 3] = 0;
}
}
g_ImageBitmapPixels = bits;
and elsewhere WM_PAINT handles drawing this like so
hdc = BeginPaint(hwnd, &ps);
if (g_hImageBitmap != NULL) {
GetObject(g_hImageBitmap, sizeof(BITMAP), &bm);
hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, g_hImageBitmap);
BitBlt(hdc, UPPER_LEFT_IMAGE_X, UPPER_LEFT_IMAGE_Y,
bm.bmWidth, bm.bmHeight, hMemoryDC, 0, 0, SRCCOPY);
SelectObject(hMemoryDC, hOldBitmap);
}
Given the global variable g_ImageBitmapPixels other parts of the program can change and manipulate individual pixels in the bitmap, and when that happens, I use
InvalidateRect(hwnd, &RECT_ImageUpdate_Window, TRUE);
UpdateWindow(hwnd);
to update just that little portion of the screen. Works great. Hooray for me.
To get to the point, my question is, if a function has ONLY the HBITMAP (g_hImageBitmap) ... is there a way to call the Windows library functions to draw lines, text, circles, filled circles, to the HBITMAP? Like these functions
MoveToEx(hDC, x1, y1, NULL);
LineTo(hDC, x2, y2 );
HBRUSH hRedBrush = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hDC, &somerectangle, hRedBrush);
except instead of needing a device context, they just take the HBITMAP?
I have a pointer to the actual pixels (g_ImageBitmapPixels) so I could just write my own line drawing, circle drawing, rectangle filling functions. Indeed I have done that, but it seems a shame not to use the functions Microsoft so kindly provides. Also, I'm not smart enough to make my own text-drawing functions.
Thank you for your help.

Related

C++ get average colour of multible squares on screen (ambilight)

Im new and i hope you can help me.
First some infos:
I wanna create a ambilight for my Keyboard(Roccat MK FX)
My Screen is 2560x1440.
I wanna split the screen in multible squares. For each Key on the board 1 square.
The board layout: enter image description here
The Splitting of the screen(more or less):
enter image description here
SOOO i wanna get the average colour of field "esc" and put it in the key "0" and so on.
I only have problems to get the colour of the screen.
I wanna only read every 10 pixel for speed and so on. maybe i lower it or so. i will see it later^^
the code is only for the top row of keys.
every square for a key has the dimension of 160x240 pixels for all 16 keys.
HDC hScreen = GetDC(0);
ScreenX = GetDeviceCaps(hScreen, HORZRES);
ScreenY = GetDeviceCaps(hScreen, VERTRES);
HDC hdcMem = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, ScreenX, ScreenY);
HGDIOBJ hOld = SelectObject(hdcMem, hBitmap);
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hScreen, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOld);
BITMAPINFOHEADER bmi = { 0 };
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biWidth = ScreenX;
bmi.biHeight = -ScreenY;
bmi.biCompression = BI_RGB;
bmi.biSizeImage = 0;// 3 * ScreenX * ScreenY;
if (ScreenData)
free(ScreenData);
ScreenData = (BYTE*)malloc(4*ScreenX * ScreenY);
GetDIBits(hdcMem, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
ReleaseDC(0, hScreen);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
if (tasta)
{
//teiler = 1;
redcolor = 0;
greencolor = 0;
bluecolor = 0;
redtemp = 0;
greentemp = 0;
bluetemp = 0;
hori = 0;
vert = 0;
memset(frame_dataOnOff, 0, 110);
memset(frame_dataRed, 0, 110);
memset(frame_dataGreen, 0, 110);
memset(frame_dataBlue, 0, 110);
x_cord = 0;
for (int k = 0; k <= 15; k++)//eigne einteilung wählen
{
redcolor = 0;
greencolor = 0;
bluecolor = 0;
teiler = 0;
for (int y = 1440; y > 1200; y = y - 10)//VERTIKAL 1440 240
{
for (int x = 160 * k; x <= 160 * k + 160; x = x + 10)//Horizontal 2560 variable
{
redcolor = redcolor + ScreenData[4 * (((y)*ScreenX) + x) + 2];
greencolor = greencolor + ScreenData[4 * (((y)*ScreenX) + x) + 1];
bluecolor = bluecolor + ScreenData[4 * (((y)*ScreenX) + x)];
teiler++;
}
}
redcolor = redcolor / teiler;
greencolor = greencolor / teiler;
bluecolor = bluecolor / teiler;
frame_dataOnOff[k] = 1;
frame_dataRed[k] = redcolor;
frame_dataGreen[k] = greencolor;
frame_dataBlue[k] = bluecolor;
}
x_cord = 0;
ReleaseDC(0, hScreen);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
}
with this code i get a error when k is 6 and x is 1010 error is: Ausgelöste Ausnahme: Lesezugriffsverletzung "ScreenData" war "0x62C511C". ScreenData for red = 14749642
ScreenData for blue= 14749641
ScreenData for green = 14749640
I think im out of range or so. but dont know
Thx for help ;)
ps. when i change the multiplier from ScreenData from 4 to 3, the code works, but the colour squares ae on the wrong place. more in the left middle of the screen.

MFC casting Handle into pointer and DIB to DDB conversion

I am trying to create a bitmap by hardcoding an array of pixel values, converting this array of pixels into a DIB, and then turn this DIB into a DDB. I found two functions to convert CreateBitmapFromPixels and DIBToDDB on the internet. My problem is that the program would crash at line 244. I found that, at line 243, lpbi does not retrieve information from hDIB. Then I added the code at lines 229 and 230 to see if doing the same thing in the function that created the BITMAPINFO structure would help. Still, nothing was gotten from the HBITMAP. I am wondering if there is anything wrong with casting a handle into a pointer, what does it do, and are there other ways to get the HBITMAPINFOHEADER from a handle to a DIB so I can fix the problem.
HBITMAP ColorChange2Dlg::CreateBitmapFromPixels( HDC hDC,
UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
{
if(uBitsPerPixel < 8) // NOT IMPLEMENTED YET
return NULL;
if(uBitsPerPixel == 8)
return Create8bppBitmap(hDC, uWidth, uHeight, pBits);
HBITMAP hBitmap = 0;
if ( !uWidth || !uHeight || !uBitsPerPixel )
return hBitmap;
LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel/8) ;
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
bmpInfo.bmiHeader.biHeight = uHeight;
bmpInfo.bmiHeader.biWidth = uWidth;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
if(bmpInfo.bmiHeader.biBitCount==32) {
bmpInfo.bmiHeader.biCompression=BI_RGB;
//bmpInfo.bmiColors=NULL;
}
// Pointer to access the pixels of bitmap
UINT * pPixels = 0;
hBitmap = CreateDIBSection( hDC, (BITMAPINFO *)&
bmpInfo, DIB_RGB_COLORS, (void **)&
pPixels , NULL, 0);
if ( !hBitmap )
return hBitmap; // return if invalid bitmaps
//SetBitmapBits( hBitmap, lBmpSize, pBits);
// Directly Write
memcpy(pPixels, pBits, lBmpSize );
LPBITMAPINFOHEADER lpbi; //Line 229
lpbi = (LPBITMAPINFOHEADER)hBitmap; //Line 230
return hBitmap;
}
HBITMAP ColorChange2Dlg::DIBToDDB( HANDLE hDIB, CDC& dc )
{
LPBITMAPINFOHEADER lpbi;
HBITMAP hbm;
CPalette pal;
CPalette* pOldPal;
//CClientDC dc(NULL);
if (hDIB == NULL)
return NULL;
lpbi = (LPBITMAPINFOHEADER)hDIB; //Line 243
int nColors = lpbi->biClrUsed ? lpbi->biClrUsed : 1 << lpbi->biBitCount; //Line 244
BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
LPVOID lpDIBBits;
if( bmInfo.bmiHeader.biBitCount > 8 )
lpDIBBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors +
bmInfo.bmiHeader.biClrUsed) +
((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
else
lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
// Create and select a logical palette if needed
if( nColors <= 256 && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
{
UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors);
LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
pLP->palVersion = 0x300;
pLP->palNumEntries = nColors;
for( int i=0; i < nColors; i++)
{
pLP->palPalEntry[i].peRed = bmInfo.bmiColors[i].rgbRed;
pLP->palPalEntry[i].peGreen = bmInfo.bmiColors[i].rgbGreen;
pLP->palPalEntry[i].peBlue = bmInfo.bmiColors[i].rgbBlue;
pLP->palPalEntry[i].peFlags = 0;
}
pal.CreatePalette( pLP );
delete[] pLP;
// Select and realize the palette
pOldPal = dc.SelectPalette( &pal, FALSE );
dc.RealizePalette();
}
hbm = CreateDIBitmap(dc.GetSafeHdc(), // handle to device context
(LPBITMAPINFOHEADER)lpbi, // pointer to bitmap info header
(LONG)CBM_INIT, // initialization flag
lpDIBBits, // pointer to initialization data
(LPBITMAPINFO)lpbi, // pointer to bitmap info
DIB_RGB_COLORS ); // color-data usage
if (pal.GetSafeHandle())
dc.SelectPalette(pOldPal,FALSE);
return hbm;
}
void ColorChange2Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CClientDC dc(this);
COLORREF *pix = (COLORREF *)malloc(255*255*sizeof(COLORREF));
//int x = 1;
if(pix!=NULL){
for(int i=0;i<255;i++)
{
for(int j=0;j<255;j++)
{
pix[i*255+j] = RGB(i,j,0);
}
}
}
CDC tempDC;
tempDC.CreateCompatibleDC(&dc);
HBITMAP dib = CreateBitmapFromPixels(tempDC.m_hDC,255,255,8*sizeof(COLORREF),(BYTE*)pix);
HBITMAP finalMap = DIBToDDB(dib,tempDC);
HBITMAP oldMap = (HBITMAP)tempDC.SelectObject(finalMap);
dc.BitBlt(201,50,255,255,&tempDC,0,0,SRCCOPY);
tempDC.SelectObject(oldMap);
tempDC.DeleteDC();
}
To write compatible code, it's better not to access bits directly at all. You can use Gradient functions and GDI or GDI+ draw functions to do anything you want.
The code you have in mind pix[i*255+j] = RGB(i,j,0); is of a 32-bit image. Each pixel points to a color. It's not a palette image where each pixel points to an entry in the color table.
If display is 32 bit (most modern computers are, but check to make sure), you can do this with the following code
CBitmap m_bitmap;
void CMyWnd::make_bitmap()
{
if (m_bitmap.GetSafeHandle()) return;
int w = 255;
int h = 255;
int *pix = new int[w*h];
for (int i = 0; i < w; i++)
for (int j = 0; j < h; j++)
pix[i + j*w] = RGB(i, j, 0);
m_bitmap.CreateBitmap(w, h, 1, 32, pix);
delete[]pix;
}
And to draw the bitmap:
void CMyWnd::paint_bitmap(CDC &dc)
{
if (!m_bitmap.GetSafeHandle()) return;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
HBITMAP oldbitmap = (HBITMAP)memdc.SelectObject(m_bitmap);
BITMAP bm;
m_bitmap.GetBitmap(&bm);
dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
}
void CMyWnd::OnPaint()
{
__super::OnPaint();
CClientDC dc(this);
paint_bitmap(dc);
}
Edit: For historical reasons the RGB value are saved backward as BGR. Use this function instead:
void CMyWnd::make_bitmap()
{
if (m_bitmap.GetSafeHandle()) return;
int w = 256;
int h = 256;
BYTE *pix = new BYTE[4*w*h];
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
int p = (i + j*w) * 4;
pix[p + 0] = 0;//blue
pix[p + 1] = i;//green
pix[p + 2] = j;//red
pix[p + 3] = 0;//not used in GDI functions
}
}
m_bitmap.CreateBitmap(w, h, 1, 32, pix);
delete[]pix;
}

GetPixel() not working correctly Windows API C++

I'm writing a program that reads each pixel of a window and store it in an array of bytes as black and white, each bit of the bytes is a black/white value.
But GetPixel() doesn't seem to work the way I expected. Here's the part of the code for reading pixels and storing them:
byte *colors = new byte[250000 / 8 + 1];
ZeroMemory(colors, 250000 / 8 + 1);
HDC hdc = GetDC(hwnd);
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, 500, 500);
SelectObject(memDC, memBitmap);
BitBlt(memDC, 0, 0, 500, 500, hdc, 0, 0, SRCCOPY);
for (int y = 0; y < 500; y++) {
for (int x = 0; x < 500; x++) {
COLORREF pxcolor = GetPixel(memDC, x, y);
if (pxcolor == CLR_INVALID) {
MessageBox(hwnd, _T("Oops..."), NULL, NULL);
}
int r = GetRValue(pxcolor);
int g = GetGValue(pxcolor);
int b = GetBValue(pxcolor);
int average = (r + g + b) / 3;
bool colorBW = average >= 128;
int currentIndex = y * 500 + x;
if (colorBW) {
SetBit(colors, currentIndex);
}
}
}
ReleaseDC(hwnd, hdc);
DeleteDC(memDC);
DeleteObject(memBitmap);
delete[] colors;
SetBit():
inline VOID SetBit(byte *bytes, int index, bool state = true) {
byte byteToSet = bytes[index / 8];
int bitNumber = index % 8;
bytes[index / 8] = state ? (byteToSet | (0b1000'0000 >> bitNumber)) : (byteToSet & ((0b1111'1111 >> (bitNumber + 1)) | (0b1111'1111 << (8 - bitNumber - 1))));
}
Every pixel read in by GetPixel() seems to give me 0x000000, or pure black.
My code used to call GetPixel() with the first parameter being hdc, without all the bitmap and memory DC stuff, but that way every pixel returns CLR_INVALID. I came across this question, and the above code is after I have changed it into using memory DCs and bitmaps. But it just went from returning CLR_INVALID to 0x000000 for each pixel.
If I add this line before I use GetPixel():
SetPixel(memDC, x, y, RGB(255, 255, 255));
GetPixel() returns the correct result. Why is it functioning this way?

c/c++ assign RGBQUAD array to a bitmap

i am doing a program where you take a screenshot of a window and then scan every pixel of that picture. But I have a problem assigning RGBQUAD array to the taken screen. Every pixel has the same RGB which is 205. Here is a piece of my code:
RGBQUAD *pixel = malloc((ssWidth * ssHeight)* sizeof(RGBQUAD));
hdcScreen = GetDC(gameHandle);
hdc = CreateCompatibleDC(hdcScreen);
hBmp = CreateCompatibleBitmap(hdcScreen, ssWidth, ssHeight);
SelectObject(hdc, hBmp);
BitBlt(hdc, 0, 0, ssWidth, ssHeight, hdcScreen, xCenter, yCenter, SRCCOPY);
GetDIBits(hdc, hBmp, 0, ssHeight, pixel, &bmpInfo, DIB_RGB_COLORS);
int p = -1;
for(y_var = 0; y_var < ssWidth; y_var++)
{
for(x_var = 0; x_var < ssHeight; x_var++)
{
if(ComparePixel(&pixel[++p]))
{
SetCursorPos(xCenter + x_var + 3, yCenter + y_var + 3);
}
}
}
bool ComparePixel(RGBQUAD *pixel)
{
printf("%d, %d, %d\n"; pixel -> rgbRed, pixel -> rgbGreen, pixel -> rgbBlue);
return false;
}
ComparePixel(RGBQUAD *pixel) function just checks the RGB values. How do i assign the RGBQUAD to the bitmap of the screenshot?
Multiple issues.
The RGBQUAD **pixel = malloc(... and free(*pixel) appear to be the problem. I think you want RGBQUAD *pixel = malloc((ssWidth * ssHeight)* sizeof(RGBQUAD)); (only 1 *)
Suspect the pixels in GetDIBits() s/b pixel.
I think you want y_var = 0; (x_var = 0; also)
ComparePixel() is not defined, but I think you want something closer to if(ComparePixel(pixel[x_var+(y_var*ssWidth)], the_pixel_to_compare_against))
The free(*pixel); s/b _after the 2 for loops and should be free(pixel);

Get Pixel color fastest way?

I'm trying to make an auto-cliker for an windows app. It works well, but it's incredibly slow!
I'm currently using the method "getPixel" which reloads an array everytime it's called.
Here is my current code:
hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);
for(y=start_by; y<end_by; y+=10)
{
for(x=start_bx; x<end_bx; x+=10)
{
pixel = GetPixel(*hdc, x, y);
if(pixel==RGB(255, 0, 0))
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
So basically, it just scan a range of pixel in the screen and starts a mouse event if it detects a red button.
I know there are other ways to get the pixel color, such as bitblt. But I've made some researches, and I don't understand how I'm supposed to do, in order to scan a color array. I need something which scans screen very fast in order to catch the button.
Could you please help me?
Thanks.
I found a perfect way which is clearly faster than the GetPixel one:
HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;
while(true)
{
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);
for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];
x = i / (4 * MAX_HEIGHT);
y = i / (4 * MAX_WIDTH);
if (red == 255 && green == 0 && blue == 0)
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
I hope this could help someone else.
The simple answer is that if this is the method you insist on using then there isn't much to optimize. As others have pointed out in comments, you should probably use a different method for locating the area to click. Have a look at using FindWindow, for example.
If you don't want to change your method, then at least sleep your thread for a bit after each complete screen scan.