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