使用 SFML 在屏幕顶部绘制 (Windows)

Draw on top of the screen (Windows) with SFML

在 Windows 上,您可以使用 GDI 在所有内容之上绘制,采用 null 的绘制上下文:

HDC hdc = GetDC(NULL);

我希望对 SFML 做同样的事情,但如果我尝试一些等效的东西(创建渲染 window 以 NULL 作为参数,在将其转换为 hwnd 之后)任何地方都不会绘制。 我正在尝试的东西甚至可以用 sfml 实现吗?

好吧,如果你想要 OpenGL,你需要一个 window 句点。但是 window 不需要在屏幕上可见。您可以结合使用 GDIOpenGL 来实现您的目标。

  1. 通过 OpenGL 将屏幕外的内容渲染为位图

    使用与桌面分辨率相同的隐形 window。如果 window 是不可见的,它不会对鼠标或键盘事件作出反应 ...

  2. 将GL图像复制到CPU端内存

    简单 glReadPixels 就可以了。

  3. 将图像复制到桌面(使用 GDI 位图)

    简单地convert/copy将原始图像数据转换为GDI兼容位图,然后简单地将其绘制到桌面上canvas。所以不再有标准 GL 应用程序中的 SwapBuffers(hdc);

我在 C++/VCL 环境中编码,所以我没有纯 WinAPI/GDI 知识(VCL 为我但是代码应该非常相似传递的名称和操作数可能会有所不同但不会太多)。

这是我带来的:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <gl\gl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1  *Form1;                 // VCL Application window object
TCanvas *scr=NULL;              // Desktop
DWORD   *txr=NULL;              // memory for GPU->CPU image transfer
Graphics::TBitmap *bmp=NULL;    // bitmap for CPU->Desktop image transfer
int     xs,ys;                  // desktop resolution
HDC     hdc=NULL;               // device context for GL
HGLRC   hrc=NULL;               // rendering context for GL
//---------------------------------------------------------------------------
void gl_draw()
    {
    if (scr==NULL) return;
    if (bmp==NULL) return;
    if (txr==NULL) return;

    glClearColor(0.0,0.0,0.0,0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    // desktop pixel units
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glTranslatef(-1.0,+1.0,0.0);
    glScalef(2.0/float(xs),-2.0/float(ys),1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // render rectangle
    GLfloat fx=xs/2,fy=ys/2,fz=0.0,fa=(xs/2)-10,fb=(ys/2)-10;
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_LINE_LOOP);
    glVertex3f(fx-fa,fy-fb,fz);
    glVertex3f(fx-fa,fy+fb,fz);
    glVertex3f(fx+fa,fy+fb,fz);
    glVertex3f(fx+fa,fy-fb,fz);
    glEnd();

    if (Form1->Visible)     // normal window GL render
        {
        glFlush();
        SwapBuffers(hdc);
        }
    else{                   // copy GL image directly to desktop
        // copy GL image to CPU side memory
        glFlush();
        glReadPixels(0,0,xs,ys,GL_RGBA,GL_UNSIGNED_BYTE,txr);
        // copy it to bitmap
        int x,y,a; DWORD *p;
        for (a=0,y=0;y<ys;y++)
         for (p=(DWORD*)bmp->ScanLine[y],x=0;x<xs;x++,a++)
          p[x]=txr[a];
        // render it to desktop
        scr->Draw(0,0,bmp);
        }

    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    // desktop
    scr=new TCanvas();
    scr->Handle=GetDC(NULL);
    xs=scr->ClipRect.Width();
    ys=scr->ClipRect.Height()-31;           // leave taskbar out of it
    // BMP
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    bmp->SetSize(xs,ys);
    // txr buffer
    txr=new DWORD[xs*ys];
    // window
    BorderStyle=bsNone;
    SetBounds(0,0,xs,ys);
    // GL init
    hdc = GetDC(Handle);                    // get device context for this App window
    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory( &pfd, sizeof( pfd ) );      // set the pixel format for the DC
    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;
    SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
    hrc = wglCreateContext(hdc);            // create current rendering context
    if(hrc == NULL)
        {
        ShowMessage("Could not initialize OpenGL Rendering context !!!");
        Application->Terminate();
        }
    if(wglMakeCurrent(hdc, hrc) == false)
        {
        ShowMessage("Could not make current OpenGL Rendering context !!!");
        wglDeleteContext(hrc);          // destroy rendering context
        Application->Terminate();
        }
    glViewport(0,0,xs,ys);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    // GL exit
    wglMakeCurrent(NULL, NULL);     // release current rendering context
    wglDeleteContext(hrc);          // destroy rendering context
    // release buffers
    if (scr){ delete scr; scr=NULL; }
    if (bmp){ delete bmp; bmp=NULL; }
    if (txr){ delete[] txr; txr=NULL; }
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
    {
    if (Visible) Visible=false; // hide
    gl_draw();
    }
//---------------------------------------------------------------------------

它是带有单个计时器的单个 Form VCL 应用程序。它创建 GL 上下文并在第一次机会时变得不可见。然后它会定期用黑色背景和白色矩形边框覆盖桌面 ...

因此您需要将 VCL 内容(Form1 和事件)移植到您的环境中。此外,您可能希望为该行添加透明度:

scr->Draw(0,0,bmp);

并将其用作GL渲染的背景纹理。

PS。纯 GDI 渲染比这简单得多,而且可能更快,但是您需要 GL,所以 ...