Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » libimg: Transformation et composition d'images pour gint
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

libimg: Transformation et composition d'images pour gint

Posté le 10/03/2020 16:08

Ce topic fait partie de la série de topics du fxSDK.

Salut ! Juste à temps pour le CPC #26, voici une bibliothèque pour gint permettant de jouer avec les images sur Graph mono et Graph 90 Ça a été demandé par KikooDX (ici, ici).


Un petit aperçu de ce qu'on peut faire.

Voilà les fonctionnalités principales :

• Gestion complète de la transparence, composition alpha.
• Miroir horizontal et vertical, rotation de 90/180/270 degrés, agrandissement par facteur entier.
• Éclaircissement et assombrissement, fondu au blanc, recoloriage avec une couleur unie.
• Presque toutes les transformations peuvent être utilisées en place.
• Système de positionnement et références à des sous-images très flexible.

La suite de ce topic est un tutoriel d'utilisation directement traduit du README du dépôt, qui est en anglais. Enjoy!

Conversion des images et rendu VRAM

La première chose à faire pour utiliser libimg est de convertir les images au format img_t. Ça se fait en sélectionnant le type libimg-image dans les paramètres du fichier.

IMG.sprite.png = type:libimg-image

L'image fraîchement convertie peut être utilisée depuis le code C en donnant la déclaration externe qui convient, comme pour tout ce qui est converti par fxconv. Elle est prête à l'emploi dès que la définition de img_t et des fonctions de la bibliothèque, qui sont fournies par <libimg.h>, sont incluses.

#include <libimg.h>
extern img_t const sprite;

Faites attention au fait que les images converties sont en lecture seule, comme toujours quand c'est converti à la compilation. Donc on peut pas la modifier ou la transformer en-place ; j'y reviendrai. Pour se rappeller que l'image est en lecture seule, j'ai ajouté le mot-clé const ; c'est entièrement optionnel.

Pour afficher cette superbe image dans la VRAM, utilisez img_render_vram(). Sur les Graph mono type Graph 35+E, c'est la seule façon de dessiner dans la VRAM, car tristement la VRAM n'a pas le même format qu'une img_t. Sur Graph 90+E, des méthodes plus pétées sont disponibles.

img_render_vram(sprite, x, y);

img_render_vram(), comme toutes les autres fonctions de la bibliothèque, tient compte des pixels transparents. Seuls les pixels opaques sont copiés, ce qui permet de combiner facilement des images en les dessinant juste les unes après les autres.

Si vous avez une image en niveaux de gris, utilisez img_render_vram_gray() qui va afficher correctement les pixels gris (avec les mêmes paramètres). img_render_vram() se contentera de faire une approximation des gris par du noir et blanc.


Créer et détruire de nouvelles images

Puisque notre image convertie est en lecture seule, on ne peut pas faire grand-chose d'autre pour l'instant. Si on veut s'amuser avec les transformations et les animations, on doit créer des images dans lesquelles on peut véritablement écrire. Il y a deux moyens de le faire : soit en créant des nouvelles images vides, soit en dupliquant une image existante.

img_create() crée une nouvelle image non initialisée. La valeur (couleur) des pixels est aléatoire dans une nouvelle image, on verra plus tard comment utiliser img_fill() pour leur donner une couleur fixe. Parfois initialiser l'image n'est pas nécessaire et ne ferait que réduire les performances, donc libimg essaie de ne pas vous gêner et ne le fait pas automatiquement.

img_t new_32x48_image = img_create(32, 48);

img_copy() crée une copie d'une image existante. La copie est de la même taille que la source, mais tous les pixels sont dupliqués. On peut toujours écrire dans une copie, même si la source est en lecture seule.

img_t copy_of_sprite = img_copy(sprite);

Bien sûr, la création de nouvelles images peut échouer. Ces deux fonctions utilisent malloc() pour obtenir de la mémoire en interne, et malloc() échoue s'il n'y a pas assez de mémoire disponible. Dans cette situation, img_create() et img_copy() renvoie des images nulles, qui sont comme le pointeur NULL pour les images. Pour savoir si une image est nulle, utilisez img_null().

if(img_null(copy_of_sprite)) {
  /* Argh, copy has failed! */
}
else {
  /* Everything is fine, let's go! */
}

Une image nulle n'a pas de pixels, et ne peut pas être lue ou modifiée. Mais vous pouvez quand même donner des images nulles aux fonctions de la libimg ; elles s'en rendront compte et ne feront rien ou renverront d'autres images nulles. Pas de risque de crash avec ça.

Évidemment, puisqu'il y a un malloc(), il doit y avoir un free(). Les images crées par img_create() et img_copy() doivent être libérées une fois que vous n'en avez plus besoin. Appelez img_destroy() pour les libérer.

img_destroy(new_32x48_image);
img_destroy(copy_of_sprite);

