Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » libJPEG pour Casio prizm/G90+E
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

libJPEG pour Casio prizm/G90+E

Posté le 08/05/2022 21:17

Hello,

Comme annoncé dans l'update de la SDL_image, il est désormais possible d'importer des images JPG (ou JPEG) dans les addins gint grâce à la libJPG recompilée pour nos chères Casio CG10/20/50 (PRIZM et G90+E).

Cette librairie a pour but de permettre la lecture des fichiers JPG et de rendre les données du fichiers accessible sous une forme facilement exploitable (en gros un tableau de pixels (R,G,B,A) associé à une structure d'entête contenant diverses infos utiles telles que la largeur et la hauteur de l'image lue).

Cette librairie est utilisée par la SDL_image mais il peut s'avérer utile le l'utiliser en dehors de la SDL, par exemple pour un addin gint avec dimage(). L'un des principaux avantage du format JPG étant d'offrir des ratios de compression d'images très importants, avec ou sans perte de qualité.

Ceci est tout à fait possible et je vous prépare la version "standalone" que vous pourrez utiliser pour créer une image bopti_image_t (format utilisé par gint via les méthodes dimage et dsubimage).

Il faudra bien penser à disposer de cette lib pour pouvoir utiliser le code joint, ainsi que de mettre les options de linkage adhoc dans le Makefile ou CMakeLists.txt (Je ferai un tuto quand tout sera fignolé aux petits oignons , je commence à avoir pas mal de taf pour écrire les tutos qui vont bien, coder c'est bien, mais documenter c'est mieux).

Code pour convertir un JPG au format bopti - Cliquer pour dérouler
Cliquer pour enrouler


#include <gint/gint.h>
#include <gint/display.h>
#include <gint/keyboard.h>
#include <stdio.h>
#include <stdlib.h>

#include <jpeglib.h>
#include <jerror.h>



struct imgRawImage
{
    unsigned int numComponents;
    unsigned long int width, height;

    unsigned char* lpData;
};


struct imgRawImage* loadJpegImageFile(char* lpFilename)
{
    struct jpeg_decompress_struct info;
    struct jpeg_error_mgr err;

    struct imgRawImage* lpNewImage;

    unsigned long int imgWidth, imgHeight;
    int numComponents;

    unsigned long int dwBufferBytes;
    unsigned char* lpData;

    unsigned char* lpRowBuffer[1];

    FILE* fHandle;

    fHandle = fopen(lpFilename, "rb");
    if(fHandle == NULL)
    {
#ifdef DEBUG
        fprintf(stderr, "%s:%u: Failed to read file %s\n", __FILE__, __LINE__, lpFilename);
#endif
        return NULL; /* ToDo */
    }

    info.err = jpeg_std_error(&err);
    jpeg_create_decompress(&info);

    jpeg_stdio_src(&info, fHandle);
    jpeg_read_header(&info, TRUE);

    jpeg_start_decompress(&info);
    imgWidth = info.output_width;
    imgHeight = info.output_height;
    numComponents = info.num_components;

    dwBufferBytes = imgWidth * imgHeight * 3; /* We only read RGB, not A */
    lpData = (unsigned char*)malloc(sizeof(unsigned char)*dwBufferBytes);

    lpNewImage = (struct imgRawImage*)malloc(sizeof(struct imgRawImage));
    lpNewImage->numComponents = numComponents;
    lpNewImage->width = imgWidth;
    lpNewImage->height = imgHeight;
    lpNewImage->lpData = lpData;

    /* Read scanline by scanline */
    while(info.output_scanline < info.output_height)
    {
        lpRowBuffer[0] = (unsigned char *)(&lpData[3*info.output_width*info.output_scanline]);
        jpeg_read_scanlines(&info, lpRowBuffer, 1);
    }

    jpeg_finish_decompress(&info);
    jpeg_destroy_decompress(&info);
    fclose(fHandle);

    return lpNewImage;
}

size_t image_size_profile(int width, int height)
{
    size_t size = sizeof(bopti_image_t);
    size += width * height * 2;
    return size;
}

