Obligement - L'Amiga au maximum

Dimanche 24 février 2019 - 02:14  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

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

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


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


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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Dossier : Les layers et les régions d'Intuition
(Article écrit par Max et extrait d'Amiga News Tech - octobre 1991)


Si l'on essayait de donner une définition la plus vaste possible des layers, on pourrait simplement dire que ce sont l'âme des fenêtres d'Intuition. Mais que sont-ils donc exactement et surtout, à quoi servent-ils ?

D'après le dictionnaire anglais-français qui traîne toujours sur un coin de mon bureau, le mot "layer" signifie "couche". Rien à voir cependant avec Pampers, même si le but est identique : éviter les fuites... Car en effet, les layers sont le coeur même de tout le système de "clipping" (détourage) du système graphique de l'Amiga. Excusez du peu.

Quèsaco ?

Pour citer ce monument de bêtise qu'est Le Livre du Graphisme sur Amiga (editions Micro Application, bien sûr), qui pour une fois disait quelque chose d'intelligent : "de même qu'un écran n'est rien d'autre qu'un ViewPort étendu, une fenêtre n'est rien d'autre qu'un layer étendu. Les layers se chargent du plus gros du travail nécessité par la gestion des fenêtres".

En fait et pour clarifier les choses, les layers sont des portions rectangulaires d'une même BitMap ; toute sortie graphique (dessin, texte...) dans un layer est limitée aux dimensions de ce layer. Les layers peuvent être déplacés, agrandis ou rétrécis, passés les uns derrière les autres, etc. Exactement comme les fenêtres d'Intuition, en fait.

Il peut arriver qu'une opération mettant en action un layer, révèle un autre layer qui était caché sous lui, ou au contraire en dissimule une partie. Dans ce cas, un drapeau est positionné pour le layer concerné, qui indique à l'application qu'il est temps de rafraîchir le contenu de ce layer. Nous retrouvons là, encore une fois, la base des messages IDCMP de type REFRESHWINDOW d'Intuition.

Utilisation des layers

Les layers sont à ce point important pour le système, qu'une bibliothèque particulière leur est dédiée. Il s'agit bien sûr de la layers.library, que l'on ouvre de manière tout à fait conventionnelle :

C

Cette bibliothèque se charge de créer les layers, de les manipuler (déplacement, changement de taille, superposition, etc.), mais n'y effectue aucune sortie graphique. C'est bien entendu la graphics.library qui s'en charge, ce qui explique que ces deux bibliothèques soient souvent utilisées de concert. De même, tous les calculs inhérents au détourage sont gérées par la graphics.library, la layers.library ne se chargeant que de la mise en place des zones de détourage à l'intérieur des layers. Nous aurons l'occasion d'en reparler un peu plus loin.

Lorsque l'on crée un layer à l'aide de la fonction adéquate, un RastPort lui est automatiquement associé, qui permet l'utilisation des primitives graphiques dans ce layer. Au risque de me répéter, toutes les opérations graphiques dans ce RastPort sont alors détourées à ses limites. A l'inverse, lorsque l'on crée un RastPort directement (à l'aide de la fonction InitRastPort() par exemple), aucun layer ne lui est encore associé, et les sorties graphiques ne sont pas détourées.

