/*
 * Copyright (C) 1989-95 GROUPE BULL 
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software. 
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 *
 * Except as contained in this notice, the name of GROUPE BULL shall not be used
 * in advertising or otherwise to promote the sale, use or other dealings in
 * this Software without prior written authorization from GROUPE BULL. 
 */

/*****************************************************************************\
* create.c:                                                                   *
*                                                                             *
*  XPM library                                                                *
*  Create an X image and possibly its related shape mask                      *
*  from the given XpmImage.                                                   *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by HeDu
 * (hedu@cul-ipn.uni-kiel.de) 4/94 
 */

#include "xpmP.h"
#include <ctype.h>

LFUNC(xpmVisualType, int, (Visual * visual));

#ifndef FOR_MSW
LFUNC(SetCloseColor, int, (Display * display, Colormap colormap,
			   Visual * visual, XColor * col,
			   Pixel * image_pixel, Pixel * mask_pixel,
			   Pixel * alloc_pixels, unsigned int *nalloc_pixels,
			   XpmAttributes * attributes,
			   XColor * cols, int ncols));
#else
/* let the window system take care of close colors */
#endif

LFUNC(SetColor, int, (Display * display, Colormap colormap, Visual * visual,
		      char *colorname, unsigned int color_index,
		      Pixel * image_pixel, Pixel * mask_pixel,
		      unsigned int *mask_pixel_index,
		      Pixel * alloc_pixels, unsigned int *nalloc_pixels,
		      Pixel * used_pixels, unsigned int *nused_pixels,
		      XpmAttributes * attributes, XColor * cols, int ncols));

LFUNC(CreateXImage, int, (Display * display, Visual * visual,
			  unsigned int depth, unsigned int width,
			  unsigned int height, XImage ** image_return));

LFUNC(CreateColors, int, (Display * display, XpmAttributes * attributes,
			  XpmColor * colors, unsigned int ncolors,
			  Pixel * image_pixels, Pixel * mask_pixels,
			  unsigned int *mask_pixel_index,
			  Pixel * alloc_pixels, unsigned int *nalloc_pixels,
			  Pixel * used_pixels, unsigned int *nused_pixels));

#ifndef FOR_MSW
/* XImage pixel routines */
LFUNC(SetImagePixels, void, (XImage * image, unsigned int width,
			     unsigned int height, unsigned int *pixelindex,
			     Pixel * pixels));

LFUNC(SetImagePixels32, void, (XImage * image, unsigned int width,
			       unsigned int height, unsigned int *pixelindex,
			       Pixel * pixels));

LFUNC(SetImagePixels16, void, (XImage * image, unsigned int width,
			       unsigned int height, unsigned int *pixelindex,
			       Pixel * pixels));

LFUNC(SetImagePixels8, void, (XImage * image, unsigned int width,
			      unsigned int height, unsigned int *pixelindex,
			      Pixel * pixels));

LFUNC(SetImagePixels1, void, (XImage * image, unsigned int width,
			      unsigned int height, unsigned int *pixelindex,
			      Pixel * pixels));
#else				/* ndef FOR_MSW */
/* FOR_MSW pixel routine */
LFUNC(MSWSetImagePixels, void, (Display * dc, XImage * image,
				unsigned int width, unsigned int height,
				unsigned int *pixelindex, Pixel * pixels));
#endif				/* ndef FOR_MSW */

#ifdef NEED_STRCASECMP
FUNC(strcasecmp, int, (char *s1, char *s2));

/*
 * in case strcasecmp is not provided by the system here is one which does
 * the trick 
 */
int
strcasecmp(s1, s2)
register char *s1, *s2;
{
 register int c1, c2;

 while (*s1 && *s2)
 {
  c1 = tolower(*s1);
  c2 = tolower(*s2);
  if (c1 != c2)
   return (c1 - c2);
  s1++;
  s2++;
 }
 return (int) (*s1 - *s2);
}

#endif

/*
 * return the default color key related to the given visual 
 */
static int
xpmVisualType(visual)
Visual *visual;
{
#ifndef FOR_MSW
 switch (visual->class)
 {
 case StaticGray:
 case GrayScale:
  switch (visual->map_entries)
  {
  case 2:
   return (XPM_MONO);
  case 4:
   return (XPM_GRAY4);
  default:
   return (XPM_GRAY);
  }
 default:
  return (XPM_COLOR);
 }
#else
 /* there should be a similar switch for MSW */
 return (XPM_COLOR);
#endif
}


typedef struct
{
 int cols_index;
 long closeness;
}   CloseColor;

static int
closeness_cmp(a, b)
Const void *a, *b;
{
 CloseColor *x = (CloseColor *) a, *y = (CloseColor *) b;

 /* cast to int as qsort requires */
 return (int) (x->closeness - y->closeness);
}

#ifndef FOR_MSW
/*
 * set a close color in case the exact one can't be set return 0 if success,
 * 1 otherwise. 
 */

static int
SetCloseColor(display, colormap, visual, col, image_pixel, mask_pixel,
	      alloc_pixels, nalloc_pixels, attributes, cols, ncols)