void set_pixel(bopti_image_t *img, int x, int y, int color)
{
    if((unsigned)x >= img->width || (unsigned)y >= img->height)
        return;
    uint8_t *bytes = (void *)img->data;
    img->data[y * img->width + x] = color;
}


bopti_image_t *convert_jpg_to_bopti( char *filename )
{
    struct imgRawImage* myJPEG;
    myJPEG = (struct imgRawImage*) gint_world_switch( GINT_CALL( loadJpegImageFile, "testimg.jpg" ) );
    if (myJPEG==NULL) return NULL;

    int Wmax = myJPEG->width;
    if (myJPEG->width%2==1) Wmax++; // si largeur impaire, on rajoute un pixel transparent

    size_t size = image_size_profile(Wmax, myJPEG->height);
    bopti_image_t *image_temp = malloc( size );
    if(!image_temp) return NULL;

    image_temp->profile = 0;    // par defaut on a pas la transparence (on updatera si besoin après)
    image_temp->alpha = 0x0001;
    image_temp->width = Wmax;
    image_temp->height = myJPEG->height;

    uint16_t color;

    for (long unsigned int v=0; v<myJPEG->height; v++ )
    {
        for( long unsigned int u=0; u<myJPEG->width; u++ )
        {
            if (myJPEG->numComponents == 1)
            {
                unsigned char Grey = myJPEG->lpData[v*myJPEG->width+u]>>3;
                set_pixel(image_temp, u, v, C_RGB( Grey, Grey, Grey ) );
            }
            else if (myJPEG->numComponents == 3)
            {
                unsigned char R = myJPEG->lpData[(v*myJPEG->width+u)*3+0]>>3;
                unsigned char G = myJPEG->lpData[(v*myJPEG->width+u)*3+1]>>3;
                unsigned char B = myJPEG->lpData[(v*myJPEG->width+u)*3+2]>>3;
                color = C_RGB( R, G, B );
                if (color!=0x0001) set_pixel(image_temp, u, v, color);
                else set_pixel(image_temp, u, v, 0x0000);
            }
            else if (myJPEG->numComponents == 4)
            {
                unsigned char R = myJPEG->lpData[(v*myJPEG->width+u)*4+0]>>3;
                unsigned char G = myJPEG->lpData[(v*myJPEG->width+u)*4+1]>>3;
                unsigned char B = myJPEG->lpData[(v*myJPEG->width+u)*4+2]>>3;
                unsigned char A = myJPEG->lpData[(v*myJPEG->width+u)*4+3];
                if (A>=128) set_pixel(image_temp, u, v, C_RGB( R, G, B ) );
                else
                {
                    set_pixel(image_temp, u, v, 0x0001);   // set transparency
                    image_temp->profile = 1;    // on mets à jour le profil car il y a de la transparence
                }
            }
        }
        if (Wmax!=myJPEG->width)    // si on doit ajouter un pixel supplémentaire sur la largeur
        {
            set_pixel(image_temp, Wmax, v, 0x0001);
        }
    }

    free(myJPEG->lpData);
    free(myJPEG);

    return image_temp;
}




bopti_image_t *image = NULL;
bopti_image_t *image2 = NULL;

int main(void)
{
    // Jute ici pour désactiver le triple buffering par défaut
    uint16_t *vram1, *vram2;
    dgetvram(&vram1, &vram2);
    dsetvram(vram1, vram1);



    dclear(C_WHITE);
    dtext(1, 190, C_BLACK, "Sample libcJPG to bopti test :");
    dupdate();

    image = convert_jpg_to_bopti( "testimg.jpg" );
    image2 = convert_jpg_to_bopti( "nofile.jpg" );  // fichier qui n'existe pas, controle de la stabilité

    dimage( 0, 0, image );
    dimage( 0, 0, image2 ); // ne doit pas planter

    dsubimage( 200, 50, image, 21,15, 120, 88, DIMAGE_NONE );
    dsubimage( 200, 50, image2, 21,15, 120, 88, DIMAGE_NONE ); // idem, sans plantage

    dupdate();
    getkey();

    if (image!=NULL) free(image);
    if (image2!=NULL) free(image2);

    return 1;
}