Tous les layers se partageant une même BitMap, se partagent également une structure Layer_Info qui aide le système à ne pas se mélanger les pédales dans tout ce foutoir (car c'en est un, croyez-moi !).

Il existe également une autre notion importante pour le détourage : les régions. Il s'agit de portions rectangulaires des layers, qui sont utilisées pour détourer les sorties graphiques à l'intérieur des layers. Cette notion peut paraître obscure et inutile ; nous verrons plus loin qu'il n'en n'est rien et quelle est leur utilité réelle.

Enfin, les layers fournissent un mécanisme de verrouillage ("Lock"), basé sur les Semaphores d'Exec, qui empêche provisoirement d'autres tâche d'accéder à ce layer et/ou à la BitMap qu'il partage.

Création de layers

Il existe trois types de layers, que vous reconnaîtrez sûrement si vous êtes un habitué des fenêtres.

Layers Simple Refresh

Toutes les sorties graphiques sont limitées à la (aux) parties visibles du layer. Ce qui "sort" du layer est simplement perdu ; lorsqu'une partie du layer redevient visible, un drapeau est positionné indiquant que l'application doit redessiner cette partie.

Layers Smart Refresh

Quand une sortie graphique s'effectue dans une partie invisible du layer, ou quand un autre layer vient le recouvrir, le système alloue dynamiquement une mémoire tampon dans laquelle il sauve les informations cachées ; plus tard, lorsque ce layer redeviendra visible, le système copiera cette mémoire tampon dans le layer.

Layers SuperBitMap

Le principe est identique aux layers Smart Refresh, si ce n'est que le système n'alloue aucune mémoire tampon ; c'est au contraire l'application qui fournit une BitMap particulière qui servira de mémoire tampon. Cette BitMap peut être plus grande que le layer lui-même, ce qui permet des défilements.

De plus, chacun des types de layers ci-dessus peut également être "BackDrop", c'est-à-dire qu'il apparaîtra toujours derrière tous les autres layers et ne pourra pas être superposé à d'autres layers.

Les constantes C correspondant à ces différents types de layers sont définies dans graphics/layers.h et se nomment respectivement LAYERSIMPLE, LAYERSMART, LAYERSUPER et LAYERBACKDROP.

Pour créer un layer, il faut d'abord disposer une structure Layer_Info. Si vous travaillez avec Intuition, chaque écran possède sa propre structure Layer_Info que vous pouvez utiliser ; sinon, il faudra créer la vôtre au moyen de la fonction NewLayerInfo() :

C

NewLayerInfo() alloue une structure Layer_Info et toutes les structures qui lui sont rattachées. Elle initialise de plus certaines paramètres par défaut des layers. Pour libérer cette mémoire, il faudra donc utiliser le pendant de NewLayerInfo(), à savoir DisposeLayerInfo() :

C

A partir de là, vous pouvez créer tous les layers que vous voudrez (du moins, tant que la mémoire libre le permettra), en utilisant l'une des fonctions CreateUpfrontLayer() ou CreateBehindLayer(), suivant que le nouveau layer doit apparaître devant ou derrière tous les autres (layers BackDrop non compris). Ces deux fonctions s'utilisent exactement de la même manière.

CreateUpfrontLayer(li,bm,x1,y1,x2,y2,t,sb)
CreateBehindLayer(li,bm,x1,y1,x2,y2,t,sb))

Les paramètres à ces deux fonctions sont :
  • li : pointeur sur la structure Layer_Info commune à tous les layers.
  • bm : pointeur sur la BitMap commune à tous les layers.
  • x1, y1, x2, y2 : coordonnées du layer.
  • t : type du layer.
  • sb : pointeur sur une BitMap supplémentaire, pour le cas où le layer est de type LAYERSUPER ; NULL sinon.
Ces fonctions renvoient NULL si la création du layer a échoué (généralement par manque de mémoire).

Une fois le layer créé, vous pouvez récupérer un pointeur sur son RastPort directement dans la structure Layer :

struct RastPort *rp;
rp = layer->rp;

Attention : la structure Layer_Info est définie dans graphics/layers.h ; la structure Layer est définie dans graphics/clip.h.

Une fois tous vos layers créés, vous pourrez les déplacer, les agrandir, changer leur ordre de superposition, faire défiler leur contenu (pour les layers de type LAYERSUPER uniquement) et déterminer quel layer se trouve actuellement à une coordonnée précise grâce à la batterie de fonctions que voici. Note : dans ces fonctions, le paramètre "dummy" est pour l'instant inutilisé mais doit être mis à 0 pour permettre la compatibilité avec de futures versions de la bibliothèque :
  • MoveLayer(dummy, layer, dx, dy) : déplace le layer spécifié de (dx, dy) pixels.
  • SizeLayer(dummy, layer, dx, dy) : agrandit/rétrécit le layer spécifié de (dx, dy) pixels.
  • BehindLayer(dummy, layer) : place le layer spécifié derrière tous les autres, à l'exception des layers BackDrop.
  • UpfrontLayer(dummy, layer) : place le layer spécifié devant tous les autres, sauf s'il s'agit d'un layer BackDrop.
  • MoveLayerInFrontOf(layer1, layer2) : place le layer1 devant le layer2, sauf si le layer1 est du type BackDrop.
  • ScrollLayer(dummy, layer, dx, dy) : déplace le contenu du SuperLayer spécifié de (dx, dy) pixels. Il s'agit d'un défilement logiciel, effectué au Blitter.
  • WhichLayer(layerinfo, x, y) : retourne un pointeur sur le layer se trouvant exactement sous les coordonnées (x, y) spécifiées.
  • DeleteLayer(dummy, layer) : supprime le layer spécifié du système.
