Exemple d'affichage d'une image sur un écran privé

Un article de GuruMed.

Affichage d'une image avec sa palette dans un écran privé

Par Corto

L'article précédent montrait comment utiliser les datatypes pour afficher une image sur le Workbench avec une adaptation des couleurs aux siennes. Ca répondra certainement aux cas les plus courant, mais nous voyons aujourd'hui une autre possibilité : l'affichage d'une image dans un écran privé avec ses propres couleurs. Bien sûr, les explications sont suivies d'un source d'exemple.

Recommandations et indications

Elles sont peu nombreuses, le fonctionnement des datatypes ayant déjà été abordé. Cette fois-ci l'image affichée ne cherchera pas à se calquer sur la palette de l'écran récepteur : elle imposera la sienne. L'utilité de tout ça peut sembler mince mais rappelons-le, il s'agit de découvrir différentes facettes des datatypes. Puisque l'on veut obtenir l'image avec la palette la meilleure qui soit, on ouvrira un écran 8 bits quel que soit le nombre de couleurs de l'image. Sinon, la notion de palette n'aurait plus de sens avec un écran de profondeur supérieure (>= 16 bits). En cas d'images 24 bits par exemple, le picture.datatype se charge de la conversion des couleurs, mais attention, cette fonctionnalité n'est présente qu'à partir de la version 44.

Après l'ouverture des bibliothèques nécessaires, on récupère des informations sur l'image, indispensables pour ouvrir un écran adapté. Une fenêtre est créée ensuite pour pouvoir recevoir le BitMap. Souvenez-vous, on obtient un bitmap valide (mais pas forcément standard !) qu'après avoir appliqué la méthode PROC_LAYOUT. C'est d'ailleurs elle qui va se charger de calculer la meilleure palette et de la déposer à l'adresse du pointeur passé à l'argument PDTA_CRegs. La palette obtenue n'est qu'un suite de triplets RGB (dont le nombre est donné par le flag PDTA_NumColors) à partir desquelles on construit une palette Intuition (fonction adaptCmap). La fonction LoadRGB32 se charge de l'appliquer à notre écran, alors prêt à recevoir le BitMap adapté à la couleur près à cette palette.

Le BitMap est enfin copié (blit) puis on attend avant de terminer le programme. Contrairement à l'exemple précédent, j'ai retiré la fonction d'attente waitInput, au profit d'une attente d'un événement quelconque (appui sur une touche, clic sur la souris, ...). Ca permet juste de gagner un peu de place et de se concentrer sur l'essentiel.

A propos de PMODE

On peut en apprendre sur cette option mystérieuse en consultant les autodocs : le mode 42 garantie l'obtention de bitmaps standard, alors que le mode 43 récupère un bitmap dont on ne peut rien savoir de l'organisation interne ... En outre, le mode 43 permet d'accéder à des fonctions étendues, comme par exemple la lecture de données au format chunky (ce que nous nous réservons éventuellement pour le prochain article :) Il permet également l'affichage 16/24 bits sur des écrans de cette profondeur.

	
/*
 * Affichage d'une image sur un écran privé grâce aux datatypes
 * Par Corto pour www.guru-meditation.net
 * Date : 21/01/03
 */

#include <stdio.h>
#include <stdlib.h>

#include <exec/exec.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <graphics/display.h>

#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/datatypes.h>

// Variables communes au module
struct Screen *screen = NULL;
struct Window *window = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct Library *DataTypesBase = NULL;

ULONG *palette = NULL;
Object *dtype = NULL;

// Prototypes
static void quit(char *msg);
static ULONG *adaptCmap(int nc, ULONG *cr);


/*
 * Code du programme principal
 */
