/* #ident	"@(#)x11:contrib/clients/xloadimage/merge.c 6.10 92/03/03 Labtam" */
/* merge.c:
 *
 * merge two images
 *
 * jim frost 09.27.89
 *
 * Copyright 1989, 1990, 1991 Jim Frost.
 * See included file "copyright.h" for complete copyright information.
 */

#include "copyright.h"
#include "xloadimage.h"

/* if merging bitmaps they don't have to be converted to
 * 24-bit.  this saves a lot of space.
 */

static Image *bitmapToBitmap(src, dest, atx, aty, clipw, cliph, genopp)
     Image          *src, *dest;
     unsigned int    atx, aty, clipw, cliph;
     GeneralOptions *genopp;
{ unsigned int  destlinelen, srclinelen;
  unsigned int  deststart;
  unsigned int  flip;
  unsigned int  x, y;
  byte         *destline, *srcline;
  byte          deststartmask;
  byte          destmask, srcmask;
  byte         *destpixel, *srcpixel;

  destlinelen= (dest->width / 8) + (dest->width % 8 ? 1 : 0);
  srclinelen= (src->width / 8) + (src->width % 8 ? 1 : 0);
  destline= dest->data + (aty * destlinelen);
  srcline= src->data;
  deststart= atx / 8;
  deststartmask= 0x80 >> (atx % 8);
  flip= ((*dest->rgb.red == *(src->rgb.red + 1)) &&
	 (*dest->rgb.green == *(src->rgb.green + 1)) &&
	 (*dest->rgb.blue == *(src->rgb.blue + 1)));
  for (y= 0; y < cliph; y++) {
    destpixel= destline + deststart;
    srcpixel= srcline;
    destmask= deststartmask;
    srcmask= 0x80;
    for (x= 0; x < clipw; x++) {
      if (flip)
	if (*srcpixel & srcmask)
	  *destpixel &= ~destmask;
	else
	  *destpixel |= destmask;
      else
	if (*srcpixel & srcmask)
	  *destpixel |= destmask;
	else
	  *destpixel &= ~destmask;
      destmask >>= 1;
      srcmask >>= 1;
      if (destmask == 0) {
	destmask= 0x80;
	destpixel++;
      }
      if (srcmask == 0) {
	srcmask= 0x80;
	srcpixel++;
      }
    }
    destline += destlinelen;
    srcline += srclinelen;
  }
  if (genopp->verbose)
    printf("done\n");
  return(dest);
}

