Credits

Thanks to George Peter Staplin - his example was invaluable and can be found at http://www.xmission.com/~georgeps/Xlib_JPEG_tutorial.html . As you'll see a good deal of that example code went into my demo code

Intro

This code is, admittedly, a mess. But it gets the important ideas across and got the job done for my demo I had to do. I stumbled for hours with the ImageMagick code trying to figure out how to simply get it to draw something in an Xwindow without giving it the whole heaping window information and letting it take it from there. More specifically, all I wanted was it to spit back an XImage that I could splosh on my XWindow - and let me take it from there.

Well, the program below does that.

Handily, because of the way *nix shells work, you simply invoke the program with a list of any combination of wildcards or files and it takes it from there. You can say...

magicexample *.jpg /somedir/*.gif singleFile.bmp

...and it handles it nice and clean. This is because the *nix shell expands out the wildcards for you and presents them as a handy list in argv[] .

It also uses ImageMagick to scale down the image to fit in the window, doing the letterbox thing to keep proportions. Probably I could've done that more cleanly using the Magick++ interface, but it the compiler complained about missing header files referred to in other header files, and I gave up since this worked.

Things to do:

  • detect the RGB configuration. That is, whether the display is rgb or bgr in terms of the bit packing per pixel. Right now it assumes RGB, with 5 bits for red, 6 for green, 5 for blue
  • test it on a system that's other than 16 bits in color depth
  • handle the function pointer assignment more cleanly
  • scrub to catch places where I don't clean up after myself


the Code

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h> 
#include <setjmp.h>
#include <X11/keysym.h>

#include <time.h>
#include <sys/types.h>
#include <magick/api.h>

#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480

#define MAX_IMAGES 100


Window theWindow;
int g_depth; // depth of our screen
Display *display;
Colormap colormap;
GC image_gc;
unsigned short int *buffer_16bpp;
long *buffer_32bpp;


ExceptionInfo exception; // conveient global for image magick exceptions


int width, height;
int offset_x, offset_y;
int have_image = 0;

/* pointers to functions used to copy pixels
 *  based on bits per pixels screen depth
 */
void (*store_data) ();
void convert_for_16 ();
void convert_for_32 ();


/* if you don't have the ImageMagick library installed you
 *  can use this program instead of 'xloadImage' just
 *  to put pixels on the screen
 */
void makeBlackImage(XImage** theXImage)
{
      width = SCREEN_WIDTH;
      height = SCREEN_HEIGHT;

      buffer_16bpp = (unsigned short int *) malloc ((width) * (height) * 2);
      *theXImage = XCreateImage (display, CopyFromParent, g_depth, ZPixmap, 0,
            (char *) buffer_16bpp, width,
            height, 16, width * 2);

      /* Make all the pixels white
       */
      memset(buffer_16bpp, 0x00, width*height*2);
}


