Exemple d'affichage d'une image sur le Workbench

Un article de GuruMed.

Affichage d'une image dans une fenêtre avec les datatypes

Par Corto

Le précédent article sur les datatypes se destinait à présenter un maximum d'informations techniques, collectés 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 aujourd'hui 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.


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 lock 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 quelquechose 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.


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 un 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 infos 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 infos */
      res = GetDTAttrs(dtype,
          PDTA_BitMapHeader, (ULONG)&bmh,
          TAG_DONE);

      // Le résultat attendu est le nombre de tags correctement traités
      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 libs 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");
  }

  // Lock 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);
    }
  }
}