Explications sur la conversion entre les differentes profondeurs de couleur

 

Bonjour,

nous allons parler de conversions. Il faut savoir que l'on peut représenter une couleur de différentes facons. Cela a commencé par un codage simple sur 1 bit, ce qui a donné deux couleurs possibles selon l'état du bit en question. Puis cela a évolué rapidement vers 16 couleurs et, avec l'apparition des cartes graphiques VGA, vers les 256 couleurs. Maintenant, nous en sommes à coder l'information sur 32 bits, et même 48 bits pour les cartes graphiques et scanners professionnels.
Note : nous parlerons de bpp pour parler du nombre de bits utilisés pour contenir l'information d'un pixel. Par exemple 8 bpp pour les modes 256 couleurs.

 

I. Le mode 8 bpp, le début du début

Nous ne nous étendrons pas sur ce mode, celui ci étant un peu dépassé. Sachez cependant que 8 bits suffisent à contenir l'information : cet octet indique un numéro de couleur. Pour contenir les informations de couleur, nous avons une palette de 256 couleurs disponible quelque part (cela dépend de la librairie graphique, du système que vous utilisez). Chacune des entrées de cette palette comporte 3 octets indiquant les proportions de rouge, de vert et de bleu à utiliser pour former la couleur.

La conversion de 8 bpp vers des mode high-color ou true-color sera donc aisée. L'opération inverse est beaucoup plus difficile. Elle s'appelle "dithering" et n'entre pas dans le cadre de cet article.

 

II. Les modes high-color ou comment voir la vie de toutes les couleurs

Avec l'apparition du SVGA et du VESA, les utilisateurs ont pu voir apparaitre des milliers de couleurs sur leur écran. Les modes en 16 bpp sont arrivés. Ainsi, le concept de palette (qui aurait du contenir 65 536 entrées donc beaucoup trop) a été abandonné pour maintenant inscrire l'information sur la couleur du pixel directement.

Ainsi, le pixel ne donne pas un index dans la palette mais les proportions de rouge, de vert et de bleu directement. Voici l'organisation des bits :

	76543210 76543210
	RRRRRVVV VVVBBBBB

Nous avons donc 2 octets pour chaque pixel. Comment extraire les informations de couleurs ? Réponse : les décalages de bit, les OU et les ET sont vos amis : pour avoir le bleu, il suffit de mettre à 0 tous les bits qui ne nous interessent pas, c'est à dire les bits correspondant aux composantes rouge et vert.

Voici les formules (le >>1 représente un décalage de bits vers la droite) :

	rouge = (couleur >> 11) & 31 (31 = 011111b)
	vert  = (couleur >> 5 ) & 63 (63 = 0111111b)
	bleu  = couleur & 31

De la meme maniere, pour recomposer une couleur 16 bpp, vous ferez :

	couleur = (rouge << 11) | (vert << 5) | bleu

 

III. Les modes true-color ou comment se simplifier la vie de toutes les couleurs

Et nous voilà dans ce qui est la situation actuelle, meme si les modes 16 bpp sont encore d'actualité, la tendance est au 24 ou 32 bpp. Notons que le 24 bpp tend à disparaitre du fait qu'il n'est pas très rapide (ni sur 16 bits ni sur 32 bits). Dans chacun de ces modes, les composantes rouge, vert et bleu sont codées sur 8 bits. Nous avons donc le schéma suivant :

	76543210 76543210 76543210  pour le 24 bpp
	RRRRRRRR VVVVVVVV BBBBBBBB

voici les formules pour le 24 bpp :

	rouge = (couleur >> 16) & 255
	vert  = (couleur >> 8 ) & 255
	bleu  = couleur & 255

De la meme maniere, pour recomposer une couleur 16 bpp, vous ferez :

	couleur = (rouge << 11) | (vert << 5) | bleu

