wxWidgets绘制实时图形的最佳控件

时间:2019-04-27 06:09:33

标签: c++ wxwidgets

我使用SDL制作了一个小应用程序来显示一些精灵动画,并且效果很好。重要的是渲染必须尽可能平滑。

因为现在我需要一些GUI,所以我决定使用wxWidgets 3来创建相同的应用程序,但是现在我还可以在其中添加一些GUI元素,例如菜单,模式等。

进入wxWidget的Wiki后,我发现有多种方法可以绘制窗口。

例如:

  • wxClientDC
  • wxPaintDC
  • wxGLCanvas

我还发现了其他种类的类,例如“ 图形上下文类”,但我仍然不确定它们是否可以满足我的需求。

我的应用程序已经使用了一个内部缓冲区,只需要复制即可。

我的问题是,在wxWidgets上执行此操作的最合适方法是什么? 而且,该解决方案带来了多少性能/开销?

wxWidgets在窗口内绘制像素的不同方法之间的性能/开销有什么区别?为什么一个人使用OnPaint事件而其他人不使用。

1 个答案:

答案 0 :(得分:1)

这是执行此操作的一种方法:

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include <wx/timer.h>
#include <wx/dcclient.h>
#include <wx/rawbmp.h>

///////////// Declarations

class MyFrame : public wxFrame
{
    public:
        MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
                 wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize,
                 int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
        ~MyFrame();

    private:
        // Event Handlers
        void OnPaint( wxPaintEvent& event );
        void OnTimer(wxTimerEvent& event);

        // Helper function
        void RebuildBufferAndRefresh();

        // Private data
        wxWindow* m_renderSurface;
        int m_width;
        int m_height;
        wxBitmap m_bitmapBuffer;
        wxTimer m_timer;
        int m_curRGB;
        unsigned char* m_pixelData;
};

class MyApp : public wxApp
{
    public:
        virtual bool OnInit() wxOVERRIDE;
};

wxDECLARE_APP(MyApp);


///////////// Implementation

MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos,
                  wxSize size, int style )
        :wxFrame( parent, id, title, pos, size, style )
{
    m_width=100;
    m_height=100;
    m_pixelData = new unsigned char[3*m_width*m_height];

    m_renderSurface = new wxWindow(this, wxID_ANY, wxDefaultPosition,
                                     wxSize(m_width,m_height));
    m_renderSurface->SetBackgroundStyle(wxBG_STYLE_PAINT);
    m_renderSurface->Bind(wxEVT_PAINT,&MyFrame::OnPaint,this);

    wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
    bSizer->Add( m_renderSurface, 0 );
    this->SetSizer( bSizer );
    Layout();

    m_timer.SetOwner(this);
    m_timer.Start(17);
    this->Bind(wxEVT_TIMER,&MyFrame::OnTimer,this);
    m_curRGB=0;
}

MyFrame::~MyFrame()
{
    m_timer.Stop();
    delete[] m_pixelData;
}

void MyFrame::OnPaint( wxPaintEvent& event )
{
    wxPaintDC dc(m_renderSurface);
    if(m_bitmapBuffer.IsOk())
    {
        dc.DrawBitmap(m_bitmapBuffer,0,0);
    }
}

void MyFrame::OnTimer(wxTimerEvent& event)
{
    RebuildBufferAndRefresh();
}

void MyFrame::RebuildBufferAndRefresh()
{
    // Build the pixel buffer here, for this simple example just set all
    // pixels to the same value and then increment that value.
    for ( int y = 0; y < m_height; ++y )
    {
        for ( int x = 0; x < m_width; ++x )
        {
            m_pixelData[3*y*m_width+3*x]=m_curRGB;
            m_pixelData[3*y*m_width+3*x+1]=m_curRGB;
            m_pixelData[3*y*m_width+3*x+2]=m_curRGB;
        }
    }

    ++m_curRGB;
    if(m_curRGB>255)
    {
        m_curRGB=0;
    }

    // Now transfer the pixel data into a wxBitmap
    wxBitmap b(m_width,m_height,24);
    wxNativePixelData data(b);

    if ( !data )
    {
        // ... raw access to bitmap data unavailable, do something else ...
        return;
    }

    wxNativePixelData::Iterator p(data);

    int curPixelDataLoc = 0;
    for ( int y = 0; y < m_height; ++y )
    {
        wxNativePixelData::Iterator rowStart = p;
        for ( int x = 0; x < m_width; ++x, ++p )
        {
            p.Red() = m_pixelData[curPixelDataLoc++];
            p.Green() = m_pixelData[curPixelDataLoc++];
            p.Blue() = m_pixelData[curPixelDataLoc++];
        }
        p = rowStart;
        p.OffsetY(data, 1);
    }

    m_bitmapBuffer=b;
    m_renderSurface->Refresh();
    m_renderSurface->Update();
}


 bool MyApp::OnInit()
{
    MyFrame* frame = new MyFrame(NULL);
    frame->Show();
    return true;
}

wxIMPLEMENT_APP(MyApp);

这里的基本思想是将缓冲区复制到wxBitmap中,然后在绘制处理程序中绘制该位图。然后,通过调用“刷新”和“更新”来触发绘制处理程序。原因是系统还会出于各种原因(例如鼠标光标在渲染表面上运行)而调用paint方法。通过这种方式,可以为您和系统调用绘制一个位图以刷新表面。

在上面发布的简单示例中,我只是使用一个简单的计时器每秒大约调用60次RebuildBufferAndRefresh方法。您的应用程序可能会有更好的方法来确定何时需要刷新,而您可以使用该方法来调用RebuildBufferAndRefresh。

显然,大多数工作是在RebuildBufferAndRefresh方法中完成的。该方法分为三个部分。第一部分构建内部缓冲区。第二部分由于上述原因将缓冲区复制到wxBitmap中。第三部分只是调用刷新和更新,以强制重绘渲染表面。

也许有更好的方法来执行此操作,但是我认为这是最直接的wxWidgets-ey,它可以不断地将像素数据的缓冲区呈现到窗口。

关于这种方法需要多少开销,基本上是从缓冲区到位图的内存复制,但嵌套循环的效率不如真实内存复制。之后,绘制处理程序不执行任何操作,只绘制位图。这可能是通过对系统的后备缓冲区进行blit来完成的,并且应该相当快。希望对您有所帮助。