5ème partie: changer une palette et accélérer les tracés 2D

Un article de GuruMed.

Sommaire

LoadRGB32() et BltBitMapRastPort()

par StAn.

Aujourd'hui, nous allons voir deux choses: d'abord, l'utilisation de LoadRGB32() pour changer la palette de couleurs d'un écran privé; ensuite, comment accélérer le tracé d'images en utilisant BltBitMapRastPort() au lieu de WriteChunkyPixels().


LoadRGB32()

La seule méthode vue jusqu'à maintenant pour changer la palette d'un écran, et notamment du Workbench, était l'utilisation d'ObtainBestPen() de la graphics.library. Cette fonction marche sur tout écran, public comme privé, mais je ne peux pas cependant m'abstenir de vous présenter LoadRGB32().

LoadRGB32() permet de changer une ou plusieurs couleurs de la palette d'un écran d'un seul coup. A la différence d'ObtainBestPen(), les pinceaux modifiés ne seront cependant pas alloués. LoadRGB32() convient donc très bien pour les écrans privés où seul votre programme a accès, et très mal pour les écrans publics, susceptibles d'accueillir des fenêtres d'autres programmes que le votre, qui pourraient vouloir modifier la palette.

LoadRGB32() est très simple à utiliser, vous permet d'obtenir la palette exacte souhaitée, et vous épargne le remappage des pinceaux de votre image chunky. Mais, je le répête, elle n'est utile que si votre programme s'ouvre dans un écran privé (sauf cas particuliers).

Cette fonction prend 2 paramètres:

  • struct ViewPort *vp
Le ViewPort de l'écran dont vous voulez modifier la palette.


  • ULONG *table
La palette telle que décrite dans l'article numéro 3 de cette série. Des outils tels que ArtPro et PersonnalPaint peuvent générer des palettes au format LoadRGB32.


Comme je l'avais dit précédemment, une palette de format LoadRGB32 commence par 2 mots de 16 bits chacun. Le premier contient le nombre de couleurs de la palette, et le second le numéro de la première couleur à modifier.

Donc, si vous souhaitez changer la couleur des pinceaux 0 à 5 (soit 6 pinceaux), votre palette devrait commencer par les deux nombres 6 et 0, soit en hexadécimal 0x00060000. Par exemple, pScreen étant un pointeur sur votre écran:

	
ULONG palette[] = {
 0x00060000,
 0x12345678, 0x12345678, 0x12345678,  /* couleur du pinceau 0 */
 0x12345678, 0x12345678, 0x12345678,  /* couleur du pinceau 1 */
 0x12345678, 0x12345678, 0x12345678,
 0x12345678, 0x12345678, 0x12345678,
 0x12345678, 0x12345678, 0x12345678,
 0x12345678, 0x12345678, 0x12345678,  /* couleur du pinceau 5 */
 0x00000000  /* zéro final */
};

LoadRGB32( &pScreen->ViewPort, palette );

Maintenant, si vous souhaitez modifier les pinceaux 10 à 15 de la palette en utilisant ces mêmes couleurs, il vous suffit de remplacer 0x00060000 par 0x006000A (car 0x000A == 10).


BltBitMapRastPort()

Nous n'avons pour l'instant vu que la fonction WriteChunkyPixels() pour tracer des images. Hors, cette fonction nécessite une conversion de l'image par le processeur, pour adapter les pixels "chunky" de l'image au format de pixel de l'écran de destination.

Par exemple, vous connaissez déjà sûrement la fameuse conversion "Chunky to planar" ou "C2P". C'est ce qu'il se passe lorsque vous tracez une image chunky sur un écran AGA à l'aide de WriteChunkyPixels(): chaque pixel tenant à l'origine sur un seul octet doit être décomposé en 8 bits séparés appartenant chacun à un plan différent de l'écran.

Sur cartes graphiques, les écrans ne sont habituellement pas au format planar, mais des opérations sur le format des pixels sont en général également nécessaires (par exemple pour tracer une image chunky 8 bits sur un écran 24 bits, il est évident qu'une conversion est indispensable).

Cette transformation prend du temps, et si vous tracez une image de nombreuses fois dans votre programme, il peut être très intéressant de la faire une fois pour toutes à l'initialisation du programme... et d'ensuite utiliser le blitter pour tracer l'image dans votre fenêtre très rapidement.

Rassurez-vous, il ne sera pas nécessaire de programmer le blitter directement: la graphics.library se charge des détails pour vous ;-). Notez également que par le terme "blitter", je désigne aussi bien le blitter du chipset AGA que les blitters des différentes cartes graphiques, c'est-à-dire un coprocesseur dédié à la copie d'images 2D.


Pré-conversion de l'image chunky

Pas de surprise, nous allons utiliser WriteChunkyPixels(). Cependant, comme nous n'allons pas tracer directement dans la fenêtre, nous avons besoin de créer un RastPort et un BitMap pour y stocker l'image transformée au format de l'écran.

Un RastPort se crée très facilement avec la fonction InitRastPort():

	
struct RastPort rp;

InitRastPort( &rp );

