Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Игры";
Текущий архив: 2007.09.02;
Скачать: [xml.tar.bz2];

Вниз

Вывод спрайтов в DrawGrid   Найти похожие ветки 

 
ruzzz   (2006-09-19 21:34) [0]

Здравствуйте, товарищи программисты!

Писал в форум "Media" по своей проблеме, но так и не смог получить дельного ответа. Посему обращаюсь в этот форум, так как проблема близка к тематике игр.

Итак, я пишу одно приложение, которое представлет из себя что-то вроде расписания движения поездов (Направление поезда -> Время). Информация о движении выводится в такое табло, которое раньше можно было наблюдать на железнодорожных вокзалах и аэропортах: каждая буква и цифра в отдельной ячейке на табло. При изменении информации (меняется только Время) цифры начинают перелистываться на подобие того, как это имело бы место при перелистывании страниц книги. Вид этого табло можно посмотреть здесь: http://podonok-studio.jino-net.ru/screen.jpg

Для реализации этого табло я использовал класс TDrawGrid, в ячейки объекта класса TDrawGrid выводил буквы и цифры. Объект DrawGrid1 занимает весь экран. Все цифры и все промежуточные состояния при их перелистывании (при изменении времени движения поездов) прорисовал в CorelDraw, получил кучу *.bmp файлов и засунул их в ImageList1, из которого потом методом ImageList1.Draw(DrawGrid.Canvas,R.Left,R.Top,i) по событию Timer1Timer выводил информацию на табло (т.е. вся эта мультипликация реализована в виде спрайтов). Так вот, изображения из ImageList1 в DrawGrid1 должны выводиться достаточно быстро, чтобы создавалась иллюзия перелистывания цифр. Максимальное число полей, которое может одновременно меняться (перелистываться) 30. При этом меня бы устроило, если бы следующее движение (изображение) выводилось через 30 ms. Однако по моим замерам при изменении изображений в 30 полях время составляет 90 ms (и это на моем достаточно новом ноутбуке: WinXP, Pentium 1.7 GHz mobile, RAM 512 MB, 256 MB video NVIDIA GeForce Go 6200, 798 MHz шина).

В связи с этим вопрос: как ускорить вывод изображений в табло? Создается впечатление, что это можно сделать только, используя DirectX или OpenGL, с которыми я не знаком. В частности, я не могу найти статей, которые бы описывали работу с DirectX при выводе спрайтов на какую либо форму (т.е. работу НЕ в полноэкранном режиме). Буду рад ЛЮБЫМ ИДЕЯМ по поводу создания данного приложения и ссылкам на статьи по работе с DirectX НЕ в полноэкранном режиме.

Заранее благодарен
Никита


 
Наиль ©   (2006-09-20 07:53) [1]

По моему ты мыслишь не в том направлении.
в DirectX прорисовка не зависит от сообщений Windows, в отличии от  DrawGrid. Т.е. эти две технологии не совместимы. Если хочешь использовать DirectX, то придётся отказаться от DrawGrid. Если уж отказываться, то в сторону Image.
Первое, что нужно сделать выяснить причину задержки 90мс.
Их может быть две:
1. Тормознутость таймера. Оценивается по разнице времени между двумя последовательным выполнениями OnTimer.
2. Медленая прорисовка. Оценивается по времени между началом и концом выполнения OnTimer.
Соотвествено два решения:
1. Отказаться от стандартного Таймера.
2. Оптимизировать. Возможно с нашей помощью (потребуется код, особено 17 строка). Уж кто-кто, а мастера игр лучше всех разбираются в оптимизации.


 
Cash ©   (2006-09-20 09:00) [2]

ruzzz   (19.09.06 21:34):
Уж ежели заводить разговоры о граф API, то в сторону DirectX советую
не глядеть! Бери на вооружение API OpenGL, у них расширяемость больше!
Если на опене кодить не охота, то GDI/GDI+ тебе в руки!

Медленно это может выводиться как из за неправильного кода, так и из за
не правильной обработки процесса отрисовки. Таймер тебе накой? OnPaint
бери или ваще Application.OnIdle!

Кста, опен может выводить на любой контрол, если у него имеется свой
контекст (канва, через ее хендл можно рисовать используя опен).

