Dessiner des pixels.

Dessiner des pixels grâce à la souris.

SDL_LockSurface

Voir version :

Pas de dépendances

Télécharger :

#include <sdl/sdl.h>

#ifdef WIN32
#pragma comment(lib,"sdl.lib")
#pragma comment(lib,"sdlmain.lib")
#endif

void UpdateEvents(Sint32* mousex,Sint32* mousey,char boutons[8],char key[SDLK_LAST])
{
    SDL_Event event;
    while(SDL_PollEvent(&event))
    {
        switch (event.type)
        {
        case SDL_KEYDOWN:
            key[event.key.keysym.sym]=1;
            break;
        case SDL_KEYUP:
            key[event.key.keysym.sym]=0;
            break;
        case SDL_MOUSEMOTION:
            *mousex=event.motion.x;
            *mousey=event.motion.y;
            break;
        case SDL_MOUSEBUTTONDOWN:
            boutons[event.button.button]=1;
            break;
        case SDL_MOUSEBUTTONUP:
            boutons[event.button.button]=0;
            break;
        }
    }
}

void SDL_PutPixel32(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    Uint8 *p = (Uint8*)surface->pixels + y * surface->pitch + x * 4;
    *(Uint32*)p = pixel;
}

Uint32 SDL_GetPixel32(SDL_Surface *surface, int x, int y)
{
    Uint8 *p = (Uint8*)surface->pixels + y * surface->pitch + x * 4;
    return *(Uint32*)p;
}

int main(int argc,char** argv)
{
    Sint32 mousex = 0;
    Sint32 mousey = 0;
    char boutons[8] = {0};
    char key[SDLK_LAST] = {0};
    SDL_Surface* screen;
    SDL_Init(SDL_INIT_VIDEO);
    screen=SDL_SetVideoMode(800,600,32,SDL_SWSURFACE|SDL_DOUBLEBUF);  
    SDL_ShowCursor(1);
    while(!key[SDLK_ESCAPE])
    {
        UpdateEvents(&mousex,&mousey,boutons,key);
        if (SDL_MUSTLOCK(screen))
            SDL_LockSurface(screen);
        if (boutons[SDL_BUTTON_LEFT])
        {
            SDL_PutPixel32(screen,mousex,mousey,0xFFFFFF);
        }
        if (boutons[SDL_BUTTON_RIGHT])
        {
            SDL_SaveBMP(screen,"out.bmp");
        }
        if (SDL_MUSTLOCK(screen))
            SDL_UnlockSurface(screen);        
        SDL_Flip(screen);
    }
    return 0;
}



Commentaires


  Bien que la philosophie SDL (et des libs d'affichage 2D en général) soit le "BLIT", c'est a dire copier/coller des rectangles,
  et rien que ça ; il peut etre utile de descendre au niveau du pixel.

Comprendre ce qu'il se passe avant de continuer :
-------------------------------------------------

  SACHEZ AVANT TOUT QUE TOUT TRAITEMENT AU PIXEL EST LENT.

  Une surface a pour but d'etre blittée, donc a plutot intéret a etre en VRAM pour accélérer l'affichage.

  Si vous voulez accéder aux pixels d'une surface, pour devez la rappatrier en RAM (on parle de faire un LOCK de l'image)
  Si vous voulez de nouveau l'afficher, vous devez la réenvoyer en VRAM (on parle de UnLock).
  Si vous décidez de manipuler une image en RAM, et non en VRAM, le lock sera surement inutile, mais a chaque Flip, l'image sera renvoyée en VRAM

  Tous ces voyages de la RAM vers la VRAM sont couteux en temps de calcul, notament si les images sont grandes.
  Voila pourquoi manipuler des pixels de surface est quelque chose de lent.

  Le conseil : si vraiment vous avez des modifications a faire sur les images, faites les hors boucle principale.

  Exemple :
  Vous programmez un jeu de shoot'em up : un jeu ou des petits avions tirent sur tout ce qui bouge, avec des explosions de partout.
  Vous avez dessiné un avion rouge pour le joueur 1.

  Au lieu de dessiner un avion bleu, un vert et un jaune pour les 3 autres joueurs, vous décidez de programmer une fonction, qui, 
  a partir de l'avion rouge, va fabriquer un bleu, un vert, et un jaune. 
  Pour cela, vous chargez l'avion en mémoire dans une SDL_Surface*, puis vous la dupliquez, et pour chaque copie, vous lisez chaque pixel, 
  et si vous voyez un pixel rouge, vous le changez en bleu....

  Disons que votre avion a plusieurs positions, et que tout ce calcul mette une demi seconde...

  Si cette demi seconde se passe avant votre boucle principale, pendant le temps de chargement, ce n'est pas grave
  Par contre, si vous le faites a chaque itération de boucle, la, vous avez un jeu tout lent....
  Donc a n'utiliser que Hors boucle, dans votre intéret.
  

