Учебная деятельность    

SDL - Simple DirectMedia Layer

http://www.libsdl.org/

SDL - бесплатный кроссплатформенный мультимедийный программный интерфейс. Как следует из названия, функции SDL оперируют структурами, максимально приближенными к аппаратному обеспечению мультимедиа-средств, что позволяет существенно повысить производительность приложений, использующих мультимедиа (игры, медиа-плееры и т. п.)

Интерфес SDL делится на несколько подсистем: видео, события (клавиатура и мышь), звук, CD-ROM аудио, таймеры, нити (threads). Видео-подсистема SDL предоставляет возможность использования видеорежима с любой глубиной цвета (от 8 бит и выше), при необходимости используя преобразование, если режим не поддерживается аппаратно; обеспечивает прямой доступ в линейный графический видеобуфер; позволяет работать с атрибутами прозрачности или альфа-сопряжения (alpha blending); использует аппаратное ускорение для операций копирования, заполнение и преобразования (например, оптимизация под MMX на x86).
  При компиляции программ на Си, использующих SDL, компилятору необходимо передавать дополнительную опцию -lSDL:
gcc sdlex.c -lSDL

Перед использованием функций SDL следует инициализировать библиотеку при помощи функции SDL_Init, ее параметр - битовое поле, отражающее, какие подсистемы SDL приложение собирается задействовать. Для вычисления этого параметра удобно использовать константы SDL_INIT_AUDIO, SDL_INIT_VIDEO, SDL_INIT_CDROM, SDL_INIT_TIMER, которые, при необходимости, объединяются побитовым "ИЛИ". Для выгрузки библиотеки и освобождения ресурсов по окончанию работы используется процедура SDL_Quit. Если в процессе работы с библиотекой возникает ошибка, то ее текстовое описание можно получить при помощи SDL_GetError.
uses SDL;

function SDL_Init(flags:Uint32):integer;
procedure SDL_Quit;
function SDL_GetError:PChar;
#include <SDL/SDL.h>

int SDL_Init(Uint32 flags);
void SDL_Quit(void);
char *SDL_GetError(void);
Во FreePascal процедуры и функциии каждой подсистемы объявлены в отдельных модулях, поэтому, кроме модуля SDL, необходимо подключать еще и модули используемых подсистем (например, SDL_Video, SDL_Audio,...) В Си в зависимости от версии и платформы, возможно, потребуется подключить заголовочные файлы подсистем SDL ("SDL_video.h", "SDL_audio.h",...), если это не делается в основном файле "SDL.h".

Видео-подсистема SDL оперирует структурами SDL_Surface ("поверхность"). Поверхность представляет область "графической" памяти, кодирующей изображение. Эта структура содержит следующие поля:

Ссылка на поверхность, связанную с линейным графическим видеобуфером (то, что отображается на дисплее), возвращается как результат функций SDL_SetVideoMode или SDL_GetVideoSurface (дисплейная поверхность). Прежде, чем использовать эту поверхность, надо задать для нее видеорежим (SDL_SetVideoMode). При этом в X11 будет создано окно соответствующего размера (если только не задан полноэкранный режим). Второй параметр этой функции (bpp) определяет глубину цвета - bits-per-pixel. Если запрашиваемая глубина цвета отличается от той, что в данный момент используется системой вывода графики, SDL создаст промежуточный теневой буфер и будет преобразовывать цветовую информацию пикселей из формата поверхности в формат графической системы. Если указан флаг SDL_ANYFORMAT, параметр bpp игнорируется и для поверхности будет использована текущая глубина цвета системы вывода графики. Можно также указать флаги SDL_RESIZABLE (окно изменяемого размера), SDL_NOFRAME (окно без рамки и заголовка), а также перечисленные выше флаги, объединяя их побитовым "ИЛИ". Оптимальные значения разрешения и глубины цвета могут быть получены при помощи функций SDL_GetVideoInfo и SDL_ListModes.
function SDL_SetVideoMode(width,height:integer; bpp:integer; flags:Uint32):PSDL_Surface;
function SDL_GetVideoSurface:PSDL_Surface;
SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
SDL_Surface *SDL_GetVideoSurface(void);