static Image *anyToTrue(src, dest, atx, aty, clipw, cliph, genopp)
     Image          *src, *dest;
     unsigned int    atx, aty, clipw, cliph;
     GeneralOptions *genopp;
{ Pixel         fg, bg;
  unsigned int  destlinelen, srclinelen;
  unsigned int  deststart;
  unsigned int  x, y;
  byte         *destline, *srcline;
  byte         *destpixel, *srcpixel;
  byte          srcmask;
  Pixel         pixval;

  if (!TRUEP(dest))
    dest= expand(dest);
  if(src->gamma !=1.0)
    gammacorrect(src, 1.0, genopp->verbose);
  if(dest->gamma !=1.0)
    gammacorrect(dest, 1.0, genopp->verbose);

  switch (src->type) {
  case IBITMAP:
    fg= RGB_TO_TRUE(src->rgb.red[1], src->rgb.green[1], src->rgb.blue[1]);
    bg= RGB_TO_TRUE(src->rgb.red[0], src->rgb.green[0], src->rgb.blue[0]);
    destlinelen= dest->width * dest->pixlen;
    srclinelen= (src->width / 8) + (src->width % 8 ? 1 : 0);
    destline= dest->data + (aty * destlinelen);
    srcline= src->data;
    deststart= atx * dest->pixlen;
    for (y= 0; y < cliph; y++) {
      destpixel= destline + deststart;
      srcpixel= srcline;
      srcmask= 0x80;
      if(dest->pixlen == 3)	/* the ususal case */
        for (x= 0; x < clipw; x++) {
          valToMem((*srcpixel & srcmask ? fg : bg), destpixel, 3);
          destpixel += 3;
          srcmask >>= 1;
          if (srcmask == 0) {
            srcpixel++;
            srcmask= 0x80;
          }
        }
      else	/* less ususal case */
        for (x= 0; x < clipw; x++) {
          valToMem((*srcpixel & srcmask ? fg : bg), destpixel, dest->pixlen);
          destpixel += dest->pixlen;
          srcmask >>= 1;
          if (srcmask == 0) {
            srcpixel++;
            srcmask= 0x80;
          }
        }
      destline += destlinelen;
      srcline += srclinelen;
    }
    break;
  
  case IRGB:
    destlinelen= dest->width * dest->pixlen;
    srclinelen= src->width * src->pixlen;
    deststart= atx * dest->pixlen;
    destline= dest->data + (aty * destlinelen);
    srcline= src->data;

    if(src->pixlen == 1)	/* the usual case */
      for (y= 0; y < cliph; y++) {
        destpixel= destline + deststart;
        srcpixel= srcline;
        for (x= 0; x < clipw; x++) {
          pixval= memToVal(srcpixel, 1);
          *(destpixel++)= src->rgb.red[pixval] >> 8;
          *(destpixel++)= src->rgb.green[pixval] >> 8;
          *(destpixel++)= src->rgb.blue[pixval] >> 8;
          srcpixel += 1;
        }
        destline += destlinelen;
        srcline += srclinelen;
      }
    else	/* the less ususal */
      for (y= 0; y < cliph; y++) {
        destpixel= destline + deststart;
        srcpixel= srcline;
        for (x= 0; x < clipw; x++) {
          pixval= memToVal(srcpixel, src->pixlen);
          *(destpixel++)= src->rgb.red[pixval] >> 8;
          *(destpixel++)= src->rgb.green[pixval] >> 8;
          *(destpixel++)= src->rgb.blue[pixval] >> 8;
          srcpixel += src->pixlen;
        }
        destline += destlinelen;
        srcline += srclinelen;
      }
    break;

  case ITRUE:
    destlinelen= dest->width * dest->pixlen;
    srclinelen= src->width * src->pixlen;
    deststart= atx * dest->pixlen;
    destline= dest->data + (aty * destlinelen);
    srcline= src->data;

    for (y= 0; y < cliph; y++) {
      destpixel= destline + deststart;
      srcpixel= srcline;
      for (x= 0; x < clipw; x++) {
	*(destpixel++)= *(srcpixel++);
	*(destpixel++)= *(srcpixel++);
	*(destpixel++)= *(srcpixel++);
      }
      destline += destlinelen;
      srcline += srclinelen;
    }
    break;
  }
  if (genopp->verbose)
    printf("done\n");
  return(dest);
}

/* put src image on dest image
 */

Image *merge(dest, src, atx, aty, imgopp, genopp)
     Image          *dest;
     Image          *src;
     int             atx, aty;
     ImageOptions   *imgopp;
     GeneralOptions *genopp;
{ int clipw, cliph;
  Image *timage,*outimage;

  goodImage(dest, "merge");
  goodImage(src, "merge");

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

  /* adjust clipping of src to fit within dest
   */

  clipw= src->width;
  cliph= src->height;
  if ((atx + clipw < 0) || (aty + cliph < 0) ||
      (atx >= (int)dest->width) ||
      (aty >= (int)dest->height)) /* not on dest, ignore */
    return dest;

  if (atx + clipw > dest->width)
    clipw = dest->width - atx;
  if (aty + cliph > dest->height)
    cliph = dest->height - aty;

  /* extra clipping required for negative offsets
   */

  if ( atx < 0 || aty < 0 ) {
    int clipx, clipy;
 
    if ( atx < 0 ) {
      clipx = -atx;
      clipw += atx;
      atx = 0;
    }
    else
      clipx = 0;
    
    if ( aty < 0 ) {
      clipy = -aty;
      cliph += aty;
      aty = 0;
    }
    else
      clipy = 0;
    
    timage = clip(src, clipx, clipy, clipw, cliph, imgopp, genopp);
  } else 
    timage = src;
 
  if (BITMAPP(dest) && BITMAPP(src))
    outimage= bitmapToBitmap(timage, dest, (unsigned int)atx, (unsigned int)aty,
			     clipw, cliph, genopp);
  else
    outimage= anyToTrue(timage, dest, (unsigned int)atx, (unsigned int)aty,
			clipw, cliph, genopp);
  if (timage != src)
    freeImage(timage);
  outimage->gamma = 1.0;
  return(outimage);
}
