![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как устранить флики
Danger © (2007-06-22 21:00:00)Вопрос по ходу портирования одного компонента: как ликвидировать мигания при перерисовках контрола? (контрол представляет собой нечто вроде прогресс-бара, поэтому должен обновляться часто и без фликов).
Вот куски кода, ответственные за перерисовку:
// ----------------------------------------------------------
function QProgBar_WndProc( Control: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
var
PaintStruct: TPaintStruct;
begin
Result := False;
case Msg.message of
WM_PAINT:
begin
BeginPaint( Control.Handle, PaintStruct );
PQProgressBar( Control ).Paint;
EndPaint( Control.Handle, PaintStruct );
Result:= True;
Rslt:= 0;
end;
WM_SIZE:
PQProgressBar( Control ).Resize;
end; // case
end;
Функция QProgBar_WndProc аттачена к обработке событий при создании компонента:
// ----------------------------------------------------------
function NewQProgressBar( AParent: PControl ): PQProgressBar;
.........
begin
.........
Result.AttachProc( QProgBar_WndProc );
end;
Сама процедура отрисовки, полностью:
// ----------------------------------------------------------
procedure TQProgressBar.Paint;
// Main loop. Called each time a setting changes, notably, each time
// a new position is sent.
// Surround is drawn first, then the bar itself. Caption is added lastly (if needed).
var i,k,sp: Integer;
OldBkMode : Integer;
D: PQDataObj;
begin
D:= PQDataObj( CustomObj );
// -1- Bevel
if ( D.fHasShape ) then
with Canvas^ do
begin
Pen.PenWidth := 1;
Brush.BrushStyle := bsSolid;
Brush.Color := D.fBkgClr;
Pen.Color := D.fShapeClr;
RoundRect (0, 0, Width, Height, D.fCorner, D.fCorner);
end;
// -2- The bar itself
case D.fOrientation of
boHorizontal :
begin
for i := ( D.fBorderSize - 1 ) to D.fPosition do
begin
if ( D.fByBlock ) then
begin
if ( D.fPosDescr[i].isInBlock = true) then
begin
if ( (D.fFullBlock) and (D.fPosition >= D.fPosDescr[i].blkLimit) )
or not( D.fFullBlock ) then
for k := (D.fBorderSize - 1) to (Height - (D.fBorderSize))
do Canvas.Pixels [i,k] := D.fPixDescr[i,k]
else if (D.fShowInactPos) then
for k := (D.fBorderSize - 1) to (Height -(D.fBorderSize))
do Canvas.Pixels [i,k] := D.fInactDescr[k];
end;
end else
begin
for k := (D.fBorderSize - 1) to (Height -(D.fBorderSize)) do
Canvas.Pixels [i,k] := D.fPixDescr[i,k];
end;
end;
// Now dealing with inactive positions, if they're to be drawn.
if ( D.fShowInactPos ) then
begin
if (D.fPosition < 3) then sp := 3
else sp := D.fPosition + 1;
for i := sp to D.fUSefullDrawSpace do
begin
if (D.fByBlock) then
begin
if (D.fPosDescr[i].isInBlock = True) then
begin
for k := (D.fBorderSize -1) to (Height -(D.fBorderSize)) do
Canvas.Pixels [i,k] := D.fInactDescr[k];
end;
end else //If not(byBlock), all pixels must be drawn
begin
for k := (D.fBorderSize - 1) to (Height -(D.fBorderSize)) do
Canvas.Pixels [i,k] := D.fInactDescr[k];
end;
end; {for}
end; {inactive}
end; {boHorizontal}
boVertical :
begin
for i := (D.fUSefullDrawSpace-1) downto Height - D.fPosition do
begin
if (D.fByBlock) then
begin
if (D.fPosDescr[i].isInBlock = true) then
begin
if ( (D.fFullBlock) and ((Height - D.fPosition) <= D.fPosDescr[i].blkLimit) )
or not( D.fFullBlock ) then
for k := (D.fBorderSize - 1 ) to (Width - (D.fBorderSize))
do Canvas.Pixels [k,i] := D.fPixDescr[i,k]
else if ( D.fShowInactPos ) then
for k := (D.fBorderSize - 1) to (Width -(D.fBorderSize))
do Canvas.Pixels [k,i] := D.fInactDescr[k];
end;
end
else
for k := (D.fBorderSize - 1) to (Width -(D.fBorderSize))
do Canvas.Pixels [k,i] := D.fPixDescr[i,k];
end;
// inactive positions :
if (D.fShowInactPos) then
begin
if ( D.fPosition < 3 ) then sp := D.fUSefullDrawSpace
else sp := Height - D.fPosition - 1;
for i := sp downto D.fBorderSize do
begin
if ( D.fByBlock ) then
begin
if ( D.fPosDescr[i].isInBlock = true ) then
begin
for k := (D.fBorderSize - 1) to (Width -(D.fBorderSize)) do
Canvas.Pixels [k,i] := D.fInactDescr[k];
end;
end else
for k := (D.fBorderSize - 1) to (Width -(D.fBorderSize))
do Canvas.Pixels [k,i] := D.fInactDescr[k];
end; {for... downto}
end; {inactive}
end; {boVertical}
end; // Case
// caption management. The font is the canvas' one. Can be overrided
// using the Font property :
if ( D.fCaptionOvr ) then
if ( D.fShowPosAsPct ) then _SetCaption( Double2Str( D.fUSerPosPct ) + '%')
else SetCaption( Int2Str(D.fUSerPos) );
if ( D.fHasCaption ) then
begin
OldBkMode := SetBkMode(Canvas.Handle, Windows.TRANSPARENT);
with Canvas^ do
begin
TextOut(D.fCapPos.X, D.fCapPos.Y, Caption);
end;
SetBkMode(Canvas.Handle, OldBkMode);
end;
end;
В чем может быть проблема, что при перерисовке контрол сильно мигает? Подскажитте, как можно исправить?
homm (2007-08-29 23:20:00) [21]> Зря ты homm, я ведь по делу.
Да нет, не зря.
> Лучше бы хотя бы предположил – как Office так быстро отрисовывает
> диалоговое окно с большим количеством на нем контролов.
Временный битмап? Кто мешает его сделать в своем приложении?
Andrey_rus (2007-08-29 22:40:00) [22]Зря ты homm, я ведь по делу.
Причем здесь написание файлового менеджера?
Лучше бы хотя бы предположил – как Office так быстро отрисовывает диалоговое окно с большим количеством на нем контролов.
homm (2007-08-29 22:02:00) [23]> которые рисуются между прочим без BitBlt, а прямо в DC
Сегодня день перлов что-ли? :)
homm (2007-08-29 22:01:00) [24]> [11] Andrey_rus © (29.08.07 18:00)
> Вот бы подумать сообща и реализовать собственный DoubleBuffer'инг.
> Это явно выделяло бы программы написанные на KOL.
http://www.delphimaster.ru/cgi-bin/nuts.pl?showpage=2
Первый пост про тебя :)
Jimmy Lee (2007-08-29 18:41:00) [25]Жаль нельзя аттачить в форуме, но если кто желает - могу прислать два примера:
1 KOL (форма->панель(align=caClient)->прогрессбар(алигн=caBottom))
2 Форма (не KOL), на ней панель(align=caClient) и на ней еще разные градиентные контролы, которые рисуются между прочим без BitBlt, а прямо в DC
во втором примере фликов намного меньше. для стресс-тестирования тягать окна надо за правый верхний угол.
Andrey_rus (2007-08-29 18:00:00) [26]То, что флики KOL аналогичны фликам VCL, не повод успокаиваться. Я специально использую слабый компьютер (Celeron Pentium II - 300 Mz) для того чтобы воочию наблюдать медленную отрисовку контролов и соответсвенно предпринемать какие то действия. Однако на моем компбтере даже перегруженные контролами диалоговые окна MS Office отрисовываются моментально. Использование стандарного DoubleBuffer приносит больше вреда чем пользы (глюки с темами, не работает с RichEdit). Вот бы подумать сообща и реализовать собственный DoubleBuffer'инг. Это явно выделяло бы программы написанные на KOL и привлекло бы дополнительное количество разработчиков с VCL лагеря.
Vladimir Kladov (2007-08-29 15:30:00) [27]Если не бросать панельку, а сразу на форму, то флики меньше. Сначала выравнивается то, что есть на формеЮ когда меняются ее размеры. Потом идёт каскадная обработка сообщения изменения размеров для дочерних панелей. И так далее, а поскольку изменение размеров - явление непрерывное и продолжается сколько-то во времени выравнивание выполняется многократно. В процессе окна перерисовываются, происходит обработка OnResize, если есть, и не факт, что всё это не тормозит процесс ещё больше.
Вывод: хотите убрать флики в конкретном приложении? Уберите все Align, при изменении размеров 1. вычислите все прямоугольники всех контролов сами 2. Присвойте новые координаты через BoundsRect :=. При этом, если ещё в процессе срабатывают какие-то ваши обработчики, сделайте так чтобы обработка шла потом, т.е. никакая отрисовка и никакие действия не выполнялись внутри вашей процедуры выравнивания.
Еще можно обрабатывать WM_WINDOWPOSCHANGING (так, кажется), и "правильно" задавать при этом, какие пиксели сдвигать, т.е. корректно определять только небольшую изменившуюся прямоугольную область, а остальное не перерисовывать (не всегда она оказывается внизу справа, но даже это по умолчанию Windows не используется, и инвалидируется весь контрол).
danger (2007-08-29 07:43:00) [28]
> Jimmy Lee (27.08.07 19:59) [6]
> Тестировал компонент. - все равно флики сильно заметные,
> если caBottom и таскать верхнюю границу.
В большинстве случаев флики в компонентах KOL такие же, как и в VCL аналогах. Не больше.
Касательно align для компонента - нужно обращаться к гуру KOL. Все это должно средствами библиотеки реализовываться. Не должно компоненту быть умнее библиотеки, отслеживание разных версий ALIGN'a и всевозможные фиксы для разных версий align'a не является задачей.
Vladimir Kladov (2007-08-28 21:15:00) [29]2JimmyLee: разве в VCL меньше фликов или в любой другой программе в Windows, в том же проводнике? (Вы еще в Linux'е посмотрите, какие там флики в X-окнах, наши флики вам совсем незаметными покажутся). В принципе, с фликами бороться можно. Но - в каждом конкретном случае - отдельно. Кому-нибудь этот геморрой из разработчиков нужен?
exero (2007-08-28 08:47:00) [30]В продолжение темы пример:
1. Кидаем на форму панель с Align = caClient
2. Кидаем на нее три панельки у первой Align = caTop у второй caBottom у третей caClient.
3. У нижней ставим все Anchor в True.
Запускаем - сворачиваем-разворачиваем - получаем совсем не ту картинку которую хотели (.
PS. Если OLD_ALIGN то все нормально.
Jimmy Lee (2007-08-27 19:59:00) [31]Тестировал компонент. - все равно флики сильно заметные, если caBottom и таскать верхнюю границу. На мой взгляд сам метод align в KOL реализован не совсем оптимально. При ресайзе за верхнюю границу - нижняя прыгает как сумасшедшая. Что скажете? Тема далека от закрытия!
Danger (2007-06-23 15:08:00) [32]
> Vladimir Kladov (23.06.07 11:18) [4]
> Залить сначала тем цветом, которым надо чтобы были уголки.
Спасибо, Владимир, сработало.
Vladimir Kladov (2007-06-23 11:18:00) [33]Залить сначала тем цветом, которым надо чтобы были уголки.
Danger (2007-06-23 09:42:00) [34]
> Vladimir Kladov (22.06.07 21:56) [1]
> Как вариант этой возможности - DoubleBuffered := true -
> всего лишь создает такой битмап в памяти сам, и отрабатывает
> WM_PAINT и WM_ERASEBKGND на канву этого битмапа, после чего сам
> перебрасывает содержимое на экран.
DoubleBuffered решает проблему мигания, но появляется небольшая проблемка: рамка контрола изначально не прямоугольная (рисуется через RoundRect в процедуре Paint):
// -1- Bevel
if ( D.fHasShape ) then
with Canvas^ do
begin
Pen.PenWidth := 1;
Brush.BrushStyle := bsSolid;
Brush.Color := D.fBkgClr;
Pen.Color := D.fShapeClr;
RoundRect (0, 0, Width, Height, D.fCorner, D.fCorner);
end;
И при использовании DoubleBuffered у контрола появляются черные уголки, "спрямляющие" его в прямоугольник. В отрисовке без DoubleBuffered рамка отрисовывается нормально. Как заставить DoubleBuffered отрисовывать правильно?
>Следует только учесть, что все
> дочерние контролы такого контрола с двойной буферизацией так же
> рисуются на этом же битмапе в памяти.
Контрол не будет иметь потомков, поэтому несущественно.
homm (2007-06-22 22:30:00) [35]> Следует только учесть, что все дочерние контролы такого
> контрола с двойной буферизацией так же рисуются на этом
> же битмапе в памяти.
Неправда ваша. Только те у которых самих DoubleBuffered = true
Vladimir Kladov (2007-06-22 21:56:00) [36]Мелькание происходит из-за того, что в процессе обновления сначала затирается прежнее изображение (цветом фона), а затем поверх него рисуется заново новое состояние. Если есть возможность обесчпечить рисование нового состояния без затирания предыдущего, то надо так и поступить. И не забыть в этом случае отработать WM_ERASEBKGND, ответив системе, что все уже сделано, и ничего делать не надо, иначе сама система по умолчанию займется созданием флика. Если такой возможности нет (изображение слишком сложное), то единственный выход - рисовать на битмапе в памяти, и перебрасывать все содержимое битмапа на DC в OnPaint. Как вариант этой возможности - DoubleBuffered := true - всего лишь создает такой битмап в памяти сам, и отрабатывает WM_PAINT и WM_ERASEBKGND на канву этого битмапа, после чего сам перебрасывает содержимое на экран. Следует только учесть, что все дочерние контролы такого контрола с двойной буферизацией так же рисуются на этом же битмапе в памяти.