Среда, 15.05.2024, 18:33
Personal dimanche13 site
Главная | Регистрация | Вход Приветствую Вас Гость | RSS
Категории каталога
Мои статьи [22]
BlitzMax [17]
раздел содержит статьи относящиеся к языку программирования BlitzMax
SDL [9]
раздел содержит статьи на тему SDL
Code::Blocks [3]
в этом разделе рассказывается как прикрутить движок к интегрированной среде разработки(IDE) Code::Blocks
Форма входа
Поиск
Друзья сайта
Главная » Статьи » SDL

Основы SDL (часть№4 рисуем спрайты)
ВВЕДЕНИЕ.
    И снова «ПРИВЕТ»! это опять я, dimanche13 и мой новый урок по библиотеке СДЛ.
    В прошлый раз, мы очень внимательно изучили устройство, так называемых, сурфейсов или в просторечье – поверхностей. Просмотрели все поля структуры и даже залезли глубже, дабы полностью понять уж если не ее функционирование, то как минимум ее содержимое, чтобы впоследствии манипулировать ею. К тому же, я надеюсь, вы уже поэкспериментировали с поверхностями текста, из второго урока, и узнали много интересного.
    Теперь же пришло время загрузить и показать на экране картинку. Думаю, вы уже давно ждали этого момента и сейчас потираете в предвкушении руки. ( может и меня за глаза называете садистом, что мол тянул-то? )
    Возможно, вы изучаете СДЛ для того, чтобы в дальнейшем сделать какую-нибудь игрушку. А игры без картинок, достаточно большая редкость. Потому что я не припомню игруху, в которой вообще бы не было картинок? Разве что, текстовые адвенчуры… больше на ум ничего не приходит.
    Но у меня были основания на протяжении прошлых уроков ничего не говорить о картинках в СДЛ, я счел необходимым, сначала ознакомить Вас с некими общими понятиями и структурами библиотеки. В отличии от других уроков, которые кидают сразу всем и помногу, я пошел иным путем, с Вашего молчаливого согласия. И сейчас, я надеюсь, что все уроки до этого и вместе с этим и будущие тоже, выстроят все ваше представление о данной библиотеке в единую цепочку знаний. Так это или нет, судить Вам, поэтому жду отзывов и комментариев с разумной и обоснованной критикой.
 
НАЧНЕМСсссс.
    А теперь к делу. Черт, вы наверное сейчас будете смеяться, но картинка это тоже сурфейс. Не, я не прикалываюсь, серьезно. Мы просто загружаем нужную картинку в память и преобразуем ее в удобный для СДЛ вид, то естьв поверхность. По сути своей, нам ведь не важно, как эта картинка хранилась в файле, нам намного важнее как мы сможем ей пользоваться, загрузив ее.
    Штатные средства библиотеки СДЛ умеют загружать только файлы в формате BMP. Это один из родных форматов картинок в windows. Если вы, к примеру, программируете для Linux, то это мягко говоря, может вас насторожить. Но не ворчите, нет никакого сионистского заговора, и не ищите. Давайте для начала я расскажу, как загрузить BMP-файл и отобразить его на экране. А уже потом расскажу, как поступать с файлами других форматов.
    Так вот загрузить файл с рисунком из BMP, очень просто. Для этого есть функция LoadBMP( filename ). Она принимает в себя название файла, а возвращает сюрфейс, уже достаточно хорошо нам известную:
 
SDL_Surface *myImage = SDL_LoadBMP("cb.bmp");
 
    Теперь myImage имеет все характеристики загруженной картинки, то есть в *pixels содержится массив пикселей рисунка, w & h - длина и ширина картинки в пикселях, ну и вся остальная информация, включая формат пикселей и палитру цветов, если необходимо. Прежде чем работать дальше, по правилам хорошего тона, следует проверить, а открылся ли вообще файл и имеем ли мы заполненную сурфейс:
 
if( !myImage ) // или лучше if(myImage == NULL)
  {
    printf("hey! where is the cb.bmp");
    return 1;
  }
 
    Я взял рисунок cb.bmp, это новый 3Д-логотип Code::Blocks, поищите, эта картинка должна быть где-то в папках самой ИДЕ. Если вы ее не нашли, то ничего страшного, можете взять любую другую картинку в BMP-формате. Естественно в коде следует указать уже ее имя, а не cb.bmp. Итак, картинка у нас загружена, теперь надо создать поверхность экрана, куда мы эту картинку и попытаемся вывести.
 
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE|SDL_DOUBLEBUF);
  if ( !screen )
    {
      printf("Unable to set 640x480 video: %s\n", SDL_GetError());
      return 1;
    }
 
    Возьмем 640х480, этого вполне хватит для наших нужд.
 
РИСУЕМ.
 
    Подготовительная работа завершена, теперь будем выводить картинку на экран. В этом нам поможет функция SDL_BlitSurface, (неправда ли нам знакома эта функция? Еще со времен работы над шрифтами.) Тогда мы затронули ее вскользь, теперь она нам снова поможет. Как говорится, старый друг, лучше новых двух. Можете посмотреть ее описание в предыдущей статье. А пока, напомню, она имеет 4 параметра:
