Как рисовать на форме (окне) в отдельном потоке?

Задача - делать отрисовку изображения (на форме/окне) в отдельном потоке.
Судя по тому, что пишут в интернете, необходимо отрисовку делать в отдельном буфере, который потом передавать в основной поток, который непосредственно и будет заниматься рисованием на форме.
Сделал вот такой маленький тестовый проектик:

Код (С++ Builder)

UMain.cpp
#pragma hdrstop

#include "UMain.h"

#include <thread>
#include <vcl.h>

#include "UGuiTask.h"
#include "UMessages.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TFormMain* FormMain;

HWND hWndMain;
std::shared_ptr<TGuiTask> guiTask;
TRect rect = TRect {
    TPoint { 0, 0 },
    480,
    272
};
//---------------------------------------------------------------------------
__fastcall TFormMain::TFormMain(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TFormMain::onUserMessageEvent(TMessage& message)
{
    WPARAM wParam = message.WParam;
    LPARAM lParam = message.LParam;

    switch (wParam) {
	case WM_WIN_UPDATE: {
        this->Canvas->CopyRect(rect, &guiTask->getCanvas(), rect);
    } break;

    default:;
	}
}

void __fastcall TFormMain::FormCreate(TObject* Sender)
{
	hWndMain = this->Handle;
	_bitmap = new TBitmap();
	_bitmap->Width = 480;
	_bitmap->Height = 272;

	guiTask = std::make_shared<TGuiTask>(_bitmap->Canvas);
	std::thread t(&TGuiTask::run, guiTask.get());
	t.detach();
}
//---------------------------------------------------------------------------


UMain.h
#ifndef UMainH
#define UMainH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.StdCtrls.hpp>

extern HWND hWndMain;
//---------------------------------------------------------------------------
class TFormMain : public TForm {
__published: // IDE-managed Components
    void __fastcall FormCreate(TObject* Sender);

private: // User declarations
    void __fastcall onUserMessageEvent(TMessage& message);
    TBitmap* _bitmap;

public: // User declarations
    __fastcall TFormMain(TComponent* Owner);

    BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(WM_USER, TMessage, onUserMessageEvent);
    END_MESSAGE_MAP(TForm);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormMain* FormMain;
//---------------------------------------------------------------------------
#endif


UGuiTask.cpp
//---------------------------------------------------------------------------

#pragma hdrstop

#include "UGuiTask.h"
#include "UMain.h"
#include "UMessages.h"

#include <chrono>
#include <thread>
//---------------------------------------------------------------------------
#pragma package(smart_init)

using namespace std::chrono_literals;

TGuiTask::TGuiTask(TCanvas* canvas)
{
    _canvas = canvas;
}

TGuiTask::~TGuiTask()
{
}

void TGuiTask::run()
{
    uint16_t x = 0;
    uint16_t y = 0;
    while (true) {
        std::this_thread::sleep_for(20ms);

        std::unique_lock<std::shared_mutex> ul(_sm);
        _canvas->TextOut(x, y, IntToStr(x));
        x++;
        y++;
        if (x >= 480) {
            x = 0;
        }
        if (y >= 272) {
            y = 0;
        }
        ul.unlock();
        SendMessage(hWndMain, WM_USER, WM_WIN_UPDATE, 0);
    }
}

TCanvas& TGuiTask::getCanvas()
{
    std::shared_lock<std::shared_mutex> sl(_sm);
    return *_canvas;
}


UGuiTask.h
#ifndef UGuiTaskH
#define UGuiTaskH

#include <Vcl.Graphics.hpp>
#include <shared_mutex>

//---------------------------------------------------------------------------
class TGuiTask {
private:
    //    TBitmap* _bitmap;
    TCanvas* _canvas;
    mutable std::shared_mutex _sm;

private:
protected:
public:
    TGuiTask() = delete;
    TGuiTask(TCanvas* canvas);
    ~TGuiTask();
    void run();
    TCanvas& getCanvas();
};

#endif


UMessages.h
#ifndef UMessagesH
#define UMessagesH
//---------------------------------------------------------------------------
#define WM_WIN_UPDATE (WM_USER + 0x0001)

#endif




Правильно ли я понял как это надо реализовывать? Если нет, то как это делать корректно?
Если правильно, то в чём может быть причина того, что через некоторое время обновление изображения прекращается, хотя, судя по отладчику, и поток работает, и сообщения о необходимости перерисовки canvas приходят.
  • Вопрос задан
  • 107 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы