clinuxjpegx11libjpeg

JPEG image doesn't display properly in my C photo viewer (using X11 and jpeglib)


I am making a minimalistic and light weight picture viewer in C for Linux. I use the libraries X11 to create the window and jpeglib to read a JPEG file.

I am facing two problems :

Examples (sorry for the low quality, some images were to big to be shown entirely from my program) :

Example with a big tall image

Example with a big wide image

Example with a small wide image

Here is the code :

#include    <stdio.h>
#include    <stdlib.h>
#include    <jpeglib.h>
#include    <X11/Xlib.h>
#include    <X11/Xutil.h>

int main(int argc, char** argv){

    // Ouverture du fichier JPEG - Opens the JPEG file
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    if (argc != 2){
        fprintf(stderr, "[ERREUR] Mauvais arguments entres, exemple : %s <fichier image>\n", argv[0]);
        return -1;
    }

    FILE* imgFile = fopen(argv[1], "rb");
    if(imgFile == NULL){
        fprintf(stderr, "[ERREUR] Impossible d'ouvrir le fichier \"%s\".\n", argv[1]);
        return -1;
    }

    printf("Ouverture du fichier \"%s\" reussie.\n", argv[1]);

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, imgFile);
    (void) jpeg_read_header(&cinfo, TRUE);
    (void) jpeg_start_decompress(&cinfo);

    printf("Largeur : %d\n", cinfo.output_width);
    printf("Hauteur : %d\n", cinfo.output_height);
    printf("Nombre de composantes : %d\n", cinfo.output_components);

    printf("Lecture de l'image en cours...\n");

    // Création de la fenêtre - Creates the window
    Display *display = XOpenDisplay(NULL);
    XEvent event;
    int screen = DefaultScreen(display);
    Window root = RootWindow(display, screen);

    if (display == NULL){
        fprintf(stderr, "[ERREUR] Impossible de créer la fenêtre.\n");
        return -1;
    }

    Window window = XCreateSimpleWindow(display, root, 0, 0, cinfo.output_width, cinfo.output_height, 0, 0, 0);

    printf("Creation de la fenetre reussie.\n");

    XMapWindow(display, window);

    // Boucle parcours de l'image - Image browsing loop
    unsigned char *image32 = (unsigned char *)malloc(cinfo.output_width*cinfo.output_height*4);
    unsigned char *p = image32;
    // Pour chaque ligne - for each line
    while (cinfo.output_scanline < cinfo.output_height){
        JSAMPROW buffer[1];
        int row_stride = cinfo.output_width * cinfo.output_components;
        buffer[0] = &image32[cinfo.output_scanline * row_stride];
        jpeg_read_scanlines(&cinfo, buffer, 1);
        // Pour chaque pixel de la ligne - for each pixel of the  line
        for(unsigned int i=0; i<cinfo.output_width; i++){
            *p++ = buffer[0][i * cinfo.output_components + 2]; // B
            *p++ = buffer[0][i * cinfo.output_components + 1]; // G
            *p++ = buffer[0][i * cinfo.output_components];     // R
            *p++ = 0;
        }
    }
    printf("Lecture de l'image terminee.\n");

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(imgFile);

    printf("Fermeture du fichier reussie.\n");

    XImage* ximage = XCreateImage(display, DefaultVisual(display, 0), DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char*)image32, cinfo.output_width, cinfo.output_height, 32, 0);

    printf("Creation de l'image reussie.\n");

    // Affichage de l'image - Shows the image
    XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, 0, 0, cinfo.output_height, cinfo.output_width);
    XFlush(display);
    printf("Affichage de l'image reussie.\n");

    while(XNextEvent(display, &event) == 0){

    }

    XDestroyImage(ximage);
    XUnmapWindow(display, window);
    XDestroyWindow(display, window);
    XCloseDisplay(display);

    printf("Fermeture de la fenetre reussie.\n");

    return 0;
}

This is the first time I use these libraries so I'm a bit lost, I though the problem would be the number of channels since all the images I tried do not have an alpha channel but removing the line *p++ = 0; in my pixels loop didn't help me. I also think the depth of the image could cause these problems but I have no idea if I have to declare the depth and where to.

What should I do to fix these issues?

Thanks for your help.


Solution

  • Your problem comes from the jpeg image reading.

    You store the decoded line in the image buffer, then, you try to write into the image (*p++==) from buffer[0] which point also on image32

    To read correctly, you should create a buffer to store the line being read.

    Corrected reading is:

    // memory to store line
    unsigned char *linebuffer = malloc(cinfo.output_width * cinfo.output_components);
    
    while (cinfo.output_scanline < cinfo.output_height){
        JSAMPROW buffer[1];
    
        buffer[0] =  linebuffer;
    
        jpeg_read_scanlines(&cinfo, buffer, 1); 
    
        // Pour chaque pixel de la ligne - for each pixel of the  line
        for(unsigned int i=0; i<cinfo.output_width; i++){
            *p++ = linebuffer[i * cinfo.output_components + 2]; // B
            *p++ = linebuffer[i * cinfo.output_components + 1]; // G
            *p++ = linebuffer[i * cinfo.output_components];     // R
            *p++ = 0;
        }
    }
    free(linebuffer);
    

    And also, you mixed width and height in XPutImage

    // corrected call
    XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, 0, 0, cinfo.output_width, cinfo.output_height);