А если ваще в лес податься, то надо комбинацию сделать.
Там же у тебя наверняк есть еще всякие контролы, их оставить как есть,
а таблицу расписания сделать на OpenGL используя хендл панели (TPanel).
Всю таблицу ручками рисовать или в Application.OnIdle или в OnPaint панели,
используя OpenGL.
(При этом твои бмп-хи никуда девать не надо)


 
ruzzz   (2006-09-21 10:07) [3]

toНиаль: естественно, я использовал время между началом и концом выполнения OnTimer. То есть это именно отрисовка медленная, хотя время срабатывания таймера у меня стоит 30 ms. Привожу код отрисовки ниже.

//-----срабатывает на все ячейки DrawGrid!---------
procedure TForm2.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
 Rect: TRect; State: TGridDrawState);
var
 Symbol, index: integer;
begin

 //1) выяснить необходимый символ по массиву Goods
 //2) найти порядковый номер изображения в ImageList
 //3) вывести изображение с помощью ImageList1.Draw

 //Goods[ARow+1][ACol+1] - это дает символ, если Length(Goods[ARow+1])>=ACol
 //последние три поля заполняем НУЛЯМИ
 //все остальное заполняем пустым полем BLANK

 //далее используем конструкцию case, предварительно переведя буквы в коды

 Symbol:=0; //Если Symbol не переопределяется ниже, то остается=0 и index:=10 (blank)
 //Символы в типе String индексируются 1, 2, 3, ...
 //ACol меняется от 0 до 24
 if (Length(Goods[ARow+1])>=ACol)
 and (ACol<DrawGrid1.ColCount-4)              //последние 4 символа для пробела и цены
 and (Length(Goods[ARow+1])<>0)                //если строка пустая, то выводим blank
 then Symbol:=Ord(Goods[ARow+1][ACol+1]);
 case Symbol of
   192, 224: index:=37; //буква "а"
   193, 225: index:=38; //буква "б"
   194, 226: index:=39; //буква "в"
   195, 227: index:=40; //буква "г"
   196, 228: index:=41; //буква "д"
   197, 229: index:=42; //буква "е"
   168, 184: index:=43; //буква "ё"
   198, 230: index:=44; //буква "ж"
   199, 231: index:=45; //буква "з"
   200, 232: index:=46; //буква "и"
   201, 233: index:=47; //буква "й"
   202, 234: index:=48; //буква "к"
   203, 235: index:=49; //буква "л"
   204, 236: index:=50; //буква "м"
   205, 237: index:=51; //буква "н"
   206, 238: index:=52; //буква "о"
   207, 239: index:=53; //буква "п"
   208, 240: index:=54; //буква "р"
   209, 241: index:=55; //буква "с"
   210, 242: index:=56; //буква "т"
   211, 243: index:=57; //буква "у"
   212, 244: index:=58; //буква "ф"
   213, 245: index:=59; //буква "х"
   214, 246: index:=60; //буква "ц"
   215, 247: index:=61; //буква "ч"
   216, 248: index:=62; //буква "ш"
   217, 249: index:=63; //буква "щ"
   218, 250: index:=64; //буква "ъ"
   219, 251: index:=65; //буква "ы"
   220, 252: index:=66; //буква "ь"
   221, 253: index:=67; //буква "э"
   222, 254: index:=68; //буква "ю"
   223, 255: index:=69; //буква "я"

   48: index:=0; //цифра "0"
   49: index:=1; //цифра "1"
   50: index:=2; //цифра "2"
   51: index:=3; //цифра "3"
   52: index:=4; //цифра "4"
   53: index:=5; //цифра "5"
   54: index:=6; //цифра "6"
   55: index:=7; //цифра "7"
   56: index:=8; //цифра "8"
   57: index:=9; //цифра "9"

   65, 97: index:=11; //letter "a"
   66, 98: index:=12; //letter "b"
   67, 99: index:=13; //letter "c"
   68, 100: index:=14; //letter "d"
   69, 101: index:=15; //letter "e"
   70, 102: index:=16; //letter "f"
   71, 103: index:=17; //letter "g"
   72, 104: index:=18; //letter "h"
   73, 105: index:=19; //letter "i"
   74, 106: index:=20; //letter "j"
   75, 107: index:=21; //letter "k"
   76, 108: index:=22; //letter "l"
   77, 109: index:=23; //letter "m"
   78, 110: index:=24; //letter "n"
   79, 111: index:=25; //letter "o"
   80, 112: index:=26; //letter "p"
   81, 113: index:=27; //letter "q"
   82, 114: index:=28; //letter "r"
   83, 115: index:=29; //letter "s"
   84, 116: index:=30; //letter "t"
   85, 117: index:=31; //letter "u"
   86, 118: index:=32; //letter "v"
   87, 119: index:=33; //letter "w"
   88, 120: index:=34; //letter "x"
   89, 121: index:=35; //letter "y"
   90, 122: index:=36; //letter "z"

   34: index:=70; //"""
   40: index:=71; //"("
   41: index:=72; //")"
   45: index:=73; //"-"
   44: index:=74; //","
   46: index:=75; //"."
   95: index:=76; //"_"
   61: index:=77; //"="
   58: index:=78; //":"
   59: index:=79; //";"

 else index:=10;  //blank
 end;

 if (Length(Goods[ARow+1])<>0) and (ACol>DrawGrid1.ColCount-4) then index:=0;

 with Sender as TDrawGrid do
 begin
   //Canvas.Brush.Color := clBlack;
   //Canvas.FillRect(Rect);
   ImageList1.Draw(Canvas,Rect.Left,Rect.Top,index);
 end;

 Timer2.Enabled:=True; //включаем перелистывание цифр (тест)
