/* 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 "image.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, verbose)
     Image        *src, *dest;
     unsigned int  atx, aty, clipw, cliph;
     unsigned int  verbose;
{ 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 (verbose)
    printf("done\n");
  return(dest);
}

static Image *anyToTrue(src, dest, atx, aty, clipw, cliph, verbose)
     Image        *src, *dest;
     unsigned int  atx, aty, clipw, cliph;
     unsigned int  verbose;
{ 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);

  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;
      for (x= 0; x < clipw; x++) {
	valToMem((*srcpixel & srcmask ? fg : bg), destpixel, 3);
	destpixel += 3;
	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;

    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 (verbose)
    printf("done\n");
  return(dest);
}

/* put src image on dest image
 */

Image *merge(dest, src, atx, aty, verbose)
     Image        *dest;
     Image        *src;
     int           atx, aty;
     unsigned int  verbose;
{ int clipw, cliph;
  int clipped = 0;
  Image *newimage;

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

  if (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 */
    if (verbose)
      printf("not on destination\n");
    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;
    
    clipped = 1;
    src = clip(src, clipx, clipy, clipw, cliph, verbose);
  }
 
  if (BITMAPP(dest) && BITMAPP(src))
    newimage= bitmapToBitmap(src, dest, (unsigned int)atx, (unsigned int)aty,
			     clipw, cliph, verbose);
  else
    newimage= anyToTrue(src, dest, (unsigned int)atx, (unsigned int)aty,
			clipw, cliph, verbose);
  if (clipped)
    freeImage(src);
  return(newimage);
}

/* this creates an image of the specified size by tiling a base image
 */
Image *tile(image, x, y, width, height, verbose)
     Image *image;
     int x, y;
     unsigned int width, height, verbose;
{ Image *base, *tmp;

  if (verbose) {
    printf("  Tiling...");
    fflush(stdout);
  }

  /* if the image is a bitmap merge onto a bitmap image (so we don't
   * need do dither on some displays).  otherwise promote to truecolor
   * since that's what merge() will do anyway.
   */
  if (BITMAPP(image))
    base = newBitImage(width, height);
  else
    base = newTrueImage(width, height);

  while (x < base->width) {
    while(y < base->height) {
      tmp = merge(base, image, x, y, 0);
      if (tmp != base) {
	freeImage(base);
	base = tmp;
      }
      y += image->width;
    }
    x += image->width;
  }
  printf("done.\n");
  return(base);
}