Programme de démonstration des layers

Il est assez difficile de faire un programme réellement démonstratif des capacités des layers, aussi celui-ci se contente de créer un layer sur l'écran du Workbench et de le déplacer à volonté.

#include <stdlib.h>
#include <exec/types.h>
#include <intuition/screens.h>
#include <graphics/clip.h>
#include <graphics/layers.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/layers.h>

#define DUMMY 0L

/* variables gloables */
struct Library *GfxBase, *LayersBase, *IntuitionBase;

/* Prototypes */
void main(void);
void cleanExit(void);
void layer_demo(struct Layer *lay);

/* Ouverture des bibliothèques */
void main(void)
{
struct BitMap *bm;
struct RastPort *rp;
struct Layer_Info *li;
struct Layer *lay;
struct Screen WBScreen;

	if (!(IntuitionBase = OpenLibrary("intuition.library", 33L)))
		cleanExit();

	if (!(GfxBase = OpenLibrary("graphics.library", 33L)))
		cleanExit();

	if (!(LayersBase = OpenLibrary("layers.library", 0L)))
		cleanExit();

	GetScreenData( (UBYTE *)&WBScreen, sizeof(WBScreen),
	                WBENCHSCREEN, NULL);
	li = &WBScreen.LayerInfo;
	bm = &WBScreen.BitMap;

	if (!(lay = CreateUpfrontLayer(li, bm,
	                               0, 0, 199, 99,
	                               LAYERSMART, NULL)))
		cleanExit();

	rp = lay->rp;

	/*** Rend le layer visible et affiche un texte dedans */
	SetRast(rp, 2);   SetDrMd(rp, JAM1);
	Move(rp, 10, 10); Text(rp, "Et un Layer, un !", 17);
	Move(rp, 10, 20); Text(rp, "C'est une gomme !", 17);

	/*** Démo layer */
	layer_demo(lay);

	/*** Et c'est fini */
	DeleteLayer(DUMMY, lay);

	cleanExit();
}

/*
 * Fermeture des bibliothèques, retour au CLI
 */
void cleanExit(void)
{
	if (LayersBase)    CloseLibrary(LayersBase);
	if (GfxBase)       CloseLibrary(GfxBase);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
	exit(0);
}

/*
 * Démo layer (déplacement...)
 */
void layer_demo(struct Layer *lay)
{
register int ii;

	/*** Déplace le layer sur l'écran du WB (et l'efface !) */
	for (ii = 0; ii < 44; ii++) MoveLayer(DUMMY, lay,  10,   0);
	for (ii = 0; ii < 15; ii++)	MoveLayer(DUMMY, lay,   0,  10);
	for (ii = 0; ii < 44; ii++)	MoveLayer(DUMMY, lay, -10,   0);
	for (ii = 0; ii < 15; ii++)	MoveLayer(DUMMY, lay,   0, -10);
}

Sémaphores, mécanisme de verrouillage des layers et de la Layer_Info

De quoi s'agit-il Auguste ? Tout simplement et en simplifiant quelque peu, de permettre à une tâche donnée d'empêcher une autre tâche quelconque d'accéder à une zone de mémoire particulière. Bref, c'est à un véritable "verrou de la mémoire" que nous avons à faire. Ce verrou étant purement logiciel (aucun matériel n'intervenant dans l'histoire), cela suppose évidemment que la seconde tâche soit disciplinée et accepte de se soumettre à ce procédé. Ce qui est souvent le cas, parfois même à son nez et à sa barbe. Les exemples les plus frappants étant d'une part la structure IntuitionBase, que l'on peut verrouiller en appelant la fonction LockIBase() et, donc, nos layers.