Remarquez que les images converties comme sprite, les images nulles et les sous-images (présentées plus tard !) n'ont pas besoin d'être détruites car elles ne sont pas créées avec malloc(). Mais pour vous simplifier la vie, img_destroy() est programmé pour les détecter, donc en cas de doute libérez tout ce dont vous n'avez plus besoin et vous n'aurez pas de problème.


Transformations basiques

Il est temps de s'amuser avec nos superbes images. On va commencer par renverser horizontalement notre sprite pour simuler une animation de marche dans l'autre sens. Pour ça, on a besoin d'une image de la bonne taille qu'on va remplir de blanc. C'est important de remplir parce que le sprite a des pixels transparents, donc si on ne remplit pas la nouvelle image, certains des pixels aléatoires seront visibles autour du sprite transformé.

#include <gint/display.h>

img_t flipped_sprite = img_create(sprite.width, sprite.height);
img_fill(flipped_sprite, C_WHITE);

Cet exemple en profite pour démontrer quelques éléments importants :

• La taille d'une image peut être obtenue en lisant ses attributs width et height.
• La fonction img_fill() remplace tous les pixels d'une image par une couleur uniforme.
• Les couleurs utilisées dans libimg sont les même que celles utilisées dans gint, à l'exception de la transparent sur Graph 90+E, qui est 0x0001. J'y reviendrai très vite.

Maintenant qu'on a une image blanche avec la même taille que sprite, on peut se lancer et y générer le miroir horizontal de sprite :

img_hflip(sprite, flipped_sprite);

Et voilà. Maintenant img_render_vram(flipped_sprite, x, y) affiche la version renversée. Tant que flipped_sprite n'est pas libéré, il peut être affiché autant de fois qu'on veut. (Remarquez que la transformation n'aurait pas marché si flipped_sprite était en lecture seule. En particulier, on ne pourrait pas utiliser sprite comme la cible d'une transformation).

Mais attendez, puisque flipped_sprite était blanc à l'origine, on vient de faire un gros carré blanc tout moche sur l'écran. Ce qu'on voulait en fait c'est un fond transparent. On peut utiliser la couleur spéciale IMG_ALPHA pour l'obtenir.

/* Rend flipped_sprite complètement transparent : */
img_fill(flipped_sprite, IMG_ALPHA);

Sur les Graph mono, les couleurs de la libimg sont exactement les mêmes que les couleurs de gint, donc IMG_ALPHA est la même chose que C_NONE. Sur Graph 90+E, les choses sont un peu plus compliquées. Les couleurs sont au format RGB565, et tous les entiers de 16 bits sont des couleurs RGB565 valides, donc il n'y a pas de valeur disponible pour représenter la transparence. libimg résoud ce dilemme en décretant que 0x0001 représente la transparence. Cela signifie que la couleur 0x0001 n'est pas disponible dans les images de type img_t. Cette couleur a été choisie parce qu'elle est extrêmement sombre, indistinguable du noir, et qu'il est facile de comparer un nombre à 1 donc c'est un poil plus rapide dans le code. Retenez qu'il faut vraiment utiliser IMG_ALPHA pour obtenir de la transparence dans libimg.

Puisque remplir une image avec des pixels transparents est une opération courante, un raccourci appelé img_clear() est fourni pour le faire.

/* Rend aussi flipped_sprite complètement transparent : */
img_clear(flipped_sprite);

Le code complet appelle maintenant img_create(), img_clear() puis img_hflip() pour transformer. C'est aussi une séquence courante, donc un raccourci appelé img_hflip_create() a été conçu pour aller plus vite.

img_t flipped_sprite = img_hflip_create(sprite);

Toutes les transformations fonctionnent sur ce modèle. Cela comprend:

img_hflip() et img_vflip() qui renverse horizontalement et verticalement.
img_rotate() qui tourne de 0, 90, 180 ou 270 degrés.
img_upscale() qui multiple la taille d'un facteur entier.
img_dye() qui remplace tous les pixels opaques par une couleur unie.
img_ligthen(), img_whiten() et img_darken() qui jouent sur la luminosité.

On peut aussi commencer à mentionner des conventions utiles de la bibliothèque :

• Toutes les transformations prennent la source en la destination en premiers arguments, et ensuite le reste des paramètres comme l'angle de rotation.
• Toutes les transformations ont une variante _create() qui ne prend pas de destination en argument, et en crée une avec juste la bonne taille, puis la retourne après la transformation.


Sous-surfaces et positionnement

Jusqu'ici on n'a transformé des images que vers des destinations de la même taille, ou si vous avez essayé la rotation ou l'agrandissement, de tailles similaires. Imaginons qu'on veut créer une spritesheet avec à la fois le sprite original et la version renversée. On a un problème parce que img_hflip() ne nous permet pas de dire où placer le résultat transformé dans l'image de destination. En fait, img_hflip() place toujours le résultat dans le coin haut gauche.

C'est parce que la libimg possède un système de positionnement plus puissant que de rajouter des paramètres. Ce système est construit autour de l'idée d'extraire des références à des sous-images.

