/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/************************************<+>*************************************
 ****************************************************************************
 **
 **  File:	Graphics.c
 **
 **  Project:    CDE Help System
 **
 **  Description: This code processes a graphic (X11 bitmap, X11 pixmap,
 **               X11 XWD file, and TIFF). It will degrade graphics to
 **		  match the type of monitor the image is displayed upon.
 **
 **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Novell, Inc.
 **
 ****************************************************************************
 ************************************<+>*************************************/
/******************************************************************************
 **  The GreyScale and Perform_Dither routine were take and modified from
 **  the g-job 'xgedit' created by Mark F. Cook. The following is the
 **  disclaimer from the dither.c source code
 ******************************************************************************
 **  Program:		xgedit.c
 **
 **  Description:	X11-based graphics editor
 **
 **  File:		dither.c, containing the following
 **                     subroutines/functions:
 **                       Dither_Image()
 **                       Perform_Dither()
 **
 **  Copyright 1988 by Mark F. Cook and Hewlett-Packard, Corvallis
 **  Workstation Operation.  All rights reserved.
 **
 **  Permission to use, copy, and modify this software is granted, provided
 **  that this copyright appears in all copies and that both this copyright
 **  and permission notice appear in all supporting documentation, and that
 **  the name of Mark F. Cook and/or Hewlett-Packard not be used in advertising
 **  without specific, written prior permission.  Neither Mark F. Cook or
 **  Hewlett-Packard make any representations about the suitibility of this
 **  software for any purpose.  It is provided "as is" without express or
 **  implied warranty.
 **
 ******************************************************************************
 **  CHANGE LOG:
 **
 **     1.00.00 - 1/11/89 - Start of Rev. number.
 **
 ** @@@ 4.05.00 - 12/07/89 - Created Dither_Image() and Perform_Dither() to
 ** @@@           allow conversion of color images into a dithered black &
 ** @@@           white image suitable for output on a laser printer.
 **
 ******************************************************************************/

/*
static char rcs_id[]="$XConsortium: Graphics.c /main/23 1996/12/06 11:12:54 cde-hp $";
*/

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include <X11/XWDFile.h>
#include <X11/ImUtil.h>
#ifndef	STUB
#include "il.h"
#include "ilfile.h"
#include "ilX.h"
#include "ilerrors.h"
#endif
#include <Xm/Xm.h>
#include <Xm/XmPrivate.h>
#include <Dt/Dts.h>
#include <X11/xpm.h>

#include "bufioI.h"
#include "Access.h"
#include "AccessI.h"
#include "GraphicsP.h"
#include "GraphicsI.h"
#include "StringFuncsI.h"
#include "GifUtilsI.h"
#include "XbmUtilsI.h"
#include "JpegUtilsI.h"
#include "Lock.h"

/*****************************************************************************/
/*		      Private data					     */
/*****************************************************************************/
#define GR_SUCCESS            0       /* everything okay */
#define GR_DRAWABLE_ERR       1       /* couldn't get drawable attr/geom */
#define GR_FILE_ERR           2       /* file open/read/write problem */
#define GR_REQUEST_ERR        3       /* bad placement or size */
#define GR_ALLOC_ERR          4       /* memory allocation failure */
#define GR_HEADER_ERR         5       /* file header version/size problem */

#define	MAX_GREY_COLORS		8

#define RES_TOLERANCE		25	/* resolution scaling tolerance */

static	short		GreyAllocated = False;
static	Pixel		GreyScalePixels[MAX_GREY_COLORS];
static	char		*GreyScaleColors[MAX_GREY_COLORS] =
			{
				"#212121212121",
				"#424242424242",
				"#636363636363",
				"#737373737373",
				"#949494949494",
				"#adadadadadad",
				"#bdbdbdbdbdbd",
				"#dededededede",
			};

#define Missing_bm_width 75
#define Missing_bm_height 47
static unsigned char Missing_bm_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0x7f, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
   0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x04, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xc0, 0x00, 0x18, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xc0, 0x00, 0x18, 0x8c, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0xc0, 0x00,
   0x18, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x24,
   0x31, 0xf0, 0xf0, 0x30, 0xe8, 0xf0, 0xc1, 0x00, 0x18, 0x04, 0x21, 0x08,
   0x08, 0x20, 0x18, 0x09, 0xc1, 0x00, 0x18, 0x04, 0x21, 0xf0, 0xf0, 0x20,
   0x08, 0x09, 0xc1, 0x00, 0x18, 0x04, 0x21, 0x00, 0x01, 0x21, 0x08, 0x09,
   0xc1, 0x00, 0x18, 0x04, 0x21, 0x08, 0x09, 0x21, 0x08, 0x09, 0xc1, 0x00,
   0x18, 0x04, 0x71, 0xf0, 0xf0, 0x70, 0x08, 0xf1, 0xc1, 0x00, 0x18, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x18, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0xf0, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
   0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0xf0,
   0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x08, 0x01, 0x00,
   0x00, 0x08, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x08, 0x00, 0x00, 0x00, 0x08,
   0x20, 0x00, 0xc0, 0x00, 0x18, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
   0xc0, 0x00, 0x18, 0x08, 0xd8, 0xf0, 0xe8, 0xf8, 0x30, 0xf0, 0xc0, 0x00,
   0x18, 0xc8, 0x31, 0x01, 0x19, 0x09, 0x21, 0x08, 0xc1, 0x00, 0x18, 0x08,
   0x11, 0xf0, 0x09, 0x09, 0x21, 0x08, 0xc0, 0x00, 0x18, 0x08, 0x11, 0x08,
   0x09, 0x09, 0x21, 0x08, 0xc0, 0x00, 0x18, 0x08, 0x11, 0x08, 0x09, 0x09,
   0x21, 0x08, 0xc1, 0x00, 0x18, 0xf0, 0x10, 0xf0, 0xf9, 0x08, 0x71, 0xf0,
   0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xc0, 0x00,
   0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
   0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xf8, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00};

/* Used to hold image type converter registry info */
typedef struct {
    char *image_type;
    _DtGrLoadProc convert_proc;
    _DtGrDestroyContextProc destroy_context_proc;
} _DtGrRegistryRec;

/* 
 * This array maps image file name extensions to the corresponding
 * CDE data type.  It is used by the DetermineImageType function.
 * The array must be NULL-terminated.
 */
static char * img_extensions[] = {
    "xpm",  "PM",
    "pm",   "PM",
    "gif",  "GIF",
    "jpeg", "JPEG",
    "jpg",  "JPEG",
    "xbm",  "BM",
    "bm",   "BM",
    "tiff", "TIFF",
    "tif",  "TIFF",
    "xwd",  "XWD",
    "cgm",  "CGM",
    NULL
};
   
/*****************************************************************************/
/*		      Private declarations				     */
/*****************************************************************************/

static int		Do_Direct(
				Display *dpy,
				int      screen,
				XWDFileHeader *header,
				Colormap colormap,
				int ncolors,
				XColor *colors,
				enum _DtGrColorModel 	force,
				XImage *in_image,
				XImage *out_image,
				unsigned long **ret_colors,
				int  *ret_number );
static int		Do_Pseudo(
				Display *dpy,
				int	 screen,
				Colormap colormap,
				int ncolors,
				XColor *colors,
				enum _DtGrColorModel	force,
				XImage *in_image,
				XImage *out_image,
				unsigned long **ret_colors,
				int  *ret_number );
static int		GreyScale (
				Display   *dpy,
				int	   screen,
				Colormap   cmap,
				XImage    *in_image,
				XImage    *out_image,
				XColor    *colors,
				int        ncolors,
				enum _DtGrColorModel	force,
				int        rshift,
				int        gshift,
				int        bshift,
				Pixel      rmask,
				Pixel      gmask,
				Pixel      bmask );
static unsigned int	Image_Size(
				XImage *image );
static void		Perform_Dither(
				Display *dpy,
				int      screen,
				XImage  *image,
				int     *greyscale );
static enum _DtGrLoadStatus processBitmap(
                                _DtGrStream           *image,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
			        _DtGrContext          *context);
#ifndef	STUB
static enum _DtGrLoadStatus processTiff(
                                _DtGrStream           *stream,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
				_DtGrContext          *context);
static void destroyTiffContext(
                                _DtGrContext          *context);
#endif
static enum _DtGrLoadStatus processXwd(
                                _DtGrStream           *stream,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
			        _DtGrContext          *context);
static void		_swaplong (
				char *bp,
				unsigned n );
static void		_swapshort (
				char *bp,
				unsigned n );
static	int		XwdFileToPixmap (
				Display  *dpy,
				int	  screen,
				int	  depth,
				Pixmap    pixmap,
				Colormap  colormap,
				Visual	 *visual,
				GC        gc,
                                enum _DtGrColorModel  color_model,
				int       src_x,
				int       src_y,
				int       dst_x,
				int       dst_y,
				int       width,
				int       height,
				_DtGrStream *stream,
				unsigned long **ret_colors,
				int      *ret_number );
static enum _DtGrLoadStatus processXpm(
                                _DtGrStream           *stream,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
				_DtGrContext          *context);
static enum _DtGrLoadStatus processGIF(
                                _DtGrStream           *image,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
				_DtGrContext          *context);
static enum _DtGrLoadStatus processJPEG(
                                _DtGrStream           *image,
				Screen                *screen,
				int                   depth,
				Colormap              colormap,
				Visual                *visual,
				Pixel                 foreground,
				Pixel                 background,
				GC                    gc,
				enum _DtGrColorModel  color_model,
				Boolean               allow_reduced_colors,
				Dimension             *in_out_width,
				Dimension             *in_out_height,
				unsigned short        media_resolution,
				Pixmap                *ret_pixmap,
				Pixmap                *ret_mask,
				Pixel                 **ret_colors,
				int                   *ret_num_colors,
				_DtGrContext          *context);
static enum _DtGrLoadStatus DetermineImageType(
                                _DtGrStream *stream,
				char        *image_type);
static enum _DtGrLoadStatus GetConverterAndDestructor(
                                char *image_type, 
				_DtGrLoadProc *converter,
				_DtGrDestroyContextProc *destructor);
static float ComputeRatio(int media_res, int image_res);

/***************************************************************************** 
 *         Image type registry declarations
 *****************************************************************************/

/* Registry of converters and destructors for default image types */
static _DtGrRegistryRec registry[] =
{
    {
        "PM",
	processXpm,
        NULL
    },

    {
        "BM",
	processBitmap,
        NULL
    },

    {
        "GIF",
	processGIF,
        NULL
    },

    {
        "JPEG",
	processJPEG,
        NULL
    },

    {
        "TIFF",
	processTiff,
        destroyTiffContext
    },

    {
        "XWD",
	processXwd,
        NULL
    },
};

static int registry_count = XtNumber(registry);

