Revenir au plan du site

La voie du véritable clipping, une histoire de découpe!



Nous allons reprendre notre routine de sprite masquée et ajouter la gestion du clipping vertical en suivant les principes évoqués précedemment mais je vous remets quand même les schémas et les formules à appliquer :
 

La gestion du clipping haut se place logiquement au tout début de la routine de sprite. On teste le Y et on ajuste au besoin. Et la gestion du clipping bas AUSSI se trouve au début, car elle va se contenter de changer le nombre d'itération (réduire le paramètre correspondant à la hauteur du sprite).
AfficheSprite
; BC=page + coordonnée X (0-79) / HL=coordonnée Y (0-199) / DE=adresse des données du sprite
; XL=largeur du sprite en octets / XH=hauteur du sprite en nombre de lignes
bit 7,h : jr z,.pasClipHaut ; si le Y est positif ou nul, pas de clipping en haut
; XH est la hauteur affichable, on soustrait le Y négatif
push hl : ld a,xh : add l : ld l,a : ld a,h : adc 0 : ld h,a : ld a,xh : ld xh,a : pop hl
; vu qu'on saute des lignes, il faut avancer le pointeur de données
ld a,l : ex hl,de : ld d,0 : ld e,xl
.sauteLignes add hl,de : inc a : jr nz,.sauteLignes ; +simple qu'une multiplication
ex hl,de ; remettre le pointeur dans DE
ld hl,0 ; tout est corrigé, on dit qu'on part maintenant de la ligne 0
.pasClipHaut
; pour rester sur des comparaisons 8 bits, on peut soustraire sprHauteur à hauteur et comparer à sprY
ld a,200 : sub xh : cp l : jr nc,.pasClipBas ;
ld a,200 : sub l : ld xh,a ; nouvelle hauteur +petite
.pasClipBas
call CalculeAdressePixel
...


La suite de la routine est identique, en fait on a modifié à la volée les paramètres d'entrée, à savoir :
- la coordonnée Y et l'adresse des données du sprite dans le cas du clipping haut.
- la hauteur du sprite dans le cas du clipping bas.



Pour gérer le clipping gauche/droite, il va à nouveau falloir tester en début de routine nos coordonnées. Cette fois on teste le X avec la largeur écran.
 

On va reprendre le code à partir du label .pasClipBas
.pasClipBas
xor a ; offsetDebut par défaut
bit 7,c : jr z,.pasClipGauche
ld a,c : add xl : ld xl,a ; nouvelle largeur
ld a,c : neg ; nouvel offsetDebut
ld c,0 ; nouveau X en zéro
.pasClipGauche
ld (offsetDebut),a ; soit zéro, soit celui calculé lors du clipping
ld a,c : add xl : cp 81 ; largeur+1 rapport à la comparaison souhaitée
ld a,0 : jr c,.pasClipDroite ; A=0 au cas où on n'ait pas de clipping, pour reset offsetFin
ld a,xl : exa ; on met la largeur du sprite de côté
ld a,80 : sub c : ld xl,a ; nouvelle largeur de sprite
exa ; on récupère la largeur d'origine
sub xl
.pasClipDroite
ld (offsetFin),a

call CalculeAdressePixel


Vous remarquez qu'on patche deux zones de code avec les labels offsetDebut et offsetFin, qui sont des décalages qui vont nous permettre à chaque ligne de bien caler les données que l'on lit rapport au clipping en court (ou zéro si pas de clipping). Plaçons nos deux additions dans la boucle principale. Vous aurez compris que la valeur #12 est remplacée par le code qui teste le clipping, dans un cas comme dans l'autre.
ld a,e : add #12 : offsetDebut=$-1 : ld e,a : ld a,d : adc 0 : ld d,a
.pixelMasque
ld a,(de) ; lire le sprite
ld l,a ; octet du sprite dans L
ld a,(bc) ; on récupère l'octet de l'écran
and (hl) ; on applique le masque
or l ; on fusionne avec la donnée du sprite
ld (bc),a ; on remet dans l'écran
inc bc
inc de ; et on incrémente data+ecran
dec yl
jr nz,.pixelMasque
ld a,e : add #12 : offsetFin=$-1 : ld e,a : ld a,d : adc 0 : ld d,a



Et voilà, nos deux additions encapsulent la boucle principale. Réalisons un BREF test pour valider que tout est ok.
BUILDSNA : BANKSET 0
ORG #38 : EI : RET
ORG #100 : RUN #100

ld sp,#100 : ei
ld bc,#7F00+%10001100+%00 : out (c),c ; 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,#C000+#FC : ld hl,60 ; à quatre octets du bord gauche
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+76 : ld hl,100 ; à quatre octets du bord
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+26 : ld hl,-18 ; à moitié
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+46 : ld hl,184 ; à moitié
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

; Et maintenant les coins!!!

ld bc,#C000+#FC : ld hl,-18 ; à quatre octets du bord gauche
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+#FC : ld hl,184 ; à quatre octets du bord gauche
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+76 : ld hl,-18 ; à quatre octets du bord gauche
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite

ld bc,#C000+76 : ld hl,184 ; à quatre octets du bord gauche
ld xl,9 : ld xh,36 : ld de,donnees_sprite ; dimensions du sprite largeur/lignes
call AfficheSprite