Display *display;
Colormap colormap;
Visual *visual;
XColor *col;
Pixel *image_pixel, *mask_pixel;
Pixel *alloc_pixels;
unsigned int *nalloc_pixels;
XpmAttributes *attributes;
XColor *cols;
int ncols;
{

 /*
  * Allocation failed, so try close colors. To get here the visual must be
  * GreyScale, PseudoColor or DirectColor (or perhaps StaticColor? What about
  * sharing systems like QDSS?). Beware: we have to treat DirectColor
  * differently. 
  */


 long int red_closeness, green_closeness, blue_closeness;
 int n;

 if (attributes && (attributes->valuemask & XpmCloseness))
  red_closeness = green_closeness = blue_closeness =
   attributes->closeness;
 else
 {
  red_closeness = attributes->red_closeness;
  green_closeness = attributes->green_closeness;
  blue_closeness = attributes->blue_closeness;
 }


 /*
  * We sort the colormap by closeness and try to allocate the color closest
  * to the target. If the allocation of this close color fails, which almost
  * never happens, then one of two scenarios is possible. Either the colormap
  * must have changed (since the last close color allocation or possibly
  * while we were sorting the colormap), or the color is allocated as
  * Read/Write by some other client. (Note: X _should_ allow clients to check
  * if a particular color is Read/Write, but it doesn't! :-( ). We cannot
  * determine which of these scenarios occurred, so we try the next closest
  * color, and so on, until no more colors are within closeness of the
  * target. If we knew that the colormap had changed, we could skip this
  * sequence. 
  *
  * If _none_ of the colors within closeness of the target can be allocated,
  * then we can finally be pretty sure that the colormap has actually
  * changed. In this case we try to allocate the original color (again), then
  * try the closecolor stuff (again)... 
  *
  * In theory it would be possible for an infinite loop to occur if another
  * process kept changing the colormap every time we sorted it, so we set a
  * maximum on the number of iterations. After this many tries, we use
  * XGrabServer() to ensure that the colormap remains unchanged. 
  *
  * This approach gives particularly bad worst case performance - as many as
  * <MaximumIterations> colormap reads and sorts may be needed, and as many
  * as <MaximumIterations> * <ColormapSize> attempted allocations may fail.
  * On an 8-bit system, this means as many as 3 colormap reads, 3 sorts and
  * 768 failed allocations per execution of this code! Luckily, my
  * experiments show that in general use in a typical 8-bit color environment
  * only about 1 in every 10000 allocations fails to succeed in the fastest
  * possible time. So virtually every time what actually happens is a single
  * sort followed by a successful allocate. The very first allocation also
  * costs a colormap read, but no further reads are usually necessary. 
  */

#define ITERATIONS 2		/* more than one is almost never necessary */

 for (n = 0; n <= ITERATIONS; ++n)
 {
  CloseColor *closenesses =
  (CloseColor *) XpmCalloc(ncols, sizeof(CloseColor));
  int i, c;

  for (i = 0; i < ncols; ++i)
  {				/* build & sort closenesses table */
#define COLOR_FACTOR       3
#define BRIGHTNESS_FACTOR  1

   closenesses[i].cols_index = i;
   closenesses[i].closeness =
    COLOR_FACTOR * (abs((long) col->red - (long) cols[i].red)
		    + abs((long) col->green - (long) cols[i].green)
		    + abs((long) col->blue - (long) cols[i].blue))
    + BRIGHTNESS_FACTOR * abs(((long) col->red +
			       (long) col->green +
			       (long) col->blue)
			      - ((long) cols[i].red +
				 (long) cols[i].green +
				 (long) cols[i].blue));
  }
  qsort(closenesses, ncols, sizeof(CloseColor), closeness_cmp);

  i = 0;
  c = closenesses[i].cols_index;
  while ((long) cols[c].red >= (long) col->red - red_closeness &&
	 (long) cols[c].red <= (long) col->red + red_closeness &&
	 (long) cols[c].green >= (long) col->green - green_closeness &&
	 (long) cols[c].green <= (long) col->green + green_closeness &&
	 (long) cols[c].blue >= (long) col->blue - blue_closeness &&
	 (long) cols[c].blue <= (long) col->blue + blue_closeness)
  {
   if (XAllocColor(display, colormap, &cols[c]))
   {
    if (n == ITERATIONS)
     XUngrabServer(display);
    XpmFree(closenesses);
    *image_pixel = cols[c].pixel;
    *mask_pixel = 1;
    alloc_pixels[(*nalloc_pixels)++] = cols[c].pixel;
    return (0);
   } else
   {
    ++i;
    if (i == ncols)
     break;
    c = closenesses[i].cols_index;
   }
  }

  /* Couldn't allocate _any_ of the close colors! */

  if (n == ITERATIONS)
   XUngrabServer(display);
  XpmFree(closenesses);

  if (i == 0 || i == ncols)	/* no color close enough or cannot */
   return (1);			/* alloc any color (full of r/w's) */

  if (XAllocColor(display, colormap, col))
  {
   *image_pixel = col->pixel;
   *mask_pixel = 1;
   alloc_pixels[(*nalloc_pixels)++] = col->pixel;
   return (0);
  } else
  {				/* colormap has probably changed, so
				 * re-read... */
   if (n == ITERATIONS - 1)
    XGrabServer(display);

#if 0
   if (visual->class == DirectColor)
   {
    /* TODO */
   } else
#endif
    XQueryColors(display, colormap, cols, ncols);
  }
 }
 return (1);
}

#define USE_CLOSECOLOR attributes && \
(((attributes->valuemask & XpmCloseness) && attributes->closeness != 0) \
 || ((attributes->valuemask & XpmRGBCloseness) && \
     (attributes->red_closeness != 0 \
      || attributes->green_closeness != 0 \
      || attributes->blue_closeness != 0)))