La principale utilité des sémaphores est donc d'arbitrer l'accès à la mémoire entre les différentes tâches. Imaginez par exemple que vous soyiez en train d'ajouter un noeud dans une liste publique, tandis qu'au même moment, une autre tâche parcourt cette même liste à la recherche d'un autre noeud ; il y a alors une chance sur deux pour que l'insertion de votre noeud fasse se mélanger les pinceaux à l'autre tâche. Le cas est encore pire si deux tâches essaient chacune d'ajouter un noeud à une même liste en même temps. Dans tous ces cas, interdire le multitâche avec Forbid() ou même Disable() ne suffit plus, étant donné que l'autre tâche peut avoir déjà commencé son travail avant que vous ne lui coupiez les vivres. Bien sûr, lorsqu'il s'agit de données "statiques" (non modifiables) comme la ROM, un mécanisme de verrouillage devient superflu.

Les sémaphores sont entièrement basés sur le double système des listes et des signaux. Lorsqu'une tâche a obtenu avec succès un sémaphore, toutes les autres tâches qui essaieraient d'y accéder sont mises en état d'attente (avec Wait()) jusqu'à ce que celui-ci soit libéré. De cette manière, on est sûr que deux tâches n'accéderont jamais aux mêmes données en même temps.

Si le sujet vous intéresse, nous le décrirons sans doute plus avant dans ces mêmes pages, mais pour l'heure, il est temps de revenir à nos moutons, les layers, que l'on peut donc verrouiller.

La clef des champs

Cela se fait très simplement avec les fonctions adéquates de la layers.library, qui se charge toute seule comme une grande de l'interfaçage avec les sémaphores, nous libérant de tout ce travail. Il est ainsi possible de verrouiller soit un layer particulier, soit une Layer_Info complète, et donc tous les layers qui en dépendent. Par exemple, Intuition verrouille la Layer_Info de l'écran concerné lorsque vous déplacez ou agrandissez une fenêtre, ou encore lorsque vous déroulez un menu. Par autre exemple, le Workbench verrouille la Layer_Info de son écran lorsque vous déplacez une icône.

Voici listées pour vous et dans le tableau ci-dessous, les fameuses "fonctions adéquates" dont il est question dans le paragraphe précédent. Rendez-vous tout de suite après pour les explications de rigueur.
  • LockLayer() : verrouille un layer donné.
  • UnlockLayer() : déverrouille ledit layer.
  • LockLayers() : verrouille les layers d'une Layer_Info.
  • UnlockLayers() : déverrouille ces mêmes layers.
  • LockLayerInfo() : verrouille la Layer_Info elle-même.
  • UnlockLayerInfo() : déverrouille ladite Layer_Info.
Avec LockLayer(), vous verrouillez un layer donné afin qu'aucune autre tâche ne puisse y effectuer de sortie graphique en même temps que vous. Rappelons que ce verrouillage étant basé sur les sémaphores, votre tâche sera mise en état d'attente si d'aventure le layer concerné était déjà verrouillé par une autre tâche. Si vous désirez verrouiller plusieurs layers dans un même écran mais pas tous, il vous faudra entourer tous les appels à LockLayer() par une paire de LockLayerInfo() et UnlockLayerInfo(), ceci afin d'éviter un blocage intempestif du système.

LockLayers() vous permet de verrouiller en une seule fois tous les layers d'un écran donné, sans toucher à la structure Layer_Info associée. De fait, une autre tâche peut toujours la modifier, même si les layers eux-mêmes sont protégés.

Enfin, LockLayerInfo() est la plus puissante des trois fonctions de verrouillage, puisque non contente de bloquer tous les layers de l'écran, elle verrouille également la Layer_Info elle-même. De cette manière, vous êtes sûr que rien d'imprévu ne peut arriver à vos layers pendant que vous les travaillez au corps-à-corps.

Ces trois fonctions possèdent chacune leur vis-à-vis, qu'il convient d'appeler absolument lorsque le travail désiré a été effectué : n'oubliez pas que d'autres tâches peuvent attendre d'avoir le droit d'accéder au(x) layer(s) verrouillé(s).

Dernier point intéressant à noter, le fait que la graphics.library dispose de deux fonctions similaires à LockLayer() et UnlockLayer(), s'appelant respectivement LockLayerRom() et UnlockLayerRom() et qui peuvent donc permettre, dans certains cas ma foi assez limités, d'éviter d'avoir à ouvrir la layers.library.