1)копируемая поверхность (источник)
2)отображаемый прямоугольник поверхности источника
3)принимающая поверхность (назначение)
4)координаты прямоугольника отрисовки в принимающей поверхности
    В технических терминах, операция Blit есть не что иное, как копирование массива пикселей одной поверхности в массив второй поверхности. А если еще точнее, то копирование массива байтов, представляющих пиксели, из массива в массив. Таким образом, переходя на термин «байты», в нашем рассуждении, мы приходим к выводу, что удобнее, выгоднее, а главное, быстрее, копировались бы сюрфейсы с одинаковым форматом пикселей. И если, копирование сюрфейсов не разовое, а повторяемое время от времени, то неплохо было бы привести их к единому формату. Волшебная функция называется SDL_ConvertSurface, я думаю, вы уже догадались, что она делает :) Частой задачей, если не сказать основной, в любом графическом приложении, все же является копирование картинки на экран и для преобразования сюрфейса картинки, к сюрфейсу дисплея, есть специальная функция SDL_DisplayFormat. Воспользуемся ею, чуть позже, в нашем примере.
    А пока, переваривая полученные знания, мы можем написать функцию вывода картинки на экран.
 
 SDL_BlitSurface(myImage,NULL,screen,NULL);
 
    Впишем маленькую задержку, чтобы окно не закрылось, моргнув, а находилось на экране, хотя бы 3 секунды, чтобы узреть к чему привели наши старания:
 
SDL_Delay(3000);
 
   И запускаем….. Брюки превращаются, превращаются брюки …. Блин, в пустой экран… Ээээ что-то мы забыли. Ах да, дело в том, что СДЛ сама автоматически не отображает изменения в сюрфейсах, для этого ее надо немного «встряхнуть» или иными словами обновить сюрфейс, чтобы изменения в ней, были видимы. Для этого можно воспользоваться функцией SDL_UpdateRect. Первый параметр это обновляемая сюрфейс, а 4 остальных - это x,y,w,h координаты прямоугольника обновления. Если все нули, то обновляется вся сюрфейс. Итак вставляем после отрисовки и до задержки строку:
 
SDL_UpdateRect(screen,0,0,0,0);
 
    Запускаем…. Вот! Уже другой коленкор! Брюки превращаются, в элегантные шорты!
    Кстати, можете поиграться с параметрами, которые сейчас нули, получите в результате обновление не всей поверхности, а только ее части, в пределах прямоугольника x,y,w,h.
    Стоит так же заметить, что для обновления поверхностей экрана, можно воспользоваться функцией SDL_Flip() заместо SDL_UpdateRect, результат будет тем же. Отличия этих двух функций просты: если в функции создания поверхности окна(SDL_SetVideoMode) мы задали параметр SDL_DOUBLEBUF, это создаст дополнительный буфер экрана для рисования. Если железом такой буфер не поддерживается, то для обновления поверхности будет выполняться SDL_UpdateRect, если же поддерживается, то выполняется SDL_Flip, которая меняет буферы местами да еще и ждет вертикальной прорисовки(vertical retrace).
    Давайте нарисуем картинку теперь в центре нашего окна. Для того чтобы спозиционировать нашу картинку на экране, там где мы хотим ее видеть, надо немного изменить параметры функции SDL_BlitSurface()
    Первый параметр, сурфейс картинки оставляем, второй NULL – что значит, рисуется вся картинка, третий параметр место куда копируется, это экранная поверхность screen, а вот четвертый параметр, это как раз координаты, куда поместить картинку на экране.
    Заполним новый прямоугольник так, чтобы наша картинка рисовалась в центре. И отобразим ее:
 
SDL_Rect position = { (screen->w - myImage->w) / 2,(screen->h - myImage->h) / 2,myImage->w,myImage->h };
 
SDL_BlitSurface(myImage,NULL,screen,&position);
 
    Снова получилось! Уфф, выдыхаем… Это было достаточно просто, не так ли?
 
ДРУГИЕ ФОРМАТЫ ГРАФИЧЕСКИХ ФАЙЛОВ.
    Это все замечательно, но мне надо, к примеру, отобразить JPEG-файл. И вот, очевидно, мы снова уперлись в вопрос о том, как же все-таки загружать и отображать картинки других форматов? Я мог бы долго заливать, о том, что формат BMP –чудо из чудес и не пользоваться им, просто 11-смертный грех. Или петь соловьем, что другие форматы изображений все как один от лукавого. Но я этого делать не буду. Просто не зачем. Каждый в праве сам выбирать, что, как и почему что-то делать, тем более выбирать удобный для него формат. И потом, это не так уж это и сложно, загрузить файл другого формата. :)
    Но, чтобы это делать в коде, необходимо сначала скачать библиотеку SDL_Image. Я скачал SDL_image-devel-1.2.7-VC9.zip . Думаю, найдете, не маленькие. Распаковав ее, вы увидите всего 2 папки includes которую, как вы догадались мы перепишем в includes mingw и папку lib, которую, вы так же без моей помощи, перепишите в lib вашего компилятора. Про dll-файлы вообще молчу, потому как говорил об этом раньше. Теперь в Code::Blocks надо прилинковать файл SDL_image.lib к нашему текущему проекту. Все! Теперь мы можем грузить, картинки из достаточно распространенных форматов хранения графики, таких как PNG,TIFF,GIF тот же JPEG и других, посмотрите полный список в документации к SDL_Image.
    Теперь чтобы изобразить на экране картинку JPEG надо совсем немного изменить наш код. Во-первых это подключить SDL_Image, в самом начале программы:
 
