Obligement - L'Amiga au maximum

Vendredi 06 juin 2025 - 12:19  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


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 :
  1. 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.
  2. 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) :
  1. Le gif.datatype charge l'image chunky.
  2. Il la convertit en format planaire.
  3. Le picture.datatype convertit l'image en chunky pour pouvoir procéder à une conversion des couleurs.
  4. Le picture.datatype applique l'affectation des couleurs.
  5. Le picture.datatype convertit l'image en planaire pour pouvoir l'afficher.
Avec la version 43 (8 secondes ) :
  1. Le gif.datatype charge l'image chunky.
  2. Le picture.datatype applique l'affectation des couleurs.
  3. 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;
}



[Retour en haut] / [Retour aux articles]