/* Registry of converters and destructors for non-default image types */
static _DtGrRegistryRec *new_registry = NULL;
static int new_registry_count = 0;

/***************************************************************************** 
 *         Private Routines
 *****************************************************************************/
/***************************************************************************** 
 * taken straight out of xwud and modified.
 *****************************************************************************/
/* Copyright 1985, 1986, 1988 Massachusetts Institute of Technology */

static void
_swapshort (
    char *bp,
    unsigned n )
{
    char c;
    char *ep = bp + n;

    while (bp < ep) {
	c = *bp;
	*bp = *(bp + 1);
	bp++;
	*bp++ = c;
    }
}

static void
_swaplong (
    char *bp,
    unsigned n )
{
    char c;
    char *ep = bp + n;
    char *sp;

    while (bp < ep) {
	sp = bp + 3;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	sp = bp + 1;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	bp += 2;
    }
}

static float
ComputeRatio(int media_res, int image_res)
{
    float ratio;
    int iratio, dx;

    if (media_res >= image_res) {
	ratio = (float)media_res / (float)image_res;
	if (media_res % image_res == 0)
	    return ratio;
	iratio = ratio + 0.5;
	dx = media_res - image_res * iratio;
	if (dx >= -RES_TOLERANCE && dx <= RES_TOLERANCE)
	    ratio = (float)iratio;
    } else {
	ratio = (float)image_res / (float)media_res;
	if (image_res % media_res == 0)
	    return 1.0 / ratio;
	iratio = ratio + 0.5;
	dx = image_res - media_res * iratio;
	if (dx >= -RES_TOLERANCE && dx <= RES_TOLERANCE)
	    ratio = (float)iratio;
	ratio = 1.0 / ratio;
    }
    return ratio;
}

/*****************************************************************************
 * Function: GreyScale
 *
 * Turn a color image into a 8 grey color image
 * If it can't create a grey scale image, GreyScale will call
 * Perform_Dither to create a bi-tonal image.
 *
 *****************************************************************************
 * The GreyScale code was ported from xgedit and changed to fit our needs
 *****************************************************************************/
static	int
GreyScale (
    Display   *dpy,
    int	       screen,
    Colormap   cmap,
    XImage    *in_image,
    XImage    *out_image,
    XColor    *colors,
    int        ncolors,
    enum _DtGrColorModel force,
    int        rshift,
    int        gshift,
    int        bshift,
    Pixel      rmask,
    Pixel      gmask,
    Pixel      bmask )
{
  int i, j, x;
  int     inc;
  int     count;
  int	  width = in_image->width;
  int	  height = in_image->height;
  int     value;
  int    *grey_scale;
  Pixel   valueArray[256];
  Pixel   n;
  XColor  ret_color;

/*--- allocate an array big enough to map each pixel in the image  ---*/
/*--- into a corresponding greyscale value (in the range [0-255]). ---*/

  grey_scale = (int *) calloc(width*height, sizeof(int));
  if (grey_scale == NULL) {
     return GR_ALLOC_ERR;
   }

/*---          < ESTABLISH THE GREYSCALE IMAGE >            ---*/
/*--- The NTSC formula for converting an RGB value into the ---*/
/*--- corresponding greyscale value is:                     ---*/
/*---      luminosity = .299 red + .587 green + .114 blue   ---*/

  /*
   * zero the flag array
   */
  for (i = 0; i < 256; i++)
      valueArray [i] = 0;

  for (j=0, x = 0; j<height; j++)
    for (i=0; i<width; i++, x++) {
      n = XGetPixel(in_image, i, j);
      if (rshift)
	{
	  ret_color.red   = (n >> rshift) & rmask;
	  ret_color.green = (n >> gshift) & gmask;
	  ret_color.blue  = (n >> bshift) & bmask;
	  if (ncolors)
	    {
	      ret_color.red   = colors[ret_color.red  ].red;
	      ret_color.green = colors[ret_color.green].green;
	      ret_color.blue  = colors[ret_color.blue ].blue;
	    }
	  else
	    {
	      ret_color.red   = (((Pixel) ret_color.red  ) * 65535) / rmask;
	      ret_color.green = (((Pixel) ret_color.green) * 65535) / gmask;
	      ret_color.blue  = (((Pixel) ret_color.blue ) * 65535) / bmask;
	    }

          value = (((int)(ret_color.red*299) + (int)(ret_color.green*587) +
				(int)(ret_color.blue*114)) / 1000) >> 8;
	}
      else
          value = (((int)(colors[n].red*299) + (int)(colors[n].green*587) +
				(int)(colors[n].blue*114)) / 1000) >> 8;
      grey_scale[x] = value;
      valueArray[value]++;
     } /* for(i...) */

  /*
   * Find out if we can/have allocate the pre-defined grey colors.
   */
  _DtHelpProcessLock();
  if (!GreyAllocated && force != _DtGrBITONAL)
    {
      for (i = 0; !GreyAllocated && i < MAX_GREY_COLORS; i++)
	{
	  if (!XParseColor (dpy, cmap, GreyScaleColors[i], &ret_color) ||
		!XAllocColor (dpy, cmap, &ret_color))
	    {
	      /*
	       * Have a problem allocating one of the pre-defined
	       * grey colors. Free the already allocated pixels
	       * and set the flag.
	       */
	      if (i)
	          XFreeColors (dpy, cmap, GreyScalePixels, i, 0);
	      GreyAllocated = -1;
	    }
	  else
	      GreyScalePixels[i] = ret_color.pixel;
	}
      if (!GreyAllocated)
          GreyAllocated = True;
    }

  /*
   * Had a problem allocating the pre-defined grey colors.
   * Try to dither into black and white.
   */
  if (force == _DtGrBITONAL || GreyAllocated == -1)
    {
      Perform_Dither (dpy, screen, out_image, grey_scale);
      free(grey_scale);
      _DtHelpProcessUnlock();
      return GR_SUCCESS;
    }
  _DtHelpProcessUnlock();

  /*
   * Find out how many grey scale colors there are.
   */
  for (count = 0, i = 0; i < 256; i++)
      if (valueArray[i])
	  count++;

  /*
   * If we have less than maximum grey colors we want to spread the load
   * between the colors
   */
  if (count < MAX_GREY_COLORS)
      inc = count;
  else
      inc = MAX_GREY_COLORS;

  /*
   * This is rather esoteric code.
   * The line
   *	valueArray[n] = GreyScalePixels[i * MAX_GREY_COLORS / inc]
   *
   * causes the colors to be evenly distributed if the total number
   * of calculated grey shades is less than the number of pre-defined
   * grey colors.
   *
   * The if stmt
   *	if (j >= count / (MAX_GREY_COLORS - i))
   *
   * uniformly reduces the calculated grey shades into the pre-defined
   * grey colors, if the total number of grey shades is greater than
   * the number of pre-defined grey colors.
   *
   * If after reading the following code, the reader still doesn't
   * understand it, pick a couple of numbers between 1-255 for 'count'
   * and walk each of them thru the 'for' loop. Hopefully it will help.
   * (Suggestion - pick a large number for one value and pick
   * a number less than 8 for the other value).
   */
  for (i = 0, n = 0, j = 0; n < 256 && count && i < MAX_GREY_COLORS; n++)
    {
      if (valueArray[n])
	{
	  _DtHelpProcessLock();
	  valueArray[n] = GreyScalePixels[i * MAX_GREY_COLORS / inc];
	  _DtHelpProcessUnlock();
	  j++;
	  if (j >= count / (MAX_GREY_COLORS - i))
	    {
	      count -= j;
	      i++;
	      j = 0;
	    }
	}
    }

  /*
   * Now replace the image pixels with the grey pixels
   */
  for (j = 0, x = 0; j < height; j++)
    for (i = 0; i < width; i++, x++) {
      XPutPixel (out_image, i, j, valueArray[grey_scale[x]]);
     } /* for(i...) */

  free(grey_scale);

  return GR_SUCCESS;
}

/***************************************************************************
 *                                                                         *
 * Routine:   Perform_Dither                                               *
 *                                                                         *
 * Purpose:   Given a color XImage and a greyscale representation of it,   *
 *            for each pixel in the image, determine whether it should be  *
 *            coverted to Black or White base on the weighted average of   *
 *            the greyscale value of it and the pixels surrounding it.     *
 *            Be sure to do bounds checking for pixels that are at the     *
 *            edge of the image.                                           *
 *                                                                         *
 *            The dithering is done using the Floyd-Steinberg error        *
 *            diffusion algorithm, which incorporates a Stucki error       *
 *            filter (specifics can be found in Chapter 8, "Dithering      *
 *            with Blue Noise", of the book "Digital Halftoning" by        *
 *            Robert Ulichney (MIT Press, 1988).                           *
 *                                                                         *
 *X11***********************************************************************/
static	void
Perform_Dither(
    Display *dpy,
    int      screen,
    XImage  *image,
    int   *greyscale )
{
  int i, j, width, height;
  int result, error, max_lum;
  Pixel    blackPixel = XBlackPixel (dpy, screen);
  Pixel    whitePixel = XWhitePixel (dpy, screen);

  width  = image->width;
  height = image->height;
  max_lum = 256;

  for (j=0; j<height; j++) /* rows */
    for (i=0; i<width; i++) { /* columns */
      if (greyscale[(j*width)+i] < (max_lum/2))
	result = 0;
      else
        result = max_lum - 1;
      error = greyscale[(j*width)+i] - result;
      if (i+1 < width)			/*--- G [j] [i+1] ---*/
        greyscale[(j*width)+i+1] += (error * 8)/42;
      if (i+2 < width)			/*--- G [j] [i+2] ---*/
        greyscale[(j*width)+i+2] += (error * 4)/42;

      if (j+1 < height) {
        if (i-2 >= 0)			/*--- G [j+1] [i-2] ---*/
          greyscale[((j+1)*width)+i-2] += (error * 2)/42;
        if (i-1 >= 0)			/*--- G [j+1] [i-1] ---*/
          greyscale[((j+1)*width)+i-1] += (error * 4)/42;
	/*--- G [j+1] [i] ---*/
        greyscale[((j+1)*width)+i] += (error * 8)/42;
        if (i+1 < width)		/*--- G [j+1] [i+1] ---*/
          greyscale[((j+1)*width)+i+1] += (error * 4)/42;
        if (i+2 < width)		/*--- G [j+1] [i+2] ---*/
          greyscale[((j+1)*width)+i+2] += (error * 2)/42;
       }

      if (j+2 < height) {
        if (i-2 >= 0)			/*--- G [j+2] [i-2] ---*/
          greyscale[((j+2)*width)+i-2] += error/42;
        if (i-1 >= 0)			/*--- G [j+2] [i-1] ---*/
          greyscale[((j+2)*width)+i-1] += (error * 2)/42;
	/*--- G [j+2] [i] ---*/
        greyscale[((j+2)*width)+i] += (error * 4)/42;
        if (i+1 < width)		/*--- G [j+2] [i+1] ---*/
          greyscale[((j+2)*width)+i+1] += (error * 2)/42;
        if (i+2 < width)		/*--- G [j+2] [i+2] ---*/
          greyscale[((j+2)*width)+i+2] += error/42;
       }

      if (result)
	XPutPixel(image, i, j, whitePixel);
      else
	XPutPixel(image, i, j, blackPixel);
     } /* for(i...) */
}