Les régions

Cette fois-ci, tout ce qui pouvait être dit sur les layers l'a été, nous pouvons donc aborder sans plus attendre ce nouveau concept qu'est la région. La layers.library est maintenant oubliée, c'est la graphics.library qui se charge de tout (à une exception près, que nous verrons plus loin). Franchement, je n'ai jamais compris pourquoi elle ne se chargeait pas également des layers, mais bon, hein, c'est pas moi qui ai écrit le système (Dieu merci !).

Tout comme les layers, les régions font intervenir la notion de rectangle de détourage. Le principe est pourtant assez simple : alors que les layers servent à limiter les sorties graphiques à une zone particulière d'une BitMap, les régions servent à les limiter à une zone particulière d'un layer. Pourquoi donc se compliquer ainsi la vie ? N'oubliez pas que les layers peuvent se chevaucher entre eux, changer de taille, être déplacés... L'utilité des régions réside donc dans la capacité qu'elles sont de limiter les sorties graphiques aux parties visibles d'un layer donné.

Lorsque plusieurs layers se chevauchent, le système maintient et met à jour une liste des régions perdues, appelée la DamageList. Ainsi, plus tard, lorsque l'application concernée voudra rafraîchir sa fenêtre, le système limitera les sorties graphiques aux régions contenues dans cette liste, accélérant d'autant l'opération. Ceci est tout à fait transparent pour l'application, qui se contente de redessiner "bêtement" le layer entier.

La graphics.library met à notre disposition plusieurs routines de création et de gestion des régions au sein d'un layer. Celles-ci sont résumées dans le tableau ci-dessous, que je vous laisse examiner avant de continuer.

C

Enfin, la layers.library offre une seule fonction de gestion des régions, à savoir InstallClipRegion(), qui installe effectivement la région dans le layer. Tout cela semble un peu bordélique, mais c'est finalement assez logique.

Avec NewRegion(), on alloue dynamiquement et initialise une structure Region que l'on pourra par la suite lier au layer. Cette région est "vierge", c'est-à-dire qu'elle ne permet pas encore de sortie graphique. L'allocation étant dynamique, la fonction DisposeRegion() sert à libérer la mémoire utilisée.

Les fonctions suivantes, AndRectRegion(), OrRectRegion() et XorRectRegion() servent à mettre en place la région. Elles utilisent une structure supplémentaire, connue sous le nom de structure Rectangle, qui définit les coordonnées de la zone de détourage effective. Ce n'est qu'une fois l'une de ces trois fonctions appelée que la région sera opérative et pourra être installée dans le layer avec InstallClipRegion(). La différence entre ces trois fonctions tient dans la manière dont elles calculent la zone effectivement visible :
  • AndRectRegion() limite la sortie aux parties du layer visibles à la fois dans le rectangle et dans la région.
  • OrRectRegion() étend la région aux parties visibles dans le rectangle.
  • XorRectRegion() limite la sortie aux parties visibles dans le rectangle ou dans la région, mais pas dans les deux.
Arrivé ici, un schéma sera peut-être plus clair.

C

C

C

Enfin, ClearRectRegion() supprime un rectangle donné de la région considérée.

Quant aux trois dernières fonctions, à savoir AndRegionRegion(), OrRegionRegion() et XorRegionRegion(), elles agissent de la même manière que les précédentes, mais jonglent avec deux régions plutôt qu'avec une région et un rectangle.

Finalement, lorsque vous avez décidé et correctement initialisé votre région, vous l'installez dans le layer avec InstallClipRegion() - qui, rappelons-le encore une fois, fait partie de la layers.library - et enfin, toutes les sorties graphiques dans ce layer seront limitées à cette région.

Programme de mise en oeuvre des layers

Ce programme met en oeuvre trois layers : deux de type SIMPLELAYER et un de type SUPERLAYER, c'est-à-dire utilsant une BitMap qui lui est personnelle, plus grande que le layer lui-même. Une seule partie en est donc effectivement affichée et l'on peut en faire défiler le contenu, histoire de visualiser la BitMap entière. Ce layer est au départ rempli - par le biais de SetAfPt() et de RectFill() - par un motif multicolore, pour être sûr que le défilement sera bien visible...

