Revenir au plan du site

Fondu entre deux images utilisant la même palette


Si vous avez raté l'interview de Mickaël Pointier chez Génération Micros, ben déjà allez la regarder, c'est pas très long et Mickaël réussi a être à la fois technique et accessible, c'est toujours un plaisir de l'écouter.

Dans cette interview, il parle de son nouveau jeu [Encounter pour Oric] dans lequel il explique avoir réalisé des effets de transition avec une matrice de Bayer (enfin, il parle de tramage ordonné au sens général mais c'est Bayer qui nous intéresse).

Alors histoire de bien voir ce qu'est une matrice de Bayer, voici un gradient du blanc au noir qui utilise une matrice 4x4 (matrice pour pompeusement parler d'un bête carré de 4 pixels par 4...). Sur l'image ci-dessous, ce carré est répété plusieurs fois pour aussi montrer que la trame qui le compose est AUSSI conçue pour les répétitions contiguës :)


L'idée derrière la tête est de convertir cette matrice en masques de transparence qui se complètent. On utiliserait le masque correspondant aux points noirs avec la première image, le masque correspondant aux points blancs avec la deuxième image. Ensuite, utiliser ces masques en se déplaçant de droite à gauche ou de gauche à droite, nous permettrait de faire un fondu d'image.
La matrice de démonstration étant une matrice 4x4, cela va arranger nos affaires pour le mode 1. Il faudra passer sur 2 octets en mode 0 mais d'abord, le mode 1!
; masques de PIXELS (voir structure écran)
bayerMatrix
defb %00000000
defb %00000000
defb %00000000
defb %00000000
;
defb %10001000
defb %00000000
defb %00000000
defb %00000000
;
defb %10001000
defb %00000000
defb %00100010
defb %00000000
;
defb %10001000
defb %00000000
defb %10101010
defb %00000000
;
defb %10101010
defb %00000000
defb %10101010
defb %00000000
;
defb %10101010
defb %01000100
defb %10101010
defb %00000000
;
defb %10101010
defb %01000100
defb %10101010
defb %00010001
;
defb %10101010
defb %01000100
defb %10101010
defb %00010001
;
defb %10101010
defb %01000100
defb %10101010
defb %01010101
; à cette étape, il y a autant de pixels allumés qu'éteints!
defb %10101010
defb %01010101
defb %10101010
defb %01010101
;
defb %10101010
defb %01110111
defb %10101010
defb %01010101
;
defb %10101010
defb %01110111
defb %10101010
defb %01010101
;
defb %10101010
defb %01110111
defb %10101010
defb %11011101
;
defb %10101010
defb %01110111
defb %10101010
defb %11111111
;
defb %10101010
defb %11111111
defb %10101010
defb %11111111
;
defb %10101010
defb %11111111
defb %11101110
defb %11111111
;
defb %10111011
defb %11111111
defb %11101110
defb %11111111
;
defb %10111011
defb %11111111
defb %11111111
defb %11111111
;
defb %11111111
defb %11111111
defb %11111111
defb %11111111

On remarque que chaque étape de la matrice ne change qu'un pixel rapport à la précédente (ou la suivante). Il n'y a pas de changement global à chaque itération.

Pour appliquer notre matrice 4x4, une remarque : Il est possible de parcourir chaque bloc (composés de 8 lignes) d'un coup, car 8 est un multiple de 4.
La routine va d'abord traiter les lignes 0, 8, 16, 24, 32, ... (toutes celles du premier bloc de 2048 octets)
Puis les lignes 1,9,17,25, ...
Suivent les lignes 2,10,18,26, ...
3,11,19,27,...
bon, je ne continue pas, vous avez compris?

Pensez à télécharger les deux images [ici] et [là] avant d'assembler ce source.
BUILDSNA : BANKSET 0
ORG #100
RUN #100

ld bc,#7F80+%1101 : out (c),c ; utiliser le mode 1
ld bc,#7F10 : out (c),c : ld a,#54 : out (c),a
ld bc,#7F00 : out (c),c : ld a,#54 : out (c),a
ld bc,#7F01 : out (c),c : ld a,#4C : out (c),a
ld bc,#7F02 : out (c),c : ld a,#55 : out (c),a
ld bc,#7F03 : out (c),c : ld a,#43 : out (c),a

transitionInfinie
ld hl,#4000 : ld (laSource),hl : call transitionBayer
ld hl,#8000 : ld (laSource),hl : call transitionBayer
jr transitionInfinie

