/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
%         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
%         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
%         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
%         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
%                                                                             %
%                                                                             %
%                   ImageMagick Image Transform 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"

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   F l i p I m a g e                                                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method FlipImage creates a new image that reflects each scanline in the
%  vertical direction It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the FlipImage method is:
%
%      Image *FlipImage(const Image *image)
%
%  A description of each parameter follows:
%
%    o flipped_image: Method FlipImage returns a pointer to the image
%      after reflecting.  A null image is returned if there is a memory
%      shortage.
%
%    o image: The address of a structure of type Image.
%
%
*/
Export Image *FlipImage(const Image *image)
{
#define FlipImageText  "  Flipping image...  "

  Image
    *flipped_image;

  int
    y;

  register int
    runlength,
    x;

  register RunlengthPacket
    *p,
    *q,
    *s;

  RunlengthPacket
    *scanline;

  /*
    Initialize flipped image attributes.
  */
  assert(image != (Image *) NULL);
  flipped_image=CloneImage(image,image->columns,image->rows,False);
  if (flipped_image == (Image *) NULL)
    {
      MagickWarning(ResourceLimitWarning,"Unable to flip image",
        "Memory allocation failed");
      return((Image *) NULL);
    }
  /*
    Allocate scan line buffer and column offset buffers.
  */
  scanline=(RunlengthPacket *)
    AllocateMemory(image->columns*sizeof(RunlengthPacket));
  if (scanline == (RunlengthPacket *) NULL)
    {
      MagickWarning(ResourceLimitWarning,"Unable to reflect image",
        "Memory allocation failed");
      DestroyImage(flipped_image);
      return((Image *) NULL);
    }
  /*
    Flip each row.
  */
  p=image->pixels;
  runlength=p->length+1;
  q=flipped_image->pixels+flipped_image->packets-1;
  for (y=0; y < (int) flipped_image->rows; y++)
  {
    /*
      Read a scan line.
    */
    s=scanline;
    for (x=0; x < (int) image->columns; x++)
    {
      if (runlength != 0)
        runlength--;
      else
        {
          p++;
          runlength=p->length;
        }
      *s=(*p);
      s++;
    }
    /*
      Flip each column.
    */
    s=scanline+image->columns;
    for (x=0; x < (int) flipped_image->columns; x++)
    {
      s--;
      *q=(*s);
      q->length=0;
      q--;
    }
  }
  FreeMemory((char *) scanline);
  return(flipped_image);
}

