Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Панорама

Автор: Alx 16.08.2004 17:33

Люди, подскажите пожалуйста. Мне необходимо сделать панораму. Длинная кортинка, которая едет по форме. Но когда я двигаю компонент Image по форме image.left:=image.left+1 картинка как будто мерцает и на какоето время пропадает. Мне сказали что так сделать не получится. А как же тогда мне сделать панораму? Заранее спасибо! Alx

Автор: BlackShadow 16.08.2004 19:05

Проще всего было бы через DX... С простым TImage это не думаю, что пройдёт...

Автор: Guest 16.08.2004 19:20

:p2: А тогда, соответственно, вопрос. В двух словах - что такое DX? Это какойто компонент?

Автор: BlackShadow 16.08.2004 20:03

В общем случае - нет smile.gif DX - Это уж совсем кратко от DirectX smile.gif Хотя и компоненты такие есть...

Автор: P@sh@ 16.08.2004 20:18

DX - это видимо DirectX, компонент, ага... только не дельфы, а винды...

но и без него можно сделать кое-что:

var Bmp: TBitmap;
...
Form1.Canvas.CopyRect(Form1.ClientRect,
 Bmp.Canvas, Rect(offset, 0, Form1.ClientWidth+offset, Form1.ClientHeight));
...
// ClientRect=(0,0,ClientWidth,ClientHeight) - внутреннее пространство формы

блок из Bitmapа размером с форму Form1, только с горизонтальным смещением offset копируется прямо на форму Form1

и не забудь добавить строчку DoubleBuffered, чтоб не моргало при перерисовке...


вот, только что сделал для проверки:
unit Unit1;
interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, ExtCtrls;

type
 TForm1 = class(TForm)
   Timer1: TTimer;
   procedure FormCreate(Sender: TObject);
   procedure FormPaint(Sender: TObject);
   procedure FormClose(Sender: TObject; var Action: TCloseAction);
   procedure Timer1Timer(Sender: TObject);
 end;

var
 Form1: TForm1;
 Bitmap: TBitmap;
 x: integer;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 doublebuffered:=true;
 Bitmap := TBitmap.Create;
 Bitmap.LoadFromFile('EARTH2.bmp');
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
 Canvas.CopyRect(clientrect,Bitmap.Canvas,rect(x,0,x+clientwidth,clientheight));
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 Bitmap.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 inc(x);
 if x+clientwidth>=1024 then x:=0;
 repaint;
end;
end.

на форме только Timer1

Автор: Alx 16.08.2004 20:38

Вау какой код. ОГРОМНЫЙ РЕСПЕКТ и человеческое СПАСИБО. Вечером попробую включить.

Автор: BlackShadow 17.08.2004 15:23

Да, вот про DoubleBuffered я и не подумал sad.gif А под DX есть и дельфийские компоненты, с которыми всё довольно легко получается. Там инкапсулирована вся работа с интерфейсами, так что пишешь как под GDI через Canvas smile.gif

Автор: Guest 17.08.2004 16:40

Так этот код не будет работать? sad.gif Я даже не знаю что такое DoubleBuffered... Ладно, лезу в интернет искать ДиректХ.

Автор: BlackShadow 17.08.2004 18:08

DoubleBuffered, это когда форма поддерживает что-то типа того, что в DOS'е называелось видеостраницами. Т. е. когда есть возможность рисования "в какой-то кусок памяти", который в последствии подставляется на место видеопамяти. Т. к. такой вариант заметно превосходит по скорости все другие, то мерцание исчезает. В DX есть такие BackBuffers, которые именно это и представляют.

Автор: Alx 17.08.2004 19:28

Дааа. Нашел я какие-то тексты по изучению DirectX (DelphiX).... Это не для меня. Я пока не настолько крут в программировании. BlackShadow, а почему не будет работать тот код который ты тут написал??? Неужели на Делфи так трудно сдвинуть картинку с места (без мирцания)?

Автор: BlackShadow 17.08.2004 20:48

Я тут код написал???

Тот код, что присла P@sh@ вполне может и отработать. Очень даже может. Я же этого и не отрицал... Попробуй. DX просто круче smile.gif Посмотрю дома модули. У меня вроде было что-то по-круче чем DelphiX, который только интерфейсы и описывает...

Автор: P@sh@ 18.08.2004 10:39

DoubleBuffered просто включает режим, когда Canvas формы становится как бы невидимым, дублируется в памяти, и все рисование делается на нём, а на экран выводится уже готовая картинка, мерцания нет, потому что на экране ничего не ПЕРЕрисовывается...
PS: этот режим памяти больше требует...