int main(int argc, char*argv[]){
	ULONG modeID = 0L;
	struct BitMapHeader *bmh = NULL;
	int width, height, depth;
	int res;
	struct BitMap *bitmap = NULL;
	ULONG colorNumber;
	ULONG *colorRegisters;


	/* Vérification des arguments */
	if (argc != 2){
		printf("Usage : %s FichierImage\n", argv[0]);
		return(1);
	}

	/* Ouverture des bibliothèques nécessaires */
	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 36);
	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 40L);
	DataTypesBase = (struct Library *)OpenLibrary("datatypes.library", 39);
	
	if ((IntuitionBase == NULL) || (GfxBase == NULL) || (DataTypesBase == NULL)){
		quit("Problème d'initialisation des bibliothèques système\n");
	}

	
	/* Création du datatype et récupération des infos générales */
	dtype = NewDTObject(argv[1],
							PDTA_DestMode, PMODE_V43,
							PDTA_Remap, TRUE,
							TAG_END);
	if (dtype == NULL){
		quit("Création du datatype impossible\n");
	}
	
	res = GetDTAttrs(dtype, PDTA_BitMapHeader, (ULONG)&bmh, TAG_END);
	if (res != 1){
		quit("Obtention des infos du datatype impossible\n");
	}
	width = bmh->bmh_Width;
	height = bmh->bmh_Height;
	depth = bmh->bmh_Depth;
									

	/* Pour l'exemple, on force la profondeur de l'écran à 8 ... */
	depth = 8;

	/* ... et on cherche quel est le meilleur écran 8 bits pour l'image */
	modeID = BestModeID(
			BIDTAG_NominalWidth, width,
			BIDTAG_NominalHeight, height,
			BIDTAG_DesiredWidth, width,
			BIDTAG_DesiredHeight, height,
			BIDTAG_Depth, depth,
			TAG_END);
	if (modeID == INVALID_ID){
		quit("Impossible d'ouvrir un écran adapté à la demande\n");
	}


	/* Ouverture de l'écran puis de la fenêtre */
	screen = OpenScreenTags(NULL,
			SA_Width, width,
			SA_Height, height,
			SA_Title, (ULONG)"Datatype sur écran privé",
			SA_DisplayID, modeID,
			SA_Depth, depth,
			SA_Type, CUSTOMSCREEN,
			SA_Interleaved, TRUE,
			SA_FullPalette, TRUE,
			TAG_END);
	if (!screen){
		quit("Impossible d'ouvrir l'écran\n");
	}

	window = OpenWindowTags(NULL,
			WA_Left, 0,
			WA_Top, 0,
			WA_Width, width,
			WA_Height, height,
			WA_IDCMP, IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS,
			WA_CustomScreen, (ULONG)screen,
			WA_SizeGadget, FALSE,
			WA_DepthGadget, FALSE,
			WA_CloseGadget, FALSE,
			WA_Borderless, TRUE,
			WA_RMBTrap, TRUE,
			WA_SimpleRefresh, TRUE,
			WA_InnerWidth, width,
			WA_InnerHeight, height,
			TAG_DONE);

	if (!window){
	 quit("Impossible d'ouvrir la fenêtre\n");
	}

	if(DoMethod(dtype, DTM_PROCLAYOUT, NULL, 1)){

		res = GetDTAttrs(dtype,
			PDTA_DestBitMap, (ULONG)&bitmap,
			PDTA_NumColors, (ULONG)&colorNumber,
			PDTA_CRegs, (ULONG)&colorRegisters,
			TAG_DONE);
		if (res != 3){
			quit("Le chargement de l'image a échoué\n");
		}

		if(bitmap){
			palette = adaptCmap(colorNumber, colorRegisters);
			LoadRGB32(&screen->ViewPort, palette);
			BltBitMapRastPort(bitmap, 0, 0, window->RPort, 0, 0, width, height, 0xc0);
		}
	}else{
		quit("Echec dans l'exécution de la méthode DTM_PROCLAYOUT\n");
	}
   
	/* Attente et libération ressources */
	WaitPort(window->UserPort);
	
	quit(NULL);

	return 0;
}
	
 
/*
* quit
* Pour sortir proprement en tout point du programme
*/
static void quit(char *msg){
	if (msg != NULL){
		printf("%s\n",msg);
	}
	
	if (dtype != NULL){
		DisposeDTObject(dtype);
	}
	if (window != NULL){
		CloseWindow(window);
	}
	if (screen != NULL){
		CloseScreen(screen);
	}
	if (palette != NULL){
		free(palette);
	}
	if (DataTypesBase != NULL){
		CloseLibrary((struct Library *)DataTypesBase);
	}
	if (IntuitionBase != NULL){
		CloseLibrary((struct Library *)IntuitionBase);
	}
	if (GfxBase != NULL){
		CloseLibrary((struct Library *)GfxBase);
	}
}

	
/*
 * adaptCmap
 * Transformation de la palette obtenue par le datatype en une palette
 * système au format LoadRGB32().
 */
static ULONG *adaptCmap(int nc, ULONG *cr){
	int c;
	ULONG	*colourtable;

	colourtable = (ULONG *)malloc((1 + 3 * nc + 1) * sizeof(ULONG));
	if (colourtable != NULL){
		colourtable[0] = (nc << 16) + 0;
		for (c = 0; c < nc; c++) {
			colourtable[3 * c + 1] = cr[3 * c + 0];
			colourtable[3 * c + 2] = cr[3 * c + 1];
			colourtable[3 * c + 3] = cr[3 * c + 2];
		}
		colourtable[1 + 3 * nc] = 0;
	}
	return colourtable;
}