static int
Do_Pseudo(
    Display *dpy,
    int	     screen,
    Colormap colormap,
    int ncolors,
    XColor *colors,
    enum _DtGrColorModel force,
    XImage *in_image,
    XImage *out_image,
    unsigned long **ret_colors,
    int  *ret_number )
{
    int i, x, y, colorCount = 0;
    XColor *color;
    int      result = 0;
    Pixel    pixel;

    for (i = 0; i < ncolors; i++)
	colors[i].flags = 0;

    *ret_colors = NULL;
    *ret_number = 0;

    /*
     * beware 'result'.
     * It is set to one upon entering this routine.
     * The only way it can be modified is by the call to XAllocColor.
     */
    if (force == _DtGrCOLOR)
	result = 1;

    for (y = 0; result && y < in_image->height; y++)
      {
        for (x = 0; result && x < in_image->width; x++)
	  {
	    pixel = XGetPixel(in_image, x, y);
	    color = &colors[pixel];
	    if (!color->flags)
	      {
		color->flags = DoRed | DoGreen | DoBlue;
		result = XAllocColor(dpy, colormap, color);
		if (!result)
		    color->flags = 0;
		else
		    colorCount++;
	      }
	    if (result)
	        XPutPixel(out_image, x, y, color->pixel);
	  }
      }

    /*
     * If result == 0, a call to XAllocColor failed
     * Try to grey scale the image.
     */
    if (!result)
      {
	if (colorCount)
	  {
	    for (i = 0; i < ncolors; i++)
	      {
	        if (colors[i].flags)
	          {
	            XFreeColors (dpy, colormap, &(colors[i].pixel), 1, 0);
		    colors[i].flags = 0;
	          }
	      }
	  }
	result = GreyScale (dpy, screen, colormap, in_image, out_image, colors,
			ncolors, force, 0, 0, 0, 0, 0, 0);
      }
    else if (colorCount)
      {
	result = GR_SUCCESS;
        *ret_colors = (unsigned long *) malloc (
					sizeof (unsigned long) * colorCount);
	if (*ret_colors == NULL)
	  {
	    colorCount = 0;
	    result = GR_ALLOC_ERR;
	  }

        for (i = 0, x = 0; i < ncolors && x < colorCount; i++)
	    if (colors[i].flags)
	        (*ret_colors)[x++] = colors[i].pixel;

	*ret_number = colorCount;
      }

    /*
     * result was set to a XHPIF value via the 'else' stmt or
     * returned from GreyScale routine.
     */
    return result;
}

static int
Do_Direct(
    Display *dpy,
    int      screen,
    XWDFileHeader *header,
    Colormap colormap,
    int ncolors,
    XColor *colors,
    enum _DtGrColorModel force,
    XImage *in_image,
    XImage *out_image,
    unsigned long **ret_colors,
    int  *ret_number )
{
    int x, y;
    XColor color;
    unsigned long rmask, gmask, bmask;
    int   rshift = 0, gshift = 0, bshift = 0;
    int   i;
    int   result;
    int   pixMax = 256;
    int   pixI   = 0;
    Pixel pix;
    Pixel *oldPixels;
    Pixel *newPixels;

    oldPixels = (Pixel *) malloc (sizeof (Pixel) * pixMax);
    newPixels = (Pixel *) malloc (sizeof (Pixel) * pixMax);

    if (oldPixels == NULL || newPixels == NULL)
      {
	if (oldPixels)
	    free (oldPixels);
	if (newPixels)
	    free (newPixels);

	return GR_ALLOC_ERR;
      }

    rmask = header->red_mask;
    while (!(rmask & 1)) {
	rmask >>= 1;
	rshift++;
    }
    gmask = header->green_mask;
    while (!(gmask & 1)) {
	gmask >>= 1;
	gshift++;
    }
    bmask = header->blue_mask;
    while (!(bmask & 1)) {
	bmask >>= 1;
	bshift++;
    }
    if (in_image->depth <= 12)
	pix = 1 << in_image->depth;

    if (force == _DtGrCOLOR)
        color.flags = DoRed | DoGreen | DoBlue;
    else
	color.flags = 0;

    for (y = 0; color.flags && y < in_image->height; y++)
      {
	for (x = 0; color.flags && x < in_image->width; x++)
	  {
	    pix = XGetPixel(in_image, x, y);

	    i = 0;
	    while (i < pixI && oldPixels[i] != pix)
		i++;

	    if (i == pixI)
	      {
		color.red = (pix >> rshift) & rmask;
		color.green = (pix >> gshift) & gmask;
		color.blue = (pix >> bshift) & bmask;
		if (ncolors) {
		    color.red = colors[color.red].red;
		    color.green = colors[color.green].green;
		    color.blue = colors[color.blue].blue;
		} else {
		    color.red = ((unsigned long)color.red * 65535) / rmask;
		    color.green = ((unsigned long)color.green * 65535) / gmask;
		    color.blue = ((unsigned long)color.blue * 65535) / bmask;
		}
		if (!XAllocColor(dpy, colormap, &color))
		    color.flags = 0;
		else 
		  {
		    if (pixI >= pixMax)
		      {
			pixMax += 128;
			oldPixels = (Pixel *) realloc ((void *) oldPixels,
					(sizeof (Pixel) * pixMax));
			newPixels = (Pixel *) realloc ((void *) newPixels,
					(sizeof (Pixel) * pixMax));

			/*
			 * check the realloc
			 */
			if (oldPixels == NULL || newPixels == NULL)
			  {
			    if (oldPixels)
				free (oldPixels);

			    if (newPixels)
			      {
				XFreeColors(dpy, colormap, newPixels, pixI, 0);
				free (newPixels);
			      }

			    return GR_ALLOC_ERR;
			  }
		      }
		    oldPixels[pixI]   = pix;
		    newPixels[pixI++] = color.pixel;
		  }
	      }
	    if (color.flags)
	        XPutPixel(out_image, x, y, newPixels[i]);
	  }
      }
    if (color.flags)
      {
	result = GR_SUCCESS;
        if (pixI < pixMax)
	  {
	    newPixels = (Pixel *) realloc ((void *) newPixels,
					(sizeof (Pixel) * pixI));
	    if (newPixels == NULL)
	        result = GR_ALLOC_ERR;
	  }

	free (oldPixels);

        *ret_colors = newPixels;
        *ret_number = pixI;
      }
    else
      {
	if (pixI)
	    XFreeColors (dpy, colormap, newPixels, pixI, 0);

        free (oldPixels);
        free (newPixels);

	result = GreyScale(dpy, screen, colormap, in_image, out_image, colors,
			ncolors, force, rshift, gshift, bshift, rmask, gmask, bmask);
      }

    return result;
}

static unsigned int
Image_Size(
     XImage *image )
{
    if (image->format != ZPixmap)
      return(image->bytes_per_line * image->height * image->depth);

    return((unsigned)image->bytes_per_line * image->height);
}

static int
XwdFileToPixmap (
    Display  *dpy,
    int       screen,
    int       depth,
    Pixmap    pixmap,
    Colormap  colormap,
    Visual   *visual,
    GC        gc,
    enum _DtGrColorModel  color_model,
    int       src_x,
    int       src_y,
    int       dst_x,
    int       dst_y,
    int       width,
    int       height,
    _DtGrStream *stream,
    unsigned long **ret_colors,
    int      *ret_number )
{
    int result;
    int i;
    XImage in_image, *out_image;
    char *buffer;
    unsigned long swaptest = 1;
    int count;
    unsigned buffer_size;
    int ncolors;
    Bool rawbits = False;
    XColor *colors = NULL;
#ifdef __alpha
/* Use a different structure for compatibility with 32-bit platform */
    XWDColor   xwd_color;
#endif /* __alpha */
     XWDFileHeader header;

    /* Reset the pointer to the beginning of the stream */
    _DtGrSeek(stream, 0, SEEK_SET);
    
    /*
     * Read in header information.
     */
    if(_DtGrRead((char *)&header, sizeof(header), 1, stream) != 1)
      {
        return GR_HEADER_ERR;
      }

    if (*(char *) &swaptest)
	_swaplong((char *) &header, sizeof(header));

    /* check to see if the dump file is in the proper format */
    if (header.file_version != XWD_FILE_VERSION)
      {
	return GR_HEADER_ERR;
      }

    if (header.header_size < sizeof(header))
      {
	return GR_HEADER_ERR;
      }

     /*
      * skip the window name
      */
    if (_DtGrSeek(stream, (header.header_size - sizeof(header)), 1))
      {
	return GR_FILE_ERR; 
      }

    /*
     * initialize the input image
     */
    in_image.width = (int) header.pixmap_width;
    in_image.height = (int) header.pixmap_height;
    in_image.xoffset = (int) header.xoffset;
    in_image.format = (int) header.pixmap_format;
    in_image.byte_order = (int) header.byte_order;
    in_image.bitmap_unit = (int) header.bitmap_unit;
    in_image.bitmap_bit_order = (int) header.bitmap_bit_order;
    in_image.bitmap_pad = (int) header.bitmap_pad;
    in_image.depth = (int) header.pixmap_depth;
    in_image.bits_per_pixel = (int) header.bits_per_pixel;
    in_image.bytes_per_line = (int) header.bytes_per_line;
    in_image.red_mask = header.red_mask;
    in_image.green_mask = header.green_mask;
    in_image.blue_mask = header.blue_mask;
    in_image.obdata = NULL;
    _XInitImageFuncPtrs(&in_image);

    /* read in the color map buffer */
    ncolors = header.ncolors;
    if (ncolors) {
	colors = (XColor *)malloc((unsigned) ncolors * sizeof(XColor));
	if (!colors)
          {
	    return GR_ALLOC_ERR;
	  }

#ifdef __alpha
/* Use XWDColor instead of XColor. Byte-swapping if it is necessary.
 * Move values back into Xcolor structure.
 */
        for (i = 0; i < ncolors; i++) {
            if (_DtGrRead( (char *) &xwd_color, sizeof(XWDColor), 1 , stream)  != 1 )
            {
                XFree ((char *) colors);
                return GR_FILE_ERR;
            }

            if (*(char *) &swaptest) {
                _swaplong((char *) &xwd_color.pixel, sizeof(xwd_color.pixel));
                _swapshort((char *) &xwd_color.red, 3 * sizeof(xwd_color.red));
            }

            colors[i].pixel = xwd_color.pixel;
            colors[i].red   = xwd_color.red;
            colors[i].green = xwd_color.green;
            colors[i].blue  = xwd_color.blue;
            colors[i].flags = xwd_color.flags;
        }
#else
	if(_DtGrRead((char *) colors, sizeof(XColor), ncolors, stream) != ncolors)
	  {
	    XFree ((char *) colors);
	    return GR_FILE_ERR;
	  }

	if (*(char *) &swaptest) {
	    for (i = 0; i < ncolors; i++) {
		_swaplong((char *) &colors[i].pixel, sizeof(long));
		_swapshort((char *) &colors[i].red, 3 * sizeof(short));
	    }
	}
#endif /* __alpha */
    }

    /*
     * alloc the pixel buffer
     */
    buffer_size = Image_Size(&in_image);
    buffer = (char *) malloc(buffer_size);
    if (buffer == NULL)
      {
	if (NULL != colors)
	    XFree ((char *) colors);
        return GR_ALLOC_ERR;
      }

    /* read in the image data */
    count = _DtGrRead(buffer, sizeof(char), (int)buffer_size, stream);

    if (count != buffer_size)
      {
	if (NULL != colors)
	    XFree ((char *) colors);
	XFree (buffer);
        return GR_FILE_ERR;
      }

    if (in_image.depth == 1) {
	in_image.format = XYBitmap;
	rawbits = True;
    }
    in_image.data = buffer;

    /* create the output image */
    result = GR_SUCCESS;

    if (rawbits) {
	out_image = &in_image;
    } else {
	out_image = XCreateImage(dpy, visual, depth,
			 (depth == 1) ? XYBitmap : in_image.format,
				 in_image.xoffset, NULL,
				 in_image.width, in_image.height,
				 XBitmapPad(dpy), 0);

	out_image->data = (char *) malloc(Image_Size(out_image));

	if ((header.visual_class == TrueColor) ||
		   (header.visual_class == DirectColor))
	    result = Do_Direct(dpy, screen, &header, colormap, ncolors, colors,
			color_model,
		      &in_image, out_image, ret_colors, ret_number);
	else
	    result = Do_Pseudo(dpy, screen, colormap, ncolors, colors, color_model,
			&in_image, out_image, ret_colors, ret_number);
    }

    if (result != GR_ALLOC_ERR)

    _XmPutScaledImage(dpy, pixmap, gc, out_image,
		      src_x, src_y, dst_x, dst_y,
		      in_image.width, in_image.height,
		      width, height);

    /*
     * free the buffers
     */
    if (NULL != colors)
        XFree ((char *) colors);
    XFree (buffer);
    if (!rawbits)
	XDestroyImage (out_image);

    return result;
}

/*****************************************************************************/
/*  General functions for changing a file into a pixmap                      */
/*                                                                           */
/*****************************************************************************/
/***********
 *
 * Function processBitmap
 *
 * takes a string.
 *	Make a bitmap into the pixmap for the graphic.
 *
 ***********/
static enum _DtGrLoadStatus processBitmap(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    int           result;
    int           junk;
    unsigned int  width, height;
    unsigned char *data;
    Pixmap        scaled_pixmap = 0;
    Display       *dpy = DisplayOfScreen(screen);
    Drawable      drawable = RootWindowOfScreen(screen);
    XImage        ximage;
    float          ratio = 1.0;

    if (media_resolution == 0) 
        return(_DtGrCONVERT_FAILURE);

    if (*in_out_width == 0 && *in_out_height == 0) 
	 ratio = ComputeRatio(media_resolution, 100);

    if (stream->type == _DtGrNONE)
        return(_DtGrCONVERT_FAILURE);

    result = _DtGrReadBitmapStreamData (stream,
					&width, &height, &data, &junk, &junk);

    /*  Be sure to implement XvpCopyPlane later         */
    if (result == BitmapSuccess)
    {
	ximage.height = height;
	ximage.width = width;
	ximage.depth = 1;
	ximage.bits_per_pixel = 1;
	ximage.xoffset = 0;
	ximage.format = XYBitmap;
	ximage.data = (char *)data;
	ximage.byte_order = LSBFirst;
	ximage.bitmap_unit = 8;
	ximage.bitmap_bit_order = LSBFirst;
	ximage.bitmap_pad = 8;
	ximage.bytes_per_line = (width+7)/8;
	XInitImage(&ximage);

        *in_out_width = (Dimension) width * ratio + 0.5;
        *in_out_height = (Dimension) height * ratio + 0.5;
	if (*in_out_width == 0)
	   *in_out_width = 1;
	if (*in_out_height == 0) 
	   *in_out_height = 1;

	scaled_pixmap = XCreatePixmap (dpy, drawable, (*in_out_width),
				       (*in_out_height), depth);
       _XmPutScaledImage(dpy, scaled_pixmap, gc, &ximage,0, 0, 0, 0,
			 width,height,(*in_out_width),(*in_out_height));
       XFree((char *)data);
       *ret_pixmap = scaled_pixmap;
    }

    if (result == BitmapSuccess)
        return(_DtGrSUCCESS);
    else if (result == BitmapOpenFailed)
        return(_DtGrOPEN_FAILED);
    else if (result == BitmapFileInvalid)
        return(_DtGrFILE_INVALID);
    else if (result == BitmapNoMemory)
        return(_DtGrNO_MEMORY);
    else 
        return(_DtGrCONVERT_FAILURE);
}

#ifndef	STUB
/***********
 *
 * Function processTiff
 *
 * takes a string.
 *	Make a tiff into the pixmap for the graphic.
 *
 ***********/
static enum _DtGrLoadStatus processTiff(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    int		 result = -1;
    Pixmap       pixmap = 0;
    ilFile	 inFile;
    ilPipe	 inPipe;
    ilFileImage	 inImage;
    const ilImageDes   *inDes = 0;
    static ilContext	IlContext = NULL;
    Display      *dpy = DisplayOfScreen(screen);
    Drawable      drawable = RootWindowOfScreen(screen);  
    ilXWC	 *tiff_xwc;
    float         ratio = 1.0;

    _DtHelpProcessLock();
    if ((context == NULL) || (stream->type == _DtGrNONE))
      {
	_DtHelpProcessUnlock();
        return (_DtGrCONVERT_FAILURE);
      }

    if (media_resolution == 0) 
      {
	_DtHelpProcessUnlock();
        return(_DtGrCONVERT_FAILURE);
      }

    tiff_xwc =  (ilXWC *) &(context->context);
    if (IlContext == NULL)
      {
	if (IL_CREATE_CONTEXT (&IlContext, 0))
	  {
	    IlContext = NULL;
	    _DtHelpProcessUnlock();
 	    return (_DtGrCONVERT_FAILURE);
	  }
      }

    if (*tiff_xwc == NULL)
      {
	/* Fill in the context record fields */
        *tiff_xwc = ilCreateXWC (IlContext, dpy, visual, colormap, gc, 0, 0);
        if (*tiff_xwc == NULL)
	  {
	    _DtHelpProcessUnlock();
 	    return(_DtGrCONVERT_FAILURE);
	  }
        context->image_type = XtNewString("TIFF");
      }

    inFile = ilConnectFile (IlContext, stream, 0, 0);
    if (inFile)
      {
	inImage = ilListFileImages (inFile, 0);
	if (inImage)
	  {
	    int image_resolution = 100 ; /* assume that if not in file */


	    if (inImage->xRes != 0) {
		image_resolution = inImage->xRes ; /* dpi from the file */
	    }

	    if (*in_out_width == 0 && *in_out_height == 0)
		ratio = ComputeRatio(media_resolution, image_resolution);

	    *in_out_width = inImage->width * ratio + 0.5;
	    *in_out_height = inImage->height * ratio + 0.5;
	    if (*in_out_width == 0)
		*in_out_width = 1;
	    if (*in_out_height == 0) 
		*in_out_height = 1;

	    pixmap = XCreatePixmap (dpy, drawable, (*in_out_width),
				(*in_out_height), depth);
	    if (pixmap)
	      {
	        inPipe = ilCreatePipe (IlContext, 0);
	        if (inPipe)
	          {
		    if (color_model == _DtGrGRAY_SCALE)
			inDes = IL_DES_GRAY;
		    else if (color_model == _DtGrBITONAL)
			inDes = IL_DES_BITONAL;

		    result = 0;
		    if (!ilReadFileImage (inPipe, inImage, (ilRect *) NULL, 0))
			result = -1;

		    if (result == 0 && color_model != _DtGrCOLOR &&
			ilConvert (inPipe, inDes, ((ilImageFormat *) NULL), 0, NULL) != True)
			result  = -1;

		    if (result == 0 &&
			ilWriteXDrawable (inPipe, pixmap, *tiff_xwc,
						(ilRect *) NULL, 0, 0, 0))
		      {
			ilExecutePipe (inPipe, 0, ratio);
			if (IlContext->error != 0)
			    result = -1;
		      }
		    ilDestroyObject (inPipe);
	          }
		if (result == -1)
		  {
		    XFreePixmap (dpy, pixmap);
		    pixmap = 0;
		  }
	      }
	  }
	ilDestroyObject (inFile);
      }

    *ret_pixmap = pixmap;
    if (result < 0)
      {
        if ((IlContext->error == IL_ERROR_FILE_IO) ||
            (IlContext->error == IL_ERROR_FILE_NOT_TIFF))
	  {
	     _DtHelpProcessUnlock();
             return(_DtGrFILE_INVALID);
	  }
        else if (IlContext->error == IL_ERROR_MALLOC)
	  {
	     _DtHelpProcessUnlock();
             return(_DtGrNO_MEMORY);
	  }
        else
	  {
	     _DtHelpProcessUnlock();
             return(_DtGrCONVERT_FAILURE);
	  }
      }
    else
      {
	_DtHelpProcessUnlock();
        return(_DtGrSUCCESS);
      }
}
#endif

/***********
 *
 * Function processXwd
 *
 * takes a string.
 *	Make an XWD file into the pixmap for the graphic.
 *
 ***********/