DirectX штука несложная, если не лезть в Direct3D, а ограничиться только прямым доступом к видеопамяти... само собой, это будет намного быстрее GDI

Автор: Alx 18.08.2004 15:48

Люди, а вот посмотрите. В том коде, который написал P@sh@ я объединил 2 процедуры.

Код
procedure TForm1.FormCreate(Sender: TObject);
begin
doublebuffered:=true;
Bitmap := TBitmap.Create;
Bitmap.LoadFromFile('EARTH2.bmp');
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
x:=x-1;
repaint;
Canvas.CopyRect(clientrect,Bitmap.Canvas,rect(x,0,x+clientwidth,clientheight));
end;

Так можно делать? Но у меня все равно мирцание!!! И присутствие строчки doublebuffered:=true ничего не меняет. А вот если убрать repaint, то тогда мирцание изчезает. Но тогда за рисунком остается размазаный след от него. sad.gif Ну мирцание же не может быть из за того, что у меня Делфи 5???

Автор: BlackShadow 18.08.2004 16:31

Это не из-зп версии Delphi. Я бы посоветывал тебе добавить ещё и InvalidateRect после изменения положения картинки.

Автор: Alx 18.08.2004 16:44

А вот если я в коде убираю repaint, то тогда мирцание изчезает. Но тогда за рисунком остается размазаный след от него. Но этот след тогда у меня будет оставаться под картинкой и за пределами формы. Этот след не занимает много памяти. Это не картинка на картинке на картинке?

А вот InvalidateRect; - это селая строчка? Там никакие парамерры не нужны?

Автор: Бродяжник 18.08.2004 17:18

procedure TForm1.Timer1Timer(Sender: TObject);
begin
x:=x-1;
repaint;
Canvas.CopyRect(clientrect,Bitmap.Canvas,rect(x,0,x+clientwidth,clientheight));
end;

Так, конечно, будет мерцать. Потому что Вы сначала делаете repaint, то есть побуждаете форму перерисоваться, и сразу же после этого вручную на нее накладываете битмап. А вообще нужно так, как было у Паши, потому что если Вы сделаете отрисовку в таймере, а не в OnPaint, тогда при сворачивании, перекрывании и другими манипуляциями с Вашим окном, система будет слать ему WM_PAINT, и перерисовка формы будет перебивать отрисовку в таймере. Наверное, по этому поводу можно где-то прочесть лучше, чем я пишу, но все же попробую объяснить.
Когда мы рисуем через Canvas.CopyRect и другие методы канвы, это, так сказать, наше личное дело. Мы чегой-то нарисовали, и окно не обязано об этом помнить. Если после нашего рисования окно было временно перекрыто другим, то после этого на месте нашего рисования останется область, залитая фоновым цветом окна. Восстанавливая свой вид, окно нарисовало лишь то, о чем ему было известно: свой фон, рамку, заголовок и подчиненные контролы. А вся наша графика улетучилась. Чтобы этого избежать, всю подобную графику принято помещать в обработчик события OnPaint, каковое событие генерируется при перерисовке окна. В этом случае при каждой перерисовке окна программа будет рисовать и наши художества. А для того, чтобы заставить окно принудительно перерисоваться, и используются repaint и invalidaterect. Неплохо бы Вам попробовать немного без VCL попрограммировать, с чистым API, так сказать... много узнаете интересного. smile.gif

Автор: Alx 18.08.2004 17:27

Но так как написал P@sh@ у меня не как не выходит. Я не понимаю что вызывает процедуру

Код
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.CopyRect(clientrect,Bitmap.Canvas,rect(x,0,x+clientwidth,clientheight));
end;

У меня почему то после запуска программы форма остается совершенно пустая.

Автор: BlackShadow 18.08.2004 17:46

Цитата
потому что если Вы сделаете отрисовку в таймере, а не в OnPaint, тогда при сворачивании, перекрывании и другими манипуляциями с Вашим окном, система будет слать ему WM_PAINT, и перерисовка формы будет перебивать отрисовку в таймере.

А вот это не надо smile.gif Свёрнутое окно не рисует на своей канве. Всё, что оно может делать это рисовать в taskbar'е себя как-то иначе. Например - WinAmp, прокручивающий там название играющей песни.
Цитата
всю подобную графику принято помещать в обработчик события OnPaint

Вот тут полностью согласен.
Цитата
Неплохо бы Вам попробовать немного без VCL попрограммировать, с чистым API, так сказать... много узнаете интересного.

Золотые слова smile.gif

InvalidateRect трбует Handle окна (Form1.Handle например) и естественно сам Rect, который перерисовывать. Идея такая: сместил свою картинку, получил "след", вычислил габариты этого следа и произвёл Invalidate на этом кусочке. По сравнению с перерисовкой всей формы выигрыш обеспечен smile.gif