transitionBayer
ld ix,bayerMatrix
ld d,19 ; 19 étapes de Bayer
.executerMatrice
exx : ld hl,#4000 : laSource=$-2 : ld de,#C000 : exx
ld c,2
.deuxFois4
ld b,4 ; 4 lignes
.leBloc
exx
ld b,8
ld a,(ix+0) : ld (leMasque1),a : cpl : ld (leMasque2),a
.fusion
ld a,(hl) : and #12 : leMasque1=$-1 : ld c,a
ld a,(de) : and #12 : leMasque2=$-1 : or c : ld (de),a
inc e : inc l ; augmenter le poids faible 256 fois
jr nz,.fusion
inc d : inc h ; augmenter le poids fort
djnz .fusion ; 8x256=2048
; ligne de bloc suivante, changer de masque
inc ix
exx
djnz .leBloc
dec ix : dec ix : dec ix : dec ix ; on réarme le masque toutes les 4 lignes
dec c
jr nz,.deuxFois4
inc ix : inc ix : inc ix : inc ix ; on a terminé, on peut enfin changer de masque
dec d
jr nz,.executerMatrice
ret

; masques de PIXELS (voir structure écran)
bayerMatrix
defb %00000000,%00000000,%00000000,%00000000
defb %10001000,%00000000,%00000000,%00000000
defb %10001000,%00000000,%00100010,%00000000
defb %10001000,%00000000,%10101010,%00000000
defb %10101010,%00000000,%10101010,%00000000
defb %10101010,%01000100,%10101010,%00000000
defb %10101010,%01000100,%10101010,%00010001
defb %10101010,%01000100,%10101010,%00010001
defb %10101010,%01000100,%10101010,%01010101
; à cette étape, il y a autant de pixels allumés qu'éteints!
defb %10101010,%01010101,%10101010,%01010101
defb %10101010,%01110111,%10101010,%01010101
defb %10101010,%01110111,%10101010,%01010101
defb %10101010,%01110111,%10101010,%11011101
defb %10101010,%01110111,%10101010,%11111111
defb %10101010,%11111111,%10101010,%11111111
defb %10101010,%11111111,%11101110,%11111111
defb %10111011,%11111111,%11101110,%11111111
defb %10111011,%11111111,%11111111,%11111111
defb %11111111,%11111111,%11111111,%11111111

org #4000 : incbin 'human0.bin'
org #8000 : incbin 'human1.bin'
org #C000 : incbin 'human1.bin'

Et la preview du résultat


Alors on ne va pas se mentir, sur un écran complet, le traitement est un peu lent (et peu sujet à optimisations) mais il est possible de sauter des étapes. Là on en a 19 c'est beaucoup. Et aussi de réduire la zone traitée au prix d'une augmentation de la complexité du code, c'est à choisir ;)

Pour sauter des étapes, on incrémentera de 8 au lieu de 4 le registre IX en fin de transition et le nombre total d'étapes à 10 => (19+1)/2

Bon, on passe au mode 0?

Je n'ai personnellement pas la motivation de retaper des defines à partir de l'image pour les pixels mode 0. Et comme mes masques en mode 1 sont beaux et sans erreur, on va s'en servir pour générer du code
macro masqueMode0 octet
haute={octet}&0xF0
basse={octet}&0x0F
print 'defb ',{hex2}(haute|(haute>>4)),',',{hex2}(basse|(basse<<4)),':'
mend

macro conversionLigne v1,v2,v3,v4
masqueMode0 {v1}
masqueMode0 {v2}
masqueMode0 {v3}
masqueMode0 {v4}
mend

bayerMatrix
conversionLigne %00000000,%00000000,%00000000,%00000000
conversionLigne %10001000,%00000000,%00000000,%00000000
conversionLigne %10001000,%00000000,%00100010,%00000000
conversionLigne %10001000,%00000000,%10101010,%00000000
conversionLigne %10101010,%00000000,%10101010,%00000000
conversionLigne %10101010,%01000100,%10101010,%00000000
conversionLigne %10101010,%01000100,%10101010,%00010001
conversionLigne %10101010,%01000100,%10101010,%00010001
conversionLigne %10101010,%01000100,%10101010,%01010101
conversionLigne %10101010,%01010101,%10101010,%01010101
conversionLigne %10101010,%01110111,%10101010,%01010101
conversionLigne %10101010,%01110111,%10101010,%01010101
conversionLigne %10101010,%01110111,%10101010,%11011101
conversionLigne %10101010,%01110111,%10101010,%11111111
conversionLigne %10101010,%11111111,%10101010,%11111111
conversionLigne %10101010,%11111111,%11101110,%11111111
conversionLigne %10111011,%11111111,%11101110,%11111111
conversionLigne %10111011,%11111111,%11111111,%11111111
conversionLigne %11111111,%11111111,%11111111,%11111111