static enum _DtGrLoadStatus processXwd(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    Pixmap         pixmap;
    XWDFileHeader  header;
    int            result;
    unsigned long swaptest = TRUE;
    Display       *dpy = DisplayOfScreen(screen);
    Drawable       drawable = RootWindowOfScreen(screen);  
    int            screen_num = XScreenNumberOfScreen(screen);
    float          ratio = 1.0;

    if (media_resolution == 0) 
        return(_DtGrCONVERT_FAILURE);

    if (stream->type == _DtGrNONE)
        return(_DtGrCONVERT_FAILURE);

    /* Read in XWDFileHeader structure */
    result = _DtGrRead((char *)&header, sizeof(header), 1, stream);

    if (result == 1 && *(char *) &swaptest)
	_swaplong((char *) &header, sizeof(header));

    if (result != 1 || header.file_version != XWD_FILE_VERSION)
	return (_DtGrFILE_INVALID);

    if (*in_out_width == 0 && *in_out_height == 0) 
	 ratio = ComputeRatio(media_resolution, 100);

    *in_out_width = header.pixmap_width * ratio + 0.5;
    *in_out_height = header.pixmap_height * ratio + 0.5;

     if (*in_out_width == 0)
	*in_out_width = 1;
     if (*in_out_height == 0) 
	*in_out_height = 1;

    pixmap = XCreatePixmap (dpy, drawable, (*in_out_width), 
			     (*in_out_height),depth);

    if (pixmap) {
        if ((result=XwdFileToPixmap (dpy, screen_num, depth, pixmap, colormap, 
                visual, gc, color_model, 0, 0, 0, 0,
		(*in_out_width), (*in_out_height), stream,
		ret_colors, ret_num_colors))
			== GR_SUCCESS) {
	    *ret_pixmap = pixmap;
            return (_DtGrSUCCESS);
        }
        else if (result == GR_HEADER_ERR)
            return (_DtGrFILE_INVALID);
        else if (result == GR_FILE_ERR)
            return (_DtGrFILE_INVALID);
        else if (result == GR_ALLOC_ERR)
            return (_DtGrNO_MEMORY);
        else
            return (_DtGrCONVERT_FAILURE);
    }
    else
        return (_DtGrNO_MEMORY);
}

/************************************
 * myXpmReadFileToPixmap
 ***********************************/
static int
myXpmReadFileToPixmap(
    Display        *display,
    Screen         *screen,
    Drawable        d,
    _DtGrStream    *stream,
    Pixmap         *pixmap_return,
    Pixmap         *shapemask_return,
    XpmAttributes  *attributes,
    GC              gc,
    Pixel	    bg,
    Pixel	    fg,
    int             depth,
    float           ratio
)
{
    XImage *image = NULL;
    XImage **imageptr = 0;
    XImage *shapeimage, **shapeimageptr = NULL;
    int ErrorStatus;
    int switchFlag = 0;
    int scaledWidth, scaledHeight;
    /*
     * initialize return values
     */

    if (pixmap_return) {
        *pixmap_return = 0;
        imageptr = &image;
    }
    if (shapemask_return) {
        *shapemask_return = 0;
        shapeimageptr = &shapeimage;
    }

    /*
     * create the images
     */

    if (stream->type == _DtGrFILE)
    {
        if (stream->source.file.uncompressed_filename != NULL)
            ErrorStatus = XpmReadFileToImage(display,
                                    stream->source.file.uncompressed_filename, 
                                    imageptr, shapeimageptr, attributes);
        else
            ErrorStatus = XpmReadFileToImage(display,
                                    stream->source.file.filename, 
                                    imageptr, shapeimageptr, attributes);
    }
    else if (stream->type == _DtGrBUFFER)
    {
            ErrorStatus = XpmCreateImageFromBuffer(display, 
                                    (char *) stream->source.buffer.base, 
                                    imageptr, shapeimageptr, attributes);
    }
    else
        ErrorStatus = XpmFileInvalid; /* stream type of _DtGrNONE */

    if (ErrorStatus < 0)
        return (ErrorStatus);

    /*
     * Check to see if we will need to switch the foreground and
     * background colors.  When forced to a depth of 1, the Xpm call
     * returns a ZPixmap ready to place in pixmap of depth 1.
     * Unfortunately, usually the pixmap is of a different depth.  This
     * causes a depth mismatch for the XPutImage.  Therefore, we change
     * the format type to XYBitmap and XPutImage is happy.  But on
     * servers where BlackPixel is not at pixel 1 in the colormap, this
     * causes the image to be inverted.  So by switching the foreground
     * and background in the gc, the image is corrected for these
     * systems.
     *
     * The other way of doing this, is to create a pixmap of depth 1 and
     * do an XPutImage to it.  Then do a XCopyPlane to a pixmap of the
     * correct depth.  But this is a major performance hit compared to
     * switching the colors.
     *
     * The downside of switching colors is that it works only when
     * BlackPixel and WhitePixel fill colormap entries 0 and 1.  But
     * since this is the 99.9% case, we'll go with it.
     */
    if (image->depth == 1 && BlackPixelOfScreen(screen) != 1L)
	switchFlag = 1;

    /*
     * create the pixmaps
     */
    if (imageptr && image) {
	scaledWidth = image->width * ratio + 0.5;
	scaledHeight = image->height * ratio + 0.5;

	if (scaledWidth == 0)
	    scaledWidth = 1;
	if (scaledHeight == 0)
	    scaledHeight = 1;
	
        *pixmap_return = XCreatePixmap(display, d, scaledWidth,
                                       scaledHeight, depth);
	if (image->depth == 1)
	    image->format = XYBitmap;

	if (switchFlag)
	  {
            XSetBackground (display, gc, fg);
            XSetForeground (display, gc, bg);
	  }
        _XmPutScaledImage(display, *pixmap_return, gc, image, 
			  0, 0, 0, 0,
			  image->width, image->height,
			  scaledWidth, scaledHeight);
	if (switchFlag)
	  {
	    XSetBackground (display, gc, bg);
	    XSetForeground (display, gc, fg);
	  }

        XDestroyImage(image);
    }

    if (shapeimageptr && shapeimage)
        XDestroyImage(shapeimage);

    return (ErrorStatus);
}

static XpmColorSymbol colorSymbol = {"none", NULL, 0};

/***********
 *
 * Function processXpm
 *
 * takes a string.
 *	Make an XPM file into the pixmap for the graphic.
 *
 ***********/

static enum _DtGrLoadStatus processXpm(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    int		    i, j;
    int             result;
    short           done;
    XpmAttributes   xpmAttr;
    Visual          vis2;
    float           ratio = 1.0;

    enum _DtGrLoadStatus status = _DtGrSUCCESS;

    if (media_resolution == 0) 
        return(_DtGrCONVERT_FAILURE);

    if (stream->type == _DtGrNONE)
        return(_DtGrCONVERT_FAILURE); /* stream type of _DtGrNONE */


    _DtHelpProcessLock();
    do
      {
        colorSymbol.pixel = background;

        xpmAttr.valuemask    = XpmVisual       | XpmReturnPixels |
			       XpmColorSymbols | XpmColormap     | XpmDepth;
        xpmAttr.visual       = visual;
        xpmAttr.colorsymbols = &colorSymbol;
        xpmAttr.colormap     = colormap;
        xpmAttr.numsymbols   = 1;
        xpmAttr.depth        = depth;
        xpmAttr.pixels       = NULL;

        /*
         * If not color, force to black and white.
         */
        if (color_model != _DtGrCOLOR)
          {
            memcpy (&vis2, xpmAttr.visual, sizeof(Visual));

            vis2.class       = StaticGray;
            vis2.map_entries = 2;

            xpmAttr.depth  = 1;
            xpmAttr.visual = &vis2;
          }
	if (*in_out_width == 0 && *in_out_height == 0) 
	    ratio = ComputeRatio(media_resolution, 100);

        result = myXpmReadFileToPixmap (DisplayOfScreen(screen), 
					screen, 
					RootWindowOfScreen(screen), 
					stream, ret_pixmap,
					ret_mask, &xpmAttr, gc,
					background, foreground, depth,
					ratio);
	done = True;
	/*
	 * if we did not successfully get our icon, force the color
	 * to black and white.
	 */
	if (result == XpmColorFailed && color_model != _DtGrBITONAL)
	  {
            if (allow_reduced_colors)
	    {
	        color_model = _DtGrBITONAL;
                status = _DtGrCOLOR_REDUCE;
	        done = False;
            }
            else
		status = _DtGrCOLOR_FAILED;
          }

      } while (done == False);

    if (result == XpmSuccess || result == XpmColorError)
      {
	*in_out_width   = xpmAttr.width * ratio + 0.5;
	*in_out_height  = xpmAttr.height * ratio + 0.5;
	if (*in_out_width == 0)
	   *in_out_width = 1;
	if (*in_out_height == 0) 
	   *in_out_height = 1;
	*ret_colors     = xpmAttr.pixels;
	*ret_num_colors = xpmAttr.npixels;

	/*
	 * squeeze out the pixel used for the transparent color since we
	 * don't want to free it when we free the other colors
	 */
	i = 0;
	while (i < xpmAttr.npixels && xpmAttr.pixels[i] != colorSymbol.pixel)
	    i++;

	if (i < xpmAttr.npixels)
	  {
	    for (j = i, i = i + 1; i < xpmAttr.npixels; i++, j++)
		xpmAttr.pixels[j] = xpmAttr.pixels[i];

	    *ret_num_colors = j;
	  }
      }
    _DtHelpProcessUnlock();

    if (result == XpmOpenFailed)
        status = _DtGrOPEN_FAILED;
    else if (result == XpmFileInvalid)
        status = _DtGrFILE_INVALID;
    else if (result == XpmNoMemory)
        status = _DtGrNO_MEMORY;
    else if (result == XpmColorFailed)
        status = _DtGrCOLOR_FAILED;

    return(status);
}

/******************************************************************************
 *
 * Function processGIF
 *
 * Default gif converter, creates a pixmap from a file-associated or 
 * buffer-associated stream of gif data.
 *
 *****************************************************************************/