#else
/* FOR_MSW part */
/* nothing to do here, the window system does it */
#endif

/*
 * set the color pixel related to the given colorname, return 0 if success, 1
 * otherwise. 
 */

static int
SetColor(display, colormap, visual, colorname, color_index,
	 image_pixel, mask_pixel, mask_pixel_index,
	 alloc_pixels, nalloc_pixels, used_pixels, nused_pixels,
	 attributes, cols, ncols)
Display *display;
Colormap colormap;
Visual *visual;
char *colorname;
unsigned int color_index;
Pixel *image_pixel, *mask_pixel;
unsigned int *mask_pixel_index;
Pixel *alloc_pixels;
unsigned int *nalloc_pixels;
Pixel *used_pixels;
unsigned int *nused_pixels;
XpmAttributes *attributes;
XColor *cols;
int ncols;
{
 XColor xcolor;

 if (strcasecmp(colorname, TRANSPARENT_COLOR))
 {
  if (!XParseColor(display, colormap, colorname, &xcolor))
   return (1);
  if (!XAllocColor(display, colormap, &xcolor))
  {
#ifndef FOR_MSW
   if (USE_CLOSECOLOR)
    return (SetCloseColor(display, colormap, visual, &xcolor,
			  image_pixel, mask_pixel,
			  alloc_pixels, nalloc_pixels,
			  attributes, cols, ncols));
   else
#endif				/* ndef FOR_MSW */
    return (1);
  } else
   alloc_pixels[(*nalloc_pixels)++] = xcolor.pixel;
  *image_pixel = xcolor.pixel;
  *mask_pixel = 1;
  used_pixels[(*nused_pixels)++] = xcolor.pixel;
 } else
 {
  *image_pixel = 0;
  *mask_pixel = 0;
  /* store the color table index */
  *mask_pixel_index = color_index;
 }
 return (0);
}


static int
CreateColors(display, attributes, colors, ncolors, image_pixels, mask_pixels,
	     mask_pixel_index, alloc_pixels, nalloc_pixels,
	     used_pixels, nused_pixels)
Display *display;
XpmAttributes *attributes;
XpmColor *colors;
unsigned int ncolors;
Pixel *image_pixels;
Pixel *mask_pixels;
unsigned int *mask_pixel_index;
Pixel *alloc_pixels;
unsigned int *nalloc_pixels;
Pixel *used_pixels;
unsigned int *nused_pixels;
{
 /* variables stored in the XpmAttributes structure */
 Visual *visual;
 Colormap colormap;
 XpmColorSymbol *colorsymbols;
 unsigned int numsymbols;

 char *colorname;
 unsigned int color, key;
 Bool pixel_defined;
 XpmColorSymbol *symbol;
 char **defaults;
 int ErrorStatus = XpmSuccess;
 char *s;
 int default_index;

 XColor *cols = NULL;
 unsigned int ncols = 0;

 /*
  * retrieve information from the XpmAttributes 
  */
 if (attributes && attributes->valuemask & XpmColorSymbols)
 {
  colorsymbols = attributes->colorsymbols;
  numsymbols = attributes->numsymbols;
 } else
  numsymbols = 0;

 if (attributes && attributes->valuemask & XpmVisual)
  visual = attributes->visual;
 else
  visual = XDefaultVisual(display, XDefaultScreen(display));

 if (attributes && attributes->valuemask & XpmColormap)
  colormap = attributes->colormap;
 else
  colormap = XDefaultColormap(display, XDefaultScreen(display));

 if (attributes && attributes->valuemask & XpmColorKey)
  key = attributes->color_key;
 else
  key = xpmVisualType(visual);

#ifndef FOR_MSW
 if (USE_CLOSECOLOR)
 {
  /* originally from SetCloseColor */
#if 0
  if (visual->class == DirectColor)
  {

   /*
    * TODO: Implement close colors for DirectColor visuals. This is difficult
    * situation. Chances are that we will never get here, because any machine
    * that supports DirectColor will probably also support TrueColor (and
    * probably PseudoColor). Also, DirectColor colormaps can be very large,
    * so looking for close colors may be too slow. 
    */
  } else
  {
#endif
   int i;

   ncols = visual->map_entries;
   cols = (XColor *) XpmCalloc(ncols, sizeof(XColor));
   for (i = 0; i < ncols; ++i)
    cols[i].pixel = i;
   XQueryColors(display, colormap, cols, ncols);
#if 0
  }
#endif
 }
#endif				/* ndef FOR_MSW */

 switch (key)
 {
 case XPM_MONO:
  default_index = 2;
  break;
 case XPM_GRAY4:
  default_index = 3;
  break;
 case XPM_GRAY:
  default_index = 4;
  break;
 case XPM_COLOR:
 default:
  default_index = 5;
  break;
 }

 for (color = 0; color < ncolors; color++, colors++,
      image_pixels++, mask_pixels++)
 {
  colorname = NULL;
  pixel_defined = False;
  defaults = (char **) colors;

  /*
   * look for a defined symbol 
   */
  if (numsymbols)
  {

   unsigned int n;

   s = defaults[1];
   for (n = 0, symbol = colorsymbols; n < numsymbols; n++, symbol++)
   {
    if (symbol->name && s && !strcmp(symbol->name, s))
     /* override name */
     break;
    if (!symbol->name && symbol->value)
    {				/* override value */
     int def_index = default_index;

     while (defaults[def_index] == NULL)	/* find defined colorname */
      --def_index;
     if (def_index < 2)
     {				/* nothing towards mono, so try towards color */
      def_index = default_index + 1;
      while (def_index <= 5 && defaults[def_index] == NULL)
       ++def_index;
     }
     if (def_index >= 2 && defaults[def_index] != NULL &&
	 !strcasecmp(symbol->value, defaults[def_index]))
      break;
    }
   }
   if (n != numsymbols)
   {
    if (symbol->name && symbol->value)
     colorname = symbol->value;
    else
     pixel_defined = True;
   }
  }
  if (!pixel_defined)
  {				/* pixel not given as symbol value */

   unsigned int k;

   if (colorname)
   {				/* colorname given as symbol value */
    if (!SetColor(display, colormap, visual, colorname, color,
		  image_pixels, mask_pixels, mask_pixel_index,
		  alloc_pixels, nalloc_pixels, used_pixels,
		  nused_pixels, attributes, cols, ncols))
     pixel_defined = True;
    else
     ErrorStatus = XpmColorError;
   }
   k = key;
   while (!pixel_defined && k > 1)
   {
    if (defaults[k])
    {
     if (!SetColor(display, colormap, visual, defaults[k],
		   color, image_pixels, mask_pixels,
		   mask_pixel_index, alloc_pixels,
		   nalloc_pixels, used_pixels, nused_pixels,
		   attributes, cols, ncols))
     {
      pixel_defined = True;
      break;
     } else
      ErrorStatus = XpmColorError;
    }
    k--;
   }
   k = key + 1;
   while (!pixel_defined && k < NKEYS + 1)
   {
    if (defaults[k])
    {
     if (!SetColor(display, colormap, visual, defaults[k],
		   color, image_pixels, mask_pixels,
		   mask_pixel_index, alloc_pixels,
		   nalloc_pixels, used_pixels, nused_pixels,
		   attributes, cols, ncols))
     {
      pixel_defined = True;
      break;
     } else
      ErrorStatus = XpmColorError;
    }
    k++;
   }
   if (!pixel_defined)
   {
    if (cols)
     XpmFree(cols);
    return (XpmColorFailed);
   }
  } else
  {
   /* simply use the given pixel */
   *image_pixels = symbol->pixel;
   /*
    * the following makes the mask to be built even if none is given a
    * particular pixel 
    */
   if (symbol->value
       && !strcasecmp(symbol->value, TRANSPARENT_COLOR))
   {
    *mask_pixels = 0;
    *mask_pixel_index = color;
   } else
    *mask_pixels = 1;
   used_pixels[(*nused_pixels)++] = *image_pixels;
  }
 }
 if (cols)
  XpmFree(cols);
 return (ErrorStatus);
}