int xloadImage(char* theImageName, XImage** theXImage)
    {
   int i, j;
   float screenRatio, imageRatio;
   float newHeight, newWidth;


   register const PixelPacket* thePixels;

      Image
        *image,
        *images,
        *resize_image;

      ImageInfo
        *image_info;

   /* Initialize the image info structure and read an image.
    */
      image_info = CloneImageInfo( (ImageInfo *) NULL );
      (void) strcpy(image_info->filename, theImageName);
      images = ReadImage(image_info, &exception);
      if (images == (Image *) NULL)
      {
         MagickError(exception.severity, 
                     exception.reason,
                     exception.description);
      }

   /* Turn the images into a thumbnail sequence.
    */
   image = ShiftImageList(&images);
   if ( image != (Image *) NULL)
   {
      screenRatio = (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT;
      imageRatio = (float)image->columns / (float)image->rows;

      newHeight = SCREEN_HEIGHT;
      newWidth = SCREEN_WIDTH;
   
      /* if we're wider than screen proportions, make the width == SCREEN_WIDTH
       *  and scale the height
       */   
      if (imageRatio > screenRatio)
      {
         newWidth = SCREEN_WIDTH;
         newHeight = newWidth /  (float)imageRatio;
      }
      else
      {   newHeight = SCREEN_HEIGHT;
         newWidth = newHeight *  (float)imageRatio;
      }
      
      width = (int)newWidth;
      height = (int)newHeight;

      offset_x = (SCREEN_WIDTH - width) / 2;
      offset_y = (SCREEN_HEIGHT - height) / 2;

      resize_image=ResizeImage(image,
         width, height,
         MitchellFilter, 1.0, &exception);

      if (resize_image == (Image *) NULL)
      {   
         MagickError(exception.severity,
                     exception.reason,
                     exception.description);
         return 0; // need cleanup!!
      }


      width = resize_image->columns;
      height = resize_image->rows;

   if (g_depth == 16)
   {
      store_data = &convert_for_16;

      buffer_16bpp = (unsigned short int *) malloc ((width) * (height) * 2);
      *theXImage = XCreateImage (display, CopyFromParent, g_depth, ZPixmap, 0,
            (char *) buffer_16bpp, width,
            height, 16, width * 2);
   }
   else
   {
      if (g_depth == 24)
        {
         store_data = &convert_for_32;

         buffer_32bpp = malloc (width * height * 4);
         *theXImage = XCreateImage (display, CopyFromParent, g_depth, 
            ZPixmap, 0, (char *) buffer_32bpp, width,
            height, 32, width * 4);
      }
      else
      {
         if (g_depth == 32)
           {
      printf("32 bit depth\n");
            store_data = &convert_for_32;

            buffer_32bpp = malloc (width * height * 4);
            *theXImage = XCreateImage (display, CopyFromParent, g_depth, \
            ZPixmap, 0, (char *) buffer_32bpp, width, \
            height, 32, width * 4);
         }
         else
         {
            fprintf (stderr, "This is not a supported depth.%d\n",
            g_depth);
            return 0;
         }
      }
   }


   for(i = 0; i < height; i++)
   {
   // walk down the row

      thePixels =
         AcquireImagePixels(resize_image, 0, i, width, 1, &exception);

      for (j = 0; j < width; j++)
      {
         if (thePixels == (PixelPacket*) NULL)
         {
            MagickError(exception.severity,exception.reason,exception.description);
            return 0; // need cleanup!!
          }
         else
         {
            (*store_data) (width, j, i,
               thePixels->red, thePixels->green, thePixels->blue);
         }
         thePixels++;
      }
   }

         have_image = 1;

      }
      else
      {
         return 0; // need cleanup!!
      }

      DestroyImageInfo(image_info);
      return(1);
    }


/* this assumes that r, g, and b are 16 bits long.  If
 *  you want to do 8 bit values, shift them by 8 bits 
 *  less (see commented out line)
 */

void convert_for_16 (int w, int x, int y, int r, int g, int b)
{
// buffer_16bpp[y * w + x] = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
buffer_16bpp[y * w + x] = ((r >> 11) << 11) + ((g >> 10) << 5) + (b >> 11);
//buffer_16bpp[y * w + x] = ((g >> 10) << 5) + (b >> 11);
}

void convert_for_32 (int w, int x, int y, int r, int g, int b)
{
buffer_32bpp[y * w + x] = ( ((r >> 8)<< 16) +
                            ((g >> 8)<< 8) +
                            ((b >> 8)) );
}



int main (int argc, char *argv[])
{
   int i;

   int indexNow = 0;
   int numImages;

   XSetWindowAttributes window_attributes;
   int screen;

   XImage *images[MAX_IMAGES]; // the image we display
   XImage *blackImage;

   unsigned long window_mask;

   InitializeMagick(argv[0]);      // initialize IMagick w/ my path
   GetExceptionInfo(&exception); // initialize exception object

   if (argc < 2)
   {   printf("no image filename(s) specified\n");
      exit(1);
   }

   display = XOpenDisplay (NULL);
   if (!display)
   {   printf("can't execute 'XOpenDisplay'\n");
      exit(1);
   }

   screen = DefaultScreen (display);
   g_depth = DefaultDepth (display, screen);
   window_attributes.border_pixel = BlackPixel (display, screen);
   window_attributes.background_pixel = BlackPixel (display, screen);
   window_attributes.override_redirect = 0;
   window_mask = CWBackPixel | CWBorderPixel;

   makeBlackImage(&blackImage);

   numImages = argc - 1;


/* Uncomment out the next section and comment out the code that loads 
 *  images in the event handler to pre-load the images.  For my purposes
 *  I intentionally wanted to force a load of an image before I displayed
 *  each one
 */

//   for (i = 0; i < numImages; i++)
//   {   


      /* Load the image, scale down, convert to an X-Image.
       *  need to handle failures !!!
       */
//      xloadImage(argv[i + 1], &images[i]);
//   }
//

   assert(images[0] != NULL);

   theWindow = XCreateWindow (
      display, 
      DefaultRootWindow (display), 
      0, 0,
      SCREEN_WIDTH, SCREEN_HEIGHT, 
      0, 
      g_depth, 
      InputOutput, 
      CopyFromParent, 
      window_mask,
      &window_attributes);


   image_gc = XCreateGC (display, theWindow, 0, 0);

   XSelectInput(display, theWindow, 
      ExposureMask | 
      KeyPressMask | 
      ButtonPressMask | 
      StructureNotifyMask);

   XMapWindow (display, theWindow);

   // Wait for the MapNotify event
   for(;;) 
   {   XEvent e;
      XNextEvent(display, &e);
      if (e.type == MapNotify)
      {   break;
   }   }
 

   while (1)
    {
      XEvent x_event;
      XNextEvent(display, &x_event);
      switch  (x_event.type) 
      {
       case Expose:
         if (1)
         {
            /* Draw the first image
             */
            xloadImage(argv[indexNow + 1], &images[indexNow]);
            XPutImage (display, theWindow, image_gc, images[indexNow], 0, 0, 
               offset_x, offset_y, width, height);
         }
         XFlush(display);
         break;            

       case KeyPress:
         if(XLookupKeysym(&x_event.xkey, 0) == XK_Escape)
         {   exit (0);
         }

         if(XLookupKeysym(&x_event.xkey, 0) == XK_Left)
         {
            /* Destroy the last Image
             */
            XDestroyImage(images[indexNow]);
            indexNow--;
            if (indexNow < 0)
            {   indexNow = numImages - 1;
            }
   
            xloadImage(argv[indexNow + 1], &images[indexNow]);
            XPutImage (display, theWindow, image_gc, blackImage, 0, 0,
               0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            XPutImage (display, theWindow, image_gc, images[indexNow], 0, 0, 
               offset_x, offset_y, width, height);
            XFlush(display);
         }
         if(XLookupKeysym(&x_event.xkey, 0) == XK_Right)
         {
            XDestroyImage(images[indexNow]);
            indexNow++;
            if (indexNow > numImages - 1)
            {   indexNow = 0;
            }
   
            xloadImage(argv[indexNow + 1], &images[indexNow]);
            XPutImage (display, theWindow, image_gc, blackImage, 0, 0,
               0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            XPutImage (display, theWindow, image_gc, images[indexNow], 0, 0, 
               offset_x, offset_y, width, height);
            XFlush(display);
         }


         break;
      }         
   }

   DestroyMagick();
   exit(0);
} 


the Makefile

all: magick

magick: MagickExample.c
   rm -f MagickExample
   gcc `Magick-config --cflags --cppflags` MagickExample.c `Magick-config --ldflags --libs` -o MagickExample

-- MattWalsh - 25 Mar 2002

Topic revision: r2 - 21 Mar 2009 - 00:33:11 - MattWalsh
 
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback