/* #ident	"@(#)x11:contrib/clients/xloadimage/clip.c 6.10 92/03/03 Labtam" */
/* clip.c:
 *
 * return a new image which is a clipped subsection of the old image
 *
 * jim frost 10.04.89
 *
 * Copyright 1989, 1991 Jim Frost.
 * See included file "copyright.h" for complete copyright information.
 */

#include "copyright.h"
#include "xloadimage.h"
#include <math.h>

#define ABS(x)   ((x) < 0 ? -(x) : (x))

Image *clip(iimage, clipx, clipy, clipw, cliph, imgopp, genopp)
     Image          *iimage;
     int             clipx, clipy;
     unsigned int    clipw, cliph;
     ImageOptions   *imgopp;
     GeneralOptions *genopp;
{ Image *simage = iimage, *image;
  int  dclipx, dclipy;
  unsigned int  dclipw, dcliph;
  unsigned int  x, y;
  unsigned int  slinelen, dlinelen;
  unsigned int  start, dstart;
  byte          startmask, smask;
  byte          dstartmask, dsmask, dmask;
  byte         *sp, *sline, *dp, *dline;
  boolean       border_shows = FALSE;
  Pixel         border_pv;

  goodImage(simage, "clip");

  if (genopp->verbose) {
    printf("  Clipping image...");
    fflush(stdout);
  }

if (RGBP(simage))

  /* sane-ify clip area with respect to image
   */

  dclipx = 0;
  dclipy = 0;
  dclipw = clipw;
  dcliph = cliph;

  if (clipx < 0) {
    dclipx = -clipx;
    clipx = 0;
    border_shows = TRUE;
  }
  if (clipy < 0) {
    dclipy = -clipy;
    clipy = 0;
    border_shows = TRUE;
  }

  if (clipx + dclipw > simage->width) {
    if (clipx > simage->width)
      dclipw = 0;
    else
      dclipw = simage->width - clipx;
    border_shows = TRUE;
  }
  if (dclipx + dclipw > clipw) {
    if (dclipx > clipw)
      dclipw = 0;
    else
      dclipw = clipw - dclipx;
  }

  if (clipy + dcliph > simage->height) {
    if (clipy > simage->height)
      dcliph = 0;
    else
      dcliph = simage->height - clipy;
    border_shows = TRUE;
  }
  if (dclipy + dcliph > cliph) {
    if (dclipy > cliph)
      dcliph = 0;
    else
      dcliph = cliph - dclipy;
  }

  if (genopp->verbose) {
    if (border_shows)
      printf("(Adding border)");
    printf("...");
    fflush(stdout);
  }

  /* If the background is going to show after clipping
   * (ie. we are clipping the image to to make it larger
   * rather than smaller), then look up a suitable pixel
   * value to use as the background value. If we can't find
   * a close match, allocate a new color, or as a last
   * resort, convert the image to a true color image
   */
  if (border_shows) {
    Intensity red,green,blue;
    int i,bestd;
    if (imgopp->border) {
      red = imgopp->bordercol.red;
      green = imgopp->bordercol.green;
      blue = imgopp->bordercol.blue;
    } else {
      red = green = blue = 65535;	/* default is white */
    }

    /* Gamma correct the background colour to suit image */
    red = (int)(0.5 + 65535 * pow( (double)red / 65535.0, 1.0/simage->gamma ));
    green = (int)(0.5 + 65535 * pow( (double)green / 65535.0, 1.0/simage->gamma ));
    blue = (int)(0.5 + 65535 * pow( (double)blue / 65535.0, 1.0/simage->gamma ));

    switch (simage->type) {
    case IBITMAP:
    case IRGB:

      bestd = 65535; border_pv = 0;
      for (i= 0; i < simage->rgb.used; i++) {
	int temp,cworst;
	temp = *(simage->rgb.red + i)- red;
	cworst = ABS(temp);
        temp = *(simage->rgb.green + i)- green;
	temp = ABS(temp);
        if(temp > cworst)
	  cworst = temp;
        temp = *(simage->rgb.blue + i)- blue;
	temp = ABS(temp);
        if(temp > cworst)
	  cworst = temp;
	if (cworst < bestd) {
	  bestd = cworst;
	  border_pv =i;
	}
      }
      if (bestd > 1024) { /* Failed to find close color */
	border_pv = simage->rgb.used;
	if (border_pv >= simage->rgb.size) {
	  compress(simage,FALSE);
	  border_pv = simage->rgb.used;
        }
	if (border_pv < simage->rgb.size) {
	  /* allocate a new color */
	  *(simage->rgb.red + border_pv) = red;
	  *(simage->rgb.green + border_pv) = green;
	  *(simage->rgb.blue + border_pv) = blue;
	  simage->rgb.used++;
        } else {
	  /* Umm. bump this image up to the next highest type */
	  if (BITMAPP(simage)) {
	    simage = expandbittoirgb(simage,8);
	    border_pv  = 2;
	    *(simage->rgb.red + border_pv) = red;
	    *(simage->rgb.green + border_pv) = green;
	    *(simage->rgb.blue + border_pv) = blue;
	    simage->rgb.used++;
	  } else {
	    simage = expand(simage);
	    border_pv =  RGB_TO_TRUE(red,green,blue);
	  }
	}
      }
      break;
    case ITRUE:
      border_pv =  RGB_TO_TRUE(red,green,blue);
      break;
    default:
      printf("clip: Unsupported image type\n");
      exit(1);
    }
  }

  switch (simage->type) {
  case IBITMAP:

    /* this could be sped up; i don't care
     */

    image= newBitImage(clipw, cliph);
    for (x= 0; x < simage->rgb.used; x++) {
      *(image->rgb.red + x)= *(simage->rgb.red + x);
      *(image->rgb.green + x)= *(simage->rgb.green + x);
      *(image->rgb.blue + x)= *(simage->rgb.blue + x);
    }
    if (border_shows)
      fill(image, 0, 0, clipw, cliph, border_pv);
    slinelen= (simage->width / 8) + (simage->width % 8 ? 1 : 0);
    start= clipx / 8;
    startmask= 0x80 >> (clipx % 8);
    sline= simage->data + (slinelen * clipy);
    dlinelen= (clipw / 8) + (clipw % 8 ? 1 : 0);
    dstart= dclipx / 8;
    dstartmask= 0x80 >> (dclipx % 8);
    dline= image->data + (dlinelen * dclipy);
    for (y= 0; y < dcliph; y++) {
      sp= sline + start;
      dp= dline + dstart;
      smask= startmask;
      dmask= dstartmask;
      for (x= 0; x < dclipw; x++) {
	if (*sp & smask)
	  *dp |= dmask;
	else
	  *dp &= ~dmask;
	if (! (smask >>= 1)) {
	  smask= 0x80;
	  sp++;
	}
	if (! (dmask >>= 1)) {
	  dmask= 0x80;
	  dp++;
	}
      }
      sline += slinelen;
      dline += dlinelen;
    }
    break;

  case IRGB:
  case ITRUE:
    if (RGBP(simage)) {
      image= newRGBImage(clipw, cliph, simage->depth);
      for (x= 0; x < simage->rgb.used; x++) {
	*(image->rgb.red + x)= *(simage->rgb.red + x);
	*(image->rgb.green + x)= *(simage->rgb.green + x);
	*(image->rgb.blue + x)= *(simage->rgb.blue + x);
      }
      image->rgb.used= simage->rgb.used;
    }
    else
      image= newTrueImage(clipw, cliph);
    if (border_shows)
      fill(image, 0, 0, clipw, cliph, border_pv);
    slinelen= simage->width * simage->pixlen;
    start= clipx * simage->pixlen;
    sline= simage->data + (clipy * slinelen);
    dlinelen = simage->pixlen * clipw;
    dstart= dclipx * simage->pixlen;
    dline= image->data + (dclipy * dlinelen);
    for (y= 0; y < dcliph; y++) {
      sp= sline + start;
      dp= dline + dstart;
      bcopy(sp,dp,simage->pixlen * dclipw);
      sline += slinelen;
      dline += dlinelen;
    }
    break;
  default:
    printf("clip: Unsupported image type\n");
    exit(1);
  }
  image->title= dupString(simage->title);
  image->gamma= simage->gamma;
  if (simage != iimage)	/* free intermediate image */
    freeImage(simage);
  if (genopp->verbose)
    printf("done\n");
  return(image);
}