/* function call in case of error, frees only locally allocated variables */
#undef RETURN
#define RETURN(status) \
{ \
    if (ximage) XDestroyImage(ximage); \
    if (shapeimage) XDestroyImage(shapeimage); \
    if (image_pixels) XpmFree(image_pixels); \
    if (mask_pixels) XpmFree(mask_pixels); \
    if (nalloc_pixels) \
	XFreeColors(display, colormap, alloc_pixels, nalloc_pixels, 0); \
    if (alloc_pixels) XpmFree(alloc_pixels); \
    if (used_pixels) XpmFree(used_pixels); \
    return (status); \
}

int
XpmCreateImageFromXpmImage(display, image,
			   image_return, shapeimage_return, attributes)
Display *display;
XpmImage *image;
XImage **image_return;
XImage **shapeimage_return;
XpmAttributes *attributes;
{
 /* variables stored in the XpmAttributes structure */
 Visual *visual;
 Colormap colormap;
 unsigned int depth;

 /* variables to return */
 XImage *ximage = NULL;
 XImage *shapeimage = NULL;
 unsigned int mask_pixel_index = XpmUndefPixel;
 int ErrorStatus;

 /* calculation variables */
 Pixel *image_pixels = NULL;
 Pixel *mask_pixels = NULL;
 Pixel *alloc_pixels = NULL;
 Pixel *used_pixels = NULL;
 unsigned int nalloc_pixels = 0;
 unsigned int nused_pixels = 0;

 /* initialize return values */
 if (image_return)
  *image_return = NULL;
 if (shapeimage_return)
  *shapeimage_return = NULL;

 /* retrieve information from the XpmAttributes */
 if (attributes && (attributes->valuemask & XpmVisual))
  visual = attributes->visual;
 else
  visual = XDefaultVisual(display, XDefaultScreen(display));

 if (attributes && (attributes->valuemask & XpmColormap))
  colormap = attributes->colormap;
 else
  colormap = XDefaultColormap(display, XDefaultScreen(display));

 if (attributes && (attributes->valuemask & XpmDepth))
  depth = attributes->depth;
 else
  depth = XDefaultDepth(display, XDefaultScreen(display));

 ErrorStatus = XpmSuccess;

 /* malloc pixels index tables */
 image_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
 if (!image_pixels)
  return (XpmNoMemory);

 mask_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
 if (!mask_pixels)
  RETURN(XpmNoMemory);

 /* maximum of allocated pixels will be the number of colors */
 alloc_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
 if (!alloc_pixels)
  RETURN(XpmNoMemory);

 /* maximum of allocated pixels will be the number of colors */
 used_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
 if (!used_pixels)
  RETURN(XpmNoMemory);

 /* get pixel colors, store them in index tables */
 ErrorStatus = CreateColors(display, attributes, image->colorTable,
			    image->ncolors, image_pixels, mask_pixels,
			    &mask_pixel_index, alloc_pixels, &nalloc_pixels,
			    used_pixels, &nused_pixels);

 if (ErrorStatus != XpmSuccess
     && (ErrorStatus < 0 || (attributes
			     && (attributes->valuemask & XpmExactColors)
			     && attributes->exactColors)))
  RETURN(ErrorStatus);

 /* create the ximage */
 if (image_return)
 {
  ErrorStatus = CreateXImage(display, visual, depth,
			     image->width, image->height, &ximage);
  if (ErrorStatus != XpmSuccess)
   RETURN(ErrorStatus);

#ifndef FOR_MSW

  /*
   * set the ximage data 
   *
   * In case depth is 1 or bits_per_pixel is 4, 6, 8, 24 or 32 use optimized
   * functions, otherwise use slower but sure general one. 
   *
   */

  if (ximage->depth == 1)
   SetImagePixels1(ximage, image->width, image->height,
		   image->data, image_pixels);
  else
  if (ximage->bits_per_pixel == 8)
   SetImagePixels8(ximage, image->width, image->height,
		   image->data, image_pixels);
  else
  if (ximage->bits_per_pixel == 16)
   SetImagePixels16(ximage, image->width, image->height,
		    image->data, image_pixels);
  else
  if (ximage->bits_per_pixel == 32)
   SetImagePixels32(ximage, image->width, image->height,
		    image->data, image_pixels);
  else
   SetImagePixels(ximage, image->width, image->height,
		  image->data, image_pixels);
#else				/* FOR_MSW */
  MSWSetImagePixels(display, ximage, image->width, image->height,
		    image->data, image_pixels);
#endif
 }
 /* create the shape mask image */
 if (mask_pixel_index != XpmUndefPixel && shapeimage_return)
 {
  ErrorStatus = CreateXImage(display, visual, 1, image->width,
			     image->height, &shapeimage);
  if (ErrorStatus != XpmSuccess)
   RETURN(ErrorStatus);

#ifndef FOR_MSW
  SetImagePixels1(shapeimage, image->width, image->height,
		  image->data, mask_pixels);
#else
  MSWSetImagePixels(display, shapeimage, image->width, image->height,
		    image->data, mask_pixels);
#endif

 }
 XpmFree(image_pixels);
 XpmFree(mask_pixels);

 /* if requested return used pixels in the XpmAttributes structure */
 if (attributes && (attributes->valuemask & XpmReturnPixels ||
 /* 3.2 backward compatibility code */
		    attributes->valuemask & XpmReturnInfos))
 {
  /* end 3.2 bc */
  attributes->pixels = used_pixels;
  attributes->npixels = nused_pixels;
  attributes->mask_pixel = mask_pixel_index;
 } else
  XpmFree(used_pixels);

 /* if requested return alloc'ed pixels in the XpmAttributes structure */
 if (attributes && (attributes->valuemask & XpmReturnAllocPixels))
 {
  attributes->alloc_pixels = alloc_pixels;
  attributes->nalloc_pixels = nalloc_pixels;
 } else
  XpmFree(alloc_pixels);

 /* return created images */
 if (image_return)
  *image_return = ximage;
 if (shapeimage_return)
  *shapeimage_return = shapeimage;

 return (ErrorStatus);
}