static enum _DtGrLoadStatus processGIF(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    struct stat stbuf;
    char *buffer;
    GifObj g;
    enum _DtGrLoadStatus status;
    Display *display = DisplayOfScreen(screen);   
    Drawable drawable = RootWindowOfScreen(screen);
    int i, size=0;
    float ratio = 1.0;

    if (media_resolution == 0) 
        return(_DtGrCONVERT_FAILURE);

    if (stream->type == _DtGrNONE)
        return(_DtGrCONVERT_FAILURE); /* stream type of _DtGrNONE */

    *ret_pixmap = 0; /* Initialize the return pixmap to zero */

    /* 
    ** The gif-to-pixmap utilities operate only on in-memory buffers,
    ** so if this is a buffer-based stream, simply pass the buffer, and
    ** if this is a file-based stream, read the file contents into a buffer 
    ** and pass that.
    */

    if (stream->type == _DtGrBUFFER)
    {
        buffer = (char *) stream->source.buffer.base;
        size = stream->source.buffer.size;
    }
    else
    {
       if (stream->source.file.uncompressed_filename != NULL)
           stat (stream->source.file.uncompressed_filename, &stbuf);
       else
           stat (stream->source.file.filename, &stbuf);
       size = stbuf.st_size;
       buffer = XtMalloc(size);
       fread (buffer, 1, size, stream->source.file.fileptr);  
    }

    /* Initialize the gif object */
    status = InitGifObject (&g, display, drawable, screen, depth, colormap, 
                            visual, gc, color_model, allow_reduced_colors);

    if (*in_out_width == 0 && *in_out_height == 0) 
	 ratio = ComputeRatio(media_resolution, 100);

    /* Create an X pixmap from the gif object */
    if ((status == _DtGrSUCCESS) || (status == _DtGrCOLOR_REDUCE))
        *ret_pixmap = gif_to_pixmap (&g, (unsigned char *) buffer, size, 
                                     in_out_width, in_out_height, 
                                     g.f_black, g.f_white, ratio);

    /* Set the returned colors parameters */
    if (*ret_pixmap != 0)
    {
        if (g.f_do_visual == DO_COLOR)
	{
            *ret_num_colors = g.total_colors;       
            *ret_colors = (Pixel *) malloc(*ret_num_colors * sizeof(Pixel));
            for (i=0; i<*ret_num_colors; i++)
                (*ret_colors)[i] = g.GifCMap[i].pixel;
        }
        else /* DO_GREY */
	{
            *ret_num_colors = g.f_total_greys;       
            *ret_colors = (Pixel *) malloc(*ret_num_colors * sizeof(Pixel));
            for (i=0; i<*ret_num_colors; i++)
                (*ret_colors)[i] = g.GifGMap[i];
        }
    }

    /* Free up any resources associated with the gif object */
    DeleteGifObjectResources (&g);

    /* If we allocated a buffer, free it */
    if (stream->type == _DtGrFILE)
        XtFree(buffer);

    /* Return the status */
    return (status);
}

/******************************************************************************
 *
 * Function processJPEG
 *
 * Default jpeg converter, creates a pixmap from a file-associated or 
 * buffer-associated stream of jpeg data.
 *
 * The function first converts the jpeg stream to an XImage with a virtual
 * colormap using the jpeg_to_ximage call, then generates a new XImage that
 * uses the X colormap by calling the Do_Pseudo routine.  The Do_Pseudo 
 * routine, which is also used by the XWD converter, automatically handles X 
 * color allocation and color model degradation where necessary.  Finally,
 * a pixmap is generated from the XImage returned by Do_Pseudo.
 *
 *****************************************************************************/
static enum _DtGrLoadStatus processJPEG(
    _DtGrStream           *stream,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    enum _DtGrLoadStatus status;
    XImage *in_image, *out_image;
    XColor *colors;
    int ncolors;
    int xres;
    int result = GR_SUCCESS;
    Display *dpy = DisplayOfScreen(screen); 
    int screen_num = XScreenNumberOfScreen(screen);
    Drawable drawable = RootWindowOfScreen(screen); 
    float ratio = 1.0;
    unsigned int scaledWidth,scaledHeight;

    if (media_resolution == 0) 
        return(_DtGrCONVERT_FAILURE);

    if (*in_out_width == 0 && *in_out_height == 0) 
	ratio = 0.0;

    if (stream->type == _DtGrNONE)
        return(_DtGrCONVERT_FAILURE); /* stream type of _DtGrNONE */

    /*
    ** Convert the stream to an XImage with a virtual colormap
    */
    status = jpeg_to_ximage (stream, screen, visual, in_out_width,
                             in_out_height, &in_image, &colors, &ncolors,
			     &xres);

    if (ratio == 0.0) {
	if (!xres)
	    xres = 100;
	ratio = ComputeRatio(media_resolution, xres);
    }

    if (status == _DtGrSUCCESS)
    {
        if (in_image->depth == 1)
	{
            /*
	    ** Bitmap, no further image transformation necessary 
            */
	    out_image = in_image; 
            result = GR_SUCCESS;
        }
        else
        {
            /*
	    ** Create an output image and have Do_Pseudo allocate the needed
            ** pixels and store image data that uses these pixels into
            ** the output image.
            */
	    out_image = XCreateImage(dpy, visual, depth,
			           (depth == 1) ? XYBitmap : in_image->format,
				   in_image->xoffset, NULL,
				   in_image->width, in_image->height,
				   XBitmapPad(dpy), 0);

	    out_image->data = (char *) malloc(Image_Size(out_image));

            result = Do_Pseudo(dpy, screen_num, colormap, ncolors, colors, 
                               color_model, in_image, out_image, 
                               ret_colors, ret_num_colors);
	}

        /*
	** If we've got a good XImage, go ahead and make a pixmap out of it
        */
        if (result != GR_ALLOC_ERR)
	{
            /*
	    ** Create a pixmap the same size as the XImage 
            */        

	    scaledWidth = out_image->width * ratio + 0.5;
	    scaledHeight = out_image->height * ratio + 0.5;

	    if (scaledWidth == 0)
		scaledWidth = 1;
	    if (scaledHeight == 0)
		scaledHeight = 1;
	
            *ret_pixmap = XCreatePixmap (dpy, drawable, scaledWidth, 
                                         scaledHeight, depth);
            if (*ret_pixmap)
	    {
                /*
		** Copy the XImage into the pixmap and set the other
                ** return parameters.
                */
                _XmPutScaledImage(dpy, *ret_pixmap, gc, out_image, 0, 0, 0, 0, 
				  out_image->width, out_image->height, 
		                  scaledWidth, scaledHeight); 

                *in_out_width = scaledWidth;
                *in_out_height = scaledHeight;              
                status = _DtGrSUCCESS;
            }
            else 
                status = _DtGrNO_MEMORY;

            if (out_image != in_image)
	        XDestroyImage (out_image);
        }
        else
            status = _DtGrNO_MEMORY;

        /*
	** Free the colors array and the initial XImage 
        */
        XFree ((char *) colors);
        XDestroyImage (in_image);
    }

    return (status);
}

/******************************************************************************
 *
 * Function destroyTiffContext
 *
 * Default tiff context destructor, destroys the ilXWC object pointed to
 * by context->context and frees the context->image_type string.
 *
 *****************************************************************************/
static void destroyTiffContext(
    _DtGrContext          *context)
{
    if (context != NULL)
    {
        if (context->context != NULL)
	  {
	    _DtHelpProcessLock();
            ilDestroyObject ((ilObject) context->context);
	    _DtHelpProcessUnlock();
	  }
        if (context->image_type != NULL)
            XtFree(context->image_type);
    }
}

/******************************************************************************
 *
 * Function DetermineImageType
 *
 * Determines the image type of the specified stream and copies the
 * string representing that image type into the image_type buffer.
 *
 * If the stream is a buffer, the buffer is passed to DtDtsBufferToData
 * in hopes that the file might be identified through content criteria.
 * 
 * If the stream is a file, the following checks are made:
 *
 *   1. The file name extension is checked against a list of known image file 
 *      type extensions.
 *   2. The filename is passed to DtDtsFileToDataType in hopes that the file
 *      type might be identified through filename extension or content 
 *      criteria. 
 *   3. If the file was uncompressed into a temporary file, the temporary
 *      filename is passed to DtDtsFileToDataType in hopes that the file
 *      type might be identified through content criteria.
 *
 * If none of these checks are successful, _DtCONVERT_FAILURE is returned.
 * Otherwise, _DtGrSUCCESS is returned.
 *
 *****************************************************************************/
static enum _DtGrLoadStatus DetermineImageType(
    _DtGrStream *stream,
    char        *image_type)
{
    int i=0;
    char *ext;

    if (stream->type == _DtGrFILE)
    {
        /* First try to match the file name extension to a known mapping */
        if (stream->source.file.filename != NULL)
	{
            /* Get the filename extension */
            if (_DtHelpCeStrrchr(stream->source.file.filename, ".", 
                                 MB_CUR_MAX, &ext) == 0)
	    {
                ext++; /* Increment past the dot */
        	while (img_extensions[i] != NULL)
		{
                    if (strcmp(ext, img_extensions[i]) == 0)
		    {
                        /* Found a match */
		        strcpy (image_type,img_extensions[++i]);
                        return (_DtGrSUCCESS);
                    }
                    /* Skip two ahead to the next file extension name */
                    i += 2;
                }
            }
	}

        /* 
         * Didn't work, see if CDE datatyping can determine the image type.  
         * First try the filename, then try the uncompressed temporary 
         * filename, if it exists.
         */
        if ((ext = DtDtsFileToDataType(stream->source.file.filename)) == NULL)
            if (stream->source.file.uncompressed_filename != NULL)
                ext = DtDtsFileToDataType(
                                   stream->source.file.uncompressed_filename);

        /* If successful, save the image type and return */
        if (ext != NULL)
	{
            strcpy (image_type, ext);
            DtDtsFreeDataType(ext);
            return (_DtGrSUCCESS);
        }
    }
    else if (stream->type == _DtGrBUFFER)
    {
        /* See if the CDE datatyping mechanism can determine the image type */
        ext = DtDtsBufferToDataType (stream->source.buffer.base,
				     stream->source.buffer.size,
				     NULL);

        /* If successful, save the image type and return */
        if (ext != NULL)
	{
            strcpy (image_type, ext);
            DtDtsFreeDataType(ext);
            return (_DtGrSUCCESS);
        }
     }

    /* Couldn't figure out the type, return failure */
    return (_DtGrCONVERT_FAILURE);
}

/******************************************************************************
 *
 * Function GetConverterAndDestructor
 *
 * This function returns the converter and context destructor functions for
 * the specified image type.  The registry of default image types is searched
 * first, followed by the registry of new image types, until a match is made.
 * The function returns _DtGrSUCCESS if the image_type has been registered 
 * and _DtGrCONVERT_FAILURE if it has not.
 *
 *****************************************************************************/
static enum _DtGrLoadStatus GetConverterAndDestructor(
    char *image_type, 
    _DtGrLoadProc *converter,
    _DtGrDestroyContextProc *destructor)
{
    int i;

    /* No image_type string, return failure */
    if (image_type == NULL)
        return (_DtGrCONVERT_FAILURE);

    /* Search the default image type registry for the specified image type */
    _DtHelpProcessLock();
    for (i=0; i<registry_count; i++)
    {
        if (strcmp (registry[i].image_type, image_type) == 0)
	{
	    /* Found it, return the converter and destructor */
            *converter = registry[i].convert_proc;
            *destructor = registry[i].destroy_context_proc;
	    _DtHelpProcessUnlock();
            return (_DtGrSUCCESS);
	}
    }    

    /* Search the new image type registry for the specified image type */
    for (i=0; i<new_registry_count; i++)
    {
        if (strcmp (new_registry[i].image_type, image_type) == 0)
	{
	    /* Found it, return the converter */
            *converter = new_registry[i].convert_proc;
            *destructor = registry[i].destroy_context_proc;
	    _DtHelpProcessUnlock();
            return (_DtGrSUCCESS);
	}
    }    
    _DtHelpProcessUnlock();

    /* Image type wasn't registered, return failure */
    return (_DtGrCONVERT_FAILURE);
}