end;

procedure TForm2.Timer2Timer(Sender: TObject);
var
 j: integer;
 //t1, t2: comp;                                    //это используется для измерения скорости прорисовки
begin
 inc(i);
 if i>79 then i:=0;   //цифр 10, а кадров 79 (с промежуточными кадрами)

 //t1:=TimeStampToMSecs(DateTimeToTimeStamp(Time));  //это используется для измерения скорости прорисовки

 for j:=0 to 10 do   //цикл по строкам
 begin
   R:=DrawGrid1.CellRect(22,j);             //первый столбец цифр
   ImageList2.Draw(DrawGrid1.Canvas,R.Left,R.Top,i);

   R:=DrawGrid1.CellRect(23,j);             //второй столбец цифр
   ImageList2.Draw(DrawGrid1.Canvas,R.Left,R.Top,i);

   R:=DrawGrid1.CellRect(24,j);             //третий столбец цифр
   ImageList2.Draw(DrawGrid1.Canvas,R.Left,R.Top,i);
 end;

 //t2:=TimeStampToMSecs(DateTimeToTimeStamp(Time));  //это используется для измерения скорости прорисовки
 //Timer2.Enabled:=False;                            //это используется для измерения скорости прорисовки
 //Panel1.Caption:=FloatToStr(t2-t1);                //это используется для измерения скорости прорисовки

 //RESUME: чтобы перевернуть все цифры, требуется 94 ms, против 30 ms нужных
 //полей с цифрами 11(рядов)x3(колонки)=33, на одно поле примерно 3 ms
 //размер поля 49x61 пикселей, черно-белое изображение
end;


 
Наиль ©   (2006-09-21 12:25) [4]

Для упрощение анализа я переписал код так, как мне удобно. С ним и будем работать. Надеюсь ты не против.
procedure TForm2.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
Symbol, index: integer;
begin
Symbol:=0;
if (Length(Goods[ARow+1])>=ACol)
and (ACol<DrawGrid1.ColCount-4)
and (Length(Goods[ARow+1])<>0)
then Symbol:=Ord(UpperCase(Goods[ARow+1][ACol+1])[1]);
// Быстрое (но не короткое) преобразование Symbol в Index
case Symbol of
  192..197: index:=Symbol-155;
  198..223: index:=Symbol-154;
  48..57: index:=Symbol-48;
  65..90, 97: index:=Symbol-54; //letter "a"
  168: index:=43; //буква "ё"
  34: index:=70; //"""
  40: index:=71; //"("
  41: index:=72; //")"
  45: index:=73; //"-"
  44: index:=74; //","
  46: index:=75; //"."
  95: index:=76; //"_"
  61: index:=77; //"="
  58: index:=78; //":"
  59: index:=79; //";"
else index:=10;  //blank
end;
if (Length(Goods[ARow+1])<>0) and (ACol>DrawGrid1.ColCount-4) then index:=0;
with Sender as TDrawGrid do begin
 ImageList1.Draw(Canvas,Rect.Left,Rect.Top,index);
end;
Timer2.Enabled:=True; //включаем перелистывание цифр (тест)
end;