/*
 * Create an XImage 
 */
static int
CreateXImage(display, visual, depth, width, height, image_return)
Display *display;
Visual *visual;
unsigned int depth;
unsigned int width;
unsigned int height;
XImage **image_return;
{
 int bitmap_pad;

 /* first get bitmap_pad */
 if (depth > 16)
  bitmap_pad = 32;
 else
 if (depth > 8)
  bitmap_pad = 16;
 else
  bitmap_pad = 8;

 /* then create the XImage with data = NULL and bytes_per_line = 0 */
 *image_return = XCreateImage(display, visual, depth, ZPixmap, 0, 0,
			      width, height, bitmap_pad, 0);
 if (!*image_return)
  return (XpmNoMemory);

#ifndef FOR_MSW
 /* now that bytes_per_line must have been set properly alloc data */
 (*image_return)->data =
  (char *) XpmMalloc((*image_return)->bytes_per_line * height);

 if (!(*image_return)->data)
 {
  XDestroyImage(*image_return);
  *image_return = NULL;
  return (XpmNoMemory);
 }
#else
 /* under FOR_MSW XCreateImage has done it all */
#endif
 return (XpmSuccess);
}

#ifndef FOR_MSW
/*
 * The functions below are written from X11R5 MIT's code (XImUtil.c) 
 *
 * The idea is to have faster functions than the standard XPutPixel function to
 * build the image data. Indeed we can speed up things by suppressing tests
 * performed for each pixel. We do the same tests but at the image level. We
 * also assume that we use only ZPixmap images with null offsets. 
 */

LFUNC(_putbits, void, (register char *src, int dstoffset,
		       register int numbits, register char *dst));

LFUNC(_XReverse_Bytes, int, (register unsigned char *bpt, register int nb));