Prenons directement un exemple. On va créer une spritesheet de 32x16 avec un sprite de 16x16 sur la gauche et un sur la droite.

img_t spritesheet = img_create(32, 16);

Le sprite de droite est à la position (16,0) dans spritesheet. Si on veut l'utiliser souvent ou appliquer beaucoup de transformations, on va devoir répéter ces coordonnées de nombreuses fois, ce qui est casse-pieds et source d'erreur. À la place, on peut utiliser la fonction img_sub() pour obtenir une référence vers la moitié droite de spritesheet.

img_t right_sprite = img_sub(spritesheet, 16, 0, 16, 16);

img_sub() prend cinq paramètres : l'image source, et la position et taille de la sous-image qui nous intéresse, sous la forme x, y, w et h. Le sprite de droite commence à x=16 et y=0, et est de largeur w=16 et hauteur h=16.

img_sub() ne crée pas une nouvelle image. Elle donne simplement une nouvelle vue sur les mêmes pixels. Modifier right_sprite affecterait totalement la moitié droite de spritesheet. D'ailleurs, on n'a qu'à faire ça.

img_fill(right_sprite, C_BLACK);

Et juste comme ça, on vient de remplir uniquement la moitié de spritesheet. Appeler img_fill(spritesheet, C_BLACK) en aurait rempli la totalité. Voyez comment le système de positionnement rend img_fill() aussi polyvalent qu'une fonction de remplissage de rectangles.

Avec tout ça sous la main, on peut maintenant créer notre spritesheet :

/* Copie le sprite normal dans la moitié gauche */
img_render(sprite, spritesheet);
/* Renverse le sprite sur la moitié droite */
img_hflip(sprite, right_sprite);

Bien sûr, ce système marche avec toutes les transformations. Notez cependant que si l'image ou sous-image donnée comme destination doit être au moins aussi grande que le résultat de la transformation. Si la destination est plus petite, la transformation ne fera rien.

La sous-image ne contient pas nouveaux pixels, donc elle n'a pas besoin d'être détruite. Vous pouvez quand même appeler img_destroy() dessus et il ne se passera rien. Mais si on n'a pas besoin de la détruire, on n'a pas besoin de la stocker dans une variable. Et donc on peut renverser le sprite de cette façon :

img_hflip(sprite, img_sub(spritesheet, 16, 0, 16, 16));

Ça se lit « renverse sprite horizontalement, et écris le résultat dans spritesheet, à la position (16,0) dans un rectangle de taille 16x16 ».

Ici on sait que la taille du rectangle doit être 16x16, parce que c'est la taille du sprite. Donc on peut s'en passer en indiquant seulement une largeur et une hauteur de -1. Ça créera une référence qui commencera à la positon (16,0) et ira jusqu'au coin en bas à droite de spritesheet. img_hflip() ne modifiera quand même que les premiers 16x16 pixels dans le coin haut gauche, donc ce n'est pas grave si la référence est plus grande que nécessaire.

img_hflip(sprite, img_sub(spritesheet, 16, 0, -1, -1));

Si vous avez l'impression que c'est une construction commune, vous avez parfaitement raison ! Et donc un raccourci a été créé pour aller plus vite. Au lieu de spécifier une largeur et une hauteur de -1, on peut appeler img_at() qui les spécifiera à notre place. img_at(img, x, y) est la même chose que img_sub(img, x, y, -1, -1). Donc la transformation devient :

img_hflip(sprite, img_at(spritehseet, 16, 0));

Ce qui se lit « renverse sprite horizontalement et écris le résultat dans spritesheet, à la position (16,0) ». C'est beaucoup plus court que tout à l'heure, et comme la référence à la sous-image n'a pas besoin d'être libérée, on ne risque pas de créer de fuite de mémoire.

Les références aux sous-images sont un outil puissant et elles peuvent être utilisées d'un bon nombre de façons. Mais il y a une règle d'or : détruire l'original détruit aussi les sous-images. Une fois que vous avez appelé img_destroy(spritesheet), toutes les sous-images, y compris right_sprite, deviennent invalides et toute utilisation serait ue erreur. Gardez toujours un oeil sur les originaux !

Comme exemple de la polyvalence des références aux sous-images, remarques qu'on peut utiliser img_sub() sur la source de la transformation pour n'en transformer qu'une partie !

J'ai utilisé img_render() sans expliquer ce qu'elle faisait, donc c'est un bon moment pour y jeter un oeil.

Rendu direc sur la VRAM (Graph 90+E)

Sur Graph mono, la VRAM n'a pas le même format qu'une img_t, donc on ne peut pas utiliser la VRAM comme la destination d'une transformation. On ne peut qu'utiliser img_render_vram() pour copier une image vers la VRAM une fois qu'elle a été préparée.

Mais sur Graph 90+E, la VRAM a bel et bien le même format qu'une img_t. Pour en obtenir une référence, utilisez img_vram() :

img_t vram_as_an_image = img_vram();

Comme ce n'est qu'une référence, il n'y a pas besoin de la détruire avec img_destroy(). Le faire quand même ne produit aucun effet, donc en cas de doute, libérez toujours toutes les images après utilisation.

Cette référence vers la VRAM nous permet de transformer directement vers la VRAM sans passer par des images temporaires. Par exemple, on peut écrire :

img_hflip(sprite, img_at(img_vram(), x, y));

De la même façon, les deux appels suivants reùplissent un rectangle de la VRAM (bien que drect() soit plus rapide) :

#include <gint/display.h>

drect(x, y, w, h, color);
img_fill(img_sub(img_vram(), x, y, w, h), color);

C'est ici que la fonction img_render() commence à briller. Cette fonction copie l'image source vers l'image destination sans la transformer. Contrairement aux transformations, elle supporte le clipping, donc si la destination est plus petite que la source, elle copie uniquement les pixels visibles. (Les transformations paniquent et ne font rien si cette situation se présente.) En fait, sur Graph 90+E la fonction img_render_vram() n'est qu'un raccourci pour :

img_render(src, img_at(img_vram(), x, y));

img_render() est utilisée le plus souvent de ette façon, pour copier (faire le rendu) d'une image vers la VRAM. D'où son nom.


Transformations en-place

Certaines transformations peuvent transformer une image sans utiliser d'image destination auxiliaire. L'image source est écrasée durant la transformation, donc l'original est perdu mais il n'y a pas besoin de mémoire supplémentaire. Ça s'appelle une transformation en-place.

Il n'est possible de transformer en-place que si l'image transformée est de la même taille que l'image source. Dans la version actuelle, cela inclut toutes les transformations, sauf les rotations par 90 et 270 degrés quand l'image n'est pas carrée, et l'agrandissement par une facteur non trivial.

Pour exécuter une transformation en-place, utilisez la source comme destination :

img_t sprite_copy = img_copy(sprite);
img_hflip(sprite_copy, sprite_copy);

Il est également possible d'utiliser une sous-image d'une image comme source et une autre sous-image comme cible. L'appel ci-dessous renverse horizontalement la moitié gauche de spritesheet dans la moitié droite :

img_hflip(img_sub(spritesheet, 0, 0, 16, 16), img_at(spritesheet, 16, 0));

On peut aussi faire ça directement sur la VRAM sur Graph 90+E puisque la VRAM n'est qu'une img_t déguisée.

Remarquez cependant qu'on ne peut pas transformer si la source et la cible se recouvrent partiellement (ie. elles ne sont pas disjointes même elles en sont pas non plus la même zone). Aucune transformation ne le supporte et le résultats sera toujours faux !


Accès aux pixels et transformations personnalisées

Toute image peut être lue manuellement pour connaître la couleur des pixels, et également écrite si elle n'est pas en lecture seule. img_t est une stucture avec une description de l'image et les attributs utiles suivants :

img.width et img.height sont la largeur et la hauteur de l'image, respectivement.
img.stride est le nombre de pixels entre deux lignes. Souvent c'est plus grand que img.width !
img.pixels est le tableau des pixels.

Le format est ligne par ligne (row-major order) avec espacement (stride). Voici à quoi une image de taille 8x3 avec un espacement de 12 ressemble en mémoire. Les nombres sur le diagramme correspondent aux indices dans img.pixels.

<------ img.width ------>
+-------------------------+-------------+
|  0  1  2  3  4  5  6  7 |  .  .  .  . |  ^
| 12 13 14 15 16 18 18 19 |  .  .  .  . |  img.height
| 24 25 26 27 28 29  etc  |  .  .  .  . |  v
+-------------------------+-------------+
<------------ img.stride ------------->

Par exemple, le troisième pixel de la deuxième ligne est img.pixels[13]. Remarquez qu'il faut ajouter img.stride pour descendre d'une ligne, et non pas img.width. Par ailleurs, les indices représentés par des points, comme pixels[8], ne font pas partie de l'image et ne doivent en aucun cas être lus.

La raison derrière ce format avec espacement est pour supporter les références aux sous-images. Quand on crée une spritesheet de 32x16, l'espacement est de 32 et donc il n'y a pas de vide, comme on pourrait s'y atendre. Mais quand on extrait une moitié de 16x16, même si on ne regarde plus que la moitié des lignes, il y a quand même 32 pixels entre chaque ! C'est pour ça que l'espacement d'une image est souvent plus grand que sa largeur.

Cela signifie aussi que les images ne sont pas continues dans la mémoire, donc il n'est pas possible de remplacer tous les pixels en un seul appel à memcpy().

Voici comment une fonction fait pour itérer sur tous les pixels d'une image. Le code suivant remplace tous les pixels transparents par du blanc :

img_pixel_t *px = img.pixels;