Export void CoalesceImages(Image *images)
{
  char
    geometry[MaxTextExtent];

  Image
    *cloned_image,
    *image;

  int
    x,
    y;

  unsigned int
    matte,
    sans;

  SegmentInfo
    bounding_box,
    previous_box;

  /*
    Coalesce the image sequence.
  */
  assert(images != (Image *) NULL);
  if (images->next == (Image *) NULL)
    {
      MagickWarning(OptionWarning,"Unable to coalesce images",
        "image sequence required");
      return;
    }
  for (image=images->next; image != (Image *) NULL; image=image->next)
  {
    assert(image->previous != (Image *) NULL);
    x=0;
    y=0;
    if (image->previous->page != (char *) NULL)
      (void) ParseGeometry(image->previous->page,&x,&y,&sans,&sans);
    previous_box.x1=x;
    previous_box.y1=y;
    previous_box.x2=image->previous->columns+x;
    previous_box.y2=image->previous->rows+y;
    x=0;
    y=0;
    if (image->page != (char *) NULL)
      (void) ParseGeometry(image->page,&x,&y,&sans,&sans);
    if (!image->matte && (x <= previous_box.x1) && (y <= previous_box.y1) &&
        ((image->columns+x) >= previous_box.x2) &&
        ((image->rows+y) >= previous_box.y2))
      continue; /* image completely obscures previous image */
    bounding_box.x1=x < previous_box.x1 ? x : previous_box.x1;
    bounding_box.y1=y < previous_box.y1 ? y : previous_box.y1;
    bounding_box.x2=(image->columns+x) > previous_box.x2 ?
      (image->columns+x) : previous_box.x2;
    bounding_box.y2=(image->rows+y) > (previous_box.y2) ?
      (image->rows+y) : previous_box.y2;
    assert(!image->orphan);
    image->orphan=True;
    cloned_image=CloneImage(image,image->columns,image->rows,True);
    image->orphan=False;
    if (cloned_image == (Image *) NULL)
      {
        MagickWarning(ResourceLimitWarning,"Unable to coalesce images",
          "Memory allocation failed for cloned image");
        return;
      }
    image->columns=(unsigned int) (bounding_box.x2-bounding_box.x1+0.5);
    image->rows=(unsigned int) (bounding_box.y2-bounding_box.y1+0.5);
    image->packets=image->columns*image->rows;
    image->pixels=(RunlengthPacket *) ReallocateMemory((char *)
      image->pixels,image->packets*sizeof(RunlengthPacket));
    if (image->pixels == (RunlengthPacket *) NULL)
      {
        MagickWarning(ResourceLimitWarning,"Unable to coalesce images",
          "Memory reallocation failed");
        return;
      }
    image->matte |=
      ((((bounding_box.x1 != x) ||
         (bounding_box.y1 != y)) &&
        ((bounding_box.x1 != previous_box.x1) ||
         (bounding_box.y1 != previous_box.y1))) ||
       (((bounding_box.x2 != (image->columns+x)) ||
   (bounding_box.y2 != (image->rows+y))) &&
        ((bounding_box.x2 != previous_box.x2) ||
         (bounding_box.y2 != previous_box.y2))) ||
       (((bounding_box.x1 != x) ||
         (bounding_box.y2 != (image->rows+y))) &&
        ((bounding_box.x1 != previous_box.x1) ||
         (bounding_box.y2 != previous_box.y2))) ||
       (((bounding_box.x2 != (image->columns+x)) ||
         (bounding_box.y1 != y)) &&
        ((bounding_box.x2 != previous_box.x2) ||
         (bounding_box.y1 != previous_box.y1))) ||
       (previous_box.x2-previous_box.x1+image->columns <
        bounding_box.x2-bounding_box.x1) ||
       (previous_box.y2-previous_box.y1+image->rows <
        bounding_box.y2-bounding_box.y1));
    matte=image->matte;
    SetImage(image);
    CompositeImage(image,ReplaceCompositeOp,image->previous,
      (int) (previous_box.x1-bounding_box.x1+0.5),
      (int) (previous_box.y1-bounding_box.y1+0.5));
    CompositeImage(image,
      cloned_image->matte ? OverCompositeOp : ReplaceCompositeOp,
      cloned_image,(int) (x-bounding_box.x1+0.5),(int) (y-bounding_box.y1+0.5));
    cloned_image->orphan=True;
    DestroyImage(cloned_image);
    FormatString(geometry,"%ux%u%+d%+d",image->columns,image->rows,
      (int) bounding_box.x1,(int) bounding_box.y1);
    if (image->page != (char *) NULL)
      FreeMemory(image->page);
    image->page=PostscriptGeometry(geometry);
    image->matte=matte;
  }
}