static unsigned char Const _reverse_byte[0x100] = {
			     0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
			     0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
			     0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
			     0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
			     0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
			     0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
			     0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
			     0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
			     0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
			     0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
			     0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
			     0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
			     0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
			     0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
			     0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
			     0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
			     0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
			     0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
			     0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
			     0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
			     0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
			     0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
			     0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
			     0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
			     0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
			     0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
			     0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
			     0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
			     0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
			     0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
			     0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
			      0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

static int
_XReverse_Bytes(bpt, nb)
register unsigned char *bpt;
register int nb;
{
 do
 {
  *bpt = _reverse_byte[*bpt];
  bpt++;
 } while (--nb > 0);
 return 0;
}


void
xpm_xynormalizeimagebits(bp, img)
register unsigned char *bp;
register XImage *img;
{
 register unsigned char c;

 if (img->byte_order != img->bitmap_bit_order)
 {
  switch (img->bitmap_unit)
  {

  case 16:
   c = *bp;
   *bp = *(bp + 1);
   *(bp + 1) = c;
   break;

  case 32:
   c = *(bp + 3);
   *(bp + 3) = *bp;
   *bp = c;
   c = *(bp + 2);
   *(bp + 2) = *(bp + 1);
   *(bp + 1) = c;
   break;
  }
 }
 if (img->bitmap_bit_order == MSBFirst)
  _XReverse_Bytes(bp, img->bitmap_unit >> 3);
}

void
xpm_znormalizeimagebits(bp, img)
register unsigned char *bp;
register XImage *img;
{
 register unsigned char c;

 switch (img->bits_per_pixel)
 {

 case 2:
  _XReverse_Bytes(bp, 1);
  break;

 case 4:
  *bp = ((*bp >> 4) & 0xF) | ((*bp << 4) & ~0xF);
  break;

 case 16:
  c = *bp;
  *bp = *(bp + 1);
  *(bp + 1) = c;
  break;

 case 24:
  c = *(bp + 2);
  *(bp + 2) = *bp;
  *bp = c;
  break;

 case 32:
  c = *(bp + 3);
  *(bp + 3) = *bp;
  *bp = c;
  c = *(bp + 2);
  *(bp + 2) = *(bp + 1);
  *(bp + 1) = c;
  break;
 }
}

static unsigned char Const _lomask[0x09] = {
		      0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
static unsigned char Const _himask[0x09] = {
		      0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};

static void
_putbits(src, dstoffset, numbits, dst)
register char *src;		/* address of source bit string */
int dstoffset;			/* bit offset into destination; range is 0-31 */
register int numbits;		/* number of bits to copy to destination */
register char *dst;		/* address of destination bit string */
{
 register unsigned char chlo, chhi;
 int hibits;

 dst = dst + (dstoffset >> 3);
 dstoffset = dstoffset & 7;
 hibits = 8 - dstoffset;
 chlo = *dst & _lomask[dstoffset];
 for (;;)
 {
  chhi = (*src << dstoffset) & _himask[dstoffset];
  if (numbits <= hibits)
  {
   chhi = chhi & _lomask[dstoffset + numbits];
   *dst = (*dst & _himask[dstoffset + numbits]) | chlo | chhi;
   break;
  }
  *dst = chhi | chlo;
  dst++;
  numbits = numbits - hibits;
  chlo = (unsigned char) (*src & _himask[hibits]) >> hibits;
  src++;
  if (numbits <= dstoffset)
  {
   chlo = chlo & _lomask[numbits];
   *dst = (*dst & _himask[numbits]) | chlo;
   break;
  }
  numbits = numbits - dstoffset;
 }
}

/*
 * Default method to write pixels into a Z image data structure. The
 * algorithm used is: 
 *
 * copy the destination bitmap_unit or Zpixel to temp normalize temp if needed
 * copy the pixel bits into the temp renormalize temp if needed copy the temp
 * back into the destination image data 
 */

static void
SetImagePixels(image, width, height, pixelindex, pixels)
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 register char *src;
 register char *dst;
 register unsigned int *iptr;
 register int x, y, i;
 register char *data;
 Pixel pixel, px;
 int nbytes, depth, ibu, ibpp;

 data = image->data;
 iptr = pixelindex;
 depth = image->depth;
 if (depth == 1)
 {
  ibu = image->bitmap_unit;
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    pixel = pixels[*iptr];
    for (i = 0, px = pixel; i < sizeof(unsigned long);
	 i++, px >>= 8)
     ((unsigned char *) &pixel)[i] = px;
    src = &data[XYINDEX(x, y, image)];
    dst = (char *) &px;
    px = 0;
    nbytes = ibu >> 3;
    for (i = nbytes; --i >= 0;)
     *dst++ = *src++;
    XYNORMALIZE(&px, image);
    _putbits((char *) &pixel, (x % ibu), 1, (char *) &px);
    XYNORMALIZE(&px, image);
    src = (char *) &px;
    dst = &data[XYINDEX(x, y, image)];
    for (i = nbytes; --i >= 0;)
     *dst++ = *src++;
   }
 } else
 {
  ibpp = image->bits_per_pixel;
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    pixel = pixels[*iptr];
    if (depth == 4)
     pixel &= 0xf;
    for (i = 0, px = pixel; i < sizeof(unsigned long); i++,
	 px >>= 8)
     ((unsigned char *) &pixel)[i] = px;
    src = &data[ZINDEX(x, y, image)];
    dst = (char *) &px;
    px = 0;
    nbytes = (ibpp + 7) >> 3;
    for (i = nbytes; --i >= 0;)
     *dst++ = *src++;
    ZNORMALIZE(&px, image);
    _putbits((char *) &pixel, (x * ibpp) & 7, ibpp, (char *) &px);
    ZNORMALIZE(&px, image);
    src = (char *) &px;
    dst = &data[ZINDEX(x, y, image)];
    for (i = nbytes; --i >= 0;)
     *dst++ = *src++;
   }
 }
}