et ce qui donne sur l'écran (la grosse image de rose est un dimage() obtenu depuis une image externe en JPG et la petite est obtenue via un dsubimage() ).


On peut donc bien utiliser les fonctions usuelles de gint avec la librairie.

Par contre attention, contrairement à l'habitude, les images ne sont pas allouée en statique, il faut donc bien penser à libérer la mémoire avec un free() et surtout attention à ne pas dépasser la taille maxie de RAM !!!

Pour info cette image de rose, de dimension 227x149 pixels en RGB888 pèse :
- 49,3ko en PNG
- 5,8ko en JPG
on comprend vite l'intérêt de ce format.

Le dépôt gitea est en cours de fabrication. Je passerai les infos asap.

Ciao

Sly


Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 09/05/2022 18:38 | #


Le dépôt Gitea est désormais opérationnel.
La libJPEG peut donc être installée via un

giteapc install Slyvtt/cJPEGv9d


de manière automatique (ou presque).

Ciao

Sly

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 28/05/2022 12:37 | #


Hello,

suite au changement de gint <2.7.1 vers 2.8 avec l'introduction des nouvelles routines image et du nouveau format image_t en remplacement du "vieux" format bopti_image_t, je vous fais une MàJ de la routine d'import de libJPG vers ce nouveau format image_t.

Donc si vous êtes en gint <2.7.1, c'est l'ancienne routine qu'il faut utiliser (voir deux posts plus haut), si vous êtes en gint 2.8 (ou ultérieur, cela viendra), il vous faudra désormais utiliser le code suivant :

Cliquez pour découvrir
Cliquez pour recouvrir


#include <gint/gint.h>
#include <gint/display.h>
#include <gint/keyboard.h>
#include <stdio.h>
#include <stdlib.h>

#include <jpeglib.h>
#include <jerror.h>



struct imgRawImage
{
    unsigned int numComponents;
    unsigned long int width, height;
    unsigned char* lpData;
};


struct imgRawImage* loadJpegImageFile(char* lpFilename)
{
    struct jpeg_decompress_struct info;
    struct jpeg_error_mgr err;

    struct imgRawImage* lpNewImage;

    unsigned long int imgWidth, imgHeight;
    int numComponents;

    unsigned long int dwBufferBytes;
    unsigned char* lpData;

    unsigned char* lpRowBuffer[1];

    FILE* fHandle;

    fHandle = fopen(lpFilename, "rb");
    if(fHandle == NULL)
    {
        return NULL;
    }

    info.err = jpeg_std_error(&err);
    jpeg_create_decompress(&info);

    jpeg_stdio_src(&info, fHandle);
    jpeg_read_header(&info, TRUE);

    jpeg_start_decompress(&info);
    imgWidth = info.output_width;
    imgHeight = info.output_height;
    numComponents = info.num_components;

    dwBufferBytes = imgWidth * imgHeight * 3;
    lpData = (unsigned char*)malloc(sizeof(unsigned char)*dwBufferBytes);

    lpNewImage = (struct imgRawImage*)malloc(sizeof(struct imgRawImage));
    lpNewImage->numComponents = numComponents;
    lpNewImage->width = imgWidth;
    lpNewImage->height = imgHeight;
    lpNewImage->lpData = lpData;

    while(info.output_scanline < info.output_height)
    {
        lpRowBuffer[0] = (unsigned char *)(&lpData[3*info.output_width*info.output_scanline]);
        jpeg_read_scanlines(&info, lpRowBuffer, 1);
    }

    jpeg_finish_decompress(&info);
    jpeg_destroy_decompress(&info);
    fclose(fHandle);

    return lpNewImage;
}


