使用glReadPixels
如前所述,这必须直接在目标应用中实现,因此您需要拥有它的源代码或在正确的位置/时间注入您的代码。我用来截取这段代码:
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;
}
其中xs,ys 是OpenGL 窗口分辨率,您可以忽略整个bmp 内容(它是我用来存储屏幕截图的VCL 位图),也可以忽略for,它只是将图像从缓冲区复制到位图。所以重要的事情就是这样:
int *dat=new int[xs*ys]; // each pixel is 32bit int
glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat);
glFinish();
您需要在渲染完成后执行此代码,否则您将获得未完成或空缓冲区。我在重绘/重绘事件后使用它。如前所述,这将仅获得 GL 渲染的内容,因此如果您的应用程序结合了 GDI+OpenGL,则最好使用下一种方法。
WinAPI 方法
要获取我写的这个类的任何窗口的画布图像:
//---------------------------------------------------------------------------
//--- 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));
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
它再次使用 VCL,因此将位图bmp 和画布scr 重写为您的编程环境风格。也忽略_mmap_h 代码块,它们只是用于调试/跟踪与我在写这篇文章时遇到的一些讨厌的编译器错误相关的内存指针。
用法很简单:
// globals
scrcap cap;
// init
cap.init();
// on screenshot
cap.capture();
// here use cap.bmp
如果您调用cap.init(),它将锁定整个 Windows 桌面。如果您调用cap.init(window_handle),它将锁定特定的可视窗口/组件。要从第 3 个应用程序端获取窗口句柄,请参阅:
抱歉,它是在 SE/RE 上而不是在 SE/SO 上,但我在此处涉及该主题的回答已被删除。我用它来捕捉视频……我的答案中的所有动画 GIF 都是由这段代码创建的。在我的这个答案的底部可以看到另一个例子:
如您所见,它也适用于带有经典媒体播放器的 DirectX 覆盖(即使是 Windows PrintScreen 功能也无法做到这一点)。正如我所写的那样,我对此还没有任何问题。
当心视觉内容 WinAPI 调用必须从应用程序主线程 (WNDPROC) 调用,否则可能会出现严重问题,导致应用程序中任何地方出现随机无关的 WinAPI 调用异常。