1
\$\begingroup\$

I have this code that is working as intended I would just like to speed it up.

#include <windows.h>

void pressW();

int main(int argc, char** argv) {

    DWORD x = 566;
    DWORD y = 967;
    DWORD red = 2039797;
    DWORD gold = 43963;
    DWORD blue = 15715270;

    while (true) {
        if (GetAsyncKeyState(0x31)) {
            pressW();
            while (true) {
                HDC dc = GetDC(NULL);
                COLORREF color = GetPixel(dc, x, y);
                ReleaseDC(NULL, dc);
                if (color == blue) {
                    pressW();
                    break;
                }
            }
        }
        else if (GetAsyncKeyState(0x32)) {
            pressW();
            while (true) {
                HDC dc = GetDC(NULL);
                COLORREF color = GetPixel(dc, x, y);
                ReleaseDC(NULL, dc);
                if (color == red) {
                    pressW();
                    break;
                }
            }
        }
        else if (GetAsyncKeyState(0x33)) {
            pressW();
            while (true) {
                HDC dc = GetDC(NULL);
                COLORREF color = GetPixel(dc, x, y);
                ReleaseDC(NULL, dc);
                if (color == gold) {
                    pressW();
                    break;
                }
            }
        }
    }
}

void pressW() {
    INPUT i;
    i.type = INPUT_KEYBOARD;
    i.ki.wScan = MapVirtualKey(0x57, MAPVK_VK_TO_VSC);
    i.ki.time = 0;
    i.ki.dwExtraInfo = 0;
    i.ki.wVk = 0x57; 
    i.ki.dwFlags = 0;
    SendInput(1, &i, sizeof(INPUT));
    i.ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(1, &i, sizeof(INPUT));
}

Basically what it does is when I press 1/2/3 it presses W and waits for the the pixel at x, y coordinates to change color corresponding to what key I pressed and presses W again.

I want it to have 10-15ms delay from the point when I press 1/2/3 to when it scans the pixel and presses the 2nd W, but I don't really know how to optimize this. Right now it's taking a snapshot of the entire screen but I just want to check that single pixel, also it's declaring everything for the sendinput every time I call that function because simply putting

SendInput(1, &i, sizeof(INPUT));
i.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &i, sizeof(INPUT));

in the pressW() didn't work.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Yes, GetPixel is very slow. You could possibly speed things up by copying the screen's contents to a DIB section and then using pointer arithmetic to extract the appropriate per-pixel color data. But this still won't be blazingly fast, and more importantly, it is unclear to me why this design even makes sense. You should know what color the pixel is, because your code is what's making it that color! \$\endgroup\$
    – Cody Gray
    Jan 3 '17 at 14:24
  • \$\begingroup\$ @CodyGray I'm causing the pixel to change, but I'm not changing it myself, there isn't any SetPixel() functions in the code doing that. When I press 1/2/3 the code presses W and that begins sort of a cycle where the pixel changes every some milliseconds and then when it's at the corresponding color it will press W again. Basically what I want to do is somehow search for only that single pixel and not the entire screen. \$\endgroup\$
    – edsheeran
    Jan 3 '17 at 15:29
  • \$\begingroup\$ I still don't quite understand what is happening, then. You are writing code that automates some third-party application? The third-party application does something that causes a color change when the 'W' key is pressed, and you want to detect that color-change? \$\endgroup\$
    – Cody Gray
    Jan 3 '17 at 15:31
  • \$\begingroup\$ Yeah, that's what I'm trying to do, but the method above is too slow for me. \$\endgroup\$
    – edsheeran
    Jan 3 '17 at 15:36
2
\$\begingroup\$

The Fundamentals: Style & Readability

