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 : Assembleur - Effet de zoom
(Article écrit par Jérôme Étienne et extrait d'Amiga News Tech - mars 1992)
|
|
A la demande d'un lecteur, j'ai ce mois-ci programmé une routine de zoom bitmap, telle que l'on peut
la voir dans Deluxe Paint par exemple avec l'option "Stretch" (Étirement).
Pour se servir de cette routine, il faut initialiser les coordonnées du rectangle source dans OX1,
OY1, OX2 et OY2. (OX1, OY1) sont les coordonnées du point en haut à gauche du rectangle source
et (OX2, OY2) celles du point en bas à droite. De plus, il faut initialiser les coordonnées du
rectangle destination (DX1, DY1) pour le coin en haut à droite, et (DX2, DY2) pour en bas à
gauche. La routine dans sa version actuelle, ne fait qu'afficher une seule fois la fenêtre "étendue",
mais en se creusant un peu la tête, il est possible de l'utiliser pour faire des effets vidéo
très intéressants.
L'image où l'on prend le rectangle source (disponible sur la disquette n°31)
est chargée dans le programme par un INCBIN
et est pointée par l'étiquette PIC_BMP. Cette image est au format 320x256 en 16 couleurs
et est au format bitmap standard (d'abord le plan 1, puis le plan 2, etc.). Elle est suivie
de sa palette de couleurs.
On peut penser qu'il est assez stupide d'avoir choisi ce format, puisqu'une routine dont le
seul but est de transformer l'image source en un format où chaque plan de ligne est entrelacé,
est appelée en début de programme... Ce choix se justifie par le fait que ce format est celui
que l'on obtient par défaut avec IFF_Converter, donc le plus facile à obtenir pour le lecteur.
Fonctionnement du zoom
Le principe de base est très simple et est, en même temps, le plus rapide que je connaisse pour
une telle application. Il consiste dans un premier temps à zoomer verticalement la fenêtre source,
puis horizontalement le résultat obtenu. Cette technique permettant de ne zoomer que dans une
seule direction à la fois (horizontale ou verticale), elle permet également l'utilisation du
Blitter. Ceci est bien plus rapide que la technique de base qui vient tout de suite à l'esprit
d'une personne n'ayant jamais réfléchi au problème, à savoir tester la couleur de chaque point
de la fenêtre source et de dessiner un rectangle de la même couleur dans la fenêtre destination.
Le gain de vitesse de l'algorithme vient du fait que l'on traite des ensembles de points, et
non les points un par un. Cette méthode présente cependant un certain désavantage : elle nécessite
une étape intermédiaire et donc, utilise plus de mémoire.
Voici plus de détails : l'image d'où l'on extrait les premières données (les lignes horizontales)
est appelée FIRST_SCR_ADR. On la déforme verticalement et l'on stocke l'étape intermédiaire
dans une page appelée LOG_SCRADR. Puis on déforme horizontalement cette étape et on stocke le
résultat dans la page PHY_SCR_ADR, qui est celle que l'on voit. C'est pour cette raison que l'on
voit l'image se dessiner (rapidement) devant nos yeux quand on lance le programme.
Je vais vous expliquer comment faire un zoom d'une partie d'image en prenant comme exemple le
zoom horizontal (qui augmente la hauteur de la fenêtre source). On initialise deux curseurs
virtuels : un qui va se balader sur les y de la fenêtre destination du haut vers le bas, par
pas de un, et un qui va se balader sur les y de la fenêtre source et dont le pas va varier
selon un coefficient prédéfini. A chaque itération, on incrémente chaque y avec le pas qui lui
correspond et on copie la ligne du y source sur la ligne du y destination.
C'est très simple, mais cela mérite une petite précision sur la manière de calculer le
coefficient : il doit être un nombre réel et non un entier. Or le 68000 ne manipule que les
entiers. J'ai donc choisi la technique des bits fractionné : sur un chiffre de 32 bits,
on déclare qu'un certain nombre de bits sont considérés comme étant derrière la virgule.
Dans mon cas, ce chiffre est de 16 bits car il permet d'extraire plus
facilement la partie entière. En effet, quand on utilise un registre, une simple instruction
"swap" suffit ; quand on utilise la mémoire, il suffit d'adresser en ".w" au lieu de ".l".
Pourquoi notre coefficient est-il réel, et non entier ? Tout simplement parce qu'il est le
résultat de la division de la hauteur de la fenêtre source par la hauteur de la fenêtre destination.
Le problème immédiat est donc : comment faire une division de telle sorte que l'on obtienne un
résultat sur 32 bits ? J'ai trouvé la réponse dans le livre Mise En Oeuvre Du 68000, paru chez Sybex.
Nous savons maintenant comment fonctionne globalement le zoom et nous allons nous attarder
sur les routines appelées copy_horizontal_line et copy_vertical_line. Pour la copie de ligne
horizontale, rien de génial en fait : c'est une simple copie de la ligne avec un mintern de
A=D. Pour la copie verticale, c'est légèrement plus complexe : dans un premier temps, le mintern
doit être différent, car il est nécessaire de ne pas effacer tous les bits du mot destination,
mais seulement le bit correspondant à la ligne verticale en cours. Pour cela, on choisit un
mintern tel que AB+aC=D, avec A désignant le mask, B l'image et C le fond.
Mais tous les problèmes ne sont pas résolus pour autant, car comme chacun sait, le Blitter
décale ses bits vers la droite et uniquement vers la droite, d'où un problème évident.
Imaginons que l'on veut copier la ligne qui se trouve sur la colonne 13 sur la colonne 5. Le glissement
doit s'effectuer vers la gauche, ce dont le Blitter est incapable. On est forcé de le simuler
en utilisant le fait que tout les bits qui sortent à droite au bout de la ligne réapparaissent
au début de la ligne suivante. Pour simuler le glissement à gauche, il faut donc remonter le
pointeur d'une ligne (pour que les bits aillent bien là où il faut, car l'image sera descendue
d'une ligne) puis augmenter "bltsize" d'une ligne pour que l'image soit affichée dans son
intégralité et qu'il ne manque pas un point tout en bas de la ligne verticale.
Pour finir, je voudrais préciser que l'on aurait pu être plus rapide en utilisant le fait que
bouger une ligne verticale est plus long que pour une ligne horizontale. Il est donc possible
de faire une routine qui décide quel zoom faire en premier, afin de gagner du temps. Le
test est assez simple : si l'on grossit la fenêtre verticalement, alors on zoome en horizontal
en premier, sinon on reste dans l'ordre actuel.
La phrase du mois : On ne manipule pas une marionnette avec un seul fil.
Dernière minute : emporté par mon enthousiasme, j'ai soudain réalisé, mais trop tard, qu'en
mode incrémentation, notre cher Blitter travaille complètement à l'envers, c'est-à-dire
non seulement du bas vers le haut, mais aussi de la droite vers la gauche !
On gagnerait donc encore du temps en utilisant ce mode ! N'ayez crainte, je vous
proposerai une routine de remplacement... dès que je l'aurai programmée.
* AUTEUR: j.etienne
* SUJET: zoom bitmap avec le blt
* ASSEMBLEUR: devpac 2.14
OPT C- * opt case off
incdir 'include:'
include 'exec/exec_lib.i'
include 'hardware/custom.i'
custom = $dff000
execbase = 4
BPL_X = 320
BPL_Y = 256
BPL_DEPTH = 4
BPL_WIDTH = BPL_X/8
BPL_SIZE = BPL_WIDTH*BPL_Y
WAIT_BLT: macro
lea CUSTOM,a5
btst #14,dmaconr(a5)
.LOOP_WAIT_BLT\@:
btst #14,dmaconr(a5)
bne.s .LOOP_WAIT_BLT\@
endm
******************************************************
************** programme principal *****************
******************************************************
move.l (execbase).w,a6
lea CUSTOM,a5
CALLEXEC Forbid
move.w #$03e0,dmacon(a5) * all dma off except disk
move.w #(BPL_DEPTH<<12)+$200,bplcon0(a5)
clr.w bplcon1(a5)
clr.w bplcon2(a5)
move.w #BPL_WIDTH*(BPL_DEPTH-1),bpl1mod(a5)
move.w #BPL_WIDTH*(BPL_DEPTH-1),bpl2mod(a5)
move.w #$2981,diwstrt(a5) *\
move.w #$29c0,diwstop(a5) * > init un ecran 320*256
move.w #$0038,ddfstrt(a5) * >
move.w #$00d0,ddfstop(a5) */
bsr BUILD_COPLIST
move.l COPLIST_adr,cop1lc(a5) * > run my COPLIST
clr.w copjmp1(a5) */
move.w #$83C0,dmacon(a5) * dma blitter,copper & bitplane on
bsr PUT_PIC_IN_PHY_SCR
* fait le zoom
bsr ZOOM
WAIT move.b $bfec01,d0 *\
not d0 * >capture the key wich is pressed
ror.b #1,d0 */ and put is code RAW in d0
cmp.b #$45,d0 *\
beq INIT_END */ sort si on press sur esc
btst #6,$bfe001
bne WAIT
bra INIT_END
ZOOM: ***************************************************************
* init coor du rectangle source
move.l #120*$10000,OX1
move.l #000*$10000,OY1
move.l #220*$10000,OX2
move.l #100*$10000,OY2
* init coor du rectangle cible
move.l #000*$10000,DX1
move.l #000*$10000,DY1
move.l #319*$10000,DX2
move.l #255*$10000,DY2
* fait le zoom vertical puis horizontal
bsr ZOOM_VERTICAL
bsr ZOOM_HORIZONTAL
rts
********************************************************** END_ZOOM
ZOOM_HORIZONTAL: ****************************************************
moveq #0,d0
move.w DX2(pc),d0
sub.w DX1(pc),d0
moveq #0,d1
move.w OX2(pc),d1
sub.w OX1(pc),d1
swap d1
bsr DIV_32_BITS
move.l d0,COEF_HORIZONTAL
move.l DX1,CUR_OX
move.l DX1,CUR_DX
.LOOP_EACH_LINE
move.w CUR_OX(pc),d0
move.w DY1(pc),d1
move.w DY2(pc),d2
move.w CUR_DX(pc),d3
move.w DY1(pc),d4
bsr COPY_VERTICAL_LINE
move.l COEF_HORIZONTAL(pc),d0
add.l d0,CUR_OX
addq.w #1,CUR_DX
move.w CUR_DX(pc),d0
cmp.w DX2(pc),d0
blt.s .LOOP_EACH_LINE
rts
************************************************* END_ZOOM_HORIZONTAL
COPY_VERTICAL_LINE: *******************************************
* IN: d0 = CUR_OX = coor origine
* d1 = OY1 = coor origine
* d2 = OY2 = coor origine
* d3 = CUR_DX = coor destination
* d4 = DY1 = coor destination
sub.w d1,d2 * d2 = height
move.l LOG_SCR_ADR(pc),a0
mulu #BPL_WIDTH*BPL_DEPTH,d1
add.l d1,a0
moveq #$f,d1
and.w d0,d1 * d1 = shift of source
moveq #$f,d6
and.w d3,d6 * d6 = shift of target
sub.w d1,d6 * d6 = shift for blt
move.l PHY_SCR_ADR(pc),a1
mulu #BPL_WIDTH*BPL_DEPTH,d4
add.l d4,a1
and.w #$fff0,d3
lsr.w #3,d3
lea (a1,d3.w),a1 * a1 = target adr
tst.w d6
bge.s .SHIFT_GE_0
add.w #16,d6
lea -BPL_WIDTH*BPL_DEPTH(a1),a1
addq.w #1,d2
.SHIFT_GE_0
move.w #$8000,d5
lsr.w d1,d5
and.w #$fff0,d0
lsr.w #3,d0
lea (a0,d0.w),a0 * a0 = source adr
lsl.w #6,d2
or.w #1,d2 * d2 = BLTSIZE
ror.w #4,d6
and.w #$f000,d6
move.w d6,d1
or.w #$07ca,d1
lea CUSTOM,a5
WAIT_BLT
move.w #BPL_WIDTH*BPL_DEPTH-2,BLTBMOD(a5)
move.w #BPL_WIDTH*BPL_DEPTH-2,BLTCMOD(a5)
move.w #BPL_WIDTH*BPL_DEPTH-2,BLTDMOD(a5)
move.l #-1,BLTAFWM(a5)
moveq #BPL_DEPTH-1,d7
.LOOP_EACH_BPL
WAIT_BLT
move.w d5,BLTADAT(a5)
move.w d1,BLTCON0(a5)
move.w d6,BLTCON1(a5)
move.l a0,BLTBPT(a5)
move.l a1,BLTCPT(a5)
move.l a1,BLTDPT(a5)
move.w d2,BLTSIZE(a5)
lea BPL_WIDTH(a0),a0
lea BPL_WIDTH(a1),a1
dbf d7,.LOOP_EACH_BPL
rts
**************************************** END_COPY_VERTICAL_LINE
ZOOM_VERTICAL: ****************************************************
moveq #0,d0
move.w DY2(pc),d0
sub.w DY1(pc),d0
moveq #0,d1
move.w OY2(pc),d1
sub.w OY1(pc),d1
swap d1
bsr DIV_32_BITS
move.l d0,COEF_VERTICAL
move.l OY1,CUR_OY
move.l DY1,CUR_DY
.LOOP_EACH_LINE
move.w OX1(pc),d0
move.w CUR_OY(pc),d1
move.w OX2(pc),d2
move.w DX1(pc),d3
move.w CUR_DY(pc),d4
bsr COPY_HORIZONTAL_LINE
move.l COEF_VERTICAL(pc),d0
add.l d0,CUR_OY
addq.w #1,CUR_DY
move.w CUR_DY,d0
cmp.w DY2,d0
blt.s .LOOP_EACH_LINE
rts
************************************************* END_ZOOM_HORIZONTAL
COPY_HORIZONTAL_LINE: *******************************************
* IN: d0 = OX1 = coor origine
* d1 = CUR_OY = coor origine
* d2 = OX2 = coor origine
* d3 = DX1 = coor destination
* d4 = CUR_DY = coor destination
move.l FIRST_SCR_ADR(pc),a0
mulu #BPL_WIDTH*BPL_DEPTH,d1
add.l d1,a0
and.w #$fff0,d0
and.w #$fff0,d2
lsr.w #3,d0
lsr.w #3,d2
lea (a0,d0.w),a0
move.l LOG_SCR_ADR(pc),a1
subq.w #1,d4
mulu #BPL_WIDTH*BPL_DEPTH,d4
add.l d4,a1
and.w #$fff0,d3
lsr.w #3,d3
lea (a1,d3.w),a1
sub.w d0,d2
addq.w #2,d2 * d2 = largeur de la ligne en octet
move.w #BPL_WIDTH,d1
sub.w d2,d1 * d1 = modulo
lsr.w #1,d2
or.w #BPL_DEPTH<<6,d2 * d2 = BLTSIZE
lea CUSTOM,a5
WAIT_BLT
move.l #-1,BLTAFWM(a5)
move.l #$09f00000,BLTCON0(a5)
move.w d1,BLTAMOD(a5)
move.w d1,BLTDMOD(a5)
move.l a0,BLTAPT(a5)
move.l a1,BLTDPT(a5)
move.w d2,BLTSIZE(a5)
rts
**************************************** END_COPY_HORIZONTAL_LINE
DIV_32_BITS: ***********************************************
* routine de division d'un chiffre 32 bits part un chiffre 16 bit avec un
* resultat 32 bits. Elle est directement tire de mise en oeuvre du 68000
* de sybex (tres bon livre soit dit en passant).
* in: d0 = X chiffre 16 bits
* d1 = YZ chiffre 32 bits
* out: d0 = resultat 32 bits
* d0-d3 sont detruit
moveq #0,d3
divu d0,d1
bvc.s .RESULT
move.l d1,d2
clr.w d1
swap d1
divu d0,d1
move.w d1,d3
move.w d2,d1
divu d0,d1
.RESULT move.l d1,d0
swap d1
move.w d3,d1
swap d1
move.l d1,d0
rts
******************************************** END_DIV_32_BITS
PUT_PIC_IN_PHY_SCR: ***************************************************
* Cette routine prend l'image en format bitmap et la met au format
* plan de ligne entrelacee.
lea PIC_BMP,a0
move.l FIRST_SCR_ADR(pc),a2
move.w #BPL_DEPTH-1,d0
.LOOP_EACH_BPL
move.l a2,a1
move.w #BPL_Y-1,d1
.LOOP_EACH_LINE:
move.w #BPL_X/16-1,d2
.LOOP_EACH_WORD:
move.w (a0)+,(a1)+
dbf d2,.LOOP_EACH_WORD
lea BPL_WIDTH*(BPL_DEPTH-1)(a1),a1
dbf d1,.LOOP_EACH_LINE
lea BPL_WIDTH(a2),a2
dbf d0,.LOOP_EACH_BPL
lea COLOR+CUSTOM,a1
move.w #(1<<BPL_DEPTH)-1,d0
.LOOP_EACH_COLOR:
move.w (a0)+,(a1)+
dbf d0,.LOOP_EACH_COLOR
rts
************************************************ END_PUT_PIC_IN_PHY_SCR
INIT_END: *********************************************************
* reactivation de l'ancienne COPLIST
lea CUSTOM,a5
move.l (EXECBASE).w,a6
lea GfxName(pc),a1 * nom de la library ds a1
moveq #0,d0 * version 0 (the last)
CALLEXEC OpenLibrary * lib graphique ouverte
move.l d0,a4 * adr de graphicbase ds a4
move.l 38(a4),cop1lc(a5) * chargement de l'adr de
clr.w copjmp1(a5) * l'old COPLIST et lancement
move.w #$83e0,dmacon(a5) * activation des canaux dma necessaires
CALLEXEC Permit * multi switching autorise
* et on retourne d'ou l'on vient.
moveq #0,d0 * flag d'erreur desactive
rts
GfxName: dc.b "graphics.library",0
EVEN
******************************************************* end_INIT_END
BUILD_COPLIST: ********************************************************
move.l COPLIST_ADR,a0
move.l PHY_SCR_ADR,d2
moveq #BPL_DEPTH-1,d0
move.w #bplpt,d1
.LOOP_INIT_BPL_IN_CLIST
move.w d1,(a0)+
addq.l #2,d1
swap d2
move.w d2,(a0)+
swap d2
move.w d1,(a0)+
addq.l #2,d1
move.w d2,(a0)+
add.l #BPL_WIDTH,d2
dbf d0,.LOOP_INIT_BPL_IN_CLIST
move.l #$fffffffe,(a0)+ * montre la fin de la clist
rts
********************************************************* END_BUILD_COPLIST
COEF_VERTICAL: ds.l 1
COEF_HORIZONTAL: ds.l 1
*
CUR_OX: ds.l 1
CUR_OY: ds.l 1
CUR_DX: ds.l 1
CUR_DY: ds.l 1
*
OX1: ds.l 1
OY1: ds.l 1
OX2: ds.l 1
OY2: ds.l 1
*
DX1: ds.l 1
DY1: ds.l 1
DX2: ds.l 1
DY2: ds.l 1
******** datas
* variables
LOG_SCR_ADR: dc.l ECRAN1
PHY_SCR_ADR: dc.l ECRAN2
FIRST_SCR_ADR: dc.l ECRAN3
COPLIST_ADR: dc.l COPLIST
PIC_BMP: INCBIN Freddy.raw
INCBIN Freddy.pal
section ZONE_CHIP,BSS_C
ECRAN3: ds.l BPL_WIDTH*BPL_Y*BPL_DEPTH/4
ECRAN1: ds.l BPL_WIDTH*BPL_Y*BPL_DEPTH/4
ECRAN2: ds.l BPL_WIDTH*BPL_Y*BPL_DEPTH/4
COPLIST: ds.l 1000*4 * taiile inconnue donc on prevoit gros
end
|
|