Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Gestion des datatypes
(Article écrit par Mathias Parnaudeau et extrait de GuruMed.net - janvier 2003)
|
|
Les datatypes : une merveille du système
Un concept simple pour une mise en oeuvre parfois délicate. Les datatypes représentent un des aspects
modulaires et évolutifs du système d'exploitation Amiga, leur nature est orientée objet. Deux atouts
qui en font une force (malgré de grosses lacunes, nous le verrons), à tel point qu'ils sont mis en
avant dans le développement d'AmigaOS 4 et même AmigaOS 5 !
Nous apprendrons donc ici à utiliser les datatypes en connaissant leurs particularités et à travers
différents exemples.
1. Comprendre les datatypes et leur utilisation
L'étude des formats de fichiers et des modes de compression m'avaient tenu écarté des datatypes mais le
génie de ces greffons du système d'exploitation m'a ramené à la raison, me contraignant à m'y pencher de
plus près. Les datatypes apportent une grande souplesse et laissent transparaître une étonnante facilité
d'utilisation. C'est bien le cas, mais il en va autrement lorsque l'on arpente la face développeur...
La documentation est à l'unanimité jugée très pauvre et les datatypes ont suivi des évolutions parallèles
et pas forcément officielles. Des recherches (principalement dans les archives des groupes de discussion sur Google)
m'ont permis de compiler bon nombre d'observations que je vous présente ici.
1.1 Principe
Les datatypes se présentent comme une hiérarchie de classes orientées objet, de type BOOPSI.
Ainsi, pour manipuler un son, on accède à une unique interface (la classe mère) quel que soit le
format d'origine : pour ce dernier, nous avons une classe fille qui est sollicité pour chaque format.
Ben Hutching, dans un message de novembre 1999, se livre à cette présentation des datatypes. Ils sont
implémentés par une combinaison de deux choses :
- Un (ou plusieurs) fichier(s) descripteur de datatype. Ils décrivent comment reconnaître les types
de fichiers et quel code doit lui être appliqué. AddDatatypes tente de lire ces informations pour maintenir
une liste en mémoire grâce à la datatypes.library. Ces descripteurs sont normalement installés dans
DEVS:Datatypes donc ils seront lus par AddDataTypes au démarrage.
- Une bibliothèque implémentant une classe BOOPSI qui hérite de datatypesclass, souvent indirectement.
Par exemple, le datatype gérant le JPEG implémente une sous-classe de picture.datatype, à savoir jpeg.datatype.
1.2 Considérations techniques
Le chargement d'un objet (nous travaillerons ici sur les images) doit respecter une certaine marche
à suivre. Nous décrirons donc les différentes étapes nécessaires pour afficher un fichier image.
1.2.1 Allouer un nouvel objet avec NewDTObject
L'appel à cette fonction constitue la base même de l'utilisation des datatypes. On a à nouveau recourt aux
"tags" (balises/mots clés) que l'on passe en paramètre. La plupart du temps, l'image à afficher devra subir des
transformations pour pouvoir s'adapter aux paramètres de l'écran. Ainsi, soit on passe les informations indiquant
comment doit être transformée l'image, soit on s'en passe et le bitmap chargé par défaut devra être ajusté
par la suite. Quoi qu'il en soit, NewDTObject donne vie à une entité datatype que l'on peut ensuite façonner.
Les balises permettent d'indiquer des directives au datatype mais aussi de récupérer par le biais de pointeurs
des informations (nous verrons juste après un autre moyen). Parmi les balises, citons les plus importantes
avec leur signification :
- DTA_GroupID, GID_PICTURE : nous voulons traiter un fichier de nature "image".
- GA_Width : largeur du RastPort.
- GA_Height : hauteur du RastPort.
- PDTA_ModeID : modeID de l'écran dans lequel il faudra rendre l'image.
- PDTA_Screen : pointeur sur l'écran destiné à recevoir l'image. Dans ce cas-là, les couleurs de
l'écran sont utilisées comme couleurs de référence pour la conversion des couleurs.
- PDTA_FreeSourceBitMap, TRUE : indique si la source bitmap doit être libérée immédiatement par
le picture.datatype après l'appel de la méthode GM_LAYOUT.
- PDTA_DestMode, MODE_V43 : cette balise détermine si le picture.datatype retournera uniquement des
bitmaps de format standard (PMODE_V42) ou s'il renvoie des bitmaps dont l'appelant ne doit pas spéculer sur
la disposition de la mémoire (PMODE_V43). S'il est défini sur "PMODE_V43", vous ne devez pas examiner le
contenu du bitmap que vous recevez de le picture.datatype.
- PDTA_UseFriendBitMap, TRUE : si la valeur est TRUE, toutes les allocations de bitmap faites
par le picture.datatype seront faites avec un bitmap ami comme référence.
- PDTA_NumColors : nombre de couleurs utilisées par l'image.
- PDTA_NumSparse : nombre de stylos pour l'image (fixé par l'application).
- PDTA_SparseTable, (UBYTE *) : tableau de stylos.
- PDTA_DestBitMap : NewDTObject ne fournit pas de bitmap, celui-ci n'est accessible qu'après appel
à la méthode DTM_PROCLAYOUT. Voici une des erreurs les plus courantes.
- OBP_Precision, PRECISION_EXACT : précision à utiliser pour obtenir les couleurs.
- PDTA_Remap : il doit être renseigné (que ce soit à TRUE ou FALSE suivant ce qui est souhaité),
sinon ça peut poser des problèmes pour obtenir le bitmap.
1.2.2 L'accesseur GetDTAttrs
Il permet de récupérer les informations de l'image contenues dans la structure associée BitMapHeader,
ainsi que les registres de couleurs de l'image d'origine. Si nous souhaitons afficher une image sur un
écran propriétaire, nous ne pouvons ouvrir ce dernier dans les bonnes dimensions qu'après avoir obtenu
les informations sur l'image. Cette fonction GetDTAttrs nous donne ces informations, comme la palette de
couleurs et les dimensions de l'objet.
1.2.3 DoMethod DTM_FRAMEBOX : FrameBox
L'organisation objet des datatypes autorise l'appel de méthodes sur notre instance de datatype. La solution
retenue, comme dans MUI, s'appelle DoMethod à laquelle on indique la méthode (hum...) à exécuter.
1.2.4 DoMethod DTM_PROCLAYOUT
NewDTObject avait préparé notre image en reconnaissant le format, en invoquant la bonne classe fille
et en stockant les propriétés de l'image. Et bien l'opération PROCLAYOUT révèle tout cela en réajustant
l'image aux conditions spécifiées et en récupérant l'image aux couleurs converties et sa palette. Pour
rappel, si la balise PDTA_Screen est passée à NewDTObject, c'est la palette de cet écran qui devient
référente. D'autre part, c'est la valeur de la balise booléenne PDTA_Remap qui commande ou non la conversion
des couleurs.
L'appel à cette méthode est inévitable ! Olaf Barthel confirme que le picture.datatype Picasso fonctionne
conformément au datatype d'origine. En revanche, il semble que le datatype CyberGraphX permet d'accéder
au bitmap sans utiliser DTM_PROCLAYOUT. Sachez qu'avec les datatypes, on ne peut présager de rien et
surtout pas du type des datatypes et des versions installés chez l'utilisateur !
Après le PROCLAYOUT, on peut donc accéder au DestBitMap qui contient désormais l'image, ou en cas d'échec
à PDTA_BitMap mais alors rien n'est garanti quant à l'état de l'image (surtout si NewDTObject avait été
invoqué avec FreeSourceBitMap à TRUE).
1.2.5. Obtention du bitmap
Nous avons abordé la fonction GetDTAttrs mais après l'opération de "layout", nous pouvons en plus accéder
à la fonction ReadPixelArray qui ouvre de nouveaux horizons. Avant la V43, il n'était pas possible de
récupérer les données 24 bits d'un bitmap, la meilleure restitution étant le HAM8 pour l'AGA.
Depuis la version V43 introduite par CyberGraphX, l'accès au tampon mémoire du bitmap est autorisé,
et cela grâce à la méthode PDTA_READPIXELARRAY (la version de Picasso96 ne la gère pas, cependant).
La méthode PDTM_READPIXELARRAY permet, en effet, de récupérer le tampon mémoire d'une image sous la
forme d'un tableau de pixels, ce qui peut s'avérer primordial dans certains cas.
Ces différentes manières d'accéder à l'image soulèvent un problème concernant le terme de bitmap :
on note une différence entre bitmap système et bitmap CyberGraphX. Il est, en effet, possible d'avoir un
pointeur sur un bitmap sans pouvoir l'utiliser directement car on n'est pas censé savoir comment il est
structuré. Encore une fois : prudence !
La sous-classe du picture.datatype (IFF, JPEG...) délivre l'image comme un bitmap alloué par AllocBitMap,
et à moins qu'il ne soit réarrangé, il sera libéré avec un FreeBitMap(). Les bitmaps standards doivent
être en mémoire Chip, mais un datatype intelligent ne chargera pas toutes les données en mémoire tant
qu'il n'a pas reçu l'appel à sa méthode GM_LAYOUT duquel il peut obtenir un pointeur sur l'écran de destination.
1.2.6 Copie du bitmap
Ça y est, notre image est reconnue, chargée, traitée et moulinée. Encore faut-il l'afficher, n'oublions
pas que c'est la raison pour laquelle nous nous sommes donnés tant de mal ! Il n'y a qu'à copier les données
lues dans un bitmap écran de façon classique, via le RastPort bien sûr.
1.2.7 DisposeDTObject
Si l'on reprend la terminologie objet, NewDTObject s'apparente au constructeur et crée une instance de
datatype. Lorsque le travail est effectué, un appel à son pendant, le destructeur DisposeDTObject, permet
de libérer proprement les ressources allouées.
1.3 Évolution des datatypes
C'est certainement la démocratisation des cartes graphiques qui a poussé à la réalisation de la version
43 des datatypes. On ne peut que s'en réjouir, même si cela a apporté des divergences. Il serait bon
dans l'avenir qu'une future version pousse les différents acteurs à se conformer à des spécifications
communes et écrites de façon détaillée.
Afin d'illustrer à nouveau le mécanisme d'ouverture d'une image sur le Workbench, prenons ici un exemple
d'une image GIF. Des tests effectués par Matthias Scheler en 1996, sur un A4000/040 en AGA, nous permettent
de revenir brièvement sur le fonctionnement interne des datatypes.
Avec la version 42 (15 secondes) :
- Le gif.datatype charge l'image chunky.
- Il la convertit en format planaire.
- Le picture.datatype convertit l'image en chunky pour pouvoir procéder à une conversion des couleurs.
- Le picture.datatype applique l'affectation des couleurs.
- Le picture.datatype convertit l'image en planaire pour pouvoir l'afficher.
Avec la version 43 (8 secondes ) :
- Le gif.datatype charge l'image chunky.
- Le picture.datatype applique l'affectation des couleurs.
- Le picture.datatype convertit l'image en planaire pour pouvoir l'afficher.
1.4 Notes techniques sur les palettes
Comme pour les bitmaps, on ne sera pas surpris des nombreuses possibilités d'accéder aux palettes de
couleurs. Ces informations proviennent d'un message d'Olaf Barthel.
Le picture.datatype initialise toujours PDTA_CRegs, en concordance avec PDTA_NumColors. Chaque sous-classe
doit remplir, en toute rigueur, les balises PDTA_GRegs et PDTA_ColorRegisters (toutes ne le font pas...
ça serait trop facile !).
Voici la distinction entre les trois tables :
- PDTA_CRegs : palette de l'image originale telle qu'elle est lue par son datatype (ILBM, GIF...).
Si on charge une image sans demander au picture.datatype de recalculer les couleurs pour en utiliser moins
ou pour utiliser les couleurs d'un écran spécifique, c'est la table à utiliser avec SetRGB32() avant
d'afficher l'image.
- PDTA_GRegs : le datatype qui lit l'image (exemple : ILBM) doit copier le contenu de PDTA_CRegs
dans cette table. Le picture.datatype doit changer cette table plus tard en recalculant ou en adaptant les
couleurs à celles d'un écran donné, avec éventuellement moins de couleurs. Le "G" dans PDTA_GRegs signifie
"Got" ou obtenu, en substitution à la palette d'origine.
- PDTA_ColorRegisters : c'est normalement une copie de la table PDTA_GRegs, avec les valeurs
des composantes 32 bits décalées de 24 bits pour donner une composante 8 bits.
2. Exemple d'affichage d'une image dans une fenêtre avec les datatypes
Le précédent chapitre sur les datatypes se destinait à présenter un maximum d'informations techniques,
collectées de-ci de-là, pour tenter de combler un manque. Mais la densité de l'article a pu le rendre
un peu indigeste. C'est pourquoi nous voyons ici un exemple qui illustre les propos principaux.
Vous trouverez en effet un programme en C pour visualiser une image dans une fenêtre du Workbench.
2.1 Composition du programme
Rien que du classique ! Vous ne serez pas dépaysé cette fois-ci. Il y a bien entendu le main(),
qui se charge ici de récupérer le nom du fichier image en unique paramètre et d'ouvrir une fenêtre sur
l'écran Workbench. On notera au passage le verrou de l'écran entourant la création de la fenêtre,
c'est plus sûr. Histoire de prendre de bonnes manières. Passons la partie affichage sur laquelle nous
reviendrons pour aborder quelque chose de primordial quand on commence à jouer avec les fenêtres :
je parle des évènements. La fonction waitInput() donne un avant-goût de leur étendue et ça reste suffisant
pour comprendre le mécanisme : on attend des évènements puis on traite chacun d'eux suivant leur intérêt
pour notre application (clic de souris, touches de fonctions, Ctrl-C...). Ensuite il est impératif
de répondre au message évènement reçu par un appel à ReplyMsg(). Puis on recommence l'attente de messages...
La fonction quit() est évidente, on l'appelle en fin de programme ou bien au moment où une erreur se produit
et qu'on ne souhaite pas continuer.
2.2 Utilisation des datatypes
On ne rappellera pas la logique d'utilisation des datatypes et les étapes pour afficher les fichiers
images. Tout ceci se trouve dans la fonction loadPictureIntoWindow() et devrait être clairement compréhensible.
Cette fonction a été écrite dans le but d'être réutilisée dans d'autres programmes. On lui indique quel est
le nom du fichier à afficher et quelle est la fenêtre concernée, elle s'occupe du reste. Bien entendu, cela
oblige à redimensionner une fenêtre qu'on a auparavant ouverte avec des dimensions arbitraires. On aurait pu
adapter la fonction pour qu'elle ouvre sa propre fenêtre à chaque fois. Il est d'ailleurs tout à fait possible
d'envisager ajouter cette création de fenêtre dans la fonction si le pointeur sur la fenêtre est NULL.
Ce serait encore mieux !
On notera que les codes d'erreur pouvant être retournés sont documentés dans le source et que les couleurs
de l'image seront adaptées aux couleurs de l'écran courant.
A vous maintenant de mettre en oeuvre ce délicat mais merveilleux système de datatypes !
/*
* Exemple d'utilisation des datatypes avec l'affichage d'une image dans
* une fenêtre du Workbench. On ne traite pas le cas où l'on souhaite
* afficher l'image dans un écran privé.
* Une fonction a été créée uniquement dans le but d'afficher un fichier
* image dans une fenêtre correctement ouverte. Ainsi, cette fonction
* devrait être réutilisable dans d'autres programmes.
*
* L'appel à la méthode FRAMEBOX ... je ne sais toujours pas à quoi ça sert !
*
* Un autre programme d'exemple sera nécessaire (si demandé) pour montrer
* comment afficher une image sur un écran privé, en affectant la palette
* de l'image à l'écran.
*/
#include <stdio.h>
#include <stdlib.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <graphics/display.h>
#include <clib/alib_protos.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>
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct Screen *screen = NULL;
struct Window *window = NULL;
// Prototypes des fonctions
static void quit(const char *msg);
static int loadPictureIntoWindow(char *pictureName, struct Window *pictureWindow);
static void waitInput(void);
/*
* loadPictureIntoWindow()
*
* Charge une image grâce aux datatypes et l'affiche dans une fenêtre
* préalablement ouverte.
* Codes d'erreur possibles :
* 0 : pas d'erreur
* 1 : impossible d'ouvrir la datatypes.library >= v39
* 2 : échec sur la création de l'objet Datatype
* 3 : impossible d'accéder aux informations de l'objet
* 4 : bitmap inaccessible
* 5 : échec de l'appel à la méthode interne PROC_LAYOUT
*/
static int loadPictureIntoWindow(char *pictureName, struct Window *pictureWindow)
{
int err = 0;
struct Library *DataTypesBase = NULL;
struct BitMapHeader *bmhd = NULL;
struct BitMap *bitmap = NULL;
struct FrameInfo fri;
Object *dtype = NULL;
struct BitMapHeader *bmh = NULL;
int width, height, depth;
int res;
DataTypesBase = (struct Library *)OpenLibrary("datatypes.library", 39);
if (DataTypesBase == NULL){
err = 1;
}else{
/* 1. Création du datatype */
dtype = NewDTObject(pictureName,
PDTA_DestMode, PMODE_V43,
PDTA_Screen, pictureWindow->WScreen,
PDTA_Remap, TRUE,
PDTA_DitherQuality, 1,
TAG_END);
if (dtype == NULL){
err = 2;
}else{
/* 2. Récupération des informations */
res = GetDTAttrs(dtype,
PDTA_BitMapHeader, (ULONG)&bmh,
TAG_DONE);
// Le résultat attendu est le nombre de balises correctement traitées
if (res != 1){
err = 3;
}else{
/* 3. Adaptation des dimensions de la fenêtre */
int windowWidth;
int windowHeight;
width = bmh->bmh_Width;
height = bmh->bmh_Height;
windowWidth = bmh->bmh_Width + pictureWindow->BorderLeft + pictureWindow->BorderRight;
windowHeight = bmh->bmh_Height + pictureWindow->BorderTop + pictureWindow->BorderBottom;
ChangeWindowBox(pictureWindow, 20, 20, windowWidth, windowHeight);
/* 4. Copie du contexte graphique dans le bitmap de la fenêtre */
DoMethod(dtype,DTM_FRAMEBOX,NULL,&fri,&fri,sizeof(struct FrameInfo),0);
if(fri.fri_Dimensions.Depth>0){
if(DoMethod(dtype, DTM_PROCLAYOUT, NULL, 1)){
res = GetDTAttrs(dtype,
PDTA_DestBitMap, &bitmap,
PDTA_BitMapHeader, (ULONG)&bmhd,
TAG_DONE);
if ((bitmap != NULL) && (res == 2)){
BltBitMapRastPort(bitmap,0,0,pictureWindow->RPort,pictureWindow->BorderLeft,
pictureWindow->BorderTop,bmhd->bmh_Width,bmhd->bmh_Height,0xc0);
}else{
err = 4;
}
}else{
err = 5;
}
}
}
DisposeDTObject(dtype);
}
CloseLibrary(DataTypesBase);
}
return err;
}
/*
* Code du programme principal
*
* Il doit :
* - initialiser un écran (ouverture ou récupération du Workbench)
* - ouvrir une fenêtre capable de recevoir un bitmap
* - charger l'image dans la fenêtre
*
* Si l'écran support est le Workbench alors il faut adapter les couleurs
* de l'image à charger à la palette de l'écran Workbench.
* Si un écran propriétaire est ouvert, on peut récupérer la palette
* de l'image.
*/
int main(int argc, char*argv[]){
char *filename;
int resCreate;
// Traitement des arguments
if (argc != 2){
printf("Usage : dtype nom_image\n");
return(1);
}
filename = argv[1];
// Ouverture des bibliothèques Intuition et Graphics
IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0);
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 40L);
if((IntuitionBase == NULL) || (GfxBase == NULL)){
quit("Problème d'init des libs\n");
}
// Verrou de l'écran Workbench pour que la fenêtre s'ouvre sur lui
screen = LockPubScreen("Workbench");
if (screen == NULL){
quit("Impossible d'accéder à l'écran Workbench\n");
}
// Ouverture d'une fenêtre avec une taille arbitraire
window = OpenWindowTags(NULL,
WA_Left, 20,
WA_Top, 20,
WA_Width, 200,
WA_Height, 200,
WA_IDCMP, IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_ACTIVEWINDOW,
WA_Title, (ULONG)"Utilisation des datatypes",
WA_CustomScreen, (ULONG)screen,
WA_SizeGadget, FALSE,
WA_DragBar, TRUE,
WA_DepthGadget, TRUE,
WA_CloseGadget, FALSE,
WA_Backdrop, FALSE,
WA_ReportMouse, FALSE,
WA_Borderless, FALSE,
WA_Activate, TRUE,
WA_RMBTrap, TRUE,
WA_SimpleRefresh, FALSE,
TAG_DONE);
if (!window){
quit("Impossible d'ouvrir la fenêtre\n");
}
UnlockPubScreen(NULL, screen);
SetWindowTitles(window, (APTR) -1, "guru-meditation.net aime les datatypes");
// On peut afficher une image dans la fenêtre
resCreate = loadPictureIntoWindow(filename, window);
if (resCreate != 0){
quit("Erreur dans le chargement de l'image\n");
}
// Attente d'une touche d'échappement ou d'un clic souris
waitInput();
quit(NULL);
}
/*
* quit()
* Fonction de nettoyage des ressources
*/
static void quit(const char *msg){
if (msg != NULL){
printf("%s\n",msg);
}
if (window != NULL){
CloseWindow(window);
}
if (IntuitionBase != NULL){
CloseLibrary((struct Library *)IntuitionBase);
}
if (GfxBase != NULL){
CloseLibrary((struct Library *)GfxBase);
}
exit(0);
}
/*
* waitInput
* Gère les évènements de la fenêtre.
*/
static void waitInput(void)
{
BOOL exitloop = FALSE;
ULONG waitsigs;
ULONG portsig;
struct IntuiMessage *imsg = NULL;
portsig = 1L << window->UserPort->mp_SigBit;
while(!exitloop)
{
waitsigs = Wait(portsig); // Wait ne retourne jamais 0
while (imsg = (struct IntuiMessage *)GetMsg(window->UserPort))
{
// switch puis reply au message
switch(imsg->Class)
{
case IDCMP_RAWKEY:
switch(imsg->Code)
{
/* ESC */
case 0x45:
/* Space */
case 0x40:
exitloop = TRUE;
default:
break;
}
case IDCMP_MOUSEBUTTONS:
switch(imsg->Code)
{
case IECODE_LBUTTON:
exitloop = TRUE;
default:
break;
}
}
ReplyMsg((struct Message *)imsg);
}
}
}
|
3. Exemple d'affichage d'une image sur un écran privé
Le chapitre précédent montrait comment utiliser les datatypes pour afficher une image sur le Workbench
avec une adaptation des couleurs aux siennes. Cela répondra certainement aux cas les plus courants,
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.
3.1 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'une suite de triplets RGB (dont le nombre est donné par le drapeau
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, etc.). Cela permet juste de gagner un peu de place et de se
concentrer sur l'essentiel.
3.2 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 la lecture de données au format chunky.
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 informations 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("Échec 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;
}
|
4. Récupérer une image en tant que tampon mémoire RGB
Voici comment obtenir un tampon mémoire RGB par lecture d'une image par les datatypes.
4.1 Possibilités et limitations
Les datatypes vont finir par ne plus avoir de secret ! Ce nouveau chapitre traite d'un nouvelle manière
de manipuler les datatypes puisqu'il s'agit de la récupération des données d'une image dans un
tampon mémoire de type RGB ou apparenté. Cette fonctionnalité ouvre de nouvelles perspectives, pour
charger des textures ou obtenir des images destinées à être retravaillées.
Traditionnellement, les datatypes permettaient uniquement de récupérer un bitmap système. A partir de
la picture V43, il en est autrement (bien que ça puisse dépendre des implémentations, mais ça on y est
habitué...).
Le programme proposé :
- Requiert CyberGraphX et Picasso96 (en émulation CyberGraphX).
- Fonctionne sous AmigaOS 68k et MorphOS (voir ci-après les limitations).
- A été compilé avec GCC MorphOS, SAS/C, vbcc (problème du DoMethodA).
Il a été testé sur plusieurs systèmes et pour l'affichage des images 8 bits ou inférieur, cela
ne fonctionne pas en MorphOS 1.2 et 1.3 (picture.datatype 50.1 et 50.3), alors que ça a été testé
avec succès sur :
- AmigaOS 3.9 (BoingBag 2) - picture.datatype 45.17 - CyberGraphX.
- AmigaOS 3.9 - picture.datatype 44.15 - CyberGraphX.
- AmigaOS 3.1 (UAE) - picture.datatype 43.41 - Picasso96.
Je penche pour un problème de picture.datatype... j'attendais la réponse de quelqu'un "qui s'y connaît"
mais après plusieurs semaines, je crois qu'il est préférable de mettre en ligne cet article, si on veut
vous en faire bénéficier. Quitte à apporter un rectificatif par la suite.
4.2 Principe et voie détournée
Techniquement, on travaille ici uniquement sur le Workbench : une fenêtre est ouverte aux dimensions de
l'image puis on copie le tampon mémoire obtenu. Le programme gère la transparence (utilisation de
la couche alpha), c'est-à-dire peut préciser si l'on veut obtenir un tampon mémoire RGB ou RGBA.
D'autres personnes (David Cadenas ainsi que je ne sais plus qui :)) ont adopté une approche différente
qui mène au même résultat, en récupérant un bitmap de manière traditionnelle puis en récupérant son
contenu RGB avec ReadPixelArray.
#include <stdlib.h>
#include <stdio.h>
#include <amiga-align.h>
#include <datatypes/pictureclass.h>
#include <clib/alib_protos.h>
#include <proto/datatypes.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <cybergraphx/cybergraphics.h>
#include <proto/cybergraphics.h>
#include <default-align.h>
#ifdef __SASC
#include <exec/memory.h>
#endif
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *CyberGfxBase;
struct Library *DataTypesBase;
struct Picture{
int width;
int height;
int depth;
unsigned char *pixels;
};
struct Picture * AllocPicture(int width, int height, int depth)
{
struct Picture *pt = NULL;
int bytes;
bytes = (depth + 7) / 8;
pt = AllocVec( sizeof(struct Picture), MEMF_ANY|MEMF_CLEAR );
if (pt){
pt->pixels = AllocVec(width * height * bytes, MEMF_ANY);
pt->width = width;
pt->height = height;
pt->depth = depth;
if (pt->pixels == NULL){
FreeVec(pt);
pt = NULL;
}
}
return pt;
}
void FreePicture(struct Picture *pt)
{
if( pt ) {
FreeVec( pt->pixels );
pt->pixels = NULL;
FreeVec( pt );
}
}
struct Picture * LoadPicture(STRPTR filename, int alpha)
{
struct Picture *pt = NULL;
Object *dto = NULL;
ULONG nb;
struct BitMapHeader *bmh;
struct pdtBlitPixelArray bpa;
if( !filename ) return NULL;
DataTypesBase = OpenLibrary("datatypes.library", 39);
if( DataTypesBase ) {
dto = NewDTObject( filename,
DTA_GroupID, GID_PICTURE,
PDTA_DestMode, PMODE_V43,
PDTA_Remap, FALSE,
TAG_END );
if (dto){
nb = GetDTAttrs( dto,
PDTA_BitMapHeader, (ULONG)&bmh,
TAG_END );
if (nb == 1){
int resread;
printf("Dimensions : %dx%dx%d\n", bmh->bmh_Width, bmh->bmh_Height, bmh->bmh_Depth );
/* Allocation de la structure Picture et du tampon mémoire */
pt = AllocPicture(bmh->bmh_Width, bmh->bmh_Height, (3 + alpha) * 8 );
if (pt){
bpa.MethodID = PDTM_READPIXELARRAY;
bpa.pbpa_PixelData = pt->pixels;
bpa.pbpa_PixelFormat = alpha?PBPAFMT_RGBA:PBPAFMT_RGB;
bpa.pbpa_PixelArrayMod = bmh->bmh_Width * (3 + alpha);
bpa.pbpa_Left = 0;
bpa.pbpa_Top = 0;
bpa.pbpa_Width = bmh->bmh_Width;
bpa.pbpa_Height = bmh->bmh_Height;
resread = DoMethodA( dto, (Msg)&bpa );
}else{
printf("Impossible d'allouer la structure Picture\n");
}
}else{
printf("Impossible de lire les informations de l'image\n");
}
DisposeDTObject( dto );
}else{
printf("Échec dans le chargement du fichier\n");
}
CloseLibrary( DataTypesBase );
}else{
printf("Impossible d'ouvrir la datatypes.library\n");
}
return pt;
}
int main(int argc, char **argv)
{
struct Screen *WbScreen;
struct Window *win;
struct Picture *picture = NULL;
int alpha = 0;
if (argc != 2){
printf("Usage : picture image\n");
}else{
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39L);
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39L);
CyberGfxBase = (struct Library *)OpenLibrary("cybergraphics.library", 39L);
if ((GfxBase) && (CyberGfxBase) && (IntuitionBase)){
WbScreen = LockPubScreen("Workbench");
if (WbScreen){
/* Chargement de l'image et affichage dans une fenêtre */
picture = LoadPicture(argv[1], alpha);
if (picture){
if (picture->depth >= 24){
win = OpenWindowTags(NULL,
WA_Left, 0,
WA_Top, 0,
WA_InnerWidth, picture->width,
WA_InnerHeight, picture->height,
WA_CustomScreen, (ULONG)WbScreen,
WA_SizeGadget, FALSE,
WA_CloseGadget, TRUE,
WA_RMBTrap, TRUE,
WA_Activate, TRUE,
WA_Title, (unsigned long)"Datatype (READPIXELARRAY method)",
WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_ACTIVEWINDOW| IDCMP_IDCMPUPDATE | IDCMP_CHANGEWINDOW | \
IDCMP_NEWSIZE | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
WA_Flags, WFLG_SIZEGADGET | WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET, // | WFLG_REPORTMOUSE,
TAG_DONE);
UnlockPubScreen(NULL, WbScreen);
if (win){
(void)WritePixelArray(picture->pixels,
0,0,
picture->width * (picture->depth / 8),
win->RPort,
win->BorderLeft, win->BorderTop,
picture->width,
picture->height,
alpha ? RECTFMT_RGBA : RECTFMT_RGB);
Delay(200);
CloseWindow(win);
}
}else{
printf("Le programme ne traite que les images 24 bits\n");
}
FreePicture(picture);
}
}
}
/* Libération des bibliothèques */
if (CyberGfxBase){
CloseLibrary((struct Library *)CyberGfxBase);
}
if (GfxBase){
CloseLibrary((struct Library *)GfxBase);
}
if (IntuitionBase){
CloseLibrary((struct Library *)IntuitionBase);
}
}
return 0;
}
|
|