Итак, libSDL (Simple DirectMedia Layer) это кроссплатформенная библиотека для работы с видео, аудио, устройствами ввода. Ничего не напоминает? Конечно же, DirectX. Но почему я пишу про про SDL, а не повсеместно известный Прямой Х? У меня на это есть пять причин:
1. SDL кросплатформенная библиотека (Linux, Win, WinNT, BSD, Mac);
2. В качестве 3D-рендера в SDL используется OpenGL, который проще для изучения и одновременно кое-где помощнее Direct3D (в линуксовом windows-эмуляторе последний эмулируется именно через OpenGL);
3. SDL можно использовать в программах на C/C++, Object Pascal (Delphi&Kylix), FreePascal, Perl;
4. Библиотека обладает некоторыми уникальными возможностями;
5. libSDL очень проста в изучении.
На основе SDL были созданы дополнительные библиотеки, расширяющие возможности родительской. К самым популярным библиотекам относятся:
SDL_image (обработка графики в форматах BMP, PNM (PPM/PGM/PBM), XPM, LBM, PCX, GIF, JPEG, PNG, TGA, и TIFF чистая SDL позволяет работать только с BMP)
SDL_mixer (позволяет выводить звук через несколько каналов).
Найти библиотеку можно на сайте http://www.libsdl.org/. Там же обитают ссылки на разные родственные проекты (большей частью игры и производные библиотеки).
Небольшое отступление: когда я начинал писать эту статью, я долго думал, на каком языке давать примеры. Так я одинаково люблю С и Delphi, я решил комментировать на Дельфи код в примерах под Windows, а на Си под Linux.
Первые шаги
Начнем, пожалуй, с Дельфи. В Си проблем меньше, так как оригинальные исходники библиотеки и ее производных написаны именно на этом языке. В большинстве современных дистрибутивов есть RPM'ы SDL'а. Чтобы использовать SDL в Delphi/Kylix, вам понадобится пакет JEDI-SDL (http://www.delphi-jedi.org/, вес около 16 Мб). Кроме самого SDL, в пакет входят порты библиотек SDL_Mixer, SDL_Net, SDL_Image, SMPEG, SDL_sound и SFont. Установка пакета предельно проста, к тому же детально описана в документации, поэтому на ней мы останавливаться не будем и сразу перейдем к программированию.
Создайте новый проект и первым делом удалите основную форму. Откройте основной файл проекта и начинайте писать:
или в Си:
Для начала напишем традиционную программу «Hello World!». Рис. 1.
Перед началом работы, SDL необходимо инициализировать. Также необходимо инициализировать все сторонние производные SDL. В связи с этим первым оператором нашей программы будет функция SDL_Init(SDL_INIT_VIDEO). Кроме того, с ее помощью можно инициализировать другие части библиотеки, к примеру звуковую подсистему. Результатом работы функции будет код ошибки. Если он не равен -1, то все в порядке, в противном случае функция SDL_GetError выдаст строку, описывающую ошибку. Кроме того, в конце программы необходимо корректно отключить все используемые библиотеки. Для SDL это выглядит так:
Также следует соблюдать порядок вложения библиотек, но к этому мы вернемся чуть позже. А сейчас запустим нашу программу. Скорее всего, она завершится, едва вы успеете нажать Enter. Теперь надо расширить код, чтобы он давал видимый результат.
Небольшое отступление: Программа может выдать ошибку вроде той, что показана на Рис. 2. Это может быть связано с тем, что динамическая составляющая SDL не была найдена или ее версия и версия библиотеки, использовавшейся при компиляции, различны. При необходимости выполните ldconfig. В случае с Windows отправьте SDL.dll в c:\windows\system\ (или куда-то еще в зависимости от того, где у вас находится системная папка).
Работа с WM
WM (Window Manager) это программа, которая занимается отрисовкой окон и взаимодействием с ними. SDL может взаимодействовать с WM и контролировать вид окна заголовок, иконку, состояние (минимизированное, развернутое) и еще несколько параметров. К примеру, для установки заголовка окна служит функция SDL_WM_SetCaption:
Или в C:
Чтобы создать окно, следует объявить переменную поверхности (в таких переменных можно хранить и обрабатывать изображения, кроме того, они могут служить ссылками на окна):
Такая переменная, собственно говоря, является указателем и должна быть инициализирована и уничтожена (для инициализации есть несколько функций, а убивает указатели на поверхность функция SDL_FreeSurface). Хочу отметить, что изображение может храниться как в ОЗУ, так и в видеопамяти. В любом случае манипулировать им достаточно просто.
Один из способов создать указатель сослать его на окно при помощи функции SDL_SetVideoMode. Ее входные параметры: width, height, bpp : Integer; flags : UInt32. Первые три параметра задают размер и глубину цвета (если bpp равен нулю, то используется текущая глубина), параметр flags устанавливает флаги окна, которые можно комбинировать, используя or. Список допустимых флагов приведен в Таблице 1.
Итак, окно у нас есть, его заголовок SDL Hello World Application. Но чтобы успеть его увидеть, необходимо поставить задержку. Это можно сделать средствами языка (sleep, delay), но лучше использовать SDL_Delay.
Наша программа приобретет следующий вид:
Сишники, подтягивайтесь! Для вас код выглядит приблизительно вот так:
Компилируем, исправляем «очепятки», опять компилируем :-), запускаем, смотрим... Ура! Мы получили пустое черное окно, которое ждет пять секунд и исчезает (если вы увидели окно в красно-черно-белых разводах оторвитесь от чтения МК и отдохните :-)).
Ну и что теперь?
А все что захотите! Теперь вся мощь библиотеки SDL у вас в руках. Знать бы, как ее использовать... Этим мы сейчас и займемся. Начнем с основной подсистемы, которая называется...
Видео
SDL позволяет выполнять разные операции над графикой. В него встроен интерфейс работы с OpenGL, кроме того, с помощью SDL_Image можно обрабатывать разные форматы графики. Недостатком библиотеки можно считать отсутствие функций для рисования примитивов (точек, линий, прямоугольников, кругов и т.п.) на поверхностях. В самом SDL изображение представлено указателем pixels в структуре SDL_Surface, но как этот указатель обрабатывать, для меня осталось загадкой. Конечно, на помощь может прийти SDL_Draw (http://sourceforge.net/projects/sdl-draw/) библиотека, в которой эти функции реализованы, но под Delphi/Kylix она еще не портирована. Поэтому я ее описывать не буду, а если кого из сишников она заинтересует, пусть читают прилагающуюся к ней документацию кстати, она очень качественно написана.
Давайте посмотрим, как загружать изображения разных форматов, заодно и приукрасим наш «Hello World». Для работы нам понадобится библиотека SDL_Image. Подключаем:
или
Насчет последнего я не могу быть уверен, но в моем дистрибутиве Линукса rpm кинул заголовки именно туда.
Библиотека SDL_Image в инициализации не нуждается. Функция библиотеки IMG_Load загружает графический файл одного из форматов и инициализирует поверхность его изображением. Данная функция сама определяет тип файла. Обратите внимание на то, что некоторые типы файлов требуют дополнительных библиотек (libpng1.dll, jpeg.dll и zlib.dll необходимы для соответствующих форматов. Список под Linux мне, к сожалению, не попадался, но тем, кто будет компилировать библиотеку, не составит труда определить зависимости).
Теперь мы можем создать еще одну поверхность и загрузить в нее изображение из файла. Для того чтобы отобразить одну поверхность на другой, используйте функцию SDL_Blit. В качестве параметров она принимает поверхность-источник, поверхность-приемник и два прямоугольника размеров типа PSDL_Rect (*SDL_Rect в Си). В случае с Дельфи проще использовать переменные TSDL_Rect и передавать их в виде @rect. Если вместо rect'ов передать nil (0), то будет использована вся поверхность, но если передать два nil'а, то поверхности не будут масштабироваться друг к другу, просто вся поверхность-источник отобразится на приемнике начиная с левого верхнего угла.
После работы с поверхностями для перерисовки окна используйте SDL_UpdateRect (screen : PSDL_Surface; x, y: SInt32; w, h: UInt32). Эта функция обновит экран в указанном прямоугольнике или весь экран (для этого передайте x, y , w и h равными нулю). Но если вы используете двойную буферизацию, то для обновления экрана необходимо использовать SDL_Flip. Эта функция автоматически отобразит все сделанные изменения и поменяет буферы. Тройной буферизации в SDL'е нет, но вы ведь не собираетесь обгонять второй Unreal по графике :-)?
Что еще пригодится при работе с поверхностями? Мне часто приходится использовать свойство прозрачности для отрисовки непрямоугольных фигур. Для установки цвета прозрачных пикселов служит конструкция SDL_SetColorKey. Я думаю, что проще всего обрабатывать цвет Fuchsia (RGB: 255/0/255). Он обычно нигде не используется и хорошо заметен:
На этом я с вами прощаюсь. А пока вы будете ждать продолжения, можете написать программу, способную отобразить графический файл, переданный в параметре (какой лентяй сказал «ACDSee» :-)?) Пусть программа завершит работу через пять секунд после отображения рисунка. Исходники и откомпилированые бинарники присылайте мне на farcaller@bigmir.net.
В следующий раз мы рассмотрим события и усложним наш вьювер. Удачи!