GetPixel() and multithreading

Discuss other programming languages besides AutoHotkey
TadGhostal
Posts: 15
Joined: 25 Dec 2014, 08:24

GetPixel() and multithreading

23 Jan 2015, 02:50

I have a script that uses PixelGetColor to get the color of 10 pixels (using the normal method which uses GetPixel), and I want to speed it up. I'd like to do that part in C++ using multiple threads, but I don't have much experience with GDI, and I'm not sure if it's even possible. MSDN says that the device context handle returned by GetDC() can only be used from one thread at a time, so is there any way at all to use GetPixel from multiple threads at the same time?

Edit: here's the code that I would use, if only that damn DC could be used from multiple threads. The idea is to detect if any of those pixels is 0 (black).

Code: Select all

#include "Windows.h"
#include <thread>
#include <atomic>

using namespace std;
inline int round_int(float f) {return f + 0.5f;}

const int scrw(GetSystemMetrics(SM_CXSCREEN)), scrh(GetSystemMetrics(SM_CYSCREEN));
const struct {float x; float y;} coords[] = { 
	{7.7083f, 7.4074f}, 
	{6.4583f, 94.2592f},
	{45.5208f, 77.1296f}, 
	{54.375f, 78.1481f},	// dialogue (1044, 844)
	{46.875f, 75.3703f},	// book (900, 814)
	{21.7187f, 19.7222f},	// loot (417, 213)
	{49.7395f, 22.6851f},	// loot (955, 245)
	{49.6875f, 22.6851f},	// loot (954, 245)
	{49.7395f, 23.7962f},	// loot (955, 257)
	{50.0f, 23.2407f}		// loot (960, 251)
};

int main()
{
	atomic_int ret, numthreads;
	HDC scrdc(GetDC(NULL));
	if(!scrdc) return 0xdead;
	if(!GetPixel(scrdc, 1, 0)) return 1;

	ret = numthreads = 0;
	for(auto &coord : coords)
		thread([&]{
			if(!ret)
			{
				numthreads++;
				if(!GetPixel(scrdc, round_int(scrw * coord.x / 100), round_int(scrh * coord.y / 100)))
					ret = 1;
				numthreads--;
			}
		}).detach();

	while(numthreads) Sleep(20);
	ReleaseDC(NULL, scrdc);
	return ret;
}
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: GetPixel() and multithreading

23 Jan 2015, 18:57

You could get one device context handle (HDC) for each thread. I wouldn't recommend it.

Are you using Windows XP? If not, GetPixel itself is probably your biggest problem.

When desktop composition is enabled (such as on all Windows 8+ systems and most Vista/7 systems), there is a bitmap (or texture surface in video memory) for each window. As I understand it, to get a pixel from "the screen", the system must first combine all of these surfaces into one bitmap. This is done each time GetPixel is called, making it very, very slow when desktop composition is enabled. Good search terms for finding more about this are "GetPixel" and "Aero".

I think that if you GetPixel directly from a window or bitmap, this "composition" step isn't needed, so it's not so slow. For instance, see PixelColorSimple().

Even if you're reading from the screen, if you're reading multiple pixels it can be faster to read the whole screen into a bitmap and then get pixels from that. I couldn't find any examples on the forum, but you could try Gdip_BitmapFromScreen() and Gdip_GetPixel() from Gdip.ahk.
TadGhostal
Posts: 15
Joined: 25 Dec 2014, 08:24

Re: GetPixel() and multithreading

25 Jan 2015, 09:58

Thanks for the reply. I had no idea Gdip.ahk existed, that's good to know. I'm using Win7, but I'm actually reading pixels from the window of a game (Dragon Age: Inquisition), which is running in windowed fullscreen mode, so I don't know if desktop composition plays much of a role here. In any case, it's become clear that going through a DC and using GetPixel is not a good idea for what I'm trying to do here, which is to gain as much performance as possible (the script should ideally do this operation as many times a second as possible).

I decided to bite the bullet and try to access the bitmap directly, which is much faster, and probably the right way to go in this situation. I'm using the following code to obtain the array of pixels:

Code: Select all

HDC scrdc = GetDC(FindWindowA("Dragon Age: Inquisition", NULL));
HBITMAP bmp = CreateCompatibleBitmap(scrdc, scrw, scrh);
HDC memdc = CreateCompatibleDC(scrdc);
SelectObject(memdc, bmp);
BitBlt(memdc, 0, 0, scrw, scrh, scrdc, 0, 0, SRCCOPY);

BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
GetDIBits(memdc, bmp, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
RGBQUAD *pixels = new RGBQUAD[scrw * scrh];
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = abs(bmi.bmiHeader.biHeight);
GetDIBits(memdc, bmp, 0, bmi.bmiHeader.biHeight, pixels, &bmi, DIB_RGB_COLORS);
That's working beautifully, but the pixels in the array don't seem to have the colors I expect them to, which I imagine is probably because they are not organized the way I think they are. I've saved the raw pixel data to a file, as well as a proper .bmp file, and then compared the two. I opened the .bmp file in an image editor, and verified that a certain pixel was black, as expected. I then located that pixel in the raw data file, and it was some random color instead of black.

I'm working with the assumption that the pixels are organized in consecutive lines, starting from top to bottom (unless BITMAPINFO::bmiHeader.biHeight is negative, in which case it begins from the bottom). This is the code/logic I'm using to access a pixel of (x,y) coordinates:

Code: Select all

const int ix = scrw * y + x - (y>0);
if(!pixels[ix].rgbBlue && !pixels[ix].rgbGreen && !pixels[ix].rgbRed)
	ret = 1;
I'm currently in the process of trying to figure out what's going on. Any idea what that might be? :)

Edit: Gdip.ahk opened my eyes to the wonders of GDI+, so I've constructed a Bitmap object from the BITMAPINFO and the pixel data, and now I'm using its GetPixel method to read the pixels. It works great! The GDI+ overhead seems minimal, and it seems to be OK with concurrency. When using sequential calls to GetPixel with a DC, the whole thing took about 380ms on average. Now it's about 40ms on average, and I've discovered that as fast as possible is actually too fast, so I've put it on a 100ms timer within the script. Anyway, thanks for the help!

2nd edit: In case anyone cares, the problem was that the bitmap was bottom-up after all (I misread the documentation). I am now reading the pixel colors directly from the array, and I've dropped the multithreading since it's pointless.

Return to “Other Programming Languages”

Who is online

Users browsing this forum: No registered users and 38 guests