Kirjautuminen

Haku

Tehtävät

Kilpailu

Murra koodi!
Lue ja osallistu!
Seuraava vihje 29.1.
Voittajia 1 + yrittäjiä 1

Koodit: C: SDL-pinta OpenGL-tekstuuriksi

Kirjoittaja: Metabolix

Kirjoitettu: 23.04.2007 – 23.04.2007

Tagit: grafiikka, 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);

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta