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 :
My program doesn't display the image entirely, it only shows the bottom-left corner of it. Also, it shows multiples portions of that image without colours above it.
When the image is wider than it's tall, the programs shows it the same way than I described but with a 90° rotation on the left.
Examples (sorry for the low quality, some images were to big to be shown entirely from my program) :
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.
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);