Ce SuperLayer se superpose à un SimpleLayer dans lequel un Quix joue tout seul. Les lignes sont clippées par le système, de sorte qu'elles ne coupent pas le layer supérieur.

Enfin, le troisième layer sert uniquement à afficher un petit texte de bienvenue.

Certes, cette "Little LayersDemo" comme je l'ai appelée ne montre pas toutes les capacités de la layers.library... Il faut en effet savoir que mis à part le détourage (clipping) en lui-même, la gestion des layers est très lente (déplacement, changement de taille, rafraîchissement...), ce dont on peut de temps en temps se rendre compte au clignotement du SuperLayer et au ralentissement du Quix, d'autant plus que le multitâche est toujours actif. Mais enfin, son but était de démontrer l'utilisation des layers en dehors de la bibliothèque Intuition et de ses fenêtres, et je pense qu'il a été atteint...

/*******************************************************************\
* Démonstration des Layers sans passer par Intuition.               *
* Septembre 1991, par Max pour Amiga NewsTech.                      *
* Domaine Public.                                                   *
*********************************************************************
* Serait sans doute plus joli en utilisant le double tampon mémoire *
\*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/view.h>
#include <graphics/layers.h>
#include <graphics/clip.h>

/* Constantes */
#define SALUT1	"Little Layers Demo, par Max pour ANT"
#define SALUT2	"Bouton gauche de la souris pour quitter"

#define GFXNAME		"graphics.library"
#define LAYERSNAME	"layers.library"

#define SCR_W	320		/* Largeur de l'écran */
#define SCR_H	256		/* Hauteur de l'écran */
#define SCR_D	2		/* Profondeur */

#define LAY1_X1	0		/* Position et taille du premier layer   */
#define LAY1_Y1	0
#define LAY1_W	320
#define LAY1_H	30
#define LAY1_X2	(LAY1_X1+LAY1_W-1)
#define LAY1_Y2	(LAY1_Y1+LAY1_H-1)

#define LAY2_X1	0		/* Position et taille du second layer    */
#define LAY2_Y1	40
#define LAY2_W	320
#define LAY2_H	216
#define LAY2_X2	(LAY2_X1+LAY2_W-1)
#define LAY2_Y2	(LAY2_Y1+LAY2_H-1)

#define LAY3_X1	40		/* Position et taille du troisième layer */
#define LAY3_Y1	100
#define LAY3_W	240
#define LAY3_H	100
#define LAY3_X2	(LAY3_X1+LAY3_W-1)
#define LAY3_Y2	(LAY3_Y1+LAY3_H-1)

#define SUPER_W	320		/* Largeur de la BitMap du SuperLayer    */
#define SUPER_H	256		/* Hauteur de la BitMap du SuperLayer    */

/* Prototypes */
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/layers.h>

void main(void);
void LayersDemo(void);

BOOL initScreen(void);
void freeScreen(void);

BOOL initLayers(void);
void freeLayers(void);

void doLayer1(void);
void doLayer2(void);
void doLayer3(void);

/* variables globales pour l'affichage graphique */
/* pas besoin de RastPort ici, on utilisera celui des layers */
struct View view, *oldview = NULL;
struct ViewPort vport;
struct RasInfo rinfo;
struct BitMap bmap;
struct ColorMap *cmap = NULL;
USHORT Palette[] = { 0x0000, 0x000f, 0x0f00, 0x0fff };

/* variables globales pour la gestion des layers */
struct Layer_Info *linfo = NULL;
struct Layer *lay1 = NULL;
struct Layer *lay2 = NULL;
struct Layer *lay3 = NULL;

struct BitMap superbmap;	/* Ca, c'est pour le SuperLayer */
USHORT motif[] = {			/* Un zouli 'A' en 4 couleurs   */
	0x0002,0x0002,0x0002,0x0002,0x0002,0x0012,0x0022,0x0042,
	0x0082,0x0102,0x0002,0x07F2,0x0802,0x1002,0x2002,0x0000,
	0x0002,0x0006,0x000E,0x001E,0x003E,0x007E,0x00EE,0x01CE,
	0x038E,0x070E,0x0FFE,0x1FFE,0x380E,0x700E,0xE00E,0x0000
};

/* Autres variables globales */
struct GfxBase *GfxBase = NULL;
struct Library *LayersBase = NULL;

