Page 1 of 2

How to get screen pixel color?

Posted: Fri Jan 16, 2009 6:52 pm
by terier
I was wondering: does a function like
SColor IVideoDriver::getPixelScreen(position2d<s32>);
exist? I'm sure there's a way. I get what I need with creating IImage with IVideoDriver::createScreenshot() and using it's method getPixel() but this is very slow and unefficient ...

Posted: Fri Jan 16, 2009 6:54 pm
by Dorth
Instead, try rendering to a texture, locking said texture, finding your pixel, etc., and when you're done, unlocking the texture and drawing the texture to the "real" renderer. Of course, shaders are an even faster way of doing that.

Posted: Sat Jan 17, 2009 1:04 pm
by terier
Can I render to a screen-sized texture? What's the purpose of locking a texture? And can you write a shader for me (I don't know how to write shaders)?

Posted: Sat Jan 17, 2009 2:19 pm
by skumar
createScreenShot function of the video driver can be used to get the screen as an image in the memory. Then you can get the desired pixed. Please read API regarding IImage class and above stated function.

But this will surely slow down the app.It is not recommended for realtime check .....

Posted: Sat Jan 17, 2009 3:12 pm
by terier
I tried createScreenShot and getPixel but it works with 2 FPS ... yes I need a realtime check

Posted: Sat Jan 17, 2009 4:19 pm
by Acki
if you're under Windows, then maybe this can help you ;) :

Code: Select all

// link against Gdi32.lib (libGdi32.a) !!!

#include <windows.h> 

HDC grabDesktop() {
  HWND hwnd;
  HDC ddc;
  HDC mdc;
  RECT sz;
  HBITMAP hbmp;
  hwnd = GetDesktopWindow();
  ddc = GetDC(hwnd);
  GetWindowRect(hwnd, &sz);
  mdc = CreateCompatibleDC(ddc);
  hbmp = CreateCompatibleBitmap(ddc, sz.right, sz.bottom);
  SelectObject(mdc, hbmp);
  BitBlt(mdc, 0, 0, sz.right, sz.bottom, ddc, 0, 0, SRCCOPY);
  ReleaseDC(hwnd, ddc);
  return (mdc);
} 

int main(int argc, char* argv[]) {
  HDC hDC;
  COLORREF cref;

  // to get the pixel at 100,100
  int posX = 100; 
  int posY = 100;

  hDC = grabDesktop();
  cref = GetPixel(hDC, posX, posY);
  printf("%02X %02X %02X", GetRValue(cref), GetGValue(cref), GetBValue(cref)); 
}

Posted: Sat Jan 17, 2009 4:32 pm
by Dorth
Why are you creating a screenshot???

virtual bool irr::video::IVideoDriver::setRenderTarget ( video::ITexture * texture,
bool clearBackBuffer = true,
bool clearZBuffer = true,
SColor color = video::SColor(0, 0, 0, 0)
)

Just reuse the same texture, that way it won't keep creating a new texture in memory. Then you call
virtual void* irr::video::ITexture::lock ( bool readOnly = false )
So you can access the individual pixel of the textures with the void* returned.

When you are done, use
virtual void irr::video::ITexture::unlock ( )
to tell Irrlicht the texture is accessible again.

Then you can use setrendertarget again to put the screen buffer back again.

Then you use virtual void irr::video::IVideoDriver::draw2DImage ( const video::ITexture * texture,
const core::position2d< s32 > & destPos
)

To draw back the texture on the screen buffer.

You don't need a screenshot, this is terribly wasteful, and no, I won't write you a shader, I've got other things to do and this should have all went into beginner's help.

Posted: Sat Jan 17, 2009 4:59 pm
by terier
thank you very much, Acki

Posted: Sat Jan 17, 2009 7:38 pm
by Acki
terier wrote:thank you very much, Acki
you're welcome !!! ;)

but there seems to be an error with the code I posted before...
I now changed it and made it a bit more usable for your needs:

Code: Select all

irr::video::SColor getPixelColor(irr::core::position2d<s32> pos){
  static HWND hwnd = GetDesktopWindow();
  static HDC ddc = GetDC(hwnd);
  static HDC mdc = CreateCompatibleDC(ddc);
  RECT sz;
  GetWindowRect(hwnd, &sz);
  HBITMAP hbmp = CreateCompatibleBitmap(ddc, sz.right, sz.bottom);
  SelectObject(mdc, hbmp);
  BitBlt(mdc, 0, 0, sz.right, sz.bottom, ddc, 0, 0, SRCCOPY);
  COLORREF cref;
  cref = GetPixel(mdc, pos.X, pos.Y);
  DeleteObject(hbmp);
  return irr::video::SColor(255, GetRValue(cref), GetGValue(cref), GetBValue(cref));
}

Posted: Sun Jan 18, 2009 9:17 pm
by Acki
Acki wrote:but there seems to be an error with the code I posted before...
yeah, found the error in the first code (stupid me released a context that must be deleted) !!! :lol:

now here is the clean and corrected code:

Code: Select all

video::SColor getPixel(core::position2d<s32> pos){
  static HWND hwnd = GetDesktopWindow();
  HDC ddc = GetDC(hwnd);
  HDC mdc = CreateCompatibleDC(ddc);
  RECT sz;
  GetWindowRect(hwnd, &sz);
  HBITMAP hbmp = CreateCompatibleBitmap(ddc, sz.right, sz.bottom);
  SelectObject(mdc, hbmp);
  BitBlt(mdc, 0, 0, sz.right, sz.bottom, ddc, 0, 0, SRCCOPY);
  COLORREF cref;
  cref = GetPixel(mdc, pos.X, pos.Y);
  DeleteObject(hbmp);
  DeleteDC(mdc);
  ReleaseDC(hwnd, ddc);
  return video::SColor(255, GetRValue(cref), GetGValue(cref), GetBValue(cref));
}

Posted: Sun Jan 18, 2009 9:45 pm
by hybrid
Why would that code be any better than rendering to texture and using that data? At least it would be portable as well.

Posted: Sun Jan 18, 2009 9:57 pm
by Acki
hybrid wrote:Why would that code be any better than rendering to texture and using that data?
Speed !?!?! :shock:

Posted: Sun Jan 18, 2009 10:10 pm
by Dorth
No, the irrlicht implementation I suggest keep reusing the same texture and the texture draw to the screen is lighting fast. You'll maybe gain 1-2 fps on a 100, no guarantee, and you lose all portability for it AND mix exterior rendering libs with irrlicht.

Posted: Sun Jan 18, 2009 10:25 pm
by Acki
well, I'm too lazy for this, but maybe you write the same function but using RTT, so we can make a benchmark comparison... ;)

Posted: Mon Jan 19, 2009 1:38 am
by BlindSide
Make sure to use readOnly = true if you don't want to re-upload the entire texture again every frame.

I'd be interested to see a performance comparison with Acki's method too. Does it work with all drivers?