How can I access a graphics card's output directly?
Solution 1:
I do not code under Linux but in Windows (you are running it in emulator anyway) you can use WinAPI to directly access canvas of any window or even desktop from 3th party App. Some GPU overlays could be problematic to catch (especially DirectX based) but I had no problems with mine GL/GLSL for now.
If you got access to App source you can use glReadPixels
for image extraction from GL directly (but that works only for current GL based rendering).
-
Using
glReadPixels
As mentioned this must be implemented directly in the targeted app so you need to have it source code or inject your code in the right place/time. I use for screenshoting this code:
void OpenGLscreen::screenshot(Graphics::TBitmap *bmp) { if (bmp==NULL) return; int *dat=new int[xs*ys],x,y,a,*p; if (dat==NULL) return; bmp->HandleType=bmDIB; bmp->PixelFormat=pf32bit; if ((bmp->Width!=xs)||(bmp->Height!=ys)) bmp->SetSize(xs,ys); if ((bmp->Width==xs)&&(bmp->Height==ys)) { glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat); glFinish(); for (a=0,y=ys-1;y>=0;y--) for (p=(int*)bmp->ScanLine[y],x=0;x<xs;x++,a++) p[x]=dat[a]; } delete[] dat; }
where
xs,ys
is OpenGL window resolution, you can ignore the wholebmp
stuff (it is VCL bitmap I use to store screenshot) and also can ignore thefor
it just copy the image from buffer to bitmap. So the important stuff is just this:int *dat=new int[xs*ys]; // each pixel is 32bit int glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat); glFinish();
You need to execute this code after the rendering is done otherwise you will obtain unfinished or empty buffer. I use it after redraw/repaint events. As mentioned before this will obtain only the GL rendered stuff so if your App combines GDI+OpenGL it is better to use the next approach.
-
WinAPI approach
To obtain Canvas image of any window I wrote this class:
//--------------------------------------------------------------------------- //--- screen capture ver: 1.00 ---------------------------------------------- //--------------------------------------------------------------------------- class scrcap { public: HWND hnd,hnda; TCanvas *scr; Graphics::TBitmap *bmp; int x0,y0,xs,ys; scrcap() { hnd=NULL; hnda=NULL; scr=new TCanvas(); bmp=new Graphics::TBitmap; #ifdef _mmap_h mmap_new('scrc',scr,sizeof(TCanvas() )); mmap_new('scrc',bmp,sizeof(Graphics::TBitmap)); #endif if (bmp) { bmp->HandleType=bmDIB; bmp->PixelFormat=pf32bit; } x0=0; y0=0; xs=1; ys=1; hnd=GetDesktopWindow(); } ~scrcap() { #ifdef _mmap_h mmap_del('scrc',scr); mmap_del('scrc',bmp); #endif if (scr) delete scr; scr=NULL; if (bmp) delete bmp; bmp=NULL; } void init(HWND _hnd=NULL) { RECT r; if (scr==NULL) return; if (bmp==NULL) return; bmp->SetSize(1,1); if (!IsWindow(_hnd)) _hnd=hnd; scr->Handle=GetDC(_hnd); hnda=_hnd; resize(); } void resize() { if (!IsWindow(hnda)) return; RECT r; // GetWindowRect(hnda,&r); GetClientRect(hnda,&r); x0=r.left; xs=r.right-x0; y0=r.top; ys=r.bottom-y0; bmp->SetSize(xs,ys); xs=bmp->Width; ys=bmp->Height; } void capture() { if (scr==NULL) return; if (bmp==NULL) return; bmp->Canvas->CopyRect(Rect(0,0,xs,ys),scr,TRect(x0,y0,x0+xs,y0+ys)); } }; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //---------------------------------------------------------------------------
Again it uses VCL so rewrite bitmap
bmp
and canvasscr
to your programing environment style. Also igneore the_mmap_h
chunks of code they are just for debugging/tracing of memory pointers related to some nasty compiler bug I was facing at that time I wrote this.The usage is simple:
// globals scrcap cap; // init cap.init(); // on screenshot cap.capture(); // here use cap.bmp
If you call
cap.init()
it will lock on the whole windows desktop. If you callcap.init(window_handle)
it will lock on specific visual window/component instead. To obtain window handle from 3th app side see:- is ther a way an app can display a message without the use of messagebox API?
Sorry it is on SE/RE instead of here on SE/SO but My answer here covering this topic was deleted. I use this for video capture ... all of the animated GIFs in my answers where created by this code. Another example could be seen on the bottom of this answer of mine:
- Image to ASCII art conversion
As you can see it works also for DirectX overlay with media player classic (even Windows PrintScreen function cant do it right). As I wrote I got no problems with this yet.
Beware visual stuff WinAPI calls MUST BE CALLED FROM APP MAIN THREAD (WNDPROC) otherwise serious problems could occur leading to random unrelated WinAPI calls exceptions anywhere in the App.