/*****************************************************************\
* main()                                                          *
\*****************************************************************/
void main(void)
{
	if (GfxBase = (struct GfxBase *)OpenLibrary(GFXNAME,  33L))
	{	if (LayersBase = OpenLibrary(LAYERSNAME, 33L))
		{	if (initScreen())
				LayersDemo();
			freeScreen();
			CloseLibrary(LayersBase);
		}
		CloseLibrary((struct Library *)GfxBase);
	}
	exit(0);
}

/*****************************************************************\
* initScreen()                                                    *
\*****************************************************************/
BOOL initScreen(void)
{
register i;

	/* Alloue et initialise une ColorMap */
	if (!(cmap = GetColorMap(1L << 2)))
		return(FALSE);
	memcpy(cmap->ColorTable, Palette, sizeof(Palette));

	/* Prépare le View */
	InitView(&view);
	view.ViewPort = &vport;

	/* Prépare le ViewPort */
	InitVPort(&vport);
	vport.DWidth   = 320;
	vport.DHeight  = 256;
	vport.RasInfo  = &rinfo;
	vport.ColorMap = cmap;

	/* Prépare la BitMap principale */
	InitBitMap(&bmap, 2, 320, 256);

	/* Alloue les bitplanes */
	for (i = 0; i < 2; i++)
	{	if (!(bmap.Planes[i] = AllocRaster(320, 256)))
			return(FALSE);
		BltClear(bmap.Planes[i], RASSIZE(320, 256), 1);
	}

	/* Prépare le RasInfo */
	rinfo.BitMap   = &bmap;
	rinfo.RxOffset = 0;
	rinfo.RyOffset = 0;

	/* Sauve le View actif (celui d'Intuition) */
	oldview = GfxBase->ActiView;

	/* Construit l'affichage */
	MakeVPort(&view, &vport);
	MrgCop(&view);
	LoadView(&view);
	WaitTOF();

	return(TRUE);
}

/*****************************************************************\
* freeScreen()                                                    *
\*****************************************************************/
void freeScreen(void)
{
register i;

	if (oldview)	/* si non null, notre view a été chargé */
	{	LoadView(oldview);
		WaitTOF();
		FreeVPortCopLists(&vport);
		FreeCprList(view.LOFCprList);
	}

	if (cmap)
		FreeColorMap(cmap);

	for (i = 0; i < 2; i++)
	{	if (bmap.Planes[i])
			FreeRaster(bmap.Planes[i], 320, 256);
	}
}

/*****************************************************************\
* LayersDemo() - routine principale de démonstration des layers   *
\*****************************************************************/
void LayersDemo(void)
{
register UBYTE *ciaapra = (UBYTE *)0xbfe001;

	if (initLayers())
	{	while (*ciaapra & 0x40)
		{	doLayer1();	/* Gère le 1er layer  */
			doLayer2();	/* Gère le layer quix */
			doLayer3();	/* Gère le SuperLayer */
			WaitTOF();
		}
	}
	freeLayers();
}

