Форум — Ответы     (  К темам )
 ?  Dr.Phoenix: Как рисовать на форме из вторичного потока? (27-12-2002 16:02:40)
Моей проге надо часто и много рисовать на форме, если делать это последовательно с другими действиями (напиример она показавает координаты положения курсора),
то все остальное начинает заметно тормозить. Если функцию рисования запускать отдельным потоком, то прога вылетает почти сразу и говорит, что форма не разрешает рисование. Почему такое происходит и как это исправить?
 Devnvd (27-12-2002 20:22:12) http://devnvd.narod.ru
Приведу полностью пример рисования на форме в потоке
Тонкое место здесь это использование Sleep. Вместо него лучше сделать
вертушку PeekMessage<->DispatchMessage или организовывать Event'ы.

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
/*****************************************************************
Прорисовка в режиме SCROLL картинки ImageBmp в области PaintBox1'а
PaintBox вообще-то здесь не к месту, в потоке лучше рисовать
на TWinControl. Здесь PaintBox используем только в качестве примера
Используются компоненты:
TForm *Form1; // Сама форма на которой всё происходит
TButton *Button1; //Вызов диалога зарузки картинки
TPaintBox *PaintBox1; // Задаёт область рисования на форме
TOpenDialog *OpenDialog1; // Для ввода имени файла BMP-картинки
TEdit *Edit1; // Для изменения величины таймаута
******************************************************************/
Graphics::TBitmap *ImageBmp=NULL; //Картинка для рисования в потоке
DWORD ThreadID=NULL; //Идентификатор потока рисования
int dY=1; //Шаг смещения рисунка, может быть и — и +.
int TimeScroll=10; //Таймаут для потока в миллисекундах
bool idScroll=false; //Флажок для завершения потока
//---------- Поток рисования ------------
DWORD CALLBACK ThreadDraw(void *P)
{
if(!ImageBmp){ThreadID=NULL; return 0;}
//Временный битмап
Graphics::TBitmap *tmpBmp=new Graphics::TBitmap();
tmpBmp->Width=Form1->PaintBox1->Width;
tmpBmp->Height=Form1->PaintBox1->Height;

//Создадим HDC для собственного пользования
HDC hdcTo=CreateCompatibleDC(tmpBmp->Canvas->Handle);
//Привяжем битмап к HDC
SelectObject(hdcTo,tmpBmp->Handle);

//Создадим HDC для собственного пользования
HDC hdcFrom=CreateCompatibleDC(ImageBmp->Canvas->Handle);
//Привяжем битмап к HDC
SelectObject(hdcFrom,ImageBmp->Handle);
//Используемые переменные, не все они вообще-то необходимы
int X0,W0,Y0,H0,X1,W1,Y1,H1;
int hTo=tmpBmp->Height;
int hFrom=ImageBmp->Height;
X0=0; W0=tmpBmp->Width;
Y0=0; H0=hTo;
X1=0; W1=ImageBmp->Width;
Y1=0;
//Приступаем к беганию по кругу
while(idScroll)
{
//Поспим, чтобы дать другим поработать
Sleep(TimeScroll);
//В области вывода может оказаться несколько кадров
//рисуемой картинки
//Рисуем верхний кусок
if(Y1 < 0)Y1 =Y1%hFrom+hFrom;
if(Y1 > hFrom)Y1 =Y1%hFrom;
H0=hFrom-Y1;
if(H0 >hTo)H0=hTo;
if(H0)
StretchBlt(hdcTo,X0,Y0,W0,H0,hdcFrom,X1,Y1,W1,H0,SRCCOPY);
//До рисовываем цепочку до конца
while(hTo >H0)
{
H1=hTo-H0;
if(H1 >hFrom)H1=hFrom;
if(H1)
StretchBlt(hdcTo,X0,Y0+H0,W0,H1,hdcFrom,X1,0,W1,H1,SRCCOPY);
H0+=H1;
}
//У PaintBox'
 Devnvd (27-12-2002 20:29:27) http://devnvd.narod.ru
Извините,но весь текст не влез.
Если кому необходим исходник этого примера пишите письмо по адресу
devnvd@imail.ru
 Devnvd (28-12-2002 09:03:59)
Теперь наверно точно полностью.
Приведу полностью пример рисования на форме в потоке
Тонкое место здесь это использование Sleep. Вместо него лучше сделать
вертушку PeekMessage<->DispatchMessage или организовывать Event'ы.
Чтобы быстро рисовать надо рисовать в Bitmap и после формирования изображения
копировать его на экран.

#include
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
/*****************************************************************
Прорисовка в режиме SCROLL картинки ImageBmp в области PaintBox1'а
PaintBox вообще-то здесь не к месту, в потоке лучше рисовать
на TWinControl. Здесь PaintBox используем только в качестве примера
Используются компоненты:
TForm *Form1; // Сама форма на которой всё происходит
TButton *Button1; //Вызов диалога зарузки картинки
TPaintBox *PaintBox1; // Задаёт область рисования на форме
TOpenDialog *OpenDialog1; // Для ввода имени файла BMP-картинки
TEdit *Edit1; // Для изменения величины таймаута
TLabel *Label1; //Для отображения текущих координат мыши в PaintBox'е
******************************************************************/
Graphics::TBitmap *ImageBmp=NULL; //Картинка для рисования в потоке
DWORD ThreadID=NULL; //Идентификатор потока рисования
int dY=1; //Шаг смещения рисунка, может быть и — и +.
int TimeScroll=10; //Таймаут для потока в миллисекундах
bool idScroll=false; //Флажок для завершения потока
//---------- Поток рисования ------------
DWORD CALLBACK ThreadDraw(void *P)
{
if(!ImageBmp){ThreadID=NULL; ExitThread(0); return 0;}
//Временный битмап
Graphics::TBitmap *tmpBmp=new Graphics::TBitmap();
tmpBmp->Width=Form1->PaintBox1->Width;
tmpBmp->Height=Form1->PaintBox1->Height;

//Создадим HDC для собственного пользования
HDC hdcTo=CreateCompatibleDC(tmpBmp->Canvas->Handle);
//Привяжем битмап к HDC
SelectObject(hdcTo,tmpBmp->Handle);

//Создадим HDC для собственного пользования
HDC hdcFrom=CreateCompatibleDC(ImageBmp->Canvas->Handle);
//Привяжем битмап к HDC
SelectObject(hdcFrom,ImageBmp->Handle);
//Используемые переменные, не все они вообще-то необходимы
int X0,W0,Y0,H0,X1,W1,Y1,H1;
int hTo=tmpBmp->Height;
int hFrom=ImageBmp->Height;
X0=0; W0=tmpBmp->Width;
Y0=0; H0=hTo;
X1=0; W1=ImageBmp->Width;
Y1=0;
//Приступаем к беганию по кругу
while(idScroll)
{
//Поспим, чтобы дать другим поработать
Sleep(TimeScroll);
//В области вывода может оказаться несколько кадров
//рисуемой картинки
//Рисуем верхний кусок
if(Y1 < 0)Y1 =Y1%hFrom+hFrom;
if(Y1> hFrom)Y1 =Y1%hFrom;
H0=hFrom-Y1;
if(H0>hTo)H0=hTo;
if(H0)
StretchBlt(hdcTo,X0,Y0,W0,H0,hdcFrom,X1,Y1,W1,H0,SRCCOPY);
//До рисовываем цепочку до конца
while(hTo>H0)
{
H1=hTo-H0;
if(H1>hFrom)H1=hFrom;
if(H1)
StretchBlt(hdcTo,X0,Y0+H0,W0,H1,hdcFrom,X1,0,W1,H1,SRCCOPY);
H0+=H1;
}
//У PaintBox'а нет Handle и постоянного HDC
//PaintBox это всего лишь обозначение места, там где его положили
//Поэтому берём HDC окна на котором он лежит
//Так как окно Form1 может подвергаться процедуре ReCreate,
// то будем получать его контекст динамически
HDC hdc=GetDC(Form1->Handle);
//Получим место на окне где лежит PaintBox, вдруг его сместили :)
int XP=Form1->PaintBox1->Left;
int YP=Form1->PaintBox1->Top;
BitBlt(hdc,XP,YP,W0,hTo,hdcTo,0,0,SRCCOPY);
ReleaseDC(Form1->Handle,hdc);
Y1+=dY; //Сдвинемся
}
//Убираем то что мы для себя временно сделали
DeleteDC(hdcTo);
DeleteDC(hdcFrom);
delete tmpBmp;
//Завершаем поток
ThreadID=NULL;
return 0;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
//Чтобы не моргало, мы сами будем перерисовывать фон
Form1->ControlStyle< PaintBox1->ControlStyle< }
//---------------------------------------------------------------------------
//Считываем файл картинки заносим её в ImageBmp
//Запускаем поток для отображения картинки
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(!OpenDialog1->Execute())return;
//Завершим поток на всякий случай
idScroll=false;
while(ThreadID){Application->ProcessMessages();}
if(!ImageBmp)ImageBmp=new Graphics::TBitmap();
try{
ImageBmp->LoadFromFile(OpenDialog1->FileName);
}catch(...){ShowMessage("Error load Image");return;}
idScroll=true;
HANDLE hThread=::CreateThread(0,0,ThreadDraw,0,0,&ThreadID);
::SetThreadPriority(hThread,THREAD_PRIORITY_NORMAL);
::CloseHandle(hThread); //Больше не нужен
}
//---------------------------------------------------------------------------
//Если нажмём левую кнопку мыши в области PainBox'а
// то сможем смещать всю форму
// Если дополнительно нажмём клавишу Cntr,
// то сместим только PaintBox
void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
static int X0,Y0,id_set=0;
if(!Shift.Contains(ssLeft))
{
id_set=0;
Label1->Caption="X="+IntToStr(X)+"rnY="+IntToStr(Y);
return;
}
if(!id_set && Shift.Contains(ssLeft))
{ X0=X; Y0=Y; id_set=1; return; }//Запомним место где прижали мышь
TControl *mb=(TControl *)Sender;
if(!Shift.Contains(ssCtrl)&& mb->Parent)
{
//сместим родителя
mb->Parent->Left+=(X-X0);
mb->Parent->Top+=(Y-Y0);
}
else
{
//сместим компонент
mb->Left+=(X-X0);
mb->Top+=(Y-Y0);
}
}
//---------------------------------------------------------------------------
//Рисуем фон окна Form1
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color=Color;
Canvas->FillRect(ClientRect);
}
//---------------------------------------------------------------------------
//Подтираем за собой
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
idScroll=false;
while(ThreadID){Application->ProcessMessages();}
if(ImageBmp)delete ImageBmp;
}
//---------------------------------------------------------------------------
//Динамически меняем величину таймаута для потока
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
int i=atoi(Edit1->Text.c_str());
if(i <1000)TimeScroll=i;
}