Кроме дисплейной поверхности (связанной с видеобуфером) программа может оперировать неотображаемыми поверхностями. Создать пустую неотображаемую поверхность можно при помощи функций SDL_CreateRGBSurface и (непустую) SDL_CreateRGBSurfaceFrom. Можно также создать поверхность и заполнить ее изображение из графического файла в формате BMP. Кроме того, SDL поддерживает сохрание изображения поверхности в BMP-файле. Для копирования изображения между поверхностями можно использовать SDL_BlitSurface. Параметры SDL_Rect задают положение и размер копируемых областей. В случае дисплейной поверхности, чтобы изменения вступили в силу, надо обновить отображаемую область при помощи SDL_UpdateRect или SDL_UpdateRects. Если вы копируете изображение на дисплейную поверхность, для ускорения копирования можно предварительно конвертировать исходную поверхность в формат дисплейной поверхности - SDL_DisplayFormat. Для конвертирования поверхности в произвольный формат предназначена функция SDL_ConvertSurface. При завершении работы необходимо освободить память от каждой из неотображаемых поверхностей при помощи процедуры SDL_FreeSurface.
function SDL_LoadBMP(file:PChar):PSDL_Surface;
function SDL_SaveBMP(surface:PSDL_Surface; file:PChar):integer;
function SDL_BlitSurface(src:PSDL_Surface; srcrect:PSDL_Rect; dst:PSDL_Surface; dstrect:PSDL_Rect):integer;
procedure SDL_UpdateRect(screen:PSDL_Surface; x,y:Sint32; w,h:Sint32);
procedure SDL_UpdateRects(screen:PSDL_Surface; numrects:integer; var rects:SDL_RectArray);
function SDL_DisplayFormat(surface:PSDL_Surface):PSDL_Surface;
procedure SDL_FreeSurface(surface:PSDL_Surface);
SDL_Surface *SDL_LoadBMP(const char *file);
int SDL_SaveBMP(SDL_Surface *surface, const char *file);
int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h);
void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface);
void SDL_FreeSurface(SDL_Surface *surface);

Приведенная ниже программа загружает из файла картинку в BMP-формате и отображает ее в разных местах дисплейной поверхности.
program gr1;
  uses SDL, SDL_Video;
  var
    screen,img:PSDL_Surface;
    r:SDL_Rect;
    i:integer;

begin
  if SDL_Init(SDL_INIT_VIDEO)<>0 then begin
    writeln('SDL_Init failed: ',SDL_GetError);
    halt(1);
  end;
  screen:=SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
  if screen=nil then begin
    writeln('SDL mode failed: ',SDL_GetError);
    halt(1);
  end;
  img:=SDL_LoadBMP('img_ex.bmp');
  if img=nil then begin
    writeln('Picture is not available!');
    halt(2);
  end;
  r.w:=img^.w; r.h:=img^.h;
  SDL_SetColorKey(img,SDL_SRCCOLORKEY,
    SDL_MapRGB(img^.format,179,217,255));
  for i:=0 to 20 do begin
    r.x:=i*20; r.y:=i*20;
    SDL_BlitSurface(img,nil,screen,@r);
  end;
  SDL_UpdateRect(screen,0,0,640,480);
  readln;
  SDL_FreeSurface(img);
  SDL_Quit;
end.
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_video.h>
SDL_Surface *screen,*img;
SDL_Rect r;
int i;

int main() {
  if (SDL_Init(SDL_INIT_VIDEO)) {
    fprintf(stderr,"SDL_Init failed: %s\n",SDL_GetError());
    return 1;
  }
  screen=SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
  if (!screen) {
    fprintf(stderr,"SDL mode failed: %s\n",SDL_GetError());
    return 1;
  }
  img=SDL_LoadBMP("img_ex.bmp");
  if (!img) {
    fprintf(stderr,"Picture is not available!\n");
    return 2;
  }
  r.w=img->w; r.h=img->h;
  SDL_SetColorKey(img,SDL_SRCCOLORKEY,
    SDL_MapRGB(img->format,179,217,255));
  for (i=0; i<400; i+=20) {
    r.x=r.y=i;
    SDL_BlitSurface(img,NULL,screen,&r);
  }
  SDL_UpdateRect(screen,0,0,640,480);
  getchar();
  SDL_FreeSurface(img);
  SDL_Quit();
}

В данной программе используется функция SDL_SetColorKey, которая позволяет задать ключевой цвет (цвет, соответствующий прозрачному). Ключевой цвет (параметр key) задается в формате кодирования цвета для данной поверхности. Для преобразования цвета из RGB-компонент в заданный формат кодирования удобно использовать функцию SDL_MapRGB. Формат кодирования обычно задается полем format структуры SDL_Surface. Данная функция возвращает цвет пиксела в формате поверхности. Следует иметь в виду, что при записи в видеобуфер надо использовать лишь SDL_PixelFormat->BitsPerPixel бит данного значения, игнорируя старшие биты, в противном случае может быть испорчена информация о цвете для последующих пикселов.
function SDL_SetColorKey(surface:PSDL_Surface; flag:Uint32; key:Uint32):integer;
function SDL_MapRGB(fmt:PSDL_PixelFormat; r,g,b:Uint8):Uint32;
int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);
Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b);