/*****************************************************************\
* initLayers() - Crée et initialise les 3 layers                  *
\*****************************************************************/
BOOL initLayers(void)
{
register SHORT i;

	/* Alloue la structure Layer_Info */
	if (!(linfo = NewLayerInfo()))
		return(FALSE);

	/* Crée le premier layer (bleu) */
	if (!(lay1 = CreateBehindLayer(linfo, &bmap,
	             LAY1_X1, LAY1_Y1, LAY1_X2, LAY1_Y2,
	             LAYERSIMPLE, NULL)))
		return(FALSE);

	SetRast(lay1->rp, 1);
	SetDrMd(lay1->rp, JAM1);
	SetAPen(lay1->rp, 3);

	Move(lay1->rp, (LAY1_W-(sizeof(SALUT1)-1)*8)/2, 14);
	Text(lay1->rp, SALUT1, sizeof(SALUT1)-1);

	Move(lay1->rp, (LAY1_W-(sizeof(SALUT2)-1)*8)/2, 24);
	Text(lay1->rp, SALUT2, sizeof(SALUT2)-1);

	/* Crée le second layer (blanc) */
	if (!(lay2 = CreateBehindLayer(linfo, &bmap,
	             LAY2_X1, LAY2_Y1, LAY2_X2, LAY2_Y2,
	             LAYERSIMPLE, NULL)))
		return(FALSE);

	SetRast(lay2->rp, 3);
	SetDrMd(lay2->rp, JAM1);

	/* Crée le troisième layer (rouge) de type LAYERSUPER */
	InitBitMap(&superbmap, 2, SUPER_W, SUPER_H);
	for (i = 0; i < 2; i++)
	{	if (!(superbmap.Planes[i] = AllocRaster(SUPER_W, SUPER_H)))
			return(FALSE);
		BltClear(superbmap.Planes[i], RASSIZE(SUPER_W,SUPER_H), 1);
	}

	if (!(lay3 = CreateUpfrontLayer(linfo, &bmap,
	             LAY3_X1, LAY3_Y1, LAY3_X2, LAY3_Y2,
	             LAYERSUPER, &superbmap)))
		return(FALSE);

	SetAfPt(lay3->rp, motif, -4); /* motif de 16 lignes de haut */
	SetAPen(lay3->rp, 255);
	SetBPen(lay3->rp, 0);
	SetDrMd(lay3->rp, JAM2);
	RectFill(lay3->rp, 0, 0, SUPER_W-1, SUPER_H-1);

	return(TRUE);
}

/*****************************************************************\
* freeLayers() - Libère la mémoire des 3 layers et du Layer_Info  *
\*****************************************************************/
void freeLayers(void)
{
register SHORT i;

	if (lay3)	DeleteLayer(NULL, lay3);
	if (lay2)	DeleteLayer(NULL, lay2);
	if (lay1)	DeleteLayer(NULL, lay1);

	for (i = 0; i < 2; i++)
	{	if (superbmap.Planes[i])
				FreeRaster(superbmap.Planes[i], SUPER_W, SUPER_H);
	}

	if (linfo)	DisposeLayerInfo(linfo);
}

/*****************************************************************\
* doLayer1() - Gère le premier Layer                              *
\*****************************************************************/
void doLayer1(void)
{
	/* Rien à faire !!! */
}

/*****************************************************************\
* doLayer2() - Gère le Quix dans le second Layer                  *
\*****************************************************************/
void doLayer2(void)
{
/* Données statiques pour le Quix */
static new = 0, old = 0, full = FALSE;
static SHORT x1[32], y1[32], x2[32], y2[32];
static SHORT veloc[4] = {  -4,  5,    3,  -7 };
static SHORT start[4] = { 110, 25,  160,  74 };
static SHORT max[4] = { LAY2_W+20, LAY2_H+20,
                        LAY2_W+20, LAY2_H+20 };

register SHORT i, temp;

	for (i = 0; i < 4; i++)
	{	temp = start[i] + veloc[i];
		if (temp >= max[i])
		{	temp = (max[i] * 2) - start[i] - veloc[i];
			veloc[i] = -veloc[i];
		}
		else if (temp < -20)
		{	temp = -20;
			veloc[i] = -veloc[i];
		}
		start[i] = temp;
	}
	if (full)
	{	SetAPen(lay2->rp, 3);
		Move(lay2->rp, x1[old], y1[old]);
		Draw(lay2->rp, x2[old], y2[old]);
		old++;
		old %= 32;
	}
	x1[new] = start[0];
	y1[new] = start[1];
	x2[new] = start[2];
	y2[new] = start[3];

	if (new == 31)
		full = TRUE;

	SetAPen(lay2->rp, 0);
	Move(lay2->rp, x1[new], y1[new]);
	Draw(lay2->rp, x2[new], y2[new]);
	new++;
	new %= 32;
}

/*****************************************************************\
* doLayer3() - Gère le scrolling du SuperLayer                    *
\*****************************************************************/
void doLayer3(void)
{
static SHORT posx = 0, posy = 0, dirx = -2, diry = -1;

	if ((posx <= 0) || (posx >= SUPER_W-LAY3_W-2))
		dirx = -dirx;

	if ((posy <= 0) || (posy >= SUPER_H-LAY3_H-1))
		diry =- diry;

	ScrollLayer(NULL, lay3, dirx, diry);
	posx += dirx;
	posy += diry;
}


[Retour en haut] / [Retour aux articles]