Export Image *CropImage(const Image *image,const RectangleInfo *crop_info)
{
#define CropImageText  "  Cropping image...  "

  Image
    *cropped_image;

  int
    y;

  RectangleInfo
    local_info;

  register int
    runlength,
    x;

  register long
    max_packets;

  register RunlengthPacket
    *p,
    *q;

  /*
    Check crop geometry.
  */
  assert(image != (Image *) NULL);
  assert(crop_info != (const RectangleInfo *) NULL);
  if (((crop_info->x+(int) crop_info->width) < 0) ||
      ((crop_info->y+(int) crop_info->height) < 0) ||
      (crop_info->x >= (int) image->columns) ||
      (crop_info->y >= (int) image->rows))
    {
      MagickWarning(OptionWarning,"Unable to crop image",
        "geometry does not contain any part of the image");
      return((Image *) NULL);
    }
  local_info=(*crop_info);
  if ((local_info.x+(int) local_info.width) > (int) image->columns)
    local_info.width=(unsigned int) ((int) image->columns-local_info.x);
  if ((local_info.y+(int) local_info.height) > (int) image->rows)
    local_info.height=(unsigned int) ((int) image->rows-local_info.y);
  if (local_info.x < 0)
    {
      local_info.width-=(unsigned int) (-local_info.x);
      local_info.x=0;
    }
  if (local_info.y < 0)
    {
      local_info.height-=(unsigned int) (-local_info.y);
      local_info.y=0;
    }
  if ((local_info.width == 0) && (local_info.height == 0))
    {
      int
        x_border,
        y_border;

      register int
        i;

      RunlengthPacket
        corners[4];

      /*
        Set bounding box to the image dimensions.
      */
      x_border=local_info.x;
      y_border=local_info.y;
      local_info.width=0;
      local_info.height=0;
      local_info.x=image->columns;
      local_info.y=image->rows;
      p=image->pixels;
      runlength=p->length+1;
      corners[0]=(*p);
      for (i=1; i <= (int) (image->rows*image->columns); i++)
      {
        if (runlength != 0)
          runlength--;
        else
          {
            p++;
            runlength=p->length;
          }
        if (i == (int) image->columns)
          corners[1]=(*p);
        if (i == (int) (image->rows*image->columns-image->columns+1))
          corners[2]=(*p);
        if (i == (int) (image->rows*image->columns))
          corners[3]=(*p);
      }
      p=image->pixels;
      runlength=p->length+1;
      for (y=0; y < (int) image->rows; y++)
      {
        for (x=0; x < (int) image->columns; x++)
        {
          if (runlength != 0)
            runlength--;
          else
            {
              p++;
              runlength=p->length;
            }
          if (!ColorMatch(*p,corners[0],image->fuzz))
            if (x < local_info.x)
              local_info.x=x;
          if (!ColorMatch(*p,corners[1],image->fuzz))
            if (x > (int) local_info.width)
              local_info.width=x;
          if (!ColorMatch(*p,corners[0],image->fuzz))
            if (y < local_info.y)
              local_info.y=y;
          if (!ColorMatch(*p,corners[2],image->fuzz))
            if (y > (int) local_info.height)
              local_info.height=y;
        }
      }
      if ((local_info.width != 0) || (local_info.height != 0))
        {
          local_info.width-=local_info.x-1;
          local_info.height-=local_info.y-1;
        }
      local_info.width+=x_border*2;
      local_info.height+=y_border*2;
      local_info.x-=x_border;
      if (local_info.x < 0)
        local_info.x=0;
      local_info.y-=y_border;
      if (local_info.y < 0)
        local_info.y=0;
      if ((((int) local_info.width+local_info.x) > (int) image->columns) ||
          (((int) local_info.height+local_info.y) > (int) image->rows))
        {
          MagickWarning(OptionWarning,"Unable to crop image",
            "geometry does not contain image");
          return((Image *) NULL);
        }
    }
  if ((local_info.width == 0) || (local_info.height == 0))
    {
      MagickWarning(OptionWarning,"Unable to crop image",
        "geometry dimensions are zero");
      return((Image *) NULL);
    }
  if ((local_info.width == image->columns) &&
      (local_info.height == image->rows) && (local_info.x == 0) &&
      (local_info.y == 0))
    return((Image *) NULL);
  /*
    Initialize cropped image attributes.
  */
  cropped_image=CloneImage(image,local_info.width,local_info.height,True);
  if (cropped_image == (Image *) NULL)
    {
      MagickWarning(ResourceLimitWarning,"Unable to crop image",
        "Memory allocation failed");
      return((Image *) NULL);
    }
  /*
    Skip pixels up to the cropped image.
  */
  p=image->pixels;
  runlength=p->length+1;
  for (x=0; x < (int) (local_info.y*image->columns+local_info.x); x++)
    if (runlength != 0)
      runlength--;
    else
      {
        p++;
        runlength=p->length;
      }
  /*
    Extract cropped image.
  */
  max_packets=0;
  q=cropped_image->pixels;
  SetRunlengthEncoder(q);
  for (y=0; y < (int) (cropped_image->rows-1); y++)
  {
    /*
      Transfer scanline.
    */
    for (x=0; x < (int) cropped_image->columns; x++)
    {
      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 (max_packets != 0)
            q++;
          max_packets++;
          *q=(*p);
          q->length=0;
        }
    }
    /*
      Skip to next scanline.
    */
    for (x=0; x < (int) (image->columns-cropped_image->columns); x++)
      if (runlength != 0)
        runlength--;
      else
        {
          p++;
          runlength=p->length;
        }
  }
  /*
    Transfer last scanline.
  */
  for (x=0; x < (int) cropped_image->columns; x++)
  {
    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 (max_packets != 0)
          q++;
        max_packets++;
        *q=(*p);
        q->length=0;
      }
  }
  cropped_image->packets=max_packets;
  cropped_image->pixels=(RunlengthPacket *) ReallocateMemory((char *)
    cropped_image->pixels,cropped_image->packets*sizeof(RunlengthPacket));
  return(cropped_image);
}