procedure TForm2.Timer2Timer(Sender: TObject);
var
i,j: integer;
R:TRect;
begin
inc(Frame);
if Frame>79 then Frame:=0;
for i:=0 to 2 do
 for j:=0 to 10 do begin
  R:=DrawGrid1.CellRect(22+i,j);
  ImageList2.Draw(DrawGrid1.Canvas,R.Left,R.Top,Frame);
 end;
end;


 
Наиль ©   (2006-09-21 12:56) [5]

Не тестировал. Выглядеть это должно ужасно, но важен принцип.
Хотелось бы узнать о результатах.
procedure TForm2.Timer2Timer(Sender: TObject);
var
i,j: integer;
R:TRect;
MainBm,Bm:TBitmap;
begin
inc(Frame);
if Frame>79 then Frame:=0;
bm:=TBitmap.Create;
//размер поля 49x61 пикселей, черно-белое изображение
bm.Width:=49;
bm.Height:=61;
MainBm:=TBitmap.Create;
// 1ая экономия. Дополнительная обработка по совмещению маски и рисунка
// выполняется 1 раз.
ImageList2.Draw(bm.Canvas,0,0,Frame);
MainBm.Width:=3*49;
MainBm.Height:=11*61;
for i:=0 to 2 do
 for j:=0 to 10 do
  MainBm.Canvas.Draw(i*49,j*61,bm); // 2я экономия. Работа с изобранием в памяти
r:=DrawGrid1.CellRect(22,0);
With r.TopLeft do
 // Единственный (медленный) вывод на контрол
 DrawGrid1.Canvas.Draw(x,y,MainBm);
bm.Free;
MainBm.Free;
end;

Думаю ты сможешь доработать до нормально вида.


 
Cash ©   (2006-09-21 14:47) [6]

ruzzz, идейка!
С классами работать умеешь? Делай каждую позицию буквы как класс!
У него метод перелистывания на нужную букву и позиция отрисовки.
А по таймеру их обновлять, чтоб те, которые изменились, отрисовывались на
backbuffer (TBitmap). А когда все отрисуются, выводи Backbuffer на экран.

Отрисовку каждой буквы делай через BitBlt, backbuffer копируй на экран
так же.

Это вроде должно быть быстрее.

Желательно еще все кадрики хранить в одной bmp-хе, так будет меньше
забот и можно будет от ImageList-а отказаться. Это тоже должно повысить
быстроту.


 
ruzzz   (2006-09-22 20:13) [7]

toНаиль:
Спасибо, мультипликация работает без тормозов, просто супер!
Попытался измерить время вывода 33 полей, получил 0 ms ;)

Однако есть два "но":

> // 1ая экономия. Дополнительная обработка по совмещению маски и рисунка
> // выполняется 1 раз.

Ты заводишь один Bm: TBitmap, а потом его размножаешь на MainBm: TBitmap.
В моем тестовом примере все изображения одинаковы и меняются синхронно,
в рабочем же приложении изображения в каждой ячейке в общем случае разные, хотя и меняются синхронно. Поэтому просто размножать Bm: TBitmap не получится. Поэтому 1ая экономия отпадает.

> // 2ая экономия. Работа с изображением в памяти.

Изображения Bm: TBitmap на MainBm: TBitmap соседствуют друг с другом без промежутка. Однако, у меня GridLineWidth:=2; и бордюр затирается битмэпом. Однако, это должно быть решаемо.


 
ruzzz   (2006-09-22 21:25) [8]

toНаиль:
Решил тут все-таки проверить как будет с разными числами (отказаться от копирования Bm: TBitmap по всему полю MainBm: TBitmap). Переделал код вот так:


procedure TForm2.Timer2Timer(Sender: TObject);
var
 i, j: integer;
 R: TRect;
 MainBm, Bm: TBitmap;
 //t1, t2: comp;                                        //TIME-TEST
begin
 //t1:=TimeStampToMSecs(DateTimeToTimeStamp(Time));       //TIME-TEST
 inc(Frame);
 if Frame > 79 then Frame := 0;

