/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                     IIIII  M   M   AAA    GGGG  EEEEE                       %
%                       I    MM MM  A   A  G      E                           %
%                       I    M M M  AAAAA  G  GG  EEE                         %
%                       I    M   M  A   A  G   G  E                           %
%                     IIIII  M   M  A   A   GGGG  EEEEE                       %
%                                                                             %
%                                                                             %
%                          ImageMagick Image Methods                          %
%                                                                             %
%                                                                             %
%                              Software Design                                %
%                                John Cristy                                  %
%                                 July 1992                                   %
%                                                                             %
%                                                                             %
%  Copyright 1999 E. I. du Pont de Nemours and Company                        %
%                                                                             %
%  Permission is hereby granted, free of charge, to any person obtaining a    %
%  copy of this software and associated documentation files ("ImageMagick"),  %
%  to deal in ImageMagick without restriction, including without limitation   %
%  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
%  and/or sell copies of ImageMagick, and to permit persons to whom the       %
%  ImageMagick is furnished to do so, subject to the following conditions:    %
%                                                                             %
%  The above copyright notice and this permission notice shall be included in %
%  all copies or substantial portions of ImageMagick.                         %
%                                                                             %
%  The software is provided "as is", without warranty of any kind, express or %
%  implied, including but not limited to the warranties of merchantability,   %
%  fitness for a particular purpose and noninfringement.  In no event shall   %
%  E. I. du Pont de Nemours and Company be liable for any claim, damages or   %
%  other liability, whether in an action of contract, tort or otherwise,      %
%  arising from, out of or in connection with ImageMagick or the use or other %
%  dealings in ImageMagick.                                                   %
%                                                                             %
%  Except as contained in this notice, the name of the E. I. du Pont de       %
%  Nemours and Company shall not be used in advertising or otherwise to       %
%  promote the sale, use or other dealings in ImageMagick without prior       %
%  written authorization from the E. I. du Pont de Nemours and Company.       %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
%
*/

/*
  Include declarations.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "magick.h"
#include "defines.h"

/* local prototype */
void wvStrToUpper(char *str);

/*
  Constant declaration.
*/
const char
  *DefaultPointSize = "12",
  *DefaultTileFrame = "15x15+3+3",
  *DefaultTileLabel = "%f\n%wx%h\n%b",
  *DefaultTileGeometry = "106x106+4+3>",
  *LoadImageText = "  Loading image...  ",
  *LoadImagesText = "  Loading images...  ",
  *ReadBinaryType = "rb",
  *ReadBinaryUnbufferedType = "rbu",
  *SaveImageText = "  Saving image...  ",
  *SaveImagesText = "  Saving images...  ",
  *WriteBinaryType = "wb";

const char
  *BackgroundColor = "#bdbdbd",  /* gray */
  *BorderColor = "#bdbdbd",  /* gray */
  *ForegroundColor = "#000",  /* black */
  *MatteColor = "#bdbdbd";  /* gray */

const InterlaceType
  DefaultInterlace = NoInterlace;



