ftp.nice.ch/pub/next/unix/graphics/ImageMagick.3.8.6.s.tar.gz#/ImageMagick/magick/decode.c

This is decode.c in view mode; [Download] [Up]

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                   DDDD   EEEEE   CCCC   OOO   DDDD   EEEEE                  %
%                   D   D  E      C      O   O  D   D  E                      %
%                   D   D  EEE    C      O   O  D   D  EEE                    %
%                   D   D  E      C      O   O  D   D  E                      %
%                   DDDD   EEEEE   CCCC   OOO   DDDD   EEEEE                  %
%                                                                             %
%                                                                             %
%                    Utility Routines to Read Image Formats                   %
%                                                                             %
%                                                                             %
%                             Software Design                                 %
%                               John Cristy                                   %
%                              January 1992                                   %
%                                                                             %
%                                                                             %
%  Copyright 1997 E. I. du Pont de Nemours and Company                        %
%                                                                             %
%  Permission to use, copy, modify, distribute, and sell this software and    %
%  its documentation for any purpose is hereby granted without fee,           %
%  provided that the above Copyright notice appear in all copies and that     %
%  both that Copyright notice and this permission notice appear in            %
%  supporting documentation, and that the name of E. I. du Pont de Nemours    %
%  and Company not be used in advertising or publicity pertaining to          %
%  distribution of the software without specific, written prior               %
%  permission.  E. I. du Pont de Nemours and Company makes no representations %
%  about the suitability of this software for any purpose.  It is provided    %
%  "as is" without express or implied warranty.                               %
%                                                                             %
%  E. I. du Pont de Nemours and Company disclaims all warranties with regard  %
%  to this software, including all implied warranties of merchantability      %
%  and fitness, in no event shall E. I. du Pont de Nemours and Company be     %
%  liable for any special, indirect or consequential damages or any           %
%  damages whatsoever resulting from loss of use, data or profits, whether    %
%  in an action of contract, negligence or other tortious action, arising     %
%  out of or in connection with the use or performance of this software.      %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Functions in this library convert to and from `alien' image formats to the
%  MIFF image format.
%
%
*/

/*
  Include declarations.
*/
#include "magick.h"
#include "XWDFile.h"
#include "plug-ins.h"

/*
  Define declarations.
*/
#define LoadImageText  "  Loading image...  "
#define PrematureExit(message,image) \
{ \
  Warning(message,image->filename); \
  DestroyImages(image); \
  return((Image *) NULL); \
}

/*
  Function prototypes.
*/
static Image
  *ReadMIFFImage(const ImageInfo *),
  *ReadPNMImage(const ImageInfo *),
  *ReadPSImage(const ImageInfo *),
  *ReadXCImage(const ImageInfo *);

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d A V S I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadAVSImage reads a AVS X image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadAVSImage routine is:
%
%      image=ReadAVSImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadAVSImage returns a pointer to the image after
%      reading. A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadAVSImage(const ImageInfo *image_info)
{
  Image
    *image;

  Quantum
    blue,
    green,
    red;

  register int
    x,
    y;

  register RunlengthPacket
    *q;

  unsigned int
    packets;

  unsigned long
    height,
    width;

  unsigned short
    index;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read AVS image.
  */
  width=MSBFirstReadLong(image->file);
  height=MSBFirstReadLong(image->file);
  if ((width == (unsigned long) ~0) || (height == (unsigned long) ~0))
    PrematureExit("Not a AVS image file",image);
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=width;
    image->rows=height;
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert AVS raster image to runlength-encoded packets.
    */
    q=image->pixels;
    q->length=MaxRunlength;
    for (y=0; y < image->rows; y++)
    {
      for (x=0; x < image->columns; x++)
      {
        index=UpScale(fgetc(image->file));
        image->matte|=index != Transparent;
        red=UpScale(fgetc(image->file));
        green=UpScale(fgetc(image->file));
        blue=UpScale(fgetc(image->file));
        if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
            (index == q->index) && ((int) q->length < MaxRunlength))
          q->length++;
        else
          {
            if (image->packets != 0)
              q++;
            image->packets++;
            if (image->packets == packets)
              {
                packets<<=1;
                image->pixels=(RunlengthPacket *) realloc((char *)
                  image->pixels,packets*sizeof(RunlengthPacket));
                if (image->pixels == (RunlengthPacket *) NULL)
                  PrematureExit("Unable to allocate memory",image);
                q=image->pixels+image->packets-1;
              }
            q->red=red;
            q->green=green;
            q->blue=blue;
            q->index=index;
            q->length=0;
          }
      }
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    width=MSBFirstReadLong(image->file);
    height=MSBFirstReadLong(image->file);
    if ((width != (unsigned long) ~0) && (height != (unsigned long) ~0))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while ((width != (unsigned long) ~0) && (height != (unsigned long) ~0));
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d B M P I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadBMPImage reads a Microsoft Windows bitmap image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadBMPImage routine is:
%
%      image=ReadBMPImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadBMPImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadBMPImage(const ImageInfo *image_info)
{
  typedef struct _BMPHeader
  {
    unsigned long
      file_size;

    unsigned short
      reserved[2];

    unsigned long
      offset_bits,
      size,
      width,
      height;

    unsigned short
      planes,
      bit_count;

    unsigned long
      compression,
      image_size,
      x_pixels,
      y_pixels,
      number_colors,
      colors_important;
  } BMPHeader;

  BMPHeader
    bmp_header;

  Image
    *image;

  long
    start_position;

  register int
    bit,
    i,
    x,
    y;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *bmp_data,
    *bmp_pixels,
    magick[12];

  unsigned int
    bytes_per_line,
    image_size,
    status;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine if this is a BMP file.
  */
  status=ReadData((char *) magick,1,2,image->file);
  do
  {
    /*
      Verify BMP identifier.
    */
    start_position=ftell(image->file)-2;
    if ((status == False) || (strncmp((char *) magick,"BM",2) != 0))
      PrematureExit("Not a BMP image file",image);
    bmp_header.file_size=LSBFirstReadLong(image->file);
    bmp_header.reserved[0]=LSBFirstReadShort(image->file);
    bmp_header.reserved[1]=LSBFirstReadShort(image->file);
    bmp_header.offset_bits=LSBFirstReadLong(image->file);
    bmp_header.size=LSBFirstReadLong(image->file);
    if (bmp_header.size == 12)
      {
        /*
          OS/2 BMP image file.
        */
        bmp_header.width=(unsigned long) LSBFirstReadShort(image->file);
        bmp_header.height=(unsigned long) LSBFirstReadShort(image->file);
        bmp_header.planes=LSBFirstReadShort(image->file);
        bmp_header.bit_count=LSBFirstReadShort(image->file);
        bmp_header.x_pixels=0;
        bmp_header.y_pixels=0;
        bmp_header.number_colors=0;
        bmp_header.compression=0;
        bmp_header.image_size=0;
      }
    else
      {
        /*
          Microsoft Windows BMP image file.
        */
        bmp_header.width=LSBFirstReadLong(image->file);
        bmp_header.height=LSBFirstReadLong(image->file);
        bmp_header.planes=LSBFirstReadShort(image->file);
        bmp_header.bit_count=LSBFirstReadShort(image->file);
        bmp_header.compression=LSBFirstReadLong(image->file);
        bmp_header.image_size=LSBFirstReadLong(image->file);
        bmp_header.x_pixels=LSBFirstReadLong(image->file);
        bmp_header.y_pixels=LSBFirstReadLong(image->file);
        bmp_header.number_colors=LSBFirstReadLong(image->file);
        bmp_header.colors_important=LSBFirstReadLong(image->file);
        for (i=0; i < ((int) bmp_header.size-40); i++)
          (void) fgetc(image->file);
      }
    if (bmp_header.bit_count < 24)
      {
        unsigned char
          *bmp_colormap;

        unsigned int
          packet_size;

        /*
          Read BMP raster colormap.
        */
        image->class=PseudoClass;
        image->colors=(unsigned int) bmp_header.number_colors;
        if (image->colors == 0)
          image->colors=1 << bmp_header.bit_count;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        bmp_colormap=(unsigned char *)
          malloc(4*image->colors*sizeof(unsigned char));
        if ((image->colormap == (ColorPacket *) NULL) ||
            (bmp_colormap == (unsigned char *) NULL))
          PrematureExit("Unable to allocate memory",image);
        packet_size=4;
        if (bmp_header.size == 12)
          packet_size=3;
        (void) ReadData((char *) bmp_colormap,packet_size,image->colors,
          image->file);
        p=bmp_colormap;
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].blue=UpScale(*p++);
          image->colormap[i].green=UpScale(*p++);
          image->colormap[i].red=UpScale(*p++);
          if (bmp_header.size != 12)
            p++;
        }
        free((char *) bmp_colormap);
      }
    /*
      Read image data.
    */
    while (ftell(image->file) < (start_position+bmp_header.offset_bits))
      (void) fgetc(image->file);
    image_size=
      ((bmp_header.width*bmp_header.bit_count+31)/32)*4*bmp_header.height;
    if ((bmp_header.image_size == 0) || (bmp_header.image_size > image_size))
      bmp_header.image_size=image_size;
    bmp_data=(unsigned char *)
      malloc(bmp_header.image_size*sizeof(unsigned char));
    if (bmp_data == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    (void) ReadData((char *) bmp_data,1,(unsigned int) bmp_header.image_size,
      image->file);
    bmp_pixels=bmp_data;
    if (bmp_header.compression != 0)
      {
        unsigned int
          packets;

        /*
          Convert run-length encoded raster pixels.
        */
        packets=(unsigned int)
          (((bmp_header.width*bmp_header.bit_count+31)/32)*4*bmp_header.height);
        if (bmp_header.compression == 2)
          packets<<=1;
        bmp_pixels=(unsigned char *) malloc(packets*sizeof(unsigned char));
        if (bmp_pixels == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) BMPDecodeImage(bmp_data,bmp_pixels,
          (unsigned int) bmp_header.compression,(unsigned int) bmp_header.width,
          (unsigned int) bmp_header.height);
        if (bmp_header.compression == 2)
          bmp_header.bit_count<<=1;
        free((char *) bmp_data);
      }
    /*
      Initialize image structure.
    */
    image->columns=(unsigned int) bmp_header.width;
    image->rows=(unsigned int) bmp_header.height;
    image->units=PixelsPerCentimeterResolution;
    image->x_resolution=bmp_header.x_pixels/100.0;
    image->y_resolution=bmp_header.y_pixels/100.0;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert BMP raster image to runlength-encoded packets.
    */
    bytes_per_line=((image->columns*bmp_header.bit_count+31)/32)*4;
    switch (bmp_header.bit_count)
    {
      case 1:
      {
        /*
          Convert bitmap scanline to runlength-encoded color packets.
        */
        for (y=image->rows-1; y >= 0; y--)
        {
          p=bmp_pixels+(image->rows-y-1)*bytes_per_line;
          q=image->pixels+(y*image->columns);
          for (x=0; x < ((int) image->columns-7); x+=8)
          {
            for (bit=0; bit < 8; bit++)
            {
              q->index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
              q->length=0;
              q++;
            }
            p++;
          }
          if ((image->columns % 8) != 0)
            {
              for (bit=0; bit < (image->columns % 8); bit++)
              {
                q->index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
                q->length=0;
                q++;
              }
              p++;
            }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);
        }
        SyncImage(image);
        break;
      }
      case 4:
      {
        /*
          Convert PseudoColor scanline to runlength-encoded color packets.
        */
        for (y=image->rows-1; y >= 0; y--)
        {
          p=bmp_pixels+(image->rows-y-1)*bytes_per_line;
          q=image->pixels+(y*image->columns);
          for (x=0; x < ((int) image->columns-1); x+=2)
          {
            q->index=(*p >> 4) & 0xf;
            q->length=0;
            q++;
            q->index=(*p) & 0xf;
            q->length=0;
            p++;
            q++;
          }
          if ((image->columns % 2) != 0)
            {
              q->index=(*p >> 4) & 0xf;
              q->length=0;
              q++;
              p++;
            }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);
        }
        SyncImage(image);
        break;
      }
      case 8:
      {
        /*
          Convert PseudoColor scanline to runlength-encoded color packets.
        */
        if (bmp_header.compression == 1)
          bytes_per_line=image->columns;
        for (y=image->rows-1; y >= 0; y--)
        {
          p=bmp_pixels+(image->rows-y-1)*bytes_per_line;
          q=image->pixels+(y*image->columns);
          for (x=0; x < image->columns; x++)
          {
            q->index=(*p++);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);
        }
        SyncImage(image);
        break;
      }
      case 24:
      {
        /*
          Convert DirectColor scanline to runlength-encoded color packets.
        */
        for (y=image->rows-1; y >= 0; y--)
        {
          p=bmp_pixels+(image->rows-y-1)*bytes_per_line;
          q=image->pixels+(y*image->columns);
          for (x=0; x < image->columns; x++)
          {
            q->index=0;
            if (image->matte)
              q->index=UpScale(*p++);
            q->blue=UpScale(*p++);
            q->green=UpScale(*p++);
            q->red=UpScale(*p++);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);
        }
        break;
      }
      default:
        PrematureExit("Not a BMP image file",image);
    }
    free((char *) bmp_pixels);
    CompressImage(image);
    /*
      Proceed to next image.
    */
    status=ReadData((char *) magick,1,2,image->file);
    if ((status == True) && (strncmp((char *) magick,"BM",2) == 0))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while ((status == True) && (strncmp((char *) magick,"BM",2) == 0));
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d C G M I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadCGMImage reads a Computer Graphic Meta image file
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadCGMImage routine is:
%
%      image=ReadCGMImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadCGMImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadCGMImage(const ImageInfo *image_info)
{
  char
    filename[MaxTextExtent];

  Image
    *image,
    *next_image,
    *proxy_image;

  /*
    Allocate image structure.
  */
  proxy_image=AllocateImage(image_info);
  if (proxy_image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,proxy_image,ReadBinaryType);
  if (proxy_image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",proxy_image);
  CloseImage(proxy_image);
  DestroyImage(proxy_image);
  /*
    Use ralcgm to convert Macintosh CGM image.
  */
  (void) strcpy(filename,image_info->filename);
  (void) sprintf(image_info->filename,CGMCommand,filename);
  image=ReadPSImage(image_info);
  if (image == (Image *) NULL)
    {
      Warning("CGM translation failed",image_info->filename);
      return((Image *) NULL);
    }
  /*
    Assign proper filename.
  */
  do
  {
    (void) strcpy(image->filename,filename);
    next_image=image->next;
    if (next_image != (Image *) NULL)
      image=next_image;
  } while (next_image != (Image *) NULL);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d C M Y K I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadCMYKImage reads an image of raw cyan, magenta, yellow, and
%  black bytes and returns it.  It allocates the memory necessary for the new
%  Image structure and returns a pointer to the new image.
%
%  The format of the ReadCMYKImage routine is:
%
%      image=ReadCMYKImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadCMYKImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadCMYKImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    count,
    x,
    x_offset,
    y,
    y_offset;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *scanline;

  unsigned int
    black,
    cyan,
    height,
    magenta,
    packet_size,
    yellow,
    width;

  unsigned short
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x_offset=0;
  y_offset=0;
  x=0;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  if (image_info->interlace != PartitionInterlace)
    {
      /*
        Open image file.
      */
      OpenImage(image_info,image,ReadBinaryType);
      if (image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",image);
      for (i=0; i < x; i++)
        (void) fgetc(image->file);
    }
  /*
    Allocate memory for a scanline.
  */
  packet_size=4*(QuantumDepth >> 3);
  scanline=(unsigned char *) malloc(packet_size*width*sizeof(unsigned char));
  if (scanline == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  if (image_info->subrange != 0)
    while (image->scene < image_info->subimage)
    {
      /*
        Skip to next image.
      */
      image->scene++;
      for (y=0; y < height; y++)
        (void) ReadData((char *) scanline,packet_size,width,image->file);
    }
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=width;
    image->rows=height;
    if (image_info->subrange == 0)
      if (image_info->tile != (char *) NULL)
        (void) XParseGeometry(image_info->tile,&x_offset,&y_offset,
          &image->columns,&image->rows);
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert raster image to runlength-encoded packets.
    */
    switch (image_info->interlace)
    {
      case NoneInterlace:
      default:
      {
        /*
          No interlacing:  CMYKCMYKCMYKCMYKCMYK...
        */
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        q=image->pixels;
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            ReadQuantum(q->green,p);
            ReadQuantum(q->blue,p);
            ReadQuantum(q->index,p);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case LineInterlace:
      {
        /*
          Line interlacing:  CCC...MMM...YYY...KKK...CCC...MMM...YYY...KKK...
        */
        packet_size=image->depth >> 3;
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            q->length=0;
            q++;
          }
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->green,p);
            q++;
          }
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->blue,p);
            q++;
          }
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->index,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case PlaneInterlace:
      case PartitionInterlace:
      {
        /*
          Plane interlacing:  CCCCCC...MMMMMM...YYYYYY...KKKKKK...
        */
        if (image_info->interlace == PartitionInterlace)
          {
            AppendImageFormat("C",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        packet_size=image->depth >> 3;
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        i=0;
        q=image->pixels;
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,image->rows << 2);
        }
        if (image_info->interlace == PartitionInterlace)
          {
            CloseImage(image);
            AppendImageFormat("M",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        q=image->pixels;
        for (y=0; y < (height-image->rows); y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->green,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,image->rows << 2);
        }
        if (image_info->interlace == PartitionInterlace)
          {
            CloseImage(image);
            AppendImageFormat("Y",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        q=image->pixels;
        for (y=0; y < (height-image->rows); y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->blue,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,image->rows << 2);
        }
        if (image_info->interlace == PartitionInterlace)
          {
            CloseImage(image);
            AppendImageFormat("K",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        q=image->pixels;
        for (y=0; y < (height-image->rows); y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->index,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,image->rows << 2);
        }
        if (image_info->interlace == PartitionInterlace)
          (void) strcpy(image->filename,image_info->filename);
        break;
      }
    }
    /*
      Transform image from CMYK to RGB.
    */
    q=image->pixels;
    for (y=0; y < image->rows; y++)
    {
      for (x=0; x < image->columns; x++)
      {
        cyan=q->red;
        magenta=q->green;
        yellow=q->blue;
        black=q->index;
        if ((cyan+black) > MaxRGB)
          q->red=0;
        else
          q->red=MaxRGB-(cyan+black);
        if ((magenta+black) > MaxRGB)
          q->green=0;
        else
          q->green=MaxRGB-(magenta+black);
        if ((yellow+black) > MaxRGB)
          q->blue=0;
        else
          q->blue=MaxRGB-(yellow+black);
        q->index=0;
        q->length=0;
        q++;
      }
    }
    CompressImage(image);
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    /*
      Proceed to next image.
    */
    count=ReadData((char *) scanline,packet_size,width,image->file);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (count > 0);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  free((char *) scanline);
  CloseImage(image);
  return(image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d D P S I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadDPSImage reads a Adobe Postscript image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadDPSImage routine is:
%
%      image=ReadDPSImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadDPSImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
#ifdef HasDPS
static Image *ReadDPSImage(const ImageInfo *image_info)
{
  Display
    *display;

  float
    pixels_per_point;

  Image
    *image;

  int
    sans,
    status,
    x,
    y;

  Pixmap
    pixmap;

  register int
    i;

  register RunlengthPacket
    *p;

  register unsigned long
    pixel;

  Screen
    *screen;

  XColor
    *colors;

  XImage
    *dps_image;

  XRectangle
    bounding_box,
    pixel_size;

  XResourceInfo
    resource_info;

  XrmDatabase
    resource_database;

  XStandardColormap
    *map_info;

  XVisualInfo
    *visual_info;

  /*
    Open X server connection.
  */
  display=XOpenDisplay(image_info->server_name);
  if (display == (Display *) NULL)
    return((Image *) NULL);
  /*
    Set our forgiving error handler.
  */
  XSetErrorHandler(XError);
  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    return((Image *) NULL);
  /*
    Get user defaults from X resource database.
  */
  resource_database=XGetResourceDatabase(display,client_name);
  XGetResourceInfo(resource_database,client_name,&resource_info);
  /*
    Allocate standard colormap.
  */
  map_info=XAllocStandardColormap();
  if (map_info == (XStandardColormap *) NULL)
    Warning("Unable to create standard colormap","Memory allocation failed");
  else
    {
      /*
        Initialize visual info.
      */
      resource_info.visual_type="default";
      visual_info=XBestVisualInfo(display,map_info,&resource_info);
      map_info->colormap=(Colormap) NULL;
    }
  if ((map_info == (XStandardColormap *) NULL) ||
      (visual_info == (XVisualInfo *) NULL))
    {
      DestroyImage(image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  /*
    Create a pixmap the appropriate size for the image.
  */
  screen=ScreenOfDisplay(display,visual_info->screen);
  pixels_per_point=XDPSPixelsPerPoint(screen);
  if (image_info->density != (char *) NULL)
    {
      float
        x_resolution,
        y_resolution;

      int
        count;

      x_resolution=72.0*XDPSPixelsPerPoint(screen);
      count=sscanf(image_info->density,"%fx%f",&x_resolution,&y_resolution);
      if (count != 2)
        y_resolution=x_resolution;
      pixels_per_point=Min(x_resolution,y_resolution)/72.0;
    }
  status=XDPSCreatePixmapForEPSF((DPSContext) NULL,screen,image->file,
    visual_info->depth,pixels_per_point,&pixmap,&pixel_size,&bounding_box);
  if ((status == dps_status_failure) || (status == dps_status_no_extension))
    {
      DestroyImage(image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  /*
    Rasterize the file into the pixmap.
  */
  status=XDPSImageFileIntoDrawable((DPSContext) NULL,screen,pixmap,image->file,
    pixel_size.height,visual_info->depth,&bounding_box,-bounding_box.x,
    -bounding_box.y,pixels_per_point,True,False,True,&sans);
  if (status != dps_status_success)
    {
      DestroyImage(image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  /*
    Initialize DPS X image.
  */
  dps_image=XGetImage(display,pixmap,0,0,pixel_size.width,pixel_size.height,
    AllPlanes,ZPixmap);
  XFreePixmap(display,pixmap);
  if (dps_image == (XImage *) NULL)
    {
      DestroyImage(image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  /*
    Get the colormap colors.
  */
  colors=(XColor *) malloc(visual_info->colormap_size*sizeof(XColor));
  if (colors == (XColor *) NULL)
    {
      DestroyImage(image);
      XDestroyImage(dps_image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  if ((visual_info->class != DirectColor) &&
      (visual_info->class != TrueColor))
    for (i=0; i < visual_info->colormap_size; i++)
    {
      colors[i].pixel=i;
      colors[i].pad=0;
    }
  else
    {
      unsigned long
        blue,
        blue_bit,
        green,
        green_bit,
        red,
        red_bit;

      /*
        DirectColor or TrueColor visual.
      */
      red=0;
      green=0;
      blue=0;
      red_bit=visual_info->red_mask & (~(visual_info->red_mask)+1);
      green_bit=visual_info->green_mask & (~(visual_info->green_mask)+1);
      blue_bit=visual_info->blue_mask & (~(visual_info->blue_mask)+1);
      for (i=0; i < visual_info->colormap_size; i++)
      {
        colors[i].pixel=red | green | blue;
        colors[i].pad=0;
        red+=red_bit;
        if (red > visual_info->red_mask)
          red=0;
        green+=green_bit;
        if (green > visual_info->green_mask)
          green=0;
        blue+=blue_bit;
        if (blue > visual_info->blue_mask)
          blue=0;
      }
    }
  XQueryColors(display,XDefaultColormap(display,visual_info->screen),colors,
    visual_info->colormap_size);
  /*
    Convert X image to MIFF format.
  */
  if ((visual_info->class != TrueColor) &&
      (visual_info->class != DirectColor))
    image->class=PseudoClass;
  image->columns=dps_image->width;
  image->rows=dps_image->height;
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    {
      DestroyImage(image);
      free((char *) colors);
      XDestroyImage(dps_image);
      XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
        (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
      return((Image *) NULL);
    }
  p=image->pixels;
  switch (image->class)
  {
    case DirectClass:
    default:
    {
      register unsigned long
        color,
        index;

      unsigned long
        blue_mask,
        blue_shift,
        green_mask,
        green_shift,
        red_mask,
        red_shift;

      /*
        Determine shift and mask for red, green, and blue.
      */
      red_mask=visual_info->red_mask;
      red_shift=0;
      while ((red_mask & 0x01) == 0)
      {
        red_mask>>=1;
        red_shift++;
      }
      green_mask=visual_info->green_mask;
      green_shift=0;
      while ((green_mask & 0x01) == 0)
      {
        green_mask>>=1;
        green_shift++;
      }
      blue_mask=visual_info->blue_mask;
      blue_shift=0;
      while ((blue_mask & 0x01) == 0)
      {
        blue_mask>>=1;
        blue_shift++;
      }
      /*
        Convert X image to DirectClass packets.
      */
      if ((visual_info->colormap_size > 0) &&
          (visual_info->class == DirectColor))
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            pixel=XGetPixel(dps_image,x,y);
            index=(pixel >> red_shift) & red_mask;
            p->red=XDownScale(colors[index].red);
            index=(pixel >> green_shift) & green_mask;
            p->green=XDownScale(colors[index].green);
            index=(pixel >> blue_shift) & blue_mask;
            p->blue=XDownScale(colors[index].blue);
            p->index=0;
            p->length=0;
            p++;
          }
          ProgressMonitor(LoadImageText,y,image->rows);
        }
      else
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            pixel=XGetPixel(dps_image,x,y);
            color=(pixel >> red_shift) & red_mask;
            p->red=XDownScale((color*65535L)/red_mask);
            color=(pixel >> green_shift) & green_mask;
            p->green=XDownScale((color*65535L)/green_mask);
            color=(pixel >> blue_shift) & blue_mask;
            p->blue=XDownScale((color*65535L)/blue_mask);
            p->index=0;
            p->length=0;
            p++;
          }
          ProgressMonitor(LoadImageText,y,image->rows);
        }
      break;
    }
    case PseudoClass:
    {
      /*
        Create colormap.
      */
      image->colors=visual_info->colormap_size;
      image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
      if (image->colormap == (ColorPacket *) NULL)
        {
          DestroyImage(image);
          free((char *) colors);
          XDestroyImage(dps_image);
          XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
            (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
          return((Image *) NULL);
        }
      for (i=0; i < image->colors; i++)
      {
        image->colormap[colors[i].pixel].red=XDownScale(colors[i].red);
        image->colormap[colors[i].pixel].green=XDownScale(colors[i].green);
        image->colormap[colors[i].pixel].blue=XDownScale(colors[i].blue);
      }
      /*
        Convert X image to PseudoClass packets.
      */
      for (y=0; y < image->rows; y++)
      {
        for (x=0; x < image->columns; x++)
        {
          p->index=(unsigned short) XGetPixel(dps_image,x,y);
          p->length=0;
          p++;
        }
        ProgressMonitor(LoadImageText,y,image->rows);
      }
      SyncImage(image);
      break;
    }
  }
  if (image->class == PseudoClass)
    CompressColormap(image);
  free((char *) colors);
  XDestroyImage(dps_image);
  /*
    Rasterize matte image.
  */
  status=XDPSCreatePixmapForEPSF((DPSContext) NULL,screen,image->file,1,
    pixels_per_point,&pixmap,&pixel_size,&bounding_box);
  if ((status != dps_status_failure) && (status != dps_status_no_extension))
    {
      status=XDPSImageFileIntoDrawable((DPSContext) NULL,screen,pixmap,
        image->file,pixel_size.height,1,&bounding_box,-bounding_box.x,
        -bounding_box.y,pixels_per_point,True,True,True,&sans);
      if (status == dps_status_success)
        {
          XImage
            *matte_image;

          /*
            Initialize image matte.
          */
          matte_image=XGetImage(display,pixmap,0,0,pixel_size.width,
            pixel_size.height,AllPlanes,ZPixmap);
          XFreePixmap(display,pixmap);
          if (matte_image != (XImage *) NULL)
            {
              image->class=DirectClass;
              image->matte=True;
              p=image->pixels;
              for (y=0; y < image->rows; y++)
                for (x=0; x < image->columns; x++)
                {
                  p->index=Opaque;
                  if (!XGetPixel(matte_image,x,y))
                    p->index=Transparent;
                  p++;
                }
              XDestroyImage(matte_image);
            }
        }
    }
  /*
    Free resources.
  */
  XFreeResources(display,visual_info,map_info,(XPixelInfo *) NULL,
    (XFontStruct *) NULL,&resource_info,(XWindowInfo *) NULL);
  CompressImage(image);
  CloseImage(image);
  return(image);
}
#else
static Image *ReadDPSImage(const ImageInfo *image_info)
{
  Warning("Cannot read DPS images",image_info->filename);
  return((Image *) NULL);
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d F A X I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadFAXImage reads a Group 3 FAX image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadFAXImage routine is:
%
%      image=ReadFAXImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadFAXImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadFAXImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    x,
    y;

  unsigned int
    height,
    status,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=2592;
  height=3508;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  /*
    Initialize image structure.
  */
  image->class=PseudoClass;
  image->columns=width;
  image->rows=height;
  image->packets=Max((image->columns*image->rows+8) >> 4,1);
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  image->colors=2;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (image->colormap == (ColorPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Monochrome colormap.
  */
  image->colormap[0].red=MaxRGB;
  image->colormap[0].green=MaxRGB;
  image->colormap[0].blue=MaxRGB;
  image->colormap[1].red=0;
  image->colormap[1].green=0;
  image->colormap[1].blue=0;
  status=HuffmanDecodeImage(image);
  if (status == False)
    PrematureExit("Unable to read image data",image);
  CloseImage(image);
  TransformImage(&image,"0x0",(char *) NULL);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d F I T S I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadFITSImage reads a FITS image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadFITSImage routine is:
%
%      image=ReadFITSImage(image_info)
%
%  A description of each parameter follows:
%
%    o image: Function ReadFITSImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o filename: Specifies the name of the image to read.
%
%
*/
static Image *ReadFITSImage(const ImageInfo *image_info)
{
  typedef struct _FITSHeader
  {
    unsigned int
      simple;

    int
      bits_per_pixel;

    unsigned int
      number_of_axis,
      columns,
      rows,
      depth;

    double
      min_data,
      max_data,
      zero,
      scale;
  } FITSHeader;

  char
    long_quantum[8],
    keyword[MaxTextExtent],
    value[MaxTextExtent];

  double
    pixel,
    scale,
    scaled_pixel;

  FITSHeader
    fits_header;

  Image
    *image;

  int
    j,
    packet_size,
    y;

  long
    count,
    quantum;

  register int
    c,
    i,
    x;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *fits_pixels;

  unsigned int
    status,
    value_expected;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Initialize image header.
  */
  fits_header.simple=False;
  fits_header.bits_per_pixel=8;
  fits_header.columns=1;
  fits_header.rows=1;
  fits_header.depth=1;
  fits_header.min_data=0.0;
  fits_header.max_data=0.0;
  fits_header.zero=0.0;
  fits_header.scale=1.0;
  /*
    Decode image header.
  */
  c=fgetc(image->file);
  count=1;
  if (c == EOF)
    {
      DestroyImage(image);
      return((Image *) NULL);
    }
  for ( ; ; )
  {
    if (!isalnum(c))
      {
        c=fgetc(image->file);
        count++;
      }
    else
      {
        register char
          *p;

        /*
          Determine a keyword and its value.
        */
        p=keyword;
        do
        {
          if ((p-keyword) < (MaxTextExtent-1))
            *p++=(char) c;
          c=fgetc(image->file);
          count++;
        } while (isalnum(c) || (c == '_'));
        *p='\0';
        if (strcmp(keyword,"END") == 0)
          break;
        value_expected=False;
        while (isspace(c) || (c == '='))
        {
          if (c == '=')
            value_expected=True;
          c=fgetc(image->file);
          count++;
        }
        if (value_expected == False)
          continue;
        p=value;
        while (isalnum(c) || (c == '-') || (c == '+') || (c == '.'))
        {
          if ((p-value) < (MaxTextExtent-1))
            *p++=(char) c;
          c=fgetc(image->file);
          count++;
        }
        *p='\0';
        /*
          Assign a value to the specified keyword.
        */
        if (strcmp(keyword,"SIMPLE") == 0)
          fits_header.simple=(*value == 'T') || (*value == 't');
        if (strcmp(keyword,"BITPIX") == 0)
          fits_header.bits_per_pixel=(unsigned int) atoi(value);
        if (strcmp(keyword,"NAXIS") == 0)
          fits_header.number_of_axis=(unsigned int) atoi(value);
        if (strcmp(keyword,"NAXIS1") == 0)
          fits_header.columns=(unsigned int) atoi(value);
        if (strcmp(keyword,"NAXIS2") == 0)
          fits_header.rows=(unsigned int) atoi(value);
        if (strcmp(keyword,"NAXIS3") == 0)
          fits_header.depth=(unsigned int) atoi(value);
        if (strcmp(keyword,"DATAMAX") == 0)
          fits_header.max_data=atof(value);
        if (strcmp(keyword,"DATAMIN") == 0)
          fits_header.min_data=atof(value);
        if (strcmp(keyword,"BZERO") == 0)
          fits_header.zero=atof(value);
        if (strcmp(keyword,"BSCALE") == 0)
          fits_header.scale=atof(value);
      }
    while (isspace(c))
    {
      c=fgetc(image->file);
      count++;
    }
  }
  while (count > 2880)
    count-=2880;
  for ( ; count < 2880; count++)
    (void) fgetc(image->file);
  /*
    Verify that required image information is defined.
  */
  if ((!fits_header.simple) || (fits_header.number_of_axis < 1) ||
      (fits_header.number_of_axis > 4) ||
      (fits_header.columns*fits_header.rows) == 0)
    PrematureExit("image type not supported",image);
  /*
    Create linear colormap.
  */
  image->columns=fits_header.columns;
  image->rows=fits_header.rows;
  image->class=PseudoClass;
  image->colors=MaxRGB+1;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if (image->colormap == (ColorPacket *) NULL)
    PrematureExit("Unable to open file",image);
  for (i=0; i < image->colors; i++)
  {
    image->colormap[i].red=(Quantum) UpScale(i);
    image->colormap[i].green=(Quantum) UpScale(i);
    image->colormap[i].blue=(Quantum) UpScale(i);
  }
  /*
    Initialize image structure.
  */
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  packet_size=fits_header.bits_per_pixel/8;
  if (packet_size < 0)
    packet_size=(-packet_size);
  fits_pixels=(unsigned char *)
    malloc(image->packets*packet_size*sizeof(unsigned char));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (fits_pixels == (unsigned char *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Convert FITS pixels to runlength-encoded packets.
  */
  status=ReadData((char *) fits_pixels,(unsigned int) packet_size,
    image->packets,image->file);
  if (status == False)
    Warning("Insufficient image data in file",image->filename);
  if ((fits_header.min_data == 0.0) && (fits_header.max_data == 0.0))
    {
      /*
        Determine minimum and maximum intensity.
      */
      p=fits_pixels;
      long_quantum[0]=(*p);
      quantum=(*p++);
      for (j=0; j < (packet_size-1); j++)
      {
        long_quantum[j+1]=(*p);
        quantum=(quantum << 8) | (*p++);
      }
      pixel=(double) quantum;
      if (fits_header.bits_per_pixel == -32)
        pixel=(double) (*((float *) &quantum));
      if (fits_header.bits_per_pixel == -64)
        pixel=(double) (*((double *) long_quantum));
      fits_header.min_data=pixel*fits_header.scale+fits_header.zero;
      fits_header.max_data=pixel*fits_header.scale+fits_header.zero;
      for (i=1; i < image->packets; i++)
      {
        long_quantum[0]=(*p);
        quantum=(*p++);
        for (j=0; j < (packet_size-1); j++)
        {
          long_quantum[j+1]=(*p);
          quantum=(quantum << 8) | (*p++);
        }
        pixel=(double) quantum;
        if (fits_header.bits_per_pixel == -32)
          pixel=(double) (*((float *) &quantum));
        if (fits_header.bits_per_pixel == -64)
          pixel=(double) (*((double *) long_quantum));
        scaled_pixel=pixel*fits_header.scale+fits_header.zero;
        if (scaled_pixel < fits_header.min_data)
          fits_header.min_data=scaled_pixel;
        if (scaled_pixel > fits_header.max_data)
          fits_header.max_data=scaled_pixel;
      }
    }
  /*
    Convert FITS pixels to runlength-encoded packets.
  */
  scale=1.0;
  if (fits_header.min_data != fits_header.max_data)
    scale=(MaxRGB+1)/(fits_header.max_data-fits_header.min_data);
  p=fits_pixels;
  q=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    for (x=0; x < image->columns; x++)
    {
      long_quantum[0]=(*p);
      quantum=(*p++);
      for (j=0; j < (packet_size-1); j++)
      {
        long_quantum[j+1]=(*p);
        quantum=(quantum << 8) | (*p++);
      }
      pixel=(double) quantum;
      if (fits_header.bits_per_pixel == -32)
        pixel=(double) (*((float *) &quantum));
      if (fits_header.bits_per_pixel == -64)
        pixel=(double) (*((double *) long_quantum));
      scaled_pixel=scale*
        (pixel*fits_header.scale+fits_header.zero-fits_header.min_data);
      while (scaled_pixel < 0)
        scaled_pixel+=(MaxRGB+1);
      while (scaled_pixel > MaxRGB)
        scaled_pixel-=(MaxRGB+1);
      q->index=(unsigned short) scaled_pixel;
      q->length=0;
      q++;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  free((char *) fits_pixels);
  SyncImage(image);
  CompressColormap(image);
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d G I F I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadGIFImage reads a Compuserve Graphics image file and returns it.
%  It allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadGIFImage routine is:
%
%      image=ReadGIFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadGIFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      an error occurs.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadGIFImage(ImageInfo *image_info)
{
#define BitSet(byte,bit)  (((byte) & (bit)) == (bit))
#define LSBFirstOrder(x,y)  (((y) << 8) | (x))

  char
    geometry[MaxTextExtent];

  Image
    *image;

  int
    status,
    x,
    y;

  RectangleInfo
    page_info;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  short int
    transparency_index;

  unsigned char
    background,
    c,
    flag,
    *global_colormap,
    header[MaxTextExtent],
    magick[12];

  unsigned int
    delay,
    dispose,
    global_colors,
    image_count,
    iterations;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine if this is a GIF file.
  */
  status=ReadData((char *) magick,1,6,image->file);
  if ((status == False) || ((strncmp((char *) magick,"GIF87",5) != 0) &&
      (strncmp((char *) magick,"GIF89",5) != 0)))
    PrematureExit("Not a GIF image file",image);
  global_colors=0;
  global_colormap=(unsigned char *) NULL;
  page_info.width=LSBFirstReadShort(image->file);
  page_info.height=LSBFirstReadShort(image->file);
  (void) ReadData((char *) &flag,1,1,image->file);
  (void) ReadData((char *) &background,1,1,image->file);
  (void) ReadData((char *) &c,1,1,image->file);  /* reserved */
  if (BitSet(flag,0x80))
    {
      /*
        Read global colormap.
      */
      global_colors=1 << ((flag & 0x07)+1);
      global_colormap=(unsigned char *)
        malloc(3*global_colors*sizeof(unsigned char));
      if (global_colormap == (unsigned char *) NULL)
        PrematureExit("Unable to read image colormap file",image);
      (void) ReadData((char *) global_colormap,3,global_colors,image->file);
    }
  delay=0;
  dispose=0;
  iterations=1;
  transparency_index=(-1);
  image_count=0;
  for ( ; ; )
  {
    status=ReadData((char *) &c,1,1,image->file);
    if (status == False)
      break;
    if (c == ';')
      break;  /* terminator */
    if (c == '!')
      {
        /*
          GIF Extension block.
        */
        status=ReadData((char *) &c,1,1,image->file);
        if (status == False)
          PrematureExit("Unable to read extention block",image);
        switch (c)
        {
          case 0xf9:
          {
            /*
              Read Graphics Control extension.
            */
            while (ReadDataBlock((char *) header,image->file) > 0);
            dispose=header[0] >> 2;
            delay=(header[2] << 8) | header[1];
            if ((header[0] & 0x01) == 1)
              transparency_index=header[3];
            break;
          }
          case 0xfe:
          {
            int
              length;

            /*
              Read Comment extension.
            */
            for ( ; ; )
            {
              length=ReadDataBlock((char *) header,image->file);
              if (length <= 0)
                break;
              if (image->comments != (char *) NULL)
                image->comments=(char *) realloc((char *) image->comments,
                  (Extent(image->comments)+length+1)*sizeof(char));
              else
                {
                  image->comments=(char *) malloc((length+1)*sizeof(char));
                  if (image->comments != (char *) NULL)
                    *image->comments='\0';
                }
              if (image->comments == (char *) NULL)
                PrematureExit("Unable to allocate memory",image);
              header[length]='\0';
              (void) strcat(image->comments,(char *) header);
            }
            break;
          }
          case 0xff:
          {
            /*
              Read Netscape Loop extension.
            */
            while (ReadDataBlock((char *) header,image->file) > 0);
            iterations=(header[2] << 8) | header[1];
            break;
          }
          default:
          {
            while (ReadDataBlock((char *) header,image->file) > 0);
            break;
          }
        }
      }
    if (c != ',')
      continue;
    if (image_count != 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
    image_count++;
    /*
      Read image attributes.
    */
    image->class=PseudoClass;
    page_info.x=LSBFirstReadShort(image->file);
    page_info.y=LSBFirstReadShort(image->file);
    image->columns=LSBFirstReadShort(image->file);
    image->rows=LSBFirstReadShort(image->file);
    image->packets=image->columns*image->rows;
    if (image->packets == 0)
      PrematureExit("image size is 0",image);
    /*
      Allocate image.
    */
    if (image->pixels != (RunlengthPacket *) NULL)
      free((char *) image->pixels);
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    (void) sprintf(geometry,"%ux%u%+d%+d",page_info.width,page_info.height,
      page_info.x,page_info.y);
    image->page=PostscriptGeometry(geometry);
    image->delay=delay;
    image->dispose=dispose;
    image->iterations=iterations;
    delay=0;
    dispose=0;
    iterations=1;
    /*
      Inititialize colormap.
    */
    (void) ReadData((char *) &flag,1,1,image->file);
    image->interlace=BitSet(flag,0x40) ? PlaneInterlace : NoneInterlace;
    image->colors=!BitSet(flag,0x80) ? global_colors : 1 << ((flag & 0x07)+1);
    image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
    if (image->colormap == (ColorPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    if (!BitSet(flag,0x80))
      {
        /*
          Use global colormap.
        */
        p=global_colormap;
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].red=UpScale(*p++);
          image->colormap[i].green=UpScale(*p++);
          image->colormap[i].blue=UpScale(*p++);
        }
        image->background_color=image->colormap[background];
      }
    else
      {
        unsigned char
          *colormap;

        /*
          Read local colormap.
        */
        colormap=(unsigned char *)
          malloc(3*image->colors*sizeof(unsigned char));
        if (colormap == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData((char *) colormap,3,image->colors,image->file);
        p=colormap;
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].red=UpScale(*p++);
          image->colormap[i].green=UpScale(*p++);
          image->colormap[i].blue=UpScale(*p++);
        }
        free((char *) colormap);
      }
    /*
      Decode image.
    */
    status=GIFDecodeImage(image);
    if (image->interlace != NoneInterlace)
      {
        Image
          *interlaced_image;

        int
          pass;

        register RunlengthPacket
          *p;

        static int
          interlace_rate[4] = { 8, 8, 4, 2 },
          interlace_start[4] = { 0, 4, 2, 1 };

        /*
          Interlace image.
        */
        image_info->interlace=LineInterlace;
        image->orphan=True;
        interlaced_image=CloneImage(image,image->columns,image->rows,True);
        image->orphan=False;
        if (interlaced_image == (Image *) NULL)
          PrematureExit("Unable to allocate memory",image);
        p=interlaced_image->pixels;
        q=image->pixels;
        for (pass=0; pass < 4; pass++)
        {
          y=interlace_start[pass];
          while (y < image->rows)
          {
            q=image->pixels+(y*image->columns);
            for (x=0; x < image->columns; x++)
            {
              *q=(*p);
              p++;
              q++;
            }
            y+=interlace_rate[pass];
          }
        }
        DestroyImage(interlaced_image);
      }
    if (transparency_index >= 0)
      {
        /*
          Create matte channel.
        */
        q=image->pixels;
        for (i=0; i < image->packets; i++)
        {
          if (q->index != (unsigned short) transparency_index)
            q->index=Opaque;
          else
            q->index=Transparent;
          q++;
        }
        transparency_index=(-1);
        image->class=DirectClass;
        image->matte=True;
      }
    if (status == False)
      {
        Warning("Corrupt GIF image",image->filename);
        break;
      }
    CompressImage(image);
    if (image_info->subrange != 0)
      if ((image->scene+1) < image_info->subimage)
        {
          Image
            subimage;

          /*
            Destroy image.
          */
          subimage=(*image);
          image->file=(FILE *) NULL;
          DestroyImage(image);
          image=AllocateImage(image_info);
          if (image == (Image *) NULL)
            return((Image *) NULL);
          image->file=subimage.file;
          image->scene=subimage.scene+1;
          image_count=0;
        }
      else
        if ((image->scene+1) > (image_info->subimage+image_info->subrange-1))
          break;
  }
  if (global_colormap != (unsigned char *) NULL)
    free((char *) global_colormap);
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Corrupt GIF image or subimage not found",image);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d G R A D A T I O N I m a g e                                        %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadGRADATIONImage creates a gradation image and initializes it to
%  the X server color range as specified by the filename.  It allocates the
%  memory necessary for the new Image structure and returns a pointer to the
%  new image.
%
%  The format of the ReadGRADATIONImage routine is:
%
%      image=ReadGRADATIONImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadGRADATIONImage returns a pointer to the image after
%      creating it. A null image is returned if there is a a memory shortage
%      or if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadGRADATIONImage(const ImageInfo *image_info)
{
  char
    colorname[MaxTextExtent];

  double
    brightness,
    brightness_step,
    hue,
    hue_step,
    saturation,
    saturation_step;

  Image
    *image;

  int
    x,
    y;

  register RunlengthPacket
    *q;

  unsigned int
    height,
    width;

  XColor
    color;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  /*
    Initialize Image structure.
  */
  (void) strcpy(image->filename,image_info->filename);
  image->columns=width;
  image->rows=height;
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Determine (Hue, Saturation, Brightness) gradient.
  */
  (void) strcpy(colorname,image_info->filename);
  (void) sscanf(image_info->filename,"%[^-]",colorname);
  (void) XQueryColorDatabase(colorname,&color);
  TransformHSL(XDownScale(color.red),XDownScale(color.green),
    XDownScale(color.blue),&hue,&saturation,&brightness);
  (void) strcpy(colorname,"white");
  if (Intensity(color) > 32767)
    (void) strcpy(colorname,"black");
  (void) sscanf(image_info->filename,"%*[^-]-%s",colorname);
  (void) XQueryColorDatabase(colorname,&color);
  TransformHSL(XDownScale(color.red),XDownScale(color.green),
    XDownScale(color.blue),&hue_step,&saturation_step,&brightness_step);
  hue_step=(hue_step-hue)/(double) image->packets;
  saturation_step=(saturation_step-saturation)/(double) image->packets;
  brightness_step=(brightness_step-brightness)/(double) image->packets;
  /*
    Initialize image pixels.
  */
  q=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    for (x=0; x < image->columns; x++)
    {
      HSLTransform(hue,saturation,brightness,&q->red,&q->green,&q->blue);
      q->index=0;
      q->length=0;
      q++;
      hue+=hue_step;
      saturation+=saturation_step;
      brightness+=brightness_step;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  CompressImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d G R A Y I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadGRAYImage reads an image of raw grayscale bytes and returns it.
%  It allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadGRAYImage routine is:
%
%      image=ReadGRAYImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadGRAYImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadGRAYImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    count,
    x,
    x_offset,
    y,
    y_offset;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *scanline;

  unsigned int
    height,
    packet_size,
    packets,
    width;

  unsigned short
    index,
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x_offset=0;
  y_offset=0;
  if (image_info->size != (char *) NULL)
    {
      x=0;
      (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
      for (i=0; i < x; i++)
        (void) fgetc(image->file);
    }
  /*
    Allocate memory for a scanline.
  */
  packet_size=image->depth >> 3;
  scanline=(unsigned char *) malloc(packet_size*width*sizeof(unsigned char));
  if (scanline == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  if (image_info->subrange != 0)
    while (image->scene < image_info->subimage)
    {
      /*
        Skip to next image.
      */
      image->scene++;
      for (y=0; y < height; y++)
        (void) ReadData((char *) scanline,packet_size,width,image->file);
    }
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=width;
    image->rows=height;
    if (image_info->subrange == 0)
      if (image_info->tile != (char *) NULL)
        (void) XParseGeometry(image_info->tile,&x_offset,&y_offset,
          &image->columns,&image->rows);
    image->packets=0;
    packets=Max((image->columns*image->rows+2) >> 2,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Create linear colormap.
    */
    image->class=PseudoClass;
    image->colors=1 << image->depth;
    image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
    if (image->colormap == (ColorPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    for (i=0; i < image->colors; i++)
    {
      image->colormap[i].red=(Quantum) i;
      image->colormap[i].green=(Quantum) i;
      image->colormap[i].blue=(Quantum) i;
    }
    /*
      Convert raster image to runlength-encoded packets.
    */
    for (y=0; y < y_offset; y++)
      (void) ReadData((char *) scanline,packet_size,width,image->file);
    q=image->pixels;
    q->length=MaxRunlength;
    for (y=0; y < image->rows; y++)
    {
      if ((y > 0) || (image->previous == (Image *) NULL))
        (void) ReadData((char *) scanline,packet_size,width,image->file);
      p=scanline+packet_size*x_offset;
      for (x=0; x < image->columns; x++)
      {
        ReadQuantum(index,p);
        if ((index == q->index) && ((int) q->length < MaxRunlength))
          q->length++;
        else
          {
            if (image->packets != 0)
              q++;
            image->packets++;
            if (image->packets == packets)
              {
                packets<<=1;
                image->pixels=(RunlengthPacket *) realloc((char *)
                  image->pixels,packets*sizeof(RunlengthPacket));
                if (image->pixels == (RunlengthPacket *) NULL)
                  {
                    free((char *) scanline);
                    PrematureExit("Unable to allocate memory",image);
                  }
                q=image->pixels+image->packets-1;
              }
            q->index=index;
            q->length=0;
          }
      }
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    SyncImage(image);
    CompressColormap(image);
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    /*
      Proceed to next image.
    */
    count=ReadData((char *) scanline,packet_size,width,image->file);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (count > 0);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  free((char *) scanline);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d H D F I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadHDFImage reads a Hierarchical Data Format image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadHDFImage routine is:
%
%      image=ReadHDFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadHDFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
#ifdef HasHDF
static Image *ReadHDFImage(ImageInfo *image_info)
{
#include "hdf.h"
#undef BSD
#undef LOCAL

  ClassType
    class;

  Image
    *image;

  int
    interlace,
    is_palette,
    status,
    y;

  int32
    height,
    length,
    width;

  register int
    i,
    x;

  register unsigned char
    *p;

  register RunlengthPacket
    *q;

  uint16
    reference;

  unsigned char
    *hdf_pixels;

  unsigned int
    packet_size;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  CloseImage(image);
  /*
    Read HDF image.
  */
  class=DirectClass;
  status=DF24getdims(image->filename,&width,&height,&interlace);
  if (status == -1)
    {
      class=PseudoClass;
      status=DFR8getdims(image->filename,&width,&height,&is_palette);
    }
  if (status == -1)
    PrematureExit("Image file or does not contain any image data",image);
  do
  {
    /*
      Initialize image structure.
    */
    image->class=class;
    image->columns=width;
    image->rows=height;
    image->packets=image->columns*image->rows;
    packet_size=1;
    if (image->class == DirectClass)
      packet_size=3;
    hdf_pixels=(unsigned char *)
      malloc(packet_size*image->packets*sizeof(unsigned char));
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if ((hdf_pixels == (unsigned char *) NULL) ||
        (image->pixels == (RunlengthPacket *) NULL))
      PrematureExit("Unable to allocate memory",image);
    q=image->pixels;
    if (image->class == PseudoClass)
      {
        unsigned char
          *hdf_palette;

        /*
          Create colormap.
        */
        hdf_palette=(unsigned char *) malloc(768*sizeof(unsigned char));
        image->colors=256;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if ((hdf_palette == (unsigned char *) NULL) ||
            (image->colormap == (ColorPacket *) NULL))
          PrematureExit("Unable to allocate memory",image);
        (void) DFR8getimage(image->filename,hdf_pixels,(int) image->columns,
          (int) image->rows,hdf_palette);
        reference=DFR8lastref();
        /*
          Convert HDF raster image to PseudoClass runlength-encoded packets.
        */
        p=hdf_palette;
        if (is_palette)
          for (i=0; i < 256; i++)
          {
            image->colormap[i].red=UpScale(*p++);
            image->colormap[i].green=UpScale(*p++);
            image->colormap[i].blue=UpScale(*p++);
          }
        else
          for (i=0; i < image->colors; i++)
          {
            image->colormap[i].red=(Quantum) UpScale(i);
            image->colormap[i].green=(Quantum) UpScale(i);
            image->colormap[i].blue=(Quantum) UpScale(i);
          }
        free((char *) hdf_palette);
        p=hdf_pixels;
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            q->index=(*p++);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        SyncImage(image);
      }
    else
      {
        int
          y;

        /*
          Convert HDF raster image to DirectClass runlength-encoded packets.
        */
        (void) DF24getimage(image->filename,(void *) hdf_pixels,image->columns,
          image->rows);
        reference=DF24lastref();
        p=hdf_pixels;
        image->interlace=interlace ? PlaneInterlace : NoneInterlace;
        q=image->pixels;
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            q->red=UpScale(*p++);
            q->green=UpScale(*p++);
            q->blue=UpScale(*p++);
            q->index=0;
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      }
    length=DFANgetlablen(image->filename,DFTAG_RIG,reference);
    if (length > 0)
      {
        /*
          Read the image label.
        */
        length+=MaxTextExtent;
        image->label=(char *) malloc(length*sizeof(char));
        if (image->label != (char *) NULL)
          DFANgetlabel(image->filename,DFTAG_RIG,reference,image->label,length);
      }
    length=DFANgetdesclen(image->filename,DFTAG_RIG,reference);
    if (length > 0)
      {
        /*
          Read the image comments.
        */
        length+=MaxTextExtent;
        image->comments=(char *) malloc(length*sizeof(char));
        if (image->comments != (char *) NULL)
          DFANgetdesc(image->filename,DFTAG_RIG,reference,image->comments,
            length);
      }
    free((char *) hdf_pixels);
    CompressImage(image);
    class=DirectClass;
    status=DF24getdims(image->filename,&width,&height,&interlace);
    if (status == -1)
      {
        class=PseudoClass;
        status=DFR8getdims(image->filename,&width,&height,&is_palette);
      }
    if (status != -1)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (status != -1);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}
#else
static Image *ReadHDFImage(const ImageInfo *image_info)
{
  Warning("HDF library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d H I S T O G R A M I m a g e                                        %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadHISTOGRAMImage reads a HISTOGRAM image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadHISTOGRAMImage routine is:
%
%      image=ReadHISTOGRAMImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadHISTOGRAMImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadHISTOGRAMImage(const ImageInfo *image_info)
{
  Image
    *image;

  image=ReadMIFFImage(image_info);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d H T M L I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadHTMLImage reads a HTML image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadHTMLImage routine is:
%
%      image=ReadHTMLImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadHTMLImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadHTMLImage(const ImageInfo *image_info)
{
  Image
    *image;

  Warning("Cannot read HTML images",image_info->filename);
  image=ReadMIFFImage(image_info);
  return(image);
}

#ifdef HasJBIG
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d J B I G I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadJBIGImage reads a JBIG image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadJBIGImage routine is:
%
%      image=ReadJBIGImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadJBIGImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadJBIGImage(const ImageInfo *image_info)
{
#define MaxBufferSize  8192

  Image
    *image;

  int
    status,
    x,
    y;

  long
    length;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  register unsigned short
    index;

  size_t
    count;

  struct jbg_dec_state
    jbig_info;

  unsigned char
    bit,
    buffer[MaxBufferSize];

  unsigned int
    byte,
    height,
    packets,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine maximum width and height, e.g. 640x512.
  */
  width=65535L;
  height=65535L;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  /*
    Read JBIG file.
  */
  jbg_dec_init(&jbig_info);
  jbg_dec_maxsize(&jbig_info,(unsigned long) width,(unsigned long) height);
  status=JBG_EAGAIN;
  do
  {
    length=(long) fread(buffer,1,MaxBufferSize,image->file);
    if (length == 0)
      break;
    p=buffer;
    count=0;
    while ((length > 0) && ((status == JBG_EAGAIN) || (status == JBG_EOK)))
    {
      status=jbg_dec_in(&jbig_info,p,length,&count);
      p+=count;
      length-=count;
    }
  } while ((status == JBG_EAGAIN) || (status == JBG_EOK));
  /*
    Initialize image structure.
  */
  image->columns=(unsigned int) jbg_dec_getwidth(&jbig_info);
  image->rows=(unsigned int) jbg_dec_getheight(&jbig_info);
  image->packets=0;
  packets=Max((image->columns*image->rows+8) >> 4,1);
  image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Create colormap.
  */
  image->class=PseudoClass;
  image->colors=2;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if (image->colormap == (ColorPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  image->colormap[0].red=0;
  image->colormap[0].green=0;
  image->colormap[0].blue=0;
  image->colormap[1].red=MaxRGB;
  image->colormap[1].green=MaxRGB;
  image->colormap[1].blue=MaxRGB;
  image->x_resolution=300;
  image->y_resolution=300;
  /*
    Convert X bitmap image to runlength-encoded packets.
  */
  byte=0;
  p=jbg_dec_getimage(&jbig_info,0);
  q=image->pixels;
  q->length=MaxRunlength;
  for (y=0; y < image->rows; y++)
  {
    bit=0;
    for (x=0; x < image->columns; x++)
    {
      if (bit == 0)
        byte=(*p++);
      index=(byte & 0x80) ? 0 : 1;
      if ((index == q->index) && ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (image->packets != 0)
            q++;
          image->packets++;
          if (image->packets == packets)
            {
              packets<<=1;
              image->pixels=(RunlengthPacket *) realloc((char *) image->pixels,
                packets*sizeof(RunlengthPacket));
              if (image->pixels == (RunlengthPacket *) NULL)
                {
                  jbg_dec_free(&jbig_info);
                  PrematureExit("Unable to allocate memory",image);
                }
              q=image->pixels+image->packets-1;
            }
          q->index=index;
          q->length=0;
        }
      bit++;
      byte<<=1;
      if (bit == 8)
        bit=0;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  jbg_dec_free(&jbig_info);
  SyncImage(image);
  image->pixels=(RunlengthPacket *)
    realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
  CloseImage(image);
  return(image);
}
#else
static Image *ReadJBIGImage(const ImageInfo *image_info)
{
  Warning("JBIG library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

#ifdef HasJPEG
static Image
  *image;

static jmp_buf
  error_recovery;

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d J P E G I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadJPEGImage reads a JPEG image file and returns it.  It allocates
%  the memory necessary for the new Image structure and returns a pointer to
%  the new image.
%
%  The format of the ReadJPEGImage routine is:
%
%      image=ReadJPEGImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadJPEGImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o filename:  Specifies the name of the jpeg image to read.
%
%
*/

static unsigned int GetCharacter(j_decompress_ptr jpeg_info)
{
  struct jpeg_source_mgr
    *data;

  data=jpeg_info->src;
  if (data->bytes_in_buffer == 0)
    (*data->fill_input_buffer) (jpeg_info);
  data->bytes_in_buffer--;
  return(GETJOCTET(*data->next_input_byte++));
}

static boolean CommentHandler(j_decompress_ptr jpeg_info)
{
  long int
    length;

  register char
    *p;

  /*
    Determine length of comment.
  */
  length=GetCharacter(jpeg_info) << 8;
  length+=GetCharacter(jpeg_info);
  length-=2;
  if (image->comments != (char *) NULL)
    image->comments=(char *) realloc((char *) image->comments,
      (unsigned int) (Extent(image->comments)+length+1)*sizeof(char));
  else
    {
      image->comments=(char *)
        malloc((unsigned int) (length+1)*sizeof(char));
      if (image->comments != (char *) NULL)
        *image->comments='\0';
    }
  if (image->comments == (char *) NULL)
    {
      Warning("Memory allocation error",(char *) NULL);
      return(False);
    }
  /*
    Read comment.
  */
  p=image->comments+Extent(image->comments);
  while (--length >= 0)
    *p++=GetCharacter(jpeg_info);
  *p='\0';
  return(True);
}

static void EmitMessage(j_common_ptr jpeg_info,int level)
{
  char
    message[JMSG_LENGTH_MAX];

  struct jpeg_error_mgr
    *jpeg_error;

  jpeg_error=jpeg_info->err;
  (jpeg_error->format_message) (jpeg_info,message);
  if (level < 0)
    {
      if ((jpeg_error->num_warnings == 0) || (jpeg_error->trace_level >= 3))
        Warning((char *) message,image->filename);
      jpeg_error->num_warnings++;
    }
  else
    if (jpeg_error->trace_level >= level)
      Warning((char *) message,image->filename);
}

static void ErrorExit(j_common_ptr jpeg_info)
{
  EmitMessage(jpeg_info,0);
  longjmp(error_recovery,1);
}

static Image *ReadJPEGImage(const ImageInfo *image_info)
{
  int
    x,
    y;

  JSAMPLE
    *jpeg_pixels;

  JSAMPROW
    scanline[1];

  Quantum
    blue,
    green,
    red;

  register int
    i;

  register JSAMPLE
    *p;

  register RunlengthPacket
    *q;

  struct jpeg_decompress_struct
    jpeg_info;

  struct jpeg_error_mgr
    jpeg_error;

  unsigned int
    packets;

  unsigned short
    index;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Initialize image structure.
  */
  jpeg_info.err=jpeg_std_error(&jpeg_error);
  jpeg_info.err->emit_message=EmitMessage;
  jpeg_info.err->error_exit=ErrorExit;
  image->pixels=(RunlengthPacket *) NULL;
  jpeg_pixels=(JSAMPLE *) NULL;
  if (setjmp(error_recovery))
    {
      /*
        JPEG image is corrupt.
      */
      if (jpeg_pixels != (JSAMPLE *) NULL)
        {
          free((char *) jpeg_pixels);
          jpeg_destroy_decompress(&jpeg_info);
        }
      DestroyImage(image);
      return((Image *) NULL);
    }
  jpeg_create_decompress(&jpeg_info);
  jpeg_set_marker_processor(&jpeg_info,JPEG_COM,CommentHandler);
  jpeg_stdio_src(&jpeg_info,image->file);
  (void) jpeg_read_header(&jpeg_info,True);
  if (jpeg_info.saw_JFIF_marker)
    {
      /*
        Set image resolution.
      */
      image->x_resolution=jpeg_info.X_density;
      image->y_resolution=jpeg_info.Y_density;
      if (jpeg_info.density_unit == 1)
        image->units=PixelsPerInchResolution;
      if (jpeg_info.density_unit == 2)
        image->units=PixelsPerCentimeterResolution;
    }
  if (image_info->size != (char *) NULL)
    {
      unsigned int
        height,
        width;

      unsigned long
        scale_factor;

      /*
        Let the JPEG library subsample for us.
      */
      jpeg_calc_output_dimensions(&jpeg_info);
      image->magick_columns=jpeg_info.output_width;
      image->magick_rows=jpeg_info.output_height;
      width=jpeg_info.output_width;
      height=jpeg_info.output_height;
      (void) ParseImageGeometry(image_info->size,&x,&y,&width,&height);
      if (width == 0)
        width=1;
      scale_factor=UpShift(jpeg_info.output_width)/width;
      if (height == 0)
        height=1;
      if (scale_factor > (UpShift(jpeg_info.output_height)/height))
        scale_factor=UpShift(jpeg_info.output_height)/height;
      jpeg_info.scale_denom=DownShift(scale_factor);
      jpeg_calc_output_dimensions(&jpeg_info);
    }
#if (JPEG_LIB_VERSION >= 61)
  jpeg_info.dct_method=JDCT_FLOAT;
  image->interlace=jpeg_info.progressive_mode ? PlaneInterlace : NoneInterlace;
#endif
  jpeg_start_decompress(&jpeg_info);
  image->columns=jpeg_info.output_width;
  image->rows=jpeg_info.output_height;
  image->packets=0;
  packets=Max((image->columns*image->rows+2) >> 2,1);
  image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
  jpeg_pixels=(JSAMPLE *)
    malloc(jpeg_info.output_components*image->columns*sizeof(JSAMPLE));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (jpeg_pixels == (JSAMPLE *) NULL))
    PrematureExit("Unable to allocate memory",image);
  if (jpeg_info.out_color_space == JCS_GRAYSCALE)
    {
      /*
        Initialize grayscale colormap.
      */
      image->class=PseudoClass;
      image->colors=256;
      image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
      if (image->colormap == (ColorPacket *) NULL)
        PrematureExit("Unable to allocate memory",image);
      for (i=0; i < image->colors; i++)
      {
        image->colormap[i].red=UpScale(i);
        image->colormap[i].green=UpScale(i);
        image->colormap[i].blue=UpScale(i);
      }
    }
  /*
    Convert JPEG pixels to runlength-encoded packets.
  */
  red=0;
  green=0;
  blue=0;
  index=0;
  scanline[0]=(JSAMPROW) jpeg_pixels;
  q=image->pixels;
  q->length=MaxRunlength;
  for (y=0; y < image->rows; y++)
  {
    (void) jpeg_read_scanlines(&jpeg_info,scanline,1);
    p=jpeg_pixels;
    for (x=0; x < image->columns; x++)
    {
      if (jpeg_info.data_precision > QuantumDepth)
        {
          if (jpeg_info.out_color_space == JCS_GRAYSCALE)
            index=GETJSAMPLE(*p++) >> 4;
          else
            {
              red=(Quantum) (GETJSAMPLE(*p++) >> 4);
              green=(Quantum) (GETJSAMPLE(*p++) >> 4);
              blue=(Quantum) (GETJSAMPLE(*p++) >> 4);
              if (jpeg_info.out_color_space == JCS_CMYK)
                index=(Quantum) (GETJSAMPLE(*p++) >> 4);
            }
         }
       else
         if (jpeg_info.out_color_space == JCS_GRAYSCALE)
           index=GETJSAMPLE(*p++);
         else
           {
             red=(Quantum) UpScale(GETJSAMPLE(*p++));
             green=(Quantum) UpScale(GETJSAMPLE(*p++));
             blue=(Quantum) UpScale(GETJSAMPLE(*p++));
             if (jpeg_info.out_color_space == JCS_CMYK)
               index=(Quantum) UpScale(GETJSAMPLE(*p++));
           }
      if (jpeg_info.out_color_space == JCS_CMYK)
        {
          index=MAXJSAMPLE-index;
          if ((int) (red-index) < 0)
            red=0;
          else
            red-=index;
          if ((int) (green-index) < 0)
            green=0;
          else
            green-=index;
          if ((int) (blue-index) < 0)
            blue=0;
          else
            blue-=index;
          index=0;
        }
      if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
          (index == q->index) && ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (image->packets != 0)
            q++;
          image->packets++;
          if (image->packets == packets)
            {
              packets<<=1;
              image->pixels=(RunlengthPacket *)
                realloc((char *) image->pixels,packets*sizeof(RunlengthPacket));
              if (image->pixels == (RunlengthPacket *) NULL)
                {
                  free((char *) jpeg_pixels);
                  jpeg_destroy_decompress(&jpeg_info);
                  PrematureExit("Unable to allocate memory",image);
                }
              q=image->pixels+image->packets-1;
            }
          q->red=red;
          q->green=green;
          q->blue=blue;
          q->index=index;
          q->length=0;
        }
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  image->pixels=(RunlengthPacket *)
    realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
  if (image->class == PseudoClass)
    {
      SyncImage(image);
      CompressColormap(image);
    }
  /*
    Free memory.
  */
  free((char *) jpeg_pixels);
  (void) jpeg_finish_decompress(&jpeg_info);
  jpeg_destroy_decompress(&jpeg_info);
  CloseImage(image);
  return(image);
}
#else
static Image *ReadJPEGImage(const ImageInfo *image_info)
{
  Warning("JPEG library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d L O G O I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadLOGOImage reads a LOGO image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadLOGOImage routine is:
%
%      image=ReadLOGOImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadLOGOImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadLOGOImage(ImageInfo *image_info)
{
#include "logo.h"

  char
    *filename,
    logo_filename[MaxTextExtent];

  FILE
    *file;

  Image
    *image;

  register int
    i;

  register unsigned char
    *p;

  unsigned int
    extent;

  /*
    Open temporary output file.
  */
  TemporaryFilename(logo_filename);
  file=fopen(logo_filename,WriteBinaryType);
  if (file == (FILE *) NULL)
    {
      Warning("Unable to write file",logo_filename);
      return(ReadXCImage(image_info));
    }
  p=LogoImage;
  extent=LogoImageExtent;
  if (strcmp(image_info->magick,"GRANITE") == 0)
    {
      p=GraniteImage;
      extent=GraniteImageExtent;
    }
  if (strcmp(image_info->magick,"NETSCAPE") == 0)
    {
      p=NetscapeImage;
      extent=NetscapeImageExtent;
    }
  for (i=0; i < extent; i++)
  {
    (void) fputc((char) *p,file);
    p++;
  }
  if (ferror(file))
    {
      Warning("An error has occurred writing to file",logo_filename);
      (void) fclose(file);
      (void) remove(logo_filename);
      return(ReadXCImage(image_info));
    }
  (void) fclose(file);
  filename=image_info->filename;
  image_info->filename=logo_filename;
  image=ReadGIFImage(image_info);
  image_info->filename=filename;
  if (image != (Image *) NULL)
    (void) strcpy(image->filename,image_info->filename);
  (void) remove(logo_filename);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d M A P I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMAPImage reads an image of raw RGB colormap and colormap index
%  bytes and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadMAPImage routine is:
%
%      image=ReadMAPImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadMAPImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMAPImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    colors;

  register int
    i;

  register unsigned char
    *p;

  unsigned char
    *colormap;

  unsigned int
    height,
    packet_size,
    status,
    width;

  unsigned short
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine width, height, and number of colors, e.g. 640x512+256.
  */
  width=512;
  height=512;
  colors=256;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&colors,&colors,&width,&height);
  /*
    Initialize image structure.
  */
  image->class=PseudoClass;
  image->compression=NoCompression;
  image->columns=width;
  image->rows=height;
  image->colors=colors;
  image->packets=image->columns*image->rows;
  packet_size=3*(image->depth >> 3);
  colormap=(unsigned char *)
    malloc(packet_size*image->colors*sizeof(unsigned char));
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  image->packed_pixels=(unsigned char *)
    malloc(image->packets*packet_size*(image->depth >> 3));
  if ((colormap == (unsigned char *) NULL) ||
      (image->colormap == (ColorPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Read image colormap.
  */
  (void) ReadData((char *) colormap,1,image->colors*packet_size,image->file);
  p=colormap;
  for (i=0; i < image->colors; i++)
  {
    ReadQuantum(image->colormap[i].red,p);
    ReadQuantum(image->colormap[i].green,p);
    ReadQuantum(image->colormap[i].blue,p);
  }
  free((char *) colormap);
  /*
    Convert raster image to runlength-encoded packets.
  */
  packet_size=1;
  if (image->colors > 256)
    packet_size++;
  if (image->packed_pixels != (unsigned char *) NULL)
    free((char *) image->packed_pixels);
  image->packed_pixels=(unsigned char *)
    malloc(image->packets*packet_size);
  if (image->packed_pixels == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  (void) ReadData((char *) image->packed_pixels,packet_size,image->packets,
    image->file);
  status=RunlengthDecodeImage(image);
  if (status == False)
    {
      DestroyImages(image);
      return((Image *) NULL);
    }
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d M A T T E I m a g e                                                %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMATTEImage reads an image of raw matte bytes and returns it.
%  It allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadMATTEImage routine is:
%
%      image=ReadMATTEImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadMATTEImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMATTEImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    x,
    x_offset,
    y,
    y_offset;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *scanline;

  unsigned int
    height,
    packet_size,
    packets,
    width;

  unsigned short
    index,
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x_offset=0;
  y_offset=0;
  if (image_info->size != (char *) NULL)
    {
      x=0;
      (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
      for (i=0; i < x; i++)
        (void) fgetc(image->file);
    }
  /*
    Initialize image structure.
  */
  image->matte=True;
  image->columns=width;
  image->rows=height;
  packet_size=image->depth >> 3;
  scanline=(unsigned char *)
    malloc(packet_size*image->columns*sizeof(unsigned char));
  if (image_info->tile != (char *) NULL)
    (void) XParseGeometry(image_info->tile,&x_offset,&y_offset,&image->columns,
      &image->rows);
  image->packets=0;
  packets=Max((image->columns*image->rows+2) >> 2,1);
  image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
  if ((scanline == (unsigned char *) NULL) ||
      (image->pixels == (RunlengthPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Convert raster image to runlength-encoded packets.
  */
  for (y=0; y < y_offset; y++)
    (void) ReadData((char *) scanline,packet_size,width,image->file);
  q=image->pixels;
  q->length=MaxRunlength;
  for (y=0; y < image->rows; y++)
  {
    (void) ReadData((char *) scanline,packet_size,width,image->file);
    p=scanline+packet_size*x_offset;
    for (x=0; x < image->columns; x++)
    {
      ReadQuantum(index,p);
      if ((index == q->index) && ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (image->packets != 0)
            q++;
          image->packets++;
          if (image->packets == packets)
            {
              packets<<=1;
              image->pixels=(RunlengthPacket *) realloc((char *) image->pixels,
                packets*sizeof(RunlengthPacket));
              if (image->pixels == (RunlengthPacket *) NULL)
                {
                  free((char *) scanline);
                  PrematureExit("Unable to allocate memory",image);
                }
              q=image->pixels+image->packets-1;
            }
          q->red=0;
          q->green=0;
          q->blue=0;
          q->index=index;
          q->length=0;
        }
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  image->pixels=(RunlengthPacket *)
    realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
  free((char *) scanline);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d M I F F I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMIFFImage reads a MIFF image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadMIFFImage routine is:
%
%      image=ReadMIFFImage(filename)
%
%  A description of each parameter follows:
%
%    o image: Function ReadMIFFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMIFFImage(const ImageInfo *image_info)
{
  char
    keyword[MaxTextExtent],
    value[MaxTextExtent];

  Image
    *image;

  register int
    c,
    i;

  register unsigned char
    *p;

  unsigned int
    length,
    packet_size,
    status;

  unsigned long
    count,
    packets;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  image->depth=8;
  /*
    Decode image header;  header terminates one character beyond a ':'.
  */
  c=fgetc(image->file);
  if (c == EOF)
    {
      DestroyImage(image);
      return((Image *) NULL);
    }
  do
  {
    /*
      Decode image header;  header terminates one character beyond a ':'.
    */
    image->compression=NoCompression;
    while (isgraph(c) && (c != ':'))
    {
      register char
        *p;

      if (c == '{')
        {
          /*
            Read comment-- any text between { }.
          */
          if (image->comments != (char *) NULL)
            {
              length=Extent(image->comments);
              p=image->comments+length;
            }
          else
            {
              length=MaxTextExtent;
              image->comments=(char *) malloc(length*sizeof(char));
              p=image->comments;
            }
          for ( ; image->comments != (char *) NULL; p++)
          {
            c=fgetc(image->file);
            if ((c == EOF) || (c == '}'))
              break;
            if ((p-image->comments+1) >= length)
              {
                *p='\0';
                length<<=1;
                image->comments=(char *)
                  realloc((char *) image->comments,length*sizeof(char));
                if (image->comments == (char *) NULL)
                  break;
                p=image->comments+Extent(image->comments);
              }
            *p=(unsigned char) c;
          }
          if (image->comments == (char *) NULL)
            PrematureExit("Unable to allocate memory",image);
          *p='\0';
          c=fgetc(image->file);
        }
      else
        if (isalnum(c))
          {
            /*
              Determine a keyword and its value.
            */
            p=keyword;
            do
            {
              if ((p-keyword) < (MaxTextExtent-1))
                *p++=(char) c;
              c=fgetc(image->file);
            } while (isalnum(c));
            *p='\0';
            while (isspace(c) || (c == '='))
              c=fgetc(image->file);
            p=value;
            if (c != '"')
              while (!isspace(c) && (c != EOF))
              {
                if ((p-value) < (MaxTextExtent-1))
                  *p++=(char) c;
                c=fgetc(image->file);
              }
            else
              {
                c=fgetc(image->file);
                while ((c != '"') && (c != EOF))
                {
                  if ((p-value) < (MaxTextExtent-1))
                    *p++=(char) c;
                  c=fgetc(image->file);
                }
              }
            *p='\0';
            /*
              Assign a value to the specified keyword.
            */
            if (strcmp(keyword,"class") == 0)
              if (strcmp(value,"PseudoClass") == 0)
                image->class=PseudoClass;
              else
                if (strcmp(value,"DirectClass") == 0)
                  image->class=DirectClass;
                else
                  image->class=UndefinedClass;
            if (strcmp(keyword,"colors") == 0)
              image->colors=(unsigned int) atoi(value);
            if (strcmp(keyword,"compression") == 0)
              if ((strcmp(value,"Zip") == 0) || (strcmp(value,"Zlib") == 0))
                image->compression=ZipCompression;
              else
                if (strcmp(value,"RunlengthEncoded") == 0)
                  image->compression=RunlengthEncodedCompression;
                else
                  image->compression=UndefinedCompression;
            if (strcmp(keyword,"columns") == 0)
              image->columns=(unsigned int) atoi(value);
            if (strcmp(keyword,"delay") == 0)
              image->delay=atoi(value);
            if (strcmp(keyword,"depth") == 0)
              image->depth=atoi(value) <= 8 ? 8 : 16;
            if (strcmp(keyword,"gamma") == 0)
              image->gamma=atof(value);
            if (strcmp(keyword,"id") == 0)
              if (strcmp(value,"ImageMagick") == 0)
                image->id=ImageMagickId;
              else
                image->id=UndefinedId;
            if (strcmp(keyword,"label") == 0)
              {
                image->label=(char *) malloc(Extent(value)+1*sizeof(char));
                if (image->label == (char *) NULL)
                  PrematureExit("Unable to allocate memory",image);
                (void) strcpy(image->label,value);
              }
            if ((strcmp(keyword,"matte") == 0) ||
                (strcmp(keyword,"alpha") == 0))
              if ((strcmp(value,"True") == 0) || (strcmp(value,"true") == 0))
                image->matte=True;
              else
                image->matte=False;
            if (strcmp(keyword,"montage") == 0)
              {
                image->montage=(char *) malloc(Extent(value)+1*sizeof(char));
                if (image->montage == (char *) NULL)
                  PrematureExit("Unable to allocate memory",image);
                (void) strcpy(image->montage,value);
              }
            if (strcmp(keyword,"packets") == 0)
              image->packets=(unsigned int) atoi(value);
            if (strcmp(keyword,"rows") == 0)
              image->rows=(unsigned int) atoi(value);
            if (strcmp(keyword,"scene") == 0)
              image->scene=(unsigned int) atoi(value);
            if (strcmp(keyword,"signature") == 0)
              {
                image->signature=(char *)
                  malloc((Extent(value)+1)*sizeof(char));
                if (image->signature == (char *) NULL)
                  PrematureExit("Unable to allocate memory",image);
                (void) strcpy(image->signature,value);
              }
          }
        else
          c=fgetc(image->file);
      while (isspace(c))
        c=fgetc(image->file);
    }
    (void) fgetc(image->file);
    /*
      Verify that required image information is defined.
    */
    if ((image->id == UndefinedId) || (image->class == UndefinedClass) ||
        (image->compression == UndefinedCompression) || (image->columns == 0) ||
        (image->rows == 0))
      PrematureExit("Incorrect image header in file",image);
    if (image->montage != (char *) NULL)
      {
        register char
          *p;

        /*
          Image directory.
        */
        image->directory=(char *) malloc(MaxTextExtent*sizeof(char));
        if (image->directory == (char *) NULL)
          PrematureExit("Unable to read image data",image);
        p=image->directory;
        do
        {
          *p='\0';
          if (((Extent(image->directory)+1) % MaxTextExtent) == 0)
            {
              /*
                Allocate more memory for the image directory.
              */
              image->directory=(char *) realloc((char *) image->directory,
                (Extent(image->directory)+MaxTextExtent+1)*sizeof(char));
              if (image->directory == (char *) NULL)
                PrematureExit("Unable to read image data",image);
              p=image->directory+Extent(image->directory);
            }
          c=fgetc(image->file);
          *p++=(unsigned char) c;
        } while (c != '\0');
      }
    if (image->class == PseudoClass)
      {
        unsigned int
          colors;

        unsigned short
          value;

        /*
          PseudoClass image cannot have matte data.
        */
        if (image->matte)
          PrematureExit("Matte images must be DirectClass",image);
        /*
          Create image colormap.
        */
        colors=image->colors;
        if (colors == 0)
          colors=256;
        image->colormap=(ColorPacket *) malloc(colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        if (image->colors == 0)
          for (i=0; i < colors; i++)
          {
            image->colormap[i].red=(Quantum) UpScale(i);
            image->colormap[i].green=(Quantum) UpScale(i);
            image->colormap[i].blue=(Quantum) UpScale(i);
            image->colors++;
          }
        else
          {
            unsigned char
              *colormap;

            /*
              Read image colormap from file.
            */
            packet_size=3*(image->depth >> 3);
            colormap=(unsigned char *)
              malloc(packet_size*image->colors*sizeof(unsigned char));
            if (colormap == (unsigned char *) NULL)
              PrematureExit("Unable to allocate memory",image);
            (void) ReadData((char *) colormap,1,packet_size*image->colors,
              image->file);
            p=colormap;
            for (i=0; i < image->colors; i++)
            {
              ReadQuantum(image->colormap[i].red,p);
              ReadQuantum(image->colormap[i].green,p);
              ReadQuantum(image->colormap[i].blue,p);
            }
            free((char *) colormap);
          }
      }
    /*
      Determine packed packet size.
    */
    if (image->class == PseudoClass)
      {
        image->packet_size=1;
        if (image->colors > 256)
          image->packet_size++;
      }
    else
      {
        image->packet_size=3*(image->depth >> 3);
        if (image->matte)
          image->packet_size++;
      }
    if (image->compression == RunlengthEncodedCompression)
      image->packet_size++;
    packet_size=image->packet_size;
    if (image->compression == ZipCompression)
      packet_size=1;
    /*
      Allocate image pixels.
    */
    if (image->compression == NoCompression)
      image->packets=image->columns*image->rows;
    packets=image->packets;
    if (image->packets == 0)
      packets=image->columns*image->rows;
    image->packed_pixels=(unsigned char *)
      malloc((unsigned int) packets*packet_size*sizeof(unsigned char));
    if (image->packed_pixels == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Read image pixels from file.
    */
    if ((image->compression != RunlengthEncodedCompression) ||
        (image->packets != 0))
      (void) ReadData((char *) image->packed_pixels,1,(unsigned int)
        packets*packet_size,image->file);
    else
      {
        /*
          Number of runlength packets is unspecified.
        */
        count=0;
        p=image->packed_pixels;
        do
        {
          (void) ReadData((char *) p,1,packet_size,image->file);
          image->packets++;
          p+=(packet_size-1);
          count+=(*p+1);
          p++;
        }
        while (count < (image->columns*image->rows));
      }
    if (image->compression ==  ZipCompression)
      {
        int
          status;

        unsigned char
          *compressed_pixels;

        /*
          Uncompress image pixels with Zip encoding.
        */
        compressed_pixels=image->packed_pixels;
        packets=image->columns*image->rows*image->packet_size;
        image->packed_pixels=(unsigned char *)
          malloc((packets+8)*sizeof(unsigned char));
        if (image->packed_pixels == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        status=True;
#ifdef HasPNG
        status=uncompress(image->packed_pixels,&packets,compressed_pixels,
          image->packets);
#endif
        image->packets=(unsigned int) (packets/image->packet_size);
        free((char *) compressed_pixels);
        if (status)
          PrematureExit("Unable to Zip uncompress image",image);
      }
    /*
      Unpack the packed image pixels into runlength-encoded pixel packets.
    */
    status=RunlengthDecodeImage(image);
    if (status == False)
      {
        DestroyImages(image);
        return((Image *) NULL);
      }
    /*
      Proceed to next image.
    */
    do
    {
      c=fgetc(image->file);
    } while (!isgraph(c) && (c != EOF));
    if (c != EOF)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (c != EOF);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d M O N O I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMONOImage reads an image of raw bites in LSB order and returns
%  it.  It allocates the memory necessary for the new Image structure and
%  returns a pointer to the new image.
%
%  The format of the ReadMONOImage routine is:
%
%      image=ReadMONOImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadMONOImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMONOImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    x,
    x_offset,
    y,
    y_offset;

  register int
    i;

  register RunlengthPacket
    *q;

  unsigned char
    bit,
    byte;

  unsigned int
    height,
    packets,
    width;

  unsigned short
    index;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x_offset=0;
  y_offset=0;
  if (image_info->size != (char *) NULL)
    {
      x=0;
      (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
      for (i=0; i < x; i++)
        (void) fgetc(image->file);
    }
  /*
    Initialize image structure.
  */
  image->columns=width;
  image->rows=height;
  if (image_info->tile != (char *) NULL)
    (void) XParseGeometry(image_info->tile,&x_offset,&y_offset,&image->columns,
      &image->rows);
  image->packets=0;
  packets=Max((image->columns*image->rows+8) >> 4,1);
  image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
  image->class=PseudoClass;
  image->colors=2;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (image->colormap == (ColorPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  for (i=0; i < image->colors; i++)
  {
    image->colormap[i].red=(MaxRGB*i)/(image->colors-1);
    image->colormap[i].green=(MaxRGB*i)/(image->colors-1);
    image->colormap[i].blue=(MaxRGB*i)/(image->colors-1);
  }
  /*
    Convert bi-level image to runlength-encoded packets.
  */
  for (y=0; y < ((y_offset*image->columns+7) >> 3); y++)
    (void) fgetc(image->file);
  byte=0;
  q=image->pixels;
  q->length=MaxRunlength;
  for (y=0; y < image->rows; y++)
  {
    bit=0;
    for (x=0; y < ((x_offset+7) >> 3); x++)
      (void) fgetc(image->file);
    for (x=0; x < image->columns; x++)
    {
      if (bit == 0)
        byte=fgetc(image->file);
      index=(byte & 0x01) ? 0 : 1;
      if ((index == q->index) && ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (image->packets != 0)
            q++;
          image->packets++;
          if (image->packets == packets)
            {
              packets<<=1;
              image->pixels=(RunlengthPacket *) realloc((char *)
                image->pixels,packets*sizeof(RunlengthPacket));
              if (image->pixels == (RunlengthPacket *) NULL)
                PrematureExit("Unable to allocate memory",image);
              q=image->pixels+image->packets-1;
            }
          q->index=index;
          q->length=0;
        }
      bit++;
      if (bit == 8)
        bit=0;
      byte>>=1;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  SyncImage(image);
  CloseImage(image);
  return(image);
}
#ifdef HasMPEG

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d M P E G I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMPEGImage reads a MPEG image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadMPEGImage routine is:
%
%      image=ReadMPEGImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadMPEGImage returns a pointer to the image after
%      reading. A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMPEGImage(const ImageInfo *image_info)
{
  Image
    *image;

  ImageDesc
    mpeg_info;

  int
    y;

  register int
    x;

  register unsigned char
    *p;

  register RunlengthPacket
    *q;

  unsigned char
    *mpeg_pixels;

  unsigned int
    status;

  unsigned long
    lsb_first;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Allocate MPEG pixels.
  */
  (void) OpenMPEG(image->file,&mpeg_info);
  mpeg_pixels=(unsigned char *) malloc(mpeg_info.Size*sizeof(unsigned char));
  if (mpeg_pixels == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Read MPEG image.
  */
  status=GetMPEGFrame((char *) mpeg_pixels);
  if (image_info->subrange != 0)
    while (image->scene < image_info->subimage)
    {
      /*
        Skip to next image.
      */
      image->scene++;
      status=GetMPEGFrame((char *) mpeg_pixels);
      if (status == False)
        break;
      ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
        (unsigned int) image->filesize);
    }
  for ( ; ; )
  {
    /*
      Initialize image structure.
    */
    image->columns=mpeg_info.Width;
    image->rows=mpeg_info.Height-8;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert MPEG raster image to runlength-encoded packets.
    */
    p=mpeg_pixels;
    q=image->pixels;
    lsb_first=1;
    if (*(char *) &lsb_first)
      for (y=0; y < image->rows; y++)
      {
        for (x=0; x < image->columns; x++)
        {
          q->red=UpScale(*p++);
          q->green=UpScale(*p++);
          q->blue=UpScale(*p++);
          q->index=0;
          q->length=0;
          p++;
          q++;
        }
      }
    else
      for (y=0; y < image->rows; y++)
      {
        for (x=0; x < image->columns; x++)
        {
          p++;
          q->blue=UpScale(*p++);
          q->green=UpScale(*p++);
          q->red=UpScale(*p++);
          q->index=0;
          q->length=0;
          q++;
        }
      }
    if (image_info->verbose)
      DescribeImage(image,stderr,False);
    CompressImage(image);
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    if (!status)
      break;
    /*
      Allocate next image structure.
    */
    status=GetMPEGFrame((char *) mpeg_pixels);
    AllocateNextImage(image_info,image);
    if (image->next == (Image *) NULL)
      {
        DestroyImages(image);
        return((Image *) NULL);
      }
    image=image->next;
    ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
      (unsigned int) image->filesize);
  }
  free((char *) mpeg_pixels);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}
#else
static Image *ReadMPEGImage(const ImageInfo *image_info)
{
  Warning("MPEG library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d M T V I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadMTVImage reads a MTV image file and returns it.  It allocates
%  the memory necessary for the new Image structure and returns a pointer to
%  the new image.
%
%  The format of the ReadMTVImage routine is:
%
%      image=ReadMTVImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadMTVImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadMTVImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    count,
    y;

  Quantum
    blue,
    green,
    red;

  register int
    x;

  register RunlengthPacket
    *q;

  unsigned int
    columns,
    packets,
    rows;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read MTV image.
  */
  count=fscanf(image->file,"%u %u\n",&columns,&rows);
  if (count == 0)
    PrematureExit("Not a MTV image file",image);
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=columns;
    image->rows=rows;
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert MTV raster image to runlength-encoded packets.
    */
    q=image->pixels;
    q->length=MaxRunlength;
    for (y=0; y < image->rows; y++)
    {
      for (x=0; x < image->columns; x++)
      {
        red=UpScale(fgetc(image->file));
        green=UpScale(fgetc(image->file));
        blue=UpScale(fgetc(image->file));
        if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
            ((int) q->length < MaxRunlength))
          q->length++;
        else
          {
            if (image->packets != 0)
              q++;
            image->packets++;
            if (image->packets == packets)
              {
                packets<<=1;
                image->pixels=(RunlengthPacket *) realloc((char *)
                  image->pixels,packets*sizeof(RunlengthPacket));
                if (image->pixels == (RunlengthPacket *) NULL)
                  PrematureExit("Unable to allocate memory",image);
                q=image->pixels+image->packets-1;
              }
            q->red=red;
            q->green=green;
            q->blue=blue;
            q->index=0;
            q->length=0;
          }
      }
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    /*
      Proceed to next image.
    */
    count=fscanf(image->file,"%u %u\n",&columns,&rows);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (count > 0);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d N U L L I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadNULLImage reads a NULL image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadNULLImage routine is:
%
%      image=ReadNULLImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadNULLImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadNULLImage(const ImageInfo *image_info)
{
  return(ReadXCImage(image_info));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P C D I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPCDImage reads a Photo CD image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.  Much of the PCD decoder was derived from
%  the program hpcdtoppm(1) by Hadmut Danisch.
%
%  The format of the ReadPCDImage routine is:
%
%      image=ReadPCDImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPCDImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *OverviewImage(const ImageInfo *image_info,Image *image)
{
  char
    *commands[3],
    *resource_value;

  Display
    *display;

  Image
    *montage_image;

  ImageInfo
    local_info;

  XMontageInfo
    montage_info;

  XResourceInfo
    resource_info;

  XrmDatabase
    resource_database;

  /*
    Get user defaults from X resource database.
  */
  XGetMontageInfo(&montage_info);
  display=XOpenDisplay(image_info->server_name);
  if (display != (Display *) NULL)
    XSetErrorHandler(XError);
  resource_database=XGetResourceDatabase(display,client_name);
  XGetResourceInfo(resource_database,client_name,&resource_info);
  resource_info.background_color=XGetResourceInstance(resource_database,
    client_name,"background",DefaultTileBackground);
  resource_info.foreground_color=XGetResourceInstance(resource_database,
    client_name,"foreground",DefaultTileForeground);
  montage_info.frame=XGetResourceClass(resource_database,client_name,"frame",
    (char *) NULL);
  resource_info.image_geometry=XGetResourceInstance(resource_database,
    client_name,"imageGeometry",DefaultTileGeometry);
  resource_info.matte_color=XGetResourceInstance(resource_database,client_name,
    "mattecolor",DefaultTileMatte);
  resource_value=XGetResourceClass(resource_database,client_name,"pointsize",
    DefaultPointSize);
  montage_info.pointsize=atoi(resource_value);
  resource_value=
    XGetResourceClass(resource_database,client_name,"shadow","True");
  montage_info.shadow=IsTrue(resource_value);
  montage_info.texture=XGetResourceClass(resource_database,client_name,
    "texture","granite:");
  montage_info.tile=XGetResourceClass(resource_database,client_name,"tile",
    montage_info.tile);
  if (display != (Display *) NULL)
    XCloseDisplay(display);
  /*
    Create image tiles.
  */
  local_info=(*image_info);
  commands[0]=client_name;
  commands[1]="-label";
  commands[2]=DefaultTileLabel;
  MogrifyImages(&local_info,3,commands,&image);
  /*
    Create the PCD Overview image.
  */
  (void) strcpy(montage_info.filename,image_info->filename);
  montage_image=XMontageImages(&resource_info,&montage_info,image);
  if (montage_image == (Image *) NULL)
    PrematureExit("Unable to allocate memory",image);
  DestroyImage(image);
  CompressImage(montage_image);
  return(montage_image);
}

static Image *ReadPCDImage(const ImageInfo *image_info)
{
  Image
    *image;

  long int
    offset;

  register int
    i;

  register RunlengthPacket
    *p;

  register unsigned char
    *c1,
    *c2,
    *y;

  unsigned char
    *chroma1,
    *chroma2,
    *header,
    *luma;

  unsigned int
    height,
    number_images,
    overview,
    rotate,
    status,
    subimage,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine if this is a PCD file.
  */
  header=(unsigned char *) malloc(3*0x800*sizeof(unsigned char));
  if (header == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  status=ReadData((char *) header,1,3*0x800,image->file);
  overview=strncmp((char *) header,"PCD_OPA",7) == 0;
  if ((status == False) ||
      ((strncmp((char *) header+0x800,"PCD",3) != 0) && !overview))
    PrematureExit("Not a PCD image file",image);
  rotate=header[0x0e02] & 0x03;
  number_images=(header[10] << 8) | header[11];
  free((char *) header);
  subimage=3;
  if (image_info->subrange != 0)
    subimage=image_info->subimage;
  if (image_info->size != (char *) NULL)
    {
      int
        x,
        y;

      /*
        Determine which image size to extract.
      */
      width=768;
      height=512;
      (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
      for (subimage=1; subimage <= 6; subimage++)
      {
        if ((width <= 192) && (height <= 128))
          break;
        width>>=1;
        height>>=1;
      }
    }
  if (overview)
    subimage=1;
  /*
    Initialize image structure.
  */
  width=192;
  height=128;
  for (i=1; i < Min(subimage,3); i++)
  {
    width<<=1;
    height<<=1;
  }
  image->columns=width;
  image->rows=height;
  for ( ; i < subimage; i++)
  {
    image->columns<<=1;
    image->rows<<=1;
  }
  /*
    Allocate luma and chroma memory.
  */
  image->packets=image->columns*image->rows;
  chroma1=(unsigned char *) malloc((image->packets+1)*sizeof(unsigned char));
  chroma2=(unsigned char *) malloc((image->packets+1)*sizeof(unsigned char));
  luma=(unsigned char *) malloc((image->packets+1)*sizeof(unsigned char));
  if ((chroma1 == (unsigned char *) NULL) ||
      (chroma2 == (unsigned char *) NULL) || (luma == (unsigned char *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Advance to image data.
  */
  offset=93;
  if (overview)
    offset=2;
  else
    if (subimage == 2)
      offset=20;
    else
      if (subimage == 1)
        offset=1;
  for (i=0; i < (offset*0x800); i++)
    (void) fgetc(image->file);
  if (overview)
    {
      Image
        *overview_image;

      MonitorHandler
        handler;

      register int
        j;

      /*
        Read thumbnails from overview image.
      */
      for (j=1; j <= number_images; j++)
      {
        handler=SetMonitorHandler((MonitorHandler) NULL);
        (void) sprintf(image->filename,"images/img%04d.pcd",j);
        (void) sprintf(image->magick_filename,"images/img%04d.pcd",j);
        image->scene=j;
        image->columns=width;
        image->rows=height;
        image->packets=image->columns*image->rows;
        image->pixels=(RunlengthPacket *)
          malloc(image->packets*sizeof(RunlengthPacket));
        if (image->pixels == (RunlengthPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        y=luma;
        c1=chroma1;
        c2=chroma2;
        for (i=0; i < height; i+=2)
        {
          (void) ReadData((char *) y,1,width,image->file);
          y+=image->columns;
          (void) ReadData((char *) y,1,width,image->file);
          y+=image->columns;
          (void) ReadData((char *) c1,1,width >> 1,image->file);
          c1+=image->columns;
          (void) ReadData((char *) c2,1,width >> 1,image->file);
          c2+=image->columns;
        }
        Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
        Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
        /*
          Transfer luminance and chrominance channels.
        */
        p=image->pixels;
        y=luma;
        c1=chroma1;
        c2=chroma2;
        for (i=0; i < image->packets; i++)
        {
          p->red=UpScale(*y++);
          p->green=UpScale(*c1++);
          p->blue=UpScale(*c2++);
          p->index=0;
          p->length=0;
          p++;
        }
        TransformRGBImage(image,YCCColorspace);
        CompressImage(image);
        if (j < number_images)
          {
            /*
              Allocate next image structure.
            */
            AllocateNextImage(image_info,image);
            if (image->next == (Image *) NULL)
              {
                DestroyImages(image);
                return((Image *) NULL);
              }
            image=image->next;
          }
        (void) SetMonitorHandler(handler);
        ProgressMonitor(LoadImageText,j-1,number_images);
      }
      free(chroma2);
      free(chroma1);
      free(luma);
      while (image->previous != (Image *) NULL)
        image=image->previous;
      overview_image=OverviewImage(image_info,image);
      return(overview_image);
    }
  /*
    Allocate image pixels.
  */
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Read interleaved image.
  */
  y=luma;
  c1=chroma1;
  c2=chroma2;
  for (i=0; i < height; i+=2)
  {
    (void) ReadData((char *) y,1,width,image->file);
    y+=image->columns;
    (void) ReadData((char *) y,1,width,image->file);
    y+=image->columns;
    (void) ReadData((char *) c1,1,width >> 1,image->file);
    c1+=image->columns;
    (void) ReadData((char *) c2,1,width >> 1,image->file);
    c2+=image->columns;
  }
  if (subimage >= 4)
    {
      /*
        Recover luminance deltas for 1536x1024 image.
      */
      Upsample(768,512,image->columns,luma);
      Upsample(384,256,image->columns,chroma1);
      Upsample(384,256,image->columns,chroma2);
      image->rows=1024;
      for (i=0; i < (4*0x800); i++)
        (void) fgetc(image->file);
      status=PCDDecodeImage(image,luma,chroma1,chroma2);
      if ((subimage >= 5) && status)
        {
          /*
            Recover luminance deltas for 3072x2048 image.
          */
          Upsample(1536,1024,image->columns,luma);
          Upsample(768,512,image->columns,chroma1);
          Upsample(768,512,image->columns,chroma2);
          image->rows=2048;
          offset=ftell(image->file)/0x800+12;
          (void) fseek(image->file,offset*0x800,SEEK_SET);
          status=PCDDecodeImage(image,luma,chroma1,chroma2);
          if ((subimage >= 6) && status)
            {
              /*
                Recover luminance deltas for 6144x4096 image (vaporware).
              */
              Upsample(3072,2048,image->columns,luma);
              Upsample(1536,1024,image->columns,chroma1);
              Upsample(1536,1024,image->columns,chroma2);
              image->rows=4096;
            }
        }
    }
  Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
  Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
  /*
    Transfer luminance and chrominance channels.
  */
  p=image->pixels;
  y=luma;
  c1=chroma1;
  c2=chroma2;
  for (i=0; i < image->packets; i++)
  {
    p->red=UpScale(*y++);
    p->green=UpScale(*c1++);
    p->blue=UpScale(*c2++);
    p->index=0;
    p->length=0;
    p++;
    if (QuantumTick(i,image))
      ProgressMonitor(LoadImageText,i,image->packets);
  }
  free(chroma2);
  free(chroma1);
  free(luma);
  TransformRGBImage(image,YCCColorspace);
  if ((rotate == 1) || (rotate == 3))
    {
      double
        degrees;

      Image
        *rotated_image;

      /*
        Rotate image.
      */
      degrees=rotate == 1 ? -90.0 : 90.0;
      image->orphan=True;
      rotated_image=RotateImage(image,degrees,False,True);
      image->orphan=False;
      if (rotated_image != (Image *) NULL)
        {
          DestroyImage(image);
          image=rotated_image;
        }
    }
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P C L I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPCLImage reads a Page Control Language image file and returns
%  it.  It allocates the memory necessary for the new Image structure and
%  returns a pointer to the new image.
%
%  The format of the ReadPCLImage routine is:
%
%      image=ReadPCLImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPCLImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPCLImage(const ImageInfo *image_info)
{
  Image
    *image;

  Warning("Cannot read PCL images",image_info->filename);
  image=ReadMIFFImage(image_info);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P C X I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPCXImage reads a ZSoft IBM PC Paintbrush file and returns it.
%  It allocates the memory necessary for the new Image structure and returns
%  a pointer to the new image.
%
%  The format of the ReadPCXImage routine is:
%
%      image=ReadPCXImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPCXImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPCXImage(const ImageInfo *image_info)
{
  typedef struct _PCXHeader
  {
    unsigned char
      identifier,
      version,
      encoding,
      bits_per_pixel;

    short int
      left,
      top,
      right,
      bottom,
      horizontal_resolution,
      vertical_resolution;

    unsigned char
      reserved,
      planes;

    short int
      bytes_per_line,
      palette_info;

    unsigned char
      colormap_signature;
  } PCXHeader;

  PCXHeader
    pcx_header;

  Image
    *image;

  int
    bits,
    count,
    id,
    mask,
    packets,
    pcx_packets;

  Quantum
    blue,
    green,
    red;

  register int
    i,
    x,
    y;

  register RunlengthPacket
    *q;

  register unsigned char
    *p,
    *r;

  unsigned char
    packet,
    *pcx_colormap,
    *pcx_pixels,
    *scanline;

  unsigned int
    status;

  unsigned long
    *page_table;

  unsigned short
    index;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine if this is a PCX file.
  */
  page_table=(unsigned long *) NULL;
  if (strcmp(image_info->magick,"DCX") == 0)
    {
      unsigned long
        magic;

      /*
        Read the DCX page table.
      */
      magic=LSBFirstReadLong(image->file);
      if (magic != 987654321)
        PrematureExit("Not a DCX image file",image);
      page_table=(unsigned long *) malloc(1024*sizeof(unsigned long));
      if (page_table == (unsigned long *) NULL)
        PrematureExit("Unable to allocate memory",image);
      for (id=0; id < 1024; id++)
      {
        page_table[id]=LSBFirstReadLong(image->file);
        if (page_table[id] == 0)
          break;
      }
    }
  if (page_table != (unsigned long *) NULL)
    (void) fseek(image->file,(long) page_table[0],SEEK_SET);
  status=ReadData((char *) &pcx_header.identifier,1,1,image->file);
  for (id=1; id < 1024; id++)
  {
    /*
      Verify PCX identifier.
    */
    (void) ReadData((char *) &pcx_header.version,1,1,image->file);
    if ((status == False) || (pcx_header.identifier != 0x0a) ||
        ((pcx_header.version != 2) && (pcx_header.version != 5)))
      PrematureExit("Not a PCX image file",image);
    (void) ReadData((char *) &pcx_header.encoding,1,1,image->file);
    (void) ReadData((char *) &pcx_header.bits_per_pixel,1,1,image->file);
    pcx_header.left=LSBFirstReadShort(image->file);
    pcx_header.top=LSBFirstReadShort(image->file);
    pcx_header.right=LSBFirstReadShort(image->file);
    pcx_header.bottom=LSBFirstReadShort(image->file);
    pcx_header.horizontal_resolution=LSBFirstReadShort(image->file);
    pcx_header.vertical_resolution=LSBFirstReadShort(image->file);
    /*
      Read PCX raster colormap.
    */
    image->columns=(pcx_header.right-pcx_header.left)+1;
    image->rows=(pcx_header.bottom-pcx_header.top)+1;
    image->units=PixelsPerInchResolution;
    image->x_resolution=pcx_header.horizontal_resolution;
    image->y_resolution=pcx_header.vertical_resolution;
    image->packets=image->columns*image->rows;
    image->colors=16;
    image->colormap=(ColorPacket *) malloc(256*sizeof(ColorPacket));
    pcx_colormap=(unsigned char *) malloc(3*256*sizeof(unsigned char));
    if ((image->colormap == (ColorPacket *) NULL) ||
        (pcx_colormap == (unsigned char *) NULL))
      PrematureExit("Unable to allocate memory",image);
    (void) ReadData((char *) pcx_colormap,3,image->colors,image->file);
    p=pcx_colormap;
    for (i=0; i < image->colors; i++)
    {
      image->colormap[i].red=UpScale(*p++);
      image->colormap[i].green=UpScale(*p++);
      image->colormap[i].blue=UpScale(*p++);
    }
    (void) ReadData((char *) &pcx_header.reserved,1,1,image->file);
    (void) ReadData((char *) &pcx_header.planes,1,1,image->file);
    if ((pcx_header.bits_per_pixel != 8) || (pcx_header.planes == 1))
      image->class=PseudoClass;
    pcx_header.bytes_per_line=LSBFirstReadShort(image->file);
    pcx_header.palette_info=LSBFirstReadShort(image->file);
    for (i=0; i < 58; i++)
      (void) fgetc(image->file);
    /*
      Read image data.
    */
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    if (pcx_header.bits_per_pixel == 1)
      packets=Max((image->columns*image->rows+8) >> 4,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    pcx_packets=image->rows*pcx_header.bytes_per_line*pcx_header.planes;
    pcx_pixels=(unsigned char *) malloc(pcx_packets*sizeof(unsigned char));
    scanline=(unsigned char *)
      malloc(image->columns*pcx_header.planes*sizeof(unsigned char));
    if ((image->pixels == (RunlengthPacket *) NULL) ||
        (pcx_pixels == (unsigned char *) NULL) ||
        (scanline == (unsigned char *) NULL))
      PrematureExit("Unable to allocate memory",image);
    /*
      Uncompress image data.
    */
    p=pcx_pixels;
    while (pcx_packets > 0)
    {
      packet=fgetc(image->file);
      if ((packet & 0xc0) != 0xc0)
        {
          *p++=packet;
          pcx_packets--;
          continue;
        }
      count=packet & 0x3f;
      for (packet=fgetc(image->file); count > 0; count--)
      {
        *p++=packet;
        pcx_packets--;
        if (pcx_packets == 0)
          break;
      }
    }
    if (image->class == DirectClass)
      image->matte=pcx_header.planes > 3;
    else
      if (pcx_header.version == 5)
        {
          /*
            Initialize image colormap.
          */
          image->colors=1 << (pcx_header.bits_per_pixel*pcx_header.planes);
          if (image->colors > 256)
            PrematureExit("PCX colormap exceeded 256 colors",image);
          if (image->colors > 16)
            {
              /*
                256 color images have their color map at the end of the file.
              */
              (void) ReadData((char *) &pcx_header.colormap_signature,1,1,
                image->file);
              (void) ReadData((char *) pcx_colormap,3,image->colors,
                image->file);
              p=pcx_colormap;
              for (i=0; i < image->colors; i++)
              {
                image->colormap[i].red=UpScale(*p++);
                image->colormap[i].green=UpScale(*p++);
                image->colormap[i].blue=UpScale(*p++);
              }
            }
          else
            if (Intensity(image->colormap[0]) == Intensity(image->colormap[1]))
              if (image->colors == 2)
                {
                  /*
                    Monochrome colormap.
                  */
                  image->colormap[0].red=MaxRGB;
                  image->colormap[0].green=MaxRGB;
                  image->colormap[0].blue=MaxRGB;
                  image->colormap[1].red=0;
                  image->colormap[1].green=0;
                  image->colormap[1].blue=0;
                }
          free((char *) pcx_colormap);
        }
    /*
      Convert PCX raster image to runlength-encoded packets.
    */
    red=0;
    green=0;
    blue=0;
    index=0;
    q=image->pixels;
    q->length=MaxRunlength;
    for (y=0; y < image->rows; y++)
    {
      p=pcx_pixels+(y*pcx_header.bytes_per_line*pcx_header.planes);
      r=scanline;
      if (image->class == DirectClass)
        for (i=0; i < (int) pcx_header.planes; i++)
        {
          r=scanline+i;
          for (x=0; x < pcx_header.bytes_per_line; x++)
          {
            switch (i)
            {
              case 0:
              {
                *r=UpScale(*p++);
                break;
              }
              case 1:
              {
                *r=UpScale(*p++);
                break;
              }
              case 2:
              {
                *r=UpScale(*p++);
                break;
              }
              case 3:
              default:
              {
                *r=UpScale(*p++);
                break;
              }
            }
            r+=pcx_header.planes;
          }
        }
      else
        if (pcx_header.planes > 1)
          {
            for (x=0; x < image->columns; x++)
              *r++=0;
            for (i=0; i < (int) pcx_header.planes; i++)
            {
              r=scanline;
              for (x=0; x < pcx_header.bytes_per_line; x++)
              {
                 bits=(*p++);
                 for (mask=0x80; mask != 0; mask>>=1)
                 {
                   if (bits & mask)
                     *r|=1 << i;
                   r++;
                 }
               }
            }
          }
        else
          switch (pcx_header.bits_per_pixel)
          {
            case 1:
            {
              register int
                bit;

              for (x=0; x < ((int) image->columns-7); x+=8)
              {
                for (bit=7; bit >= 0; bit--)
                  *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                p++;
              }
              if ((image->columns % 8) != 0)
                {
                  for (bit=7; bit >= (8-(image->columns % 8)); bit--)
                    *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                  p++;
                }
              break;
            }
            case 2:
            {
              for (x=0; x < ((int) image->columns-3); x+=4)
              {
                *r++=(*p >> 6) & 0x3;
                *r++=(*p >> 4) & 0x3;
                *r++=(*p >> 2) & 0x3;
                *r++=(*p) & 0x3;
                p++;
              }
              if ((image->columns % 4) != 0)
                {
                  for (i=3; i >= (4-(image->columns % 4)); i--)
                    *r++=(*p >> (i*2)) & 0x03;
                  p++;
                }
              break;
            }
            case 4:
            {
              for (x=0; x < ((int) image->columns-1); x+=2)
              {
                *r++=(*p >> 4) & 0xf;
                *r++=(*p) & 0xf;
                p++;
              }
              if ((image->columns % 2) != 0)
                *r++=(*p++ >> 4) & 0xf;
              break;
            }
            case 8:
            {
              for (x=0; x < image->columns; x++)
                *r++=(*p++);
              break;
            }
            default:
              break;
          }
      /*
        Transfer image scanline.
      */
      r=scanline;
      for (x=0; x < image->columns; x++)
      {
        if (image->class == PseudoClass)
          index=(*r++);
        else
          {
            red=UpScale(*r++);
            green=UpScale(*r++);
            blue=UpScale(*r++);
            if (image->matte)
              index=UpScale(*r++);
          }
        if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
            (index == q->index) && ((int) q->length < MaxRunlength))
          q->length++;
        else
          {
            if (image->packets != 0)
              q++;
            image->packets++;
            if (image->packets == packets)
              {
                packets<<=1;
                image->pixels=(RunlengthPacket *) realloc((char *)
                  image->pixels,packets*sizeof(RunlengthPacket));
                if (image->pixels == (RunlengthPacket *) NULL)
                  {
                    free((char *) scanline);
                    PrematureExit("Unable to allocate memory",image);
                  }
                q=image->pixels+image->packets-1;
              }
            q->red=red;
            q->green=green;
            q->blue=blue;
            q->index=index;
            q->length=0;
          }
      }
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    if (image->class == PseudoClass)
      {
        SyncImage(image);
        CompressColormap(image);
      }
    free((char *) scanline);
    free((char *) pcx_pixels);
    image->pixels=(RunlengthPacket *) realloc((char *) image->pixels,
      image->packets*sizeof(RunlengthPacket));
    /*
      Proceed to next image.
    */
    if (page_table == (unsigned long *) NULL)
      break;
    if (page_table[id] == 0)
      break;
    (void) fseek(image->file,(long) page_table[id],SEEK_SET);
    status=ReadData((char *) &pcx_header.identifier,1,1,image->file);
    if ((status == True) && (pcx_header.identifier == 0x0a))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  }
  if (page_table != (unsigned long *) NULL)
    free((char *) page_table);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P D F I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPDFImage reads a Portable Document Format image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadPDFImage routine is:
%
%      image=ReadPDFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPDFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPDFImage(const ImageInfo *image_info)
{
#define MediaBox  "/MediaBox ["

  char
    command[MaxTextExtent],
    density[MaxTextExtent],
    *device,
    filename[MaxTextExtent],
    options[MaxTextExtent],
    postscript_filename[MaxTextExtent];

  FILE
    *file;

  float
    dx_resolution,
    dy_resolution,
    lower_x,
    lower_y,
    upper_x,
    upper_y,
    x_resolution,
    y_resolution;

  Image
    *image,
    *next_image;

  ImageInfo
    local_info;

  int
    count,
    status;

  long int
    filesize;

  RectangleInfo
    bounding_box;

  register char
    *p;

  register int
    c,
    i;

  unsigned int
    height,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Open temporary output file.
  */
  TemporaryFilename(postscript_filename);
  file=fopen(postscript_filename,WriteBinaryType);
  if (file == (FILE *) NULL)
    PrematureExit("Unable to write file",image);
  /*
    Set the page geometry.
  */
  bounding_box.width=0;
  bounding_box.height=0;
  lower_x=0;
  lower_y=0;
  dx_resolution=72.0;
  dy_resolution=72.0;
  x_resolution=72.0;
  (void) strcpy(density,PSDensityGeometry);
  count=sscanf(density,"%fx%f",&x_resolution,&y_resolution);
  if (image_info->density != (char *) NULL)
    count=sscanf(image_info->density,"%fx%f",&x_resolution,&y_resolution);
  if (count != 2)
    y_resolution=x_resolution;
  *options='\0';
  if (image_info->page != (char *) NULL)
    {
      int
        x,
        y;

      /*
        Set page geometry.
      */
      (void) XParseGeometry(PSPageGeometry,&x,&y,&width,&height);
      (void) XParseGeometry(image_info->page,&x,&y,&width,&height);
      (void) sprintf(options,"-g%ux%u",
        (unsigned int) (((width*x_resolution)/dx_resolution)+0.5),
        (unsigned int) (((height*y_resolution)/dy_resolution)+0.5));
    }
  else
    for (p=command; ; )
    {
      c=fgetc(image->file);
      if (c == EOF)
        break;
      (void) fputc(c,file);
      *p++=c;
      if ((c != '\n') && (c != '\r') && ((p-command) < (MaxTextExtent-1)))
        continue;
      *p='\0';
      p=command;
      /*
        Continue unless this is a MediaBox statement.
      */
      if (strncmp(MediaBox,command,Extent(MediaBox)) != 0)
        continue;
      count=sscanf(command,"/MediaBox [ %f %f %f %f",&lower_x,&lower_y,
        &upper_x,&upper_y);
      if (count != 4)
        continue;
      if ((lower_x > upper_x) || (lower_y > upper_y))
        continue;
      /*
        Determine bounding box.
      */
      dx_resolution=72.0;
      dy_resolution=72.0;
      x_resolution=72.0;
      (void) strcpy(density,PSDensityGeometry);
      count=sscanf(density,"%fx%f",&x_resolution,&y_resolution);
      if (image_info->density != (char *) NULL)
        count=sscanf(image_info->density,"%fx%f",&x_resolution,&y_resolution);
      if (count != 2)
        y_resolution=x_resolution;
      if (image_info->page != (char *) NULL)
        continue;
      /*
        Set Postscript render geometry.
      */
      width=(unsigned int) (upper_x-lower_x+1);
      if ((float) ((int) upper_x) != upper_x)
        width++;
      height=(unsigned int) (upper_y-lower_y+1);
      if ((float) ((int) upper_y) != upper_y)
        height++;
      if ((width <= bounding_box.width) && (height <= bounding_box.height))
        continue;
      (void) sprintf(options,"-g%ux%u",
        (unsigned int) (((width*x_resolution)/dx_resolution)+0.5),
        (unsigned int) (((height*y_resolution)/dy_resolution)+0.5));
      bounding_box.width=width;
      bounding_box.height=height;
    }
  for ( ; ; )
  {
    c=fgetc(image->file);
    if (c == EOF)
      break;
    (void) fputc(c,file);
  }
  if (ferror(file))
    {
      Warning("An error has occurred writing to file",postscript_filename);
      (void) fclose(file);
      return((Image *) NULL);
    }
  (void) fclose(file);
  CloseImage(image);
  filesize=image->filesize;
  DestroyImage(image);
  /*
    Determine if the density options is specified.
  */
  (void) strcat(options," -r");
  if (image_info->density == (char *) NULL)
    (void) strcat(options,PSDensityGeometry);
  else
    (void) strcat(options,image_info->density);
  if (image_info->subrange != 0)
    {
      (void) sprintf(options,"%s -dFirstPage=%u",options,
        image_info->subimage+1);
      (void) sprintf(options,"%s -dLastPage=%u",options,image_info->subimage+
        image_info->subrange);
    }
  /*
    Use Ghostscript to convert Postscript image.
  */
  device=PostscriptColorDevice;
  if (image_info->monochrome)
    device=PostscriptMonoDevice;
  (void) strcpy(filename,image_info->filename);
  for (i=0; i < 50; i++)
  {
    /*
      Ghostscript eats % characters.
    */
    TemporaryFilename(image_info->filename);
    if (strchr(image_info->filename,'%') == (char *) NULL)
      break;
  }
  (void) sprintf(command,PostscriptCommand,device,options,image_info->filename,
    postscript_filename);
  status=SystemCommand(command);
  if (status)
    {
      /*
        Pre GS 3.51 does not support the pnmraw device.
      */
      (void) sprintf(command,PostscriptCommand,"ppmraw",options,
        image_info->filename,postscript_filename);
      status=SystemCommand(command);
    }
  if (status)
    {
      Warning("Portable Document translation failed",image_info->filename);
      (void) remove(postscript_filename);
      return((Image *) NULL);
    }
  local_info=(*image_info);
  image=ReadImage(&local_info);
  (void) remove(postscript_filename);
  (void) remove(image_info->filename);
  if (image == (Image *) NULL)
    {
      Warning("Portable Document translation failed",image_info->filename);
      return((Image *) NULL);
    }
  do
  {
    (void) strcpy(image->magick,"PDF");
    (void) strcpy(image->filename,filename);
    image->filesize=filesize;
    next_image=image->next;
    if (next_image != (Image *) NULL)
      image=next_image;
  } while (next_image != (Image *) NULL);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P I C T I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPICTImage reads an Apple Macintosh QuickDraw/PICT image file
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadPICTImage routine is:
%
%      image=ReadPICTImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPICTImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPICTImage(const ImageInfo *image_info)
{
  char
    filename[MaxTextExtent];

  Image
    *image,
    *next_image,
    *proxy_image;

  /*
    Allocate image structure.
  */
  proxy_image=AllocateImage(image_info);
  if (proxy_image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,proxy_image,ReadBinaryType);
  if (proxy_image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",proxy_image);
  CloseImage(proxy_image);
  DestroyImage(proxy_image);
  /*
    Use picttoppm to convert Macintosh PICT image.
  */
  (void) strcpy(filename,image_info->filename);
  (void) sprintf(image_info->filename,PICTCommand,filename);
  image=ReadPNMImage(image_info);
  if (image == (Image *) NULL)
    {
      Warning("PICT translation failed",image_info->filename);
      return((Image *) NULL);
    }
  /*
    Assign proper filename.
  */
  do
  {
    (void) strcpy(image->filename,filename);
    next_image=image->next;
    if (next_image != (Image *) NULL)
      image=next_image;
  } while (next_image != (Image *) NULL);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P L A S M A I m a g e                                              %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPLASMAImage creates a plasma fractal image.  The image is
%  initialized to to the X server color as specified by the filename.
%
%  The format of the ReadPLASMAImage routine is:
%
%      image=ReadPLASMAImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPLASMAImage returns a pointer to the image after
%      creating it. A null image is returned if there is a a memory shortage
%      or if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPLASMAImage(const ImageInfo *image_info)
{
#define PlasmaImageText  "  Applying image plasma...  "
#define PlasmaPixel(x,y) \
{ \
  p=PixelOffset(x,y); \
  p->red=(Quantum) (rand() % (MaxRGB+1)); \
  p->green=(Quantum) (rand() % (MaxRGB+1)); \
  p->blue=(Quantum) (rand() % (MaxRGB+1)); \
}

  Image
    *image;

  register int
    i;

  SegmentInfo
    segment_info;

  unsigned int
    depth,
    max_depth;

  /*
    Recursively apply plasma to the image.
  */
  image=ReadGRADATIONImage(image_info);
  if (image == (Image *) NULL)
    return(image);
  if (!UncompressImage(image))
    return(image);
  image->class=DirectClass;
  for (i=0; i < image->packets; i++)
    image->pixels[i].index=(Opaque-Transparent) >> 1;
  segment_info.x1=0;
  segment_info.y1=0;
  segment_info.x2=image->columns-1;
  segment_info.y2=image->rows-1;
  srand(time(0));
  if (strcmp(image_info->filename,"fractal") == 0)
    {
      register RunlengthPacket
        *p;

      /*
        Seed pixels before recursion.
      */
      PlasmaPixel(segment_info.x1,segment_info.y1);
      PlasmaPixel(segment_info.x1,(segment_info.y1+segment_info.y2)/2);
      PlasmaPixel(segment_info.x1,segment_info.y2);
      PlasmaPixel((segment_info.x1+segment_info.x2)/2,segment_info.y1);
      PlasmaPixel((segment_info.x1+segment_info.x2)/2,
        (segment_info.y1+segment_info.y2)/2);
      PlasmaPixel((segment_info.x1+segment_info.x2)/2,segment_info.y2);
      PlasmaPixel(segment_info.x2,segment_info.y1);
      PlasmaPixel(segment_info.x2,(segment_info.y1+segment_info.y2)/2);
      PlasmaPixel(segment_info.x2,segment_info.y2);
    }
  i=Max(image->columns,image->rows) >> 1;
  for (max_depth=0; i != 0; max_depth++)
    i>>=1;
  for (depth=1; ; depth++)
  {
    ProgressMonitor(PlasmaImageText,depth,max_depth);
    if (PlasmaImage(image,&segment_info,0,depth))
      break;
  }
  return(image);
}

#ifdef HasPNG
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P N G I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPNGImage reads a Portable Network Graphics image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadPNGImage routine is:
%
%      image=ReadPNGImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPNGImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/

static void PNGError(png_struct *ping,png_const_charp message)
{
  Warning(message,(char *) NULL);
  longjmp(ping->jmpbuf,1);
}

static void PNGWarning(png_struct *ping,png_const_charp message)
{
  Warning(message,(char *) NULL);
}

static Image *ReadPNGImage(const ImageInfo *image_info)
{
#define PNGTextChunk(i,value) \
{ \
  length=ping_info->text[i].text_length; \
  if (value != (char *) NULL) \
    value=(char *) realloc((char *) value,strlen(value)+length+1); \
  else \
    { \
      value=(char *) malloc(length+1); \
      if (value != (char *) NULL) \
        *value='\0'; \
    } \
  if (value == (char *) NULL) \
    PrematureExit("Unable to allocate memory",image); \
  (void) strncat(value,ping_info->text[i].text,length); \
  value[length]='\0'; \
}

  ColorPacket
    transparent_color;

  Image
    *image;

  register int
    i,
    x,
    y;

  register unsigned char
    *p;

  register RunlengthPacket
    *q;

  png_info
    *end_info,
    *ping_info;

  png_struct
    *ping;

  unsigned char
    *png_pixels,
    **scanlines;

  unsigned int
    packets,
    status;

  unsigned long
    length;

  unsigned short
    index,
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  if (strcmp(image_info->magick,"MNG") == 0)
    {
      char
        magic_number[MaxTextExtent];

      /*
        Verify MNG signature.
      */
      (void) ReadData(magic_number,1,8,image->file);
      if (strncmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
        PrematureExit("Not a MNG image file",image);
    }
  do
  {
    if (strcmp(image_info->magick,"MNG") == 0)
      {
        char
          type[MaxTextExtent];

        unsigned int
          crc;

        /*
          Read a new chunk.
        */
        length=MSBFirstReadLong(image->file);
        status=ReadData(type,1,4,image->file);
        if (status == False)
          {
            Warning("Corrupt MNG image",image->filename);
            break;
          }
        if (strncmp(type,"MEND",4) == 0)
          break;
        for (i=0; i < length; i++)
          (void) fgetc(image->file);
        crc=MSBFirstReadLong(image->file);
        if (strncmp(type,"IHDR",4) != 0)
          continue;
        (void) fseek(image->file,-(length+12),SEEK_CUR);
        if (image->pixels != (RunlengthPacket *) NULL)
          {
            /*
              Allocate next image structure.
            */
            AllocateNextImage(image_info,image);
            if (image->next == (Image *) NULL)
              {
                DestroyImages(image);
                return((Image *) NULL);
              }
            image=image->next;
            ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
              (unsigned int) image->filesize);
          }
      }
    /*
      Allocate the PNG structures
    */
    ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,(void *) NULL,PNGError,
      PNGWarning);
    if (ping == (png_struct *) NULL)
      PrematureExit("Unable to allocate memory",image);
    ping_info=png_create_info_struct(ping);
    if (ping_info == (png_info *) NULL)
      {
        png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
        PrematureExit("Unable to allocate memory",image);
      }
    end_info=png_create_info_struct(ping);
    if (end_info == (png_info *) NULL)
      {
        png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
        PrematureExit("Unable to allocate memory",image);
      }
    image->pixels=(RunlengthPacket *) NULL;
    png_pixels=(unsigned char *) NULL;
    scanlines=(unsigned char **) NULL;
    if (setjmp(ping->jmpbuf))
      {
        /*
          PNG image is corrupt.
        */
        png_destroy_read_struct(&ping,&ping_info,&end_info);
        if (scanlines != (unsigned char **) NULL)
          free((char *) scanlines);
        if (png_pixels != (unsigned char *) NULL)
          free((char *) png_pixels);
        CloseImage(image);
        if ((image->columns == 0) || (image->rows == 0))
          {
            DestroyImage(image);
            return((Image *) NULL);
          }
        return(image);
      }
    /*
      Prepare PNG for reading.
    */
    png_init_io(ping,image->file);
    if (strcmp(image_info->magick,"MNG") == 0)
      png_set_sig_bytes(ping,8);
    png_read_info(ping,ping_info);
    image->depth=ping_info->bit_depth;
    if (ping_info->bit_depth < 8)
      {
        if ((ping_info->color_type != PNG_COLOR_TYPE_PALETTE) &&
            (ping_info->color_type != PNG_COLOR_TYPE_GRAY))
          png_set_packing(ping);
        image->depth=8;
      }
    if (ping_info->valid & PNG_INFO_gAMA)
      image->gamma=ping_info->gamma;
    if (ping_info->valid & PNG_INFO_pHYs)
      {
        /*
          Set image resolution.
        */
        image->x_resolution=ping_info->x_pixels_per_unit;
        image->y_resolution=ping_info->y_pixels_per_unit;
        if (ping_info->phys_unit_type == PNG_RESOLUTION_METER)
          {
            image->units=PixelsPerCentimeterResolution;
            image->x_resolution=ping_info->x_pixels_per_unit/100.0;
            image->y_resolution=ping_info->y_pixels_per_unit/100.0;
          }
      }
    if (ping_info->valid & PNG_INFO_bKGD)
      {
        /*
          Set image background color.
        */
        image->background_color.red=ping_info->background.red;
        image->background_color.green=ping_info->background.green;
        image->background_color.blue=ping_info->background.blue;
        if (ping_info->bit_depth > QuantumDepth)
          {
            image->background_color.red=XDownScale(ping_info->background.red);
            image->background_color.green=
              XDownScale(ping_info->background.green);
            image->background_color.blue=XDownScale(ping_info->background.blue);
          }
      }
    if (ping_info->valid & PNG_INFO_tRNS)
      {
        /*
          Image has a transparent background.
        */
        transparent_color.red=ping_info->trans_values.red;
        transparent_color.green=ping_info->trans_values.green;
        transparent_color.blue=ping_info->trans_values.blue;
        transparent_color.index=ping_info->trans_values.gray;
        if (ping_info->bit_depth > QuantumDepth)
          {
            transparent_color.red=XDownScale(ping_info->trans_values.red);
            transparent_color.green=XDownScale(ping_info->trans_values.green);
            transparent_color.blue=XDownScale(ping_info->trans_values.blue);
            transparent_color.index=XDownScale(ping_info->trans_values.gray);
          }
      }
    png_read_update_info(ping,ping_info);
    /*
      Initialize image structure.
    */
    image->columns=(unsigned int) ping_info->width;
    image->rows=(unsigned int) ping_info->height;
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    if (ping_info->bit_depth == 1)
      packets=Max((image->columns*image->rows+8) >> 4,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    png_pixels=(unsigned char *)
      malloc(ping_info->rowbytes*image->rows*sizeof(Quantum));
    scanlines=(unsigned char **) malloc(image->rows*sizeof(unsigned char *));
    if ((image->pixels == (RunlengthPacket *) NULL) ||
        (png_pixels == (unsigned char *) NULL) ||
        (scanlines == (unsigned char **) NULL))
      PrematureExit("Unable to allocate memory",image);
    if ((ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
        (ping_info->color_type == PNG_COLOR_TYPE_GRAY))
      {
        /*
          Initialize image colormap.
        */
        image->class=PseudoClass;
        image->colors=1 << Min(ping_info->bit_depth,QuantumDepth);
        if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
          image->colors=ping_info->num_palette;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].red=(MaxRGB*i)/Max(image->colors-1,1);
          image->colormap[i].green=(MaxRGB*i)/Max(image->colors-1,1);
          image->colormap[i].blue=(MaxRGB*i)/Max(image->colors-1,1);
        }
        if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
          for (i=0; i < image->colors; i++)
          {
            image->colormap[i].red=UpScale(ping_info->palette[i].red);
            image->colormap[i].green=UpScale(ping_info->palette[i].green);
            image->colormap[i].blue=UpScale(ping_info->palette[i].blue);
          }
      }
    /*
      Read image scanlines.
    */
    for (i=0; i < image->rows; i++)
      scanlines[i]=png_pixels+(i*ping_info->rowbytes);
    png_read_image(ping,scanlines);
    png_read_end(ping,ping_info);
    /*
      Convert PNG pixels to runlength-encoded packets.
    */
    q=image->pixels;
    q->length=MaxRunlength;
    if (image->class == DirectClass)
      {
        Quantum
          blue,
          green,
          red;

        /*
          Convert image to DirectClass runlength-encoded packets.
        */
        if ((ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
            (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
            (ping_info->valid & PNG_INFO_tRNS))
          image->matte=True;
        for (y=0; y < image->rows; y++)
        {
          p=scanlines[y];
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(red,p);
            green=red;
            blue=red;
            if (ping_info->color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
              {
                ReadQuantum(green,p);
                ReadQuantum(blue,p);
              }
            index=Opaque;
            if ((ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
                (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
              ReadQuantum(index,p);
            if (ping_info->valid & PNG_INFO_tRNS)
              if ((red == transparent_color.red) &&
                  (green == transparent_color.green) &&
                  (blue == transparent_color.blue))
                index=Transparent;
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                (index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      {
                        free((char *) png_pixels);
                        PrematureExit("Unable to allocate memory",image);
                      }
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      }
    else
      {
        Quantum
          *quantum_scanline;

        register Quantum
          *r;

        /*
          Convert image to PseudoClass runlength-encoded packets.
        */
        quantum_scanline=(Quantum *) malloc(image->columns*sizeof(Quantum));
        if (quantum_scanline == (Quantum *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (y=0; y < image->rows; y++)
        {
          p=scanlines[y];
          r=quantum_scanline;
          switch (ping_info->bit_depth)
          {
            case 1:
            {
              register int
                bit;

              for (x=0; x < ((int) image->columns-7); x+=8)
              {
                for (bit=7; bit >= 0; bit--)
                  *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                p++;
              }
              if ((image->columns % 8) != 0)
                {
                  for (bit=7; bit >= (8-(image->columns % 8)); bit--)
                    *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                  p++;
                }
              break;
            }
            case 2:
            {
              for (x=0; x < ((int) image->columns-3); x+=4)
              {
                *r++=(*p >> 6) & 0x3;
                *r++=(*p >> 4) & 0x3;
                *r++=(*p >> 2) & 0x3;
                *r++=(*p) & 0x3;
                p++;
              }
              if ((image->columns % 4) != 0)
                {
                  for (i=3; i >= (4-(image->columns % 4)); i--)
                    *r++=(*p >> (i*2)) & 0x03;
                  p++;
                }
              break;
            }
            case 4:
            {
              for (x=0; x < ((int) image->columns-1); x+=2)
              {
                *r++=(*p >> 4) & 0xf;
                *r++=(*p) & 0xf;
                p++;
              }
              if ((image->columns % 2) != 0)
                *r++=(*p++ >> 4) & 0xf;
              break;
            }
            case 8:
            {
              for (x=0; x < image->columns; x++)
                *r++=(*p++);
              break;
            }
            case 16:
            {
              for (x=0; x < image->columns; x++)
              {
                ReadQuantum(*r,p);
                r++;
              }
              break;
            }
            default:
              break;
          }
          /*
            Transfer image scanline.
          */
          r=quantum_scanline;
          for (x=0; x < image->columns; x++)
          {
            index=(*r++);
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      {
                        free((char *) quantum_scanline);
                        PrematureExit("Unable to allocate memory",image);
                      }
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        if (image->class == PseudoClass)
          SyncImage(image);
        free((char *) quantum_scanline);
        if (ping_info->valid & PNG_INFO_tRNS)
          {
            /*
              Image has a transparent background.
            */
            image->class=DirectClass;
            image->matte=True;
            q=image->pixels;
            for (i=0; i < image->packets; i++)
            {
              index=q->index;
              q->index=Opaque;
              if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
                {
                  if (index < ping_info->num_trans)
                    q->index=UpScale(ping_info->trans[index]);
                }
              else
                if (index == transparent_color.index)
                  q->index=Transparent;
              q++;
            }
          }
      }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    if (ping_info->num_text > 0)
      for (i=0; i < ping_info->num_text; i++)
      {
        if (strcmp(ping_info->text[i].key,"Comment") == 0)
          PNGTextChunk(i,image->comments);
        if (strcmp(ping_info->text[i].key,"Delay") == 0)
          {
            char
              *delay;

            delay=(char *) NULL;
            PNGTextChunk(i,delay);
            image->delay=atoi(delay);
            free(delay);
          }
        if (strcmp(ping_info->text[i].key,"Description") == 0)
          PNGTextChunk(i,image->comments);
        if (strcmp(ping_info->text[i].key,"Directory") == 0)
          PNGTextChunk(i,image->directory);
        if (strcmp(ping_info->text[i].key,"Label") == 0)
          PNGTextChunk(i,image->label);
        if (strcmp(ping_info->text[i].key,"Montage") == 0)
          PNGTextChunk(i,image->montage);
        if (strcmp(ping_info->text[i].key,"Scene") == 0)
          {
            char
              *scene;

            scene=(char *) NULL;
            PNGTextChunk(i,scene);
            image->scene=atoi(scene);
            free(scene);
          }
        if (strcmp(ping_info->text[i].key,"Signature") == 0)
          PNGTextChunk(i,image->signature);
        if (strcmp(ping_info->text[i].key,"Title") == 0)
          PNGTextChunk(i,image->label);
      }
    /*
      Free memory.
    */
    png_destroy_read_struct(&ping,&ping_info,&end_info);
    free((char *) png_pixels);
    (void) MSBFirstReadLong(image->file);
  } while (strcmp(image_info->magick,"MNG") == 0);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}
#else
static Image *ReadPNGImage(const ImageInfo *image_info)
{
  Warning("PNG library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P N M I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPNMImage reads a Portable Anymap image file and returns it.
%  It allocates the memory necessary for the new Image structure and returns
%  a pointer to the new image.
%
%  The format of the ReadPNMImage routine is:
%
%      image=ReadPNMImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPNMImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/

static unsigned int PNMInteger(Image *image,const unsigned int base)
{
  int
    c;

  unsigned int
    value;

  /*
    Skip any leading whitespace.
  */
  do
  {
    c=fgetc(image->file);
    if (c == EOF)
      return(0);
    if (c == '#')
      {
        register char
          *p;

        unsigned int
          length;

        /*
          Read comment.
        */
        if (image->comments != (char *) NULL)
          {
            length=Extent(image->comments);
            p=image->comments+length;
          }
        else
          {
            length=MaxTextExtent;
            image->comments=(char *) malloc(length*sizeof(char));
            p=image->comments;
          }
        for ( ; image->comments != (char *) NULL; p++)
        {
          if ((p-image->comments+2) >= length)
            {
              *p='\0';
              length<<=1;
              image->comments=(char *)
                realloc((char *) image->comments,length*sizeof(char));
              if (image->comments == (char *) NULL)
                break;
              p=image->comments+Extent(image->comments);
            }
          c=fgetc(image->file);
          if ((c == EOF) || (c == '\n'))
            break;
          *p=(unsigned char) c;
        }
        if (image->comments == (char *) NULL)
          {
            Warning("Memory allocation error",(char *) NULL);
            return(0);
          }
        *p++='\n';
        *p='\0';
      }
  } while (!isdigit(c));
  if (base == 2)
    return(c-'0');
  /*
    Evaluate number.
  */
  value=0;
  do
  {
    value*=10;
    value+=c-'0';
    c=fgetc(image->file);
    if (c == EOF)
      return(0);
  }
  while (isdigit(c));
  return(value);
}

static Image *ReadPNMImage(const ImageInfo *image_info)
{
#define MaxRawValue  255

  char
    format;

  Image
    *image;

  int
    y;

  Quantum
    *scale;

  register int
    i,
    x;

  register RunlengthPacket
    *q;

  unsigned int
    max_value,
    packets,
    status;

  unsigned short
    blue,
    green,
    index,
    red;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,"r");
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read PNM image.
  */
  status=ReadData((char *) &format,1,1,image->file);
  do
  {
    /*
      Verify PNM identifier.
    */
    if ((status == False) || (format != 'P'))
      PrematureExit("Not a PNM image file",image);
    /*
      Initialize image structure.
    */
    format=fgetc(image->file);
    if (format == '7')
      (void) PNMInteger(image,10);
    image->columns=PNMInteger(image,10);
    image->rows=PNMInteger(image,10);
    if ((image->columns*image->rows) == 0)
      PrematureExit("Unable to read image: image dimensions are zero",image);
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    if ((format == '1') || (format == '4'))
      packets=Max((image->columns*image->rows+8) >> 4,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    if ((format == '1') || (format == '4'))
      max_value=1;  /* bitmap */
    else
      max_value=PNMInteger(image,10);
    scale=(Quantum *) NULL;
    if ((format != '3') && (format != '6'))
      {
        /*
          Create colormap.
        */
        image->class=PseudoClass;
        image->colors=Min(max_value,MaxRGB)+1;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        if (format != '7')
          for (i=0; i < image->colors; i++)
          {
            image->colormap[i].red=(Quantum)
              ((long) (MaxRGB*i)/(image->colors-1));
            image->colormap[i].green=(Quantum)
              ((long) (MaxRGB*i)/(image->colors-1));
            image->colormap[i].blue=(Quantum)
              ((long) (MaxRGB*i)/(image->colors-1));
          }
        else
          {
            /*
              Initialize 332 colormap.
            */
            i=0;
            for (red=0; red < 8; red++)
              for (green=0; green < 8; green++)
                for (blue=0; blue < 4; blue++)
                {
                  image->colormap[i].red=(Quantum) ((long) (red*MaxRGB)/7);
                  image->colormap[i].green=(Quantum) ((long) (green*MaxRGB)/7);
                  image->colormap[i].blue=(Quantum) ((long) (blue*MaxRGB)/3);
                  i++;
                }
          }
      }
    if (max_value != MaxRGB)
      {
        /*
          Compute pixel scaling table.
        */
        scale=(Quantum *) malloc((max_value+1)*sizeof(Quantum));
        if (scale == (Quantum *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (i=0; i <= max_value; i++)
          scale[i]=(Quantum) ((i*MaxRGB+(max_value >> 1))/max_value);
      }
    /*
      Convert PNM pixels to runlength-encoded MIFF packets.
    */
    q=image->pixels;
    q->length=MaxRunlength;
    switch (format)
    {
      case '1':
      {
        /*
          Convert PBM image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            index=!PNMInteger(image,2);
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case '2':
      {
        /*
          Convert PGM image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            index=PNMInteger(image,10);
            if (scale != (Quantum *) NULL)
              index=scale[index];
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case '3':
      {
        /*
          Convert PNM image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            red=PNMInteger(image,10);
            green=PNMInteger(image,10);
            blue=PNMInteger(image,10);
            if (scale != (Quantum *) NULL)
              {
                red=scale[red];
                green=scale[green];
                blue=scale[blue];
              }
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=0;
                q->length=0;
            }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        (void) IsPseudoClass(image);
        break;
      }
      case '4':
      {
        unsigned char
          bit,
          byte;

        unsigned int
          x,
          y;

        /*
          Convert PBM raw image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          bit=0;
          byte=0;
          for (x=0; x < image->columns; x++)
          {
            if (bit == 0)
              byte=fgetc(image->file);
            index=(byte & 0x80) ? 0 : 1;
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
            bit++;
            if (bit == 8)
              bit=0;
            byte<<=1;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case '5':
      case '7':
      {
        /*
          Convert PGM raw image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            if (max_value <= MaxRawValue)
              index=fgetc(image->file);
            else
              index=LSBFirstReadShort(image->file);
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case '6':
      {
        /*
          Convert PNM raster image to runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            if (max_value <= MaxRawValue)
              {
                red=fgetc(image->file);
                green=fgetc(image->file);
                blue=fgetc(image->file);
              }
            else
              {
                red=LSBFirstReadShort(image->file);
                green=LSBFirstReadShort(image->file);
                blue=LSBFirstReadShort(image->file);
              }
            if (scale != (Quantum *) NULL)
              {
                red=scale[red];
                green=scale[green];
                blue=scale[blue];
              }
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                      q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=0;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        (void) IsPseudoClass(image);
        break;
      }
      default:
        PrematureExit("Not a PNM image file",image);
    }
    if (scale != (Quantum *) NULL)
      free((char *) scale);
    if (image->class == PseudoClass)
      {
        SyncImage(image);
        CompressColormap(image);
      }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    /*
      Proceed to next image.
    */
    if ((format == '1') || (format == '2') || (format == '3'))
      do
      {
        /*
          Skip to end of line.
        */
        status=ReadData(&format,1,1,image->file);
        if (status == False)
          break;
      } while (format != '\n');
    status=ReadData((char *) &format,1,1,image->file);
    if ((status == True) && (format == 'P'))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while ((status == True) && (format == 'P'));
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d P S I m a g e                                                      %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadPSImage reads a Adobe Postscript image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadPSImage routine is:
%
%      image=ReadPSImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadPSImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadPSImage(const ImageInfo *image_info)
{
#define BoundingBox  "%%BoundingBox:"
#define DocumentMedia  "%%DocumentMedia:"
#define PageBoundingBox  "%%PageBoundingBox:"

  char
    command[MaxTextExtent],
    density[MaxTextExtent],
    *device,
    filename[MaxTextExtent],
    options[MaxTextExtent],
    postscript_filename[MaxTextExtent],
    translate_geometry[MaxTextExtent];

  FILE
    *file;

  float
    dx_resolution,
    dy_resolution,
    lower_x,
    lower_y,
    upper_x,
    upper_y,
    x_resolution,
    y_resolution;

  Image
    *image,
    *next_image;

  ImageInfo
    local_info;

  int
    c,
    count,
    status;

  long int
    filesize;

  RectangleInfo
    bounding_box;

  register char
    *p;

  register int
    i;

  unsigned int
    height,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Open temporary output file.
  */
  TemporaryFilename(postscript_filename);
  file=fopen(postscript_filename,WriteBinaryType);
  if (file == (FILE *) NULL)
    PrematureExit("Unable to write file",image);
  (void) sprintf(translate_geometry,"%f %f translate\n              ",0.0,0.0);
  (void) fputs(translate_geometry,file);
  /*
    Set the page geometry.
  */
  bounding_box.width=0;
  bounding_box.height=0;
  lower_x=0;
  lower_y=0;
  dx_resolution=72.0;
  dy_resolution=72.0;
  x_resolution=72.0;
  (void) strcpy(density,PSDensityGeometry);
  count=sscanf(density,"%fx%f",&x_resolution,&y_resolution);
  if (image_info->density != (char *) NULL)
    count=sscanf(image_info->density,"%fx%f",&x_resolution,&y_resolution);
  if (count != 2)
    y_resolution=x_resolution;
  *options='\0';
  if (image_info->page != (char *) NULL)
    {
      int
        x,
        y;

      /*
        Set page geometry.
      */
      (void) XParseGeometry(PSPageGeometry,&x,&y,&width,&height);
      (void) XParseGeometry(image_info->page,&x,&y,&width,&height);
      (void) sprintf(options,"-g%ux%u",
        (unsigned int) (((width*x_resolution)/dx_resolution)+0.5),
        (unsigned int) (((height*y_resolution)/dy_resolution)+0.5));
    }
  else
    for (p=command; ; )
    {
      c=fgetc(image->file);
      if (c == EOF)
        break;
      (void) fputc(c,file);
      *p++=c;
      if ((c != '\n') && (c != '\r') && ((p-command) < (MaxTextExtent-1)))
        continue;
      *p='\0';
      p=command;
      /*
        Parse a bounding box statement.
      */
      count=0;
      if (strncmp(BoundingBox,command,Extent(BoundingBox)) == 0)
        count=sscanf(command,"%%%%BoundingBox: %f %f %f %f",&lower_x,&lower_y,
          &upper_x,&upper_y);
      if (strncmp(DocumentMedia,command,Extent(DocumentMedia)) == 0)
        count=
          sscanf(command,"%%%%DocumentMedia: %*s %f %f",&upper_x,&upper_y)+2;
      if (strncmp(PageBoundingBox,command,Extent(PageBoundingBox)) == 0)
        count=sscanf(command,"%%%%PageBoundingBox: %f %f %f %f",
          &lower_x,&lower_y,&upper_x,&upper_y);
      if (count != 4)
        continue;
      if ((lower_x > upper_x) || (lower_y > upper_y))
        continue;
      /*
        Set Postscript render geometry.
      */
      (void) sprintf(translate_geometry,"%f %f translate\n",-lower_x,-lower_y);
      width=(unsigned int) (upper_x-lower_x+1);
      if ((float) ((int) upper_x) != upper_x)
        width++;
      height=(unsigned int) (upper_y-lower_y+1);
      if ((float) ((int) upper_y) != upper_y)
        height++;
      if ((width <= bounding_box.width) && (height <= bounding_box.height))
        continue;
      (void) sprintf(options,"-g%ux%u",
        (unsigned int) (((width*x_resolution)/dx_resolution)+0.5),
        (unsigned int) (((height*y_resolution)/dy_resolution)+0.5));
      bounding_box.width=width;
      bounding_box.height=height;
    }
  for ( ; ; )
  {
    c=fgetc(image->file);
    if (c == EOF)
      break;
    (void) fputc(c,file);
  }
  if (ferror(file))
    {
      Warning("An error has occurred writing to file",postscript_filename);
      (void) fclose(file);
      return((Image *) NULL);
    }
  (void) fseek(file,0L,SEEK_SET);
  (void) fputs(translate_geometry,file);
  (void) fclose(file);
  CloseImage(image);
  filesize=image->filesize;
  DestroyImage(image);
  /*
    Determine if density options is specified.
  */
  (void) strcat(options," -r");
  if (image_info->density == (char *) NULL)
    (void) strcat(options,PSDensityGeometry);
  else
    (void) strcat(options,image_info->density);
  /*
    Use Ghostscript to convert Postscript image.
  */
  device=PostscriptColorDevice;
  if (image_info->monochrome)
    device=PostscriptMonoDevice;
  (void) strcpy(filename,image_info->filename);
  for (i=0; i < 50; i++)
  {
    /*
      Ghostscript eats % characters.
    */
    TemporaryFilename(image_info->filename);
    if (strchr(image_info->filename,'%') == (char *) NULL)
      break;
  }
  (void) sprintf(command,PostscriptCommand,device,options,image_info->filename,
    postscript_filename);
  status=SystemCommand(command);
  if (status)
    {
      /*
        Pre GS 3.51 does not support the pnmraw device.
      */
      (void) sprintf(command,PostscriptCommand,"ppmraw",options,
        image_info->filename,postscript_filename);
      status=SystemCommand(command);
    }
  if (!IsAccessible(image_info->filename))
    {
      /*
        Ghostscript requires a showpage operator.
      */
      file=fopen(postscript_filename,"a");
      if (file == (FILE *) NULL)
        PrematureExit("Unable to write file",image);
      (void) fputs("showpage\n",file);
      (void) fclose(file);
      status=SystemCommand(command);
    }
  if (!IsAccessible(image_info->filename))
    {
      /*
        It's possible that the -g option caused a problem.
      */
      (void) strcpy(options,"-r");
      if (image_info->density == (char *) NULL)
        (void) strcat(options,PSDensityGeometry);
      else
        (void) strcat(options,image_info->density);
      (void) sprintf(command,PostscriptCommand,device,options,
        image_info->filename,postscript_filename);
      status=SystemCommand(command);
    }
  (void) remove(postscript_filename);
  if (status)
    {
      /*
        Ghostscript has failed-- try the Display Postscript Extension.
      */
      (void) strcpy(image_info->filename,filename);
      image=ReadDPSImage(image_info);
      if (image != (Image *) NULL)
        return(image);
      Warning("Postscript translation failed",image_info->filename);
      return((Image *) NULL);
    }
  local_info=(*image_info);
  image=ReadImage(&local_info);
  (void) remove(image_info->filename);
  if (image == (Image *) NULL)
    {
      Warning("Postscript translation failed",image_info->filename);
      return((Image *) NULL);
    }
  do
  {
    (void) strcpy(image->magick,"PS");
    (void) strcpy(image->filename,filename);
    image->filesize=filesize;
    next_image=image->next;
    if (next_image != (Image *) NULL)
      image=next_image;
  } while (next_image != (Image *) NULL);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d R A D I A N C E I m a g e                                          %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadRADIANCEImage reads a RADIANCE image file and returns it.
%  It allocates the memory necessary for the new Image structure and returns
%  a pointer to the new image.
%
%  The format of the ReadRADIANCEImage routine is:
%
%      image=ReadRADIANCEImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadRADIANCEImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadRADIANCEImage(const ImageInfo *image_info)
{
  char
    command[MaxTextExtent],
    filename[MaxTextExtent];

  Image
    *image,
    *next_image;

  int
    status;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  CloseImage(image);
  /*
    Use ra_ppm to convert RADIANCE image.
  */
  (void) strcpy(filename,image_info->filename);
  TemporaryFilename(image_info->filename);
  (void) sprintf(command,"ra_ppm -g 1.0 %s %s",filename,image_info->filename);
  status=SystemCommand(command);
  if (status)
    PrematureExit("RADIANCE translation failed",image);
  DestroyImage(image);
  image=ReadPNMImage(image_info);
  (void) remove(image_info->filename);
  if (image == (Image *) NULL)
    PrematureExit("RADIANCE translation failed",image);
  /*
    Assign proper filename.
  */
  do
  {
    (void) strcpy(image->filename,filename);
    next_image=image->next;
    if (next_image != (Image *) NULL)
      image=next_image;
  } while (next_image != (Image *) NULL);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d R G B I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadRGBImage reads an image of raw red, green, and blue bytes and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadRGBImage routine is:
%
%      image=ReadRGBImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadRGBImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadRGBImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    count,
    x,
    x_offset,
    y,
    y_offset;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *scanline;

  unsigned int
    height,
    packet_size,
    width;

  unsigned short
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x_offset=0;
  y_offset=0;
  x=0;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  if (image_info->interlace != PartitionInterlace)
    {
      /*
        Open image file.
      */
      OpenImage(image_info,image,ReadBinaryType);
      if (image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",image);
      for (i=0; i < x; i++)
        (void) fgetc(image->file);
    }
  /*
    Allocate memory for a scanline.
  */
  packet_size=3*(image->depth >> 3);
  if (strcmp(image_info->magick,"RGBA") == 0)
    {
      image->matte=True;
      packet_size=4*(image->depth >> 3);
    }
  scanline=(unsigned char *) malloc(packet_size*width*sizeof(unsigned char));
  if (scanline == (unsigned char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  if (image_info->subrange != 0)
    while (image->scene < image_info->subimage)
    {
      /*
        Skip to next image.
      */
      image->scene++;
      for (y=0; y < height; y++)
        (void) ReadData((char *) scanline,packet_size,width,image->file);
    }
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=width;
    image->rows=height;
    if (image_info->subrange == 0)
      if (image_info->tile != (char *) NULL)
        (void) XParseGeometry(image_info->tile,&x_offset,&y_offset,
          &image->columns,&image->rows);
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert raster image to runlength-encoded packets.
    */
    switch (image_info->interlace)
    {
      case NoneInterlace:
      default:
      {
        /*
          No interlacing:  RGBRGBRGBRGBRGBRGB...
        */
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        q=image->pixels;
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            ReadQuantum(q->green,p);
            ReadQuantum(q->blue,p);
            q->index=0;
            if (image->matte)
              ReadQuantum(q->index,p);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case LineInterlace:
      {
        /*
          Line interlacing:  RRR...GGG...BBB...RRR...GGG...BBB...
        */
        packet_size=image->depth >> 3;
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            q->index=0;
            q->length=0;
            q++;
          }
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->green,p);
            q++;
          }
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          q=image->pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->blue,p);
            q++;
          }
          if (image->matte)
            {
              (void) ReadData((char *) scanline,packet_size,width,image->file);
              p=scanline+packet_size*x_offset;
              q=image->pixels+y*image->columns;
              for (x=0; x < image->columns; x++)
              {
                ReadQuantum(q->index,p);
                q++;
              }
            }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        break;
      }
      case PlaneInterlace:
      case PartitionInterlace:
      {
        unsigned int
          span;

        /*
          Plane interlacing:  RRRRRR...GGGGGG...BBBBBB...
        */
        if (image_info->interlace == PartitionInterlace)
          {
            AppendImageFormat("R",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        packet_size=image->depth >> 3;
        for (y=0; y < y_offset; y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        i=0;
        span=image->rows*(image->matte ? 4 : 3);
        q=image->pixels;
        for (y=0; y < image->rows; y++)
        {
          if ((y > 0) || (image->previous == (Image *) NULL))
            (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->red,p);
            q->index=0;
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,span);
        }
        if (image_info->interlace == PartitionInterlace)
          {
            CloseImage(image);
            AppendImageFormat("G",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        q=image->pixels;
        for (y=0; y < (height-image->rows); y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->green,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,span);
        }
        if (image_info->interlace == PartitionInterlace)
          {
            CloseImage(image);
            AppendImageFormat("B",image->filename);
            OpenImage(image_info,image,ReadBinaryType);
            if (image->file == (FILE *) NULL)
              PrematureExit("Unable to open file",image);
          }
        q=image->pixels;
        for (y=0; y < (height-image->rows); y++)
          (void) ReadData((char *) scanline,packet_size,width,image->file);
        for (y=0; y < image->rows; y++)
        {
          (void) ReadData((char *) scanline,packet_size,width,image->file);
          p=scanline+packet_size*x_offset;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(q->blue,p);
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,i++,span);
        }
        if (image->matte)
          {
            /*
              Read matte channel.
            */
            if (image_info->interlace == PartitionInterlace)
              {
                CloseImage(image);
                AppendImageFormat("A",image->filename);
                OpenImage(image_info,image,ReadBinaryType);
                if (image->file == (FILE *) NULL)
                  PrematureExit("Unable to open file",image);
              }
            q=image->pixels;
            for (y=0; y < (height-image->rows); y++)
              (void) ReadData((char *) scanline,packet_size,width,image->file);
            for (y=0; y < image->rows; y++)
            {
              (void) ReadData((char *) scanline,packet_size,width,image->file);
              p=scanline+packet_size*x_offset;
              for (x=0; x < image->columns; x++)
              {
                ReadQuantum(q->index,p);
                q++;
              }
              if (image->previous == (Image *) NULL)
                ProgressMonitor(LoadImageText,i++,span);
            }
          }
        if (image_info->interlace == PartitionInterlace)
          (void) strcpy(image->filename,image_info->filename);
        break;
      }
    }
    CompressImage(image);
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    /*
      Proceed to next image.
    */
    count=ReadData((char *) scanline,packet_size,width,image->file);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (count > 0);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  free((char *) scanline);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d R L A I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadRLAImage reads a run-length encoded Wavefront RLA image file
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  Note:  This module was contributed by Lester Vecsey (master@internexus.net).
%
%  The format of the ReadRLAImage routine is:
%
%      image=ReadRLAImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadRLAImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadRLAImage(const ImageInfo *image_info)
{
  typedef struct _WindowFrame
  {
    short
      left,
      right,
      bottom,
      top;
  } WindowFrame;

  typedef struct _RLAHeader
  {
    WindowFrame
      window,
      active_window;

    short
      frame,
      storage_type,
      number_channels,
      number_matte_channels,
      number_auxillary_channels,
      revision;

    char
      gamma[16],
      red_primary[24],
      green_primary[24],
      blue_primary[24],
      white_point[24];

    long
      job_number;

    char
      name[128],
      description[128],
      program[64],
      machine[32],
      user[32],
      date[20],
      aspect[24],
      aspect_ratio[8],
      chan[32];

    short
      field;

    char
      time[12],
      filter[32];

    short
      bits_per_channel,
      matte_type,
      matte_bits,
      auxillary_type,
      auxillary_bits;

    char
      auxillary[32],
      space[36];

    long
      next;
  } RLAHeader;

  Image
    *image;

  int
    length,
    runlength,
    y;

  long
    *scanlines;

  register int
    i;

  register RunlengthPacket
    *q;

  RLAHeader
    rla_header;

  unsigned char
    byte;

  unsigned int
    channel;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  rla_header.window.left=MSBFirstReadShort(image->file);
  rla_header.window.right=MSBFirstReadShort(image->file);
  rla_header.window.bottom=MSBFirstReadShort(image->file);
  rla_header.window.top=MSBFirstReadShort(image->file);
  rla_header.active_window.left=MSBFirstReadShort(image->file);
  rla_header.active_window.right=MSBFirstReadShort(image->file);
  rla_header.active_window.bottom=MSBFirstReadShort(image->file);
  rla_header.active_window.top=MSBFirstReadShort(image->file);
  rla_header.frame=MSBFirstReadShort(image->file);
  rla_header.storage_type=MSBFirstReadShort(image->file);
  rla_header.number_channels=MSBFirstReadShort(image->file);
  if (rla_header.number_channels == 0)
    rla_header.number_channels=3;
  rla_header.number_matte_channels=MSBFirstReadShort(image->file);
  rla_header.number_auxillary_channels=MSBFirstReadShort(image->file);
  rla_header.revision=MSBFirstReadShort(image->file);
  (void) ReadData((char *) rla_header.gamma,16,1,image->file);
  (void) ReadData((char *) rla_header.red_primary,24,1,image->file);
  (void) ReadData((char *) rla_header.green_primary,24,1,image->file);
  (void) ReadData((char *) rla_header.blue_primary,24,1,image->file);
  (void) ReadData((char *) rla_header.white_point,24,1,image->file);
  rla_header.job_number=(long) MSBFirstReadLong(image->file);
  (void) ReadData((char *) rla_header.name,128,1,image->file);
  (void) ReadData((char *) rla_header.description,128,1,image->file);
  (void) ReadData((char *) rla_header.program,64,1,image->file);
  (void) ReadData((char *) rla_header.machine,32,1,image->file);
  (void) ReadData((char *) rla_header.user,32,1,image->file);
  (void) ReadData((char *) rla_header.date,20,1,image->file);
  (void) ReadData((char *) rla_header.aspect,24,1,image->file);
  (void) ReadData((char *) rla_header.aspect_ratio,8,1,image->file);
  (void) ReadData((char *) rla_header.chan,32,1,image->file);
  rla_header.field=MSBFirstReadShort(image->file);
  (void) ReadData((char *) rla_header.time,12,1,image->file);
  (void) ReadData((char *) rla_header.filter,32,1,image->file);
  rla_header.bits_per_channel=MSBFirstReadShort(image->file);
  rla_header.matte_type=MSBFirstReadShort(image->file);
  rla_header.matte_bits=MSBFirstReadShort(image->file);
  rla_header.auxillary_type=MSBFirstReadShort(image->file);
  rla_header.auxillary_bits=MSBFirstReadShort(image->file);
  (void) ReadData((char *) rla_header.auxillary,32,1,image->file);
  (void) ReadData((char *) rla_header.space,36,1,image->file);
  rla_header.next=(long) MSBFirstReadLong(image->file);
  /*
    Initialize image structure.
  */
  image->columns=rla_header.active_window.right-rla_header.active_window.left;
  image->rows=rla_header.active_window.top-rla_header.active_window.bottom;
  image->packets=image->columns*image->rows;
  image->depth=QuantumDepth;
  image->matte=rla_header.number_channels > 3;
  scanlines=(long *) malloc(image->rows*sizeof(long));
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  if (*rla_header.description != '\0')
    {
      /*
        RLA image comment.
      */
      image->comments=(char *)
        malloc((Extent(rla_header.description)+1)*sizeof(char));
      if (image->comments == (char *) NULL)
        PrematureExit("Unable to allocate memory",image)
      (void) strcpy(image->comments,rla_header.description);
    }
  /*
    Read offsets to each scanline data.
  */
  for (i=0; i < image->rows; i++)
    scanlines[i]=(long) MSBFirstReadLong(image->file);
  /*
    Read image data.
  */
  q=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    (void) fseek(image->file,scanlines[image->rows-y-1],SEEK_SET);
    for (channel=0; channel < rla_header.number_channels; channel++)
    {
      length=MSBFirstReadShort(image->file);
      q=image->pixels+(y*image->columns);
      while (length > 0)
      {
        (void) ReadData((char *) &byte,1,1,image->file);
        runlength=byte;
        if (byte > 127)
          runlength=byte-256;
        length--;
        if (length == 0)
          break;
        if (runlength < 0)
          {
            while (runlength < 0)
            {
              (void) ReadData((char *) &byte,1,1,image->file);
              length--;
              switch (channel)
              {
                case 0:
                {
                  q->red=UpScale(byte);
                  q->index=0;
                  q->length=0;
                  break;
                }
                case 1:
                {
                  q->green=UpScale(byte);
                  break;
                }
                case 2:
                {
                  q->blue=UpScale(byte);
                  break;
                }
                case 3:
                default:
                {
                  q->index=UpScale(byte);
                  break;
                }
              }
              q++;
              runlength++;
            }
            continue;
          }
        (void) ReadData((char *) &byte,1,1,image->file);
        length--;
        runlength++;
        do
        {
          switch (channel)
          {
            case 0:
            {
              q->red=UpScale(byte);
              q->index=0;
              q->length=0;
              break;
            }
            case 1:
            {
              q->green=UpScale(byte);
              break;
            }
            case 2:
            {
              q->blue=UpScale(byte);
              break;
            }
            case 3:
            default:
            {
              q->index=UpScale(byte);
              break;
            }
          }
          q++;
          runlength--;
        }
        while (runlength > 0);
      }
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d R L E I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadRLEImage reads a run-length encoded Utah Raster Toolkit
%  image file and returns it.  It allocates the memory necessary for the new
%  Image structure and returns a pointer to the new image.
%
%  The format of the ReadRLEImage routine is:
%
%      image=ReadRLEImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadRLEImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadRLEImage(const ImageInfo *image_info)
{
#define SkipLinesOp  0x01
#define SetColorOp  0x02
#define SkipPixelsOp  0x03
#define ByteDataOp  0x05
#define RunDataOp  0x06
#define EOFOp  0x07

  char
    magick[12];

  Image
    *image;

  int
    opcode,
    operand,
    status,
    x,
    y;

  register int
    i,
    j;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    background_color[256],
    *colormap,
    pixel,
    plane,
    *rle_pixels;

  unsigned int
    bits_per_pixel,
    flags,
    map_length,
    number_colormaps,
    number_planes;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Determine if this is a RLE file.
  */
  status=ReadData((char *) magick,1,2,image->file);
  if ((status == False) || (strncmp(magick,"\122\314",2) != 0))
    PrematureExit("Not a RLE image file",image);
  do
  {
    /*
      Read image header.
    */
    (void) LSBFirstReadShort(image->file);
    (void) LSBFirstReadShort(image->file);
    image->columns=LSBFirstReadShort(image->file);
    image->rows=LSBFirstReadShort(image->file);
    image->packets=image->columns*image->rows;
    flags=fgetc(image->file);
    image->matte=flags & 0x04;
    number_planes=fgetc(image->file);
    bits_per_pixel=fgetc(image->file);
    number_colormaps=fgetc(image->file);
    map_length=1 << fgetc(image->file);
    if ((number_planes == 0) || (number_planes == 2) || (bits_per_pixel != 8) ||
        (image->columns == 0))
      PrematureExit("Unsupported RLE image file",image);
    if (flags & 0x02)
      {
        /*
          No background color-- initialize to black.
        */
        for (i=0; i < number_planes; i++)
          background_color[i]=(unsigned char) 0;
        (void) fgetc(image->file);
      }
    else
      {
        /*
          Initialize background color.
        */
        p=background_color;
        for (i=0; i < number_planes; i++)
          *p++=(unsigned char) fgetc(image->file);
      }
    if ((number_planes & 0x01) == 0)
      (void) fgetc(image->file);
    colormap=(unsigned char *) NULL;
    if (number_colormaps != 0)
      {
        /*
          Read image colormaps.
        */
        colormap=(unsigned char *)
          malloc(number_colormaps*map_length*sizeof(unsigned char));
        if (colormap == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        p=colormap;
        for (i=0; i < number_colormaps; i++)
          for (j=0; j < map_length; j++)
            *p++=XDownScale(LSBFirstReadShort(image->file));
      }
    if (flags & 0x08)
      {
        unsigned int
          length;

        /*
          Read image comment.
        */
        length=LSBFirstReadShort(image->file);
        image->comments=(char *) malloc(length*sizeof(char));
        if (image->comments == (char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData((char *) image->comments,1,length-1,image->file);
        image->comments[length-1]='\0';
        if ((length & 0x01) == 0)
          (void) fgetc(image->file);
      }
    /*
      Allocate RLE pixels.
    */
    if (image->matte)
      number_planes++;
    rle_pixels=(unsigned char *)
      malloc(image->packets*number_planes*sizeof(unsigned char));
    if (rle_pixels == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    if ((flags & 0x01) && !(flags & 0x02))
      {
        /*
          Set background color.
        */
        p=rle_pixels;
        for (i=0; i < image->packets; i++)
        {
          if (!image->matte)
            for (j=0; j < number_planes; j++)
              *p++=background_color[j];
          else
            {
              for (j=0; j < (number_planes-1); j++)
                *p++=background_color[j];
              *p++=0;  /* initialize matte channel */
            }
        }
      }
    /*
      Read runlength-encoded image.
    */
    plane=0;
    x=0;
    y=0;
    opcode=fgetc(image->file);
    do
    {
      switch (opcode & 0x3f)
      {
        case SkipLinesOp:
        {
          operand=fgetc(image->file);
          if (opcode & 0x40)
            operand=LSBFirstReadShort(image->file);
          x=0;
          y+=operand;
          break;
        }
        case SetColorOp:
        {
          operand=fgetc(image->file);
          plane=operand;
          if (plane == 255)
            plane=number_planes-1;
          x=0;
          break;
        }
        case SkipPixelsOp:
        {
          operand=fgetc(image->file);
          if (opcode & 0x40)
            operand=LSBFirstReadShort(image->file);
          x+=operand;
          break;
        }
        case ByteDataOp:
        {
          operand=fgetc(image->file);
          if (opcode & 0x40)
            operand=LSBFirstReadShort(image->file);
          p=rle_pixels+((image->rows-y-1)*image->columns*number_planes)+
            x*number_planes+plane;
          operand++;
          for (i=0; i < operand; i++)
          {
            pixel=fgetc(image->file);
            if ((y < image->rows) && ((x+i) < image->columns))
              *p=pixel;
            p+=number_planes;
          }
          if (operand & 0x01)
            (void) fgetc(image->file);
          x+=operand;
          break;
        }
        case RunDataOp:
        {
          operand=fgetc(image->file);
          if (opcode & 0x40)
            operand=LSBFirstReadShort(image->file);
          pixel=fgetc(image->file);
          (void) fgetc(image->file);
          operand++;
          p=rle_pixels+((image->rows-y-1)*image->columns*number_planes)+
            x*number_planes+plane;
          for (i=0; i < operand; i++)
          {
            if ((y < image->rows) && ((x+i) < image->columns))
              *p=pixel;
            p+=number_planes;
          }
          x+=operand;
          break;
        }
        default:
          break;
      }
      opcode=fgetc(image->file);
    } while (((opcode & 0x3f) != EOFOp) && (opcode != EOF));
    if (number_colormaps != 0)
      {
        unsigned int
          mask;

        /*
          Apply colormap transformation to image.
        */
        mask=(map_length-1);
        p=rle_pixels;
        if (number_colormaps == 1)
          for (i=0; i < image->packets; i++)
          {
            *p=(unsigned char) colormap[*p & mask];
            p++;
          }
        else
          if ((number_planes >= 3) && (number_colormaps >= 3))
            for (i=0; i < image->packets; i++)
              for (j=0; j < number_planes; j++)
              {
                *p=(unsigned char) colormap[j*map_length+(*p & mask)];
                p++;
              }
      }
    /*
      Initialize image structure.
    */
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    q=image->pixels;
    if (number_planes >= 3)
      {
        /*
          Convert raster image to DirectClass runlength-encoded packets.
        */
        p=rle_pixels;
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            q->red=UpScale(*p++);
            q->green=UpScale(*p++);
            q->blue=UpScale(*p++);
            q->index=0;
            if (image->matte)
              q->index=UpScale(*p++);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      }
    else
      {
        /*
          Create colormap.
        */
        image->class=PseudoClass;
        if (number_colormaps == 0)
          map_length=256;
        image->colors=map_length;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        p=colormap;
        if (number_colormaps == 0)
          for (i=0; i < image->colors; i++)
          {
            /*
              Grayscale.
            */
            image->colormap[i].red=(MaxRGB*i)/(image->colors-1);
            image->colormap[i].green=(MaxRGB*i)/(image->colors-1);
            image->colormap[i].blue=(MaxRGB*i)/(image->colors-1);
          }
        else
          if (number_colormaps == 1)
            for (i=0; i < image->colors; i++)
            {
              /*
                Pseudocolor.
              */
              image->colormap[i].red=(Quantum) UpScale(i);
              image->colormap[i].green=(Quantum) UpScale(i);
              image->colormap[i].blue=(Quantum) UpScale(i);
            }
          else
            for (i=0; i < image->colors; i++)
            {
              image->colormap[i].red=UpScale(*p);
              image->colormap[i].green=UpScale(*(p+map_length));
              image->colormap[i].blue=UpScale(*(p+map_length*2));
              p++;
            }
        p=rle_pixels;
        if (!image->matte)
          {
            /*
              Convert raster image to PseudoClass runlength-encoded packets.
            */
            for (y=0; y < image->rows; y++)
            {
              for (x=0; x < image->columns; x++)
              {
                q->index=(unsigned short) (*p++);
                q->length=0;
                q++;
              }
              if (image->previous == (Image *) NULL)
                ProgressMonitor(LoadImageText,y,image->rows);
            }
            SyncImage(image);
          }
        else
          {
            /*
              Image has a matte channel-- promote to DirectClass.
            */
            for (y=0; y < image->rows; y++)
            {
              for (x=0; x < image->columns; x++)
              {
                q->red=image->colormap[*p++].red;
                q->green=image->colormap[*p++].green;
                q->blue=image->colormap[*p++].blue;
                q->index=UpScale(*p++);
                q->length=0;
                q++;
              }
              if (image->previous == (Image *) NULL)
                ProgressMonitor(LoadImageText,y,image->rows);
            }
            free(image->colormap);
            image->colormap=(ColorPacket *) NULL;
            image->class=DirectClass;
            image->colors=0;
          }
      }
    if (number_colormaps != 0)
      free((char *) colormap);
    free((char *) rle_pixels);
    CompressImage(image);
    /*
      Proceed to next image.
    */
    (void) fgetc(image->file);
    status=ReadData((char *) magick,1,2,image->file);
    if ((status == True) && (strncmp(magick,"\122\314",2) == 0))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while ((status == True) && (strncmp(magick,"\122\314",2) == 0));
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d S G I I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadSGIImage reads a SGI RGB image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadSGIImage routine is:
%
%      image=ReadSGIImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadSGIImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/

static void SGIDecode(unsigned char *packets,unsigned char *pixels)
{
  unsigned char
    count,
    pixel;

  for ( ; ;)
  {
    pixel=(*packets++);
    count=pixel & 0x7f;
    if (count == 0)
      break;
    if (pixel & 0x80)
      for ( ; count != 0; count--)
      {
        *pixels=(*packets++);
        pixels+=4;
      }
    else
      {
        pixel=(*packets++);
        for ( ; count != 0; count--)
        {
          *pixels=pixel;
          pixels+=4;
        }
      }
  }
}

static Image *ReadSGIImage(const ImageInfo *image_info)
{
  typedef struct _SGIHeader
  {
    unsigned short
      magic;

    unsigned char
      storage,
      bytes_per_pixel;

    unsigned short
      dimension,
      columns,
      rows,
      depth;

    unsigned long
      minimum_value,
      maximum_value;

    unsigned char
      filler[492];
  } SGIHeader;

  Image
    *image;

  SGIHeader
    iris_header;

  register int
    i,
    x,
    y,
    z;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *iris_pixels;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read SGI raster header.
  */
  iris_header.magic=MSBFirstReadShort(image->file);
  do
  {
    /*
      Verify SGI identifier.
    */
    if (iris_header.magic != 0x01DA)
      PrematureExit("Not a SGI RGB image",image);
    iris_header.storage=fgetc(image->file);
    iris_header.bytes_per_pixel=fgetc(image->file);
    if (iris_header.bytes_per_pixel != 1)
      PrematureExit("Image must have 1 byte per pixel channel",image);
    iris_header.dimension=MSBFirstReadShort(image->file);
    iris_header.columns=MSBFirstReadShort(image->file);
    iris_header.rows=MSBFirstReadShort(image->file);
    iris_header.depth=MSBFirstReadShort(image->file);
    iris_header.minimum_value=MSBFirstReadLong(image->file);
    iris_header.maximum_value=MSBFirstReadLong(image->file);
    (void) ReadData((char *) iris_header.filler,1,
      (unsigned int) sizeof(iris_header.filler),image->file);
    /*
      Allocate SGI pixels.
    */
    iris_pixels=(unsigned char *)
      malloc(4*iris_header.columns*iris_header.rows*sizeof(unsigned char));
    if (iris_pixels == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    if (iris_header.storage != 0x01)
      {
        unsigned char
          *scanline;

        /*
          Read standard image format.
        */
        scanline=(unsigned char *)
          malloc(iris_header.columns*sizeof(unsigned char));
        if (scanline == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (z=0; z < (int) iris_header.depth; z++)
        {
          p=iris_pixels+z;
          for (y=0; y < (int) iris_header.rows; y++)
          {
            (void) ReadData((char *) scanline,1,iris_header.columns,
              image->file);
            for (x=0; x < (int) iris_header.columns; x++)
            {
              *p=scanline[x];
              p+=4;
            }
          }
        }
        free((char *) scanline);
      }
    else
      {
        unsigned char
          *packets;

        unsigned int
          data_order;

        unsigned long
          offset,
          *offsets,
          *runlength;

        /*
          Read runlength-encoded image format.
        */
        offsets=(unsigned long *)
          malloc(iris_header.rows*iris_header.depth*sizeof(unsigned long));
        packets=(unsigned char *)
          malloc(((iris_header.columns << 1)+10)*sizeof(unsigned char));
        runlength=(unsigned long *)
          malloc(iris_header.rows*iris_header.depth*sizeof(unsigned long));
        if ((offsets == (unsigned long *) NULL) ||
            (packets == (unsigned char *) NULL) ||
            (runlength == (unsigned long *) NULL))
          PrematureExit("Unable to allocate memory",image);
        for (i=0; i < (int) (iris_header.rows*iris_header.depth); i++)
          offsets[i]=MSBFirstReadLong(image->file);
        for (i=0; i < (int) (iris_header.rows*iris_header.depth); i++)
          runlength[i]=MSBFirstReadLong(image->file);
        /*
          Check data order.
        */
        offset=0;
        data_order=0;
        for (y=0; ((y < (int) iris_header.rows) && !data_order); y++)
          for (z=0; ((z < (int) iris_header.depth) && !data_order); z++)
          {
            if (offsets[y+z*iris_header.rows] < offset)
              data_order=1;
            offset=offsets[y+z*iris_header.rows];
          }
        offset=512+4*((iris_header.rows*iris_header.depth) << 1);
        if (data_order == 1)
          {
            for (z=0; z < (int) iris_header.depth; z++)
            {
              p=iris_pixels;
              for (y=0; y < (int) iris_header.rows; y++)
              {
                if (offset != offsets[y+z*iris_header.rows])
                  {
                    offset=offsets[y+z*iris_header.rows];
                    (void) fseek(image->file,(long) offset,SEEK_SET);
                  }
                (void) ReadData((char *) packets,1,
                  (unsigned int) runlength[y+z*iris_header.rows],image->file);
                offset+=runlength[y+z*iris_header.rows];
                SGIDecode(packets,p+z);
                p+=(iris_header.columns*4);
              }
            }
          }
        else
          {
            p=iris_pixels;
            for (y=0; y < (int) iris_header.rows; y++)
            {
              for (z=0; z < (int) iris_header.depth; z++)
              {
                if (offset != offsets[y+z*iris_header.rows])
                  {
                    offset=offsets[y+z*iris_header.rows];
                    (void) fseek(image->file,(long) offset,SEEK_SET);
                  }
                (void) ReadData((char *) packets,1,
                  (unsigned int) runlength[y+z*iris_header.rows],image->file);
                offset+=runlength[y+z*iris_header.rows];
                SGIDecode(packets,p+z);
              }
              p+=(iris_header.columns*4);
            }
          }
        free(runlength);
        free(packets);
        free(offsets);
      }
    /*
      Initialize image structure.
    */
    image->matte=iris_header.depth == 4;
    image->columns=iris_header.columns;
    image->rows=iris_header.rows;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert SGI raster image to runlength-encoded packets.
    */
    q=image->pixels;
    if (iris_header.depth >= 3)
      {
        /*
          Convert SGI image to DirectClass runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          p=iris_pixels+((image->rows-1)-y)*(image->columns*4);
          for (x=0; x < image->columns; x++)
          {
            q->red=UpScale(*p);
            q->green=UpScale(*(p+1));
            q->blue=UpScale(*(p+2));
            q->index=UpScale(*(p+3));
            q->length=0;
            p+=4;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      }
    else
      {
        unsigned short
          index;

        /*
          Create grayscale map.
        */
        image->class=PseudoClass;
        image->colors=256;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].red=(Quantum) UpScale(i);
          image->colormap[i].green=(Quantum) UpScale(i);
          image->colormap[i].blue=(Quantum) UpScale(i);
        }
        /*
          Convert SGI image to PseudoClass runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          p=iris_pixels+((image->rows-1)-y)*(image->columns*4);
          for (x=0; x < image->columns; x++)
          {
            index=(unsigned short) (*p);
            q->index=index;
            q->length=0;
            p+=4;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        SyncImage(image);
      }
    free((char *) iris_pixels);
    CompressImage(image);
    /*
      Proceed to next image.
    */
    iris_header.magic=MSBFirstReadShort(image->file);
    if (iris_header.magic == 0x01DA)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (iris_header.magic == 0x01DA);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d S U N I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadSUNImage reads a SUN image file and returns it.  It allocates
%  the memory necessary for the new Image structure and returns a pointer to
%  the new image.
%
%  The format of the ReadSUNImage routine is:
%
%      image=ReadSUNImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadSUNImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadSUNImage(const ImageInfo *image_info)
{
#define RMT_EQUAL_RGB  1
#define RMT_NONE  0
#define RMT_RAW  2
#define RT_STANDARD  1
#define RT_ENCODED  2
#define RT_FORMAT_RGB  3

  typedef struct _SUNHeader
  {
    unsigned long
      magic,
      width,
      height,
      depth,
      length,
      type,
      maptype,
      maplength;
  } SUNHeader;

  Image
    *image;

  register int
    bit,
    i,
    x,
    y;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  SUNHeader
    sun_header;

  unsigned char
    *sun_data,
    *sun_pixels;

  unsigned int
    bytes_per_line,
    status;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read SUN raster header.
  */
  sun_header.magic=MSBFirstReadLong(image->file);
  do
  {
    /*
      Verify SUN identifier.
    */
    if (sun_header.magic != 0x59a66a95)
      PrematureExit("Not a SUN raster image",image);
    sun_header.width=MSBFirstReadLong(image->file);
    sun_header.height=MSBFirstReadLong(image->file);
    sun_header.depth=MSBFirstReadLong(image->file);
    sun_header.length=MSBFirstReadLong(image->file);
    sun_header.type=MSBFirstReadLong(image->file);
    sun_header.maptype=MSBFirstReadLong(image->file);
    sun_header.maplength=MSBFirstReadLong(image->file);
    switch (sun_header.maptype)
    {
      case RMT_NONE:
      {
        if (sun_header.depth < 24)
          {
            /*
              Create linear color ramp.
            */
            image->colors=1 << sun_header.depth;
            image->colormap=(ColorPacket *)
              malloc(image->colors*sizeof(ColorPacket));
            if (image->colormap == (ColorPacket *) NULL)
              PrematureExit("Unable to allocate memory",image);
            for (i=0; i < image->colors; i++)
            {
              image->colormap[i].red=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].green=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].blue=(MaxRGB*i)/(image->colors-1);
            }
          }
        break;
      }
      case RMT_EQUAL_RGB:
      {
        unsigned char
          *sun_colormap;

        /*
          Read SUN raster colormap.
        */
        image->colors=(unsigned int) sun_header.maplength/3;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        sun_colormap=(unsigned char *)
          malloc(image->colors*sizeof(unsigned char));
        if ((image->colormap == (ColorPacket *) NULL) ||
            (sun_colormap == (unsigned char *) NULL))
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData((char *) sun_colormap,1,image->colors,image->file);
        for (i=0; i < image->colors; i++)
          image->colormap[i].red=UpScale(sun_colormap[i]);
        (void) ReadData((char *) sun_colormap,1,image->colors,image->file);
        for (i=0; i < image->colors; i++)
          image->colormap[i].green=UpScale(sun_colormap[i]);
        (void) ReadData((char *) sun_colormap,1,image->colors,image->file);
        for (i=0; i < image->colors; i++)
          image->colormap[i].blue=UpScale(sun_colormap[i]);
        free((char *) sun_colormap);
        break;
      }
      case RMT_RAW:
      {
        unsigned char
          *sun_colormap;

        /*
          Read SUN raster colormap.
        */
        sun_colormap=(unsigned char *)
          malloc(sun_header.maplength*sizeof(unsigned char));
        if (sun_colormap == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData((char *) sun_colormap,1,(unsigned int)
          sun_header.maplength,image->file);
        free((char *) sun_colormap);
        break;
      }
      default:
        PrematureExit("Colormap type is not supported",image);
    }
    sun_data=(unsigned char *) malloc(sun_header.length*sizeof(unsigned char));
    if (sun_data == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    status=ReadData((char *) sun_data,1,(unsigned int) sun_header.length,
      image->file);
    if ((status == False) && (sun_header.type != RT_ENCODED))
      PrematureExit("Unable to read image data",image);
    sun_pixels=sun_data;
    if (sun_header.type == RT_ENCODED)
      {
        unsigned int
          height;

        /*
          Read run-length encoded raster pixels.
        */
        height=(unsigned int) sun_header.height;
        bytes_per_line=2*(sun_header.width*sun_header.depth+15)/16;
        sun_pixels=(unsigned char *)
          malloc(bytes_per_line*height*sizeof(unsigned char));
        if (sun_pixels == (unsigned char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) SUNDecodeImage(sun_data,sun_pixels,bytes_per_line,height);
        free((char *) sun_data);
      }
    /*
      Initialize image structure.
    */
    image->matte=(sun_header.depth == 32);
    image->class=(sun_header.depth < 24 ? PseudoClass : DirectClass);
    image->columns=(unsigned int) sun_header.width;
    image->rows=(unsigned int) sun_header.height;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert SUN raster image to runlength-encoded packets.
    */
    p=sun_pixels;
    q=image->pixels;
    if (sun_header.depth == 1)
      for (y=0; y < image->rows; y++)
      {
        /*
          Convert bitmap scanline to runlength-encoded color packets.
        */
        for (x=0; x < (image->columns >> 3); x++)
        {
          for (bit=7; bit >= 0; bit--)
          {
            q->index=((*p) & (0x01 << bit) ? 0x00 : 0x01);
            q->length=0;
            q++;
          }
          p++;
        }
        if ((image->columns % 8) != 0)
          {
            for (bit=7; bit >= (8-(image->columns % 8)); bit--)
            {
              q->index=((*p) & (0x01 << bit) ? 0x00 : 0x01);
              q->length=0;
              q++;
            }
            p++;
          }
        if ((((image->columns/8)+(image->columns % 8 ? 1 : 0)) % 2) != 0)
          p++;
        if (image->previous == (Image *) NULL)
          ProgressMonitor(LoadImageText,y,image->rows);
      }
    else
      if (image->class == PseudoClass)
        for (y=0; y < image->rows; y++)
        {
          /*
            Convert PseudoColor scanline to runlength-encoded color packets.
          */
          for (x=0; x < image->columns; x++)
          {
            q->index=(*p++);
            q->length=0;
            q++;
          }
          if ((image->columns % 2) != 0)
            p++;
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      else
        for (y=0; y < image->rows; y++)
        {
          /*
            Convert DirectColor scanline to runlength-encoded color packets.
          */
          for (x=0; x < image->columns; x++)
          {
            q->index=0;
            if (image->matte)
              q->index=UpScale(*p++);
            if (sun_header.type == RT_STANDARD)
              {
                q->blue=UpScale(*p++);
                q->green=UpScale(*p++);
                q->red=UpScale(*p++);
              }
            else
              {
                q->red=UpScale(*p++);
                q->green=UpScale(*p++);
                q->blue=UpScale(*p++);
              }
            if (image->colors != 0)
              {
                q->red=image->colormap[q->red].red;
                q->green=image->colormap[q->green].green;
                q->blue=image->colormap[q->blue].blue;
              }
            q->length=0;
            q++;
          }
          if (((image->columns % 2) != 0) && (image->matte == False))
            p++;
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
    free((char *) sun_pixels);
    if (image->class == PseudoClass)
      {
        SyncImage(image);
        CompressColormap(image);
      }
    CompressImage(image);
    /*
      Proceed to next image.
    */
    sun_header.magic=MSBFirstReadLong(image->file);
    if (sun_header.magic == 0x59a66a95)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (sun_header.magic == 0x59a66a95);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d T G A I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadTGAImage reads a Truevision TGA image file and returns it.
%  It allocates the memory necessary for the new Image structure and returns
%  a pointer to the new image.
%
%  The format of the ReadTGAImage routine is:
%
%      image=ReadTGAImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadTGAImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadTGAImage(const ImageInfo *image_info)
{
#define TGAColormap 1
#define TGARGB 2
#define TGAMonochrome 3
#define TGARLEColormap  9
#define TGARLERGB  10
#define TGARLEMonochrome  11

  typedef struct _TGAHeader
  {
    unsigned char
      id_length,
      colormap_type,
      image_type;

    unsigned short
      colormap_index,
      colormap_length;

    unsigned char
      colormap_size;

    unsigned short
      x_origin,
      y_origin,
      width,
      height;

    unsigned char
      pixel_size,
      attributes;
  } TGAHeader;

  Image
    *image;

  Quantum
    blue,
    green,
    red;

  register int
    i,
    x,
    y;

  register RunlengthPacket
    *q;

  TGAHeader
    tga_header;

  unsigned char
    j,
    k,
    runlength;

  unsigned int
    base,
    flag,
    offset,
    real,
    skip,
    status;

  unsigned short
    index;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read TGA header information.
  */
  status=ReadData((char *) &tga_header.id_length,1,1,image->file);
  tga_header.colormap_type=fgetc(image->file);
  tga_header.image_type=fgetc(image->file);
  do
  {
    if ((status == False) || (tga_header.image_type == 0) ||
        (tga_header.image_type > 11))
      PrematureExit("Not a TGA image file",image);
    tga_header.colormap_index=LSBFirstReadShort(image->file);
    tga_header.colormap_length=LSBFirstReadShort(image->file);
    tga_header.colormap_size=fgetc(image->file);
    tga_header.x_origin=LSBFirstReadShort(image->file);
    tga_header.y_origin=LSBFirstReadShort(image->file);
    tga_header.width=LSBFirstReadShort(image->file);
    tga_header.height=LSBFirstReadShort(image->file);
    tga_header.pixel_size=fgetc(image->file);
    tga_header.attributes=fgetc(image->file);
    /*
      Initialize image structure.
    */
    image->matte=tga_header.pixel_size == 32;
    image->columns=tga_header.width;
    image->rows=tga_header.height;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    if (tga_header.id_length != 0)
      {
        /*
          TGA image comment.
        */
        image->comments=(char *)
          malloc((tga_header.id_length+1)*sizeof(char));
        if (image->comments == (char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData(image->comments,1,tga_header.id_length,image->file);
        image->comments[tga_header.id_length]='\0';
      }
    red=0;
    green=0;
    blue=0;
    if (tga_header.colormap_type != 0)
      {
        /*
          Read TGA raster colormap.
        */
        if ((tga_header.image_type == TGARLEColormap) ||
            (tga_header.image_type == TGARLERGB))
          image->class=PseudoClass;
        image->colors=tga_header.colormap_length;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        if (image->colormap == (ColorPacket *) NULL)
          PrematureExit("Unable to allocate memory",image);
        for (i=0; i < image->colors; i++)
        {
          switch (tga_header.colormap_size)
          {
            case 8:
            default:
            {
              /*
                Gray scale.
              */
              red=UpScale(fgetc(image->file));
              green=red;
              blue=red;
              break;
            }
            case 15:
            case 16:
            {
              /*
                5 bits each of red green and blue.
              */
              j=fgetc(image->file);
              k=fgetc(image->file);
              red=(Quantum) ((MaxRGB*((int) (k & 0x7c) >> 2))/31);
              green=(Quantum)
                ((MaxRGB*(((int) (k & 0x03) << 3)+((int) (j & 0xe0) >> 5)))/31);
              blue=(Quantum) ((MaxRGB*((int) (j & 0x1f)))/31);
              break;
            }
            case 32:
            case 24:
            {
              /*
                8 bits each of blue, green and red.
              */
              blue=UpScale(fgetc(image->file));
              green=UpScale(fgetc(image->file));
              red=UpScale(fgetc(image->file));
              break;
            }
          }
          image->colormap[i].red=red;
          image->colormap[i].green=green;
          image->colormap[i].blue=blue;
        }
      }
    /*
      Convert TGA pixels to runlength-encoded packets.
    */
    base=0;
    flag=0;
    index=0;
    skip=False;
    real=0;
    runlength=0;
    offset=0;
    q=image->pixels;
    for (i=0; i < image->packets; i++)
    {
      q->red=0;
      q->green=0;
      q->blue=0;
      q->index=0;
      q->length=0;
      q++;
    }
    for (y=0; y < image->rows; y++)
    {
      real=offset;
      if (((unsigned char) (tga_header.attributes & 0x20) >> 5) == 0)
        real=image->rows-real-1;
      q=image->pixels+(real*image->columns);
      for (x=0; x < image->columns; x++)
      {
        if ((tga_header.image_type == TGARLEColormap) ||
            (tga_header.image_type == TGARLERGB) ||
            (tga_header.image_type == TGARLEMonochrome))
          if (runlength != 0)
            {
              runlength--;
              skip=flag != 0;
            }
          else
            {
              status=ReadData((char *) &runlength,1,1,image->file);
              if (status == False)
                PrematureExit("Unable to read image data",image);
              flag=runlength & 0x80;
              if (flag != 0)
                runlength-=128;
              skip=False;
            }
        if (!skip)
          switch (tga_header.pixel_size)
          {
            case 8:
            default:
            {
              /*
                Gray scale.
              */
              index=fgetc(image->file);
              if (tga_header.colormap_type == 0)
                {
                  red=(Quantum) UpScale(index);
                  green=(Quantum) UpScale(index);
                  blue=(Quantum) UpScale(index);
                }
              else
                {
                  red=image->colormap[index].red;
                  green=image->colormap[index].green;
                  blue=image->colormap[index].blue;
                }
              break;
            }
            case 15:
            case 16:
            {
              /*
                5 bits each of red green and blue.
              */
              j=fgetc(image->file);
              k=fgetc(image->file);
              red=(Quantum) ((MaxRGB*((int) (k & 0x7c) >> 2))/31);
              green=(Quantum)
                ((MaxRGB*(((int) (k & 0x03) << 3)+((int) (j & 0xe0) >> 5)))/31);
              blue=(Quantum) ((MaxRGB*((int) (j & 0x1f)))/31);
              index=((unsigned short) k << 8)+j;
              break;
            }
            case 24:
            case 32:
            {
              /*
                8 bits each of blue green and red.
              */
              blue=UpScale(fgetc(image->file));
              green=UpScale(fgetc(image->file));
              red=UpScale(fgetc(image->file));
              if (tga_header.pixel_size == 32)
                index=Opaque-UpScale(fgetc(image->file));
              break;
            }
          }
        if (status == False)
          PrematureExit("Unable to read image data",image);
        q->red=red;
        q->green=green;
        q->blue=blue;
        q->index=index;
        q->length=0;
        q++;
      }
      if (((unsigned char) (tga_header.attributes & 0xc0) >> 6) == 4)
        offset+=4;
      else
        if (((unsigned char) (tga_header.attributes & 0xc0) >> 6) == 2)
          offset+=2;
        else
          offset++;
      if (offset >= image->rows)
        {
          base++;
          offset=base;
        }
      if (feof(image->file))
        break;
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    (void) IsGrayImage(image);
    if (image->class == PseudoClass)
      SyncImage(image);
    CompressImage(image);
    /*
      Proceed to next image.
    */
    status=ReadData((char *) &tga_header.id_length,1,1,image->file);
    tga_header.colormap_type=fgetc(image->file);
    tga_header.image_type=fgetc(image->file);
    status&=((tga_header.image_type != 0) && (tga_header.image_type <= 11));
    if (status == True)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while (status == True);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d T E X T I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadTEXTImage reads a text file and returns it as an image.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadTEXTImage routine is:
%
%      image=ReadTEXTImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadTEXTImage returns a pointer to the image after
%      reading. A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadTEXTImage(const ImageInfo *image_info)
{
  AnnotateInfo
    annotate_info;

  char
    *background_color,
    *foreground_color,
    geometry[MaxTextExtent],
    text[MaxTextExtent],
    *text_status;

  Display
    *display;

  Image
    *image;

  int
    offset,
    x,
    y;

  MonitorHandler
    handler;

  register int
    i;

  register RunlengthPacket
    *p;

  RunlengthPacket
    background;

  unsigned int
    height,
    width;

  XColor
    color;

  XrmDatabase
    resource_database;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,"r");
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Initialize Image structure.
  */
  (void) XParseGeometry(TextPageGeometry,&x,&y,&width,&height);
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  else
    {
      char
        density[MaxTextExtent];

      float
        dx_resolution,
        dy_resolution,
        x_resolution,
        y_resolution;

      int
        count;

      /*
        Determine bounding box.
      */
      dx_resolution=72.0;
      dy_resolution=72.0;
      x_resolution=72.0;
      (void) strcpy(density,PSDensityGeometry);
      count=sscanf(density,"%fx%f",&x_resolution,&y_resolution);
      if (image_info->density != (char *) NULL)
        count=sscanf(image_info->density,"%fx%f",&x_resolution,&y_resolution);
      if (count != 2)
        y_resolution=x_resolution;
      width=(unsigned int) (((width*x_resolution)/dx_resolution)+0.5);
      height=(unsigned int) (((height*y_resolution)/dy_resolution)+0.5);
    }
  image->columns=width;
  image->rows=height;
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Initialize text image to background color.
  */
  display=XOpenDisplay(image_info->server_name);
  if (display != (Display *) NULL)
    XSetErrorHandler(XError);
  resource_database=XGetResourceDatabase(display,client_name);
  background_color=XGetResourceInstance(resource_database,client_name,
    "background",DefaultTextBackground);
  foreground_color=XGetResourceInstance(resource_database,client_name,
    "foreground",DefaultTextForeground);
  if (display != (Display *) NULL)
    XCloseDisplay(display);
  /*
    Initialize image annotation info.
  */
  GetAnnotateInfo(&annotate_info);
  annotate_info.server_name=image_info->server_name;
  annotate_info.font=image_info->font;
  annotate_info.text=text;
  annotate_info.geometry=geometry;
  annotate_info.pen=foreground_color;
  /*
    Initialize text image to background color.
  */
  (void) XQueryColorDatabase(background_color,&color);
  background.red=XDownScale(color.red);
  background.green=XDownScale(color.green);
  background.blue=XDownScale(color.blue);
  background.index=0;
  p=image->pixels;
  for (i=0; i < image->packets; i++)
    *p++=background;
  if (image_info->texture != (char *) NULL)
    TextureImage(image,image_info->texture);
  /*
    Annotate the text image.
  */
  offset=0;
  for ( ; ; )
  {
    /*
      Annotate image with text.
    */
    text_status=fgets(text,MaxTextExtent,image->file);
    if (text_status == (char *) NULL)
      break;
    handler=SetMonitorHandler((MonitorHandler) NULL);
    if (Extent(annotate_info.text) > 0)
      annotate_info.text[Extent(annotate_info.text)-1]='\0';
    (void) sprintf(annotate_info.geometry,"%+d%+d",x,y+offset);
    AnnotateImage(image,&annotate_info);
    offset+=annotate_info.pointsize;
    (void) SetMonitorHandler(handler);
    if (image->previous == (Image *) NULL)
      ProgressMonitor(LoadImageText,y+offset,image->rows);
    if (((y << 1)+offset+annotate_info.pointsize) < image->rows)
      continue;
    /*
      Page is full-- allocate next image structure.
    */
    handler=SetMonitorHandler((MonitorHandler) NULL);
    CompressImage(image);
    if (image_info->texture == (char *) NULL)
      {
        QuantizeInfo
          quantize_info;

        GetQuantizeInfo(&quantize_info);
        QuantizeImage(&quantize_info,image);
      }
    image->orphan=True;
    image->next=CloneImage(image,image->columns,image->rows,False);
    image->orphan=False;
    (void) SetMonitorHandler(handler);
    if (image->next == (Image *) NULL)
      {
        Warning("Unable to annotate image","Memory allocation error");
        break;
      }
    (void) strcpy(image->next->filename,image_info->filename);
    image->next->file=image->file;
    image->next->filesize=image->filesize;
    image->next->scene=image->scene+1;
    image->next->previous=image;
    image=image->next;
    ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
      (unsigned int) image->filesize);
    /*
      Initialize text image to background color.
    */
    p=image->pixels;
    for (i=0; i < image->packets; i++)
      *p++=background;
    if (image_info->texture != (char *) NULL)
      {
        handler=SetMonitorHandler((MonitorHandler) NULL);
        TextureImage(image,image_info->texture);
        (void) SetMonitorHandler(handler);
      }
    offset=0;
  }
  CompressImage(image);
  if (image_info->texture == (char *) NULL)
    {
      QuantizeInfo
        quantize_info;

     GetQuantizeInfo(&quantize_info);
     QuantizeImage(&quantize_info,image);
   }
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

#ifdef HasTIFF

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d T I F F I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadTIFFImage reads a Tagged image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadTIFFImage routine is:
%
%      image=ReadTIFFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadTIFFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/

static void TIFFWarningMessage(const char *module,const char *format,
  va_list warning)
{
  char
    message[MaxTextExtent];

  register char
    *p;

  p=message;
  if (module != (char *) NULL)
    {
      (void) sprintf(p,"%s: ",module);
      p+=Extent(message);
    }
  vsprintf(p,format,warning);
  (void) strcat(p,".");
  Warning(message,(char *) NULL);
}

static Image *ReadTIFFImage(const ImageInfo *image_info)
{
  char
    *text;

  Image
    *image;

  int
    range;

  Quantum
    blue,
    green,
    red;

  register int
    i,
    x,
    y;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  TIFF
    *tiff;

  uint16
    extra_samples,
    *sample_info;

  unsigned char
    *scanline;

  unsigned int
    height,
    method,
    packets,
    status,
    width;

  unsigned short
    bits_per_sample,
    index,
    interlace,
    max_sample_value,
    min_sample_value,
    pages,
    photometric,
    samples_per_pixel,
    units,
    value;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  if ((image->file == stdin) || image->pipe)
    {
      FILE
        *file;

      int
        c;

      /*
        Copy standard input or pipe to temporary file.
      */
      TemporaryFilename(image_info->filename);
      file=fopen(image_info->filename,WriteBinaryType);
      if (file == (FILE *) NULL)
        PrematureExit("Unable to write file",image);
      c=fgetc(image->file);
      while (c != EOF)
      {
        (void) putc(c,file);
        c=fgetc(image->file);
      }
      (void) fclose(file);
      (void) strcpy(image->filename,image_info->filename);
      image->temporary=True;
    }
  CloseImage(image);
  TIFFSetErrorHandler(TIFFWarningMessage);
  TIFFSetWarningHandler(TIFFWarningMessage);
  tiff=TIFFOpen(image->filename,ReadBinaryType);
  if (tiff == (TIFF *) NULL)
    PrematureExit("Unable to open file",image);
  if (image_info->subrange != 0)
    while (image->scene < image_info->subimage)
    {
      /*
        Skip to next image.
      */
      image->scene++;
      status=TIFFReadDirectory(tiff);
      if (status == False)
        PrematureExit("Unable to read subimage",image);
    }
  do
  {
    if (image_info->verbose)
      TIFFPrintDirectory(tiff,stderr,False);
    TIFFGetField(tiff,TIFFTAG_IMAGEWIDTH,&width);
    TIFFGetField(tiff,TIFFTAG_IMAGELENGTH,&height);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_PLANARCONFIG,&interlace);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_BITSPERSAMPLE,&bits_per_sample);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_MINSAMPLEVALUE,&min_sample_value);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_MAXSAMPLEVALUE,&max_sample_value);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_PHOTOMETRIC,&photometric);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLESPERPIXEL,&samples_per_pixel);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_RESOLUTIONUNIT,&units);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_XRESOLUTION,&image->x_resolution);
    TIFFGetFieldDefaulted(tiff,TIFFTAG_YRESOLUTION,&image->y_resolution);
    /*
      Allocate memory for the image and pixel buffer.
    */
    image->columns=width;
    image->rows=height;
    if (units == RESUNIT_INCH)
      image->units=PixelsPerInchResolution;
    if (units == RESUNIT_CENTIMETER)
      image->units=PixelsPerCentimeterResolution;
    image->depth=bits_per_sample;
    if (bits_per_sample < 8)
      image->depth=8;
    image->packets=0;
    packets=Max((image->columns*image->rows+4) >> 3,1);
    if (bits_per_sample == 1)
      packets=Max((image->columns*image->rows+8) >> 4,1);
    image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      {
        TIFFClose(tiff);
        PrematureExit("Unable to allocate memory",image);
      }
    TIFFGetFieldDefaulted(tiff,TIFFTAG_PAGENUMBER,&value,&pages);
    image->scene=value;
    text=(char *) NULL;
    TIFFGetField(tiff,TIFFTAG_PAGENAME,&text);
    if (text != (char *) NULL)
      {
        image->label=(char *)
          malloc((unsigned int) (Extent(text)+1)*sizeof(char));
        if (image->label == (char *) NULL)
          {
            TIFFClose(tiff);
            PrematureExit("Unable to allocate memory",image);
          }
        (void) strcpy(image->label,text);
      }
    text=(char *) NULL;
    TIFFGetField(tiff,TIFFTAG_IMAGEDESCRIPTION,&text);
    if (text != (char *) NULL)
      {
        image->comments=(char *)
          malloc((unsigned int) (Extent(text)+1)*sizeof(char));
        if (image->comments == (char *) NULL)
          {
            TIFFClose(tiff);
            PrematureExit("Unable to allocate memory",image);
          }
        (void) strcpy(image->comments,text);
      }
    range=max_sample_value-min_sample_value;
    if (range < 0)
      range=max_sample_value;
    q=image->pixels;
    q->length=MaxRunlength;
    method=0;
    if ((samples_per_pixel > 1) || TIFFIsTiled(tiff))
      {
        method=2;
        if ((samples_per_pixel >= 3) && (photometric == PHOTOMETRIC_RGB) &&
            (interlace == PLANARCONFIG_CONTIG))
          method=1;
      }
    switch (method)
    {
      case 0:
      {
        Quantum
          *quantum_scanline;

        register Quantum
          *r;

        /*
          Convert TIFF image to PseudoClass MIFF image.
        */
        image->class=PseudoClass;
        image->colors=range+1;
        if (bits_per_sample > QuantumDepth)
          image->colors=MaxRGB+1;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        quantum_scanline=(Quantum *) malloc(width*sizeof(Quantum));
        scanline=(unsigned char *) malloc(TIFFScanlineSize(tiff)+1);
        if ((image->colormap == (ColorPacket *) NULL) ||
            (quantum_scanline == (Quantum *) NULL) ||
            (scanline == (unsigned char *) NULL))
          {
            TIFFClose(tiff);
            PrematureExit("Unable to allocate memory",image);
          }
        /*
          Create colormap.
        */
        switch (photometric)
        {
          case PHOTOMETRIC_MINISBLACK:
          {
            for (i=0; i < image->colors; i++)
            {
              image->colormap[i].red=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].green=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].blue=(MaxRGB*i)/(image->colors-1);
            }
            break;
          }
          case PHOTOMETRIC_MINISWHITE:
          {
            unsigned int
              colors;

            colors=image->colors;
            for (i=0; i < image->colors; i++)
            {
              image->colormap[colors-i-1].red=(MaxRGB*i)/(image->colors-1);
              image->colormap[colors-i-1].green=(MaxRGB*i)/(image->colors-1);
              image->colormap[colors-i-1].blue=(MaxRGB*i)/(image->colors-1);
            }
            break;
          }
          case PHOTOMETRIC_PALETTE:
          {
            long
              range;

            unsigned short
              *blue_colormap,
              *green_colormap,
              *red_colormap;

            TIFFGetField(tiff,TIFFTAG_COLORMAP,&red_colormap,&green_colormap,
              &blue_colormap);
            range=256L;  /* might be old style 8-bit colormap */
            for (i=0; i < image->colors; i++)
              if ((red_colormap[i] >= 256) || (green_colormap[i] >= 256) ||
                  (blue_colormap[i] >= 256))
                {
                  range=65535L;
                  break;
                }
            for (i=0; i < image->colors; i++)
            {
              image->colormap[i].red=(Quantum)
                ((long) (MaxRGB*red_colormap[i])/range);
              image->colormap[i].green=(Quantum)
                ((long) (MaxRGB*green_colormap[i])/range);
              image->colormap[i].blue=(Quantum)
                ((long) (MaxRGB*blue_colormap[i])/range);
            }
            break;
          }
          default:
            break;
        }
        /*
          Convert image to PseudoClass runlength-encoded packets.
        */
        for (y=0; y < image->rows; y++)
        {
          TIFFReadScanline(tiff,(char *) scanline,y,0);
          p=scanline;
          r=quantum_scanline;
          switch (bits_per_sample)
          {
            case 1:
            {
              register int
                bit;

              for (x=0; x < ((int) width-7); x+=8)
              {
                for (bit=7; bit >= 0; bit--)
                  *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                p++;
              }
              if ((width % 8) != 0)
                {
                  for (bit=7; bit >= (8-(width % 8)); bit--)
                    *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
                  p++;
                }
              break;
            }
            case 2:
            {
              for (x=0; x < ((int) width-3); x+=4)
              {
                *r++=(*p >> 6) & 0x3;
                *r++=(*p >> 4) & 0x3;
                *r++=(*p >> 2) & 0x3;
                *r++=(*p) & 0x3;
                p++;
              }
              if ((width % 4) != 0)
                {
                  for (i=3; i >= (4-(width % 4)); i--)
                    *r++=(*p >> (i*2)) & 0x03;
                  p++;
                }
              break;
            }
            case 4:
            {
              for (x=0; x < ((int) width-1); x+=2)
              {
                *r++=(*p >> 4) & 0xf;
                *r++=(*p) & 0xf;
                p++;
              }
              if ((width % 2) != 0)
                *r++=(*p++ >> 4) & 0xf;
              break;
            }
            case 8:
            {
              for (x=0; x < width; x++)
                *r++=(*p++);
              break;
            }
            case 16:
            {
              for (x=0; x < image->columns; x++)
              {
                ReadQuantum(*r,p);
                r++;
              }
              break;
            }
            default:
              break;
          }
          /*
            Transfer image scanline.
          */
          r=quantum_scanline;
          for (x=0; x < image->columns; x++)
          {
            index=(*r++);
            if ((index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      {
                        free((char *) scanline);
                        free((char *) quantum_scanline);
                        TIFFClose(tiff);
                        PrematureExit("Unable to allocate memory",image);
                      }
                    q=image->pixels+image->packets-1;
                  }
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        free((char *) scanline);
        free((char *) quantum_scanline);
        if (image->class == PseudoClass)
          {
            SyncImage(image);
            CompressColormap(image);
          }
        break;
      }
      case 1:
      {
        /*
          Convert TIFF image to DirectClass MIFF image.
        */
        scanline=(unsigned char *) malloc((TIFFScanlineSize(tiff) << 1)+1);
        if (scanline == (unsigned char *) NULL)
          {
            TIFFClose(tiff);
            PrematureExit("Unable to allocate memory",image);
          }
        TIFFGetFieldDefaulted(tiff,TIFFTAG_EXTRASAMPLES,&extra_samples,
          &sample_info);
        image->matte=(extra_samples == 1) &&
          ((*sample_info == EXTRASAMPLE_ASSOCALPHA) ||
           (*sample_info == EXTRASAMPLE_UNASSALPHA));
        for (y=0; y < image->rows; y++)
        {
          TIFFReadScanline(tiff,(char *) scanline,y,0);
          if (bits_per_sample == 4)
            {
              register unsigned char
                *r;

              width=TIFFScanlineSize(tiff);
              p=scanline+width-1;
              r=scanline+(width << 1)-1;
              for (x=0; x < (int) width; x++)
              {
                *r--=((*p) & 0xf) << 4;
                *r--=((*p >> 4) & 0xf) << 4;
                p--;
              }
            }
          p=scanline;
          for (x=0; x < image->columns; x++)
          {
            ReadQuantum(red,p);
            ReadQuantum(green,p);
            ReadQuantum(blue,p);
            index=0;
            if (samples_per_pixel == 4)
              ReadQuantum(index,p);
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                (index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      {
                        TIFFClose(tiff);
                        free((char *) scanline);
                        PrematureExit("Unable to allocate memory",image);
                      }
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=index;
                q->length=0;
              }
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
        free((char *) scanline);
        break;
      }
      case 2:
      default:
      {
        register uint32
          *p,
          *pixels;

        /*
          Convert TIFF image to DirectClass MIFF image.
        */
        TIFFGetFieldDefaulted(tiff,TIFFTAG_EXTRASAMPLES,&extra_samples,
          &sample_info);
        image->matte=
          ((extra_samples == 1) && (sample_info[0] == EXTRASAMPLE_ASSOCALPHA));
        pixels=(uint32 *)
          malloc((image->columns*image->rows+image->columns)*sizeof(uint32));
        if (pixels == (uint32 *) NULL)
          {
            TIFFClose(tiff);
            PrematureExit("Unable to allocate memory",image);
          }
        status=TIFFReadRGBAImage(tiff,image->columns,image->rows,pixels,0);
        if (status == False)
          {
            free((char *) pixels);
            TIFFClose(tiff);
            PrematureExit("Unable to read image",image);
          }
        /*
          Convert image to DirectClass runlength-encoded packets.
        */
        for (y=image->rows-1; y >= 0; y--)
        {
          p=pixels+y*image->columns;
          for (x=0; x < image->columns; x++)
          {
            red=UpScale(TIFFGetR(*p));
            green=UpScale(TIFFGetG(*p));
            blue=UpScale(TIFFGetB(*p));
            index=image->matte ? UpScale(TIFFGetA(*p)) : 0;
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                (index == q->index) && ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      {
                        free((char *) pixels);
                        TIFFClose(tiff);
                        PrematureExit("Unable to allocate memory",image);
                      }
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=index;
                q->length=0;
              }
            p++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,image->rows-y-1,image->rows);
        }
        free((char *) pixels);
        (void) IsPseudoClass(image);
        break;
      }
    }
    image->pixels=(RunlengthPacket *)
      realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
    /*
      Proceed to next image.
    */
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    status=TIFFReadDirectory(tiff);
    if (status == True)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,image->scene-1,image->scene);
      }
  } while (status == True);
  TIFFClose(tiff);
  if (image->temporary)
    (void) remove(image_info->filename);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  return(image);
}
#else
static Image *ReadTIFFImage(const ImageInfo *image_info)
{
  Warning("TIFF library is not available",image_info->filename);
  return(ReadMIFFImage(image_info));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d T I L E I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadTILEImage tiles a texture on an image.  It allocates the
%  memory necessary for the new Image structure and returns a pointer to the
%  new image.
%
%  The format of the ReadTILEImage routine is:
%
%      image=ReadTILEImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadTILEImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadTILEImage(ImageInfo *image_info)
{
  Image
    *image,
    *tiled_image;

  int
    x,
    y;

  unsigned int
    height,
    width;

  tiled_image=ReadImage(image_info);
  if (tiled_image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  /*
    Initialize Image structure.
  */
  tiled_image->orphan=True;
  image=CloneImage(tiled_image,width,height,False);
  tiled_image->orphan=False;
  if (image == (Image *) NULL)
    PrematureExit("Unable to allocate memory",tiled_image);
  (void) strcpy(image->filename,image_info->filename);
  /*
    Tile texture onto image.
  */
  for (y=0; y < image->rows; y+=tiled_image->rows)
  {
    for (x=0; x < image->columns; x+=tiled_image->columns)
      CompositeImage(image,ReplaceCompositeOp,tiled_image,x,y);
    ProgressMonitor(LoadImageText,y,image->columns);
  }
  DestroyImage(tiled_image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d U Y V Y I m a g e                                                  %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadUYVYImage reads an image in the UYVY (16bit/pixel) format
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadUYVYImage routine is:
%
%      image=ReadUYVYImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadUYVYImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadUYVYImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    x,
    y;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *uyvy_pixels;

  unsigned int
    height,
    width;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  (void) strcpy(image->filename,image_info->filename);
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image)
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x=0;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  for (i=0; i < x; i++)
    (void) fgetc(image->file);
  /*
    Read data.
  */
  image->columns=width;
  image->rows=height;
  image->packets=image->columns*image->rows;
  uyvy_pixels=(unsigned char *)
    malloc((2*width*height)*sizeof(unsigned char));
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if ((uyvy_pixels == (unsigned char *) NULL) ||
      (image->pixels == (RunlengthPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  (void) ReadData((char *) uyvy_pixels,1,2*width*height,image->file);
  /*
    Accumulate UYVY, then unpack into two pixels.
  */
  p=uyvy_pixels;
  q=image->pixels;
  for (i=0; i < (image->packets >> 1); i++)
  {
    q->red=UpScale(p[1]);
    q->green=UpScale(p[0]);
    q->blue=UpScale(p[2]);
    q->index=0;
    q->length=0;
    q++;
    q->red=UpScale(p[3]);
    q->green=UpScale(p[0]);
    q->blue=UpScale(p[2]);
    q->index=0;
    q->length=0;
    q++;
    p+=4;
    if (QuantumTick(i,image))
      ProgressMonitor(LoadImageText,i,image->packets >> 1);
  }
  free((char *) uyvy_pixels);
  TransformRGBImage(image,YCbCrColorspace);
  CompressImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d V I C A R I m a g e                                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadVICARImage reads a VICAR image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadVICARImage routine is:
%
%      image=ReadVICARImage(image_info)
%
%  A description of each parameter follows:
%
%    o image: Function ReadVICARImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o filename: Specifies the name of the image to read.
%
%
*/
static Image *ReadVICARImage(const ImageInfo *image_info)
{
  char
    keyword[MaxTextExtent],
    value[MaxTextExtent];

  Image
    *image;

  int
    c,
    y;

  long
    count;

  register int
    i,
    x;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *vicar_pixels;

  unsigned int
    header_length,
    status,
    value_expected;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Decode image header.
  */
  c=fgetc(image->file);
  count=1;
  if (c == EOF)
    {
      DestroyImage(image);
      return((Image *) NULL);
    }
  header_length=0;
  while (isgraph(c) && ((image->columns*image->rows) == 0))
  {
    if (!isalnum(c))
      {
        c=fgetc(image->file);
        count++;
      }
    else
      {
        register char
          *p;

        /*
          Determine a keyword and its value.
        */
        p=keyword;
        do
        {
          if ((p-keyword) < (MaxTextExtent-1))
            *p++=(char) c;
          c=fgetc(image->file);
          count++;
        } while (isalnum(c) || (c == '_'));
        *p='\0';
        value_expected=False;
        while (isspace(c) || (c == '='))
        {
          if (c == '=')
            value_expected=True;
          c=fgetc(image->file);
          count++;
        }
        if (value_expected == False)
          continue;
        p=value;
        while (isalnum(c))
        {
          if ((p-value) < (MaxTextExtent-1))
            *p++=(char) c;
          c=fgetc(image->file);
          count++;
        }
        *p='\0';
        /*
          Assign a value to the specified keyword.
        */
        if (strcmp(keyword,"LABEL_RECORDS") == 0)
          header_length=(unsigned int) atoi(value);
        if (strcmp(keyword,"LBLSIZE") == 0)
          header_length=(unsigned int) atoi(value);
        if (strcmp(keyword,"RECORD_BYTES") == 0)
          image->columns=(unsigned int) atoi(value);
        if (strcmp(keyword,"NS") == 0)
          image->columns=(unsigned int) atoi(value);
        if (strcmp(keyword,"LINES") == 0)
          image->rows=(unsigned int) atoi(value);
        if (strcmp(keyword,"NL") == 0)
          image->rows=(unsigned int) atoi(value);
      }
    while (isspace(c))
    {
      c=fgetc(image->file);
      count++;
    }
  }
  /*
    Read the rest of the header.
  */
  while (count < header_length)
  {
    c=fgetc(image->file);
    count++;
  }
  /*
    Verify that required image information is defined.
  */
  if ((image->columns*image->rows) == 0)
    PrematureExit("image size is zero",image);
  /*
    Create linear colormap.
  */
  image->class=PseudoClass;
  image->colors=256;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if (image->colormap == (ColorPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  for (i=0; i < image->colors; i++)
  {
    image->colormap[i].red=(Quantum) UpScale(i);
    image->colormap[i].green=(Quantum) UpScale(i);
    image->colormap[i].blue=(Quantum) UpScale(i);
  }
  /*
    Initialize image structure.
  */
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  vicar_pixels=(unsigned char *) malloc(image->packets*sizeof(unsigned char));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (vicar_pixels == (unsigned char *) NULL))
    PrematureExit("Unable to read image data",image);
  /*
    Convert VICAR pixels to runlength-encoded packets.
  */
  status=ReadData((char *) vicar_pixels,1,image->packets,image->file);
  if (status == False)
    PrematureExit("Insufficient image data in file",image);
  /*
    Convert VICAR pixels to runlength-encoded packets.
  */
  p=vicar_pixels;
  q=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    for (x=0; x < image->columns; x++)
    {
      q->index=(unsigned short) *p;
      q->length=0;
      p++;
      q++;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  free((char *) vicar_pixels);
  SyncImage(image);
  CompressColormap(image);
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d V I D I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadVIDImage reads one of more images and creates a Visual Image
%  Directory file.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadVIDImage routine is:
%
%      image=ReadVIDImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadVIDImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadVIDImage(const ImageInfo *image_info)
{
#define ClientName  "montage"

  char
    *commands[5],
    **filelist,
    **list,
    *resource_value;

  Display
    *display;

  Image
    *image,
    *montage_image,
    *next_image;

  ImageInfo
    local_info;

  int
    number_files;

  MonitorHandler
    handler;

  register int
    i;

  XMontageInfo
    montage_info;

  XResourceInfo
    resource_info;

  XrmDatabase
    resource_database;

  /*
    Expand the filename.
  */
  list=(char **) malloc(sizeof(char *));
  if (list == (char **) NULL)
    {
      Warning("Memory allocation error",(char *) NULL);
      return((Image *) NULL);
    }
  list[0]=(char *) malloc(Extent(image_info->filename)+1);
  if (list[0] == (char *) NULL)
    {
      Warning("Memory allocation error",(char *) NULL);
      return((Image *) NULL);
    }
  (void) strcpy(list[0],image_info->filename);
  number_files=1;
  filelist=list;
  ExpandFilenames(&number_files,&filelist);
  if (number_files == 0)
    {
      Warning("VID translation failed",image_info->filename);
      return((Image *) NULL);
    }
  /*
    Get user defaults from X resource database.
  */
  XGetMontageInfo(&montage_info);
  display=XOpenDisplay(image_info->server_name);
  if (display != (Display *) NULL)
    XSetErrorHandler(XError);
  resource_database=XGetResourceDatabase(display,client_name);
  XGetResourceInfo(resource_database,client_name,&resource_info);
  resource_info.background_color=XGetResourceInstance(resource_database,
    client_name,"background",DefaultTileBackground);
  resource_info.foreground_color=XGetResourceInstance(resource_database,
    client_name,"foreground",DefaultTileForeground);
  montage_info.frame=
    XGetResourceInstance(resource_database,client_name,"frame",(char *) NULL);
  resource_info.image_geometry=XGetResourceInstance(resource_database,
    client_name,"imageGeometry",DefaultTileGeometry);
  resource_info.matte_color=XGetResourceInstance(resource_database,
    client_name,"mattecolor",DefaultTileMatte);
  resource_value=XGetResourceClass(resource_database,client_name,
    "pointsize",DefaultPointSize);
  montage_info.pointsize=atoi(resource_value);
  resource_value=
    XGetResourceClass(resource_database,client_name,"shadow","True");
  montage_info.shadow=IsTrue(resource_value);
  montage_info.texture=XGetResourceClass(resource_database,client_name,
    "texture","granite:");
  montage_info.tile=XGetResourceClass(resource_database,client_name,"tile",
    montage_info.tile);
  if (display != (Display *) NULL)
    XCloseDisplay(display);
  /*
    Read each image and convert them to a tile.
  */
  image=(Image *) NULL;
  local_info=(*image_info);
  commands[0]=client_name;
  commands[1]="-label";
  commands[2]=DefaultTileLabel;
  for (i=0; i < number_files; i++)
  {
    handler=SetMonitorHandler((MonitorHandler) NULL);
    if (local_info.size == (char *) NULL)
      local_info.size=resource_info.image_geometry;
    local_info.filename=filelist[i];
    *local_info.magick='\0';
    next_image=ReadImage(&local_info);
    free((char *) filelist[i]);
    if (next_image != (Image *) NULL)
      {
        MogrifyImages(&local_info,3,commands,&next_image);
        if (image == (Image *) NULL)
          image=next_image;
        else
          {
            image->next=next_image;
            image->next->previous=image;
            image=image->next;
          }
      }
    (void) SetMonitorHandler(handler);
    ProgressMonitor(LoadImageText,i,number_files);
  }
  free((char *) filelist);
  if (image == (Image *) NULL)
    {
      Warning("VID translation failed",image_info->filename);
      return((Image *) NULL);
    }
  while (image->previous != (Image *) NULL)
    image=image->previous;
  /*
    Create the visual image directory.
  */
  (void) strcpy(montage_info.filename,image_info->filename);
  montage_image=XMontageImages(&resource_info,&montage_info,image);
  if (montage_image == (Image *) NULL)
    {
      Warning("VID translation failed",image_info->filename);
      return((Image *) NULL);
    }
  DestroyImages(image);
  free((char *) list[0]);
  free((char *) list);
  return(montage_image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d V I F F I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadVIFFImage reads a Khoros Visualization image file and returns
%  it.  It allocates the memory necessary for the new Image structure and
%  returns a pointer to the new image.
%
%  The format of the ReadVIFFImage routine is:
%
%      image=ReadVIFFImage(image_info)
%
%  A description of each parameter follows:
%
%    o image: Function ReadVIFFImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or if
%      the image cannot be read.
%
%    o filename: Specifies the name of the image to read.
%
%
*/
static Image *ReadVIFFImage(const ImageInfo *image_info)
{
#define VFF_CM_genericRGB  15
#define VFF_CM_ntscRGB  1
#define VFF_CM_NONE  0
#define VFF_DEP_DECORDER  0x4
#define VFF_DEP_NSORDER  0x8
#define VFF_DES_RAW  0
#define VFF_LOC_IMPLICIT  1
#define VFF_MAPTYP_NONE  0
#define VFF_MAPTYP_1_BYTE  1
#define VFF_MS_NONE  0
#define VFF_MS_ONEPERBAND  1
#define VFF_MS_SHARED  3
#define VFF_TYP_BIT  0
#define VFF_TYP_1_BYTE  1
#define VFF_TYP_2_BYTE  2
#define VFF_TYP_4_BYTE  4

  typedef struct _ViffHeader
  {
    unsigned char
      identifier,
      file_type,
      release,
      version,
      machine_dependency,
      reserve[3];

    char
      comment[512];

    unsigned long
      rows,
      columns,
      subrows;

    long
      x_offset,
      y_offset;

    float
      x_pixel_size,
      y_pixel_size;

    unsigned long
      location_type,
      location_dimension,
      number_of_images,
      number_data_bands,
      data_storage_type,
      data_encode_scheme,
      map_scheme,
      map_storage_type,
      map_rows,
      map_columns,
      map_subrows,
      map_enable,
      maps_per_cycle,
      color_space_model;
  } ViffHeader;

  Image
    *image;

  register int
    bit,
    i,
    x,
    y;

  register Quantum
    *p;

  register RunlengthPacket
    *q;

  unsigned char
    buffer[7],
    *viff_pixels;

  unsigned int
    bytes_per_pixel,
    status;

  unsigned long
    packets;

  ViffHeader
    viff_header;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read VIFF header (1024 bytes).
  */
  status=ReadData((char *) &viff_header.identifier,1,1,image->file);
  do
  {
    /*
      Verify VIFF identifier.
    */
    if ((status == False) || ((unsigned char) viff_header.identifier != 0xab))
      PrematureExit("Not a VIFF raster",image);
    /*
      Initialize VIFF image.
    */
    (void) ReadData((char *) buffer,1,7,image->file);
    viff_header.file_type=buffer[0];
    viff_header.release=buffer[1];
    viff_header.version=buffer[2];
    viff_header.machine_dependency=buffer[3];
    (void) ReadData((char *) viff_header.comment,1,512,image->file);
    viff_header.comment[511]='\0';
    if (Extent(viff_header.comment) > 4)
      {
        image->comments=(char *)
          malloc((unsigned int) (Extent(viff_header.comment)+1)*sizeof(char));
        if (image->comments == (char *) NULL)
          PrematureExit("Unable to allocate memory",image);
        (void) strcpy(image->comments,viff_header.comment);
      }
    if ((viff_header.machine_dependency == VFF_DEP_DECORDER) ||
        (viff_header.machine_dependency == VFF_DEP_NSORDER))
      {
        viff_header.rows=LSBFirstReadLong(image->file);
        viff_header.columns=LSBFirstReadLong(image->file);
        viff_header.subrows=LSBFirstReadLong(image->file);
        viff_header.x_offset=(long) LSBFirstReadLong(image->file);
        viff_header.y_offset=(long) LSBFirstReadLong(image->file);
        viff_header.x_pixel_size=(float) LSBFirstReadLong(image->file);
        viff_header.y_pixel_size=(float) LSBFirstReadLong(image->file);
        viff_header.location_type=LSBFirstReadLong(image->file);
        viff_header.location_dimension=LSBFirstReadLong(image->file);
        viff_header.number_of_images=LSBFirstReadLong(image->file);
        viff_header.number_data_bands=LSBFirstReadLong(image->file);
        viff_header.data_storage_type=LSBFirstReadLong(image->file);
        viff_header.data_encode_scheme=LSBFirstReadLong(image->file);
        viff_header.map_scheme=LSBFirstReadLong(image->file);
        viff_header.map_storage_type=LSBFirstReadLong(image->file);
        viff_header.map_rows=LSBFirstReadLong(image->file);
        viff_header.map_columns=LSBFirstReadLong(image->file);
        viff_header.map_subrows=LSBFirstReadLong(image->file);
        viff_header.map_enable=LSBFirstReadLong(image->file);
        viff_header.maps_per_cycle=LSBFirstReadLong(image->file);
        viff_header.color_space_model=LSBFirstReadLong(image->file);
      }
    else
      {
        viff_header.rows=MSBFirstReadLong(image->file);
        viff_header.columns=MSBFirstReadLong(image->file);
        viff_header.subrows=MSBFirstReadLong(image->file);
        viff_header.x_offset=(long) MSBFirstReadLong(image->file);
        viff_header.y_offset=(long) MSBFirstReadLong(image->file);
        viff_header.x_pixel_size=(float) MSBFirstReadLong(image->file);
        viff_header.y_pixel_size=(float) MSBFirstReadLong(image->file);
        viff_header.location_type=MSBFirstReadLong(image->file);
        viff_header.location_dimension=MSBFirstReadLong(image->file);
        viff_header.number_of_images=MSBFirstReadLong(image->file);
        viff_header.number_data_bands=MSBFirstReadLong(image->file);
        viff_header.data_storage_type=MSBFirstReadLong(image->file);
        viff_header.data_encode_scheme=MSBFirstReadLong(image->file);
        viff_header.map_scheme=MSBFirstReadLong(image->file);
        viff_header.map_storage_type=MSBFirstReadLong(image->file);
        viff_header.map_rows=MSBFirstReadLong(image->file);
        viff_header.map_columns=MSBFirstReadLong(image->file);
        viff_header.map_subrows=MSBFirstReadLong(image->file);
        viff_header.map_enable=MSBFirstReadLong(image->file);
        viff_header.maps_per_cycle=MSBFirstReadLong(image->file);
        viff_header.color_space_model=MSBFirstReadLong(image->file);
      }
    for (i=0; i < 420; i++)
      (void) fgetc(image->file);
    /*
      Verify that we can read this VIFF image.
    */
    if ((viff_header.columns*viff_header.rows) == 0)
      PrematureExit("Image column or row size is not supported",image);
    if ((viff_header.data_storage_type != VFF_TYP_BIT) &&
        (viff_header.data_storage_type != VFF_TYP_1_BYTE) &&
        (viff_header.data_storage_type != VFF_TYP_2_BYTE) &&
        (viff_header.data_storage_type != VFF_TYP_4_BYTE))
      PrematureExit("Data storage type is not supported",image);
    if (viff_header.data_encode_scheme != VFF_DES_RAW)
      PrematureExit("Data encoding scheme is not supported",image);
    if ((viff_header.map_storage_type != VFF_MAPTYP_NONE) &&
        (viff_header.map_storage_type != VFF_MAPTYP_1_BYTE))
      PrematureExit("Map storage type is not supported",image);
    if ((viff_header.color_space_model != VFF_CM_NONE) &&
        (viff_header.color_space_model != VFF_CM_ntscRGB) &&
        (viff_header.color_space_model != VFF_CM_genericRGB))
      PrematureExit("Colorspace model is not supported",image);
    if (viff_header.location_type != VFF_LOC_IMPLICIT)
      {
        Warning("Location type is not supported",image->filename);
        DestroyImages(image);
        return((Image *) NULL);
      }
    if (viff_header.number_of_images != 1)
      PrematureExit("Number of images is not supported",image);
    switch (viff_header.map_scheme)
    {
      case VFF_MS_NONE:
      {
        if (viff_header.number_data_bands < 3)
          {
            /*
              Create linear color ramp.
            */
            if (viff_header.data_storage_type == VFF_TYP_BIT)
              image->colors=2;
            else
              image->colors=1 << (viff_header.number_data_bands*QuantumDepth);
            image->colormap=(ColorPacket *)
              malloc(image->colors*sizeof(ColorPacket));
            if (image->colormap == (ColorPacket *) NULL)
              PrematureExit("Unable to allocate memory",image);
            for (i=0; i < image->colors; i++)
            {
              image->colormap[i].red=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].green=(MaxRGB*i)/(image->colors-1);
              image->colormap[i].blue=(MaxRGB*i)/(image->colors-1);
            }
          }
        break;
      }
      case VFF_MS_ONEPERBAND:
      case VFF_MS_SHARED:
      {
        unsigned char
          *viff_colormap;

        /*
          Read VIFF raster colormap.
        */
        image->colors=(unsigned int) viff_header.map_columns;
        image->colormap=(ColorPacket *)
          malloc(image->colors*sizeof(ColorPacket));
        viff_colormap=(unsigned char *)
          malloc(image->colors*sizeof(unsigned char));
        if ((image->colormap == (ColorPacket *) NULL) ||
            (viff_colormap == (unsigned char *) NULL))
          PrematureExit("Unable to allocate memory",image);
        (void) ReadData((char *) viff_colormap,1,image->colors,image->file);
        for (i=0; i < image->colors; i++)
        {
          image->colormap[i].red=UpScale(viff_colormap[i]);
          image->colormap[i].green=UpScale(viff_colormap[i]);
          image->colormap[i].blue=UpScale(viff_colormap[i]);
        }
        if (viff_header.map_rows > 1)
          {
            (void) ReadData((char *) viff_colormap,1,image->colors,image->file);
            for (i=0; i < image->colors; i++)
              image->colormap[i].green=UpScale(viff_colormap[i]);
          }
        if (viff_header.map_rows > 2)
          {
            (void) ReadData((char *) viff_colormap,1,image->colors,image->file);
            for (i=0; i < image->colors; i++)
              image->colormap[i].blue=UpScale(viff_colormap[i]);
          }
        free((char *) viff_colormap);
        break;
      }
      default:
        PrematureExit("Colormap type is not supported",image);
    }
    /*
      Allocate VIFF pixels.
    */
    bytes_per_pixel=1;
    if (viff_header.data_storage_type == VFF_TYP_2_BYTE)
      bytes_per_pixel=2;
    if (viff_header.data_storage_type == VFF_TYP_4_BYTE)
      bytes_per_pixel=4;
    if (viff_header.data_storage_type == VFF_TYP_BIT)
      packets=((viff_header.columns+7) >> 3)*viff_header.rows;
    else
      packets=
        viff_header.columns*viff_header.rows*viff_header.number_data_bands;
    viff_pixels=(unsigned char *)
      malloc(bytes_per_pixel*packets*sizeof(Quantum));
    if (viff_pixels == (unsigned char *) NULL)
      PrematureExit("Unable to allocate memory",image);
    (void) ReadData((char *) viff_pixels,bytes_per_pixel,(unsigned int) packets,
      image->file);
    switch (viff_header.data_storage_type)
    {
      int
        max_value,
        min_value,
        value;

      register Quantum
        *q;

      unsigned long
        scale_factor;

      case VFF_TYP_1_BYTE:
      {
        register unsigned char
          *p;

        if (QuantumDepth == 8)
          break;
        /*
          Scale integer pixels to [0..MaxRGB].
        */
        p=viff_pixels;
        q=(Quantum *) viff_pixels;
        p+=packets-1;
        q+=packets-1;
        for (i=0; i < packets; i++)
        {
          value=UpScale(*p);
          *q=(Quantum) value;
          p--;
          q--;
        }
        break;
      }
      case VFF_TYP_2_BYTE:
      {
        register short int
          *p;

        /*
          Ensure the header byte-order is most-significant byte first.
        */
        if ((viff_header.machine_dependency == VFF_DEP_DECORDER) ||
            (viff_header.machine_dependency == VFF_DEP_NSORDER))
          MSBFirstOrderShort((char *) &viff_header,
            (unsigned int) (bytes_per_pixel*packets));
        /*
          Determine scale factor.
        */
        p=(short int *) viff_pixels;
        max_value=(*p);
        min_value=(*p);
        for (i=0; i < packets; i++)
        {
          if (*p > max_value)
            max_value=(*p);
          else
            if (*p < min_value)
              min_value=(*p);
          p++;
        }
        if ((min_value == 0) && (max_value == 0))
          scale_factor=0;
        else
          if (min_value == max_value)
            {
              scale_factor=UpShift(MaxRGB)/min_value;
              min_value=0;
            }
          else
            scale_factor=UpShift(MaxRGB)/(max_value-min_value);
        /*
          Scale integer pixels to [0..MaxRGB].
        */
        p=(short int *) viff_pixels;
        q=(Quantum *) viff_pixels;
        for (i=0; i < packets; i++)
        {
          value=DownShift((*p-min_value)*scale_factor);
          if (value > MaxRGB)
            value=MaxRGB;
          else
            if (value < 0)
              value=0;
          *q=(Quantum) value;
          p++;
          q++;
        }
        break;
      }
      case VFF_TYP_4_BYTE:
      {
        register int
          *p;

        /*
          Ensure the header byte-order is most-significant byte first.
        */
        if ((viff_header.machine_dependency == VFF_DEP_DECORDER) ||
            (viff_header.machine_dependency == VFF_DEP_NSORDER))
          MSBFirstOrderLong((char *) &viff_header,
            (unsigned int) (bytes_per_pixel*packets));
        /*
          Determine scale factor.
        */
        p=(int *) viff_pixels;
        max_value=(*p);
        min_value=(*p);
        for (i=0; i < packets; i++)
        {
          if (*p > max_value)
            max_value=(*p);
          else
            if (*p < min_value)
              min_value=(*p);
          p++;
        }
        if ((min_value == 0) && (max_value == 0))
          scale_factor=0;
        else
          if (min_value == max_value)
            {
              scale_factor=UpShift(MaxRGB)/min_value;
              min_value=0;
            }
          else
            scale_factor=UpShift(MaxRGB)/(max_value-min_value);
        /*
          Scale integer pixels to [0..MaxRGB].
        */
        p=(int *) viff_pixels;
        q=(Quantum *) viff_pixels;
        for (i=0; i < packets; i++)
        {
          value=DownShift((*p-min_value)*scale_factor);
          if (value > MaxRGB)
            value=MaxRGB;
          else
            if (value < 0)
              value=0;
          *q=(unsigned char) value;
          p++;
          q++;
        }
        break;
      }
    }
    /*
      Initialize image structure.
    */
    image->matte=(viff_header.number_data_bands == 4);
    image->class=
      (viff_header.number_data_bands < 3 ? PseudoClass : DirectClass);
    image->columns=(unsigned int) viff_header.rows;
    image->rows=(unsigned int) viff_header.columns;
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *)
      malloc(image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      PrematureExit("Unable to allocate memory",image);
    /*
      Convert VIFF raster image to runlength-encoded packets.
    */
    p=(Quantum *) viff_pixels;
    q=image->pixels;
    if (viff_header.data_storage_type == VFF_TYP_BIT)
      {
        unsigned int
          polarity;

        /*
          Convert bitmap scanline to runlength-encoded color packets.
        */
        polarity=(viff_header.machine_dependency == VFF_DEP_DECORDER) ||
          (viff_header.machine_dependency == VFF_DEP_NSORDER);
        for (y=0; y < image->rows; y++)
        {
          /*
            Convert bitmap scanline to runlength-encoded color packets.
          */
          for (x=0; x < (image->columns >> 3); x++)
          {
            for (bit=0; bit < 8; bit++)
            {
              q->index=
                ((*p) & (0x01 << bit) ? (int) polarity : (int) !polarity);
              q->length=0;
              q++;
            }
            p++;
          }
          if ((image->columns % 8) != 0)
            {
              for (bit=0; bit < (image->columns % 8); bit++)
              {
                q->index=
                  ((*p) & (0x01 << bit) ? (int) polarity : (int) !polarity);
                q->length=0;
                q++;
              }
              p++;
            }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      }
    else
      if (image->class == PseudoClass)
        for (y=0; y < image->rows; y++)
        {
          /*
            Convert PseudoColor scanline to runlength-encoded color packets.
          */
          for (x=0; x < image->columns; x++)
          {
            q->index=(*p++);
            q->length=0;
            q++;
          }
          if (image->previous == (Image *) NULL)
            ProgressMonitor(LoadImageText,y,image->rows);
        }
      else
        {
          unsigned long
            offset;

          /*
            Convert DirectColor scanline to runlength-encoded color packets.
          */
          offset=image->columns*image->rows;
          for (y=0; y < image->rows; y++)
          {
            for (x=0; x < image->columns; x++)
            {
              q->red=(*p);
              q->green=(*(p+offset));
              q->blue=(*(p+offset*2));
              if (image->colors != 0)
                {
                  q->red=image->colormap[q->red].red;
                  q->green=image->colormap[q->green].green;
                  q->blue=image->colormap[q->blue].blue;
                }
              q->index=(unsigned short) (image->matte ? (*(p+offset*3)) : 0);
              q->length=0;
              p++;
              q++;
            }
            if (image->previous == (Image *) NULL)
              ProgressMonitor(LoadImageText,y,image->rows);
          }
        }
    free((char *) viff_pixels);
    if (image->class == PseudoClass)
      {
        SyncImage(image);
        CompressColormap(image);
      }
    CompressImage(image);
    /*
      Proceed to next image.
    */
    status=ReadData((char *) &viff_header.identifier,1,1,image->file);
    if ((status == True) && (viff_header.identifier == 0xab))
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImageText,(unsigned int) ftell(image->file),
          (unsigned int) image->filesize);
      }
  } while ((status == True) && (viff_header.identifier == 0xab));
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d X I m a g e                                                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Procedure ReadXImage reads an image from an X window.
%
%  The format of the ReadXImage routine is:
%
%      image=ReadXImage(image_info,frame,borders,screen,descend)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%    o frame: Specifies whether to include the window manager frame with the
%      image.
%
%    o borders: Specifies whether borders pixels are to be saved with
%      the image.
%
%    o screen: Specifies whether the GetImage request used to obtain the image
%      should be done on the root window, rather than directly on the specified
%      window.
%
%    o descend: If this option is zero, check to see if the WM_COLORMAP_WINDOWS
%      property is set or if XListInstalledColormaps returns more than one
%      colormap.  If so, the image is obtained by descending the window
%      hierarchy and reading each subwindow and its colormap.
%
%
*/
Export Image *ReadXImage(ImageInfo *image_info,const unsigned int frame,
  const unsigned int borders,const unsigned int screen,unsigned int descend)
{
  Colormap
    *colormaps;

  Display
    *display;

  Image
    *image;

  int
    status,
    x;

  RectangleInfo
    crop_info;

  Window
    *children,
    client,
    prior_target,
    root,
    target;

  XTextProperty
    window_name;

  /*
    Open X server connection.
  */
  display=XOpenDisplay(image_info->server_name);
  if (display == (Display *) NULL)
    {
      Warning("Unable to connect to X server",
        XDisplayName(image_info->server_name));
      return((Image *) NULL);
    }
  /*
    Set our forgiving error handler.
  */
  XSetErrorHandler(XError);
  /*
    Select target window.
  */
  crop_info.x=0;
  crop_info.y=0;
  crop_info.width=0;
  crop_info.height=0;
  root=XRootWindow(display,XDefaultScreen(display));
  target=(Window) NULL;
  if ((image_info->filename != (char *) NULL) &&
      (*image_info->filename != '\0'))
    if (Latin1Compare(image_info->filename,"root") == 0)
      target=root;
    else
      {
        /*
          Select window by ID or name.
        */
        if (isdigit(*image_info->filename))
          target=XWindowByID(display,root,(Window) strtol(image_info->filename,
            (char **) NULL,0));
        if (target == (Window) NULL)
          target=XWindowByName(display,root,image_info->filename);
        if (target == (Window) NULL)
          Warning("No window with specified id exists",image_info->filename);
      }

  /*
    If target window is not defined, interactively select one.
  */
  prior_target=target;
  if (target == (Window) NULL)
    target=XSelectWindow(display,&crop_info);
  client=target;   /* obsolete */
  if (target != root)
    {
      unsigned int
        d;

      status=XGetGeometry(display,target,&root,&x,&x,&d,&d,&d,&d);
      if (status != 0)
        {
          for ( ; ; )
          {
            Window
              parent;

            /*
              Find window manager frame.
            */
            status=XQueryTree(display,target,&root,&parent,&children,&d);
            if (status && (children != (Window *) NULL))
              XFree((char *) children);
            if (!status || (parent == (Window) NULL) || (parent == root))
              break;
            target=parent;
          }
          /*
            Get client window.
          */
          client=XClientWindow(display,target);
          if (!frame)
            target=client;
          if (!frame && prior_target)
            target=prior_target;
          XRaiseWindow(display,target);
          XDelay(display,SuspendTime << 4);
        }
    }
  if (screen)
    {
      int
        y;

      Window
        child;

      XWindowAttributes
        window_attributes;

      /*
        Obtain window image directly from screen.
      */
      status=XGetWindowAttributes(display,target,&window_attributes);
      if (status == False)
        {
          Warning("Unable to read X window attributes",image_info->filename);
          XCloseDisplay(display);
          return((Image *) NULL);
        }
      XTranslateCoordinates(display,target,root,0,0,&x,&y,&child);
      crop_info.x=x;
      crop_info.y=y;
      crop_info.width=window_attributes.width;
      crop_info.height=window_attributes.height;
      if (borders)
        {
          /*
            Include border in image.
          */
          crop_info.x-=window_attributes.border_width;
          crop_info.y-=window_attributes.border_width;
          crop_info.width+=window_attributes.border_width << 1;
          crop_info.height+=window_attributes.border_width << 1;
        }
      target=root;
    }
  if (descend)
    {
      int
        number_colormaps,
        number_windows;

      /*
        If WM_COLORMAP_WINDOWS property is set or multiple colormaps, descend.
      */
      descend=False;
      number_windows=0;
      status=XGetWMColormapWindows(display,target,&children,&number_windows);
      if ((status == True) && (number_windows > 0))
        {
          descend=True;
          XFree ((char *) children);
        }
      colormaps=XListInstalledColormaps(display,target,&number_colormaps);
      if (number_colormaps > 0)
        {
          if (number_colormaps > 1)
            descend=True;
          XFree((char *) colormaps);
        }
    }
  /*
    Alert the user not to alter the screen.
  */
  XBell(display,0);
  /*
    Get image by window id.
  */
  XGrabServer(display);
  image=XGetWindowImage(display,target,borders,descend);
  XUngrabServer(display);
  if (image == (Image *) NULL)
    Warning("Unable to read X window image",image_info->filename);
  else
    {
      (void) strcpy(image->filename,image_info->filename);
      if ((crop_info.width != 0) && (crop_info.height != 0))
        {
          Image
            *cropped_image;

          /*
            Crop image as defined by the cropping rectangle.
          */
          cropped_image=CropImage(image,&crop_info);
          if (cropped_image != (Image *) NULL)
            {
              DestroyImage(image);
              image=cropped_image;
            }
        }
      status=XGetWMName(display,target,&window_name);
      if (status == True)
        {
          if ((image_info->filename != (char *) NULL) &&
              (*image_info->filename == '\0'))
            {
              /*
                Initialize image filename.
              */
              (void) strncpy(image->filename,(char *) window_name.value,
                (int) window_name.nitems);
              image->filename[window_name.nitems]='\0';
            }
          XFree((void *) window_name.value);
        }
    }
  /*
    Alert the user we're done.
  */
  XBell(display,0);
  XBell(display,0);
  CompressImage(image);
  XCloseDisplay(display);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d X B M I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadXBMImage reads an X11 bitmap image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadXBMImage routine is:
%
%      image=ReadXBMImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadXBMImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static int XBMInteger(FILE *file,short int *hex_digits)
{
  int
    c,
    flag,
    value;

  value=0;
  flag=0;
  for ( ; ; )
  {
    c=fgetc(file);
    if (c == EOF)
      {
        value=(-1);
        break;
      }
    c&=0xff;
    if (isxdigit(c))
      {
        value=(value << 4)+hex_digits[c];
        flag++;
        continue;
      }
    if ((hex_digits[c]) < 0 && flag)
      break;
  }
  return(value);
}

static Image *ReadXBMImage(const ImageInfo *image_info)
{
  char
    buffer[MaxTextExtent],
    name[MaxTextExtent];

  Image
    *image;

  register int
    x,
    y;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  short int
    hex_digits[256];

  unsigned char
    bit,
    *data;

  unsigned int
    byte,
    bytes_per_line,
    packets,
    padding,
    value,
    version;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read X bitmap header.
  */
  while (fgets(buffer,MaxTextExtent,image->file) != (char *) NULL)
    if (sscanf(buffer,"#define %s %u",name,&image->columns) == 2)
      if ((strlen(name) >= 6) && (strcmp(name+strlen(name)-6,"_width") == 0))
          break;
  while (fgets(buffer,MaxTextExtent,image->file) != (char *) NULL)
    if (sscanf(buffer,"#define %s %u",name,&image->rows) == 2)
      if ((strlen(name) >= 7) && (strcmp(name+strlen(name)-7,"_height") == 0))
          break;
  /*
    Scan until hex digits.
  */
  version=11;
  while (fgets(buffer,MaxTextExtent,image->file) != (char *) NULL)
  {
    if (sscanf(buffer,"static short %s = {",name) == 1)
      version=10;
    else
      if (sscanf(buffer,"static unsigned char %s = {",name) == 1)
        version=11;
      else
        if (sscanf(buffer,"static char %s = {",name) == 1)
          version=11;
        else
          continue;
    p=(unsigned char *) strrchr(name,'_');
    if (p == (unsigned char *) NULL)
      p=(unsigned char *) name;
    else
      p++;
    if (strcmp("bits[]",(char *) p) == 0)
      break;
  }
  if ((image->columns == 0) || (image->rows == 0) || feof(image->file))
    PrematureExit("XBM file is not in the correct format",image);
  /*
    Initialize image structure.
  */
  image->packets=image->columns*image->rows;
  image->class=PseudoClass;
  image->colors=2;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  padding=0;
  if ((image->columns % 16) && ((image->columns % 16) < 9)  && (version == 10))
    padding=1;
  bytes_per_line=(image->columns+7)/8+padding;
  packets=bytes_per_line*image->rows;
  data=(unsigned char *) malloc(packets*sizeof(unsigned char *));
  if ((image->colormap == (ColorPacket *) NULL) ||
      (image->pixels == (RunlengthPacket *) NULL) ||
      (data == (unsigned char *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Initialize colormap.
  */
  image->colormap[0].red=0;
  image->colormap[0].green=0;
  image->colormap[0].blue=0;
  image->colormap[1].red=MaxRGB;
  image->colormap[1].green=MaxRGB;
  image->colormap[1].blue=MaxRGB;
  /*
    Initialize hex values.
  */
  hex_digits['0']=0;
  hex_digits['1']=1;
  hex_digits['2']=2;
  hex_digits['3']=3;
  hex_digits['4']=4;
  hex_digits['5']=5;
  hex_digits['6']=6;
  hex_digits['7']=7;
  hex_digits['8']=8;
  hex_digits['9']=9;
  hex_digits['A']=10;
  hex_digits['B']=11;
  hex_digits['C']=12;
  hex_digits['D']=13;
  hex_digits['E']=14;
  hex_digits['F']=15;
  hex_digits['a']=10;
  hex_digits['b']=11;
  hex_digits['c']=12;
  hex_digits['d']=13;
  hex_digits['e']=14;
  hex_digits['f']=15;
  hex_digits['x']=0;
  hex_digits[' ']=(-1);
  hex_digits[',']=(-1);
  hex_digits['}']=(-1);
  hex_digits['\n']=(-1);
  hex_digits['\t']=(-1);
  /*
    Read hex image data.
  */
  p=data;
  if (version == 10)
    for (x=0; x < packets; (x+=2))
    {
      value=XBMInteger(image->file,hex_digits);
      *p++=value;
      if (!padding || ((x+2) % bytes_per_line))
        *p++=value >> 8;
    }
  else
    for (x=0; x < packets; x++)
    {
      value=XBMInteger(image->file,hex_digits);
      *p++=value;
    }
  /*
    Convert X bitmap image to runlength-encoded packets.
  */
  byte=0;
  p=data;
  q=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    bit=0;
    for (x=0; x < image->columns; x++)
    {
      if (bit == 0)
        byte=(*p++);
      q->index=byte & 0x01 ? 0 : 1;
      q->length=0;
      q++;
      bit++;
      byte>>=1;
      if (bit == 8)
        bit=0;
    }
    ProgressMonitor(LoadImageText,y,image->rows);
  }
  SyncImage(image);
  CompressImage(image);
  CloseImage(image);
  free((char *) data);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d X C I m a g e                                                      %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadXCImage creates a constant image and initializes it to the
%  X server color as specified by the filename.  It allocates the memory
%  necessary for the new Image structure and returns a pointer to the new
%  image.
%
%  The format of the ReadXCImage routine is:
%
%      image=ReadXCImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadXCImage returns a pointer to the image after
%      creating it. A null image is returned if there is a a memory shortage
%      or if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadXCImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    x,
    y;

  register int
    i;

  register RunlengthPacket
    *q;

  unsigned int
    height,
    width;

  XColor
    color;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  /*
    Initialize Image structure.
  */
  (void) strcpy(image->filename,image_info->filename);
  image->columns=width;
  image->rows=height;
  image->packets=((Max(image->columns*image->rows,1)-1)/(MaxRunlength+1))+1;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  image->class=PseudoClass;
  image->colors=1;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  if ((image->pixels == (RunlengthPacket *) NULL) ||
      (image->colormap == (ColorPacket *) NULL))
    PrematureExit("Unable to allocate memory",image);
  /*
    Initialize colormap.
  */
  (void) XQueryColorDatabase(image_info->filename,&color);
  image->colormap[0].red=XDownScale(color.red);
  image->colormap[0].green=XDownScale(color.green);
  image->colormap[0].blue=XDownScale(color.blue);
  q=image->pixels;
  for (i=0; i < image->packets; i++)
  {
    q->index=0;
    q->length=MaxRunlength;
    q++;
    if (QuantumTick(i,image))
      ProgressMonitor(LoadImageText,i,image->packets);
  }
  q--;
  q->length=image->columns*image->rows-(MaxRunlength+1)*(image->packets-1)-1;
  SyncImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d X P M I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadXPMImage reads an X11 pixmap image file and returns it.  It
%  allocates the memory necessary for the new Image structure and returns a
%  pointer to the new image.
%
%  The format of the ReadXPMImage routine is:
%
%      image=ReadXPMImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadXPMImage returns a pointer to the image after
%      creating it. A null image is returned if there is a a memory shortage
%      or if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/

static char *ParseColor(char *data)
{
#define NumberTargets  6

  static char
    *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };

  register char
     *p,
     *q,
     *r;

  register int
    i;

  for (i=0; i < NumberTargets; i++)
  {
    r=data;
    for (q=targets[i]; *r != '\0'; r++)
    {
      if (*r != *q)
        continue;
      if (!isspace(*(r-1)))
        continue;
      p=r;
      for ( ; ; )
      {
        if (*q == '\0')
          return(r);
        if (*p++ != *q++)
          break;
      }
      q=targets[i];
    }
  }
  return((char *) NULL);
}

static Image *ReadXPMImage(const ImageInfo *image_info)
{
  char
    key[MaxTextExtent],
    target[MaxTextExtent],
    **textlist,
    *xpm_buffer;

  Image
    *image;

  int
    count,
    length,
    x,
    y;

  register char
    *p,
    *q;

  register int
    i,
    j;

  register RunlengthPacket
    *r;

  unsigned int
    width;

  XColor
    color;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,"r");
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
    Read XPM file.
  */
  length=MaxTextExtent;
  xpm_buffer=(char *) malloc(length*sizeof(char));
  p=xpm_buffer;
  if (xpm_buffer != (char *) NULL)
    while (fgets(p,MaxTextExtent,image->file) != (char *) NULL)
    {
      if (*p == '#')
        if ((p == xpm_buffer) || (*(p-1) == '\n'))
          continue;
      if ((*p == '}') && (*(p+1) == ';'))
        break;
      p+=Extent(p);
      if ((p-xpm_buffer+MaxTextExtent+1) < length)
        continue;
      length<<=1;
      xpm_buffer=(char *) realloc((char *) xpm_buffer,length*sizeof(char));
      if (xpm_buffer == (char *) NULL)
        break;
      p=xpm_buffer+Extent(xpm_buffer);
    }
  if (xpm_buffer == (char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Remove comments.
  */
  for (p=xpm_buffer; *p != '\0'; p++)
  {
    if ((*p != '/') || (*(p+1) != '*'))
      continue;
    for (q=p+2; *q != '\0'; q++)
      if ((*q == '*') && (*(q+1) == '/'))
        break;
    (void) strcpy(p,q+2);
  }
  /*
    Remove unquoted characters.
  */
  i=0;
  for (p=xpm_buffer; *p != '\0'; p++)
  {
    if (*p != '"')
      continue;
    for (q=p+1; *q != '\0'; q++)
      if (*q == '"')
        break;
    (void) strncpy(xpm_buffer+i,p+1,q-p-1);
    i+=q-p-1;
    xpm_buffer[i++]='\n';
    p=q+1;
  }
  xpm_buffer[i]='\0';
  textlist=StringToList(xpm_buffer);
  free(xpm_buffer);
  if (textlist == (char **) NULL)
    PrematureExit("Unable to allocate memory",image);
  /*
    Read hints.
  */
  count=sscanf(textlist[0],"%u %u %u %u",&image->columns,&image->rows,
    &image->colors,&width);
  if ((count != 4) || (width > 2) ||
      ((image->columns*image->rows*image->colors) == 0))
    {
      for (i=0; textlist[i] != (char *) NULL; i++)
        free((char *) textlist[i]);
      free((char *) textlist);
      PrematureExit("Not a XPM image file",image);
    }
  /*
    Initialize image structure.
  */
  image->class=PseudoClass;
  image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
  image->packets=image->columns*image->rows;
  image->pixels=(RunlengthPacket *)
    malloc(image->packets*sizeof(RunlengthPacket));
  if ((image->colormap == (ColorPacket *) NULL) ||
      (image->pixels == (RunlengthPacket *) NULL))
    {
      for (i=0; textlist[i] != (char *) NULL; i++)
        free((char *) textlist[i]);
      free((char *) textlist);
      PrematureExit("Unable to allocate memory",image);
    }
  /*
    Read image colormap.
  */
  i=1;
  for (j=0; j < image->colors; j++)
  {
    p=textlist[i++];
    if (p == (char *) NULL)
      break;
    image->colormap[j].key[width]='\0';
    (void) strncpy(image->colormap[j].key,p,width);
    /*
      Parse color.
    */
    (void) strcpy(target,"gray");
    q=ParseColor(p+width);
    if (q != (char *) NULL)
      {
        while (!isspace(*q) && (*q != '\0'))
          q++;
        (void) strcpy(target,q);
        q=ParseColor(target);
        if (q != (char *) NULL)
          *q='\0';
      }
    Strip(target);
    image->colormap[j].flags=Latin1Compare(target,"none") == 0;
    if (image->colormap[j].flags)
      {
        image->class=DirectClass;
        image->matte=True;
        (void) strcpy(target,"gray");
      }
    (void) XQueryColorDatabase(target,&color);
    image->colormap[j].red=XDownScale(color.red);
    image->colormap[j].green=XDownScale(color.green);
    image->colormap[j].blue=XDownScale(color.blue);
  }
  if (j < image->colors)
    {
      for (i=0; textlist[i] != (char *) NULL; i++)
        free((char *) textlist[i]);
      free((char *) textlist);
      PrematureExit("Corrupt XPM image file",image);
    }
  /*
    Read image pixels.
  */
  j=0;
  key[width]='\0';
  r=image->pixels;
  for (y=0; y < image->rows; y++)
  {
    p=textlist[i++];
    if (p == (char *) NULL)
      break;
    for (x=0; x < image->columns; x++)
    {
      (void) strncpy(key,p,width);
      if (strcmp(key,image->colormap[j].key) != 0)
        for (j=0; j < (image->colors-1); j++)
          if (strcmp(key,image->colormap[j].key) == 0)
            break;
      r->red=image->colormap[j].red;
      r->green=image->colormap[j].green;
      r->blue=image->colormap[j].blue;
      if (image->class == PseudoClass)
        r->index=j;
      else
        if (image->colormap[j].flags)
          r->index=Transparent;
        else
          r->index=Opaque;
      r->length=0;
      r++;
      p+=width;
    }
  }
  if (y < image->rows)
    {
      for (i=0; textlist[i] != (char *) NULL; i++)
        free((char *) textlist[i]);
      free((char *) textlist);
      PrematureExit("Corrupt XPM image file",image);
    }
  /*
    Free resources.
  */
  for (i=0; textlist[i] != (char *) NULL; i++)
    free((char *) textlist[i]);
  free((char *) textlist);
  CompressImage(image);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d X W D I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadXWDImage reads an X Window System window dump image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadXWDImage routine is:
%
%      image=ReadXWDImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadXWDImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadXWDImage(const ImageInfo *image_info)
{
  Image
    *image;

  int
    status,
    x,
    y;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned long
    pixel;

  unsigned long
    lsb_first;

  unsigned int
    packets;

  unsigned short
    index;

  XColor
    *colors;

  XImage
    *ximage;

  XWDFileHeader
    header;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Open image file.
  */
  OpenImage(image_info,image,ReadBinaryType);
  if (image->file == (FILE *) NULL)
    PrematureExit("Unable to open file",image);
  /*
     Read in header information.
  */
  status=ReadData((char *) &header,sz_XWDheader,1,image->file);
  if (status == False)
    PrematureExit("Unable to read dump file header",image);
  /*
    Ensure the header byte-order is most-significant byte first.
  */
  lsb_first=1;
  if (*(char *) &lsb_first)
    MSBFirstOrderLong((char *) &header,sz_XWDheader);
  /*
    Check to see if the dump file is in the proper format.
  */
  if (header.file_version != XWD_FILE_VERSION)
    PrematureExit("XWD file format version mismatch",image);
  if (header.header_size < sz_XWDheader)
    PrematureExit("XWD header size is too small",image);
  packets=(header.header_size-sz_XWDheader);
  image->comments=(char *) malloc((packets+1)*sizeof(char));
  if (image->comments == (char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  status=ReadData((char *) image->comments,1,packets,image->file);
  image->comments[packets]='\0';
  if (status == False)
    PrematureExit("Unable to  read window name from dump file",image);
  /*
    Initialize the X image.
  */
  ximage=(XImage *) malloc(sizeof(XImage));
  if (ximage == (XImage *) NULL)
    PrematureExit("Unable to allocate memory",image);
  ximage->depth=header.pixmap_depth;
  ximage->format=header.pixmap_format;
  ximage->xoffset=header.xoffset;
  ximage->data=(char *) NULL;
  ximage->width=header.pixmap_width;
  ximage->height=header.pixmap_height;
  ximage->bitmap_pad=header.bitmap_pad;
  ximage->bytes_per_line=header.bytes_per_line;
  ximage->byte_order=header.byte_order;
  ximage->bitmap_unit=header.bitmap_unit;
  ximage->bitmap_bit_order=header.bitmap_bit_order;
  ximage->bits_per_pixel=header.bits_per_pixel;
  ximage->red_mask=header.red_mask;
  ximage->green_mask=header.green_mask;
  ximage->blue_mask=header.blue_mask;
  status=XInitImage(ximage);
  if (status == False)
    PrematureExit("Invalid XWD header",image);
  /*
    Read colormap.
  */
  colors=(XColor *) NULL;
  if (header.ncolors != 0)
    {
      XWDColor
        color;

      colors=(XColor *) malloc((unsigned int) header.ncolors*sizeof(XColor));
      if (colors == (XColor *) NULL)
        PrematureExit("Unable to allocate memory",image);
      for (i=0; i < header.ncolors; i++)
      {
        status=ReadData((char *) &color,sz_XWDColor,1,image->file);
        if (status == False)
          PrematureExit("Unable to read color map from dump file",image);
        colors[i].pixel=color.pixel;
        colors[i].red=color.red;
        colors[i].green=color.green;
        colors[i].blue=color.blue;
        colors[i].flags=color.flags;
      }
      /*
        Ensure the header byte-order is most-significant byte first.
      */
      lsb_first=1;
      if (*(char *) &lsb_first)
        for (i=0; i < header.ncolors; i++)
        {
          MSBFirstOrderLong((char *) &colors[i].pixel,sizeof(unsigned long));
          MSBFirstOrderShort((char *) &colors[i].red,3*sizeof(unsigned short));
        }
    }
  /*
    Allocate the pixel buffer.
  */
  if (ximage->format == ZPixmap)
    packets=ximage->bytes_per_line*ximage->height;
  else
    packets=ximage->bytes_per_line*ximage->height*ximage->depth;
  ximage->data=(char *) malloc(packets*sizeof(unsigned char));
  if (ximage->data == (char *) NULL)
    PrematureExit("Unable to allocate memory",image);
  status=ReadData(ximage->data,1,packets,image->file);
  if (status == False)
    PrematureExit("Unable to read dump pixmap",image);
  /*
    Convert image to MIFF format.
  */
  image->columns=ximage->width;
  image->rows=ximage->height;
  if ((colors == (XColor *) NULL) || (ximage->red_mask != 0) ||
      (ximage->green_mask != 0) || (ximage->blue_mask != 0))
    image->class=DirectClass;
  else
    image->class=PseudoClass;
  image->colors=header.ncolors;
  image->packets=0;
  packets=Max((image->columns*image->rows+4) >> 3,1);
  image->pixels=(RunlengthPacket *) malloc(packets*sizeof(RunlengthPacket));
  if (image->pixels == (RunlengthPacket *) NULL)
    PrematureExit("Unable to allocate memory",image);
  q=image->pixels;
  q->length=MaxRunlength;
  switch (image->class)
  {
    case DirectClass:
    default:
    {
      register unsigned long
        color;

      unsigned int
        blue,
        green,
        red;

      unsigned long
        blue_mask,
        blue_shift,
        green_mask,
        green_shift,
        red_mask,
        red_shift;

      /*
        Determine shift and mask for red, green, and blue.
      */
      red_mask=ximage->red_mask;
      red_shift=0;
      while ((red_mask & 0x01) == 0)
      {
        red_mask>>=1;
        red_shift++;
      }
      green_mask=ximage->green_mask;
      green_shift=0;
      while ((green_mask & 0x01) == 0)
      {
        green_mask>>=1;
        green_shift++;
      }
      blue_mask=ximage->blue_mask;
      blue_shift=0;
      while ((blue_mask & 0x01) == 0)
      {
        blue_mask>>=1;
        blue_shift++;
      }
      /*
        Convert X image to DirectClass packets.
      */
      if (image->colors != 0)
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            pixel=XGetPixel(ximage,x,y);
            index=(unsigned short) ((pixel >> red_shift) & red_mask);
            red=XDownScale(colors[index].red);
            index=(unsigned short) ((pixel >> green_shift) & green_mask);
            green=XDownScale(colors[index].green);
            index=(unsigned short) ((pixel >> blue_shift) & blue_mask);
            blue=XDownScale(colors[index].blue);
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=0;
                q->length=0;
              }
          }
          ProgressMonitor(LoadImageText,y,image->rows);
        }
      else
        for (y=0; y < image->rows; y++)
        {
          for (x=0; x < image->columns; x++)
          {
            pixel=XGetPixel(ximage,x,y);
            color=(pixel >> red_shift) & red_mask;
            red=XDownScale((color*65535L)/red_mask);
            color=(pixel >> green_shift) & green_mask;
            green=XDownScale((color*65535L)/green_mask);
            color=(pixel >> blue_shift) & blue_mask;
            blue=XDownScale((color*65535L)/blue_mask);
            if ((red == q->red) && (green == q->green) && (blue == q->blue) &&
                ((int) q->length < MaxRunlength))
              q->length++;
            else
              {
                if (image->packets != 0)
                  q++;
                image->packets++;
                if (image->packets == packets)
                  {
                    packets<<=1;
                    image->pixels=(RunlengthPacket *) realloc((char *)
                      image->pixels,packets*sizeof(RunlengthPacket));
                    if (image->pixels == (RunlengthPacket *) NULL)
                      PrematureExit("Unable to allocate memory",image);
                    q=image->pixels+image->packets-1;
                  }
                q->red=red;
                q->green=green;
                q->blue=blue;
                q->index=0;
                q->length=0;
              }
          }
          ProgressMonitor(LoadImageText,y,image->rows);
        }
      break;
    }
    case PseudoClass:
    {
      /*
        Convert X image to PseudoClass packets.
      */
      image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket));
      if (image->colormap == (ColorPacket *) NULL)
        PrematureExit("Unable to allocate memory",image);
      for (i=0; i < image->colors; i++)
      {
        image->colormap[i].red=XDownScale(colors[i].red);
        image->colormap[i].green=XDownScale(colors[i].green);
        image->colormap[i].blue=XDownScale(colors[i].blue);
      }
      for (y=0; y < image->rows; y++)
      {
        for (x=0; x < image->columns; x++)
        {
          pixel=XGetPixel(ximage,x,y);
          index=(unsigned short) pixel;
          if ((index == q->index) && ((int) q->length < MaxRunlength))
            q->length++;
          else
            {
              if (image->packets != 0)
                q++;
              image->packets++;
              if (image->packets == packets)
                {
                  packets<<=1;
                  image->pixels=(RunlengthPacket *) realloc((char *)
                    image->pixels,packets*sizeof(RunlengthPacket));
                  if (image->pixels == (RunlengthPacket *) NULL)
                    PrematureExit("Unable to allocate memory",image);
                  q=image->pixels+image->packets-1;
                }
              q->index=index;
              q->length=0;
            }
        }
        ProgressMonitor(LoadImageText,y,image->rows);
      }
      SyncImage(image);
      CompressColormap(image);
      break;
    }
  }
  image->pixels=(RunlengthPacket *)
    realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket));
  /*
    Free image and colormap.
  */
  if (header.ncolors != 0)
    free((char *) colors);
  free(ximage->data);
  free(ximage);
  CloseImage(image);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  R e a d Y U V I m a g e                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadYUVImage reads an image with digital YUV (CCIR 601 4:1:1) bytes
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadYUVImage routine is:
%
%      image=ReadYUVImage(image_info)
%
%  A description of each parameter follows:
%
%    o image:  Function ReadYUVImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
static Image *ReadYUVImage(const ImageInfo *image_info)
{
  char
    filename[MaxTextExtent];

  Image
    *image,
    *zoomed_image;

  int
    x,
    y;

  register int
    i;

  register RunlengthPacket
    *q;

  register unsigned char
    *p;

  unsigned char
    *uv_pixels,
    *y_pixels;

  unsigned int
    height,
    width;

  /*
    Allocate image structure.
  */
  zoomed_image=AllocateImage(image_info);
  if (zoomed_image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Determine width and height, e.g. 640x512.
  */
  width=512;
  height=512;
  x=0;
  if (image_info->size != (char *) NULL)
    (void) XParseGeometry(image_info->size,&x,&y,&width,&height);
  if (image_info->interlace != PartitionInterlace)
    {
      /*
        Open image file.
      */
      OpenImage(image_info,zoomed_image,ReadBinaryType);
      if (zoomed_image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",zoomed_image)
      for (i=0; i < x; i++)
        (void) fgetc(zoomed_image->file);
    }
  /*
    Read Y channel.
  */
  if (image_info->interlace == PartitionInterlace)
    {
      AppendImageFormat("Y",zoomed_image->filename);
      OpenImage(image_info,zoomed_image,ReadBinaryType);
      if (zoomed_image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",zoomed_image);
    }
  zoomed_image->columns=width >> 1;
  zoomed_image->rows=height >> 1;
  zoomed_image->packets=zoomed_image->columns*zoomed_image->rows;
  uv_pixels=(unsigned char *)
    malloc(zoomed_image->packets*sizeof(unsigned char));
  y_pixels=(unsigned char *)
    malloc(4*zoomed_image->packets*sizeof(unsigned char));
  zoomed_image->pixels=(RunlengthPacket *)
    malloc(zoomed_image->packets*sizeof(RunlengthPacket));
  if ((uv_pixels == (unsigned char *) NULL) ||
      (y_pixels == (unsigned char *) NULL) ||
      (zoomed_image->pixels == (RunlengthPacket *) NULL))
    PrematureExit("Unable to allocate memory",zoomed_image);
  (void) ReadData((char *) y_pixels,4,zoomed_image->packets,zoomed_image->file);
  /*
    Read U channel.
  */
  if (image_info->interlace == PartitionInterlace)
    {
      CloseImage(zoomed_image);
      AppendImageFormat("U",zoomed_image->filename);
      OpenImage(image_info,zoomed_image,ReadBinaryType);
      if (zoomed_image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",zoomed_image);
    }
  ProgressMonitor(LoadImageText,100,400);
  (void) ReadData((char *) uv_pixels,1,zoomed_image->packets,
    zoomed_image->file);
  p=uv_pixels;
  q=zoomed_image->pixels;
  for (i=0; i < zoomed_image->packets; i++)
  {
    q->green=UpScale(*p);
    q->index=0;
    q->length=0;
    p++;
    q++;
  }
  /*
    Read V channel.
  */
  if (image_info->interlace == PartitionInterlace)
    {
      CloseImage(zoomed_image);
      AppendImageFormat("V",zoomed_image->filename);
      OpenImage(image_info,zoomed_image,ReadBinaryType);
      if (zoomed_image->file == (FILE *) NULL)
        PrematureExit("Unable to open file",zoomed_image);
    }
  ProgressMonitor(LoadImageText,200,400);
  (void) ReadData((char *) uv_pixels,1,zoomed_image->packets,
    zoomed_image->file);
  p=uv_pixels;
  q=zoomed_image->pixels;
  for (i=0; i < zoomed_image->packets; i++)
  {
    q->blue=UpScale(*p);
    p++;
    q++;
  }
  CloseImage(zoomed_image);
  free((char *) uv_pixels);
  /*
    Scale image.
  */
  ProgressMonitor(LoadImageText,300,400);
  zoomed_image->orphan=True;
  image=MagnifyImage(zoomed_image);
  zoomed_image->orphan=False;
  DestroyImage(zoomed_image);
  if (image == (Image *) NULL)
    PrematureExit("Unable to allocate memory",image);
  p=y_pixels;
  q=image->pixels;
  for (i=0; i < (image->columns*image->rows); i++)
  {
    q->red=UpScale(*p);
    p++;
    q++;
  }
  free((char *) y_pixels);
  TransformRGBImage(image,YCbCrColorspace);
  (void) strcpy(image->filename,filename);
  CompressImage(image);
  if (image_info->interlace == PartitionInterlace)
    (void) strcpy(image->filename,image_info->filename);
  ProgressMonitor(LoadImageText,400,400);
  return(image);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d I m a g e                                                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function ReadImage reads an image and returns it.  It allocates
%  the memory necessary for the new Image structure and returns a pointer to
%  the new image.  By default, the image format is determined by its magic
%  number. To specify a particular image format, precede the filename with an
%  explicit image format name and a colon (i.e.  ps:image) or as the filename
%  suffix  (i.e. image.ps).
%
%  The format of the ReadImage routine is:
%
%      image=ReadImage(image_info)
%
%  A description of each parameter follows:
%
%    o image: Function ReadImage returns a pointer to the image after
%      reading.  A null image is returned if there is a a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
Export Image *ReadImage(ImageInfo *image_info)
{
  char
    magic_number[MaxTextExtent];

  Image
    decode_image,
    *image;

  ImageInfo
    decode_info;

  register char
    *p;

  register int
    i;

  assert(image_info != (ImageInfo *) NULL);
  assert(image_info->filename != (char *) NULL);
  SetImageInfo(image_info,False);
  decode_info=(*image_info);
  decode_image.temporary=strcmp(decode_info.magick,"TMP") == 0;
  decode_image.orphan=False;
  if ((strncmp(decode_info.magick,"FILE",4) == 0) ||
      (strncmp(decode_info.magick,"FTP",3) == 0) ||
      (strncmp(decode_info.magick,"GOPHER",6) == 0) ||
      (strncmp(decode_info.magick,"HTTP",4) == 0))
    {
      char
        command[MaxTextExtent],
        filename[MaxTextExtent];

      /*
        Retrieve image as specified with a WWW uniform resource locator.
      */
      decode_image.temporary=True;
      TemporaryFilename(filename);
      (void) sprintf(command,WWWCommand,decode_info.magick,decode_info.filename,
        filename);
      (void) SystemCommand(command);
      SetImageInfo(&decode_info,False);
      (void) strcpy(decode_info.filename,filename);
    }
  if (!decode_info.affirm || (strncmp(decode_info.magick,"SGI",3) == 0) ||
      (strncmp(decode_info.magick,"PCD",3) == 0))
    {
      /*
        Determine type from image magic number.
      */
      for (i=0 ; i < sizeof(magic_number); i++)
        magic_number[i]='\0';
      (void) strcpy(decode_image.filename,decode_info.filename);
      OpenImage(image_info,&decode_image,ReadBinaryType);
      if (decode_image.file != (FILE *) NULL)
        if ((decode_image.file == stdin) || decode_image.pipe)
          {
            FILE
              *file;

            int
              c;

            /*
              Copy standard input or pipe to temporary file.
            */
            decode_image.temporary=True;
            TemporaryFilename(decode_image.filename);
            decode_info.filename=decode_image.filename;
            file=fopen(decode_image.filename,WriteBinaryType);
            if (file == (FILE *) NULL)
              {
                Warning("Unable to write file",decode_info.filename);
                return((Image *) NULL);
              }
            c=fgetc(decode_image.file);
            while (c != EOF)
            {
              (void) putc(c,file);
              c=fgetc(decode_image.file);
            }
            (void) fclose(file);
            CloseImage(&decode_image);
            OpenImage(image_info,&decode_image,ReadBinaryType);
          }
      if (decode_image.file != (FILE *) NULL)
        {
          /*
            Read magic number.
          */
          (void) ReadData(magic_number,(unsigned int) sizeof(char),
            (unsigned int) sizeof(magic_number),decode_image.file);
          if (((unsigned char) magic_number[0] == 0xff) &&
              ((unsigned char) magic_number[1] == 0xff))
            {
              register int
                i;

              /*
                For PCD image type, skip to byte 2048.
              */
              for (i=0; i < (int) (0x800-sizeof(magic_number)); i++)
                (void) fgetc(decode_image.file);
              (void) ReadData(magic_number,(unsigned int) sizeof(char),
                (unsigned int) sizeof(magic_number),decode_image.file);
            }
          CloseImage(&decode_image);
        }
      /*
        Determine the image format.
      */
      magic_number[MaxTextExtent-1]='\0';
      if (strncmp(magic_number,"BM",2) == 0)
        (void) strcpy(decode_info.magick,"BMP");
      if (strncmp(magic_number,"IT0",3) == 0)
        (void) strcpy(decode_info.magick,"FITS");
      if (strncmp(magic_number,"\261\150\336\72",4) == 0)
        (void) strcpy(decode_info.magick,"DCX");
      if (strncmp(magic_number,"SIMPLE",6) == 0)
        (void) strcpy(decode_info.magick,"FITS");
      if (strncmp(magic_number,"GIF8",4) == 0)
        (void) strcpy(decode_info.magick,"GIF");
      if (strncmp(magic_number,"\016\003\023\001",4) == 0)
        (void) strcpy(decode_info.magick,"HDF");
      if ((strncmp(magic_number,"<HTML",5) == 0) ||
          (strncmp(magic_number,"<html",5) == 0))
        (void) strcpy(decode_info.magick,"HTML");
      if (strncmp(magic_number,"\001\332",2) == 0)
        (void) strcpy(decode_info.magick,"SGI");
      if (strncmp(magic_number,"\377\330\377",3) == 0)
        (void) strcpy(decode_info.magick,"JPEG");
      if (strncmp(magic_number,"id=ImageMagick",14) == 0)
        (void) strcpy(decode_info.magick,"MIFF");
      if (strncmp(magic_number,"\212MNG\r\n\032\n",8) == 0)
        (void) strcpy(decode_info.magick,"MNG");
      if ((magic_number[0] == 0x00) && (magic_number[1] == 0x00))
        if ((magic_number[2] == 0x01) && (magic_number[3] == (char) 0xb3))
          (void) strcpy(decode_info.magick,"MPEG");
      if (strncmp(magic_number,"PCD_",4) == 0)
        (void) strcpy(decode_info.magick,"PCD");
      if (strncmp(magic_number,"\12\2",2) == 0)
        (void) strcpy(decode_info.magick,"PCX");
      if (strncmp(magic_number,"\12\5",2) == 0)
        (void) strcpy(decode_info.magick,"PCX");
      if (strncmp(magic_number,"%!PDF",5) == 0)
        (void) strcpy(decode_info.magick,"PDF");
      if ((*magic_number == 'P') && isdigit(magic_number[1]))
        (void) strcpy(decode_info.magick,"PNM");
      if (strncmp(magic_number,"\211PNG\r\n\032\n",8) == 0)
        (void) strcpy(decode_info.magick,"PNG");
      if (strncmp(magic_number,"%!",2) == 0)
        (void) strcpy(decode_info.magick,"PS");
      if (strncmp(magic_number,"#?RADIANCE",10) == 0)
        (void) strcpy(decode_info.magick,"RAD");
      if (strncmp(magic_number,"\122\314",2) == 0)
        (void) strcpy(decode_info.magick,"RLE");
      if (strncmp(magic_number,"\131\246\152\225",4) == 0)
        (void) strcpy(decode_info.magick,"SUN");
      if ((strncmp(magic_number,"\115\115\000\052",4) == 0) ||
          (strncmp(magic_number,"\111\111\052\000",4) == 0))
        (void) strcpy(decode_info.magick,"TIFF");
      if ((strncmp(magic_number,"LBLSIZE",7) == 0) ||
          (strncmp(magic_number,"NJPL1I",6) == 0))
        (void) strcpy(decode_info.magick,"VICAR");
      if (strncmp(magic_number,"\253\1",2) == 0)
        (void) strcpy(decode_info.magick,"VIFF");
      p=strchr(magic_number,'#');
      if (p != (char *) NULL)
        if (strncmp(p,"#define",7) == 0)
          (void) strcpy(decode_info.magick,"XBM");
      if (strncmp(magic_number,"/* XPM */",9) == 0)
        (void) strcpy(decode_info.magick,"XPM");
      if ((magic_number[1] == 0x00) && (magic_number[2] == 0x00))
        if ((magic_number[5] == 0x00) && (magic_number[6] == 0x00))
          if ((magic_number[4] == 0x07) || (magic_number[7] == 0x07))
            (void) strcpy(decode_info.magick,"XWD");
    }
  /*
    Call appropriate image reader based on image type.
  */
  switch (*decode_info.magick)
  {
    case 'A':
    {
      if (strcmp(decode_info.magick,"AVS") == 0)
        {
          image=ReadAVSImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'B':
    {
      if (strcmp(decode_info.magick,"BIE") == 0)
        {
          image=ReadJBIGImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"BMP") == 0)
        {
          image=ReadBMPImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'C':
    {
      if (strcmp(decode_info.magick,"CGM") == 0)
        {
          image=ReadCGMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"CMYK") == 0)
        {
          image=ReadCMYKImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'D':
    {
      if (strncmp(decode_info.magick,"DCX",3) == 0)
        {
          image=ReadPCXImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"DIB") == 0)
        {
          image=ReadBMPImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'E':
    {
      if (strncmp(decode_info.magick,"EPS",3) == 0)
        {
          image=ReadPSImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'F':
    {
      if (strcmp(decode_info.magick,"FAX") == 0)
        {
          image=ReadFAXImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"FITS") == 0)
        {
          image=ReadFITSImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'G':
    {
      if (strncmp(decode_info.magick,"GIF",3) == 0)
        {
          image=ReadGIFImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"GRADATION") == 0)
        {
          image=ReadGRADATIONImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"GRANITE") == 0)
        {
          image=ReadLOGOImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"GRAY") == 0)
        {
          image=ReadGRAYImage(&decode_info);
          break;
        }
      if (strncmp(decode_info.magick,"G3",2) == 0)
        {
          image=ReadFAXImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'H':
    {
      if (strcmp(decode_info.magick,"H") == 0)
        {
          image=ReadLOGOImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"HDF") == 0)
        {
          image=ReadHDFImage(&decode_info);
          break;
        }
      if (strncmp(decode_info.magick,"HISTOGRAM",4) == 0)
        {
          image=ReadHISTOGRAMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"HTML") == 0)
        {
          image=ReadHTMLImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'J':
    {
      if (strcmp(decode_info.magick,"JBIG") == 0)
        {
          image=ReadJBIGImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"JPEG") == 0)
        {
          image=ReadJPEGImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'L':
    {
      if (strcmp(decode_info.magick,"LOGO") == 0)
        {
          image=ReadLOGOImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'M':
    {
      if (strcmp(decode_info.magick,"MAP") == 0)
        {
          image=ReadMAPImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"MATTE") == 0)
        {
          image=ReadMATTEImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"MNG") == 0)
        {
          image=ReadPNGImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"MONO") == 0)
        {
          image=ReadMONOImage(&decode_info);
          break;
        }
      if ((strcmp(decode_info.magick,"MPEG") == 0) ||
          (strcmp(decode_info.magick,"MPG") == 0))
        {
          image=ReadMPEGImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"MTV") == 0)
        {
          image=ReadMTVImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'N':
    {
      if (strcmp(decode_info.magick,"NETSCAPE") == 0)
        {
          image=ReadLOGOImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"NULL") == 0)
        {
          image=ReadNULLImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'P':
    {
      if ((strcmp(decode_info.magick,"P7") == 0) ||
          (strcmp(decode_info.magick,"PBM") == 0) ||
          (strcmp(decode_info.magick,"PGM") == 0) ||
          (strcmp(decode_info.magick,"PNM") == 0) ||
          (strcmp(decode_info.magick,"PPM") == 0))
        {
          image=ReadPNMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PCD") == 0)
        {
          image=ReadPCDImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PCL") == 0)
        {
          image=ReadPCLImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PCX") == 0)
        {
          image=ReadPCXImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PDF") == 0)
        {
          image=ReadPDFImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PICT") == 0)
        {
          image=ReadPICTImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PCD") == 0)
        {
          image=ReadPCDImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PLASMA") == 0)
        {
          image=ReadPLASMAImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PM") == 0)
        {
          image=ReadXPMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"PNG") == 0)
        {
          image=ReadPNGImage(&decode_info);
          break;
        }
      if (strncmp(decode_info.magick,"PS",2) == 0)
        {
          image=ReadPSImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'R':
    {
      if (strcmp(decode_info.magick,"RAD") == 0)
        {
          image=ReadRADIANCEImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"RAS") == 0)
        {
          image=ReadSUNImage(&decode_info);
          break;
        }
      if (strncmp(decode_info.magick,"RGB",3) == 0)
        {
          image=ReadRGBImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"RLA") == 0)
        {
          image=ReadRLAImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"RLE") == 0)
        {
          image=ReadRLEImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'S':
    {
      if (strcmp(decode_info.magick,"SGI") == 0)
        {
          image=ReadSGIImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"SHTML") == 0)
        {
          image=ReadHTMLImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"SUN") == 0)
        {
          image=ReadSUNImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'T':
    {
      if (strcmp(decode_info.magick,"TEXT") == 0)
        {
          image=ReadTEXTImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"TGA") == 0)
        {
          image=ReadTGAImage(&decode_info);
          break;
        }
      if (strncmp(decode_info.magick,"TIF",3) == 0)
        {
          image=ReadTIFFImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"TILE") == 0)
        {
          image=ReadTILEImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"TXT") == 0)
        {
          image=ReadTEXTImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'U':
    {
      if (strcmp(decode_info.magick,"UYVY") == 0)
        {
          image=ReadUYVYImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'V':
    {
      if (strcmp(decode_info.magick,"VICAR") == 0)
        {
          image=ReadVICARImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"VID") == 0)
        {
          if (decode_info.affirm)
            image=ReadVIDImage(&decode_info);
          else
            image=ReadMIFFImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"VIFF") == 0)
        {
          image=ReadVIFFImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'X':
    {
      if (strcmp(decode_info.magick,"X") == 0)
        {
          image=ReadXImage(&decode_info,False,False,False,False);
          break;
        }
      if (strcmp(decode_info.magick,"XC") == 0)
        {
          image=ReadXCImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"XBM") == 0)
        {
          image=ReadXBMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"XPM") == 0)
        {
          image=ReadXPMImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"XV") == 0)
        {
          image=ReadVIFFImage(&decode_info);
          break;
        }
      if (strcmp(decode_info.magick,"XWD") == 0)
        {
          image=ReadXWDImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    case 'Y':
    {
      if (strcmp(decode_info.magick,"YUV") == 0)
        {
          image=ReadYUVImage(&decode_info);
          break;
        }
      image=ReadMIFFImage(&decode_info);
      break;
    }
    default:
    {
      (void) strcpy(decode_info.magick,"MIFF");
      image=ReadMIFFImage(&decode_info);
    }
  }
  if (decode_image.temporary)
    (void) remove(decode_info.filename);
  if (image != (Image *) NULL)
    if ((image->next != (Image *) NULL) && (image_info->tile != (char *) NULL))
      {
        int
          count,
          offset,
          retain;

        /*
          User specified subimages (e.g. image.miff[1,3,4]).
        */
        for ( ; ; )
        {
          retain=False;
          image_info->subimage=atoi(image_info->tile);
          for (p=image_info->tile; *p != '\0'; p+=Max(offset,1))
          {
            offset=0;
            count=sscanf(p,"%u%n-%u%n",&image_info->subimage,&offset,
              &image_info->subrange,&offset);
            if (count == 1)
              image_info->subrange=image_info->subimage;
            retain|=((image->scene >= image_info->subimage) &&
              (image->scene <= image_info->subrange));
          }
          if (image->next != (Image *) NULL)
            {
              image=image->next;
              if (!retain)
                DestroyImage(image->previous);
              continue;
            }
          if (image->previous != (Image *) NULL)
            {
              image=image->previous;
              if (!retain)
                DestroyImage(image->next);
              break;
            }
          if (!retain)
            {
              DestroyImage(image);
              image=(Image *) NULL;
            }
          break;
        }
        if (image == (Image *) NULL)
          {
            Warning("Subimage specification returns no images",
              image_info->filename);
            return((Image *) NULL);
          }
        while (image->previous != (Image *) NULL)
          image=image->previous;
      }
  if (image != (Image *) NULL)
    {
      if (image->status)
        Warning("An error has occurred reading from file",image->filename);
      if (decode_image.temporary)
        (void) strcpy(image->filename,image_info->filename);
      (void) strcpy(image->magick_filename,image_info->filename);
      if (image->comments == (char *) NULL)
        CommentImage(image,"  Imported from %m image: %f");
      if (image->magick_columns == 0)
        image->magick_columns=image->columns;
      if (image->magick_rows == 0)
        image->magick_rows=image->rows;
      image_info->interlace=decode_info.interlace;
      if (image->class == PseudoClass)
        if (IsMonochromeImage(image))
          {
            image->background_color.red=MaxRGB;
            image->background_color.green=MaxRGB;
            image->background_color.blue=MaxRGB;
          }
    }
  return(image);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.