Kirjoittaja: Metabolix
Kirjoitettu: 23.04.2007 – 23.04.2007
Tagit: grafiikka, koodi näytille, vinkki
Tämä koodivinkki näyttää, kuinka SDL-pinnan saa muutettua OpenGL-tekstuuriksi. Pääpiirteittäin ajatus on, että luodaan RGBA-muotoinen SDL-pinta, kopioidaan vanha kuva SDL-funktioilla sille ja ladataan sitten tämä uusi pinta, jonka formaatti varmasti tunnetaan. Näin saadaan mikä tahansa SDL:n tukema pinta muutettua 32-bittisen RGBA-formaatin kautta OpenGL:lle sopivaksi. Lisää mutkia matkaan aiheuttaa OpenGL:n ja SDL:n erilainen koordinaatisto (OpenGL:ssä y-akseli osoittaa ylös) ja SDL:n erilaiset läpinäkyvyysominaisuudet, jotka tämä funktio pyrkii jossain määrin huomioimaan.
Koodi oli alunperin hyvin viimeistelemätön hätäratkaisu foorumilla esitettyihin kysymyksiin, mutta nyt sitä on huomattavasti parannettu. Se on myös osa opasta SDL:n ja OpenGL:n yhteiskäytöstä, jonka yhteydessä kerrotaan myös hieman lisää asian taustoista.
/* Tarvitaan SDL:n ja OpenGL:n otsikot.
* Jos ei toimi, kokeile nimiä SDL/SDL.h ja SDL/SDL_opengl.h */
#include <SDL.h>
#include <SDL_opengl.h>
/* Funktio itse. */
int MySDL_glTexImage2D(SDL_Surface *kuva)
{
SDL_Surface *apu;
/* Helpottaa, jos tavut ovat järjestyksessä RGBA.
* Säädetään siis konetyypin mukaan värien bittimaskit
* niin, että tavujen järjestys muistissa osuu oikein. */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const Uint32 rshift = 24, gshift = 16, bshift = 8, ashift = 0;
#else
const Uint32 rshift = 0, gshift = 8, bshift = 16, ashift = 24;
#endif
const Uint32
rmask = 0xff << rshift,
gmask = 0xff << gshift,
bmask = 0xff << bshift,
amask = 0xff << ashift;
Uint32 *ptr;
Uint32 kuva_flags;
Uint32 kuva_colorkey;
Uint8 kuva_alpha;
SDL_Rect r1, r2;
/* Tarkistetaan kuva. */
if (!kuva || !kuva->w || !kuva->h) {
return -1;
}
/* OpenGL:ää varten sivun pitää olla kahden potenssi. */
if (kuva->w > 1 && (kuva->w & (kuva->w - 1))) {
return -1;
}
if (kuva->h > 1 && (kuva->h & (kuva->h - 1))) {
return -1;
}
/* Otetaan talteen arvot, jotka muuttuvat funktion aikana */
kuva_flags = kuva->flags;
kuva_alpha = kuva->format->alpha;
kuva_colorkey = kuva->format->colorkey;
/* Luodaan apupinta halutussa formaatissa (RGBA). */
apu = SDL_CreateRGBSurface(SDL_SWSURFACE, kuva->w, kuva->h, 32, rmask, gmask, bmask, amask);
if (!apu) {
return -1;
}
SDL_FillRect(apu, 0, 0);
/* Poistetaan erityiset läpinäkyvyysasetukset. */
SDL_SetAlpha(kuva, 0, 0);
if ((kuva_flags & SDL_SRCALPHA) != 0 && kuva->format->Amask) {
SDL_SetColorKey(kuva, 0, 0);
}
/* OpenGL:n ja SDL:n y-akselit osoittavat eri suuntiin.
* Kopioidaan siis kuva pikselirivi kerrallaan ylösalaisin. */
r1.x = r2.x = 0;
r1.h = r2.h = 1;
r1.w = r2.w = kuva->w;
for (r1.y = 0, r2.y = kuva->h - 1; r2.y >= 0; ++r1.y, --r2.y) {
SDL_BlitSurface(kuva, &r1, apu, &r2);
}
/* Koko pinnan alfa-arvo pitää palauttaa erikseen, jos sellainen on. */
if ((kuva_flags & SDL_SRCALPHA) && !kuva->format->Amask && kuva_alpha != 0xff) {
for (r1.y = 0; r1.y < apu->h; ++r1.y) {
ptr = (Uint32*)((Uint8*) apu->pixels + r1.y * apu->pitch);
for (r1.x = 0; r1.x < apu->w; ++r1.x) {
if ((ptr[r1.x] & amask) != 0) {
ptr[r1.x] &= (kuva_alpha << ashift) | ~amask;
}
}
}
}
/* Lähetetään kuva OpenGL:lle, tuhotaan apupinta ja palautetaan asetukset. */
glTexImage2D(GL_TEXTURE_2D, 0, 4, apu->w, apu->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, apu->pixels);
SDL_FreeSurface(apu);
SDL_SetAlpha(kuva, kuva_flags, kuva_alpha);
SDL_SetColorKey(kuva, kuva_flags, kuva_colorkey);
return 0;
}Esimerkki käytöstä
GLuint tex;
SDL_Surface *kuva;
/* Ensin varataan ja valitaan GL-tekstuuri normaalisti. */
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
/* Avataan jokin kuva SDL-pinnalle. */
kuva = SDL_LoadBMP("kuva.bmp");
/* Ladataan kuvapinta tekstuuriksi. */
if (MySDL_glTexImage2D(kuva) != 0) {
fprintf(stderr, "Virhe.\n");
abort();
}
/* Vapautetaan pinta, sitä ei enää tarvita. */
SDL_FreeSurface(kuva);
/* Tässä välissä piirretään OpenGL:llä... */
/* ... ja lopuksi tuhotaan OpenGL-tekstuuri normaalisti. */
glDeleteTextures(1, &tex);