(vous aurez besoin d'inclure graphics/rastport.h pour cela)

Le BitMap lui s'alloue avec la fonction AllocBitMap():

	
struct BitMap *pBM;

pBM = AllocBitMap( largeur, hauteur, profondeur, flags, friend_bitmap );

Comment faire pour que l'image soit transformée au format de l'écran lorsqu'on la trace dans ce BitMap avec WriteChunkyPixels() ? Il suffit que ce BitMap soit justement dans le même format que l'écran. C'est à ça que sert le paramètre "friend_bitmap": il faut donner ici le BitMap de l'écran pour que le BitMap alloué ait le même format.

largeur et hauteur sont bien sûr la taille de votre image, et profondeur la profondeur de l'écran. Pour obtenir la profondeur de l'écran, il faut utiliser la fonction GetBitMapAttr() de la graphics.library sur le BitMap de l'écran. Soit pWin un pointeur sur votre fenêtre, cela donne:

	
ULONG screenDepth;

screenDepth = GetBitMapAttr( pWin->WScreen->RastPort.BitMap, BMA_DEPTH );

Attention: comme cela est précisé dans l'include intuitions/screens.h, n'utilisez jamais &Screen->BitMap, mais toujours Screen->RastPort.BitMap.

Quand aux flags... Leur liste se trouve dans le fichier d'include graphics/gfx.h. Seuls deux d'entre eux seront abordés ici:

  • BMF_DISPLAYABLE
Le flag BMF_DISPLAYABLE demande à ce que le BitMap soit alloué dans la mémoire de la carte graphique (ou dans la CHIP RAM en AGA). C'est nécessaire pour que le blitter y ait accès et donc que le tracé se fasse le plus rapidement possible.
Il y a cependant une exception: FBlit. Ce programme sert à accélérer les Amiga sous AGA en utilisant le processeur à la place du blitter (vous allez me dire que ça contredit ce que je viens de dire, et vous aurez raison, mais tant pis). Bref, lorsque FBlit est lancé, il ne faut pas positionner le flag BMF_DISPLAYABLE; ainsi le BitMap ira en FAST RAM et FBlit pourra le copier plus rapidement. Comment savoir si FBlit est lancé ? En cherchant un port nommé "FBlit" avec la fonction FindPort() d'exec.library.
  • BMF_MINPLANES
Cet autre flag est à positionner sous CyberGraphics (voire P96) mais pas en AGA. Je ne m'étendrai pas sur le sujet, mais sachez que ça va plus vite comme ça.

Voici donc comment allouer le RastPort et le BitMap intermédiaire et tracer l'image chunky dedans, en supposant que l'on souhaite tracer au final dans la fenêtre "pWin":

	
struct RastPort rp;
struct BitMap   *pBM;
ULONG           screenDepth, flags;
BOOL            isStandardBM;

InitRastPort( &rp );

screenDepth = GetBitMapAttr( pWin->WScreen->RastPort.BitMap, BMA_DEPTH );

isStandardBM = GetBitMapAttr( pWin->WScreen->RastPort.BitMap, BMA_FLAGS ) & BMF_STANDARD;
flags = ( FindPort("FBlit") ? 0 : BMF_DISPLAYABLE ) | ( isStandardBM ? 0 : BMF_MINPLANES );

pBM = AllocBitMap( IMAGE_WIDTH, IMAGE_HEIGHT, screenDepth, flags, pWin->WScreen->RastPort.BitMap );
if( !pBM ) return; /* quitte si allocation échouée */

rp.BitMap = pBM; /* lie le BitMap au RastPort */

WriteChunkyPixels( &rp, 0, 0, IMAGE_WIDTH-1, IMAGE_HEIGHT-1, image, IMAGE_WIDTH );

Tracé du BitMap dans la fenêtre

Maintenant que le plus dur est fait, il ne reste plus qu'à utiliser la fonction BltBitMapRastPort() de la graphics.library pour tracer le BitMap dans la fenêtre.

BltBitMapRastPort( struct BitMap *bitmap_source, WORD src_x, WORD src_y,

                   struct RastPort *rastport_dest, WORD dest_x, WORD dest_y,

                   WORD largeur, WORD hauteur, UBYTE minterm );
  • bitmap_source
Il s'agit du BitMap dans lequel se trouve l'image que vous voulez tracer.
  • src_x, src_y
Les coordonnées du coin supérieur gauche de l'image que vous voulez tracer, dans le BitMap source. Si vous voulez tracer tout le BitMap, il faut fournir 0,0.
  • rastport_dest
Le RastPort de la fenêtre ou de l'écran dans lequel vous voulez tracer l'image.
  • dest_x, dest_y
Les coordonnées du coin supérieur gauche de la zone du RastPort destination où vous voulez tracer l'image.
  • largeur, hauteur
La taille de l'image à tracer.
  • minterm
Une valeur décrivant la façon dont l'image doit être copiée... En effet le blitter d'origine de l'Amiga permet d'effectuer des opérations sur les images lors d'une copie. Cependant, ceci n'est pas aussi évident lors de l'utilisation d'une carte graphique, et seules certaines valeurs bien précises fonctionneront. Sachez juste qu'une valeur de 0xC0 doit être utilisée pour une simple copie d'image sans modification.

Voici donc l'appel à faire pour tracer votre image:

	
BltBitMapRastPort( pBM, 0, 0, pWin->RPort, dest_x, dest_y, IMAGE_WIDTH, IMAGE_HEIGHT, 0xC0 );

N'oubliez pas de libérer le BitMap avant de quitter le programme, avec la fonction FreeBitMap().

Notez que l'AutoDoc de FreeBitMap() recommande d'appeler WaitBlit() avant de libérer le BitMap pour être sûr que rien n'est en train d'être écrit dedans. Cependant ce n'est pas utile dans notre cas, puisque nous n'utilisons que WriteChunkyPixels() pour écrire dedans et que cette fonction n'utilise pas le blitter.

Pas de source directement compilable pour cette fois, mais il devrait être facile d'en créer un à partir de tous les bouts de source de cet article !