Статья 2. Загрузка и вывод изображения в формате bmp на экран
Цель урока: загрузить изображение из графического bmp-файла и вывести его в SDL поверхность для трех настроенных в первой статье платформ (Linux, Windows, WindowsCE).
Содержание статьи: 1. Введение; 2. Особенности WindowsCE; 3. Компиляция примера; 4. Заключение.
1. Введение Продолжим изучение кросс-платформенной графической библиотеки SDL в Linux. Для компиляции исходников статьи нужно настроить компиляторы, как описано здесь. Сегодня научимся загружать bitmap’ы – графические файлы с расширением .bmp. Почему не gif, или tga, спросите Вы – они же значительно меньше «весят»? Все верно, но SDL без библиотеки-расширения SDL_Image поддерживает только загрузку .bmp-файлов. Со временем научимся грузить и их =)
2. Особенности WindowsCE Для загрузки в системах Windows и Linux, если графический файл picture.bmp поместить в папку с исполняемым файлом, достаточно такого кода:
Этот код в WinCE, к сожалению, ничего не загрузит. А все из-за того, что в WinCE отсутствует понятие текущей директории, там для загрузки картинки в общем случае требуется указать полный путь. Допустим, если файл picture.bmp размещен в корне карты памяти: loaded_picture= SDL_LoadBMP("Storage Card/picture.bmp”);//простая загрузка в wince Так-же полный путь, указанный для загрузки в программе для Linux поможет избежать неуверенной загрузки картинки при запуске программы из GUI-файловых менеджеров (konqueror, dolphin и др.), которые не всегда корректно обозначают текущую директорию. Это не прибавляет удобства как для кросс-компиляции, так и для готовой программы. Ведь мы не знаем заранее в какой папке пользователь разместит екзешник и ресурсы. А просить его сделать это в заранее заданном месте как-то не солидно =). Тут можно попробовать узнать из какой папки был запущен исполняемый файл, и в зависимости от этого настроить загрузку нашей картинки. Создайте чистую папку для нового микропроекта. В ней создайте файл main.cpp. Для ресурсов в ней же создайте дополнительную папку res и в нее поместите небольшой файл picture.bmp.(ссылка на готовый проект в конце статьи – можете взять оттуда). Пишем в нашем main.cpp:
//Подключаем заголовочные файлы #include <cstdio>//для printf #include "SDL.h" #include <cstring>//для работы со строками: strcpy и strcat
//Определим подстановки - символические константы #define MAX_PATH_LENGTH 128 //максимальное кол-во символов пути исполняемого файла #define RESOURCE_PATH "res/" //директория ресурсов программы #define PICTURE "picture.bmp" //название файла картинки
Как Вы знаете, подстановки при компиляции будут заменены их значениями. Например, на место PICNURE будет подставлено "picture.bmp”. Это сделано для удобства, теперь, чтобы изменить путь к ресурсам программы или название загружаемого файла не нужно отыскивать все их вхождения в исходник. Достаточно изменить только одну строку в начале программы. Далее:
//Глобальные константы и переменные const int SCREEN_WIDTH= 240; const int SCREEN_HEIGHT= 320; const int SCREEN_BPP= 16; const char resource_name[]= RESOURCE_PATH;//создаем массив символов с путем от экзешника до ресурсов "res” const char pict_name[]= PICTURE;//имя .bmp-файла Uint32 back_color;//Этим цветом буду закрашивать экран SDL_Surface *screen= NULL;//Основная поверхность SDL SDL_Surface *loaded_picture=NULL;//поверхность SDL для загружаемой картинки
Выше мы задали две строки – два символьных массива: resource_name и pict_name, оканчивающихся нуль-терминатором. О работе со строками в С/С++ можно прочитать хотя бы здесь, или здесь.
Далее, используя нулевой аргумент функции main посмотрим, что передает ей каждая из наших операционных систем:
linux (console, mc): ./prog_name linux (файловые менеджеры с GUI): /home/proj/prog_name win32: D:\proj\prog_name.exe wince: \Storage Card\proj\prog_name.exe
а получить мы в итоге хотим: linux (console, mc): ./res/picture.bmp linux (файловые менеджеры с GUI): /home/proj/res/picture.bmp win32: /res/picture.bmp wince: /Storage Card/proj/res/picture.bmp
Рассмотим код далее:
//////////////////////////////////////////////// // ГЛАВНАЯ ФУНКЦИЯ ПРОГРАММЫ ////////////////////////////////////////////////
int main( int argc, char* args[] ) {
char res_path[MAX_PATH_LENGTH];//Путь запуска файла res_path[0] = '\000';//пока стринг пуст strcpy(res_path, args[0]);//скопировали полный путь запуска в res_path
printf("from OS=%s\n", res_path);
if (res_path[0]!='.')//Если вызов не из консоли linux или mc, то... { //Перевернем обратные слэши Windows for (unsigned int i= 0 ;i!= (strlen(res_path)-1); ++i)//работаем по длине стринга { if (res_path[i]=='\\') res_path[i]='/';//если нашли \, заменяем на / } }
//отсечем имя программы справа до первого слэша - просто заполним нулями for (int i= (strlen(res_path)-1);(res_path[i] !='/') && (i>=0); --i) res_path[i]='\000';
#ifndef UNDER_CE//Если это не wince #ifdef _WIN32//И это 32-разрядная windows res_path[0]='\000';//то очистим путь во избежание проблем с кирилицей в папках Windows #endif #endif
Здесь мы получаем в массив символов res_path нулевой аргумент, переданный ОС нашей функции main — путь ее запуска. Что, собственно и распечатывается в строке printf("from OS=%s\n", res_path);.Забыл сказать, что увидеть консольный вывод в WindowsCE штатными средствами нельзя — их просто нет =). Зато есть программа PocketConsole v1.3 (пользователи WinMobile5 не забудьте подправить реестр — там написано как и что сделать). Она автоматически запускается для приложений с консольным выводом. Не спрашивайте как она это делает — я не знаю, но она работает=). Далее, для вызова из консоли или mc просто отсекаем имя программы, для всех отальных ОС «переворачиваем» слэши Windows. Именно так — слэши для Linux и компиляторов семейства GCC должны быть прямыми, а не обратными как в семействе Windows (нужно переворачивать). В конце манипуляций отсекаем имя программы — оно нам не нужно. Еще один момент: чтобы избежать проблем с русскоязычными названиями в пути «большой» Windows (с CE, как ни странно все нормально), будем грузиться в ней только по относительному пути. Т.е. Для нее res_path очищаем.
В коде выше применена условная компиляция для задания кода только для «большой» Windows:
#ifndef UNDER_CE//Если это не wince #ifdef _WIN32//И это 32-разрядная windows res_path[0]='\000';//то очистим путь во избежание проблем с кирилицей в папках Windows #endif #endif
Аналогично можно иcпользовать #ifdef linux и #ifdef _WIN32 (соответственно только для Linux и всех 32-битных Windows, включая WinCE). Только в конце блоков условной компиляции не забывайте #endif. Итак в переменной (хотя это массив) res_path получили ./ или полный путь до исплняемого файла для linux, пустой стринг для win32 и путь к исполяемому файлу для wince. Добавим res/ имя папки с ресурсами и имя нашей картинки picture.bmp:
strcat(res_path, resource_name);//добавим наименование каталога ресурсов strcat(res_path, pict_name);//добавим имя файла-картинки printf("loading path=%s \n", res_path);
Оператор printf... и здесь позволит нам проконтролировать то ли мы получаем в итоге, что нужно. Ок. Теперь в res_path все готово для загрузки. Инициализируем SDL и грузим:
//Set up the screen #ifdef UNDER_CE// Под WinCE используем полноэкранное окно, исп. основную память и абсолютный путь файлов screen= SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE | SDL_FULLSCREEN); #else //Под Win32 и Linux используем стандартное окно и память видеокарты для размещения поверхностей SDL screen= SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_HWSURFACE); #endif
//If there was an error in setting up the screen if (screen== NULL) { printf("Unable to SetVideoMode\n"); return 1; } else printf("VideoMode settings -- Ok\n");
//загрузим картинку из найденного ранее пути res_path loaded_picture= SDL_LoadBMP(res_path); SDL_BlitSurface(loaded_picture, NULL, screen, NULL); SDL_Flip (screen);
SDL_Delay(4000);// Задержка перед выходом чтобы увидеть результат работы программы
//удалим поверхности SDL перед выходом printf("SDL quiting...\n"); SDL_FreeSurface(loaded_picture); SDL_FreeSurface(screen); SDL_Quit();
return 0; }
Теперь файл main.cpp сохраним, скомпилируем под linux при помощи Makefile из первой статьи. Запустим. Работает? Если нет, то проверьте, рядом с Вашим исходником должна быть папка с именем res, а в ней должна лежать картинка picture.bmp. Получилось? Отлично! Попробуйте сами скомпилировать так же под win32 и wince. P.S.: Исходный файл, Makefile, picture.bmp и готовые исполняемые файлы для 3-х платформ можно скачать здесь. Примечание: там-же лежит SDL.dll для запуска в win32 или в Wine.