I'm doing a program that reads a range of pixels and if find the red color, do a click with left mouse button, but it is too slow, like if the red dot shows up on the range, it takes 2 seconds or more to click.
This is a representation of the problem:
So if some red dot cross the green line, click with left mouse button. The green line do not exist, I drew it just to make it clearer.
Can someone propose a solution to read this interval in milliseconds?
I think that the problem is to use a for loop to check the pixel colors.
I tried to use the solution proposed in the topic "Get Pixel color fastest way?" but this made the computer very slow and did not work. (I made some adjustments to adapt the solution to my problem). Also, because my problem is much simpler, I think there is a simpler solution than the one in the other topic, which is more complex and did not work for me.
This is my code:
int main()
{
HDC dc = GetDC(NULL);
POINT pt;
int x = 150;
while (1) {
for (int y = 205; y >= 0; y--) {
dc = GetDC(NULL);
COLORREF color = GetPixel(dc, x, y);
// the red color has value = 220
if (color == 220) {
GetCursorPos(&pt);
mouse_event(MOUSEEVENTF_LEFTDOWN, pt.x, pt.y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, pt.x, pt.y, 0, 0);
}
}
}
ReleaseDC(NULL, dc);
}
Should you be getting a new image every time you're checking the pixel color? Off the top of my head maybe GETDC should be before the for loop.
while (1) {
dc = GetDC(NULL);
for (int y = 205; y >= 0; y--) {
COLORREF color = GetPixel(dc, x, y);
Related
The code provided at the end draws a grid of red 3x3px rectangles with a random constant alpha value using AlphaBlend(). The output however, turns out not "quite" random:
Notice runs of constant alpha along x-axis.
What might be causing this?
P.S. Stepping though the debugger produces the expected output.
Code to produce output:
void draw_mark(HDC hdc, int x, int y,
COLORREF mark_clr, int mark_w, int mark_h, BYTE alpha);
void produce_output(HWND hWnd) {
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
const int grid_w = 64, grid_h = 64;
const int mark_sz = 3;
HDC hdc = GetDC(hWnd);
for(int y = 0; y < grid_h; ++y) {
for(int x = 0; x < grid_w; ++x) {
BYTE rnd_alpha = rand(); // use random alpha for each mark
draw_mark(hdc, x * mark_sz, y * mark_sz,
RGB(255,0,0), mark_sz, mark_sz, rnd_alpha);
}
}
// clean-up
ReleaseDC(hWnd, hdc);
}
// draws a [mark_w x mark_h] rectangle at (x,y) with alpha
void draw_mark(HDC hdc, int x, int y,
COLORREF mark_clr, int mark_w, int mark_h, BYTE alpha)
{
HDC hdcMem = CreateCompatibleDC(NULL);
HBITMAP hbm = CreateCompatibleBitmap(hdc, mark_w, mark_h);
HGDIOBJ hOldBmp = SelectObject(hdcMem, hbm);
for(int x = 0; x < mark_w; ++x) {
for(int y = 0; y < mark_h; ++y) {
SetPixel(hdcMem, x, y, mark_clr);
}
}
POINT marker_center{mark_w / 2, mark_h / 2};
SetPixel(hdcMem, marker_center.x, marker_center.y, RGB(255, 255, 255));
BLENDFUNCTION bf{};
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = 0; // ignore source per-pixel alpha and...
bf.SourceConstantAlpha = alpha; // ...use constant alpha provided instead
AlphaBlend(hdc,
x - marker_center.x, y - marker_center.y,
mark_w, mark_h,
hdcMem, 0, 0, mark_w, mark_h, bf);
// clean-up
SelectObject(hdcMem, hOldBmp);
DeleteObject(hbm);
DeleteDC(hdcMem);
};
EDIT - As I look more into it, here are the additional issues I have noticed:
1- Output is normal when AlphaBlend() destination is a memory DC, but not when a window DC. So the issue has to do with bliting directly to screen.
2- Corrupt output is unrelated to use of rand() function. Replacing BYTE rnd_alpha = rand(); with ++alpha also produces somewhat similar corrupt outputs.
3- More interestingly, suspending the thread in the inner loop such as Sleep(some_duration) seems to reduce the corruption. Higher the some_duration, less the corruption. Here is a sample output:
First output is generated by first blitting to a memory DC, then to window. The rest is directly to the window. Notice how corruption increases(i.e. output becomes less random) as thread suspension time decreases.
I'm trying to draw sine wave using win32 api. I done this.
hDC = GetDC(hWnd);
while (TRUE)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
break;
wavefunc(hWnd, hDC);
}
void wavefunc(HWND hWnd, HDC hDC)
{
double full = 2 * pi * _freq;
static double _x = 0;
short int _y = 0;
short _y = (short)(sin(_x / _freq)*_amp) + 300;
if (_x >= full)
_x -= full;
SetPixel(hDC, 600, _y, blue);
ScrollWindow(hWnd, -1, 0, NULL, NULL);
Sleep(_sTime);
_x++;
}
Now i'm trying to figure out how to draw cartesian system. But with no result.
I have a window which is scrolled at each sin value.
I try to draw to lines on hdc. When window is scrolled there is no way to stop hdc to not scroll.
Then i create another hDc from windows but no succes.
How to do this? The problem is to have window scrolling but with some point fixes..
The way to do this is to paint everything all at once: clear the window, draw the grid, and then draw the entire waveform (not just the next point) over the grid. To make the waveform scroll, you keep redrawing with a different offset for the waveform.
To avoid flicker, you will likely need to do some double-buffering.
Typical WinAPI programs paint the window when handling the WM_PAINT command. The WM_PAINT message is generated when any or all of the window is invalid and needs to be repainted. Each time you scroll the window, you're creating an invalid region, but since you're ignoring the WM_PAINT message, nothing happens.
Here is how I would animate the sin() function:
Create a bitmap (the size of your window) and draw your static parts on it (axis, title, labels, etc.).
Create another bitmap (the height of your sin() and the width of your window + the width of the sin()’s period at your scale), and draw sin() on it.
In the wavefunc(), get the precise time and calculate the offset for your sin bitmap.
Use BitBlt() function to first blit the static part onto your window’s DC, then use TransparentBlt() to paint the sin, starting at pre-calculated offset.
Hi i have used getpixel method, bitblt or what its called (creating bitmap header) and then going though all the values. It is VERY slow. For exapmple if i had to detect something red or specific color it would take very long time. There has gotta be faster way right? I did try using desktop as HWND and then the window i need to find the colors, but desktop was somehow faster.. guesing because it had to look for the window i guess. I get HIGH cpu usage using both methods.
void Get_Color(int x,int y,int w,int h,int &red,int &green,int &blue,int action)
{
HDC hdc, hdcTemp;
RECT rect;
BYTE*bitPointer;
HWND Desktop = GetDesktopWindow();
hdc = GetDC(Desktop);
GetWindowRect(Desktop, &rect);
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmap.bmiHeader.biWidth = w;
bitmap.bmiHeader.biHeight = h;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
HGDIOBJ save = SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, w, h, hdc, x, y, SRCCOPY);
if(action==1)
{
for(int j=0;j<=w*h*4;j+=4)
{
red = bitPointer[j+2];
green = bitPointer[j+1];
blue = bitPointer[j];
if(red<30 && green>190 && blue>190)
{
break;
}
}
}
else
{
for(int j=0;j<=w*h*4;j+=4)
{
red = bitPointer[j+2];
green = bitPointer[j+1];
blue = bitPointer[j];
break;
}
}
///RELEASE
DeleteObject( SelectObject(hdcTemp, save) );
DeleteDC(hdcTemp);
DeleteDC(hdc);
ReleaseDC(NULL,hdc);
ReleaseDC(NULL,hdcTemp);
}
You may try to break your search. First search in the red channel and, when this succeed look for blue and green values:
for (int j=0; j<=w*h*4; j+=4){
red = bitPointer[j+2];
if (red<30) {
green = bitPointer[j+1];
blue = bitPointer[j];
if (green>190 && blue>190) {
do_something;
}
}
}
You can also try to accelerate via pointer arithmetic (but a good compiler will easily optimize the former):
for (BYTE *pRed=bitPointer+2; pRed<=bitPointer+w*h*4; pRed+=4){
if (pRed<30) {
green = pRed[-1];
blue = pRed[-2];
if (green>190 && blue>190) {
do_something;
}
}
}
If this is not sufficient, you may think about using threads to break the search into separate parallels smaller searches.
I suggest you create a table of RGB values from the bitmap. This would only need to be performed once.
struct RGB
{
unsigned int red;
unsigned int green;
unsigned int blue;
};
RGB rgb_pixels[MAX_ROWS][MAX_COLUMNS];
// ...
for (unsigned int row = 0; row < MAX_ROWS; ++row)
{
for (unsigned int column = 0; column < MAX_COLUMNS; ++column)
{
unsigned red = get_red_value(row, column);
unsigned green = get_green_value(row, column);
unsigned blue = get_blue_value(row, column);
RGB pixel;
pixel.red = red;
pixel.green = green;
pixel.blue = blue;
rgb_pixels[row][column] = pixel;
}
}
Whenever your program needs RGB info from the bitmap, index the array (rgb_pixels).
Accessing the RGB pixel array will be much faster than searching the bitmap and converting, for each pixel you are interested in.
Basically every time you call your method, it is creating an entirely new bitmap and copying it again. You could speed up your program the most by keeping the array of colors between calls to Get_Color(). You could put it in a class or a global variable. Thomas Matthews has a good idea for that. Once that's fixed, Jean-Baptiste Yunès has some good advice to look into.
I am trying to write text to a bitmap i am getting from an usb camera using directshow.
The problem is that the text is mirror inverted upside-down and i don't know why.
Here is the code that writes the text:
BITMAPINFOHEADER bih = m_videoInfo.bmiHeader;
Bitmap bmp(bih.biWidth, bih.biHeight, m_stride, m_pixFmt, pBuffer);
Graphics g(&bmp);
if (this->introTimer->timeToDo())
{
RectF pos(10, 10, 100, 100);
SolidBrush brush(Color::Black);
Font font(FontFamily::GenericSerif(), 30);
hr = g.DrawString(this->introText, -1, &font, pos, StringFormat::GenericDefault(), &brush);
return hr;
}
I am not sure if my code is the only thing that affects the drawing of the string. Maybe there is some configuration or something.
Update
I tried using a negative height as suggested by Hans Passant. The result is that the text is not written at all.
The obvious thing to do would be to set the transformation on GDI+.
Essentially you need to invert the Y axis (though by doing this it would now draw off screen). So you need to then translate it down by the window size.
Something like this:
graphics.Transform = new Matrix2D( 1, 0,
0, -1,
0, -windowHeight );
Then draw as normal.
(Its worth noting I'm suggesting this without testing it. The y translation may not be negative so try both!).
The scanlines are stored upside down, with the first scan (scan 0) in memory being the bottommost scan in the image. This is another artifact of Presentation Manager compatibility. GDI automatically inverts the image during the Set and Get operations.
About the suggestion from Hans Passant, did you try negative height in position?
He suggests to use negative height in bih.
BITMAPINFOHEADER bih = m_videoInfo.bmiHeader;
bih.biHeight = -bih.biHeight;
Bitmap bmp(bih.biWidth, bih.biHeight, m_stride, m_pixFmt, pBuffer);
...
Also copying another solution from this page if it helps
void BitmapControl::OnPaint()
{
EnterCriticalSection (&CriticalSection);
if (_handleBMP)
{
CPaintDC dc(this);
//dc.SetMapMode(MM_ISOTROPIC);
dc.SetMapMode(MM_LOENGLISH);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CRect rect;
GetClientRect(&rect);
dc.DPtoLP(&rect);
CBitmap* pBmpOld = dcMem.SelectObject(CBitmap::FromHandle(_handleBMP));
//tst
dc.SetStretchBltMode(COLORONCOLOR);
//BitBlt(dc,rect.left,-0,rect.Width(),rect.Height(),dcMem,rect.left,rect.top,SRCCOPY); //works with MM_TEXT but upsidedown
BitBlt(dc,0,rect.bottom,rect.Width(),-rect.Height(),dcMem,0,0,SRCCOPY); //works with MM_LOENGLISH
dcMem.SelectObject(pBmpOld);
DeleteDC(dc);
DeleteDC(dcMem);
DeleteObject(_handleBMP);
DeleteObject(pBmpOld);
_handleBMP = NULL;
}
LeaveCriticalSection (&CriticalSection);
}
Does anyone know how I can create a Completely transparent image or given am image (HBITMAP) how can I wipe it Completely so that all pixels in it are 100% transparent?
Thank you.
The bitmap needs to be 32-bit so it has an alpha channel that you can set opacity values with. If your image is not 32-bit, you will have to create a new 32-bit bitmap and copy the original pixels into it.
I also found the solution without the use of GDI+.
BITMAPV5HEADER bi = {sizeof(BITMAPV5HEADER), 320, 240, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
HDC hDc = ::CreateCompatibleDC(0);
if(NULL != hDc)
{
RGBQUAD* pArgb;
HBITMAP tBmp = ::CreateDIBSection(hDc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pArgb, NULL, 0);
if(NULL != tBmp)
{
int x, y;
DWORD pBits;
pBits = (DWORD*)pArgb;
for(y = 0; y < bi.bV5Height; y++)
{
for(x = 0; x < bi.bV5Width; x++)
{
*pBits = 0;
pBits++;
}
}
}
::DeleteObject(hDc);
}
tBmp will be a 320 x 240 bitmap that is completely transparent
You can use a monochrome bitmap as a mask to create transparent images from colour ones. This is quite complex. See this example (in VB but uses Win32 APIs directly)
Alternatively, the TransparentBlt function might make what you're trying to do unnecessary.
I found the answer in GDI+. The best way to do this is to attach the device context of the image that you are working with with GDI+ graphic object and then call its clear method.