-1

So I am looking for a way to read a color of a screen pixel in C code.
I already found implementation in C for *nix (which uses X11/Xlib library, that as I understood is for *nix systems only) and I tried the code on a linux machine, and it ran pretty fast (it reads 8K of pixels in about 1 second).
Here's the code in C that I've found and forked:

#include <X11/Xlib.h>
void get_pixel_color (Display *d, int x, int y, XColor *color)
{
   XImage *image;
   image = XGetImage (d, RootWindow (d, DefaultScreen (d)), x, y, 1, 1,        AllPlanes, XYPixmap);
  color->pixel = XGetPixel (image, 0, 0);
  XFree (image);
  XQueryColor (d, DefaultColormap(d, DefaultScreen (d)), color);
 }

 // Your code
 XColor c;
  get_pixel_color (display, 30, 40, &c);
  printf ("%d %d %d\n", c.red, c.green, c.blue);

And I was looking for equivalent solution for Windows as well.
I came across this code (I've put the code about reading screen pixel in a 'for' loop):

FARPROC pGetPixel;

HINSTANCE _hGDI = LoadLibrary("gdi32.dll");
if(_hGDI)
{
    pGetPixel = GetProcAddress(_hGDI, "GetPixel");

    HDC _hdc = GetDC(NULL);
if(_hdc)
{
    int i;
    int _red;
    int _green;
    int _blue;
    COLORREF _color;
    ReleaseDC(NULL, _hdc);
    for (i=0;i<8000;i++)
    {
        _color = (*pGetPixel) (_hdc, 30 ,40);

        _red = GetRValue(_color);
        _green = GetGValue(_color);
        _blue = GetBValue(_color);


    }
    ReleaseDC(NULL, _hdc);    
    printf("Red: %d, Green: %d, Blue: %d", _red, _green, _blue);
}
FreeLibrary(_hGDI);

(using gdi32.dll and windows.h...)
and the 'for' portion of the code (where we read 8K of pixels) runs ALOT slower than the solution in C. it takes 15 seconds to finish compared to 1 second with X11/Xlib.h library!

So, how can I make it better? or there is any other better and FASTER implementation to read pixel's colors with C code in Windows machine?

Thanks ahead!

Matan
  • 59
  • 1
  • 6
  • 2
    Hmya, an 32KB/sec bandwidth isn't much to brag about. Windows requires programmers to be smarter readers, Petzold's book explains BitBlt() well. – Hans Passant May 19 '15 at 23:11
  • How many pixels are you trying to read here? Just 1, or are you going to be checking a whole bunch? If a whole bunch, getPixel is probably not the way to do this, instead something that captures the whole screen to a texture at once. – Michael Dorgan May 20 '15 at 00:34
  • Yeah I am trying to read bunch... As I showed the *nix solution could read alot of pixels in 1 second while the Windows solution is very slow... Any suggestions, other solutions or resources? I've search for better solution in Windows for a while now and I just couldn't find one. – Matan May 20 '15 at 06:20
  • @HansPassaant - Could you elaborate your answer? what do you mean? what is it BitBlt()? how could I read the screen pixels efficiently and fast? Thanks ahead – Matan May 20 '15 at 10:15

2 Answers2

0

I would suggest using loop unwinding. Basically, what this does is execute multiple cycles of your loop in a single iteration:

// Loop the equivalent of `n` cycles, ignoring the least significant bit
for (unsigned int i = 0; i < (n & ~0x01); i += 2)
{
    do_some_operation(i);
    do_some_operation(i + 1);
}

// Perform the last cycle manually, if one needs to be completed
if (n & 0x01)
{
    do_some_operation(n - 1);
}

In this code, the loop ignores the least significant bit of n (which determines the parity of n) so that we are safe to increment i by 2 and perform the equivalent of 2 cycles in just 1 cycle, meaning that this loop is ~2 times faster than a conventional for (unsigned int i = 0; i < n; i++) loop. The final if statement checks the parity of n. If n is odd, the last cycle of the loop is performed.

Of course, this could be reimplemented to increment i by more than 2, but this would become increasingly complex. There is also an alternative to this, Duff's Device. It is basically the same idea, but uses a switch/case block.

Levi
  • 1,921
  • 1
  • 14
  • 18
  • I don't think this is a loop level issue. instead, this is a driver level inefficient way of retrieving info. If OP is going to retrieve many pixels from the screen, he might be better off capturing the screen to a texture and then looking at that instead. – Michael Dorgan May 20 '15 at 00:33
  • I considered taking a picture and then analyze it but the whole process is very slow as well... just taking the picture is about 3 ~ 4 seconds or so. @Levi - the "pGetPixel" function is the one that takes alot of time to finish... I am looking for entirely new implementation or way to read bunch of screen pixel's colors. How come X11/Xlib solution is so fast compared to Window's one? – Matan May 20 '15 at 06:36
  • How come X11/Xlib is faster? – Levi May 20 '15 at 06:39
  • Yeah I agree I prefer Linux as well. But there isn't any way to do it? The differences are ridiculous... Any suggestions? – Matan May 20 '15 at 06:52
  • Sorry, I can't help you there. I am not familiar with WinAPI. But I see @HansPassant mentioned something on BitBlt()? Good luck ;) – Levi May 20 '15 at 06:58
0

After many tests, I've found that just about /anything/ you do to read pixels off the screen in windows using the GDI takes ~16ms (or about 1 frame) whether it is reading a single pixel, or reading even a small area with BitBlt. There doesn't seem to be any clear solution. I will be experimenting with the media libraries to see if I can get anywhere, but the Internet is pretty sure doing anything like this in Windows is just an awful mess, and really terrible things have to be done to do things like VNC or Fraps.

Charles Lohr
  • 695
  • 1
  • 8
  • 23