/******************************************************************************
 *
 * Public Functions
 *
 *****************************************************************************/
/******************************************************************************
 *
 * Function _DtHelpProcessGraphic
 *
 * takes a string.
 *	Get the pixmap for the graphic.
 *
 *****************************************************************************/
Pixmap 
_DtHelpProcessGraphic(
	Display    *dpy,
	Drawable    drawable,
	int	    screen,
	int	    depth,
	GC          gc,
        Pixmap	   *def_pix,
	Dimension  *def_pix_width,
	Dimension  *def_pix_height,
        _DtGrContext *context,
	Colormap    colormap,
	Visual	   *visual,
	Pixel	    fore_ground,
	Pixel	    back_ground,
        char       *filename,
	unsigned short media_resolution,
	Dimension  *width,
	Dimension  *height,
	Pixmap     *ret_mask,
	Pixel     **ret_colors,
	int	   *ret_number )
{

    unsigned int    pixWidth  = 0;
    unsigned int    pixHeight = 0; 
    Dimension       pWidth, pHeight;
    Pixmap pix = 0, mask = 0;
    _DtGrStream stream;
    Screen *scrptr = ScreenOfDisplay (dpy, screen);
    static enum _DtGrColorModel ForceColor = _DtGrCOLOR;
    static Boolean first_time = TRUE;

    /*
     * initialize the return values
     */
    *ret_colors = NULL;
    *ret_number = 0;
    *ret_mask   = None;

    /*
     * Initialize the Force Variable if this is the first time
     */
    _DtHelpProcessLock();
    if (first_time)
      {
	char    *ptr;
	char    *ptr2;
	char    *xrmName  = NULL;
	char    *xrmClass = NULL;
	char	*retStrType;
	XrmValue retValue;

	ForceColor = _DtGrCOLOR;

	XtGetApplicationNameAndClass (dpy, &ptr, &ptr2);
	xrmName  = malloc (strlen (ptr) + 14);
	xrmClass = malloc (strlen (ptr2) + 14);
	if (xrmName && xrmClass)
	  {
	    strcpy (xrmName , ptr);
	    strcat (xrmName , ".helpColorUse");

	    strcpy (xrmClass, ptr2);
	    strcat (xrmClass, ".HelpColorUse");

	    if (XrmGetResource (XtDatabase (dpy), xrmName, xrmClass,
					&retStrType, &retValue) == True)
	      {
		ptr = (char *) retValue.addr;
		/*
		 * check for GreyScale
		 */
		if (*ptr == 'G' || *ptr == 'g')
		    ForceColor = _DtGrGRAY_SCALE;
		/*
		 * check for BlackWhite
		 */
		else if (*ptr == 'B' || *ptr == 'b')
		    ForceColor = _DtGrBITONAL;
	      }
	  }

	free (xrmName);
	free (xrmClass);

	/*
	 * choose the correct visual type to use
	 */
	if (ForceColor == _DtGrCOLOR &&
		(visual->class == GrayScale || visual->class == StaticGray))
	    ForceColor = _DtGrGRAY_SCALE;

	if (ForceColor != _DtGrBITONAL && depth == 1)
	    ForceColor = _DtGrBITONAL;

        first_time = FALSE;
      }
    _DtHelpProcessUnlock();

    if (filename != NULL)
      {
	int   result;

        /* Open the file and associate with a stream */
	result = _DtGrOpenFile(&stream, filename);

	if (result != 0)
	    pix = 0;
        else 
	{
	    /* Create a pixmap from the image data stream */
           _DtGrLoad(&stream, NULL, scrptr, depth, colormap, visual,
                     fore_ground, back_ground, gc, ForceColor, TRUE, &pWidth, 
                     &pHeight, media_resolution, &pix, &mask, ret_colors, 
		     ret_number, context);
           pixWidth = pWidth;
           pixHeight = pHeight;
           /* Close the stream and the file associated with it */
           _DtGrCloseStream( &stream );

        }
      }

    if (pix == 0)
      {
	/*
	 * Try to get a localized pixmap
         * NOTE....
         * This is a cached pixmap.....
         * Make sure XFreePixmaps does not free this one.
	 */
	if (*def_pix == 0)
	  {
	    *def_pix = XmGetPixmap(scrptr, "Dthgraphic", fore_ground, back_ground);
	    if (*def_pix != XmUNSPECIFIED_PIXMAP)
	      {
	        Window        root_id;
	        int           x, y;
	        unsigned int  border;
	        unsigned int  depth;

	        if (XGetGeometry(dpy, *def_pix, &root_id, &x, &y,
				&pixWidth, &pixHeight, &border, &depth) == 0)
	          {
		    XmDestroyPixmap(scrptr, *def_pix);
		    *def_pix = 0;
	          }
	      }
	    else
	        *def_pix = 0;

	    /*
	     * couldn't get a localized pixmap, go with a build in default
	     */
	    if (*def_pix == 0)
	      {
	        pixWidth  = Missing_bm_width;
	        pixHeight = Missing_bm_height;
	        *def_pix  = XCreatePixmapFromBitmapData ( dpy, drawable,
					(char *) Missing_bm_bits,
					pixWidth, pixHeight,
					fore_ground, back_ground, depth);
	      }

	    *def_pix_width  = (Dimension) pixWidth;
	    *def_pix_height = (Dimension) pixHeight;
	  }

	pix       = *def_pix;
        pixWidth  = *def_pix_width;
        pixHeight = *def_pix_height;
      }

    *width  = (Dimension) pixWidth;
    *height = (Dimension) pixHeight;

    return pix;
}


/******************************************************************************
 *
 * Function _DtGrLoad
 *
 * Determines the image type of the specified stream and calls the appropriate
 * imagetype-to-pixmap converter function.  If the conversion is successful
 * and the caller passed the address of a pointer to NULL in the image_type
 * parameter, a copy of the image type string is allocated and stored at this
 * address.  The caller is responsible for freeing this string.  The caller
 * is also responsible for freeing the values passed back in the ret_pixmap,
 * ret_mask, ret_colors, and context parameters.  The _DtGrLoad function 
 * returns _DtGrCONVERT_FAILURE if the appropriate converter could not be
 * determined, otherwise it returns the value returned by the converter.
 *
 *****************************************************************************/
enum _DtGrLoadStatus _DtGrLoad(
    _DtGrStream           *stream,
    char                  **image_type,
    Screen                *screen,
    int                   depth,
    Colormap              colormap,
    Visual                *visual,
    Pixel                 foreground,
    Pixel                 background,
    GC                    gc,
    enum _DtGrColorModel  color_model,
    Boolean               allow_reduced_colors,
    Dimension             *in_out_width,
    Dimension             *in_out_height,
    unsigned short        media_resolution,
    Pixmap                *ret_pixmap,
    Pixmap                *ret_mask,
    Pixel                 **ret_colors,
    int                   *ret_num_colors,
    _DtGrContext          *context
)
{
    enum _DtGrLoadStatus status;
    _DtGrLoadProc converter;
    _DtGrDestroyContextProc destructor;
    char buf[20],*itype;

    /* Determine the image type */
    if ((image_type != NULL) && (*image_type != NULL))
        itype = *image_type; /* Caller specified image type */
    else
    {
        /* Image type not specified by caller, try to figure it out */
        if ((status = DetermineImageType(stream, buf)) != _DtGrSUCCESS)
	    return (status); /* Return failure if image type is unknown */
        itype = &buf[0];     
    }

    /* Look up the proper converter for this image type */
    if ((status=GetConverterAndDestructor(itype, &converter, &destructor)) == 
	                                                     _DtGrSUCCESS)
    {
        /* Call the converter */
        if (converter != NULL)
	{
	    status = (*converter)(stream, screen, depth, colormap, visual,
                                  foreground, background, gc, color_model, 
                                  allow_reduced_colors, in_out_width,
                                  in_out_height, media_resolution, ret_pixmap, 
	                          ret_mask, ret_colors, ret_num_colors, 
	                          context);
	}
        else
            status = _DtGrCONVERT_FAILURE;
    }
    
    /* Return a copy of the image type string if successful */
    if (status == _DtGrSUCCESS || status == _DtGrCOLOR_REDUCE)
        if ((image_type != NULL) && (*image_type == NULL) && (itype != NULL))
	    *image_type = XtNewString(itype);

    /* Return the conversion status */
    return (status);
}

/******************************************************************************
 *
 * Function _DtGrDestroyContext
 *
 * Obtains and calls the context destructor function for the image type
 * indicated by the context parameter.
 *
 *****************************************************************************/
void _DtGrDestroyContext(
    _DtGrContext *context)
{
    _DtGrLoadProc converter;
    _DtGrDestroyContextProc destructor;

    if (GetConverterAndDestructor(context->image_type, &converter, 
                                  &destructor) == _DtGrSUCCESS)
    {
        if (destructor != NULL)
	    (*destructor)(context);
    }
}

/******************************************************************************
 *
 * Function _DtGrRegisterConverter
 *
 * Registers a converter and context destructor function for the specified
 * image type.  If the image_type has not been registered, a new record for
 * it is created in the new image type registry.  If the image_type has been
 * registered, the current converter and context destructor functions are
 * replaced by those specified by the caller.  If the caller passes valid
 * addresses in the current_convert_proc and current_destroy_proc parameters, 
 * the current converter and context destructor functions will be saved there
 * first so that the caller may restore them at a later time.
 *
 *****************************************************************************/
void _DtGrRegisterConverter(
    char                    *image_type,
    _DtGrLoadProc           convert_proc,
    _DtGrDestroyContextProc destroy_context_proc,
    _DtGrLoadProc           *current_convert_proc,
    _DtGrDestroyContextProc *current_destroy_proc)
{
    int i;

    if (image_type == NULL)
        return;

    /* Search the default converter registry for the specified image type */
    _DtHelpProcessLock();
    for (i=0; i<registry_count; i++)
    {
        if (strcmp (registry[i].image_type, image_type) == 0)
	{
	    /* Found it, save the current procs, then replace them */
            /* with the new ones and return                        */
	    if (current_convert_proc != NULL)
	        *current_convert_proc = registry[i].convert_proc;
	    if (current_destroy_proc != NULL)
	        *current_destroy_proc = registry[i].destroy_context_proc;
            registry[i].convert_proc = convert_proc;
	    registry[i].destroy_context_proc = destroy_context_proc;
	    _DtHelpProcessUnlock();
            return;
	}
    }    

    /* Search the new converter registry for the specified image type */
    for (i=0; i<new_registry_count; i++)
    {
        if (strcmp (new_registry[i].image_type, image_type) == 0)
	{
	    /* Found it, save the current procs, then replace them */
            /* with the new ones and return                        */
	    if (current_convert_proc != NULL)
	        *current_convert_proc = new_registry[i].convert_proc;
	    if (current_destroy_proc != NULL)
	        *current_destroy_proc = new_registry[i].destroy_context_proc;
            new_registry[i].convert_proc = convert_proc;
	    new_registry[i].destroy_context_proc = destroy_context_proc;
	    _DtHelpProcessUnlock();
            return;
	}
    }    
    
    /* If we make it here, we've got a new image type to register */
    new_registry = (_DtGrRegistryRec *) XtRealloc ((char *) new_registry, 
                    sizeof(_DtGrRegistryRec) * (new_registry_count + 1));
    new_registry[new_registry_count].image_type = XtNewString (image_type);
    new_registry[new_registry_count].convert_proc = convert_proc;
    new_registry[new_registry_count].destroy_context_proc = 
                                                        destroy_context_proc;
    new_registry_count++;
    _DtHelpProcessUnlock();

    /* Newly registered type, so return NULL for current function params */
    if (current_convert_proc != NULL)
        *current_convert_proc = NULL;
    if (current_destroy_proc != NULL)
        *current_destroy_proc = NULL;

    return;
}

