Zoom + Rotation avec MonochromeLib !
Posté le 11/02/2015 22:26
Et voilà je viens d'achever un défi personnel : écrire une fonction pour agrandir ou réduire une image !
Les fonctions sont incorporées dans la libraire de PierrotLL comme toutes les autres que nous connaissons bien.
Il suffit de modifier MonochromeLib.c et MonochromeLib.h par les nouveaux en pièce jointe.
Les prototypes :
void ML_bmp_or_zoom(const unsigned char *bmp, int x, int y, int width, int height, float zoom_w, float zoom_h);
void ML_bmp_and_zoom(const unsigned char *bmp, int x, int y, int width, int height, float zoom_w, float zoom_h);
void ML_bmp_xor_zoom(const unsigned char *bmp, int x, int y, int width, int height, float zoom_w, float zoom_h);
void ML_bmp_or_rotate(const unsigned char *bmp, int x, int y, int width, int height, int angle);
void ML_bmp_and_rotate(const unsigned char *bmp, int x, int y, int width, int height, int angle);
void ML_bmp_xor_rotate(const unsigned char *bmp, int x, int y, int width, int height, int angle);
Explications :
Il suffit de mettre en argument :
- le pointeur du bmp
- la position en x
- la position en y
- la largeur d'origine
- la hauteur d'origine
- le coefficient de zoom en largeur ou l'angle de rotation
- le coefficient de zoom en hauteur
Infos complémentaires :
Les coefficients de zoom sont des flottants compris entre 0 et l'infini
- entre 0 et 1 -> réduction
- plus de 1 -> agrandissement
L'angle est un entier en degrés et dans le sens trigo (anti-horaire)
L'image tourne autour de son centre.
Nouvelle version du zoom 3x plus rapide !
Ajout des rotations
La librairie est toujours libre.
Aussi simple que ça !
Fichier joint
Citer : Posté le 06/08/2015 14:26 | #
C'est vrai que l'antialiasing en niveau de gris ça serait pas mal
Citer : Posté le 18/08/2015 21:21 | #
Sinon, à quand la version avec anti-aliasing en niveaux de gris ? Pour peu que le moteur de Lephe devienne très fonctionnel (à priori j'ai confiance là-dedans), ça peut être cool
Non. Le gris ne devrait être utilisé que pour le pixel art. L'antialiasing ne fonctionnera jamais avec de si gros pixels.
J'ai essayé.
Citer : Posté le 18/08/2015 21:45 | #
Tant pis alors
Citer : Posté le 26/01/2016 18:48 | #
Ce midi je me demandais si c'était possible de faire tourner un sprite... Et la je tombe par hasard la dessus !
Je vais bien m'en servir merci
Un beat them all pour les CPC 19
Un jeu de Tank multijoueur en version graphique
Un jeu de boxe rigolo
Le moteur de combat épique d'un RPG
soccer physics : Un jeu de foot totalement wtf !
Survie 1 & 2 te laisseras-tu attraper par la méchante IA ?
Séquestrez les tous avec Catch'em all !
Joué à la calcultarice et pécher ? Facile !
Battle un système de combat dément !!
Débombe pas tout à fait un démineur
Mon mario pour le concours des 10 ans de PC
Casio jump un doodle jump pas comme les autres !
Rush four your life : tu cours ou tu meurs
Cookie clicker ! More cookies MOOORE !
Move et esquive : bouge pour esquiver les ennemis !
Guitar Hero !! Let's rock !
INVASION : Au secours on se fait envahir !
Un devine nombre entièrement customisable (mon 1er jeu)
Un outil pour dessiner des sprites en super drawstat et qui vous le compile pour vous donner un code utilisable dans vos programmes
Un super programme de dessin bourré de trucs funcs
Sortir une version finale de Tankasio
Bien m'améliorer en C parce que pour l'instant c'est pas jojo
Une ou plusieurs idées qui mûrissent petit à petit
Citer : Posté le 04/09/2018 11:52 | #
j'arrive à avoir un gain de ~50fps sur ML_bmp_or_rotate() en modifiant légèrement son code comme ceci:
void ML_bmp_rota(const unsigned char *bmp, int x, int y, int width, int height, int angle)
{
char i;
char j;
char bit;
char nb_width;
int ox;
int oy;
int dx;
int dy;
int xr;
int yr;
int sinus;
int cosinus;
char* vram;
if (bmp)
{
i = -1;
ox = x + (width >> 1);
oy = y + (height >> 1);
nb_width = width + 7 >> 3;
vram = (char*)asm_get_vram();
cosinus = cos_table[angle % 360];
sinus = cos_table[(angle + 90) % 360];
while (++i < width)
{
j = -1;
dx = x + i - ox;
bit = 0x80 >> (i & 7);
while (++j < height)
{
dy = y + j - oy;
xr = ox + (dx * cosinus - dy * sinus >> 14);
yr = oy + (dx * sinus + dy * cosinus >> 14);
if (!(yr & 0xffffffc0 || xr & 0xffffff80) && bmp[(i >> 3) + nb_width * j] & bit)
vram[(yr << 4) + (xr >> 3)] |= 128 >> (xr & 7);
}
}
}
}
la table de cosinus pré-calculé:
static const cos_table[360] =
{
16384, 16381, 16374, 16361, 16344, 16321, 16294, 16261, 16224,
16182, 16135, 16082, 16025, 15964, 15897, 15825, 15749, 15668,
15582, 15491, 15395, 15295, 15190, 15081, 14967, 14848, 14725,
14598, 14466, 14329, 14188, 14043, 13894, 13740, 13582, 13420,
13254, 13084, 12910, 12732, 12550, 12365, 12175, 11982, 11785,
11585, 11381, 11173, 10963, 10748, 10531, 10310, 10086, 9860,
9630, 9397, 9161, 8923, 8682, 8438, 8191, 7943, 7691, 7438, 7182,
6924, 6663, 6401, 6137, 5871, 5603, 5334, 5062,
4790, 4516, 4240, 3963, 3685, 3406, 3126, 2845,
2563, 2280, 1996, 1712, 1427, 1142, 857, 571,
285, 0, -285, -571, -857, -1142, -1427, -1712,
-1996, -2280, -2563, -2845, -3126, -3406, -3685, -3963,
-4240, -4516, -4790, -5062, -5334, -5603, -5871, -6137,
-6401, -6663, -6924, -7182, -7438, -7691, -7943, -8192,
-8438, -8682, -8923, -9161, -9397, -9630, -9860, -10086,
-10310, -10531, -10748, -10963, -11173, -11381, -11585, -11785,
-11982, -12175, -12365, -12550, -12732, -12910, -13084, -13254,
-13420, -13582, -13740, -13894, -14043, -14188, -14329, -14466,
-14598, -14725, -14848, -14967, -15081, -15190, -15295, -15395,
-15491, -15582, -15668, -15749, -15825, -15897, -15964, -16025,
-16082, -16135, -16182, -16224, -16261, -16294, -16321, -16344,
-16361, -16374, -16381, -16384, -16381, -16374, -16361, -16344,
-16321, -16294, -16261, -16224, -16182, -16135, -16082, -16025,
-15964, -15897, -15825, -15749, -15668, -15582, -15491, -15395,
-15295, -15190, -15081, -14967, -14848, -14725, -14598, -14466,
-14329, -14188, -14043, -13894, -13740, -13582, -13420, -13254,
-13084, -12910, -12732, -12550, -12365, -12175, -11982, -11785,
-11585, -11381, -11173, -10963, -10748, -10531, -10310, -10086,
-9860, -9630, -9397, -9161, -8923, -8682, -8438, -8191,
-7943, -7691, -7438, -7182, -6924, -6663, -6401, -6137,
-5871, -5603, -5334, -5062, -4790, -4516, -4240, -3963,
-3685, -3406, -3126, -2845, -2563, -2280, -1996, -1712,
-1427, -1142, -857, -571, -285, 0, 285, 571,
857, 1142, 1427, 1712, 1996, 2280, 2563, 2845,
3126, 3406, 3685, 3963, 4240, 4516, 4790, 5062,
5334, 5603, 5871, 6137, 6401, 6663, 6924, 7182,
7438, 7691, 7943, 8192, 8438, 8682, 8923, 9161,
9397, 9630, 9860, 10086, 10310, 10531, 10748, 10963,
11173, 11381, 11585, 11785, 11982, 12175, 12365, 12550,
12732, 12910, 13084, 13254, 13420, 13582, 13740, 13894,
14043, 14188, 14329, 14466, 14598, 14725, 14848, 14967,
15081, 15190, 15295, 15395, 15491, 15582, 15668, 15749,
15825, 15897, 15964, 16025, 16082, 16135, 16182, 16224,
16261, 16294, 16321, 16344, 16361, 16374, 16381,
};
et le asm_get_vram() ressemble à ça:
.extern _asm_get_vram
_asm_get_vram:
mov.l #h'80010070, r1
mov.l #h'0135, r0
jmp @r1
nop
rts
nop
Je donne ça ici en sachant qu'il est surement possible d'optimiser encore un peu et que Ninestars doit avoir un algo encore plus optimiser x)
D'ailleurs au passage j'ai essayé d'optimiser la fonction ML_clear_Vram() et ML_display_vram(), voila ce que j'ai réussie à obtenir (malheureusement je n'ai pas pu tester si c'est plus rapide ou plus lent mais sait-on jamais).
Ha oui et il faut leur mettre en paramètre l'address de la vram (parce que...je fais des tests de gris et moi j'ai 2 vrams donc voila)
_asm_clear_vram: ; r4 = address vram
mov.w #h'03fc, r6
add r4, r6
mov r6, r7
mov #4, r5
mov #3, r1
sub r5, r4
and r5, r1
add r5, r7
cmp/pl r5
bf/s while_long
mov #0, r3
while_char: ; paddig management
mov.b r3, @r4
mov.b r3, @r7
add #1, r4
add #-1, r5
cmp/eq r3, r5
bf/s while_char
add #1, r7
add #-1, r4
while_long:
mov.l r3, @r4
add #4, r4
cmp/hs r7, r4
bf while_long
rts
nop
_asm_display_vram: ;r4 = address vram
mov r13, @-r15
mov r12, @-r15
mov r11, @-r15
mov r10, @-r15
mov r9, @-r15
mov r8, @-r15
mov #192, r13
mov #0, r12
mov #7, r11
mov #4, r10
mov #16, r9
mov #0, r8
mov.l #h'b4000000, r1 ;LCD_register_selector
mov.l #h'b4010000, r2 ;LCD_data_register
mov #0, r0
loop_64:
mov.b r10, @r1 ;LCD_register_selector = 4
mov.b r13, @r2 ;LCD_data_register = r3
mov.b r10, @r1 ;LCD_register_selector = 4
mov.b r12, @r2 ;LCD_data_register = 0
mov.b r11, @r1 ;LCD_register_selector = 7
mov #0, r3
loop_16:
mov.b @r4+, r6
mov.b r6, @r2
add #1, r3
cmp/eq r9, r3
bf loop_16
add #1, r0
cmp/eq #64, r0
bf/s loop_64
add #1, r13 ;delay slot
mov @r15+, r8
mov @r15+, r9
mov @r15+, r10
mov @r15+, r11
mov @r15+, r12
mov @r15+, r13
rts
nop
Voilà voila c'est tout j'espère pas avoir fait trop n'importe quoi pour ML_clear_vram() et ML_display_vram()
Citer : Posté le 04/09/2018 12:01 | #
(Note : Tu peux utiliser [inlinecode] ou plus simplement des backticks pour écrire du texte monotypé sans utiliser l'horrible Courier. Par exemple `code`.)
Le pré-calcul est une bonne idée. Dans l'ensemble, mon avis personnel est que les rotations ne sont pas extrêmement fidèles à cause du peu de pixels et de l'absence d'interpolation
J'ai lu le code en 30 secondes ; apparemment pour ML_clear_vram(), tu as fait un memset() intelligent qui utilise des long. L'idée est bonne mais tu peux réduire sensiblement tes boucles. Par exemple la boucle principale qui fait les écritures peut s'écrire :
dt r[counter]
bf.s while_long
mov.l r3, @r4+
Tu gagnes deux cycles car tu as une instruction de moins, et tu utilises un delay slot (évite une bulle dans le pipeline). Il faut faire attention à ce que l'adresse de la boucle soit 4n + 2 pour que le mov.l soit sur une adresse non multiple de 4, ça permet de gagner un cycle sur la contention entre IF et MA dans le pipeline (conflit d'accès mémoire entre lire la prochaine instruction et écrire la donnée demandée).
Tu peux également optimiser parce que tu sais que tu dois faire au moins 63 * 4 long d'affilée, donc tu peux mettre plusieurs mov.l r3, @r4+ à la suite sans tester la condition à chaque fois. gint fait ça.
Pour ML_display_vram(), d'abord bravo : tu as probablement (je n'ai pas testé) réécrit un driver simple pour l'écran monochrome. Tu fais plus de modifications de registres que nécessaire, il me semble. Et pareil, tu peux unroll ta boucle dans le loop_16, ça te remplacerait 6 lignes de code par 16, soit 20 octets de mémoire ; pour peut-être 2 à 3 fois plus de vitesse d'exécution.
Ajouté le 04/09/2018 à 12:03 :
J'ai oublié de préciser que les techniques que tu as mises en œuvre là sont aussi utilisées par PLL, donc sur le principe ça ne devrait pas être beaucoup plus rapide ; on peut dire que le gain vient entièrement de la façon dont tu l'as codé.
Citer : Posté le 04/09/2018 12:57 | #
Hmmm bien joué si tu arrives à optimiser l'affichage
Ton tableau static const cos_table[360] n'est pas typé explicitement.
Cette lib est veille, il faudrait que je la mette à jour. Les techniques que j'ai utilisé dans Windmill serait parfaites pour refaire un algo de zoom et surtout un algo de rotation. Car celui là laisse des trous. Et aussi un algo de zoom+rotation. (En fait se serait même algo pour tous )
Citer : Posté le 02/10/2018 18:48 | #
Lephé: ça donnerait un truc du genre ?
_asm_clear_vram: ! r4 = address vram
mov.w 1f, r6
add r4, r6
mov r6, r7
mov #4, r5 ! met 4 dans r5
mov #3, r1 ! met 3 dans r1
sub r5, r4 ! 4 - vram
and r5, r1 ! (4 - vram) & 3
add r5, r7
cmp/pl r5 ! si r5 <= 0 ...
bf/s while_long
mov #0, r3 ! met 0 dans r3
while_char: ! paddig management
mov.b r3, @r4
mov.b r3, @r7
add #1, r4
add #-1, r5
cmp/eq r3, r5
bf/s while_char
add #1, r7
add #-1, r4
while_long:
mov.l r3, @r4
add #4, r4
cmp/hs r7, r4
bf while_long
rts
nop
1: .word 0x03fc
_asm_display_vram: ! r4 = address vram
mov.l r13, @-r15
mov.l r12, @-r15
mov.l r11, @-r15
mov.l r10, @-r15
mov #192, r13
mov #0, r12
mov #7, r11
mov #4, r10
mov.l LCD_register_selector, r1
mov.l LCD_data_register, r2
mov #64, r0 ! axe y
loop_64:
mov.b r10, @r1 ! LCD_register_selector = 4
mov.b r13, @r2 ! LCD_data_register = r3
mov.b r10, @r1 ! LCD_register_selector = 4
mov.b r12, @r2 ! LCD_data_register = 0
mov.b r11, @r1 ! LCD_register_selector = 7
mov #16, r3
loop_16:
mov.b @r4+, r6
dt r3
bf.s loop_16
mov.b r6, @r2
dt r0 ! rn - 1 --> rn
bf.s loop_64
add #1, r13 ! delay slot
mov.l @r15+, r10
mov.l @r15+, r11
mov.l @r15+, r12
mov.l @r15+, r13
rts
nop
Hmmm bien joué si tu arrives à optimiser l'affichage
Ton tableau static const cos_table[360] n'est pas typé explicitement.
effectivement je n'avais pas vu, c'est chose faite maintenant
Cette lib est veille, il faudrait que je la mette à jour. Les techniques que j'ai utilisé dans Windmill serait parfaites pour refaire un algo de zoom et surtout un algo de rotation. Car celui là laisse des trous. Et aussi un algo de zoom+rotation. (En fait se serait même algo pour tous )
Je serais curieux de voir l'algo que tu utilises pour Windmill parce que c'est incroyablement trop jolie <3
Juste à titre d'information, il est plus "lent" que l'ancien algo ?
Citer : Posté le 02/10/2018 20:29 | #
Je crois que tu t'es planté sur la gestion des octets individuels au début et à la fin de la zone : il n'y en a pas autant de chaque côté, au contraire plus il y en a d'un côté et moins il y en a de l'autre.
Laisse-moi te suggérer plus con :
- Tu écris 4 octets nuls au début de la zone.
- Tu écris 4 octets nuls à la fin de la zone.
- Tu écris 0x3fc octets par paquets de 4 sur une zone au milieu.
Comme ça, tu vas écrire certains octets plusieurs fois, mais on s'en fout. Actuellement le temps que tu passes sur les calculs et les tests est trop long, ce n'est pas très productif. Mieux vaut faire quelques écritures en plus si c'est plus rapide que de calculer le nombre minimal qu'on peut en faire.
Pour le display vram, c'est le principe ; je n'ai pas lu assez en détail pour te dire "ça marche" ou "ça va planter" : disons que si ça marche alors tu peux conclure que c'est du code de qualité décente.
Citer : Posté le 02/10/2018 21:42 | #
Juste à titre d'information, il est plus "lent" que l'ancien algo ?
Citer : Posté le 17/10/2018 11:21 | #
Je crois que tu t'es planté sur la gestion des octets individuels au début et à la fin de la zone : il n'y en a pas autant de chaque côté, au contraire plus il y en a d'un côté et moins il y en a de l'autre.
Laisse-moi te suggérer plus con :
- Tu écris 4 octets nuls au début de la zone.
- Tu écris 4 octets nuls à la fin de la zone.
- Tu écris 0x3fc octets par paquets de 4 sur une zone au milieu.
Effectivement je me suis planté sur la gestion des octets du début et fin de vram.
Du coup, j'ai repris de 0 et j'ai essayé de faire comme tu as dit et effectivement c'est plus rapide et moins chiant à mettre en place.
Cependant, j'ai un légé problème d'alignement de mémoire, je l'ai réglé avec une bete soustraction mais je ne suis pas sure pour la fiabilité du truc du coup....
_asm_clear_vram: ! r4 = address vram
mov.w 1f, r6 ! 1020 because we will clear 4 first and last octet
extu.w r6, r6 ! clean r6
add r4, r6 ! r6 = address vram + 1020
mov #0, r7 ! r7 = 0
mov #4, r5 ! r5 = 4
mov #3, r1 ! r1 = 3
extu.b r5, r5 ! clear r5
extu.b r7, r7 ! clear r7
extu.b r1, r1 ! clear r1
and r4, r1 ! r1 = r5 & 3
sub r1, r5
add #-3, r5 ! BUG FIX (?)
mov.b r7, @r6
add #1, r6
mov.b r7, @r6
add #1, r6
mov.b r7, @r6
add #1, r6
mov.b r7, @r6
mov.b r7, @r4
add #1, r4
mov.b r7, @r4
add #1, r4
mov.b r7, @r4
add #1, r4
mov.b r7, @r4
mov #255, r2
mov r4, r3
add r5, r3
extu.b r2, r2
loop_long:
mov.l r7, @r3
dt r2
bf.s loop_long
add #4, r3
rts
nop
1: .word 0x03fc
grosso modo pour m'aligner en mémoire je fais: vram + (4 - (vram&3)) mais je me retrouve un octet trop "loin" et mal aligné donc je sais pas trop ou je me suis planté...
Au passage je donne des nouvelles de warning Forever: Il n'est pas abandonné, seulement j'aimerait pouvoir comprendre le fonctionnement des timers et des interruptions avant de continuer donc il n'y aura pas d'update avant pas mal de temps (de plus l'école me mange pas mal de mon temps).
Citer : Posté le 17/10/2018 22:22 | #
Quand tu fais mov #i, rn, la valeur est automatiquement mise sur 32-bits donc tu n'as pas besoin de faire un ext derrière. Tu n'as besoin de faire un ext que si tu as utilisé mov.b ou mov.w.
Ensuite, si tu utilises r0 alors tu peux utiliser l'instruction raccourcie and #3, r0 sans avoir à charger 3 dans un registre.
Ton calcul est assez bizarre. Je te laisse te convaincre que tu peux faire comme ça :
1. Entre vram et vram + 4 (donc 4 address vram, vram + 1, vram + 2, vram + 3) : mettre les octets à zéro (pas de problème d'alignment)
2. Écrire 1020 octets par groupes de 4 en partant de (vram + 4) & ~3
3. Entre vram + 1020 et vram + 1024 : écrire 4 octets à zéro
N'hésite pas à tester tous les cas (vram = 4n, 4n+1, 4n+2, 4n+3) pour t'en persuader. Jusque-là tu as des -3, et des petits trucs bizarres qui montrent que tu connais les notions mais que tu n'as pas prouvé que ça marche. Si tu as des doutes je peux te faire un cas (genre vram = 4n+1).