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);