/******************************************************************************
 *
 * Input stream functions
 * 
 * These functions allow the creation and manipulation of a stream that
 * can be associated with either a file or a buffer.  They are intended to
 * be used by image data to pixmap converter functions that need to handle
 * both image files and in-memory buffers containing file data.
 *
 *****************************************************************************/

/******************************************************************************
 *
 * Function _DtGrOpenFile
 *
 * Opens a file for reading and associates it with the specified stream.  If 
 * the file is compressed, the function uncompresses it prior to opening.  
 * Returns 0 for success, -1 or the value of errno as set by fopen for 
 * failure.
 *
 *****************************************************************************/
int _DtGrOpenFile(
    _DtGrStream *stream,
    char  *path)
{
    char *fname = NULL;

    if (stream == NULL) 
        return (-1);    /* Failure */

    /* Uncompress the file if necessary and obtain the new filename */
    if (_DtHelpCeGetUncompressedFileName(path, &fname) == -1)
    {
        stream->type = _DtGrNONE;
        return (-1);  /* Failure */
    }

    /* Open the file */
    stream->source.file.fileptr = fopen(fname,"r");
    if (stream->source.file.fileptr == NULL)
    {
        /* Failure, couldn't open the file */
        stream->type = _DtGrNONE;
        if ((fname != path) && (fname != NULL))
	{
            unlink (fname);
            free (fname);             
        }   
        return(errno);
    }

    /* Set the stream type and copy the filename */
    stream->type = _DtGrFILE;
    stream->source.file.filename = XtNewString(path);

    /* If the file was uncompressed and renamed, save the new name */
    if (fname == path)
        stream->source.file.uncompressed_filename = NULL;
    else
        stream->source.file.uncompressed_filename = fname;

    return(0);    /* Success */
}

/******************************************************************************
 *
 * Function _DtGrOpenBuffer
 *
 * Associates the specified in-memory buffer with the specified stream.
 * The function returns 0 for success and the EINVAL error code for failure.
 *
 *****************************************************************************/
int _DtGrOpenBuffer(
    _DtGrStream *stream,
    const char  *buffer,
    int         buffer_size)
{
    if ((stream == NULL) || (buffer == NULL) || (buffer_size < 0))
    {
        if (stream != NULL)
	    stream->type = _DtGrNONE;
        return(EINVAL); /* Failure */
    }

    /* Set the appropriate stream fields */
    stream->type = _DtGrBUFFER;
    stream->source.buffer.base = buffer;
    stream->source.buffer.size = buffer_size;
    stream->source.buffer.current = (char *)buffer;
    stream->source.buffer.end = (char *)(buffer + buffer_size - 1);
    return(0); /* Success */
}

/******************************************************************************
 *
 * Function _DtGrCloseStream
 *
 * The function closes the specified stream by setting the stream type to
 * _DtGrNONE.  If the stream is associated with a file, then the file is
 * closed and the filename in the stream structure is freed.  If the file
 * required decompression, then the uncompressed file is unlinked and the
 * uncompressed filename is freed. 
 *
 *****************************************************************************/
int _DtGrCloseStream(
    _DtGrStream *stream)
{
    int status = 0;

    if ((stream == NULL) || (stream->type == _DtGrNONE))
        return(EOF);    /* Failure */

    if (stream->type == _DtGrFILE)
    {
        /* Close the file and free the filename */
        status = fclose(stream->source.file.fileptr);
        if (stream->source.file.filename != NULL)
            XtFree(stream->source.file.filename);      
        if (stream->source.file.uncompressed_filename != NULL)
	{ 
            /* Unlink the uncompressed file and free the filename */
            unlink(stream->source.file.uncompressed_filename);
            free(stream->source.file.uncompressed_filename);
        }
    }
    else if (stream->type == _DtGrBUFFER)
    {
        status = 0;    /* Success */
    }

    stream->type = _DtGrNONE;
    return (status);
}


/******************************************************************************
 *
 * Function _DtGrRead
 *
 * Stream version of fread, reads data from a stream into a buffer.  If the 
 * stream is file-associated, a call to fread is made.  If the stream is
 * buffer-associated, an analogous operation is performed on the stream 
 * buffer.  The number of items read from the stream is returned to the caller.
 *
 *****************************************************************************/
size_t _DtGrRead(
    void        *buffer,
    size_t      size,
    size_t      num_items,
    _DtGrStream *stream)
{
    unsigned nleft;
    int n;

    if ((stream == NULL) || (stream->type == _DtGrNONE))
        return(0);

    if (stream->type == _DtGrFILE)
    {
        return (fread(buffer, size, num_items, stream->source.file.fileptr));
    }
    else if (stream->type == _DtGrBUFFER)
    {
        /* This code mirrors that of fread in libc */

        if (size <= 0 || num_items <= 0)
            return (0);

	for (nleft = num_items * size; ; ) 
        {
            if (stream->source.buffer.current > stream->source.buffer.end) 
            { 
                /* past end of stream */
                if (stream->source.buffer.current == 
                    stream->source.buffer.end + 1)
                    return (num_items - (nleft + size - 1)/size);
                stream->source.buffer.current--;
            }
            n = (nleft < stream->source.buffer.end - 
                         stream->source.buffer.current + 1 ? nleft :
                 stream->source.buffer.end - 
                 stream->source.buffer.current + 1); 
            /* Copy the items into the caller-supplied buffer */
            buffer = (char *)memcpy(buffer, 
                                   (void *) stream->source.buffer.current, 
                                   (size_t)n) + n;
            stream->source.buffer.current += n;
            if ((nleft -= n) == 0)
                return (num_items);
        }
    }
    return 0;
}

/******************************************************************************
 *
 * Function _DtGrSeek
 *
 * Stream version of fseek, repositions the file or buffer pointer of a 
 * stream.  If the stream is file-associated, the return value is the value
 * returned by fseek.  If the stream is buffer-associated, 0 is returned if
 * the requested position is inside the buffer, -1 if it is not.
 *
 *****************************************************************************/
int _DtGrSeek(
    _DtGrStream *stream,
    long        offset,
    int         whence)
{        
    long newpos;

    if ((stream == NULL) || (stream->type == _DtGrNONE))
        return(-1); /* Failure */

    if (stream->type == _DtGrFILE)
    {
        return (fseek(stream->source.file.fileptr,offset,whence));
    }
    else if (stream->type == _DtGrBUFFER)
    {
        switch (whence) 
	{
	    case SEEK_SET:
                newpos = (long)stream->source.buffer.base + offset;
                break;
	    case SEEK_CUR:
                newpos = (long)stream->source.buffer.current + offset;
                break;
	    case SEEK_END:
                newpos = (long)stream->source.buffer.end + 1 + offset;
                break;
	    default:
                return (-1);
        }

        if ((newpos >= (long)stream->source.buffer.base) &&
            (newpos <= (long)stream->source.buffer.end))
	{
            /* New position is within buffer, reposition pointer */
            stream->source.buffer.current = (char *)newpos;
            return(0); /* Success */
        }
        else
            return(-1); /* Failure */
    }
    return -1;
}

/******************************************************************************
 *
 * Function _DtGrGetChar
 *
 * Stream version of fgetc, reads  a character from a stream and advances the 
 * stream position.  The next byte in the stream is returned, or EOF if an
 * error occurs or the end of the stream is reached.
 *
 *****************************************************************************/
int _DtGrGetChar(
    _DtGrStream *stream)
{
    if ((stream == NULL) || (stream->type == _DtGrNONE))
        return(EOF);

    if (stream->type == _DtGrFILE)
    {
        return (fgetc(stream->source.file.fileptr));
    }
    else if (stream->type == _DtGrBUFFER)
    {
        if (stream->source.buffer.current > stream->source.buffer.end)
            return (EOF);
        else
	    return ((unsigned char) *(stream->source.buffer.current++));
    }
    return EOF;
}

/******************************************************************************
 *
 * Function _DtGrGetString
 *
 * Stream version of fgets, reads a string from a stream and advances the 
 * stream position.  If an error occurs or the end of the stream is 
 * encountered and no characters have been read, no characters are transferred
 * to the buffer and a NULL pointer is returned.  Otherwise, the buffer is
 * returned.
 *
 *****************************************************************************/
char *_DtGrGetString(
    char        *buffer,
    int         num_bytes,
    _DtGrStream *stream)
{
    char *p, *save = buffer;
    int i;

    if ((stream == NULL) || (stream->type == _DtGrNONE))
        return(NULL); /* Failure */

    if (stream->type == _DtGrFILE)
    {
        return (fgets(buffer,num_bytes,stream->source.file.fileptr));
    }
    else if (stream->type == _DtGrBUFFER)
    {
        /* This code mirrors that of fgets in libc */
        for (num_bytes--; num_bytes > 0; num_bytes -= i) 
        {
            if (stream->source.buffer.current > stream->source.buffer.end) 
            {
                if (stream->source.buffer.current == 
                    stream->source.buffer.end + 1)
                {
                    if (save == buffer)
                        return (NULL);
                    break; /* no more data */
                }
                stream->source.buffer.current--;
            }
            i = (num_bytes < stream->source.buffer.end - 
		             stream->source.buffer.current + 1 ? num_bytes :
                             stream->source.buffer.end - 
                             stream->source.buffer.current + 1);
            /* Copy the data into the buffer */
            if ((p = memccpy((void *)buffer,
			     (void *)stream->source.buffer.current,
			     (int)'\n',(size_t)i)) != NULL)
                i = p - buffer;
            buffer += i;
            stream->source.buffer.current += i;
            if (p != NULL)
                break; /* found '\n' in buffer */
        }
        *buffer = '\0';
        return (save);
    }
    return NULL;
}