Export ColorPacket InterpolateColor(Image *image,const double x_offset,
  const double y_offset)
{
  ColorPacket
    interpolated_pixel;

  double
    alpha,
    beta,
    x,
    y;

  register RunlengthPacket
    *p,
    *q,
    *r,
    *s;

  RunlengthPacket
    background_pixel;

  assert(image != (Image *) NULL);
  if (image->packets != (image->columns*image->rows))
    if (!UncondenseImage(image))
      return(image->background_color);
  if ((x_offset < -1) || (x_offset >= image->columns) ||
      (y_offset < -1) || (y_offset >= image->rows))
    return(image->background_color);
  background_pixel.red=image->background_color.red;
  background_pixel.green=image->background_color.green;
  background_pixel.blue=image->background_color.blue;
  background_pixel.index=image->background_color.index;
  x=x_offset;
  y=y_offset;
  if ((x >= 0) && (y >= 0))
    {
      p=image->pixels+((int) y)*image->columns+(int) x;
      q=p+1;
      if ((x+1) >= image->columns)
        q=(&background_pixel);
      r=p+image->columns;
      if ((y+1) >= image->rows)
        r=(&background_pixel);
      s=p+image->columns+1;
      if (((x+1) >= image->columns) || ((y+1) >= image->rows))
        s=(&background_pixel);
    }
  else
    {
      p=(&background_pixel);
      q=(&background_pixel);
      r=image->pixels+(int) x;
      s=r+1;
      if ((x >= -1) && (x < 0))
        {
          q=image->pixels+(int) y*image->columns;
          r=(&background_pixel);
          s=q+(int) image->columns;
          if ((y >= -1) && (y < 0))
            {
              q=(&background_pixel);
              s=image->pixels;
            }
        }
    }
  x-=floor(x);
  y-=floor(y);
  alpha=1.0-x;
  beta=1.0-y;
  interpolated_pixel.red=(Quantum)
    (beta*(alpha*p->red+x*q->red)+y*(alpha*r->red+x*s->red));
  interpolated_pixel.green=(Quantum)
    (beta*(alpha*p->green+x*q->green)+y*(alpha*r->green+x*s->green));
  interpolated_pixel.blue=(Quantum)
    (beta*(alpha*p->blue+x*q->blue)+y*(alpha*r->blue+x*s->blue));
  interpolated_pixel.index=(unsigned short)
    (beta*(alpha*p->index+x*q->index)+y*(alpha*r->index+x*s->index));
  return(interpolated_pixel);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e t I m a g e I n f o                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method GetImageInfo initializes the ImageInfo structure.
%
%  The format of the GetImageInfo method is:
%
%      void GetImageInfo(ImageInfo *image_info)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
Export void GetImageInfo(ImageInfo *image_info)
{
  /*
    File and image dimension members.
  */
  GetBlobInfo(&(image_info->blob));
  image_info->file=(FILE *) NULL;
  *image_info->filename='\0';
  *image_info->magick='\0';
  TemporaryFilename(image_info->unique);
  (void) strcat(image_info->unique,"u");
  TemporaryFilename(image_info->zero);
  image_info->affirm=False;
  image_info->temporary=False;
  image_info->adjoin=True;
  image_info->subimage=0;
  image_info->subrange=0;
  image_info->ping=False;
  image_info->depth=QuantumDepth;
  image_info->size=(char *) NULL;
  image_info->tile=(char *) NULL;
  image_info->page=(char *) NULL;
  image_info->interlace=DefaultInterlace;
  image_info->units=UndefinedResolution;
  /*
    Compression members.
  */
  image_info->compression=UndefinedCompression;
  image_info->quality=75;
  /*
    Annotation members.
  */
  image_info->server_name=(char *) NULL;
  image_info->box=(char *) NULL;
  image_info->font=(char *) NULL;
  image_info->pen=(char *) NULL;
  image_info->texture=(char *) NULL;
  image_info->density=(char *) NULL;
  image_info->linewidth=1;
  image_info->pointsize=atoi(DefaultPointSize);
  image_info->antialias=True;
  image_info->fuzz=0;
  image_info->background_color=(char *) NULL;
  image_info->border_color=(char *) NULL;
  image_info->matte_color=(char *) NULL;
  /*
    Color reduction members.
  */
  image_info->dither=True;
  image_info->monochrome=False;
  image_info->colorspace=UndefinedColorspace;
  /*
    Animation members.
  */
  image_info->dispose=(char *) NULL;
  image_info->delay=(char *) NULL;
  image_info->iterations=(char *) NULL;
  image_info->coalesce_frames=False;
  image_info->insert_backdrops=False;
  /*
    Miscellaneous members.
  */
  image_info->verbose=False;
  image_info->preview_type=JPEGPreview;
  image_info->view=(char *) NULL;
  image_info->group=0L;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   A l l o c a t e I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method AllocateImage allocates an Image structure and initializes each
%  field to a default value.
%
%  The format of the AllocateImage method is:
%
%      Image *AllocateImage(const ImageInfo *image_info)
%
%  A description of each parameter follows:
%
%    o allocated_image: Method AllocateImage returns a pointer to an image
%      structure initialized to default values.  A null image is returned if
%      there is a memory shortage.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
Export Image *AllocateImage(const ImageInfo *image_info)
{
  ColorPacket
    color;

  Image
    *allocated_image;

  int
    flags;

  /*
    Allocate image structure.
  */
  allocated_image=(Image *) AllocateMemory(sizeof(Image));
  if (allocated_image == (Image *) NULL)
    {
      MagickWarning(ResourceLimitWarning,"Unable to allocate image",
        "Memory allocation failed");
      return((Image *) NULL);
    }
  /*
    Initialize Image structure.
  */
  GetBlobInfo(&(allocated_image->blob));
  allocated_image->file=(FILE *) NULL;
  allocated_image->exempt=False;
  allocated_image->status=False;
  allocated_image->temporary=False;
  *allocated_image->filename='\0';
  allocated_image->filesize=0;
  allocated_image->pipe=False;
  (void) strcpy(allocated_image->magick,"MIFF");
  allocated_image->comments=(char *) NULL;
  allocated_image->label=(char *) NULL;
  allocated_image->class=DirectClass;
  allocated_image->matte=False;
  allocated_image->compression=RunlengthEncodedCompression;
  allocated_image->columns=0;
  allocated_image->rows=0;
  allocated_image->depth=QuantumDepth;
  allocated_image->tile_info.width=0;
  allocated_image->tile_info.height=0;
  allocated_image->tile_info.x=0;
  allocated_image->tile_info.y=0;
  allocated_image->offset=0;
  allocated_image->interlace=DefaultInterlace;
  allocated_image->scene=0;
  allocated_image->units=UndefinedResolution;
  allocated_image->x_resolution=0.0;
  allocated_image->y_resolution=0.0;
  allocated_image->montage=(char *) NULL;
  allocated_image->directory=(char *) NULL;
  allocated_image->colormap=(ColorPacket *) NULL;
  allocated_image->colors=0;
  allocated_image->colorspace=RGBColorspace;
  allocated_image->rendering_intent=UndefinedIntent;
  allocated_image->gamma=0.0;
  allocated_image->chromaticity.red_primary.x=0.0;
  allocated_image->chromaticity.red_primary.y=0.0;
  allocated_image->chromaticity.green_primary.x=0.0;
  allocated_image->chromaticity.green_primary.y=0.0;
  allocated_image->chromaticity.blue_primary.x=0.0;
  allocated_image->chromaticity.blue_primary.y=0.0;
  allocated_image->chromaticity.white_point.x=0.0;
  allocated_image->chromaticity.white_point.y=0.0;
  allocated_image->color_profile.length=0;
  allocated_image->color_profile.info=(unsigned char *) NULL;
  allocated_image->iptc_profile.length=0;
  allocated_image->iptc_profile.info=(unsigned char *) NULL;
  allocated_image->pixels=(RunlengthPacket *) NULL;
  allocated_image->packets=0;
  allocated_image->packet_size=0;
  allocated_image->packed_pixels=(unsigned char *) NULL;
  allocated_image->geometry=(char *) NULL;
  allocated_image->page=(char *) NULL;
  allocated_image->dispose=0;
  allocated_image->delay=0;
  allocated_image->iterations=1;
  allocated_image->fuzz=0;
  allocated_image->filter=LanczosFilter;
  allocated_image->blur=1.0;
  (void) QueryColorDatabase(BackgroundColor,&color);
  allocated_image->background_color.red=XDownScale(color.red);
  allocated_image->background_color.green=XDownScale(color.green);
  allocated_image->background_color.blue=XDownScale(color.blue);
  allocated_image->background_color.index=Opaque;
  (void) QueryColorDatabase(BorderColor,&color);
  allocated_image->border_color.red=XDownScale(color.red);
  allocated_image->border_color.green=XDownScale(color.green);
  allocated_image->border_color.blue=XDownScale(color.blue);
  allocated_image->border_color.index=Opaque;
  (void) QueryColorDatabase(MatteColor,&color);
  allocated_image->matte_color.red=XDownScale(color.red);
  allocated_image->matte_color.green=XDownScale(color.green);
  allocated_image->matte_color.blue=XDownScale(color.blue);
  allocated_image->matte_color.index=Opaque;
  allocated_image->total_colors=0;
  allocated_image->normalized_mean_error=0.0;
  allocated_image->normalized_maximum_error=0.0;
  allocated_image->mean_error_per_pixel=0;
  allocated_image->signature=(char *) NULL;
  *allocated_image->magick_filename='\0';
  allocated_image->magick_columns=0;
  allocated_image->magick_rows=0;
  allocated_image->magick_time=time((time_t *) NULL);
  allocated_image->tainted=False;
  allocated_image->orphan=False;
  allocated_image->previous=(Image *) NULL;
  allocated_image->list=(Image *) NULL;
  allocated_image->next=(Image *) NULL;
  allocated_image->restart_animation_here=False;
  if (image_info == (ImageInfo *) NULL)
    return(allocated_image);
  /*
    Transfer image info.
  */
  allocated_image->blob=image_info->blob;
  (void) strcpy(allocated_image->filename,image_info->filename);
  (void) strcpy(allocated_image->magick_filename,image_info->filename);
  (void) strcpy(allocated_image->magick,image_info->magick);
  if (image_info->size != (char *) NULL)
    {
      int
        y;

      (void) sscanf(image_info->size,"%ux%u",
        &allocated_image->columns,&allocated_image->rows);
      flags=ParseGeometry(image_info->size,&allocated_image->offset,&y,
        &allocated_image->columns,&allocated_image->rows);
      if ((flags & HeightValue) == 0)
        allocated_image->rows=allocated_image->columns;
      allocated_image->tile_info.width=allocated_image->columns;
      allocated_image->tile_info.height=allocated_image->rows;
    }
  if (image_info->tile != (char *) NULL)
    if (!IsSubimage(image_info->tile,False))
      {
        (void) sscanf(image_info->tile,"%ux%u",
          &allocated_image->columns,&allocated_image->rows);
        flags=ParseGeometry(image_info->tile,&allocated_image->tile_info.x,
          &allocated_image->tile_info.y,&allocated_image->columns,
          &allocated_image->rows);
        if ((flags & HeightValue) == 0)
          allocated_image->rows=allocated_image->columns;
      }
  allocated_image->compression=image_info->compression;
  allocated_image->interlace=image_info->interlace;
  allocated_image->units=image_info->units;
  if (image_info->density != (char *) NULL)
    {
      int
        count;

      count=sscanf(image_info->density,"%lfx%lf",&allocated_image->x_resolution,
        &allocated_image->y_resolution);
      if (count != 2)
        allocated_image->y_resolution=allocated_image->x_resolution;
    }
  if (image_info->page != (char *) NULL)
    allocated_image->page=PostscriptGeometry(image_info->page);
  if (image_info->dispose != (char *) NULL)
    allocated_image->dispose=atoi(image_info->dispose);
  if (image_info->delay != (char *) NULL)
    allocated_image->delay=atoi(image_info->delay);
  if (image_info->iterations != (char *) NULL)
    allocated_image->iterations=atoi(image_info->iterations);
  allocated_image->depth=image_info->depth;
  if (image_info->background_color != (char *) NULL)
    {
      (void) QueryColorDatabase(image_info->background_color,&color);
      allocated_image->background_color.red=XDownScale(color.red);
      allocated_image->background_color.green=XDownScale(color.green);
      allocated_image->background_color.blue=XDownScale(color.blue);
    }
  if (image_info->border_color != (char *) NULL)
    {
      (void) QueryColorDatabase(image_info->border_color,&color);
      allocated_image->border_color.red=XDownScale(color.red);
      allocated_image->border_color.green=XDownScale(color.green);
      allocated_image->border_color.blue=XDownScale(color.blue);
    }
  if (image_info->matte_color != (char *) NULL)
    {
      (void) QueryColorDatabase(image_info->matte_color,&color);
      allocated_image->matte_color.red=XDownScale(color.red);
      allocated_image->matte_color.green=XDownScale(color.green);
      allocated_image->matte_color.blue=XDownScale(color.blue);
    }
  return(allocated_image);
}

Export unsigned int IsSubimage(const char *geometry,const unsigned int pedantic)
{
  int
    x,
    y;

  unsigned int
    flags,
    height,
    width;

  if (geometry == (const char *) NULL)
    return(False);
  flags=ParseGeometry((char *) geometry,&x,&y,&width,&height);
  if (pedantic)
    return((flags != NoValue) && !(flags & HeightValue));
  return(IsGeometry(geometry) && !(flags & HeightValue));
}


Export unsigned int IsGeometry(const char *geometry)
{
  double
    value;

  int
    x,
    y;

  unsigned int
    flags,
    height,
    width;

  if (geometry == (const char *) NULL)
    return(False);
  flags=ParseGeometry((char *) geometry,&x,&y,&width,&height);
  return((flags != NoValue) || sscanf(geometry,"%lf",&value));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   D e s t r o y I m a g e s                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method DestroyImages deallocates memory associated with a linked list
%  of images.
%
%  The format of the DestroyImages method is:
%
%      void DestroyImages(Image *image)
%
%  A description of each parameter follows:
%
%    o image: The address of a structure of type Image.
%
%
*/
Export void DestroyImages(Image *image)
{
  Image
    *next_image;

  /*
    Proceed to the top of the image list.
  */
  while (image->previous != (Image *) NULL)
    image=image->previous;
  do
  {
    /*
      Destroy this image.
    */
    next_image=image->next;
    if (next_image != (Image *)NULL)
      next_image->previous=(Image *)NULL;
    DestroyImage(image);
    image=next_image;
  } while (image != (Image *) NULL);
}

Export void DestroyImage(Image *image)
{
  /*
    Close image.
  */
  if (image->file != (FILE *) NULL)
    {
      CloseBlob(image);
      if (image->temporary)
        (void) remove(image->filename);
    }
  /*
    Deallocate the image comments.
  */
  if (image->comments != (char *) NULL)
    FreeMemory((char *) image->comments);
  /*
    Deallocate the image label.
  */
  if (image->label != (char *) NULL)
    FreeMemory((char *) image->label);
  /*
    Deallocate the image montage directory.
  */
  if (image->montage != (char *) NULL)
    FreeMemory((char *) image->montage);
  if (image->directory != (char *) NULL)
    FreeMemory((char *) image->directory);
  /*
    Deallocate the image colormap.
  */
  if (image->colormap != (ColorPacket *) NULL)
    FreeMemory((char *) image->colormap);
  /*
    Deallocate the image ICC profile.
  */
  if (image->color_profile.length > 0)
    FreeMemory((char *) image->color_profile.info);
  /*
    Deallocate the image IPTC profile.
  */
  if (image->iptc_profile.length > 0)
    FreeMemory((char *) image->iptc_profile.info);
  /*
    Deallocate the image signature.
  */
  if (image->signature != (char *) NULL)
    FreeMemory((char *) image->signature);
  /*
    Deallocate the image pixels.
  */
  if (image->pixels != (RunlengthPacket *) NULL)
    FreeMemory((char *) image->pixels);
  if (image->packed_pixels != (unsigned char *) NULL)
    FreeMemory((char *) image->packed_pixels);
  /*
    Deallocate the image page geometry.
  */
  if (image->page != (char *) NULL)
    FreeMemory((char *) image->page);
  if (!image->orphan)
    {
      /*
        Unlink from linked list.
      */
      if (image->previous != (Image *) NULL)
        {
          if (image->next != (Image *) NULL)
            image->previous->next=image->next;
          else
            image->previous->next=(Image *) NULL;
        }
      if (image->next != (Image *) NULL)
        {
          if (image->previous != (Image *) NULL)
            image->next->previous=image->previous;
          else
            image->next->previous=(Image *) NULL;
        }
    }
  /*
    Deallocate the image structure.
  */
  FreeMemory((char *) image);
  image=(Image *) NULL;
}

Export Image *CloneImage(const Image *image,const unsigned int columns,
  const unsigned int rows,const unsigned int clone_pixels)
{
  Image
    *clone_image;

  register int
    i;

  /*
    Allocate image structure.
  */
  clone_image=(Image *) AllocateMemory(sizeof(Image));
  if (clone_image == (Image *) NULL)
    return((Image *) NULL);
  /*
    Allocate the image pixels.
  */
  *clone_image=(*image);
  clone_image->columns=columns;
  clone_image->rows=rows;
  if (!clone_pixels)
    clone_image->packets=clone_image->columns*clone_image->rows;
  clone_image->pixels=(RunlengthPacket *) AllocateMemory((unsigned int)
    clone_image->packets*sizeof(RunlengthPacket));
  if (clone_image->pixels == (RunlengthPacket *) NULL)
    return((Image *) NULL);
  if (!clone_pixels)
    {
      clone_image->tainted=True;
      SetImage(clone_image);
    }
  else
    {
      register RunlengthPacket
        *p,
        *q;

      /*
        Copy image pixels.
      */
      p=image->pixels;
      q=clone_image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        *q=(*p);
        p++;
        q++;
      }
    }
  clone_image->packed_pixels=(unsigned char *) NULL;
  clone_image->comments=(char *) NULL;
  if (image->comments != (char *) NULL)
    (void) CloneString(&clone_image->comments,image->comments);
  clone_image->label=(char *) NULL;
  if (image->label != (char *) NULL)
    (void) CloneString(&clone_image->label,image->label);
  clone_image->montage=(char *) NULL;
  if (clone_pixels)
    if (image->montage != (char *) NULL)
      (void) CloneString(&clone_image->montage,image->montage);
  clone_image->directory=(char *) NULL;
  if (clone_pixels)
    if (image->directory != (char *) NULL)
      (void) CloneString(&clone_image->directory,image->directory);
  clone_image->signature=(char *) NULL;
  if (clone_pixels)
    if (image->signature != (char *) NULL)
      (void) CloneString(&clone_image->signature,image->signature);
  clone_image->page=(char *) NULL;
  if (clone_pixels)
    if (image->page != (char *) NULL)
      (void) CloneString(&clone_image->page,image->page);
  if (image->colormap != (ColorPacket *) NULL)
    {
      /*
        Allocate and copy the image colormap.
      */
      clone_image->colormap=(ColorPacket *)
        AllocateMemory(image->colors*sizeof(ColorPacket));
      if (clone_image->colormap == (ColorPacket *) NULL)
        return((Image *) NULL);
      for (i=0; i < (int) image->colors; i++)
        clone_image->colormap[i]=image->colormap[i];
    }
  if (image->color_profile.length > 0)
    {
      /*
        Allocate and copy the image ICC profile.
      */
      clone_image->color_profile.info=(unsigned char *)
        AllocateMemory(image->color_profile.length*sizeof(unsigned char));
      if (clone_image->color_profile.info == (unsigned char *) NULL)
        return((Image *) NULL);
      for (i=0; i < (int) image->color_profile.length; i++)
        clone_image->color_profile.info[i]=image->color_profile.info[i];
    }
  if (image->iptc_profile.length > 0)
    {
      /*
        Allocate and copy the image IPTC profile.
      */
      clone_image->iptc_profile.info=(unsigned char *)
        AllocateMemory(image->iptc_profile.length*sizeof(unsigned char));
      if (clone_image->iptc_profile.info == (unsigned char *) NULL)
        return((Image *) NULL);
      for (i=0; i < (int) image->iptc_profile.length; i++)
        clone_image->iptc_profile.info[i]=image->iptc_profile.info[i];
    }
  if (image->orphan)
    {
      clone_image->file=(FILE *) NULL;
      clone_image->previous=(Image *) NULL;
      clone_image->next=(Image *) NULL;
    }
  else
    {
      /*
        Link image into image list.
      */
      if (clone_image->previous != (Image *) NULL)
        clone_image->previous->next=clone_image;
      if (clone_image->next != (Image *) NULL)
        clone_image->next->previous=clone_image;
    }
  clone_image->orphan=False;
  return(clone_image);
}

Export void SyncImage(Image *image)
{
  register int
    i;

  register RunlengthPacket
    *p;

  register unsigned short
    index;

  if (image->class == DirectClass)
    return;
  for (i=0; i < (int) image->colors; i++)
  {
    image->colormap[i].index=0;
    image->colormap[i].flags=0;
  }
  p=image->pixels;
  for (i=0; i < (int) image->packets; i++)
  {
    index=p->index;
    p->red=image->colormap[index].red;
    p->green=image->colormap[index].green;
    p->blue=image->colormap[index].blue;
    p++;
  }
}

Export void CondenseImage(Image *image)
{
  register int
    i,
    runlength;

  register long
    packets;

  register RunlengthPacket
    *p,
    *q;

  if ((image->columns == 0) || (image->rows == 0))
    return;
  if (image->pixels == (RunlengthPacket *) NULL)
    return;
  /*
    Compress image pixels.
  */
  p=image->pixels;
  runlength=p->length+1;
  packets=0;
  q=image->pixels;
  q->length=MaxRunlength;
  if (image->matte || (image->colorspace == CMYKColorspace))
    for (i=0; i < (int) (image->columns*image->rows); i++)
    {
      if (runlength != 0)
        runlength--;
      else
        {
          p++;
          runlength=p->length;
        }
      if ((p->red == q->red) && (p->green == q->green) &&
          (p->blue == q->blue) && (p->index == q->index) &&
          ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (packets != 0)
            q++;
          packets++;
          *q=(*p);
          q->length=0;
        }
    }
  else
    for (i=0; i < (int) (image->columns*image->rows); i++)
    {
      if (runlength != 0)
        runlength--;
      else
        {
          p++;
          runlength=p->length;
        }
      if ((p->red == q->red) && (p->green == q->green) &&
          (p->blue == q->blue) && ((int) q->length < MaxRunlength))
        q->length++;
      else
        {
          if (packets != 0)
            q++;
          packets++;
          *q=(*p);
          q->length=0;
        }
    }
  SetRunlengthPackets(image,packets);
}

Export void AllocateNextImage(const ImageInfo *image_info,Image *image)
{
  /*
    Allocate image structure.
  */
  image->next=AllocateImage(image_info);
  if (image->next == (Image *) NULL)
    return;
  (void) strcpy(image->next->filename,image->filename);
  if (image_info != (ImageInfo *) NULL)
    (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;
}

Export void TransformRGBImage(Image *image,const ColorspaceType colorspace)
{
#define B (MaxRGB+1)*2
#define G (MaxRGB+1)
#define R 0
#define TransformRGBImageText  "  Transforming image colors...  "

  static const Quantum
    sRGBMap[351] =
    {
        0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,
       14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
       28,  29,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
       41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,
       55,  56,  57,  58,  59,  60,  61,  62,  63,  65,  66,  67,  68,  69,
       70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
       84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  95,  96,  97,  98,
       99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
      114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
      128, 129, 130, 131, 132, 133, 135, 136, 137, 138, 139, 140, 141, 142,
      143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
      157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
      171, 172, 173, 174, 175, 175, 176, 177, 178, 179, 180, 181, 182, 183,
      184, 185, 186, 187, 187, 188, 189, 190, 191, 192, 193, 194, 194, 195,
      196, 197, 198, 199, 199, 200, 201, 202, 203, 203, 204, 205, 206, 207,
      207, 208, 209, 210, 210, 211, 212, 213, 213, 214, 215, 215, 216, 217,
      218, 218, 219, 220, 220, 221, 222, 222, 223, 223, 224, 225, 225, 226,
      227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233, 233, 234,
      234, 235, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240,
      240, 240, 241, 241, 242, 242, 242, 243, 243, 243, 243, 244, 244, 244,
      245, 245, 245, 245, 246, 246, 246, 247, 247, 247, 247, 247, 248, 248,
      248, 248, 249, 249, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250,
      251, 251, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252,
      252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254,
      254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255,
      255
    },
    YCCMap[351] =  /* Photo CD information beyond 100% white, Gamma 2.2 */
    {
        0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,
        14, 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
        28, 29,  30,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,
        43, 45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  56,  57,  58,
        59, 60,  61,  62,  63,  64,  66,  67,  68,  69,  70,  71,  72,  73,
        74, 76,  77,  78,  79,  80,  81,  82,  83,  84,  86,  87,  88,  89,
        90, 91,  92,  93,  94,  95,  97,  98,  99, 100, 101, 102, 103, 104,
      105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
      120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134,
      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
      149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
      163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
      176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
      190, 191, 192, 193, 193, 194, 195, 196, 197, 198, 199, 200, 201, 201,
      202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 211, 212, 213,
      214, 215, 215, 216, 217, 218, 218, 219, 220, 221, 221, 222, 223, 224,
      224, 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, 233,
      234, 234, 235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241,
      242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247,
      248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251,
      251, 251, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253,
      253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254,
      254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255,
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
      255
    };
  double
    *blue,
    *green,
    *red;

  Quantum
    *range_table;

  register int
    i,
    x,
    y,
    z;

  register Quantum
    *range_limit;

  register RunlengthPacket
    *p;

  if ((image->colorspace == CMYKColorspace) && (colorspace == RGBColorspace))
    {
      unsigned int
        black,
        cyan,
        magenta,
        yellow;

      /*
        Transform image from CMYK to RGB.
      */
      image->colorspace=RGBColorspace;
      p=image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        cyan=p->red;
        magenta=p->green;
        yellow=p->blue;
        black=p->index;
        if ((cyan+black) > MaxRGB)
          p->red=0;
        else
          p->red=MaxRGB-(cyan+black);
        if ((magenta+black) > MaxRGB)
          p->green=0;
        else
          p->green=MaxRGB-(magenta+black);
        if ((yellow+black) > MaxRGB)
          p->blue=0;
        else
          p->blue=MaxRGB-(yellow+black);
        p->index=0;
        p++;
      }
      return;
    }
  if ((colorspace == RGBColorspace) || (colorspace == GRAYColorspace) ||
      (colorspace == TransparentColorspace))
    return;
  /*
    Allocate the tables.
  */
  red=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  green=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  blue=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  range_table=(Quantum *) AllocateMemory(4*(MaxRGB+1)*sizeof(Quantum));
  if ((red == (double *) NULL) || (green == (double *) NULL) ||
      (blue == (double *) NULL) || (range_table == (Quantum *) NULL))
    {
      MagickWarning(ResourceLimitWarning,"Unable to transform color space",
        "Memory allocation failed");
      return;
    }
  /*
    Initialize tables.
  */
  for (i=0; i <= MaxRGB; i++)
  {
    range_table[i]=0;
    range_table[i+(MaxRGB+1)]=(Quantum) i;
    range_table[i+(MaxRGB+1)*2]=MaxRGB;
  }
  for (i=0; i <= MaxRGB; i++)
    range_table[i+(MaxRGB+1)*3]=MaxRGB;
  range_limit=range_table+(MaxRGB+1);
  switch (colorspace)
  {
    case OHTAColorspace:
    {
      /*
        Initialize OHTA tables:

          R = I1+1.00000*I2-0.66668*I3
          G = I1+0.00000*I2+1.33333*I3
          B = I1-1.00000*I2-0.66668*I3

        I and Q, normally -0.5 through 0.5, must be normalized to the range 0
        through MaxRGB.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=i;
        green[i+R]=0.5*(2.0*i-MaxRGB);
        blue[i+R]=(-0.33334)*(2.0*i-MaxRGB);
        red[i+G]=i;
        green[i+G]=0.0;
        blue[i+G]=0.666665*(2.0*i-MaxRGB);
        red[i+B]=i;
        green[i+B]=(-0.5)*(2.0*i-MaxRGB);
        blue[i+B]=(-0.33334)*(2.0*i-MaxRGB);
      }
      break;
    }
    case sRGBColorspace:
    {
      /*
        Initialize sRGB tables:

          R = Y            +1.032096*C2
          G = Y-0.326904*C1-0.704445*C2
          B = Y+1.685070*C1

        sRGB is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=1.40200*i;
        green[i+R]=0.0;
        blue[i+R]=1.88000*(i-(double) UpScale(137));
        red[i+G]=1.40200*i;
       green[i+G]=(-0.444066)*(i-(double) UpScale(156));
        blue[i+G]=(-0.95692)*(i-(double) UpScale(137));
        red[i+B]=1.40200*i;
        green[i+B]=2.28900*(i-(double) UpScale(156));
        blue[i+B]=0.0;
        range_table[i+(MaxRGB+1)]=(Quantum) UpScale(sRGBMap[DownScale(i)]);
      }
      for ( ; i < (int) UpScale(351); i++)
        range_table[i+(MaxRGB+1)]=(Quantum) UpScale(sRGBMap[DownScale(i)]);
      break;
    }
    case XYZColorspace:
    {
      /*
        Initialize CIE XYZ tables:

          R =  3.240479*R-1.537150*G-0.498535*B
          G = -0.969256*R+1.875992*G+0.041556*B
          B =  0.055648*R-0.204043*G+1.057311*B
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=3.240479*i;
        green[i+R]=(-1.537150)*i;
        blue[i+R]=(-0.498535)*i;
        red[i+G]=(-0.969256)*i;
        green[i+G]=1.875992*i;
        blue[i+G]=0.041556*i;
        red[i+B]=0.055648*i;
        green[i+B]=(-0.204043)*i;
        blue[i+B]=1.057311*i;
      }
      break;
    }
    case YCbCrColorspace:
    {
      /*
        Initialize YCbCr tables:

          R = Y            +1.402000*Cr
          G = Y-0.344136*Cb-0.714136*Cr
          B = Y+1.772000*Cb

        Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0
        through MaxRGB.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=i;
        green[i+R]=0.0;
        blue[i+R]=(1.402000*0.5)*(2.0*i-MaxRGB);
        red[i+G]=i;
        green[i+G]=(-0.344136*0.5)*(2.0*i-MaxRGB);
        blue[i+G]=(-0.714136*0.5)*(2.0*i-MaxRGB);
        red[i+B]=i;
        green[i+B]=(1.772000*0.5)*(2.0*i-MaxRGB);
        blue[i+B]=0.0;
      }
      break;
    }
    case YCCColorspace:
    {
      /*
        Initialize YCC tables:

          R = Y            +1.340762*C2
          G = Y-0.317038*C1-0.682243*C2
          B = Y+1.632639*C1

        YCC is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=1.3584*i;
        green[i+R]=0.0;
        blue[i+R]=1.8215*(i-(double) UpScale(137));
        red[i+G]=1.3584*i;
        green[i+G]=(-0.4302726)*(i-(double) UpScale(156));
        blue[i+G]=(-0.9271435)*(i-(double) UpScale(137));
        red[i+B]=1.3584*i;
        green[i+B]=2.2179*(i-(double) UpScale(156));
        blue[i+B]=0.0;
        range_table[i+(MaxRGB+1)]=(Quantum) UpScale(YCCMap[DownScale(i)]);
      }
      for ( ; i < (int) UpScale(351); i++)
        range_table[i+(MaxRGB+1)]=(Quantum) UpScale(YCCMap[DownScale(i)]);
      break;
    }
    case YIQColorspace:
    {
      /*
        Initialize YIQ tables:

          R = Y+0.95620*I+0.62140*Q
          G = Y-0.27270*I-0.64680*Q
          B = Y-1.10370*I+1.70060*Q

        I and Q, normally -0.5 through 0.5, must be normalized to the range 0
        through MaxRGB.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=i;
        green[i+R]=0.4781*(2.0*i-MaxRGB);
        blue[i+R]=0.3107*(2.0*i-MaxRGB);
        red[i+G]=i;
        green[i+G]=(-0.13635)*(2.0*i-MaxRGB);
        blue[i+G]=(-0.3234)*(2.0*i-MaxRGB);
        red[i+B]=i;
        green[i+B]=(-0.55185)*(2.0*i-MaxRGB);
        blue[i+B]=0.8503*(2.0*i-MaxRGB);
      }
      break;
    }
    case YPbPrColorspace:
    {
      /*
        Initialize YPbPr tables:

          R = Y            +1.402000*C2
          G = Y-0.344136*C1+0.714136*C2
          B = Y+1.772000*C1

        Pb and Pr, normally -0.5 through 0.5, must be normalized to the range 0
        through MaxRGB.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=i;
        green[i+R]=0.0;
        blue[i+R]=0.701*(2.0*i-MaxRGB);
        red[i+G]=i;
        green[i+G]=(-0.172068)*(2.0*i-MaxRGB);
        blue[i+G]=0.357068*(2.0*i-MaxRGB);
        red[i+B]=i;
        green[i+B]=0.886*(2.0*i-MaxRGB);
        blue[i+B]=0.0;
      }
      break;
    }
    case YUVColorspace:
    default:
    {
      /*
        Initialize YUV tables:

          R = Y          +1.13980*V
          G = Y-0.39380*U-0.58050*V
          B = Y+2.02790*U

        U and V, normally -0.5 through 0.5, must be normalized to the range 0
        through MaxRGB.
      */
      for (i=0; i <= MaxRGB; i++)
      {
        red[i+R]=i;
        green[i+R]=0.0;
        blue[i+R]=0.5699*(2.0*i-MaxRGB);
        red[i+G]=i;
        green[i+G]=(-0.1969)*(2.0*i-MaxRGB);
        blue[i+G]=(-0.29025)*(2.0*i-MaxRGB);
        red[i+B]=i;
        green[i+B]=1.01395*(2.0*i-MaxRGB);
        blue[i+B]=0;
      }
      break;
    }
  }
  /*
    Convert to RGB.
  */
  switch (image->class)
  {
    case DirectClass:
    default:
    {
      /*
        Convert DirectClass image.
      */
      p=image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        x=p->red;
        y=p->green;
        z=p->blue;
        p->red=range_limit[(int) (red[x+R]+green[y+R]+blue[z+R])];
        p->green=range_limit[(int) (red[x+G]+green[y+G]+blue[z+G])];
        p->blue=range_limit[(int) (red[x+B]+green[y+B]+blue[z+B])];
        p++;
      }
      break;
    }
    case PseudoClass:
    {
      /*
        Convert PseudoClass image.
      */
      for (i=0; i < (int) image->colors; i++)
      {
        x=image->colormap[i].red;
        y=image->colormap[i].green;
        z=image->colormap[i].blue;
        image->colormap[i].red=
          range_limit[(int) (red[x+R]+green[y+R]+blue[z+R])];
        image->colormap[i].green=
          range_limit[(int) (red[x+G]+green[y+G]+blue[z+G])];
        image->colormap[i].blue=
          range_limit[(int) (red[x+B]+green[y+B]+blue[z+B])];
      }
      SyncImage(image);
      break;
    }
  }
  /*
    Free allocated memory.
  */
  FreeMemory((char *) range_table);
  FreeMemory((char *) blue);
  FreeMemory((char *) green);
  FreeMemory((char *) red);
}

Export void RGBTransformImage(Image *image,const ColorspaceType colorspace)
{
#define RGBTransformImageText  "  Transforming image colors...  "
#define X 0
#define Y (MaxRGB+1)
#define Z (MaxRGB+1)*2

  double
    tx,
    ty,
    tz;

  Quantum
    *range_table;

  register double
    *x,
    *y,
    *z;

  register int
    blue,
    green,
    i,
    red;

  register Quantum
    *range_limit;

  register RunlengthPacket
    *p;

  assert(image != (Image *) NULL);
  if ((colorspace == RGBColorspace) || (colorspace == TransparentColorspace))
    return;
  if (colorspace == CMYKColorspace)
    {
      Quantum
        black,
        cyan,
        magenta,
        yellow;

      /*
        Convert RGB to CMYK colorspace.
      */
      image->colorspace=CMYKColorspace;
      p=image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        cyan=MaxRGB-p->red;
        magenta=MaxRGB-p->green;
        yellow=MaxRGB-p->blue;
        black=cyan;
        if (magenta < black)
          black=magenta;
        if (yellow < black)
          black=yellow;
        p->red=cyan;
        p->green=magenta;
        p->blue=yellow;
        p->index=black;
        p++;
      }
      return;

    }
  if (colorspace == GRAYColorspace)
    {
      /*
        Return if the image is already gray_scale.
      */
      p=image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        if ((p->red != p->green) || (p->green != p->blue))
          break;
        p++;
      }
      if (i == (int) image->packets)
        return;
    }
  /*
    Allocate the tables.
  */
  x=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  y=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  z=(double *) AllocateMemory(3*(MaxRGB+1)*sizeof(double));
  range_table=(Quantum *) AllocateMemory(4*(MaxRGB+1)*sizeof(Quantum));
  if ((x == (double *) NULL) || (y == (double *) NULL) ||
      (z == (double *) NULL) || (range_table == (Quantum *) NULL))
    {
      MagickWarning(ResourceLimitWarning,"Unable to transform color space",
        "Memory allocation failed");
      return;
    }
  /*
    Pre-compute conversion tables.
  */
  for (i=0; i <= MaxRGB; i++)
  {
    range_table[i]=0;
    range_table[i+(MaxRGB+1)]=(Quantum) i;
    range_table[i+(MaxRGB+1)*2]=MaxRGB;
  }
  for (i=0; i <= MaxRGB; i++)
    range_table[i+(MaxRGB+1)*3]=MaxRGB;
  range_limit=range_table+(MaxRGB+1);
  tx=0;
  ty=0;
  tz=0;
  switch (colorspace)
  {
    case GRAYColorspace:
    {
      /*
        Initialize GRAY tables:

          G = 0.29900*R+0.58700*G+0.11400*B
      */
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.299*i;
        y[i+X]=0.587*i;
        z[i+X]=0.114*i;
        x[i+Y]=0.299*i;
        y[i+Y]=0.587*i;
        z[i+Y]=0.114*i;
        x[i+Z]=0.299*i;
        y[i+Z]=0.587*i;
        z[i+Z]=0.114*i;
      }
      break;
    }
    case OHTAColorspace:
    {
      /*
        Initialize OHTA tables:

          I1 = 0.33333*R+0.33334*G+0.33333*B
          I2 = 0.50000*R+0.00000*G-0.50000*B
          I3 =-0.25000*R+0.50000*G-0.25000*B

        I and Q, normally -0.5 through 0.5, are normalized to the range 0
        through MaxRGB.
      */
      ty=(MaxRGB+1) >> 1;
      tz=(MaxRGB+1) >> 1;
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.33333*i;
        y[i+X]=0.33334*i;
        z[i+X]=0.33333*i;
        x[i+Y]=0.5*i;
        y[i+Y]=0.0;
        z[i+Y]=(-0.5)*i;
        x[i+Z]=(-0.25)*i;
        y[i+Z]=0.5*i;
        z[i+Z]=(-0.25)*i;
      }
      break;
    }
    case sRGBColorspace:
    {
      /*
        Initialize sRGB tables:

          Y =  0.29900*R+0.58700*G+0.11400*B
          C1= -0.29900*R-0.58700*G+0.88600*B
          C2=  0.70100*R-0.58700*G-0.11400*B

        sRGB is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
      */
      ty=UpScale(156);
      tz=UpScale(137);
      for (i=0; i <= (int) (0.018*MaxRGB); i++)
      {
        x[i+X]=0.003962014134275617*MaxRGB*i;
        y[i+X]=0.007778268551236748*MaxRGB*i;
        z[i+X]=0.001510600706713781*MaxRGB*i;
        x[i+Y]=(-0.002426619775463276)*MaxRGB*i;
        y[i+Y]=(-0.004763965913702149)*MaxRGB*i;
        z[i+Y]=0.007190585689165425*MaxRGB*i;
        x[i+Z]=0.006927257754597858*MaxRGB*i;
        y[i+Z]=(-0.005800713697502058)*MaxRGB*i;
        z[i+Z]=(-0.0011265440570958)*MaxRGB*i;
      }
      for ( ; i <= MaxRGB; i++)
      {
        x[i+X]=0.2201118963486454*(1.099*i-0.099);
        y[i+X]=0.4321260306242638*(1.099*i-0.099);
        z[i+X]=0.08392226148409894*(1.099*i-0.099);
        x[i+Y]=(-0.1348122097479598)*(1.099*i-0.099);
        y[i+Y]=(-0.2646647729834528)*(1.099*i-0.099);
        z[i+Y]=0.3994769827314126*(1.099*i-0.099);
        x[i+Z]=0.3848476530332144*(1.099*i-0.099);
        y[i+Z]=(-0.3222618720834477)*(1.099*i-0.099);
        z[i+Z]=(-0.06258578094976668)*(1.099*i-0.099);
      }
      break;
    }
    case XYZColorspace:
    {
      /*
        Initialize CIE XYZ tables:

          X = 0.412453*X+0.357580*Y+0.180423*Z
          Y = 0.212671*X+0.715160*Y+0.072169*Z
          Z = 0.019334*X+0.119193*Y+0.950227*Z
      */
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.412453*i;
        y[i+X]=0.35758*i;
        z[i+X]=0.180423*i;
        x[i+Y]=0.212671*i;
        y[i+Y]=0.71516*i;
        z[i+Y]=0.072169*i;
        x[i+Z]=0.019334*i;
        y[i+Z]=0.119193*i;
        z[i+Z]=0.950227*i;
      }
      break;
    }
    case YCbCrColorspace:
    {
      /*
        Initialize YCbCr tables:

          Y =  0.299000*R+0.587000*G+0.114000*B
          Cb= -0.168736*R-0.331264*G+0.500000*B
          Cr=  0.500000*R-0.418688*G-0.083168*B

        Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
        through MaxRGB.
      */
      ty=(MaxRGB+1) >> 1;
      tz=(MaxRGB+1) >> 1;
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.299*i;
        y[i+X]=0.587*i;
        z[i+X]=0.114*i;
        x[i+Y]=(-0.16873)*i;
        y[i+Y]=(-0.331264)*i;
        z[i+Y]=0.500000*i;
        x[i+Z]=0.500000*i;
        y[i+Z]=(-0.418688)*i;
        z[i+Z]=(-0.081312)*i;
      }
      break;
    }
    case YCCColorspace:
    {
      /*
        Initialize YCC tables:

          Y =  0.29900*R+0.58700*G+0.11400*B
          C1= -0.29900*R-0.58700*G+0.88600*B
          C2=  0.70100*R-0.58700*G-0.11400*B
        YCC is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
      */
      ty=UpScale(156);
      tz=UpScale(137);
      for (i=0; i <= (int) (0.018*MaxRGB); i++)
      {
        x[i+X]=0.003962014134275617*MaxRGB*i;
        y[i+X]=0.007778268551236748*MaxRGB*i;
        z[i+X]=0.001510600706713781*MaxRGB*i;
        x[i+Y]=(-0.002426619775463276)*MaxRGB*i;
        y[i+Y]=(-0.004763965913702149)*MaxRGB*i;
        z[i+Y]=0.007190585689165425*MaxRGB*i;
        x[i+Z]=0.006927257754597858*MaxRGB*i;
        y[i+Z]=(-0.005800713697502058)*MaxRGB*i;
        z[i+Z]=(-0.0011265440570958)*MaxRGB*i;
      }
      for ( ; i <= MaxRGB; i++)
      {
        x[i+X]=0.2201118963486454*(1.099*i-0.099);
        y[i+X]=0.4321260306242638*(1.099*i-0.099);
        z[i+X]=0.08392226148409894*(1.099*i-0.099);
        x[i+Y]=(-0.1348122097479598)*(1.099*i-0.099);
        y[i+Y]=(-0.2646647729834528)*(1.099*i-0.099);
        z[i+Y]=0.3994769827314126*(1.099*i-0.099);
        x[i+Z]=0.3848476530332144*(1.099*i-0.099);
        y[i+Z]=(-0.3222618720834477)*(1.099*i-0.099);
        z[i+Z]=(-0.06258578094976668)*(1.099*i-0.099);
      }
      break;
    }
    case YIQColorspace:
    {
      /*
        Initialize YIQ tables:

          Y = 0.29900*R+0.58700*G+0.11400*B
          I = 0.59600*R-0.27400*G-0.32200*B
          Q = 0.21100*R-0.52300*G+0.31200*B

        I and Q, normally -0.5 through 0.5, are normalized to the range 0
        through MaxRGB.
      */
      ty=(MaxRGB+1) >> 1;
      tz=(MaxRGB+1) >> 1;
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.299*i;
        y[i+X]=0.587*i;
        z[i+X]=0.114*i;
        x[i+Y]=0.596*i;
        y[i+Y]=(-0.274)*i;
        z[i+Y]=(-0.322)*i;
        x[i+Z]=0.211*i;
        y[i+Z]=(-0.523)*i;
        z[i+Z]=0.312*i;
      }
      break;
    }
    case YPbPrColorspace:
    {
      /*
        Initialize YPbPr tables:

          Y =  0.299000*R+0.587000*G+0.114000*B
          Pb= -0.168736*R-0.331264*G+0.500000*B
          Pr=  0.500000*R-0.418688*G-0.081312*B
        Pb and Pr, normally -0.5 through 0.5, are normalized to the range 0
        through MaxRGB.
      */
      ty=(MaxRGB+1) >> 1;
      tz=(MaxRGB+1) >> 1;
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.299*i;
        y[i+X]=0.587*i;
        z[i+X]=0.114*i;
        x[i+Y]=(-0.168736)*i;
        y[i+Y]=(-0.331264)*i;
        z[i+Y]=0.5*i;
        x[i+Z]=0.5*i;
        y[i+Z]=(-0.418688)*i;
        z[i+Z]=(-0.081312)*i;
      }
      break;
    }
    case YUVColorspace:
    default:
    {
      /*
        Initialize YUV tables:

          Y =  0.29900*R+0.58700*G+0.11400*B
          U = -0.14740*R-0.28950*G+0.43690*B
          V =  0.61500*R-0.51500*G-0.10000*B

        U and V, normally -0.5 through 0.5, are normalized to the range 0
        through MaxRGB.  Note that U = 0.493*(B-Y), V = 0.877*(R-Y).
      */
      ty=(MaxRGB+1) >> 1;
      tz=(MaxRGB+1) >> 1;
      for (i=0; i <= MaxRGB; i++)
      {
        x[i+X]=0.299*i;
        y[i+X]=0.587*i;
        z[i+X]=0.114*i;
        x[i+Y]=(-0.1474)*i;
        y[i+Y]=(-0.2895)*i;
        z[i+Y]=0.4369*i;
        x[i+Z]=0.615*i;
        y[i+Z]=(-0.515)*i;
        z[i+Z]=(-0.1)*i;
      }
      break;
    }
  }
  /*
    Convert from RGB.
  */
  switch (image->class)
  {
    case DirectClass:
    default:
    {
      /*
        Convert DirectClass image.
      */
      p=image->pixels;
      for (i=0; i < (int) image->packets; i++)
      {
        red=p->red;
        green=p->green;
        blue=p->blue;
        p->red=range_limit[(int) (x[red+X]+y[green+X]+z[blue+X]+tx)];
        p->green=range_limit[(int) (x[red+Y]+y[green+Y]+z[blue+Y]+ty)];
        p->blue=range_limit[(int) (x[red+Z]+y[green+Z]+z[blue+Z]+tz)];
        p++;
      }
      break;
    }
    case PseudoClass:
    {
      /*
        Convert PseudoClass image.
      */
      for (i=0; i < (int) image->colors; i++)
      {
        red=image->colormap[i].red;
        green=image->colormap[i].green;
        blue=image->colormap[i].blue;
        image->colormap[i].red=
          range_limit[(int) (x[red+X]+y[green+X]+z[blue+X]+tx)];
        image->colormap[i].green=
          range_limit[(int) (x[red+Y]+y[green+Y]+z[blue+Y]+ty)];
        image->colormap[i].blue=
          range_limit[(int) (x[red+Z]+y[green+Z]+z[blue+Z]+tz)];
      }
      SyncImage(image);
      break;
    }
  }
  /*
    Free allocated memory.
  */
  FreeMemory((char *) range_table);
  FreeMemory((char *) z);
  FreeMemory((char *) y);
  FreeMemory((char *) x);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   S e t I m a g e I n f o                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method SetImageInfo initializes the `magick' field of the ImageInfo
%  structure.  It is set to a type of image format based on the prefix or
%  suffix of the filename.  For example, `ps:image' returns PS indicating
%  a Postscript image.  JPEG is returned for this filename: `image.jpg'.
%  The filename prefix has precendence over the suffix.  Use an optional index
%  enclosed in brackets after a file name to specify a desired subimage of a
%  multi-resolution image format like Photo CD (e.g. img0001.pcd[4]).
%
%  The format of the SetImageInfo method is:
%
%      void SetImageInfo(ImageInfo *image_info,const unsigned int rectify)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%    o rectify: an unsigned value other than zero rectifies the attribute for
%      multi-frame support (user may want multi-frame but image format may not
%      support it).
%
%
*/
Export void SetImageInfo(ImageInfo *image_info,const unsigned int rectify)
{
  char
    magick[MaxTextExtent];

  Image
    *image;

  register char
    *p,
    *q;

  register MagickInfo
    *r;

  unsigned int
    status;

  /*
    Look for 'image.format' in filename.
  */
  assert(image_info != (ImageInfo *) NULL);
  *magick='\0';
  p=image_info->filename+Extent(image_info->filename)-1;
  if (*p == ']')
    for (q=p-1; q > image_info->filename; q--)
    {
      char
        *tile;

      /*
        Look for sub-image specification (e.g. img0001.pcd[4]).
      */
      if (*q != '[')
        continue;
      if (!IsGeometry(q+1))
        break;
      tile=(char *) AllocateMemory((p-q)*sizeof(char));
      if (tile == (char *) NULL)
        break;
      (void) strncpy(tile,q+1,p-q-1);
      tile[p-q-1]='\0';
      *q='\0';
      p=q;
      (void) CloneString(&image_info->tile,tile);
      FreeMemory((char *) tile);
      if (!IsSubimage(image_info->tile,True))
        break;
      /*
        Determine sub-image range.
      */
      image_info->subimage=atoi(image_info->tile);
      image_info->subrange=atoi(image_info->tile);
      (void) sscanf(image_info->tile,"%u-%u",&image_info->subimage,
        &image_info->subrange);
      if (image_info->subrange < image_info->subimage)
        Swap(image_info->subimage,image_info->subrange);
      else
        {
          FreeMemory(image_info->tile);
          image_info->tile=(char *) NULL;
        }
      image_info->subrange-=image_info->subimage-1;
      break;
    }
  while ((*p != '.') && (p > (image_info->filename+1)))
    p--;
  if ((strcmp(p,".gz") == 0) || (strcmp(p,".Z") == 0) ||
      (strcmp(p,".bz2") == 0))
    do
    {
      p--;
    } while ((*p != '.') && (p > (image_info->filename+1)));
  if ((*p == '.') && (Extent(p) < (int) sizeof(magick)))
    {
      /*
        User specified image format.
      */
      (void) strcpy(magick,p+1);
      for (q=magick; *q != '\0'; q++)
        if (*q == '.')
          {
            *q='\0';
            break;
          }
	
      wvStrToUpper(magick);
      /*
        SGI and RGB are ambiguous;  TMP must be set explicitly.
      */
      if (((strncmp(image_info->magick,"SGI",3) != 0) ||
          (strcmp(magick,"RGB") != 0)) &&
          (strcmp(magick,"TMP") != 0))
        (void) strcpy(image_info->magick,magick);
    }
  /*
    Look for explicit 'format:image' in filename.
  */
  image_info->affirm=image_info->file != (FILE *) NULL;
  p=image_info->filename;
  while (isalnum((int) *p))
    p++;
#if defined(vms)
  if (*(p+1) == '[')
    p++;
#endif
  if ((*p == ':') && ((p-image_info->filename) < (int) sizeof(magick)))
    {
      /*
        User specified image format.
      */
      (void) strncpy(magick,image_info->filename,p-image_info->filename);
      magick[p-image_info->filename]='\0';
      wvStrToUpper(magick);
#if 0	/* defined(macintosh) || defined(WIN32) */
      if (!ImageFormatConflict(magick))
      if (!ImageFormatConflict(magick))
#endif
        {
          /*
            Strip off image format prefix.
          */
          p++;
          (void) strcpy(image_info->filename,p);
          if (strcmp(magick,"IMPLICIT") != 0)
            {
              (void) strcpy(image_info->magick,magick);
              if (strcmp(magick,"TMP") != 0)
                image_info->affirm=True;
              else
                image_info->temporary=True;
            }
        }
    }
  if (rectify)
    {
      char
        filename[MaxTextExtent];

      MagickInfo
        *magick_info;

      /*
        Rectify multi-image file support.
      */
      FormatString(filename,image_info->filename,0);
      if ((strcmp(filename,image_info->filename) != 0) &&
          (strchr(filename,'%') == (char *) NULL))
        image_info->adjoin=False;
      magick_info=(MagickInfo *) GetMagickInfo(magick);
      if (magick_info != (MagickInfo *) NULL)
        image_info->adjoin&=magick_info->adjoin;
      return;
    }
  if (image_info->affirm)
    return;
  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return;
  /*
    Determine the image format from the first few bytes of the file.
  */
  (void) strcpy(image->filename,image_info->filename);
  status=OpenBlob(image_info,image,ReadBinaryType);
  if (status == False)
    return;
  if ((image->blob.data != (char *) NULL)  || !image->exempt)
    (void) ReadBlob(image,MaxTextExtent,magick);
  else
    {
      FILE
        *file;

      register int
        c,
        i;

      /*
        Copy standard input or pipe to temporary file.
      */
      image_info->file=(FILE *) NULL;
      TemporaryFilename(image->filename);
      image_info->temporary=True;
      FormatString(image_info->filename,"%.1024s",image->filename);
      file=fopen(image->filename,WriteBinaryType);
      if (file == (FILE *) NULL)
        {
          MagickWarning(FileOpenWarning,"Unable to write file",image->filename);
          return;
        }
      i=0;
      for (c=fgetc(image->file); c != EOF; c=fgetc(image->file))
      {
        if (i < MaxTextExtent)
          magick[i++]=c;
        (void) putc(c,file);
      }
      (void) fclose(file);
    }
  DestroyImage(image);
  magick[MaxTextExtent-1]='\0';
  if (strncmp(magick,"BEGMF",3) == 0)
    (void) strcpy(image_info->magick,"CGM");
  if (strncmp(magick,"digraph",7) == 0)
    (void) strcpy(image_info->magick,"DOT");
  if (strncmp(magick,"#FIG",4) == 0)
    (void) strcpy(image_info->magick,"FIG");
  if (strncmp(magick,"#!/usr/local/bin/gnuplot",24) == 0)
    (void) strcpy(image_info->magick,"GPLT");
  if (strncmp(magick,"IN;",3) == 0)
    (void) strcpy(image_info->magick,"HPGL");
  if (strncmp(magick+8,"ILBM",2) == 0)
    (void) strcpy(image_info->magick,"ILBM");
  if ((magick[0] == 0x00) && (magick[1] == 0x00))
    if ((magick[2] == 0x01) && ((unsigned char) magick[3] == 0xb3))
      (void) strcpy(image_info->magick,"M2V");
  if (strncmp(magick,"#?RADIANCE",10) == 0)
    (void) strcpy(image_info->magick,"RAD");
  if (strncmp(magick,"gimp xcf file",13) == 0)
    (void) strcpy(image_info->magick,"XCF");
  for (r=GetMagickInfo((char *) NULL); r != (MagickInfo *) NULL; r=r->next)
    if (r->magick)
      if (r->magick((unsigned char *) magick,MaxTextExtent))
        (void) strcpy(image_info->magick,r->tag);
}

Export void CompositeImage(Image *image,const CompositeOperator compose,
  Image *composite_image,const int x_offset,const int y_offset)
{
  int
    y;

  long
    blue,
    green,
    index,
    red;

  Quantum
    shade;

  register int
    i,
    runlength,
    x;

  register RunlengthPacket
    *p,
    *q;

  /*
    Check composite geometry.
  */
  assert(image != (Image *) NULL);
  assert(composite_image != (Image *) NULL);
  if (((x_offset+(int) image->columns) < 0) ||
      ((y_offset+(int) image->rows) < 0) ||
      (x_offset > (int) image->columns) || (y_offset > (int) image->rows))
    {
      MagickWarning(ResourceLimitWarning,"Unable to composite image",
        "geometry does not contain image");
      return;
    }
  /*
    Image must be uncompressed.
  */
  if (!UncondenseImage(image))
    return;
  if (!UncondenseImage(composite_image))
    return;
  switch (compose)
  {
    case XorCompositeOp:
    case PlusCompositeOp:
    case MinusCompositeOp:
    case AddCompositeOp:
    case SubtractCompositeOp:
    case DifferenceCompositeOp:
    case BumpmapCompositeOp:
    case BlendCompositeOp:
    case ReplaceRedCompositeOp:
    case ReplaceGreenCompositeOp:
    case ReplaceBlueCompositeOp:
    {
      image->class=DirectClass;
      break;
    }
    case ReplaceMatteCompositeOp:
    {
      image->class=DirectClass;
      image->matte=True;
      break;
    }
    case DisplaceCompositeOp:
    {
      ColorPacket
        interpolated_color;

      double
        x_displace,
        y_displace;

      double
        horizontal_scale,
        vertical_scale;

      Image
        *displaced_image;

      register RunlengthPacket
        *r;

      /*
        Allocate the displaced image.
      */
      composite_image->orphan=True;
      displaced_image=CloneImage(composite_image,composite_image->columns,
        composite_image->rows,False);
      composite_image->orphan=False;
      if (displaced_image == (Image *) NULL)
        {
          MagickWarning(ResourceLimitWarning,"Unable to display image",
            "Memory allocation failed");
          return;
        }
      horizontal_scale=20.0;
      vertical_scale=20.0;
      if (composite_image->geometry != (char *) NULL)
        {
          int
            count;

          /*
            Determine the horizontal and vertical displacement scale.
          */
          count=sscanf(composite_image->geometry,"%lfx%lf\n",
            &horizontal_scale,&vertical_scale);
          if (count == 1)
            vertical_scale=horizontal_scale;
        }
      /*
        Shift image pixels as defined by a displacement map.
      */
      p=composite_image->pixels;
      runlength=p->length+1;
      r=displaced_image->pixels;
      for (y=0; y < (int) composite_image->rows; y++)
      {
        if (((y_offset+y) < 0) || ((y_offset+y) >= (int) image->rows))
          {
            p+=composite_image->columns;
            continue;
          }
        q=image->pixels+(y_offset+y)*image->columns+x_offset;
        for (x=0; x < (int) composite_image->columns; x++)
        {
          if (runlength != 0)
            runlength--;
          else
            {
              p++;
              runlength=p->length;
            }
          if (((x_offset+x) < 0) || ((x_offset+x) >= (int) image->columns))
            {
              q++;
              continue;
            }
          x_displace=(horizontal_scale*((double) Intensity(*p)-
            ((MaxRGB+1) >> 1)))/((MaxRGB+1) >> 1);
          y_displace=x_displace;
          if (composite_image->matte)
            y_displace=(vertical_scale*((double) p->index-
              ((MaxRGB+1) >> 1)))/((MaxRGB+1) >> 1);
          interpolated_color=
            InterpolateColor(image,x_offset+x+x_displace,y_offset+y+y_displace);
          r->red=interpolated_color.red;
          r->green=interpolated_color.green;
          r->blue=interpolated_color.blue;
          r->index=interpolated_color.index;
          r->length=0;
          q++;
          r++;
        }
      }
      composite_image=displaced_image;
      break;
    }
    case ReplaceCompositeOp:
    {
      /*
        Promote image to DirectClass if colormaps differ.
      */
      if (image->class == PseudoClass)
        {
          if ((composite_image->class == DirectClass) ||
              (composite_image->colors != image->colors))
            image->class=DirectClass;
          else
            {
              int
                status;

              status=memcmp((char *) composite_image->colormap,
                (char *) image->colormap,composite_image->colors*
                sizeof(ColorPacket));
              if (status != 0)
                image->class=DirectClass;
            }
        }
      if (image->matte && !composite_image->matte)
        MatteImage(composite_image);
      break;
    }
    default:
    {
      /*
        Initialize image matte data.
      */
      if (!image->matte)
        {
          image->class=DirectClass;
          if (compose != AnnotateCompositeOp)
            MatteImage(image);
        }
      if (!composite_image->matte)
        {
          p=composite_image->pixels;
          red=p->red;
          green=p->green;
          blue=p->blue;
          if (IsMonochromeImage(composite_image))
            {
              red=composite_image->background_color.red;
              green=composite_image->background_color.green;
              blue=composite_image->background_color.blue;
            }
          for (i=0; i < (int) composite_image->packets; i++)
          {
            p->index=Opaque;
            if ((p->red == red) && (p->green == green) &&
                (p->blue == blue))
              p->index=Transparent;
            p++;
          }
          composite_image->class=DirectClass;
          composite_image->matte=True;
        }
      break;
    }
  }
  /*
    Initialize composited image.
  */
  composite_image->tainted=True;
  p=composite_image->pixels;
  runlength=p->length+1;
  for (y=0; y < (int) composite_image->rows; y++)
  {
    if (((y_offset+y) < 0) || ((y_offset+y) >= (int) image->rows))
      {
        p+=composite_image->columns;
        continue;
      }
    q=image->pixels+(y_offset+y)*image->columns+x_offset;
    for (x=0; x < (int) composite_image->columns; x++)
    {
      if (runlength != 0)
        runlength--;
      else
        {
          p++;
          runlength=p->length;
        }
      if (((x_offset+x) < 0) || ((x_offset+x) >= (int) image->columns))
        {
          q++;
          continue;
        }
      switch (compose)
      {
        case AnnotateCompositeOp:
        case OverCompositeOp:
        default:
        {
          if (p->index == Transparent)
            {
              red=q->red;
              green=q->green;
              blue=q->blue;
              index=q->index;
            }
          else
            if (p->index == Opaque)
              {
                red=p->red;
                green=p->green;
                blue=p->blue;
                index=p->index;
              }
            else
              {
                red=(long) ((unsigned long)
                  (p->red*p->index+q->red*(Opaque-p->index))/Opaque);
                green=(long) ((unsigned long)
                  (p->green*p->index+q->green*(Opaque-p->index))/Opaque);
                blue=(long) ((unsigned long)
                  (p->blue*p->index+q->blue*(Opaque-p->index))/Opaque);
                index=(long) ((unsigned long)
                  (p->index*p->index+q->index*(Opaque-p->index))/Opaque);
              }
          break;
        }
        case InCompositeOp:
        {
          red=((unsigned long) (p->red*q->index)/Opaque);
          green=((unsigned long) (p->green*q->index)/Opaque);
          blue=((unsigned long) (p->blue*q->index)/Opaque);
          index=((unsigned long) (p->index*q->index)/Opaque);
          break;
        }
        case OutCompositeOp:
        {
          red=((unsigned long) (p->red*(Opaque-q->index))/Opaque);
          green=((unsigned long) (p->green*(Opaque-q->index))/Opaque);
          blue=((unsigned long) (p->blue*(Opaque-q->index))/Opaque);
          index=((unsigned long) (p->index*(Opaque-q->index))/Opaque);
          break;
        }
        case AtopCompositeOp:
        {
          red=((unsigned long)
            (p->red*q->index+q->red*(Opaque-p->index))/Opaque);
          green=((unsigned long)
            (p->green*q->index+q->green*(Opaque-p->index))/Opaque);
          blue=((unsigned long)
            (p->blue*q->index+q->blue*(Opaque-p->index))/Opaque);
          index=((unsigned long)
            (p->index*q->index+q->index*(Opaque-p->index))/Opaque);
          break;
        }
        case XorCompositeOp:
        {
          red=((unsigned long)
            (p->red*(Opaque-q->index)+q->red*(Opaque-p->index))/Opaque);
          green=((unsigned long)
            (p->green*(Opaque-q->index)+q->green*(Opaque-p->index))/Opaque);
          blue=((unsigned long)
            (p->blue*(Opaque-q->index)+q->blue*(Opaque-p->index))/Opaque);
          index=((unsigned long)
            (p->index*(Opaque-q->index)+q->index*(Opaque-p->index))/Opaque);
          break;
        }
        case PlusCompositeOp:
        {
          red=p->red+q->red;
          green=p->green+q->green;
          blue=p->blue+q->blue;
          index=p->index+q->index;
          break;
        }
        case MinusCompositeOp:
        {
          red=p->red-(int) q->red;
          green=p->green-(int) q->green;
          blue=p->blue-(int) q->blue;
          index=Opaque;
          break;
        }
        case AddCompositeOp:
        {
          red=p->red+q->red;
          if (red > MaxRGB)
            red-=(MaxRGB+1);
          green=p->green+q->green;
          if (green > MaxRGB)
            green-=(MaxRGB+1);
          blue=p->blue+q->blue;
          if (blue > MaxRGB)
            blue-=(MaxRGB+1);
          index=p->index+q->index;
          if (index > Opaque)
            index-=(Opaque+1);
          break;
        }
        case SubtractCompositeOp:
        {
          red=p->red-(int) q->red;
          if (red < 0)
            red+=(MaxRGB+1);
          green=p->green-(int) q->green;
          if (green < 0)
            green+=(MaxRGB+1);
          blue=p->blue-(int) q->blue;
          if (blue < 0)
            blue+=(MaxRGB+1);
          index=p->index-(int) q->index;
          if (index < 0)
            index+=(MaxRGB+1);
          break;
        }
        case DifferenceCompositeOp:
        {
          red=AbsoluteValue(p->red-(int) q->red);
          green=AbsoluteValue(p->green-(int) q->green);
          blue=AbsoluteValue(p->blue-(int) q->blue);
          index=AbsoluteValue(p->index-(int) q->index);
          break;
        }
        case BumpmapCompositeOp:
        {
          shade=Intensity(*p);
          red=((unsigned long) (q->red*shade)/Opaque);
          green=((unsigned long) (q->green*shade)/Opaque);
          blue=((unsigned long) (q->blue*shade)/Opaque);
          index=((unsigned long) (q->index*shade)/Opaque);
          break;
        }
        case ReplaceCompositeOp:
        {
          red=p->red;
          green=p->green;
          blue=p->blue;
          index=p->index;
          break;
        }
        case ReplaceRedCompositeOp:
        {
          red=DownScale(Intensity(*p));
          green=q->green;
          blue=q->blue;
          index=q->index;
          break;
        }
        case ReplaceGreenCompositeOp:
        {
          red=q->red;
          green=DownScale(Intensity(*p));
          blue=q->blue;
          index=q->index;
          break;
        }
        case ReplaceBlueCompositeOp:
        {
          red=q->red;
          green=q->green;
          blue=DownScale(Intensity(*p));
          index=q->index;
          break;
        }
        case ReplaceMatteCompositeOp:
        {
          red=q->red;
          green=q->green;
          blue=q->blue;
          index=DownScale(Intensity(*p));
          break;
        }
        case BlendCompositeOp:
        {
          red=((unsigned long)
            (p->red*p->index+q->red*q->index)/Opaque);
          green=((unsigned long)
            (p->green*p->index+q->green*q->index)/Opaque);
          blue=((unsigned long)
            (p->blue*p->index+q->blue*q->index)/Opaque);
          index=Opaque;
          break;
        }
        case DisplaceCompositeOp:
        {
          red=p->red;
          green=p->green;
          blue=p->blue;
          index=p->index;
          break;
        }
      }
      q->red=(Quantum) ((red < 0) ? 0 : (red > MaxRGB) ? MaxRGB : red);
      q->green=(Quantum) ((green < 0) ? 0 : (green > MaxRGB) ? MaxRGB : green);
      q->blue=(Quantum) ((blue < 0) ? 0 : (blue > MaxRGB) ? MaxRGB : blue);
      q->index=(unsigned short) ((index < Transparent) ? Transparent :
        (index > Opaque) ? Opaque : index);
      q->length=0;
      q++;
    }
  }
  if (compose == BlendCompositeOp)
    image->matte=False;
  if (compose == DisplaceCompositeOp)
    {
      image->matte=False;
      DestroyImage(composite_image);
    }
}


Export unsigned int UncondenseImage(Image *image)
{
  int
    length;

  register int
    i,
    j;

  register RunlengthPacket
    *p,
    *q;

  RunlengthPacket
    *uncompressed_pixels;

  assert(image != (Image *) NULL);
  if (image->packets == (image->columns*image->rows))
    return(True);
  /*
    Uncompress runlength-encoded packets.
  */
  uncompressed_pixels=(RunlengthPacket *) ReallocateMemory((char *)
    image->pixels,image->columns*image->rows*sizeof(RunlengthPacket));
  if (uncompressed_pixels == (RunlengthPacket *) NULL)
    {
      MagickWarning(ResourceLimitWarning,"Unable to uncompress image",
        "Memory allocation failed");
      return(False);
    }
  p=uncompressed_pixels+(image->packets-1);
  q=uncompressed_pixels+(image->columns*image->rows-1);
  for (i=0; i < (int) image->packets; i++)
  {
    length=p->length;
    for (j=0; j <= length; j++)
    {
      *q=(*p);
      q->length=0;
      q--;
    }
    p--;
  }
  image->packets=image->columns*image->rows;
  image->pixels=uncompressed_pixels;
  return(True);
}


Export void MatteImage(Image *image)
{
  register int
    i;

  register RunlengthPacket
    *p;

  assert(image != (Image *) NULL);
  image->class=DirectClass;
  image->matte=True;
  p=image->pixels;
  for (i=0; i < (int) image->packets; i++)
  {
    p->index=Opaque;
    p++;
  }
}