Et maintenant, le programme :
-----------------------------

  Lancez le programme : une fenetre s'ouvre, le curseur de souris est la. Si vous cliquez, vous dessinez un point blanc.
  Si vous cliquez sur le 2e bouton de la souris (ou celui du milieu), avec votre dessin est sauvegardé dans le meme répertoire,
  sous le nom de "out.bmp"

  Voyons comment cela marche.
  Regardez le main, et uniquement le main.

  Les premieres lignes vous sont familieres, l'initialisation de SDL.
  On active l'affichage du curseur avec SDL_ShowCursor, on en aura besoin.

  Le boucle principale est le while.
  On sortira quand on appuiera sur ESC.

  Remarquez ici la gestion des touches : tout est déplacé dans le UpdateEvents
  La fonction UpdateEvents met a jour a chaque appel l'état des touches et de la souris.
  Si vous voulez regarder comment elle fonctionne, elle POLL chaque event, et les range dans des tableaux passés en paramètre.
  La taille du tableau "key" est définie par constante SDL SDLK_LAST : avec ça je suis sur que le tableau contient pile la taille qu'il faut pour toutes les touches du clavier.
  La taille du tableau "boutons" est ici de 8. Malheureusement, il n'existe pas de "LAST" qui me permettrait d'avoir pile la bonne taille. 
  Je l'ai donc mis à la main.

  Du coup, on lit le clavier et la souris tres tres facilement.
  Il faut juste ne pas oublier de mettre UpdateEvents dans la boucle principale :)

	if (SDL_MUSTLOCK(screen))
		SDL_LockSurface(screen);

  Ces deux lignes vont faire un lock sur la surface screen : nécessaire pour pouvoir écrire des pixels dessus (voir explications ci dessus)
  Le if protege le lock : finalement, on va faire un lock que si on doit le faire (must lock)
  Je rappelle qu'on doit le faire si l'image est en VRAM, et il sera inutile s'il est en RAM.

  Si vous etes observateur, vous avez vu dans la fonction SDL_SetVideoMode que j'ai mis SDL_SWSURFACE.
  Cela veut dire que je crée screen en RAM et non en VRAM. Ainsi, le lock sera inutile dans ce cas.

  Si vous ne savez pas, mettez SDL_MUSTLOCK, comme ci dessus, et vous etes tranquille...


  Remarquez qu'avant le flip, je mets :

  if (SDL_MUSTLOCK(screen))
	SDL_UnlockSurface(screen);		

  Pour repréparer l'image a etre blittée (et a etre flippée)


  Ensuite, le corps du programme :
  
		if (boutons[SDL_BUTTON_LEFT])
		{
			SDL_PutPixel(screen,xsouris,ysouris,0xFFFFFF);
		}

  bouton[SDL_BUTTON_LEFT] est mis a jour par updateEvents : il contient a tout moment le statut des boutons de la souris.
  
  Ce if est validé uniquement si vous avez le bouton SDL_BUTTON_LEFT (de gauche) appuyé.
  Et dans le if, c'est un putpixel (dessine un pixel) a la coordonnée xsouris/ysouris (mis a jour par UpdateEvents), de
  couleur 0xFFFFFF. (=blanc)

  Si vous regardez dans la fonction SDL_PutPixel32, elle n'écrit des pixels que pour les surfaces 32 bits.

  Voici comment elle fonctionne :

  Dans la structure SDL_Surface, vous avez un pointeur pixels, un void* (pointeur pur).
  Ce pointeur n'est valide que si la surface est en RAM, ou a subit un Lock (d'ou l'importance du lock dans le main).
  Sous ce pointeur, tous les pixels !

  Il sont rangés dans le sens de lecteur conventionnel (de gauche à droite, puis de haut en bas).
  Chaque pixel fait chez nous 4 octets. La largeur d'une ligne en octet pourrait être de 4*W octets. (avec W la largeur de l'image).
  Pour des raisons d'alignement, ça n'est pas toujours vrai. (pour 4 octets par pixels, ça l'est, mais passons).
  Il existe une donnée, appelée pitch, qui dit exactement combien de pixels prend une ligne.

  Ainsi, pour aller au pixel x,y, le décalage mémoire est de y*picth + x*4
  C'est la formule que nous retrouvons en haut.

  L'écriture d'un pixel revient donc simplement a écrire un Uint32 au bon endroit

  vous pouvez voir la fonction SDL_GetPixel32 qui est exactement l'inverse de SDL_PutPixel32, qui n'est pas utilisée dans ce programme, 
  mais que j'ai mise quand meme, si vous en avez besoin.


  Pour les couleurs, on verra ça dans un autre chapitre.
  Ce seul if permet de dessiner.


		if (boutons[SDL_BUTTON_RIGHT])
		{
			SDL_SaveBMP(screen,"out.bmp");
		}

  Ce if ci passe si on appuie sur le bouton 1 (milieu ou droite), et il sauvegarde screen dans un BMP, tout simplement.
  Tres pratique cette fonction, et native dans SDL.  



  Note : dans cet exemple, on a fait de l'acces aux pixels de la surface principale, mais rien ne vous empeche de faire des acces 
  pixels de n'importe quelle surface, oubliez pas les lock et unlock pour blitter, ou accéder aux pixels...

  Note : Minimisez au maxium vos appels a Lock et Unlock : par exemple, si vous lockez puis delockez a chaque fois que
  vous écrivez/lisez un pixel, ça va aller tres tres lentement. Lockez une fois, faites toute vos opérations avec les pixels, puis unlockez.