Автор: Guest 18.08.2004 17:55

Ну а почему у меня процедура не вызывается?:

Код
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.CopyRect(clientrect,Bitmap.Canvas,rect(x,0,x+clientwidth,clientheight));
end;


Только если можно - человечискими словами..... а то какие-то Invalidate, WM_PAINT и API

Автор: Бродяжник 18.08.2004 18:01

BlackShadow
"Свернутое окно не рисует на своей канве". И то верно! :D Надо точнее излагать свои мысли. Я хотел сказать, что если его потом развернуть обратно...
to AIx
Ладно уж, берите, только не говорите, что оно опять не работает... Меньшего мерцания Вы едва ли добьетесь средствами GDI.


Прикрепленные файлы
Прикрепленный файл  Panorama.zip ( 199.07 килобайт ) Кол-во скачиваний: 264

Автор: BlackShadow 18.08.2004 18:26

Бродяжник, надеюсь к критике ты относишься нормально :p2:
Вот это сильно по глазам ударило:

 Timer1.Enabled:=false;

Впринципе как и это
 x:=0;

Можно было бы оформит как инициализированную переменную.
Далее. Можно было бы вырезать кнопочку "maximize", а то смотриться смешно smile.gif

Итого. Вариант неплохой. При маденьких картинках всё "на ура". Поставил большую - мерцало только в путь. Я бы порекомендовал заменить обработчик таймера таким образом, чтобы он вызывал InvalidateRect в нужной области а не Repaint (кнопкам-то зачем лишний раз перерисовывться smile.gif ).

Автор: Alx 18.08.2004 18:42

Спасибо! Вечером, как только доберусь до Делфи, буду пробовать. lol.gif

Автор: Бродяжник 18.08.2004 20:24

Нормально отношусь! Ламер я... зато со стажем! Таймер я в программе отключил, чтобы наглядно было видно, что при старте программы он должен быть отключен. Когда его на форму кидаешь, он по умолчанию включен. Конечно, его нужно отключать прямо в Инспекторе объектов. А про инициализированные переменные в Дельфи я и правда не знал... я всю жизнь думал, что это привилегия Си. И с InvalidateRect vs Repaint тоже справедливо. А мерцать все равно будет на больших картинках, другое дело, что без двойной буфферизации мерцать будет гораздо мерцательнее.

Автор: BlackShadow 18.08.2004 21:01

В зависимости от версии переменные могут быть инициализированы или так Const x:Integer=0; или так Var x:Integer=0;
Первый вариант ещё и в паскале прокатывает. А ещё так можно статические переменные в функциях объявлять... Но это уже к теме не относится...

А про InvalidateRect vs Repaint ещё как справедливо. Насколько я помню так или иначе Repaint сводится к InvalidateRect(Handle,ClientRect). Незачем в таком случае перерисовывать всё...

а ещё мысль извратная появилась: сделать 2 TImage, расположить их друг на друге и делать так:
1). Рисуем в "спрятанном"
2). Выносим его вперёд.
Дальше вроде понятно. Что-то типа "видеостраницы своими руками". Вот в душе не знаю что это даст. Но попробывать можно... Жаль что Delphi под рукой нет sad.gif

Автор: Бродяжник 19.08.2004 12:37

Version 1.1.


Прикрепленные файлы
Прикрепленный файл  PANORAMB.ZIP ( 11.39 килобайт ) Кол-во скачиваний: 218

Автор: P@sh@ 19.08.2004 13:14

Alx
Ну а почему у меня процедура не вызывается?
Этот метод вызывается автоматически...
а что не рисует... может картинка не подгрузилась?
я же рабочий пример кинул...

PS: картинка у меня была большая (1024х768) и ничего не мерцало абсолютно

Автор: Alx 19.08.2004 15:43

Ой! Всё супер! Не мерцает! СПАСИБОЧКИ!
Вот только если вы тут такие умные собрались, может скажите, пожалуйста, можно так картинку не БМП, а ЙПГ крутить????? И если да, то как?

Автор: BlackShadow 19.08.2004 17:31

А что jpg нельзя загрузить так же как и bmp? Тогда попроьуй TPaintBox что ли... Позабывал уже всё с этим .Net sad.gif

Автор: P@sh@ 25.08.2004 14:20

Alx
насчет bmp и jpeg - я использовал класс TBitmap для загрузки картинки... есть и другие - TJPEGImage, например...
но лучше использовать универсальный TPicture - он сам распознает картинку по расширению файла, а рисовать можно через его свойство Graphic: что-то типа form1.canvas.draw(...., picture1.graphic);