CPU3D.comКомпьютерная графикаДвухмерная графика → Низкоуровневая загрузка растра

Низкоуровневая загрузка растра

хранение файлов

Введение

В статье рассматриваются два способа загрузки растра - из ресурсной секции PE файла (для тех кто не в курсе, это обычных исполняемый exe файл семейства Widows 9X, NT) и непосредственно из файла. Зачем я всё это описываю, если есть прекрасный, отработанный кодVar
Bitmap : Tbitmap;

Bitmap.LoadFromFile('somepicture.bmp');


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

Итак если Вам не интересны приведенные выше доводы, можете смело закрывать окно браузера, а для оставшихся читателей продолжим. Для начала рассмотрим самый простой, с точки зрения реализации, метод - загрузка растра из ресурса.
Загрузка растра из ресурса

Перед тем как пытаться загружать растр разберёмся, как же поместить его в файл ресурса. Допустим, мы хотим подключить BMP файл с названием some24bit.bmp к нашему приложению. Создадим ресурсный файл samp.rc и добавим туда следующую строчку:MYBITMAP BITMAP ..Resourcesome24bit.bmp

Компилируем файл samp.rc для получения бинарного файла ресурса samp.res, набрав в командной строке brcc32.exe samp.rc. Подключим в наш проект полученный ресурсный файл, добавив директиву компилятора {$R samp.res}.

Описание функция для загрузки растра из ресурсного файла выглядит слдедующим образом:function LoadBitmap(hInstance: HINST; lpBitmapName: PAnsiChar): HBITMAP;

Функция имеет всего два параметра и возвращает нам указатель на структуру загруженного растра - HBitmap. В качестве hInstance передаем Instance нашего приложения, а в качестве параметра lpBitmapName используется идентификатор из RC файла, в нашем случае это MYBITMAP.

Кстати, если Вы не знаете, что такое Hbitmap, как его потом использовать или безуспешно пытаетесь заменить Hbitmap на Tbitmap - смело закрывайте браузер, статья не для Вас.

Как Вы могли заметить, это пожалуй самый простой способ инициализации и загрузки растра. Всё что от нас требуется это подключить растр к ресурсному файлу и вызвать функцию LoadBitmap. Причем функция LoadBitmap загружает любые растры, в не зависимости от количества бит цвета.

Пример использования функции описан ниже, а полный код примера находится в каталоге DIBFromRes.var
Bitmap : HBitmap;

Bitmap := LoadBitmap(Hinstance, 'MYBITMAP');

Не лишним является проверка указателя Bitmap на 0, т.к. растра может по каким то причинам не загрузится.

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

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

Допустим, у вас существует по несколько десятков спрайтов для каждого персонажа игры (движения главного героя, пули, взрывы, текстуры фона, движения врагов) и все они хранятся в отдельных файлах. Это удобно для редактирования, но совсем не применимо для хранения - ведь придется подключать в ресурс сотни файлов, и потом их загружать. По этому лучше всего объединить сходную графику в одни файлы : hero.bmp, shot.bmp, expl.bmp и т.д.

В такой реализации существенно сокращается время загрузки программы и её размер, т.к. мы сокращаем количество заголовков BMP файла с сотни до 5-10.

Теперь поговорим о защищенности графических ресурсов - её просто нет :) Любой, более менее грамотный пользователь, всегда сможет без труда извлечь или изменить эти графические данные воспользовавшись программой типа Resource Workshop или PEExplorer.

Как можно предотвратить подобные нежелательные действия? Самый простой, но далеко не оптимальный путь, сжатие готового exe файла различными компрессорами типа ASPack, UPX и т.д. Самый хороший результат даёт ASPack 2.12, но во первых, незначительно увеличивается расход памяти, а во вторых, более пытливый пользователь (скажем так, не полный ламер:)) знает о таких программках как ASPackDie - весь ваш труд окажется напрасным.

Единственный и пожалуй самый действенный метод защиты от такого "беспредела" - хранение сжатых ресурсов и изменение заголовков.

Первый способ заключается в том, что к exe файлу подключается уже сжатый bmp файл. На мой взгляд самый оптимальный вариант использование библиотеки Zlib - она поставляется вместе с Delphi, имеет не плохую степень компрессии (причем изменяемую, можно легко варьировать степенью сжатия и временем загрузки) и самое главное самодостаточна. Т.е. нет необходимости таскать с приложением набор dll файлов для разархивации. Этот метод я попробую подробно изложить в продолжении данной статьи.

Изменение заголовков. Чтобы понять суть метода приведу небольшой пример. Допустим у нас есть 10 одинаковых BMP файлов, т.е. у них одинаковый размер (высота, ширина), палитра (если 256 цветные), количество цветов, следовательно мы можем хранить только 1 заголовок и данные для остальных файлов. Скажем так, это не только защита ресурсов Вашей программы, но и экстремальная оптимизация по размеру:) Этот метод я так же изложу в продолжении данной статьи.
Загрузка растра из файла

Рассмотренный выше способ довольно прост в реализации, но не очень гибок, хотя и применяется в большинстве коммерческих продуктов. Он не подходит для большинства больших игр - разве Вы видели файл типа Dialblo.exe размеров ~600 МБ :) … нет вот и я не видел, а вот файлы типа gamedata.dat размерами 1 Гб пожалуйста.

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

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

В настоящее время широко используются растры двух форматов 24 битные и 8 битные. 24-х битные обычно применяются в качестве текстур, фонов и т.д., а 8-ми битные до сих пор применяю для хранения спрайтов. Да, да, 8 битные растры до сих пор в моде, хороший пример тому культовая игра Heroes III (на счет четвёрки не знаю).

По этому я напишу две разные процедуры для загрузки растров, хотя можно было бы объединить их в одну, но зачем на делать лишние проверки ? Ведь при загрузке мы точно знаем какой тип растра загружаем.

Первым делом объявим глобальную переменную для хранения загруженного растра:Var

// Переменная для хранения растра
Bitmap : HBITMAP;



Источник: http://www.delphigfx.narod.ru