jr $

AfficheSprite
; BC=page + coordonnée X (0-79) / HL=coordonnée Y (0-199) / DE=adresse des données du sprite
; XL=largeur du sprite en octets / XH=hauteur du sprite en nombre de lignes
bit 7,h : jr z,.pasClipHaut ; si le Y est positif ou nul, pas de clipping en haut
; XH est la hauteur affichable, on soustrait le Y négatif
push hl : ld a,xh : add l : ld l,a : ld a,h : adc 0 : ld h,a : ld a,l : ld xh,a : pop hl
; vu qu'on saute des lignes, il faut avancer le pointeur de données
ld a,l : ex hl,de : ld d,0 : ld e,xl
.sauteLignes add hl,de : inc a : jr nz,.sauteLignes ; +simple qu'une multiplication
ex hl,de ; remettre le pointeur dans DE
ld hl,0 ; tout est corrigé, on dit qu'on part maintenant de la ligne 0
.pasClipHaut
; pour rester sur des comparaisons 8 bits, on peut soustraire sprHauteur à hauteur et comparer à sprY
ld a,200 : sub xh : cp l : jr nc,.pasClipBas ;
ld a,200 : sub l : ld xh,a ; nouvelle hauteur +petite
.pasClipBas
xor a ; offsetDebut par défaut
bit 7,c : jr z,.pasClipGauche
ld a,c : add xl : ld xl,a ; nouvelle largeur
ld a,c : neg ; nouvel offsetDebut
ld c,0 ; nouveau X en zéro
.pasClipGauche
ld (offsetDebut),a ; soit zéro, soit celui calculé lors du clipping
ld a,c : add xl : cp 81 ; largeur+1 rapport à la comparaison souhaitée
ld a,0 : jr c,.pasClipDroite ; A=0 au cas où on n'ait pas de clipping, pour reset offsetFin
ld a,xl : exa ; on met la largeur du sprite de côté
ld a,80 : sub c : ld xl,a ; nouvelle largeur de sprite
exa ; on récupère la largeur d'origine
sub xl
.pasClipDroite
ld (offsetFin),a

call CalculeAdressePixel
; HL=destination écran
; DE=toujours l'adresse source des données
ld bc,hl ; on utilise BC comme destination écran

ld h,hi(tableTransparence)
.afficheLignes
ld a,xl : ld yl,a ; chargeur la largeur d'une ligne dans YL
push bc ; on met l'adresse de début de la ligne de côté

ld a,e : add #12 : offsetDebut=$-1 : ld e,a : ld a,d : adc 0 : ld d,a
.pixelMasque
ld a,(de) ; lire le sprite
ld l,a ; octet du sprite dans L
ld a,(bc) ; on récupère l'octet de l'écran
and (hl) ; on applique le masque
or l ; on fusionne avec la donnée du sprite
ld (bc),a ; on remet dans l'écran
inc bc
inc de ; et on incrémente data+ecran
dec yl
jr nz,.pixelMasque
ld a,e : add #12 : offsetFin=$-1 : ld e,a : ld a,d : adc 0 : ld d,a

pop bc ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
; notre routine de passage à la ligne suivante, adaptée pour BC
ld a,b : add 8 : ld b,a ; ajouter #800 à DE
and #38 ; on teste si on déborde de la bank (passage de page+#3800 à page+#0000)
jr nz,.nextLine ; pas zéro, on est toujours dans le même bloc de lignes
ld a,80  : add c : ld c,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant
ld a,#C0 : adc b : ld b,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000)
.nextLine
dec xh ; notre compteur de lignes
jr nz,.afficheLignes
ret
CalculeAdressePixel
; B=page vidéo #00, #40, #80 ou #C0
; C=coordonnée X (0-79)
; HL=coordonnée Y (0-199)
; adresse de la ligne dans HL en résultat
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
ld a,lo(tableau) : add l : ld l,a : ld a,h : adc hi(tableau): ld h,a
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
add hl,bc ; ajouter la position X en octets et la page!
ret
;-------------------
adresse_ecran=#0000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend


align 256
tableTransparence
repeat 256,x
px=x-1 ; car le compteur va par défaut de 1 à 256 et non 0 à 255
masque=0
if (px & (128|32|8|2))==0
    masque|=128|32|8|2 ; pour cet octet on conservera les données écran
endif
if (px & (64|16|4|1))==0
    masque|=64|16|4|1 ; pour cet octet on conservera les données écran
endif
defb masque
rend


palette defb #4D,#54,#56,#5C,#46,#5E,#40,#47,#43,#4E,#4B,#4C,0 ; Rose en zéro
donnees_sprite incbin 'hibouZero.bin'
org #C000 : incbin 'foretZero.bin'

Et hop!



Est-ce que la routine est terminée? Celle-ci oui! Mais elle n'est pas capable de gérer si le sprite est totalement en dehors de l'écran. Le plus efficace est de le tester avant de l'appeler, avec des coordonnées complètes (16 bits).
Si le sprite est en dehors de l'écran, on n'aura pas besoin de pré-charger l'adresse des données ou la page en cours. Rendez-vous dans [la deuxième partie de ce cours]