Главная » 2D графика » Способы хранение графики в играх и бизнес приложениях

RSS

Способы хранение графики в играх и бизнес приложениях

Не нравитсяНравится   Рейтинг 0

В предыдущей статье я рассказал, как можно считывать растры напрямую из файла ( надеюсь информация оказалась для Вас интересная). Теперь поговорим о том, как создать собственный, удобный для нас, формат хранения графической информации. Рассматриваемый подход пригодится не только для хранения графики, но и для совершенно различных бинарных данных. Это могут быть и музыкальные треки в популярном формате MP3, видео фрагменты, текстовые данные – в общем, любые данные Вашего приложения.

Хранение графики в приложениях

В конце предыдущей статьи, я изложил краткий алгоритм для создания таких файлов, теперь я попытаюсь последовательно описать все его тонкости.

Возможная структура файла

Для начала набросаем приблизительную структуру нашего будущего формата данных. Для примера, я создам файл для хранения обычных растров в не компрессированном виде.

FileHeader

TGameRusourceHeader

Name String[32] Название файла
Version Integer Версия
ImageCount Integer Количество изображений

ResourceTable

TGameResourceTable

ResName String[32] Название ресурса
Offset Integer Смещение от начала файла
ResourceData
Графические данные

Компилятор

Начнем создание компилятора с заготовки необходимых структур:

1
2
3
4
5
6
7
8
9
10
TGameRusourceHeader= packed record
  Name : string[32];
  Version : Integer;
  ImageCount : Integer;
end;
 
TGameResourceTable = packed record
  ResName : string[32];
  Offset : Integer;
end;

Надеюсь тут все понятно. Есть только две небольшие тонкости. Первая нельзя использовать просто String – только фиксированную длину строки! Иначе SizeOf(TGameRusourceHeader) выдаст совершенно не верный результат. Вторая тонкость по организации проекта. Т.к. нам нужно написать и компилятор ресурсов и загрузчик, лучше вынести описание заголовков в отдельный модуль.

Как уже писалось выше, я буду использовать VCL компоненты и стандартный набор классов. Это сильно сократит исходный код, да и сделает его понятным. Алгоритм процедуры «сшивания» ресурсов следующий. Процедуре будем предавать список файлов, и название выходного файла.

В переменные заведем три потока и другие необходимые переменные:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure TDIBCompilerForm.CompileResource(const SourceFileList:TStringList; const OutFileName: string);
var
  // Поток для записи окончательного файла
  OutStream : TStream;
  // Поток для хранения таблицы смещений
  TableStream : TStream;
  // Поток для хранения данных
  ResourceStream : TStream;
  // Кол-во изображений
  ImgCount : Integer;
  // Смещение от начала файла текущее растра
  Offset : Integer;
  // Размер заголовка и размер всей таблицы смещений
  HeaderSize : Integer;
  AllTableSize : Integer;
  // Экземпляр для записи в таблицу смещений
  GameResourceTable : TGameResourceTable;

Для начала работы процедуры проинициализируем все объекты и переменные:

1
2
3
4
5
6
7
8
9
...
OutStream:=TFileStream.Create(OutFileName, fmCreate);
TableStream:=TMemoryStream.Create;
ResourceStream:=TMemoryStream.Create;
ImgCount:=1;
HeaderSize:=SizeOf(TGameRusourceHeader);
AllTableSize:=SizeOf(TGameResourceTable)*SourceFileList.Count;
Offset:=HeaderSize+AllTableSize;
...

Самой сложной частью процедуры я считаю рассчет смещений, все остальное достаточно прозрачно:

1
2
3
4
5
6
7
8
9
for I:=0 to SourceFileList.Count-1 do
begin
  Bitmap.LoadFromFile(SourceFileList[I]);
  Bitmap.SaveToStream(ResourceStream);
  GameResourceTable.ResName:=ExtractFileName(SourceFileList[I]);
  GameResourceTable.Offset:=Offset;
  ...
  TableStream.WriteBuffer(GameResourceTable, SizeOf(TGameResourceTable));   Offset:=HeaderSize+AllTableSize+ResourceStream.Position; Inc(ImgCount);
end;

… на по следок копирование данных в выходной поток и очистка занятых ресурсов.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Перемещаемся на начало данных
ResourceStream.Seek(0, soFromBeginning);
TableStream.Seek(0, soFromBeginning);
// Устанавливаем количесво добавляемых битмапов в заголовок
GameRusourceHeader.ImageCount:=ImgCount;
// Запись выходного файла
OutStream.WriteBuffer(GameRusourceHeader, SizeOf(GameRusourceHeader));
OutStream.CopyFrom(TableStream, TableStream.Size);
OutStream.CopyFrom(ResourceStream, ResourceStream.Size);
// Блок финализации
ResourceStream.Free;
TableStream.Free;
OutStream.Free;
Bitmap.Free;

Вот собственно и всё. После успешного завершения процедуры у Вас получится файл с описываемой структурой. При работе процедуры, создается файл с расширением OutFileName.text, куда записывается вся информация о размерах структур, смещениях и т.д. Смещения записываются как в обычном десятичном виде, так и в шестнадцатеричной форме. Последняя форма записи очень помогает при анализе полученного файла в любом HEX редакторе (WinHex, Hview и т.д.).

Не возможно не упомянуть об одной особенности – уменьшении размера полученного файла. Поясню более подробно. Для примера я скомпилировал набор из 313 BMP файлов различного размера. Суммарный объем файлов 2, 359 Кб, после сборки получился файл размером 2,428 Кб – оно и понятно, мы записываем лишнею информацию. После сжатия архиватором ZIP отдельных BMP файлов получился архив размером 697 Кб, а вот при сжатии выходного файла – 640 Кб. Выигрыш очевиден, причем он растет с увеличением числа хранимых битмапов и уменьшения их размера. При сборке ~500 картинок размером 16×16 выигрыш получается более чем в два раза. Необходимо помнить, что для приложений распространяемых по сети размер дистрибутива до сих пор критичен. И если Ваша игра или утилитка «весит» в 5-6 раз меньше, чем аналоги, шанс что пользователь выберет именно её повышается не однократно.

Страницы : 1 2 3 4