Ce qui va nous produire la liste suivante (reformatée rapidement, il faudra que j'ajoute des options à RASM pour ne pas faire le retour chariot après un PRINT)
defb #00 , #00 : defb #00 , #00 : defb #00 , #00 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #00 , #00 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #22 , #22 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #00 , #00 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #11 , #11
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #11 , #11
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #55 , #55 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #DD , #DD
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #FF , #FF
defb #AA , #AA : defb #FF , #FF : defb #AA , #AA : defb #FF , #FF
defb #AA , #AA : defb #FF , #FF : defb #EE , #EE : defb #FF , #FF
defb #BB , #BB : defb #FF , #FF : defb #EE , #EE : defb #FF , #FF
defb #BB , #BB : defb #FF , #FF : defb #FF , #FF : defb #FF , #FF
defb #FF , #FF : defb #FF , #FF : defb #FF , #FF : defb #FF , #FF

Nous avons deux fois plus d'octets par matrice car le mode 0 n'a que deux pixels par octet et non 4 comme en mode 1. Il faudra donc précharger 4 octets par bloc à traiter, traiter les blocs 2 octets par 2 octets et...
Et c'est tout, les modifications sont mineures, téléchargez les images [LeonVictoire] et [LeonChampion]
BUILDSNA : BANKSET 0
ORG #100
RUN #100

ld bc,#7F80+%1100 : out (c),c ; utiliser le mode 0
ld hl,palette : ld bc,#7F00
setPalette out (c),c : inc c : inc b : outi : ld a,(hl) : or a : jr nz,setPalette
ld bc,#7F10 : out (c),c : ld a,#54 : out (c),a ; border Noir forcé

transitionInfinie
ld hl,#4000 : ld (laSource),hl : call transitionBayer
ld hl,#8000 : ld (laSource),hl : call transitionBayer
jr transitionInfinie

transitionBayer
ld ix,bayerMatrix
ld d,19 ; 19 étapes de Bayer
.executerMatrice
exx : ld hl,#4000 : laSource=$-2 : ld de,#C000 : exx
ld c,2
.deuxFois4
ld b,4 ; 4 lignes
.leBloc
exx
ld b,8
ld a,(ix+0) : ld (leMasque1),a : cpl : ld (leMasque2),a
ld a,(ix+1) : ld (leMasque3),a : cpl : ld (leMasque4),a
.fusion
ld a,(hl) : and #12 : leMasque1=$-1 : ld c,a
ld a,(de) : and #12 : leMasque2=$-1 : or c : ld (de),a
inc e : inc l
ld a,(hl) : and #12 : leMasque3=$-1 : ld c,a
ld a,(de) : and #12 : leMasque4=$-1 : or c : ld (de),a
inc e : inc l
jr nz,.fusion
inc d : inc h ; augmenter le poids fort
djnz .fusion ; 8x256=2048
; ligne de bloc suivante, changer de masque
inc ix : inc ix
exx
djnz .leBloc
ld a,xl : sub 8 : ld xl,a : ld a,xh : sbc 0 : ld xh,a ; Réarmer le masque (enlever 8!)
dec c
jr nz,.deuxFois4
ld a,xl : add 8 : ld xl,a : ld a,xh : adc 0 : ld xh,a ; terminé, on peut enfin changer de masque
dec d
jr nz,.executerMatrice
ret
palette defb #54,#44,#5C,#56,#58,#40,#55,#4C,#52,#57,#4E,#4A,#5B,#4B,#47,#4F,0
; masques de PIXELS (voir structure écran)
bayerMatrix
defb #00 , #00 : defb #00 , #00 : defb #00 , #00 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #00 , #00 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #22 , #22 : defb #00 , #00
defb #88 , #88 : defb #00 , #00 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #00 , #00 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #00 , #00
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #11 , #11
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #11 , #11
defb #AA , #AA : defb #44 , #44 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #55 , #55 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #55 , #55
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #DD , #DD
defb #AA , #AA : defb #77 , #77 : defb #AA , #AA : defb #FF , #FF
defb #AA , #AA : defb #FF , #FF : defb #AA , #AA : defb #FF , #FF
defb #AA , #AA : defb #FF , #FF : defb #EE , #EE : defb #FF , #FF
defb #BB , #BB : defb #FF , #FF : defb #EE , #EE : defb #FF , #FF
defb #BB , #BB : defb #FF , #FF : defb #FF , #FF : defb #FF , #FF
defb #FF , #FF : defb #FF , #FF : defb #FF , #FF : defb #FF , #FF

org #4000 : incbin 'LeonChampion.bin'
org #8000 : incbin 'LeonVictoire.bin'
org #C000 : incbin 'LeonVictoire.bin'

Et voilà notre transition à base de matrice de Bayer adaptée au mode 0. Toujours avec les mêmes contraintes, deux images partageant la même palette ;)