#include <SDL_Image.h>
 
    И вместо функции SDL_LoadBMP() использовать функцию IMG_Load("bla-bla-bla.jpg”) В общем-то, это все изменения. Теперь и BMP-файлы можно, в принципе, грузить через IMG_Load, он такой – он умеет :)
    А сейчас, давайте, запускайте уже, все должно получиться! И да, пока не забыл, обязательно очищайте за собой все сарфейсы функцией SDL_FreeSurface. Ведь каждая неочищенная поверхность, как говорят, наносит непоправимый ущерб озоновому слою Земли, сам не проверял, но теперь всегда очищаю…. а вдруг не врут люди :)
    Вот весь текст программы:
 
#include <cstdlib>
#include <SDL.h>
#include <SDL_Image.h>
 
int main ( int argc, char** argv ) {
  // initialize SDL video
  if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
      printf( "Unable to init SDL: %s\n", SDL_GetError() ); return 1;
    }
 
  // create a new window
  SDL_Surface* screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE|SDL_DOUBLEBUF);
 
   if ( !screen )
   {
      printf("Unable to set 640x480 video: %s\n", SDL_GetError());
      return 1;
   }
 
   SDL_Surface *myImage;
 // myImage = SDL_DisplayFormat ( SDL_LoadBMP("cb.bmp")); // это для BMP
myImage = SDL_DisplayFormat( IMG_Load("bla-bla-bla.jpeg")); // это для других форматов
 
  // АХТУНГ, а где картинка, ара?
  if( !myImage )
  {
    printf("hey! where is the cb.bmp?");
    return 1;
  }
 
// для координат отрисовки (центруем)
   SDL_Rect position = { (screen->w - myImage->w) / 2,(screen->h - myImage->h) / 2,myImage->w,myImage->h };
 
// рисуем
SDL_BlitSurface(myImage,NULL,screen,&position);
 
//обновляем
SDL_Flip(screen);
 
// ждем
SDL_Delay(3000);
 
//очищаем
SDL_FreeSurface(myImage);
 
//с__ываем
return 0;
}
 
 ********************************
    Ой, минуточку, еще минуточку внимания. Вы думали, я забыл, а я не забыл))) Теперь пришло время вернуться к refcount , если вы читали предыдущую часть моего урока, то мы решили оставить ее на потом, по всему выходит, что это «потом» наступило сейчас.
    Refcount – это счетчик использования данной поверхности. Когда создается сюрфейс, он становится равным единице. Когда мы освобождаем поверхность через SDL_FreeSurface, то счетчик поверхности уменьшается на единицу. Если счетчик равен нулю, то поверхность стирается из памяти. Ну то есть происходит, то чего мы и хотели освобождая ее. Так для чего же это сделано? Счетчик-то зачем? На сайте приводится пример иллюстрирующий это. В первом случае мы берем одну поверхность и вторую. В первой у нас какая-то информация, предположим картинка. А вторая это просто кусок памяти нужного размера, приведенный принудительно к типу SDL_Surface и мы берем так и просто копируем все побайтно из первой поверхности во вторую. Потом по завершении просто освобождаем их. Казалось бы, в чем подвох–то? А подвох в том, что все данные, на которые указывает указатель, не копируются в новую структуру. Поэтому первый вызов для освобождения Srfc1 пройдет нормально, а второй вызовет ошибку. Код:
 
SDL_Surface *Srfc1, *Srfc2;
Srfc1 = IMG_Load("foo.png");
Srfc2 = (SDL_Surface*)malloc(sizeof(SDL_Surface)); memcpy(Srfc2, Srfc1, sizeof(SDL_Surface));
...
SDL_FreeSurface(Srfc1); /* Work */
SDL_FreeSurface(Srfc2); /* SEGFAULT */
 
    Как следует тогда поступить в этом случае? Верный путь состоит в том, чтобы просто увеличить счетчик поверхности на единицу, каждый раз при копировании поверхности
 
SDL_Surface *Srfc1, *Srfc2;
Srfc1= IMG_Load("foo.png");
Srfc2= Srfc1;
Srfc2->refcount++;
...
SDL_FreeSurface(Srfc1);
SDL_FreeSurface(Srfc2);
 
Вот теперь всё. Удачи Вам и до будущих встреч! Чао!
Категория: SDL | Добавил: dimanche13 (14.03.2009)
Просмотров: 5451 | Комментарии: 1 | Рейтинг: 0.0/0 |
Всего комментариев: 1
1 J3d1  
0
Как всегда, отличная статья. Очень подробно, доходчиво и с юмором=)

Имя *:
Email *:
Код *:
Copyright MyCorp © 2024