Before we get into optimizing, there are a couple of glaring problems with your coding style—that is, the way the code is written and the way it looks.

  • The biggest problem is that there is a lot of duplication. You repeat yourself over and over inside the if blocks of the main function. Any time you see repeated code, you should extract it out into a function. Even if there are some subtle differences between the various blocks of code, this can be accommodated by passing parameters to the function to customize its behavior. You want to be DRY—don't repeat yourself. It just makes the code harder to read, harder to maintain, and increases the chances for bugs to slip in.

  • Variables that never change their values are called constants, and the C++ language has a special way to indicate such variables: the const keyword. Using this helps to prevent bugs, like accidentally writing code that changes a variable's value, when that variable is supposed to be constant.

    Thus, the x, y, red, gold, and blue variables declared at the top of the main function should all be const, since these are effectively read-only. Also, I would personally recommend making even local variables whose values do not change after initial initialization (such as hdc and color) explicitly const.

    (Also, I'm a little bit obsessive-compulsive, so I like to vertically align all of my assignments. Others may disagree that this is worth the effort, but I use tools in my editor that semi-automate it. I find that it not only makes the code look better overall, but makes inconsistencies—i.e., possible errors—more likely to be caught because they stick out like a sore thumb.)

  • Speaking of the red, gold, and blue variables, these appear to be magic numbers, the values of which would defy most people's intuition. Why, exactly, would 2039797 be a representation of red?

    Instead, you should be declaring these color values using the Win32 RGB macro. This takes individual red, green, and blue color components (a representation with which we are all familiar), and packs them into a 32-bit DWORD. Not only is this more readable, but it also ensures that your code is endianness-independent. The Platform SDK determines whether the encoding is RGB or BGR (in the case of Win32, it is actually BGR).

    Furthermore, you should prefer to use the COLORREF type for color values in Windows. While COLORREF is simply a typedef for DWORD, it is clearer and more semantically accurate to use COLORREF. Besides, you used the COLORREF type to store the return value of GetPixel, so you should do it here for the comparison constants in the interest of consistency.

  • In C++, you should be taking advantage of the RAII paradigm to produce cleaner, more correct code. What I mean by this is that any time you have an unmanaged object (such as a Windows handle), you should wrap that object in a class. Make the class's constructor responsible for allocating the object, and the class's destructor responsible for deallocating/freeing the object. When an object of that class is declared, its unmanaged resources are automatically initialized; when that object goes out of scope, its unmanaged resources are automatically released. This way, the compiler does all of the book-keeping automatically, and you never leak.

    Your code as written is actually correct, in that you call ReleaseDC for each call to GetDC, but I bet you had to think about this to make sure that you got it right. Best not to make yourself think so hard. Also, using RAII ensures that your code will be exception safe. (Again, not a problem in this case, since the only code in between GetDC and ReleaseDC is a call to GetPixel and an assignment to a POD type, neither of which can throw, but it's all too easy to add something later and forget to audit carefully for exception safety.)

    Most people use a framework like MFC or WTL that provides the wrapper classes for you, but if you're just writing a simple app, you don't necessarily need anything that complicated. It only takes a few lines of code to write a simple wrapper class that does everything you need:

    class DC
    {
      public:
    
        explicit DC(HWND hWnd)
            : m_hWnd(hWnd)
            , m_hDC (GetDC(hWnd))
        { }
    
    
        ~DC()
        {
            ReleaseDC(m_hWnd, m_hDC);
        }
    
    
        HDC Handle()
        {
            return m_hDC;
        }
    
    
      private:
    
        HWND m_hWnd;
        HDC  m_hDC;
    };
    

    You could improve this substantially by adding error checking—at the very least, you should probably have an assert that checks for invalid handles and/or failed function calls in debugging builds.

  • Instead of using the sizeof operator on the type, prefer to use the sizeof operator on the object. For example, change sizeof(INPUT) to sizeof(i). That way, if i ever changes to, say INPUTEX, there will be fewer places in the code that you have to remember to change.

  • Don't repeat constant literals throughout the code. To be fair, you've only done it one place—inside the pressW function by hardcoding the value 0x57. The damage is relatively contained, but still, 0x57 is repeated twice and this makes changing it in the future an error-prone task. Declare a constant instead.

    Better yet, replace the inscrutable 0x57 with 'W'. In other words, let the compiler do the evaluation and determine what the ASCII code of the W character is. The result is the same, but the code is significantly more readable. (Also do the same thing for 0x31, 0x32, and 0x33.)

    In most cases, the pressW function should probably be pressKey, and take the virtual-key code as a parameter. Personally, I would prefer writing it this way from the get-go because it's no harder. But you do have to be careful not to make the code you're writing overly generic, because that wastes development time for things you may not ever need (see also YAGNI).

One thing you are doing well, though, is following a consistent style throughout. While K&R is not my preferred brace style, you are using it consistently, and that is really what's most important.

Taking these points into account, here is how I would revise your current implementation:

#include <windows.h>

void pressW();
void waitForColorChange(DWORD x, DWORD y, COLORREF expectedColor);

class DC
{
  public:

    explicit DC(HWND hWnd)
        : m_hWnd(hWnd)
        , m_hDC (GetDC(hWnd))
    { }

    ~DC() {
        ReleaseDC(m_hWnd, m_hDC);
    }

    HDC Handle() {
        return m_hDC;
    }

  private:

    HWND m_hWnd;
    HDC  m_hDC;
};


int main(int argc, char** argv) {
    const DWORD    x    = 566;
    const DWORD    y    = 967;
    const COLORREF red  = RGB(245,  31,  31);
    const COLORREF gold = RGB(187, 171,   0);
    const COLORREF blue = RGB(198, 203, 239);

    while (true) {
        if (GetAsyncKeyState('1')) {
            waitForColorChange(x, y, blue);
        }
        else if (GetAsyncKeyState('2')) {
            waitForColorChange(x, y, red);
        }
        else if (GetAsyncKeyState('3')) {
            waitForColorChange(x, y, gold);
        }
    }
}

void pressW() {
    const WORD vkW = 'W';
    INPUT i;
    i.type           = INPUT_KEYBOARD;
    i.ki.wScan       = MapVirtualKey(vkW, MAPVK_VK_TO_VSC);
    i.ki.time        = 0;
    i.ki.dwExtraInfo = 0;
    i.ki.wVk         = vkW;
    i.ki.dwFlags     = 0;
    SendInput(1, &i, sizeof(i));
    i.ki.dwFlags     = KEYEVENTF_KEYUP;
    SendInput(1, &i, sizeof(i));
}

void waitForColorChange(DWORD x, DWORD y, COLORREF expectedColor) {
    pressW();
    while (true) {
        DC dc(NULL);
        const COLORREF color = GetPixel(dc.Handle(), x, y);
        if (color == expectedColor) {
            pressW();
            break;
        }
    }
}

There are, of course, other equally valid ways of refactoring your code, but the important thing is to be DRY! The above example achieves that, and without significantly changing the object code generated by a compiler (in other words, without slowing anything down at run-time).


Going Deeper: Cautions & Potential Optimizations

  • The GetAsyncKeyState function actually is asynchronous, meaning that if you call it twice in a row, you will get two different results if the keyboard state changes between those calls. If you're not very careful, this can result in glitchy behavior. You are probably okay here, but this is something to watch out for.

    The call also isn't free. Spinning in a busy loop, repeatedly calling GetAsyncKeyState is a good way to burn a bunch of CPU cycles and turn your computer into a space heater. A better design would be to listen for notifications of a key press event, rather than polling for a state change.

  • Since you're not passing either the KEYEVENTF_SCANCODE or KEYEVENTF_UNICODE flags, you can avoid computing the scan code and initializing that field each time you call SendInput. This won't save you a whole lot of time, but it does elide one function call.

    Even if you needed to initialize the scan-code field, you could save yourself some time by calling the MapVirtualKey function once and caching its result in a static (or global) variable. The mapping of virtual-key codes to scan codes is not going to change while your application is running, so there's no need to call the function each time.

  • You can save another function call by collapsing the two calls to SendInput into one. This function allows you to pass an array of INPUT structures, and it will synthesize a separate event for each one. The key-down event goes in the first INPUT structure, and the key-up event goes in the second INPUT structure. The required modifications to the code are trivial, but just in case:

    void pressW() {
        const WORD vkW = 'W';
    
        INPUT i[2];
        i[1].type           = INPUT_KEYBOARD;
        i[1].ki.wVk         = vkW;
        i[1].ki.time        = 0;
        i[1].ki.dwExtraInfo = 0;    
        i[1].ki.dwFlags     = 0;
    
        i[2].type           = INPUT_KEYBOARD;
        i[2].ki.wVk         = vkW;
        i[2].ki.time        = 0;
        i[2].ki.dwExtraInfo = 0;    
        i[2].ki.dwFlags     = KEYEVENTF_KEYUP;
    
        SendInput(ARRAYSIZE(i), i, sizeof(i));
    }
    

    Notice that I've used the ARRAYSIZE macro (defined in the Win32 SDK headers) to automatically determine the size of the array so I don't have to hard-code another constant. This looks like more code, but it actually executes more quickly because the cost of initializing that extra INPUT structure is dwarfed by the cost of a function call.

    If you really wanted to go all out, you could recognize that the values of these INPUT structures never change and decide to make it statically-initialized. This saves the compiler from having to emit code that performs the initialization before each call to SendInput. It simply pushes a pointer to constant data in the executable's DATA segment and calls SendInput. Micro-optimization at its finest! A different syntax will be required for this static initialization:

    void pressW() {
        const WORD vkW = 'W';
        static INPUT i[2] = {
           { INPUT_KEYBOARD, { vkW, 0, 0,               0 } },
           { INPUT_KEYBOARD, { vkW, 0, KEYEVENTF_KEYUP, 0 } },
        };
        SendInput(ARRAYSIZE(i), i, sizeof(i));
    }
    

    (I really wanted to make i const there, but I can't safely do so because the SendInput function expects a non-const pointer to the array of INPUT structures. I could reasonably assume that the function is not going to modify them, but I cannot prove that without inside knowledge of its implementation, so I cannot rely on it.)

  • Acquiring and releasing a device context (GetDC/ReleaseDC) are not particularly expensive operations, but they aren't exactly free, either. Therefore, it makes good sense to minimize the number of times you do this cycle. It certainly isn't necessary to do it on each iteration of a tight spin loop! The waitForColorChange function that I wrote could do it at the top, then enter the while loop, and only release the DC after it breaks. Using RAII, this is simple:

    void waitForColorChange(DWORD x, DWORD y, COLORREF expectedColor) {
        pressW();
        DC dc(NULL);
        while (true) {
            if (GetPixel(dc.Handle(), x, y) == expectedColor) {
                pressW();
                break;
            }
        }
    }
    

    But actually, this waitForColorChange function is itself called from within a tight spin loop! In fact, your code could be revised such that the DC is acquired at the beginning of the program, used throughout its lifetime, and released when your program exits (which will happen automatically, thanks to RAII). This will require a bit of refactoring—a const reference to the DC object will need to be passed to the waitForColorChange function—but doesn't make the code any less readable.

    The only concern is that, generally speaking, device contexts shouldn't be held onto any longer than necessary. It seems justified in this case because you are actively using it the entire time, but that may just speak to a larger design flaw with your application. I've already mentioned my reservations about polling.


The Main Event: Optimizing GetPixel

At the core of your code's logic is a call to GetPixel to retrieve the current color value of a particular pixel on the screen. That is unfortunate if you're concerned about speed, because GetPixel is a very slow function, indeed. Why is it so slow? The above-linked documentation doesn't give much of a hint. In fact, this API has a dirty little secret: individual pixels cannot be directly manipulated by the graphics subsystem, so GetPixel (and its brother, SetPixel) have to simulate it, which makes an ostensibly simple, straightforward function call into an extremely slow operation. In addition to the usual overhead of a function call, parameter validation, and switching to and from kernel mode, the GetPixel function also has to create a temporary bitmap, copy the contents of the DC into that temporary bitmap, perform all necessary color-mapping, map the coordinates and locate the pixel, retrieve its color value, and then destroy the temporary bitmap. A lot of work for a single pixel!

The throughput of GetPixel is on the order of 100,000 pixels per second, which is usually fast enough. If you want to speed up drawing operations that involve the manipulation of multiple pixels, the general strategy is to minimize overhead by copying the DC's contents into a temporary device-independent bitmap (DIB) and then using pointer arithmetic on this DIB to access individual pixels. This gives you direct access to each pixel, and allows a throughput of millions or more pixels per second. The problem for you is that you cannot simply use a cached snapshot of the screen—you really do need to update it each time through the loop, which means that this strategy doesn't save you a whole lot in terms of overhead. But it'll still be slightly faster.

Here's an approximation of the code that would be required:

COLORREF FastGetPixel(DWORD x, DWORD y)
{
   // Retrieve a device context for the screen.
   const HDC hdcScreen = GetDC(NULL);

   // Create a DIB section.
   BITMAPINFO bi;
   bi.bmiHeader.biSize         = sizeof(bi.bmiHeader);
   bi.bmiHeader.biWidth        = 1;
   bi.bmiHeader.biHeight       = 1;
   bi.bmiHeader.biPlanes       = 1;
   bi.bmiHeader.biBitCount     = 32;
   bi.bmiHeader.biCompression  = BI_RGB;
   bi.bmiHeader.biSizeImage    = ((((static_cast<unsigned int>(bi.bmiHeader.biWidth) * 32U + 31U) / 32U) * 4U) * bi.bmiHeader.biHeight);
   bi.bmiHeader.biClrUsed      = 0;
   bi.bmiHeader.biClrImportant = 0;
   BYTE* pBits;   // points to the DIB section's raw bit data
   const HBITMAP hbmpDIB = CreateDIBSection(hdcScreen,
                                            &bi,
                                            DIB_RGB_COLORS,
                                            reinterpret_cast<void**>(&pBits),
                                            NULL,
                                            NULL);

   // Create a memory device context that is compatible with the screen.
   // We need to select our DIB section into this DC so that we can draw onto it.
   // Specifically, we'll copy a 1x1 pixel area from the screen into our DIB section.
   const HDC     hdcMem  = CreateCompatibleDC(hdcScreen);
   const HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdcMem, hbmpDIB));
   BitBlt(hdcMem,    0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight,
          hdcScreen, x, y,
          SRCCOPY);
   SelectObject(hdcMem, hbmpOld);
   GdiFlush();

   // Read the RGB color value of the top-left pixel in our DIB section
   // (which is actually the only pixel that it contains).
   // Note that in a bitmap, the byte order is always RGBA!
   const COLORREF clr = RGB(pBits[2], pBits[1], pBits[0]);

   // Clean up.
   DeleteObject(hbmpDIB);
   DeleteDC(hdcMem);
   ReleaseDC(NULL, hdcScreen);

   return clr;
}

The code can be improved by writing simple C++ wrapper classes to implement automatic resource management, as was discussed above. You should also add error-checking.

To really squeeze speed out of it, you'll need to resort to some ugly tactics. As written, this does much the same thing as the Win32 GetPixel function, and like I mentioned above, your setup is such that you need to call this function repeatedly. The obvious way to make code execute faster is to reduce the amount of code that you have to execute. Here, that means factoring out common tasks and doing them only once:

  • You don't need to retrieve a screen DC and release it each time through the FastGetPixel function. You can retrieve one once at the start of your program, and free it at the end once you're done.
  • The values of the BITMAPINFO structure are not changing, so you could make it static const and initialize it only once, instead of each time. This is basically the same as what we did with the INPUT structure.
  • You don't need to create a new DIB section and free it each time. You can just reuse the same one. Create it once at the start of your program, use it as needed, and free it at the end. (This actually makes the above bullet unnecessary, since the initialization of BITMAPINFO will no longer be on the critical path.)
  • As with the screen DC, you don't need to create a memory DC and free it each time. Keep the same memory DC, with your DIB section selected into it, and re-use it each time. This saves not only repeated calls to CreateCompatibleDC and DeleteDC, but also repeated calls to SelectObject.

Although making all of these objects global is pretty much breaking every design guideline in the book, sometimes performance concerns justify ugliness. Address maintenance concerns by wrapping them in C++ classes, following a "singleton" pattern.

Now, your inner loop will simply call the following function:

// Required global objects
// (shown here as simple global variables, but should really be managed by
// singleton classes to ensure correct lifetime management)
HDC   hdcScreen;
HDC   hdcDIB;
BYTE* pDIBBits;


COLORREF FastIterativeGetPixel(DWORD x, DWORD y)
{
   // Validate global objects' state in debug builds using assertions
   // so you can easily diagnose problems but don't lose performance
   // in release builds.
   // ...

   // Copy the current contents of the screen into our DIB section.
   BitBlt(hdcDIB, 0, 0, 1, 1, hdcScreen, x, y, SRCCOPY);
   GdiFlush();

   // Retrieve the RGB color value of the top-left pixel in our DIB section
   // (which is actually the only pixel that it contains).
   // Note that in a bitmap, the byte order is always RGBA!
   return RGB(pDIBBits[2], pDIBBits[1], pDIBBits[0]);
}

All initialization and clean-up has been deferred, so that all we need to do is a single BitBlt and, to be on the safe side, a GdiFlush. If you were willing to live a bit dangerously, you could omit the flush. (In fact, you're probably safe omitting it in your case, because all that would happen is you'd get stale color values that didn't accurately reflect the current state of the screen, which would just defer your action until the next time through the loop.)

Applying all of the optimizations mentioned in the last two sections, you should see a significant performance gain—an order of magnitude, if you're lucky. But then you will have hit a wall. Real performance gains will only come—as they always do—with reimagining the fundamental algorithm and/or design of your application. Like finding a way to receive notification from the other application when it has changed the color.

\$\endgroup\$
5
  • \$\begingroup\$ "The Platform SDK determines whether the encoding is RGB or BGR (in the case of Win32, it is actually BGR)." Can you elaborate on this for me? I have noticed that the docs say that GetPixel returns RGB, but all of my calls seem to return BGR, and I don't understand what you meant in this post.. \$\endgroup\$
    – mikew
    Oct 28 '19 at 16:07
  • \$\begingroup\$ Actually looking further at MSDN, it says GetPixel returns a COLORREF which is 0x00BBGGRR, so I guess it really is BGR \$\endgroup\$
    – mikew
    Oct 28 '19 at 16:20
  • 1
    \$\begingroup\$ Yes, @mikew, a COLORREF is always in 0x00BBGGRR format on little-endian architectures like x86. This is why it is platform-dependent: the byte order can vary depending on the endianness of the host processor. \$\endgroup\$
    – Cody Gray
    Oct 28 '19 at 17:13
  • \$\begingroup\$ Thanks. And would it still be 0x00BBGGRR on normal intel x64 systems too? \$\endgroup\$
    – mikew
    Oct 29 '19 at 13:41
  • \$\begingroup\$ @mike Yes, what people colloquially call "x64" is the same endianness as x86. It is more accurately "x86-64", a set of 64-bit extensions to the Intel x86 ISA. \$\endgroup\$
    – Cody Gray
    Oct 29 '19 at 19:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.