//размер поля 49x61 пикселей, черно-белое изображение
 MainBm := TBitmap.Create;
 MainBm.Width := 3*49+22;       //+22 чтобы учесть бордюр
 MainBm.Height := 11*61+22;
 //MainBm.TransparentColor:=clWhite;
 for i := 0 to 2 do
   for j := 0 to 10 do
   begin
     bm := TBitmap.Create;
     bm.Width := 49;
     bm.Height := 61;

     ImageList2.Draw(bm.Canvas,0,0,Frame);

     MainBm.Canvas.Draw(i*49+i*2,j*61+j*2,bm); // 2я экономия. Работа с изображением в памяти
     bm.Free;
   end;  
 r:=DrawGrid1.CellRect(22,0);
 with r.TopLeft do
 // Единственный (медленный) вывод на контрол
 DrawGrid1.Canvas.Draw(x,y,MainBm);

 MainBm.Free;
 //t2:=TimeStampToMSecs(DateTimeToTimeStamp(Time));    //TIME-TEST
 //Timer2.Enabled:=False;                              //TIME-TEST
 //Panel1.Caption:=FloatToStr(t2-t1);                  //TIME-TEST
end;


Опять все тормозит, время обновления картинок примерно 90 ms.
Так что узкое место именно то, которое не получается.


 
Ketmar ©   (2006-09-22 21:35) [9]

ни в коем случае не надо каждый раз создавать TBitmap. это больно и очень долго.


 
ruzzz   (2006-09-23 06:45) [10]

toKetmar:
Даже если вынести создание и уничтожение Bm: TBitmap за цикл, ничего не
меняется, это не решает проблему:

bm := TBitmap.Create;
bm.Width := 49;
bm.Height := 61;
for i := 0 to 2 do
  for j := 0 to 10 do
  begin
    ImageList2.Draw(bm.Canvas,0,0,Frame);
    MainBm.Canvas.Draw(i*49+i*2,j*61+j*2,bm);
  end;  
bm.Free;

Время обновления картинок все равно 90 ms.


 
Ketmar ©   (2006-09-23 07:51) [11]

>[10] ruzzz 23-Sep-2006, 06:45
>Даже если вынести создание и уничтожение Bm:
>TBitmap за цикл, ничего не
>меняется, это не решает проблему:
для начала вообще перестань создавать какие-либо битмапы при каждой отрисовке. а потом будем посмотреть дальше.


 
ruzzz   (2006-09-23 08:22) [12]

Если кому еще интересно, проблема была решена следующим образом:

procedure TForm2.Timer2Timer(Sender: TObject);
var
 i, j: integer;
 Source, Dest, R: TRect;
 MainBm, Template: TBitmap;
begin
 inc(Frame);
 if Frame > 79 then Frame := 0;

 MainBm := TBitmap.Create;
 MainBm.Width := 3*49+22;      
 MainBm.Height := 11*61+22;

 Template := TBitmap.Create;
 Template.LoadFromFile("numbers.bmp");

 Source.Left := Frame*49;
 Source.Top := 0;
 Source.Right := Source.Left+49;
 Source.Bottom := 61;

 for i := 0 to 2 do
   for j := 0 to 10 do
   begin
     Dest.Left := i*49+i*2;
     Dest.Top :=j*61+j*2;
     Dest.Right := Dest.Left+49;
     Dest.Bottom := Dest.Top+61;

     MainBm.Canvas.CopyRect(Dest, Template.Canvas, Source);
   end;
 Template.Free;

 r:=DrawGrid1.CellRect(22,0);
 with r.TopLeft do  DrawGrid1.Canvas.Draw(x,y,MainBm);
 MainBm.Free;
end;

В numbers.bmp находятся все кадры перелистывающихся чисел.
Все работает шустро.


 
Наиль ©   (2006-09-29 10:22) [13]


> Изображения Bm: TBitmap на MainBm: TBitmap соседствуют друг
> с другом без промежутка. Однако, у меня GridLineWidth:=2;
> и бордюр затирается битмэпом.

Поэтому я писал про ужасный вид.

>  [12] ruzzz   (23.09.06 08:22)

Вынеси создание и уничтожение Bitmap"ов в FormOnCreate и FormOnDestroy, соотвествено. Размеры задавай там же (где создание). Там же загрузка рисунка. Экономия будет колоссальной.



Страницы: 1 вся ветка

Форум: "Игры";
Текущий архив: 2007.09.02;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.05 c
2-1186745000
AlRal
2007-08-10 15:23
2007.09.02
TListBox


2-1186835096
Ivolg
2007-08-11 16:24
2007.09.02
Про WebBrowser


2-1186718334
Алексей О.
2007-08-10 07:58
2007.09.02
Распределение квадратов по ширине


9-1159531329
KygECHuK
2006-09-29 16:02
2007.09.02
Физика


3-1178801337
DeadMeat
2007-05-10 16:48
2007.09.02
Транзакции





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский