SDL Image

Určitě se vám nelíbí mít všechny textury uložené v BMP souborech, které nejsou zrovna přátelské k místu na disku. Bohužel SDL žádný jiný formát přímo nepodporuje. Nicméně existuje malé rozšíření v podobě knihovničky SDL Image poskytující funkci IMG_Load(), která umí načíst většinu používaných grafických formátů.

Pokud tuto knihovnu nemáte, můžete si ji stáhnout ze známé adresy http://www.libsdl.org/. Začneme vložením hlavičkových souborů. Nezapomeňte kromě OpenGL a SDL přilinkovat i SDL_image.

#include <SDL.h>// Hlavní SDL knihovna

#include <SDL_opengl.h>// Vloží za nás OpenGL

#include <SDL_image.h>// Abychom mohli používat funkci IMG_Load()

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

Daklarujeme proměnnou OpenGL textury.

GLuint gl_texture;// Textura

Napíšeme funkci, která načte SDL\_Surface. Obrázek musíme současně upravit, aby byl ve složkách RGB a nebyl vzhůru nohama.

SDL_Surface* LoadBitmap(const char *filename)// Funkce pro načteni bitmapy

{

Uint8 *rowhi, *rowlo;// Ukazatele na prohazováni řádků

Uint8 *tmpbuf, tmpch;// Dočasná paměť

int i, j;// Řídící proměnné pro cykly

SDL_Surface *image;// Načítaný obrázek

image = IMG_Load(filename);// Načtení dat obrázku

if (image == NULL)// Ošetření chyby při načítání

{

fprintf(stderr, "Nepodarilo se nacist %s: %s\n", filename, SDL_GetError());

return(NULL);

}

GL surfaces jsou vzhůru nohama, takže budeme muset bitmapu převrátit. Alokujeme dynamickou paměť, do které odložíme právě přemisťovaný kousek. Její velikost se neurčí podle image->w, ale podle image->pitch, protože u SDL\_Surface se může stát, že kvůli zarovnávání v paměti SDL zabere více místa, než je skutečný rozměr obrázku.

Image->pitch udává šířku zabrané paměti, ale image->w udává šířku obrázku.

tmpbuf = (Uint8 *)malloc(image->pitch);// Alokace paměti

if (tmpbuf == NULL)// Ošetření chyby

{

fprintf(stderr, "Nedostatek pameti\n");

return NULL;

}

// Nastavení prvního a posledního řádku

rowhi = (Uint8 *)image->pixels;

rowlo = rowhi + (image->h * image->pitch) - image->pitch;

for (i = 0; i < image->h/2; i++)

{

// Převrácení BGR na RGB

if (image->format->Bshift == 0)

{

for (j = 0; j < image->w; j++)

{

tmpch = rowhi[j*3];

rowhi[j*3] = rowhi[j*3+2];

rowhi[j*3+2] = tmpch;

tmpch = rowlo[j*3];

rowlo[j*3] = rowlo[j*3+2];

rowlo[j*3+2] = tmpch;

}

}

// Prohození řádků

memcpy(tmpbuf, rowhi, image->pitch);

memcpy(rowhi, rowlo, image->pitch);

memcpy(rowlo, tmpbuf, image->pitch);

// Posun ukazatelů na řádky

rowhi += image->pitch;

rowlo -= image->pitch;

}

Zbývá už jen smazat dočasný odkládací prostor a vrátit načtenou bitmapu.

free(tmpbuf);// Úklid

return image;// Vrátí načtený obrázek

}

Teď, když máme načtený obrázek, vytvoříme funkci, která z něj vytvoří OpenGL texturu. Hned na začátku se pokusíme načíst obrázek právě napsanou funkcí LoadBitmap(). Pokud tato oprace selže, vrátíme nulu.

GLuint CreateTexture(const char* file, int min_filter, int mag_filter, bool mipmaps)// Vytvoří texturu

{

SDL_Surface *surface;// Obrázek

surface = LoadBitmap(file);// Načtení obrázku

if (surface == NULL)// Ošetření chyby

return 0;

Vytvoříme místo pro novou texturu a nastavíme filtry podle parametrů funkce.

GLuint texture;// OpenGL textura

glGenTextures(1, &texture);// Generování jedné textury

glBindTexture(GL_TEXTURE_2D, texture);// Nastavení textury

// Nastavení požadovaných filtrů

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);

Vybereme podle přání, jestli se mají používat mipmapy nebo ne.

if (mipmaps)// Mipmapovaná textura

{

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, surface->w, surface->h, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);

}

else// Obyčejná textura

{

glTexImage2D(GL_TEXTURE_2D, 0, 3, surface->w, surface->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);

}

Nakonec vše uklidíme. Pro mazání SDL\_Surface používejte zásadně SDL\_FreeSurface() a nikdy delete nebo free. Data bitmapy můžeme také smazat, protože nejsou potřeba. OpenGL si je nakopírovalo a my už se o ně nemusíme starat.

SDL_FreeSurface(surface);// Smazání SDL_Surface

surface = NULL;// Nastavení ukazatele na NULL

return texture;// Vrátí texturu

}

Funkce, která vykresluje OpenGL scénu. Obdoba DrawGLScene() z NeHe OpenGL Tutoriálů.

void RenderScene()// Vykreslí scénu

{

static float rott = 0.0f;// Statická proměnná pro úhel rotace

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže obrazovku a hloubkový buffer

glLoadIdentity();// Reset matice

glBindTexture(GL_TEXTURE_2D, gl_texture);// Zvolí texturu

glTranslatef(0.0f, 0.0f, -6.0f);// Přesun do obrazovky

glRotatef(rott,-1.0, 0.0, 0.0);// Natočení scény

glRotatef(rott, 0.0, 1.0, 0.0);

glRotatef(rott, 0.0, 0.0,-1.0);

glBegin(GL_POLYGON);// Vykreslí obdélník

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 0.0f,-1.0);

glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 0.0f, 1.0);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 0.0f, 1.0);

glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 0.0f,-1.0);

glEnd();

rott += 0.1;// Zvětší rotaci

SDL_GL_SwapBuffers();// Prohození bufferů

}

Incializace SDL.

void InitSDL()// Inicializace SDL

{

// Inicializace SDL grafiky a SDL Timeru

if (SDL_Init(SDL_INIT_VIDEO) < 0)

{

fprintf(stderr, "Nepodarilo se inicializovat SDL: %s\n", SDL_GetError());

exit(1);

}

atexit(SDL_Quit);// Nastavení funkce při volání exit();

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);// Chceme doublebuffering s OpenGL

// Nastavení velikosti a stylu okna

unsigned int flags = SDL_OPENGL;// | SDL_FULLSCREEN; // Případně bitově orovat s fullscreen

if (SDL_SetVideoMode(640, 480, 0, flags) == NULL)// Vytvoří okno

{

fprintf(stderr, "Nepodarilo se vytvorit OpenGL okno : %s\n", SDL_GetError());

SDL_Quit();

exit(2);

}

SDL_WM_SetCaption("Lesson - SDL_image", NULL);// Nastavení titulku okna

}

Incializace OpenGL.

void InitOpenGL()// Nastavení základních parametrů OpenGL

{

glViewport(0, 0, 640, 480);

glEnable(GL_TEXTURE_2D);// Povolení textur

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// Černé pozadí

glClearDepth(1.0);// Povolení mazání hloubkového bufferu

glDepthFunc(GL_LESS);

glEnable(GL_DEPTH_TEST);// Povolení testování hloubky

glShadeModel(GL_SMOOTH);

glEnable(GL_LIGHT0);// Povolení světel

glEnable(GL_COLOR_MATERIAL);// Povolení vybarvování materiálů

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(45.0f, (GLfloat)640 / (GLfloat)480, 0.1f, 1000.0f);

glMatrixMode(GL_MODELVIEW);

// Načtení textury

if ((gl_texture = CreateTexture("test.jpg", GL_LINEAR, GL_LINEAR, false)) == 0)

{

fprintf(stderr, "Nepodarilo se vytvorit texturu\n");

exit(1);

}

}

Při zavírání programu voláme ShutDownApp(), která smaže texturu a ukončí SDL.

void ShutDownApp()// Deinicializace

{

glDeleteTextures(1, &gl_texture);// Vymazání textury

SDL_Quit();// Ukončení SDL

}

Napíšeme funkci, zpracovávající zprávy, které programu posílá systém. Reagujeme na ukončení aplikace (SDL_QUIT), stisk klávesy (SDL_KEYDOWN) Esc (SDLK_ESCAPE) a překreslení okna SDL_VIDEOEXPOSE. Ostatní události nás nezajímají.

bool ProcessEvents()// Obsluha událostí

{

SDL_Event event;// Proměnná zprávy

while (SDL_PollEvent(&event))// Dokud přicházejí zprávy, zpracovávat je

{

switch (event.type)// Jaká přišla zpráva?

{

case SDL_QUIT:// Uživatel si přeje ukončit aplikaci

return false;

break;

case SDL_KEYDOWN:// Stisknutá klávesa

switch (event.key.keysym.sym)// Jaká klávesa?

{

case SDLK_ESCAPE:// Esc

return false;

break;

}

break;

case SDL_VIDEOEXPOSE:// Potřeba překreslit okno

RenderScene();// Vykreslí scénu

break;

}

}

return true;

}

Main() je první funkcí, která se volá po spuštění programu. Po jejím ukončení se ukončí i program. Na začátku inicializujeme SDL a OpenGL. V hlavní smyčce programu zpracováváme zprávy a pokud žádné nepřijdou, překreslíme scénu. Po příchodu zprázy o ukončení nebo pokud uživatel stiskl klávesu Esc, uvolníme zdroje, které program zabral a ukončíme aplikaci.

int main(int argc, char ** argv)// Hlavní funkce

{

InitSDL(); // Inicializace SDL

InitOpenGL();// Inicializace OpenGL

while (ProcessEvents())// Hlavní smyčka programu

{

RenderScene();// Pokud nepřišla zpráva překreslí scénu

}

ShutDownApp();// Deinicializace

return 0;// Konec main() a programu

}

Tak to je asi všechno. V archivu zdrojového kódu najdete pouze CPP soubor s JPG obrázkem, takže musíte program ještě přeložit. Pro kompilaci v Linuxu napište:

gcc `sdl-config --libs --cflags` -lSDL_image -L/usr/X11R6/lib -lGL -lGLU lessonSDL_image.cpp

Pracujete-li v jiném operačním systému, nezapomeňte kromě OpenGL a SDL přilinkovat i knihovnu SDL_image.

napsal: Bernard Lidický - Berny <2berny (zavináč) seznam.cz>

Zdrojové kódy