Для построения произвольных изображений необходимо непосредственно обращаться к видеопамяти поверхности (по адресу pixels структуры SDL_Surface). Перед изменением буфера необходимо заблокировать поверхность при помощи SDL_LockSurface, а когда изменения закончены - разблокировать (SDL_UnlockSurface). Между операциями блокирования / разблокирования не следует выполнять никаких системных или библиотечных вызовов.
function SDL_LockSurface(surface:PSDL_Surface):integer;
procedure SDL_UnlockSurface(surface:PSDL_Surface);
int SDL_LockSurface(SDL_Surface *surface);
void SDL_UnlockSurface(SDL_Surface *surface);

Приведенная ниже программа строит три вложенных окружности разного цвета. В примере приводится процедура putpixel, пригодная для режимов с глубиной цвета 32 бита.
program gr1;
  uses SDL,SDL_Types,SDL_Video;
  type parr=array [0..1] of byte;
  var screen:PSDL_Surface;
    x,y:real;
  const t:real=0;

procedure putpixel(s:PSDL_Surface; x,y:integer; r,g,b:Uint8);
  var ptr:^Uint32;
begin
  ptr:=addr(parr(s^.pixels^)
    [s^.pitch*y+s^.format^.BytesPerPixel*x]);
  ptr^:=SDL_MapRGB(s^.format,r,g,b);
end;

begin
  if SDL_Init(SDL_INIT_VIDEO)<>0 then begin
    writeln('SDL_Init failed: ',SDL_GetError);
    halt(1);
  end;
  screen:=SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
  if screen=nil then begin
    writeln('SDL mode failed: ',SDL_GetError);
    halt(1);
  end;
  SDL_WM_SetCaption('circles',nil);
  SDL_LockSurface(screen);
  while t<2*3.1416 do begin
    x:=20*cos(t); y:=20*sin(t);
    putpixel(screen,round(x+320),round(y+200),255,0,0);
    putpixel(screen,round(2*x+320),round(2*y+200),0,255,0);
    putpixel(screen,round(3*x+320),round(3*y+200),0,0,255);
    t:=t+0.0001;
  end;
  SDL_UnlockSurface(screen);
  SDL_UpdateRect(screen,0,0,640,480);
  readln;
  SDL_Quit;
end.
#include <stdio.h>
#include <math.h>
#include <SDL/SDL.h>
#include <SDL/SDL_video.h>
SDL_Surface *screen;
float t=0,x,y;

void putpixel(SDL_Surface *s, 
  int x, int y, Uint8 r, Uint8 g, Uint8 b)
{
  void *ptr=(char*)(s->pixels)+
    s->pitch*y+s->format->BytesPerPixel*x;
  *(Uint32*)ptr=SDL_MapRGB(s->format,r,g,b);
}

int main() {
  if (SDL_Init(SDL_INIT_VIDEO)) {
    fprintf(stderr,"SDL_Init failed: %s\n",SDL_GetError());
    return 1;
  }
  screen=SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
  if (!screen) {
    fprintf(stderr,"SDL mode failed: %s\n",SDL_GetError());
    return 1;
  }
  SDL_WM_SetCaption("circles",NULL);
  SDL_LockSurface(screen);
  while (t<2*3.1416) {
    x=20*cos(t); y=20*sin(t);
    putpixel(screen,x+320,y+200,255,0,0);
    putpixel(screen,2*x+320,2*y+200,0,255,0);
    putpixel(screen,3*x+320,3*y+200,0,0,255);
    t+=0.0001;
  }
  SDL_UnlockSurface(screen);
  SDL_UpdateRect(screen,0,0,640,480);
  getchar();
  SDL_Quit();
}

SDL предоставляет небольшой набор функций управления оконным интерфейсом: SDL_WM_SetCaption - изменить заголовок окна, SDL_WM_GetCaption - получить заголовок окна, SDL_WM_ToggleFullScreen - переключиться из оконного режима в полноэкранный и обратно и некоторые другие.
procedure SDL_WM_SetCaption(title,icon:PChar);
procedure SDL_WM_GetCaption(var title,icon:PChar);
function SDL_WM_ToggleFullScreen(surface:PSDL_Surface):integer;
void SDL_WM_SetCaption(const char *title, const char *icon);
void SDL_WM_GetCaption(char **title, char **icon);
int SDL_WM_ToggleFullScreen(SDL_Surface *surface);