Get Pixel color fastest way?
Asked Answered
P

2

9

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.

Pesek answered 9/5, 2012 at 12:0 Comment(5)
When your code is called? in idle time? when the user moves the mouse? There are different ways to detect a red button, using FindWindow() for example.Hifalutin
Btw, my code is in an infinite loop. But I want to use a color catcher whereas a offset one. So, when I start my code, it just go into the loop and check for the red button.Pesek
If this code is in an infinite loop, then the problem is not GetPixel. It's the fact that your app is not letting other apps have any CPU time.Toliver
Btw, all my stuff is in a Thread. And as I said, it works well with getPixel. i'm just looking for a better way to do thatPesek
It doesn't work well -- you said it's slow. Without profiling data, I gave the best reason it's slow.Toliver
P
17

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.

Pesek answered 9/5, 2012 at 12:59 Comment(8)
is this checking every pixel? I don't understand your x and y assignments.Foust
X and Y are the location of the pixel being reviewed. They are useless, unless you want to do an action on this pixel. If its color is full red, it will set the mouse to this pixel and do a left click.Pesek
Nice work, but there is something I do not understand. Why take into account byte assigned to the Alpha component of the pixel? Because as it is a screenshot, the value of alpha will always be 255, right? So, Would not it be better make the biBitCount parameter worth 24?Apothegm
You are perfectly right. The Aplha part is useless.Pesek
GetWindowRect with HWND_DESKTOP handle returns FALSE. need to add hWND_Desktop = GetDesktopWindow()? or use GetSystemMetrics() instead..Leister
Why do you have to multiply the biSizeImage by 4?Astaire
Red and blue need to be swapped: blue = (int)bitPointer[i];green = (int)bitPointer[i + 1];red = (int)bitPointer[i + 2];Calisaya
Unfortunatelly it is still slow for my purporse. I want to set mouse up in the exact same time that the pixel gets a specific color. But my context is when someone is drawing on the screen. If the user is drawing much fast with their hand, the method only triggers the event after the user already passed this pixel. I need to disable the mouse in the exact moment the pixel is colored so the user can't paint the next pixels. I think there is no way to achieve it.Jadeite
O
0

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.

Overarm answered 9/5, 2012 at 12:56 Comment(2)
Hello, Can you tell me more about Findwindow? I'm using it like that: mhwnd = FindWindow(NULL, "Application Window Name"); hdc = GetDC(mhwnd); What's next?Pesek
The button you want to click probably has a window handle of its own. Try using a window inspector program such as Spy++ to find the window handle of the thing you want to click, and you can probably locate that specific item with some clever code.Overarm

© 2022 - 2024 — McMap. All rights reserved.