Voir la version complète : Pixel transparent
ffomnislas
24/07/2005, 21h31
Je revient pour vous demander de l'aide :)
J'ai besoin de rendre une couleur transparente.
Vous allez me dire utilise sdl_setcolorkey(), ben non ce serait trop facile.
J'ai besoin que cette transparence soit directement inscrite dans la structure de chaque pixels car je n'utilise pas une fonction sdl pour l'afficher ensuite mais une fonction opengl qui ne prend pas en compte le champ "colorkey".
J'arrive bien entendue à savoir quel pixel est de la couleur que je veut rendre transparente.
Mais je n'arrive pas a rendre ce pixel transprent.
En regardant l'écriture de get_pixel() et de putpixel() j'ai essayé diverses modifications mais aucune n'a réussi :/
Par exemple voici comment je recupere le pixel:
int bpp = surface->format->BytesPerPixel;
Uint16 *p = (Uint16 *)surface->pixels + y * surface->pitch + x * bpp;
Mais je ne sais pas comment modififier la couche alpha:/
C'est une manière un peu tordu, mais voilà :
Tu fait une boucle pour scanner tous les pixels de la surface. Quand la valeur celle que tu veux rendre transparente, tu récupère les valeurs RGB avec la fonction SDL_GetRGB(). Ensuite, avec la fonction SDL_MapRGBA, tu mets au valeur RGB les valeurs que tu as récupérés et tu ajoutes 0 à l'apha pour que se soit transparent.
J'ai pas regarder au niveau de OpenGL pour le moment, mais je vais faire un tour.
Voila un exemple de code :
int bpp = surface->format->BytesPerPixel;
Uint16 *p = (Uint16 *)surface->pixels + y * surface->pitch + x * bpp;
//Pour récuperer les valeurs rgb
Uint8 *R_Value;
Uint8 *G_Value;
Uint8 *B_Value;
SDL_GetRGB(p, TaSurface->format, R_Value, G_Value, B_Value);
//On met à jour le pixel
p = SDL_MapRGBA(TaSurface->format, &R_Value, &G_Value, &B_Value, SDL_ALPHA_TRANSPARENT)
/* Putpixel(...) */
ffomnislas
25/07/2005, 00h55
merci, c'est vraiment sympa.
J'avais essaye plein de truc mais j'avais pas tilté sur la fonction SDL_MapRGBA().
En ce qui concerne opengl, c'est jsute que lorsque tu demande a opengl de convertir un sdl_surface en texture, il ne prend pas en compte le champ "colorkey". (qui contient la couleur qui devra etre transparente lors des blitagges sdl). C'est pourquoi j'avais besoin de mettre la valeur ed alpha directement ddans le pixel.
Je vais tester ton code de suite et te tiens au courant.
----edit
Ok ton code fonctionne moyennenement quelques modification:
void noir_transparent(SDL_Surface* surface)
{
for(int i=0;i<surface->w;i++)
{
for(int j=0;j<surface->h;j++)
{
if(getpixel(surface,i,j)==SDL_MapRGB(surface->format, 0x00, 0x00, 0x00))
{
pixel_transparent(surface,i,j);
}
}
}
}
void pixel_transparent(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
Uint32 *p = (Uint32 *)surface->pixels + y * surface->pitch + x * bpp;
//Pour récuperer les valeurs rgb
Uint8 *R_Value=new Uint8;
Uint8 *G_Value=new Uint8;
Uint8 *B_Value=new Uint8;
printf("%d %d %p\n",x,y,p);
SDL_GetRGB(*p, surface->format, R_Value, G_Value, B_Value);
printf("fin\n");
//On met à jour le pixel
*p = SDL_MapRGBA(surface->format, *R_Value, *G_Value, *B_Value, SDL_ALPHA_TRANSPARENT);
//putpixel(surface,x,y,p);
}
Le putpixel je ne suis pas sur qu'il soit necessaire vu que je modifie directement ce que p pointe.
Pour le moment le probleme n'est pas la.
J'ai une segmentation fault qui arrive a l'appelle de SDL_GetRGB() avec des coordonnees de {0,130}.
Voici ce que je fait:
J'ai une image de 256*256 comportant des parties transparentes. Je la charge puis la blitte sur une autre surface alloue avec SDL_AllocSurface(). A ce moment les parties transparentes del'image deviennent noir. C'est pourquoi j'ai besoin de rendre le noir transparent.
J'ai testé:
- p n'est pas null lors de l'erreur
- la coordonné qui provoque l'erreur existe forcement vu la boucle de la fonction noir_transparent()
Je suis fatigué, je continuerais a regarder demain.
Deux remarques:
A/ tu ne verrouilles pas ta surface... bon tant qu'elle n'est pas en VRAM ça ne plantera pas, mais ce n'est pas très prudent.
B/ as-tu vérifié que sdl a créé une structure gérant les alpha dans ta surface? si le format est de type RGB, il faut réaliser une conversion surface RGB vers surface RGBA avant ta fonction (et vu la lourdeur de l'opération t'arranger pour que surtout, surtout elle ne soit fait qu'une seule fois ^^)
En plus convertsurface optimise cf les propos de wedge dans le forum.
C/ j'ai une solution un peu différente à te proposer et totalement non-testée :)
Il faudrait changer le masque alpha de ta surface pour lui donner d'autres valeurs... à tester, le comportement avec 0x00FFFFFF. Avec un peu de chance, tu auras du noir transparent sans rien faire :p
ffomnislas
25/07/2005, 13h01
j'ai essayé de modifier le masque de transparence.
surface = SDL_AllocSurface(SDL_SWSURFACE | SDL_SRCALPHA,tmp->w ,tmp->h, 32, 0xFF0000,0xFF00, 0xFF, 0xFF000000);
//aussi teste 0x000000FF
Cela n'a rien donné
J'ai rajoué le lock et le unlock, c'est effectivement mieux meme si ca ne resout pas le probleme.
Sinon je pense que ma surface est contient bien la partie alpha puisque je n'ai pas d'erreur avant la coordonne {0,130}. Bien sur les coordonnees precendentes celle ci sont bien noir donc leur pixel subit ces modifications.
edit --
J'ai du nouveau, le plantage se produit pour toute les coordonnées du type: {x,130}
C'est byzarre ...
Si tu es sous windows, cela peut segfaulter assez loin dans le code après le début des erreurs. Je te conseille de sortir les infos sur les formats de ta surface, juste pour être sûr... je serais les concepteurs de SDL, je ne créerais pas de surface RGBA alors qu'on est sûr à 200% qu'avec un BMP de toute façon il n'y aura pas de couche alpha!
ffomnislas
25/07/2005, 14h01
Je suis sous linux.
C'est une image au format png que j'ouvre ;).
C'est juste que ja la blitte dans une autre surface et c'est donc la que la transparence du png disparait.
Pour les informations de la surface je fait:
if (surface->format->BytesPerPixel == 3) { // RGB 24bit
printf("RGB\n");
}
else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
printf("RGBA\n");
}
Et c'est bien sur RGBA, y compris lors de l'iteration qui fait planter le programme.
void noir_transparent(SDL_Surface* surface)
{
for(int i=0;i<surface->w;i++)
{
for(int j=0;j<surface->h;j++)
{
if(getpixel(surface,i,j)==SDL_MapRGB(surface->format, 0x00, 0x00, 0x00))
{
pixel_transparent(surface,i,j);
}
}
}
}
edit----
Je viens de tester avec juste ce morceau de code
SDL_Surface* surface=IMG_Load("test_opengl/SDL/small_tux.png");
if(surface==NULL)
printf("erreur\n");
noir_transparent(surface);
void noir_transparent(SDL_Surface* surface)
{
for(int i=0;i<surface->w;i++)
{
for(int j=0;j<surface->h;j++)
{
if(getpixel(surface,i,j)==SDL_MapRGB(surface->format, 0x00, 0x00, 0x00))
{
pixel_transparent(surface,i,j);
}
}
}
}
void pixel_transparent(SDL_Surface *surface, int x, int y)
{
SDL_LockSurface(surface);
if (surface->format->BytesPerPixel == 3) { // RGB 24bit
printf("RGB\n");
}
else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
printf("RGBA\n");
}
int bpp = surface->format->BytesPerPixel;
Uint32 *p = (Uint32 *)surface->pixels + y * surface->pitch + x * bpp;
//Pour récuperer les valeurs rgb
Uint8 *R_Value=new Uint8;
Uint8 *G_Value=new Uint8;
Uint8 *B_Value=new Uint8;
printf("%d %d %p\n",x,y,p);
SDL_GetRGB(*p, surface->format, R_Value, G_Value, B_Value);
printf("fin\n");
//On met à jour le pixel
*p = SDL_MapRGBA(surface->format, *R_Value, *G_Value, *B_Value, SDL_ALPHA_TRANSPARENT);
//putpixel(surface,x,y,p);
SDL_UnlockSurface(surface);
}
Et j'ai la même erreur, mais pas au meme coordonnées ...
Si ça peut t'aider j'avais codé ça :
/*
Auteur : Ptival
NB : Les fonctions getpixel and putpixel sont la propriété de leurs auteurs respectifs
*/
#ifndef _LoadTex_h_
#define _LoadTex_h_
#include <windows.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <GL/gl.h>
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp)
{
case 1:
return *p;
case 2:
return *(Uint16 *)p;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
case 4:
return *(Uint32 *)p;
default:
return 0;
}
}
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp)
{
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
bpp = 0;
break;
}
}
SDL_Surface*LoadPNG(const char* filename, unsigned char R, unsigned char G, unsigned char B)
{
SDL_Surface*surface = IMG_Load(filename);
if(surface == NULL)
{
printf("Erreur avec le fichier %s\n", filename);
exit(0);
}
SDL_Surface*tsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w, surface->h, 32, 0, 0, 0, 0);
if(tsurface == NULL)
{
printf("Erreur dans la création d'une surface SDL\n");
exit(0);
}
SDL_BlitSurface(surface,NULL,tsurface,NULL);
SDL_FreeSurface(surface);
for(int y = 0; y < tsurface->h; ++y)
{
for(int x = 0; x < tsurface->w; ++x)
{
Uint32 pixel = getpixel(tsurface, x, y);
int testR=0, testG=0, testB=0;
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
{
if((Uint8)(pixel>>24) == B && (Uint8)(pixel>>16) == G && (Uint8)(pixel>>8) == R)
{
pixel |= ((Uint8)255);
putpixel(tsurface, x, y, pixel);
}
} else {
if((Uint8)pixel == B && (Uint8)(pixel>>8) == G && (Uint8)(pixel>>16) == R)
{
pixel |= (((Uint8)255)<<24);
putpixel(tsurface, x, y, pixel);
}
}
}
}
return tsurface;
}
unsigned int LoadPNGTex(const char* filename, unsigned char R, unsigned char G, unsigned char B)
{
unsigned int numtex;
SDL_Surface*surface = LoadPNG(filename, R, G, B);
glGenTextures(1,&numtex);
glBindTexture(GL_TEXTURE_2D,numtex);
glTexImage2D(GL_TEXTURE_2D, 0, 4, surface->w, surface->h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return numtex;
}
unsigned int LoadPNGTex(const char* filename)
{
unsigned int numtex;
SDL_Surface*surface = IMG_Load(filename);
if(surface == NULL)
{
printf("Erreur avec le fichier %s\n", filename);
exit(0);
}
glGenTextures(1,&numtex);
glBindTexture(GL_TEXTURE_2D,numtex);
glTexImage2D(GL_TEXTURE_2D, 0, 3, surface->w, surface->h, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return numtex;
}
#endif
Comme ça tu peux charger une texture PNG sous OpenGL, avec ou sans ColorKey :
unsigned int indextexture = LoadPNGTex("image.png", 0, 0, 0);
Charge avec le noir (0;0;0) en ColorKey
unsigned int indextexture = LoadPNGTex("image.png");
Charge sans ColorKey
Si tu veux la version avec les commentaires pour comprendre comment j'ai fait, demandes :)
ffomnislas
25/07/2005, 18h57
merci ton code est super :D
Je l'ai adapté à mes besoins et byzarrement j'ai du inverser les conditions sur la couleur, sinon le noir resté et le reste était transparent.
void pixel_transparent(SDL_Surface *surface, unsigned char R, unsigned char G, unsigned char B)
{
for(int y = 0; y < surface->h; ++y)
{
for(int x = 0; x < surface->w; ++x)
{
Uint32 pixel = getpixel(surface, x, y);
int testR=0, testG=0, testB=0;
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
{
if(!((Uint8)(pixel>>24) == B && (Uint8)(pixel>>16) == G && (Uint8)(pixel>>8) == R))
{
pixel |= ((Uint8)255);
putpixel(surface, x, y, pixel);
}
}
else
{
if(!((Uint8)pixel == B && (Uint8)(pixel>>8) == G && (Uint8)(pixel>>16) == R))
{
pixel |= (((Uint8)255)<<24);
putpixel(surface, x, y, pixel);
}
}
}
}
}
Si tu es en BigEndian c'est possible, vu que je suis en Little j'ai pas pu tester :)
Sinon, tu peux enlever la ligne :
int testR=0, testG=0, testB=0;
J'avais fait un test et j'ai oublié de supprimer les variables :)
ffomnislas
25/07/2005, 22h10
Si tu es en BigEndian c'est possible, vu que je suis en Little j'ai pas pu tester :)
Sinon, tu peux enlever la ligne :
int testR=0, testG=0, testB=0;
J'avais fait un test et j'ai oublié de supprimer les variables :)
ok je vais enlever ca ;)
ffomnislas
26/07/2005, 14h16
J'ai encore besoin de votre aide :D
J'ai encore quelque chose d'inhabituel a faire :D
Il faudrait que je puisse redimmensionner une surface. Deja je n'ai pas trouvé de fonction sdl le faisant.
Voici l'idée. Comme vous pouvez le voir je voudrais redimmensionne la surface de sortie de TTF_RenderText.
tmp = TTF_RenderText_Blended(font.get_font(), txt.c_str(), color);
if(tmp)
{
surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,w2 ,h2, 32, 0xFF0000,0xFF00, 0xFF, 0);
//Blit_Surface(sem,tmp,NULL,surface,NULL);
for(int y = 0; y < surface->h; ++y)
{
for(int x = 0; x < surface->w; ++x)
{
putpixel(surface,x,y,getpixel(tmp,x,y));
}
}
SDL_FreeSurface(tmp);
}
TTF_RenderText_Blended() renvoie une surface avec des pixels de niveau de transparent different. Mais ma surface redimmensionner ne contient pas ces different niveau de transpanrence :/
J'en deduis que que le champ alpha se perd avec le getpixel oou le putpixel. Peut être même qu'aucun des 2 ne le prend en compte.
Pourquoi tu ne blitte pas la surface tmp dans la SDL_Surface surface. Utilise un SDL_Rect pour délimité l'endroit à blitter sur ta surface tmp.
Il existe une librairie additionelle nommée SDL_rotozoom si ça t'intéresse.
Comme son nom (semble) l'indique(r), elle gère les rotations et les agrandissements/réductions de surface.
Cependant je ne l'ai pas essayée, je ne te garantis ni sa qualité, ni sa performance ;)
ffomnislas
27/07/2005, 14h01
Pourquoi tu ne blitte pas la surface tmp dans la SDL_Surface surface. Utilise un SDL_Rect pour délimité l'endroit à blitter sur ta surface tmp.
Parce que un blit ce n'est pas une copie de pixel, ca transforme le pixel de resultat en fonction de la transparence.
par ex;
- surface de depart contient un texte, tous le reste est transparent
- surface d'arrivée completement noir
si tu blit la surface de depart dans la surface d'arrivée tu va avoir un texte sur fond noir. Ajoute a ceci le fait que le texte contient des pixels de niveau de transparence different et voit ce que tu obtiens ;)
Ptival
Je vais regarder mais je ne veut pas zoomer, juste agrandir la taille de la surface ;)
vBulletin® v.3.6.5, Copyright ©2000-2009, Jelsoft Enterprises Ltd. Tous droits réservés - Version française vbulletin-fr.org