for(int y = 0; y < img.height; y++)
{
    for(int x = 0; x < img.width; x++)
    {
        /* Et on fait ce qu'on veut avec px[x] */
        if(px[x] == IMG_ALPHA) px[x] = C_WHITE;
    }

    px += img.stride;
}

Les pixels sont de type img_pixel_t. La façon de les manipuler dépend de la plateforme visée :

• Sur Graph mono, img_pixel_t est uint8_t et ses valeurs sont les couleurs de <gint/display.h>. La couleur transparente IMG_ALPHA est égale à C_NONE.
• Sur Graph 90+E, img_pixel_t est uint16_t et ses valeurs sont les couleurs RGB565. Les couleurs opaques de <gint/display.h> peuvent être utilisées, à l'exception de IMG_ALPHA = 0x0001 qui représente la transparence. Remarquez que IMG_ALPHA n'est pas égal à C_NONE.


1, 2 Suivante
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 10/03/2020 16:58 | #


Salut ! C'est super tout ça, on va s'amuser la semaine prochaine
Merci beaucoup, c'est vraiment parfait niveau fonctionnalités.

Questions/remarques :
Quelle est la différence entre img_lighten() (que tu as écrit img_ligthen(), typo ?) et img_whiten() ? Je pense que le premier utilise des diviseurs différents pour chaque couleur et le second une division par deux simple, mais je n'en suis pas sûr.
Est-ce qu'img_upscale() peut accepter deux paramètres, un pour la hauteur et un pour la largeur ?
Une suggestion pour le coup, toutes les fonctions commencent par img_ et je trouve que ça alourdit le code. Pourquoi pas remplacer ça par la lettre i par exemple (de façon similaire aux fonctions d'affichage).
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 10/03/2020 17:19 | #


Merci ! Oui c'est une typo, je vais corriger ça. Du reste :

img_lighten() double les composantes avec saturation, c'est utilisé sur l'image du train, mais c'est pas terrible sur le pixel art. Ça augmente la luminosité. img_whiten() divise par 2 la distance entre chaque composante et sa valeur maximale, c'est utilisée sur les épées. Ça fait un fondu au blanc.
• Non, mais je pense que je vais changer ça très vite...
• Je suis pas fan, ça crée trop de conflits. J'ai fait ça avec les fonctions de dessin de gint mais c'est vraiment de l'abus. Les namespaces n'ont pas été inventés pour rien... et c'est pour ça que tous les noms de la lib commence par img_ ou IMG_.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 10/03/2020 17:34 | #


D'accord merci pour ta réponse
Je comprend pour le img_ alors
ouais ouais
Dark storm Hors ligne Labélisateur Points: 11641 Défis: 176 Message

Citer : Posté le 10/03/2020 19:46 | #


Si vraiment les img_ t'importunent, tu peux toujours faire une macro #define i_ img_. Attention aux effets de bord toutefois
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 10/03/2020 19:47 | #


Aye, non ça ne marchera pas, il faut l'identifieur complet. Tu peux faire une macro par fonction par contre

Ajouté le 10/03/2020 à 21:20 :
Les tests sur Graph 90+E sont concluants. Je vais porter l'exemple qui est donné en haut de ce poste dans gintctl à titre de test, puis pousser les modifications appropriés de gint et de fxconv.

Ensuite je pourrai passer à la Graph mono, qui est totalement supportée par la bibliothèque mais dont je n'ai pas parlé parce que je ne suis pas sûr d'avoir le temps de tout tester avant le CPC.

Ajouté le 11/03/2020 à 19:23 :
Right, donc c'est bon, j'ai juste eu à remplacer l'image de sortie par la VRAM et ça marche du tonnerre sur la Graph 90+E.

Je vais pousser les modifications nécessaires de fxconv et fxsdk pour que ça marche ce soir, puis je regarderai la Graph mono.

Ajouté le 11/03/2020 à 19:42 :
Et voilà, les modifications sont poussées. Cf Lephenixnoir/fxSDK@c79b3b1.

Ce topic n'est donc officiellement plus un WIP !

Ajouté le 15/03/2020 à 12:16 :
Au passage, voici les modifications pour Graph mono : côté fxconv et côté libimg. Pour faire le rendu en gris il faut utiliser img_render_vram_gray(), ce que j'avais oublié à l'origine.

La lib est donc maintenant compatible Graph mono et Graph 90+E sans effort !
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 14/05/2020 18:26 | #


Salut, je tente d'utiliser libimg.
Mon code ne compile pas, et je n'ai aucune idée du pourquoi.
J'ai fait du trial & error, cette ligne pose problème :
img_render(img_sub(img_elevator, 0, 0, 16, 16), img_at(img_vram(), x, y));

Le code complet est sur la branche glitchy de JTMM (https://gitea.planet-casio.com/KikooDX/jtmm/src/branch/glitchy).
Merci d'avance.
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 14/05/2020 18:49 | #


La conversion est différente de bopti, il faut spécifier type:libimg-image dans les paramètres fxconv. C'est tout au début du tuto ^^"
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 14/05/2020 18:51 | #


Lephenixnoir a écrit :
La conversion est différente de bopti, il faut spécifier type:libimg-image dans les paramètres fxconv. C'est tout au début du tuto ^^"

Mais je l'ai fait... Je crois ?
//project.cfg
IMG.elevator.png = type:libimg-image name:img_elevator

ouais ouais
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 14/05/2020 18:54 | #


My bad, j'ai lu trop vite. C'est quoi ton erreur de compilation ?
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 14/05/2020 18:55 | #


Pas de problème ^^' Merci pour ton aide
:: Making into build-cg

sh-elf-gcc -c src/levels.c -o build-cg/src/levels.c.o -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields -Wall -Wextra -Os -D FXCG50  -m4-nofpu -I include -MMD -MT build-cg/src/levels.c.o -MF build-cg/src/levels.c.d -MP
sh-elf-gcc -o build-cg/JTMM.elf build-cg/src/levels.c.o build-cg/src/collide.c.o build-cg/src/player.c.o build-cg/src/draw.c.o build-cg/src/menu.c.o build-cg/src/main.c.o build-cg/assets/img/semi_solid.png.o build-cg/assets/img/elevator13.png.o build-cg/assets/img/elevator1.png.o build-cg/assets/img/elevator6.png.o build-cg/assets/img/spike.png.o build-cg/assets/img/solid_1.png.o build-cg/assets/img/elevator8.png.o build-cg/assets/img/bouncer.png.o build-cg/assets/img/elevator2.png.o build-cg/assets/img/elevator11.png.o build-cg/assets/img/elevator5.png.o build-cg/assets/img/red.png.o build-cg/assets/img/teleporter_0.png.o build-cg/assets/img/ice.png.o build-cg/assets/img/elevator12.png.o build-cg/assets/img/blue.png.o build-cg/assets/img/elevator16.png.o build-cg/assets/img/elevator4.png.o build-cg/assets/img/elevator7.png.o build-cg/assets/img/teleporter_1.png.o build-cg/assets/img/water.png.o build-cg/assets/img/exit.png.o build-cg/assets/img/elevator10.png.o build-cg/assets/img/player.png.o build-cg/assets/img/elevator3.png.o build-cg/assets/img/blue_dot.png.o build-cg/assets/img/elevator15.png.o build-cg/assets/img/elevator9.png.o build-cg/assets/img/elevator.png.o build-cg/assets/img/red_dot.png.o build-cg/assets/img/solid_0.png.o build-cg/assets/img/elevator14.png.o -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields -Wall -Wextra -Os -D FXCG50  -m4-nofpu -I include  -T fxcg50.ld  -lgint-cg  -lgint-cg -lgcc -Wl,-Map=build-cg/map
/home/user/fxsdk/sh-elf-2.34-9.3.0/lib/gcc/sh3eb-elf/9.3.0/../../../../sh3eb-elf/bin/ld: build-cg/src/draw.c.o: in function `_draw_level':
draw.c:(.text+0x228): undefined reference to `_img_sub'
/home/user/fxsdk/sh-elf-2.34-9.3.0/lib/gcc/sh3eb-elf/9.3.0/../../../../sh3eb-elf/bin/ld: draw.c:(.text+0x22c): undefined reference to `_img_render_vram'
collect2: error: ld returned 1 exit status
make: *** [Makefile:108: JTMM.g3a] Error 1

ouais ouais
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 14/05/2020 19:07 | #


Ah mais il faut aussi ajouter -limg à ta liste de bibliothèques dans project.cfg : https://gitea.planet-casio.com/KikooDX/jtmm/src/branch/glitchy/project.cfg#L60

J'ai oublié ça dans le tuto, je vais le rajouter.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Diaowinner Hors ligne Membre Points: 42 Défis: 0 Message

Citer : Posté le 15/05/2020 06:47 | #


Thanks for the libimg!
That's the great solution of image file.
Latest Project:



DL:
A Chinese text reader.
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 15/05/2020 10:51 | #


Lephenixnoir a écrit :
Ah mais il faut aussi ajouter -limg à ta liste de bibliothèques dans project.cfg : https://gitea.planet-casio.com/KikooDX/jtmm/src/branch/glitchy/project.cfg#L60

J'ai ajouté -limg, l'erreur est différente :
$ ./build.sh
making levels.c
building for cg

:: Making into build-cg

sh-elf-gcc -c src/levels.c -o build-cg/src/levels.c.o -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields -Wall -Wextra -Os -D FXCG50  -m4-nofpu -I include -MMD -MT build-cg/src/levels.c.o -MF build-cg/src/levels.c.d -MP
sh-elf-gcc -o build-cg/JTMM.elf build-cg/src/collide.c.o build-cg/src/draw.c.o build-cg/src/levels.c.o build-cg/src/main.c.o build-cg/src/menu.c.o build-cg/src/player.c.o build-cg/assets/img/blue_dot.png.o build-cg/assets/img/blue.png.o build-cg/assets/img/bouncer.png.o build-cg/assets/img/elevator10.png.o build-cg/assets/img/elevator11.png.o build-cg/assets/img/elevator12.png.o build-cg/assets/img/elevator13.png.o build-cg/assets/img/elevator14.png.o build-cg/assets/img/elevator15.png.o build-cg/assets/img/elevator16.png.o build-cg/assets/img/elevator1.png.o build-cg/assets/img/elevator2.png.o build-cg/assets/img/elevator3.png.o build-cg/assets/img/elevator4.png.o build-cg/assets/img/elevator5.png.o build-cg/assets/img/elevator6.png.o build-cg/assets/img/elevator7.png.o build-cg/assets/img/elevator8.png.o build-cg/assets/img/elevator9.png.o build-cg/assets/img/elevator.png.o build-cg/assets/img/exit.png.o build-cg/assets/img/ice.png.o build-cg/assets/img/player.png.o build-cg/assets/img/red_dot.png.o build-cg/assets/img/red.png.o build-cg/assets/img/semi_solid.png.o build-cg/assets/img/solid_0.png.o build-cg/assets/img/solid_1.png.o build-cg/assets/img/spike.png.o build-cg/assets/img/teleporter_0.png.o build-cg/assets/img/teleporter_1.png.o build-cg/assets/img/water.png.o -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields -Wall -Wextra -Os -D FXCG50  -m4-nofpu -I include  -T fxcg50.ld  -lgint-cg -limg -lgint-cg -lgcc -Wl,-Map=build-cg/map
/home/user/opt/sh-elf-2.34-9.3.0/lib/gcc/sh3eb-elf/9.3.0/../../../../sh3eb-elf/bin/ld: cannot find -limg
collect2: error: ld returned 1 exit status
make: *** [Makefile:108 : JTMM.g3a] Erreur 1
done

Merci d'avance
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 15/05/2020 11:55 | #


Ah lol, je suis encore un imbécile. Quand j'ai ajouté la variable LIBS j'ai pas pensé que c'était pas les mêmes libs pour fx/cg souvent. Dans gintctl j'ai ça :

LIBS_FX := -limg-fx
LIBS_CG := -limg-cg

Dans ton cas c'est surtout la deuxième ligne qui compte. J'ai remplacés LIBS par cette paire de valeurs il y a deux mois. Il faut que tu modifies ton project.cfg comme dans le commit, à savoir :

• Remplacer LIBS par les deux lignes LIBS_FX et LIBS_CG (si tu compiles pas pour fx tu peux laisser LIBS_FX vide)
• Remplacer dans LDFLAGS_FX et LDFLAGS_CG l'utilisation de la variable LIBS par les deux nouvelles variables

Quand Kirafi m'a demandé un outil automatique pour compiler avec le fxSDK c'était fait pour être une création de projet rapide, pas une boîte noire qui vous permet de tout compiler sans savoir ce qui se passe. C'est pas aussi facile. x_x
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 15/05/2020 11:58 | #


D'accord merci ^^'
Vu que ce n'est pas un projet FX j'ai juste mis -limg-cg. Ça compiler, merci
ouais ouais
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 12/01/2022 00:19 | # | Fichier joint


Salut, propositions de fonctionnalités :

fonction qui renvoie la couleur dominante d'une image
et/ou
fonction qui renvoie la moyenne des couleurs des pixels d'une image

Merci beaucoup pour ta considération
ouais ouais
Fife86 Hors ligne Membre Points: 839 Défis: 0 Message

Citer : Posté le 20/06/2024 15:14 | #


Bonjour Lephenixnoir,

Pour la graph 35+, soit la FX9860G, seul libimg permet des opérations sur les images ?

J'aimerai réaliser une opération hflip sur une bopti_image_t car les bopti_image_t prennent considérablement moins de place en mémoire que les img_t.

Y'a t'il un moyen de réaliser un hflp sur une bopti_image_t ou bien, y a t'il un moyen de réduire la taille des .o lorsque l'image est converti en libimg-image ?

Merci d'avance
Fife
It's Show Time !!!
Mes Jeux :
- Street Fighter : Pour les accrocs du free-fight.
- Kirby's DreamLand : Gobe , Gobe , Gobe !!!
- L'invasion Seanchans : Détruit la flotte ennemis a bord du "Danseur des vagues".


< Le recoin du C-Engine >
Lephenixnoir En ligne Administrateur Points: 24672 Défis: 170 Message

Citer : Posté le 20/06/2024 15:48 | #


Coucou, il n'y a pas de solution qui soit à la fois économe en mémoire et en espace. Avec libimg, tu as 1 octet par pixel, mais c'est relativement rapide. Avec bopti, tu as 1 bit par pixel (ou plus selon le format, mais de cet ordre), mais inverser serait lent.

Moralement c'est facile, dans le format bopti les lignes sont stockées les unes après les autres; chacune comme une série d'entiers 32-bit. Sur les images 1-bit il te suffit de prendre la série d'entiers 32-bit correspondant à chaque ligne et d'inverser l'ordre des bits.

Sans tester, ça donnerait un truc comme ça (en supposant que l'image soit accessible en écriture dans ses métadonnées fxconv) :

#include <stdint.h>

static uint32_t revert_bit_order(uint32_t x)
{
    /* Can be optimized */
    uint32_t y = 0;
    for(int i = 0; i < 32; i++) {
        y <<= 1;
        y |= (x & 1);
        x >>= 1;
    }

    return y;
}

void hflip(bopti_image_t *img)
{
    int lw = (img->width + 31) >> 5;
    int shift = (32 - img->width) & 31;
    int layers = image_layer_count(img->profile);

    uint32_t *data = (void *)img->data;

    for(int y = 0; y < img->height; y++) {
        /* We have interleaved layers in each row */
        for(int l = 0; l < layers; l++) {
            /* First revert the bit order */
            for(int i = 0; i < (lw+1) / 2; i++) {
                int j1 = l + i*layers;
                int j2 = l + (lw-i-1)*layers;

                uint32_t d1 = data[j1];
                uint32_t d2 = data[j2];

                data[j1] = revert_bit_order(d2);
                data[j2] = revert_bit_order(d1);
            }

            /* Then shift left to move the padding back in place */
            for(int i = 0; i < lw; i++)
                data[l + i*layers] <<= shift;
        }
    }
}

Edit : oups j'ai oublié de left-shift d'une valeur à l'autre, il va y avoir du blanc en trop avec cette version

Mais c'est lent. Un compromis intermédiaire serait de générer une copie inversée de ton asset, qui coûtera toujours moins cher que la version libimg.

Deux notes. D'abord, c'est fondamentalement lié au fait que la VRAM a une unité d'un bit donc on manipule jamais des pixels directement ; on manipule des groupes de plus (dans gint, 32 autant que possible). Sur la Graph 90+E un pixel fait 2 octets donc si on avait une fonction de rendu d'image 1-bit alors on pourrait l'adapter facilement simplement parce qu'on déroule ultimement vers un format où on manipule qu'un pixel à la fois.

Deuxième note, les fonctions libimg qui sont déjà disponibles nativement dans gint pour les images couleur depuis un moment (rendant libimg obsolète sur Graph 90) seront disponibles dans le style de mon code ci-dessus pour les images mono aussi dans gint 3, rendant libimg obsolète pour de bon.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Fife86 Hors ligne Membre Points: 839 Défis: 0 Message

Citer : Posté le 20/06/2024 16:09 | #


J'avais pensé à faire une copie inversé de mon asset, ça double l'espace de stockage mais c'est facile à faire.

Cependant, comme il s'agit d'une spritesheet d'un personnage avec énormément d'animations différentes, j'essaie d'économiser la place que je peux pour la taille du g1a et aussi de l'espace utilisé en RAM.

J'étais en train de regarder trois autres solutions :

- A l'initialisation du programme, copier la spritesheet dans une autre bopti_image puis la hflip avec un code similaire à ton post
( Opération exécutée 1 fois, beaucoup d'espace mémoire utilisé d'un coup )

- Pendant l’exécution du programme, lorsque la frame spécifique doit être flip, copier seulement la frame dans une autre bopti_image et la hflip
( Opération exécuté à chaque changement de frame, peu d'espace mémoire utilisé )

- Modifier bopti_grid, bopti_render et bopti_render_scsp pour prendre un flag hflip et faire la transformation à chaque render
( Opération exécutée à chaque render, aucun espace mémoire utilisé )

J'était en train de toucher au code de bopti_render pour bien comprendre comment il fonctionnait, mais je n'arrive pas à ce que mes modifications soit prises en compte lorsque j'execute fxsdk build-fx. Pourtant, je fais bien giteapc build --skip-configure gint
It's Show Time !!!
Mes Jeux :
- Street Fighter : Pour les accrocs du free-fight.
- Kirby's DreamLand : Gobe , Gobe , Gobe !!!
- L'invasion Seanchans : Détruit la flotte ennemis a bord du "Danseur des vagues".


< Le recoin du C-Engine >
Fife86 Hors ligne Membre Points: 839 Défis: 0 Message

Citer : Posté le 20/06/2024 16:51 | #


Sans tester, ça donnerait un truc comme ça (en supposant que l'image soit accessible en écriture dans ses métadonnées fxconv)


Comment on le spécifie dans les métadonnées ? ^^'
It's Show Time !!!
Mes Jeux :
- Street Fighter : Pour les accrocs du free-fight.
- Kirby's DreamLand : Gobe , Gobe , Gobe !!!
- L'invasion Seanchans : Détruit la flotte ennemis a bord du "Danseur des vagues".


< Le recoin du C-Engine >
1, 2 Suivante

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 80 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd