W
WishMaster
Пускай на форме имеется кнопка, таймер и прямо на канве отображен рисунок. При нажатии на кнопку включается таймер, который с интервалом времени скажем 20 мс начинает сдвигать рисунок с помощью функции CopyRect или какой-нибудь другой (на самом деле здесь не суть важно) в какую-либо сторону, например влево на 5 пикселов. При этом четко видно, что изображение подергивается, при чем период таких подергиваний намного больше 20 мс, да и «размах» тоже. То есть это явно не из-за «грубости» смещений изображения. Этот эффект «рывков» возникает далеко не только при смещении изображения. Он, например, так же имеет место при последовательном рисовании квадрата с помощью функции FillRect с изменением во времени его координат и т.д.
Пикантности ситуации придает еще и тот факт, что эти подергивания видны только если смотреть непосредственно на перемещающееся изображение. Если же смотреть не на него, а на неподвижный объект (например, курсор мышки), то боковое зрение не замечает этих рывков. Создается впечатление, что эффект имеет психофизический оттенок.
Насколько я понял, этот эффект возникает не из-за ошибок в программе или тормозов системы (специально я проверял на простейших примерах, в чем вы сможете убедиться сами), а из-за соотношений частоты перерисовки изображения с частотой перерисовки всего экрана монитора. В идеале, конечно же, перерисовка изображения должна совпадать с перерисовкой всего экрана монитора и тогда этих подергиваний, скорее всего, видно не будет.
Я думаю, что я не первый натолкнулся на эту проблему и кто-нибудь да поймет, о чем я тут говорю, и возможно знает, как от этого избавиться.
Вот примеры, с которыми можно при желании поработать, посмотреть, в чем проблема и попробовать ее исправить.
1. На форме лежат Button1, Button2 и Timer1 с периодом 20. При нажатии на Button1 на форме рисуется импровизированный рисунок, а при нажатии на Button2 запускается таймер и изображение начинает смещаться, при чем видны те подергивания, про которые я говорил выше. Кнопки должны быть в нижней части экрана.
Заголовочный файл
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TTimer *Timer1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
Файл реализации:
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->Brush->Style = bsSolid;
Canvas->Brush->Color = clBlue;
for (int a = 1; a <= Width; a += 20) Canvas->FillRect(Rect(a, 1, a + 10, Height - 80));
Canvas->Brush->Color = clYellow;
for (int a = 11; a <= Width; a += 20) Canvas->FillRect(Rect(a, 1, a + 10, Height - 80));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int zmischennya = 5;
Canvas->CopyMode = cmSrcCopy;
Canvas->CopyRect(Rect(1, 1, Width - zmischennya - 6, Height - 80), Canvas, Rect(zmischennya, 1, Width - 6, Height - 80));
}
//---------------------------------------------------------------------------
2. На форме лежит Panel1 с Align = alTop, под панелькой лежит Button1. На панели лежит Image1 с Align = alClient, кроме того, на форме присутствует Timer1 с периодом 25. При нажатии на Button1на Image1 начинает двигаться график, на котором тоже легко усмотреть упомянутые подергивания. В отличии от первого примера, здесь все реализовано на функциях WinApi. Этот код взят с
Заголовочный файл:
class TForm1 : public TForm
{
__published: // IDE-managed Components
TTimer *Timer1;
TPanel *Panel1;
TImage *Image1;
TButton *Button1;
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
void __fastcall Panel1Resize(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
void __fastcall AppendValue(double Val1);
};
Файл реализации:
TForm1 *Form1;
bool Done;
long in_rndm=1;
float rndm()
{
static float r;
m2: in_rndm *= 331804469l;
r=in_rndm*0.232832e-9+0.5;
if(r >=1. || r <= 0.)goto m2;
return r;
}
//--------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Image1->Stretch=false;
}
//----------------------------------------------------------
double gx,gy;
int d=5;
double OldVal1=0;
double GraphMin=0;
double GraphMax=1;
void __fastcall TForm1::AppendValue(double Val1)
{
Graphics::TBitmap *Bitmap1=Image1->Picture->Bitmap;
Bitmap1->Canvas->Lock();
HDC hdc=Bitmap1->Canvas->Handle;
::ScrollDC(hdc,d,0,NULL,NULL,NULL,NULL);
HPEN hpen=(HPEN)SelectObject(hdc,GetStockObject(WHITE_PEN));
::Rectangle(hdc,0,0,d,Bitmap1->Height);
SelectObject(hdc,hpen);
gy = Bitmap1->Height-1.0*Bitmap1->Height*(OldVal1-GraphMin)/(GraphMax-GraphMin);
gx = d;
::MoveToEx(hdc,gx,gy,NULL);
gy = Bitmap1->Height-1.0*Bitmap1->Height*(Val1-GraphMin)/(GraphMax-GraphMin);
gx = 0;
::LineTo(hdc,gx,gy);
OldVal1 = Val1;
HDC hdcTo=GetDC(Image1->Parent->Handle);
::BitBlt(hdcTo,0,0,Bitmap1->Width,Bitmap1->Height,
hdc,0,0,SRCCOPY);
ReleaseDC(Image1->Parent->Handle,hdcTo);
Bitmap1->Canvas->Unlock();}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
AppendValue(rndm());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1:anel1Resize(TObject *Sender)
{
Graphics::TBitmap *Bitmap = new Graphics::TBitmap();
Bitmap->Width= Image1->Width;
Bitmap->Height= Image1->Height;
Bitmap->Canvas->StretchDraw(Bitmap->Canvas->ClipRect,Image1->Picture->Bitmap);
Image1->Picture->Assign(Bitmap);
delete Bitmap;
Image1->Picture->Bitmap->Canvas->Pen->Color = clRed;
}
//---------------------------------------------------------------------------
Похожая проблема обсуждалась тут:
Обсуждение свелось к использованию функций DirectX для отслеживания «обратного хода луча» и синхронизации перерисовки формы с перерисовкой всего экрана. Я в DirectX к сожалению не силен. В своей програме я использовал лишь функции Buider’a и WinApi. Неужели все действительно так сложно и плавно сдвигать рисунок на фоне невозможно без использования DirectX и упомянутой синхронизации?!
Надеюсь, что кто-нибудь сможет что-то подсказать толковое. Заранее благодарен.
Пикантности ситуации придает еще и тот факт, что эти подергивания видны только если смотреть непосредственно на перемещающееся изображение. Если же смотреть не на него, а на неподвижный объект (например, курсор мышки), то боковое зрение не замечает этих рывков. Создается впечатление, что эффект имеет психофизический оттенок.
Насколько я понял, этот эффект возникает не из-за ошибок в программе или тормозов системы (специально я проверял на простейших примерах, в чем вы сможете убедиться сами), а из-за соотношений частоты перерисовки изображения с частотой перерисовки всего экрана монитора. В идеале, конечно же, перерисовка изображения должна совпадать с перерисовкой всего экрана монитора и тогда этих подергиваний, скорее всего, видно не будет.
Я думаю, что я не первый натолкнулся на эту проблему и кто-нибудь да поймет, о чем я тут говорю, и возможно знает, как от этого избавиться.
Вот примеры, с которыми можно при желании поработать, посмотреть, в чем проблема и попробовать ее исправить.
1. На форме лежат Button1, Button2 и Timer1 с периодом 20. При нажатии на Button1 на форме рисуется импровизированный рисунок, а при нажатии на Button2 запускается таймер и изображение начинает смещаться, при чем видны те подергивания, про которые я говорил выше. Кнопки должны быть в нижней части экрана.
Заголовочный файл
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TTimer *Timer1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
Файл реализации:
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->Brush->Style = bsSolid;
Canvas->Brush->Color = clBlue;
for (int a = 1; a <= Width; a += 20) Canvas->FillRect(Rect(a, 1, a + 10, Height - 80));
Canvas->Brush->Color = clYellow;
for (int a = 11; a <= Width; a += 20) Canvas->FillRect(Rect(a, 1, a + 10, Height - 80));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int zmischennya = 5;
Canvas->CopyMode = cmSrcCopy;
Canvas->CopyRect(Rect(1, 1, Width - zmischennya - 6, Height - 80), Canvas, Rect(zmischennya, 1, Width - 6, Height - 80));
}
//---------------------------------------------------------------------------
2. На форме лежит Panel1 с Align = alTop, под панелькой лежит Button1. На панели лежит Image1 с Align = alClient, кроме того, на форме присутствует Timer1 с периодом 25. При нажатии на Button1на Image1 начинает двигаться график, на котором тоже легко усмотреть упомянутые подергивания. В отличии от первого примера, здесь все реализовано на функциях WinApi. Этот код взят с
Ссылка скрыта от гостей
.Заголовочный файл:
class TForm1 : public TForm
{
__published: // IDE-managed Components
TTimer *Timer1;
TPanel *Panel1;
TImage *Image1;
TButton *Button1;
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
void __fastcall Panel1Resize(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
void __fastcall AppendValue(double Val1);
};
Файл реализации:
TForm1 *Form1;
bool Done;
long in_rndm=1;
float rndm()
{
static float r;
m2: in_rndm *= 331804469l;
r=in_rndm*0.232832e-9+0.5;
if(r >=1. || r <= 0.)goto m2;
return r;
}
//--------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Image1->Stretch=false;
}
//----------------------------------------------------------
double gx,gy;
int d=5;
double OldVal1=0;
double GraphMin=0;
double GraphMax=1;
void __fastcall TForm1::AppendValue(double Val1)
{
Graphics::TBitmap *Bitmap1=Image1->Picture->Bitmap;
Bitmap1->Canvas->Lock();
HDC hdc=Bitmap1->Canvas->Handle;
::ScrollDC(hdc,d,0,NULL,NULL,NULL,NULL);
HPEN hpen=(HPEN)SelectObject(hdc,GetStockObject(WHITE_PEN));
::Rectangle(hdc,0,0,d,Bitmap1->Height);
SelectObject(hdc,hpen);
gy = Bitmap1->Height-1.0*Bitmap1->Height*(OldVal1-GraphMin)/(GraphMax-GraphMin);
gx = d;
::MoveToEx(hdc,gx,gy,NULL);
gy = Bitmap1->Height-1.0*Bitmap1->Height*(Val1-GraphMin)/(GraphMax-GraphMin);
gx = 0;
::LineTo(hdc,gx,gy);
OldVal1 = Val1;
HDC hdcTo=GetDC(Image1->Parent->Handle);
::BitBlt(hdcTo,0,0,Bitmap1->Width,Bitmap1->Height,
hdc,0,0,SRCCOPY);
ReleaseDC(Image1->Parent->Handle,hdcTo);
Bitmap1->Canvas->Unlock();}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
AppendValue(rndm());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1:anel1Resize(TObject *Sender)
{
Graphics::TBitmap *Bitmap = new Graphics::TBitmap();
Bitmap->Width= Image1->Width;
Bitmap->Height= Image1->Height;
Bitmap->Canvas->StretchDraw(Bitmap->Canvas->ClipRect,Image1->Picture->Bitmap);
Image1->Picture->Assign(Bitmap);
delete Bitmap;
Image1->Picture->Bitmap->Canvas->Pen->Color = clRed;
}
//---------------------------------------------------------------------------
Похожая проблема обсуждалась тут:
Ссылка скрыта от гостей
Обсуждение свелось к использованию функций DirectX для отслеживания «обратного хода луча» и синхронизации перерисовки формы с перерисовкой всего экрана. Я в DirectX к сожалению не силен. В своей програме я использовал лишь функции Buider’a и WinApi. Неужели все действительно так сложно и плавно сдвигать рисунок на фоне невозможно без использования DirectX и упомянутой синхронизации?!
Надеюсь, что кто-нибудь сможет что-то подсказать толковое. Заранее благодарен.