Mais, lecteur alerte, tu vas me dire, que fait-on des 8 bits supplémentaires du mode 32 bpp, on rajoute le pourpre ou le jaune ? Ben non, sot. On va rajouter une information indiquant le niveau de transparence du pixel (appelé composante alpha). 255 signifie que le pixel est totalement opaque, 0 signifie que le pixel est totalement transparent. Je vous laisse faire les formules de conversion, sachant que les 8 bits de la composante alpha sont les 8 bits de poids le plus fort (situés à gauche selon la convention des schémas précédents).

Comment utiliser cette information de transparence ?

supposons que le pixel ait la couleur cPixel et que le fond d'écran ait la couleur cFond. La valeur du pixel à afficher est :

	cFinale = alpha*cPixel/255 + (255-alpha)*cFond/255

Pour optimiser le calcul de la transparence, il suffit de factoriser un peu la formule. Nous obtenons au final :

	cFinale = alpha*( cPixel - cFond )/255 + cFond (une multiplication au lieu de deux).

 

IV. Routines de conversion efficaces

Nous considererons que les surfaces ont le meme nombre de pixels (en largeur et en hauteur). Voici une boucle type pour passer de 16 bpp vers 24 bpp :

  (WORD *)(source) 		        // Pointeur sur la source de pixels en 16 bpp
  (uchar *)(destination)	// Pointeur sur la destination en 24 bpp

  for (int y=0; y<hauteur; y++)
  {
    for (int x=0; x<largeur; x++)
    {
      // Pour le bleu puis on passe a la composante suivante :
      *(destination++) = ( ( (*source) & 0x001F ) << 3 );

      // Pour le vert puis idem :
      *(destination++) = ( ( (*source) & 0x07E0 ) >> 3 );
      
      // Pour le rouge puis on change de pixel car on a parcouru les 3 
      // composantes du pixel situe en (x,y)):
      *(destination++) = ( ( (*source) & 0xF800 ) >> 8 );
      
      // On change de pixel (on passe au "word" suivant) :
      source++;
    }
  }

Personellement, j'ai defini des macros qui recomposent une couleur en fonction des composantes RVB voulues :

  #define MakeColor32(a, r, g, b) ( ((uchar)(a) << 24) | 
                                    ((uchar)(r) << 16) | 
				    ((uchar)(g) << 8)  | 
				    ((uchar)(b)) 
				  )


  #define MakeColor16(r, g, b) ( (((uchar)(b) >> 3)<< 11) | 
                                 (((uchar)(g) >> 2) << 5) | 
				 ((uchar)(r) >> 3) 
			       )

Pour passer de 24 bpp a 16 bpp il suffira de faire :

  (uchar *)(source)	// Pointeur sur la source de pixels en 24 bpp
  (WORD *)(destination)		// Pointeur sur la destination en 16 bpp

  for (int y=0; y<hauteur; y++)
  {
	for (int x=0; x<largeur; x++)
	{
		*(destination) = (WORD)(MakeColor16( 	(uchar)(*(source+0) << 3),
                            				(uchar)(*(source+1) >> 2),
                            				(uchar)(*(source+2) >> 7) ));
		// On passe aux pixels suivants
		destination++;
		source+=3;
	}
  } 
 

 

V. Autres modes, remarques et précautions

quelques fabricants de carte vidéo sont très joueurs et utilisent des modes exotiques (citons entre autres ATI) : au lieu du mode 16 bpp, ils mettent en place un mode 15 bpp. ce qui fait que l'information consiste à 5 bits par composante RVB, ce qui donne visuellement :

    76543210 76543210
     RRRRRVV VVVBBBBB

Le 16e bit peut servir comme information pour savoir si le pixel est visible ou non par exemple.

 

VI. Enfin, la fin

Si tu n'as pas compris, que tu as repéré une erreur ou que tu es une jolie jeune fille libre, tu peux me contacter en utilisant les infos ci-dessous.

A la prochaine.

 

##########################################
# Auteur    : Vincent PRAT aka Gore      #
# e-mail    : vprat@ifrance.com          #
# Home page : http://vprat.ifrance.com   #
# ICQ       : 101306450                  #
##########################################

        -= Greets/coucous =-
 .. Jerome, Guillaume, Gersandre ..
 .. Les gens patients de #Codefr ..