image_t *convert_jpg_to_image_t( char *filename )
{
    struct imgRawImage* myJPEG;
    myJPEG = (struct imgRawImage*) gint_world_switch( GINT_CALL( loadJpegImageFile, "testimg.jpg" ) );
    if (myJPEG==NULL) return NULL;

    image_t *image_temp;
    int Wmax = myJPEG->width;
    if (Wmax%2==1)
    {
        Wmax++; // si largeur impaire, on rajoute un pixel transparent
        image_temp = image_alloc( Wmax, myJPEG->height, IMAGE_RGB565A );    // le format devient avec couleur transparente
    }
    else
        image_temp = image_alloc( Wmax, myJPEG->height, IMAGE_RGB565 );     // sinon le format est sans transparence

    if(!image_temp) return NULL;

    uint16_t color;

    for (long unsigned int v=0; v<myJPEG->height; v++ )
    {
        for( long unsigned int u=0; u<myJPEG->width; u++ )
        {
            if (myJPEG->numComponents == 1)
            {
                unsigned char Grey = myJPEG->lpData[v*myJPEG->width+u]>>3;
                image_set_pixel(image_temp, u, v, C_RGB( Grey, Grey, Grey ) );
            }
            else if (myJPEG->numComponents == 3)
            {
                unsigned char R = myJPEG->lpData[(v*myJPEG->width+u)*3+0]>>3;
                unsigned char G = myJPEG->lpData[(v*myJPEG->width+u)*3+1]>>3;
                unsigned char B = myJPEG->lpData[(v*myJPEG->width+u)*3+2]>>3;
                color = C_RGB( R, G, B );
                if (color!=0x0001) image_set_pixel(image_temp, u, v, color);
                else image_set_pixel(image_temp, u, v, 0x0000);
            }
            else if (myJPEG->numComponents == 4)    // il y a un canal alpha dans l'image (exotique pour du JPEG), donc on update le format
            {
                unsigned char R = myJPEG->lpData[(v*myJPEG->width+u)*4+0]>>3;
                unsigned char G = myJPEG->lpData[(v*myJPEG->width+u)*4+1]>>3;
                unsigned char B = myJPEG->lpData[(v*myJPEG->width+u)*4+2]>>3;
                unsigned char A = myJPEG->lpData[(v*myJPEG->width+u)*4+3];
                if (A>=128) image_set_pixel(image_temp, u, v, C_RGB( R, G, B ) );
                else
                {
                    image_temp->format = IMAGE_RGB565A; // on mets à jour le profil car il y a de la transparence
                    image_set_pixel(image_temp, u, v, image_alpha(IMAGE_RGB565A));   // set transparency
                }
            }
        }
        if (Wmax!=myJPEG->width)    // si on doit ajouter un pixel supplémentaire sur la largeur
        {
            image_set_pixel(image_temp, Wmax-1, v, image_alpha(IMAGE_RGB565A));
        }
    }

    free(myJPEG->lpData);
    free(myJPEG);

    return image_temp;
}




image_t *image = NULL;
image_t *image2 = NULL;

int main(void)
{
    // Jute ici pour désactiver le triple buffering par défaut
    uint16_t *vram1, *vram2;
    dgetvram(&vram1, &vram2);
    dsetvram(vram1, vram1);



    dclear(C_WHITE);
    dtext(1, 190, C_BLACK, "Simple libcJPG to image_t (gint 2.8) test :");
    dupdate();

    image = convert_jpg_to_image_t( "testimg.jpg" );
    image2 = convert_jpg_to_image_t( "nofile.jpg" );  // fichier qui n'existe pas, controle de la stabilité


    dimage( 0, 0, image );
    dimage( 0, 0, image2 );


    dsubimage( 200, 50, image, 21,15, 120, 88, DIMAGE_NONE );
    dsubimage( 200, 50, image2, 21,15, 120, 88, DIMAGE_NONE ); // idem, sans plantage

    dupdate();
    getkey();

    if (image!=NULL) free(image);
    if (image2!=NULL) free(image2);

    return 1;
}



a priori l'inversion (ou mix entre les versions de gint/cette routine) n'est pas possible, vous aurez forcément une erreur à la compilation soit pour cause de membre .profil absent (remplacé par .format dans gint 2.8), soit pour cause d'absence de définition de image_t.

A plus

Sly

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)

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 v42 © créé par Neuronix et Muelsaco 2004 - 2022 | Il y a 75 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