Fondus enchaînés complexes avec SDL
Un article de Games Creators Network.
Cet article est la suite logique du premier article sur les fondus enchaînés.
Cette fois-ci, au lieu de simplement réaliser un fondu linéaire ou toute l'image apparaît en même temps, nous allons privilégier certaines zones afin de créer de nouveaux effets.
Pour cela, il nous faut un nouveau paramètre qui va schématiser l'effet de fondu. Dans cet article, ce paramètre prendra la forme d'une image ressemblant à l'image ci-contre.
[modifier] Principe du fondu
Un fondu réalise une transition animée de l'image A vers l'image B.
Le principe algorithmique est le suivant:
- Affichage de l'image B
- Modification de la couche alpha de l'image A
- Affichage de l'image A
Le point clé réside dans la modification de la couche alpha de l'image A. Au départ, l'image A est totalement opaque (tous les pixels ont leur composante alpha à 255). Le but est de faire converger les composantes alpha vers la valeur 0 (surface totalement transparente).
Contrairement à l'article précédent, nous allons utiliser le schéma de transition afin de faire converger les composantes plus ou moins rapidement vers zéro selon leur position dans l'image.
Ainsi, à chaque tour de boucle, on diminue la couche alpha de chaque pixel proportionnellement à la composante rouge du pixel situé à la même position dans le schéma de transition.
Comparons l'évolution de la composante alpha d'un pixel aillant une composante forte ou faible dans le schéma de transition:
| Composante du pixel dans le schéma de transition à 10 | Composante du pixel dans le schéma de transition à 200 |
|---|---|
| 255 | 255 |
| 250 | 155 |
| 245 | 55 |
| 240 | 0 |
| 235 | 0 |
| etc. | etc. |
Cette différence entre les couches alphas des pixels va créer l'effet de mouvement durant le fondu.
NB: Les valeurs données sont théoriques. Les coefficients diffèrent de ceux du programme final.
[modifier] Code source
La fonction FonduEnchaine prend en paramètre:
- avant un pointeur vers l'image de départ (nommée image A plus haut)
- après un pointeur vers l'image de d'arrivée (nommée image B)
- file_transition contient le chemin vers le schéma de transition
- duree est la durée du fondu en millisecondes
- vitesse est la vitesse du fondu variant de 1 (rapide) à 255 (lent)
La fonction renvoit 1 si l'utilisateur a tenté de quitter le programme durant la transition, 0 sinon.
Les surfaces passées en paramètre ne sont pas modifiées. La surface avant est, de plus, copiée dans une surface temporaire. Si vous lui affectez un pointeur vers l'écran (SDL_GetVideoSurface()), l'effet rendu sera bien celui voulu (une transition de l'écran tel qu'il est au début de la transition vers la surface apres).
int FonduEnchaine( SDL_Surface *avant, SDL_Surface *apres, char *file_transition, unsigned int duree, unsigned int vitesse) { SDL_Event event; SDL_Surface *copie, *transition; unsigned int rmask, gmask, bmask, amask, time, i; Uint32 temp, *pt, *pta; /* pointeurs vers les surfaces */ Uint8 r,g,b,a; Uint8 comp; Uint8 v; /* Quelques vérifications pour éviter les principales sources de segfault */ if((!avant)||(!apres)) return 0; if(avant->w!=apres->w) return 0; if(avant->h!=apres->h) return 0; /* On copie la surface "avant" afin de pouvoir traviller dessus sans crainte */ #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif copie = SDL_CreateRGBSurface(SDL_SWSURFACE, 800, 600, 32, rmask, gmask, bmask, amask); SDL_BlitSurface(avant, NULL, copie, NULL); /* Chargement de la transition */ transition = SDL_LoadBMP(file_transition); transition = SDL_ConvertSurface(transition,copie->format,SDL_SWSURFACE|SDL_SRCALPHA); if(!transition) { SDL_FreeSurface(copie); return 0; } /* On fixe la transparence de la surface */ SDL_SetAlpha(copie, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); /* Début de la boucle */ time=SDL_GetTicks(); v=vitesse; while((SDL_GetTicks()-time)<duree) { while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: SDL_FreeSurface(transition); SDL_FreeSurface(copie); return 1; break; } } /* Affiche fond */ SDL_BlitSurface(apres, NULL, SDL_GetVideoSurface(), NULL); /* Mise à jour de la couche alpha */ SDL_LockSurface(copie); SDL_LockSurface(transition); pt=((Uint32*)copie->pixels); pta=((Uint32*)transition->pixels); for(i=0;i<(copie->w*copie->h);i++) { SDL_GetRGBA(pt[i],copie->format,&r,&g,&b,&a); /* je ne récupère qu'une composante... c'est du niveau de gris! */ temp=pta[i]&transition->format->Rmask; temp=temp>>transition->format->Rshift; temp=temp<<transition->format->Rloss; comp=(Uint8)temp; if(comp<v) comp=v; if(a>(comp/v)) a-=(comp/v==0)?1:(comp/v); else a=0; pt[i]=SDL_MapRGBA(copie->format,r,g,b,a); } SDL_UnlockSurface(transition); SDL_UnlockSurface(copie); /* On augmente petit à petit la vitesse afin d'éviter les fins de transition trop longs */ if(v>1) v--; /* On affiche la deuxième surface */ SDL_BlitSurface(copie, NULL, SDL_GetVideoSurface(), NULL); /* Inversion des tampons */ SDL_Flip(SDL_GetVideoSurface()); } SDL_FreeSurface(transition); SDL_FreeSurface(copie); return 0; }
Exemple d'appel de cette fonction:
fond1=SDL_LoadBMP("fond1.bmp"); fond2=SDL_LoadBMP("fond2.bmp"); FonduEnchaine(fond1, fond2, "transition.bmp", 5000, 50);
Cet appel génère un fondu de 5 secondes à vitesse moyenne entre l'image fond1.bmp et l'image fond2.bmp à l'aide du schéma transition.bmp.
[modifier] Démo
La démo exploite le code ci-dessus afin de générer 6 transitions différentes.