/*
 * write pixels into a 32-bits Z image data structure 
 */

#if !defined(WORD64) && !defined(LONG64)
/*
 * this item is static but deterministic so let it slide; doesn't hurt
 * re-entrancy of this library. Note if it is actually const then would be OK
 * under rules of ANSI-C but probably not C++ which may not want to allocate
 * space for it. 
 */
static unsigned long /* constant */ RTXpm_byteorderpixel = MSBFirst << 24;

#endif

/*
 * WITHOUT_SPEEDUPS is a flag to be turned on if you wish to use the original
 * 3.2e code - by default you get the speeded-up version. 
 */

static void
SetImagePixels32(image, width, height, pixelindex, pixels)
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 unsigned char *data;
 unsigned int *iptr;
 int y;
 Pixel pixel;

#ifdef WITHOUT_SPEEDUPS

 int x;
 unsigned char *addr;

 data = (unsigned char *) image->data;
 iptr = pixelindex;
#if !defined(WORD64) && !defined(LONG64)
 if (*((char *) &RTXpm_byteorderpixel) == image->byte_order)
 {
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    addr = &data[ZINDEX32(x, y, image)];
    *((unsigned long *) addr) = pixels[*iptr];
   }
 } else
#endif
 if (image->byte_order == MSBFirst)
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    addr = &data[ZINDEX32(x, y, image)];
    pixel = pixels[*iptr];
    addr[0] = pixel >> 24;
    addr[1] = pixel >> 16;
    addr[2] = pixel >> 8;
    addr[3] = pixel;
   }
 else
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    addr = &data[ZINDEX32(x, y, image)];
    pixel = pixels[*iptr];
    addr[0] = pixel;
    addr[1] = pixel >> 8;
    addr[2] = pixel >> 16;
    addr[3] = pixel >> 24;
   }

#else				/* WITHOUT_SPEEDUPS */

 int bpl = image->bytes_per_line;
 unsigned char *data_ptr, *max_data;

 data = (unsigned char *) image->data;
 iptr = pixelindex;
#if !defined(WORD64) && !defined(LONG64)
 if (*((char *) &RTXpm_byteorderpixel) == image->byte_order)
 {
  for (y = 0; y < height; y++)
  {
   data_ptr = data;
   max_data = data_ptr + (width << 2);

   while (data_ptr < max_data)
   {
    *((unsigned long *) data_ptr) = pixels[*(iptr++)];
    data_ptr += (1 << 2);
   }
   data += bpl;
  }
 } else
#endif
 if (image->byte_order == MSBFirst)
  for (y = 0; y < height; y++)
  {
   data_ptr = data;
   max_data = data_ptr + (width << 2);

   while (data_ptr < max_data)
   {
    pixel = pixels[*(iptr++)];

    *data_ptr++ = pixel >> 24;
    *data_ptr++ = pixel >> 16;
    *data_ptr++ = pixel >> 8;
    *data_ptr++ = pixel;

   }
   data += bpl;
  }
 else
  for (y = 0; y < height; y++)
  {
   data_ptr = data;
   max_data = data_ptr + (width << 2);

   while (data_ptr < max_data)
   {
    pixel = pixels[*(iptr++)];

    *data_ptr++ = pixel;
    *data_ptr++ = pixel >> 8;
    *data_ptr++ = pixel >> 16;
    *data_ptr++ = pixel >> 24;
   }
   data += bpl;
  }

#endif				/* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 16-bits Z image data structure 
 */

static void
SetImagePixels16(image, width, height, pixelindex, pixels)
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 unsigned char *data;
 unsigned int *iptr;
 int y;

#ifdef WITHOUT_SPEEDUPS

 int x;
 unsigned char *addr;

 data = (unsigned char *) image->data;
 iptr = pixelindex;
 if (image->byte_order == MSBFirst)
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    addr = &data[ZINDEX16(x, y, image)];
    addr[0] = pixels[*iptr] >> 8;
    addr[1] = pixels[*iptr];
   }
 else
  for (y = 0; y < height; y++)
   for (x = 0; x < width; x++, iptr++)
   {
    addr = &data[ZINDEX16(x, y, image)];
    addr[0] = pixels[*iptr];
    addr[1] = pixels[*iptr] >> 8;
   }

#else				/* WITHOUT_SPEEDUPS */

 Pixel pixel;

 int bpl = image->bytes_per_line;
 unsigned char *data_ptr, *max_data;

 data = (unsigned char *) image->data;
 iptr = pixelindex;
 if (image->byte_order == MSBFirst)
  for (y = 0; y < height; y++)
  {
   data_ptr = data;
   max_data = data_ptr + (width << 1);

   while (data_ptr < max_data)
   {
    pixel = pixels[*(iptr++)];

    data_ptr[0] = pixel >> 8;
    data_ptr[1] = pixel;

    data_ptr += (1 << 1);
   }
   data += bpl;
  }
 else
  for (y = 0; y < height; y++)
  {
   data_ptr = data;
   max_data = data_ptr + (width << 1);

   while (data_ptr < max_data)
   {
    pixel = pixels[*(iptr++)];

    data_ptr[0] = pixel;
    data_ptr[1] = pixel >> 8;

    data_ptr += (1 << 1);
   }
   data += bpl;
  }

#endif				/* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 8-bits Z image data structure 
 */

static void
SetImagePixels8(image, width, height, pixelindex, pixels)
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 char *data;
 unsigned int *iptr;
 int y;

#ifdef WITHOUT_SPEEDUPS

 int x;

 data = image->data;
 iptr = pixelindex;
 for (y = 0; y < height; y++)
  for (x = 0; x < width; x++, iptr++)
   data[ZINDEX8(x, y, image)] = pixels[*iptr];

#else				/* WITHOUT_SPEEDUPS */

 int bpl = image->bytes_per_line;
 char *data_ptr, *max_data;

 data = image->data;
 iptr = pixelindex;

 for (y = 0; y < height; y++)
 {
  data_ptr = data;
  max_data = data_ptr + width;

  while (data_ptr < max_data)
   *(data_ptr++) = pixels[*(iptr++)];

  data += bpl;
 }

#endif				/* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 1-bit depth image data structure and **offset null** 
 */

static void
SetImagePixels1(image, width, height, pixelindex, pixels)
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 if (image->byte_order != image->bitmap_bit_order)
  SetImagePixels(image, width, height, pixelindex, pixels);
 else
 {
  unsigned int *iptr;
  int y;
  char *data;

#ifdef WITHOUT_SPEEDUPS

  int x;

  data = image->data;
  iptr = pixelindex;
  if (image->bitmap_bit_order == MSBFirst)
   for (y = 0; y < height; y++)
    for (x = 0; x < width; x++, iptr++)
    {
     if (pixels[*iptr] & 1)
      data[ZINDEX1(x, y, image)] |= 0x80 >> (x & 7);
     else
      data[ZINDEX1(x, y, image)] &= ~(0x80 >> (x & 7));
    }
  else
   for (y = 0; y < height; y++)
    for (x = 0; x < width; x++, iptr++)
    {
     if (pixels[*iptr] & 1)
      data[ZINDEX1(x, y, image)] |= 1 << (x & 7);
     else
      data[ZINDEX1(x, y, image)] &= ~(1 << (x & 7));
    }

#else				/* WITHOUT_SPEEDUPS */

  char value;
  char *data_ptr, *max_data;
  int bpl = image->bytes_per_line;
  int diff, count;

  data = image->data;
  iptr = pixelindex;

  diff = width & 7;
  width >>= 3;

  if (image->bitmap_bit_order == MSBFirst)
   for (y = 0; y < height; y++)
   {
    data_ptr = data;
    max_data = data_ptr + width;
    while (data_ptr < max_data)
    {
     value = 0;

     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);
     value = (value << 1) | (pixels[*(iptr++)] & 1);

     *(data_ptr++) = value;
    }
    if (diff)
    {
     value = 0;
     for (count = 0; count < diff; count++)
     {
      if (pixels[*(iptr++)] & 1)
       value |= (0x80 >> count);
     }
     *(data_ptr) = value;
    }
    data += bpl;
   }
  else
   for (y = 0; y < height; y++)
   {
    data_ptr = data;
    max_data = data_ptr + width;
    while (data_ptr < max_data)
    {
     value = 0;
     iptr += 8;

     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);
     value = (value << 1) | (pixels[*(--iptr)] & 1);

     iptr += 8;
     *(data_ptr++) = value;
    }
    if (diff)
    {
     value = 0;
     for (count = 0; count < diff; count++)
     {
      if (pixels[*(iptr++)] & 1)
       value |= (1 << count);
     }
     *(data_ptr) = value;
    }
    data += bpl;
   }

#endif				/* WITHOUT_SPEEDUPS */
 }
}

int
XpmCreatePixmapFromXpmImage(display, d, image,
			    pixmap_return, shapemask_return, attributes)
Display *display;
Drawable d;
XpmImage *image;
Pixmap *pixmap_return;
Pixmap *shapemask_return;
XpmAttributes *attributes;
{
 XImage *ximage, *shapeimage;
 int ErrorStatus;

 /* initialize return values */
 if (pixmap_return)
  *pixmap_return = 0;
 if (shapemask_return)
  *shapemask_return = 0;

 /* create the ximages */
 ErrorStatus = XpmCreateImageFromXpmImage(display, image,
					  (pixmap_return ? &ximage : NULL),
					  (shapemask_return ?
					   &shapeimage : NULL),
					  attributes);
 if (ErrorStatus < 0)
  return (ErrorStatus);

 /* create the pixmaps and destroy images */
 if (pixmap_return && ximage)
 {
  xpmCreatePixmapFromImage(display, d, ximage, pixmap_return);
  XDestroyImage(ximage);
 }
 if (shapemask_return && shapeimage)
 {
  xpmCreatePixmapFromImage(display, d, shapeimage, shapemask_return);
  XDestroyImage(shapeimage);
 }
 return (ErrorStatus);
}

#else				/* FOR_MSW part follows */
static void
MSWSetImagePixels(dc, image, width, height, pixelindex, pixels)
Display *dc;
XImage *image;
unsigned int width;
unsigned int height;
unsigned int *pixelindex;
Pixel *pixels;
{
 unsigned int *data = pixelindex;
 unsigned int x, y;

 SelectObject(*dc, image->bitmap);
 for (y = 0; y < height; y++)
 {
  for (x = 0; x < width; x++)
  {
   SetPixel(*dc, x, y, pixels[*(data++)]);	/* data is [x+y*width] */
  }
 }
}

#endif				/* FOR_MSW */


syntax highlighted by Code2HTML, v. 0.9.1