/* Copyright 1994 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_qIMVxfer[] =
		"$Id: qIMVxfer.c 87 2005-05-25 01:57:33Z ctm $";
#endif


#include "rsys/common.h"
#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "rsys/cquick.h"

#include "rsys/fatal.h"


/* Helper function: creates a new, sorted table so the value is the
 * same as the index (although we don't bother filling in most of the
 * color table fields).
 */
static CTabPtr
sort_table (CTabPtr old, CTabPtr new, unsigned max_color)
{
  int i;

  memset (&new->ctTable, 0, (max_color + 1) * sizeof (ColorSpec));
  for (i = CW (old->ctSize); i >= 0; i--)
    new->ctTable[CW (old->ctTable[i].value) & max_color].rgb = 
      old->ctTable[i].rgb;

  return new;
}

/* declare a case in the transform switch */
#define TRANSFORM_CASE(name, macro)				\
  case MUNGE (name, 2, FALSE):					\
    CONVERT_BITS (READ_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  NONPAT_NEXT1,					\
		  macro, 2);					\
    break;							\
  case MUNGE (name, 4, FALSE):					\
    CONVERT_BITS (READ_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  NONPAT_NEXT1,					\
		  macro, 4);					\
    break;							\
  case MUNGE (name, 8, FALSE):					\
    CONVERT_BITS (READ_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  NONPAT_NEXT1,					\
		  macro, 8);					\
    break;							\
  case MUNGE (name, 16, FALSE):					\
    CONVERT_BITS (READ_DIRECT16_PIXEL, READ_DIRECT16_PIXEL,	\
		  WRITE_DIRECT16_PIXEL,				\
		  NONPAT_NEXT1,					\
		  macro, 8);					\
    break;							\
  case MUNGE (name, 32, FALSE):					\
    CONVERT_BITS (READ_DIRECT32_PIXEL, READ_DIRECT32_PIXEL,	\
		  WRITE_DIRECT32_PIXEL,				\
		  NONPAT_NEXT1,					\
		  macro, 8);					\
    break;							\
  case MUNGE (name, 2, TRUE):					\
    CONVERT_BITS (READ_PAT_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  PAT_NEXT1,					\
		  macro, 2);					\
    break;							\
  case MUNGE (name, 4, TRUE):					\
    CONVERT_BITS (READ_PAT_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  PAT_NEXT1,					\
		  macro, 4);					\
    break;							\
  case MUNGE (name, 8, TRUE):					\
    CONVERT_BITS (READ_PAT_INDIRECT_PIXEL, READ_INDIRECT_PIXEL,	\
		  WRITE_INDIRECT_PIXEL,				\
		  PAT_NEXT1,					\
		  macro, 8);					\
    break;							\
  case MUNGE (name, 16, TRUE):					\
    CONVERT_BITS (READ_PAT_DIRECT16_PIXEL, READ_DIRECT16_PIXEL,	\
		  WRITE_DIRECT16_PIXEL,				\
		  PAT_NEXT1,					\
		  macro, 8);					\
    break;							\
  case MUNGE (name, 32, TRUE):					\
    CONVERT_BITS (READ_PAT_DIRECT32_PIXEL, READ_DIRECT32_PIXEL,	\
		  WRITE_DIRECT32_PIXEL,				\
		  PAT_NEXT1,					\
		  macro, 8);					\
    break

void
convert_transparent (const PixMap *src1, const PixMap *src2,
		     PixMap *dst,
		     const Rect *r1, const Rect *r2,
		     int16 mode,
		     boolean_t tile_src1_p,
		     int pat_x_offset, int pat_y_offset)
{
  unsigned char *src1_row_base, *src2_row_base, *dst_row_base;
  int src1_rowbytes, src2_rowbytes, dst_rowbytes, src1_deltax, src2_deltax;
  int width, height, s1_width, s1_height;
  RGBColor *hilite_rgb;
  uint32 bk_color, hilite_color;
  
  int bits_per_pixel;
  const rgb_spec_t *rgb_spec;
  
  boolean_t copy1_p, copy2_p;
  write_back_data_t write_back1, write_back2;
  
  bits_per_pixel = CW (src1->pixelSize);
  rgb_spec = pixmap_rgb_spec (src1);
  
  /* For bits_per_pixel == 1, you are just supposed to use the original
   * boolean transfer modes.
   */
  gui_assert (bits_per_pixel > 1);
  
  copy1_p = pixmap_copy_if_screen (src1, r1, &write_back1);
  if (copy1_p)
    {
      src1 = &write_back1.src_pm;
      r1   = &write_back1.src_rect;
    }
  copy2_p = pixmap_copy_if_screen (src2, r2, &write_back2);
  if (copy2_p)
    {
      src2 = &write_back2.src_pm;
      r2   = &write_back2.src_rect;
    }
  
  PIXMAP_ASSERT_NOT_SCREEN (dst);
  
  if (tile_src1_p)
    {
      s1_width = RECT_WIDTH (&src1->bounds);
      s1_height = RECT_HEIGHT (&src1->bounds);
    }
  else
    {
      s1_width = RECT_WIDTH (r1);
      s1_height = RECT_HEIGHT (r1);
    }
  width  = RECT_WIDTH (r2);
  height = RECT_HEIGHT (r2);
  
  /* Grab the rowbytes for the three bitmaps. */
  src1_rowbytes = BITMAP_ROWBYTES (src1);
  src2_rowbytes = BITMAP_ROWBYTES (src2);
  dst_rowbytes = (width * bits_per_pixel + 31) / 32 * 4;
  dst->rowBytes = CW (dst_rowbytes);
  
  /* We want to run x from 0 to width; adding these offsets gives us
   * the real x for the source bitmaps.
   */
  if (tile_src1_p)
    src1_deltax = pat_x_offset;
  else
    src1_deltax = CW (r1->left) - CW (src1->bounds.left);
  src2_deltax = CW (r2->left) - CW (src2->bounds.left);

  /* Compute a pointer to the base of the first row of each bitmap. */
  if (tile_src1_p)
    {
      src1_row_base = (unsigned char *)
	(MR (src1->baseAddr)
	 + (src1_rowbytes * (pat_y_offset & (s1_height - 1))));
    }
  else
    {
      src1_row_base = (unsigned char *)
	(MR (src1->baseAddr)
	 + ((CW (r1->top) - CW (src1->bounds.top)) * src1_rowbytes));
    }
  
  src2_row_base = (unsigned char *)
    (MR (src2->baseAddr)
     + (CW (r2->top) - CW (src2->bounds.top)) * src2_rowbytes);
  dst_row_base = (unsigned char *) MR (dst->baseAddr);
  
#define RGB_TO_INDIRECT_PIXEL(rgb, pixel)	\
  ((void) ((pixel) = Color2Index (rgb)))
  
#define RGB_TO_DIRECT_PIXEL(bpp, rgb, pixel)		\
  ((void)						\
   ({							\
     uint32 swapped_pixel;				\
     							\
     swapped_pixel = ((*rgb_spec->rgbcolor_to_pixel)	\
		      (rgb_spec, rgb, TRUE));		\
     switch (bpp)					\
       {						\
       case 16:						\
	 (pixel) = CW (swapped_pixel);			\
	 break;						\
       case 32:						\
	 (pixel) = CL (swapped_pixel);			\
	 break;						\
       default:						\
	 gui_fatal ("unknown bpp");			\
       }						\
   }))
#define RGB_TO_PIXEL(bpp, rgb, pixel)		\
  ((void)					\
   ((bpp) == 32 || (bpp) == 16			\
    ? RGB_TO_DIRECT_PIXEL (bpp, rgb, pixel)	\
    : RGB_TO_INDIRECT_PIXEL (rgb, pixel)))

  if (CGrafPort_p (thePort))
    bk_color = PORT_BK_COLOR (thePort);
  else
    {
      if (active_screen_addr_p (&PORT_BITS (thePort)))
	{
	  int i;
	  
	  /* defaults */
	  bk_color = 0;
	  
	  for (i = 0; i < 8; i ++)
	    if (PORT_BK_COLOR (thePort) == ROMlib_QDColors[i].value)
	      {
		RGB_TO_PIXEL (bits_per_pixel,
			      &ROMlib_QDColors[i].rgb, bk_color);
		break;
	      }
	}
      else
	{
	  /* FIXME: this might not be right */
	  bk_color = (PORT_BK_COLOR (thePort) == whiteColor
		      ? 0
		      : ((1 << bits_per_pixel) - 1));
	}
    }

  if (CGrafPort_p (thePort))
    hilite_rgb = &CPORT_HILITE_COLOR (theCPort);
  else
    hilite_rgb = &HiliteRGB;
  RGB_TO_PIXEL (bits_per_pixel, hilite_rgb, hilite_color);
  
#define CONVERT_BITS(read1, read2, write, next1, transform, bpp)	\
  {									\
    int x, y;								\
									\
    for (y = 0; y < height; y++)					\
      {									\
	for (x = 0; x < width; x++)					\
	  {								\
	    long src1_v, src2_v, dst_v;					\
									\
	    src1_v = read1 (src1_row_base, x + src1_deltax, bpp);	\
	    src2_v = read2 (src2_row_base, x + src2_deltax, bpp);	\
	    dst_v = transform (src1_v, src2_v);				\
	    								\
	    write (dst_v, dst_row_base, x, bpp);			\
	  }								\
									\
	next1;								\
	src2_row_base += src2_rowbytes;					\
	dst_row_base  += dst_rowbytes;					\
      }									\
  }

#define NONPAT_NEXT1 src1_row_base += src1_rowbytes
#define PAT_NEXT1 \
  src1_row_base = (unsigned char *) (MR (src1->baseAddr)		\
		   + (src1_rowbytes * ((y + pat_y_offset) & (s1_height - 1))))

#define SHIFT_COUNT(x, bpp)  (8 - (bpp) - (bpp) * ((x) & (7 / (bpp))))

#define READ_INDIRECT_PIXEL(b, x, bpp)						\
  ((bpp) == 8									\
   ? b[x]									\
   : ((b[(x) * (bpp) / 8] >> SHIFT_COUNT ((x), (bpp))) & ((1 << (bpp)) - 1)))
#define READ_DIRECT16_PIXEL(b, x, bpp)		\
  ((uint16 *) b)[x]
#define READ_DIRECT32_PIXEL(b, x, bpp)		\
  ((uint32 *) b)[x]

#define READ_PAT_INDIRECT_PIXEL(b, x, bpp)	\
  READ_INDIRECT_PIXEL (b, (x) & (s1_width - 1), bpp)
#define READ_PAT_DIRECT16_PIXEL(b, x, bpp)	\
  READ_DIRECT16_PIXEL (b, (x) & (s1_width - 1), bpp)
#define READ_PAT_DIRECT32_PIXEL(b, x, bpp)	\
  READ_DIRECT32_PIXEL (b, (x) & (s1_width - 1), bpp)

#define WRITE_INDIRECT_PIXEL(v, b, x, bpp)				\
  ((void)								\
   ((bpp) == 8 ? b[x] = (v)						\
  : ({									\
       uint8 *p;							\
       p = &b[(x) * (bpp) / 8];						\
       *p &= ~(((1 << (bpp)) - 1) << SHIFT_COUNT (x, bpp));		\
       *p |= (((v) & ((1 << (bpp)) - 1)) << SHIFT_COUNT (x, bpp));	\
    })))
#define WRITE_DIRECT16_PIXEL(v, b, x, bpp)	\
  ((void)					\
   (((uint16 *) (b))[(x)] = (v)))
#define WRITE_DIRECT32_PIXEL(v, b, x, bpp)	\
  ((void)					\
   (((uint32 *) (b))[(x)] = (v)))

#define TRANSPARENT_TRANSFORM(src1_v, src2_v)	\
  (((typeof (bk_color)) src1_v != bk_color) ? src1_v : src2_v)

#define HILITE_TRANSFORM(src1_v, src2_v)				\
({									\
  long v;								\
									\
  /* only invert the pixel if the src/pattern is `on', ie., not the	\
     background color */						\
  if ((typeof (bk_color))src1_v == bk_color)				\
    v = src2_v;								\
  else									\
    {									\
      if ((typeof (bk_color)) src2_v == bk_color)			\
	v = hilite_color;						\
      else if ((typeof (hilite_color)) src2_v == hilite_color)		\
	v = bk_color;							\
      else								\
	v = src2_v;							\
    }									\
  v;									\
})
  
/* Silly macro to let me switch on both mode, bpp, tile_src1_p. */
#define MUNGE(mode, bpp, ts1) ((mode & 0x3F) + (bpp) * 0x40 + (ts1) * 0x1000)

  switch (MUNGE (mode, bits_per_pixel, tile_src1_p))
    {
      TRANSFORM_CASE (transparent, TRANSPARENT_TRANSFORM);
      TRANSFORM_CASE (hilite, HILITE_TRANSFORM);

    default:
      gui_fatal ("unknown (mode, bpp, tile_p)");
    }
#undef CONVERT_BITS

#undef RGB_TO_PIXEL
#undef RGB_TO_DIRECT_PIXEL
#undef RGB_TO_INDIRECT_PIXEL
  
  /* Set up the dst bitmap's bounds so that rectangle r2 identifies
     the newly created bits.  */
  dst->bounds = *r2;

  if (copy1_p)
    pixmap_free_copy (&write_back1.src_pm);
  if (copy2_p)
    pixmap_free_copy (&write_back2.src_pm);
}

/* This function combines rectangles from two source bitmaps via one of
 * the complex transfer modes described in IMV.  dst must have baseAddr
 * already allocated; rowBytes and bounds will be filled in by this
 * function.
 */
void
convert_pixmap_with_IMV_mode (const PixMap *src1, const PixMap *src2,
			      PixMap *dst,
			      CTabHandle src1_ctabh, CTabHandle src2_ctabh,
			      ITabHandle itabh,
			      const Rect *r1, const Rect *r2,
			      int16 mode, const RGBColor *op_color,
			      boolean_t tile_src1_p,
			      int pat_x_offset, int pat_y_offset)
{
  CTabPtr src1_ctab, src2_ctab;
  unsigned max_color;
  uint8 *src1_row_base, *src2_row_base, *dst_row_base;
  int src1_rowbytes, src2_rowbytes, dst_rowbytes, src1_deltax, src2_deltax;
  int width, height, s1_width, s1_height;
  int itab_res, itab_res_mask;
  const uint8 *itab_array;
  uint32 op_red, op_green, op_blue;
  
  const rgb_spec_t *rgb_spec;
  int bits_per_pixel;
  
  boolean_t copy1_p, copy2_p;
  write_back_data_t write_back1, write_back2;
  
  bits_per_pixel = CW (src1->pixelSize);
  rgb_spec = pixmap_rgb_spec (src1);
  
  /* For bits_per_pixel == 1, you are just supposed to use the original
   * boolean transfer modes.
   */
  gui_assert (bits_per_pixel > 1);
  
  copy1_p = pixmap_copy_if_screen (src1, r1, &write_back1);
  if (copy1_p)
    {
      src1 = &write_back1.src_pm;
      r1   = &write_back1.src_rect;
    }
  copy2_p = pixmap_copy_if_screen (src2, r2, &write_back2);
  if (copy2_p)
    {
      src2 = &write_back2.src_pm;
      r2   = &write_back2.src_rect;
    }
  
  PIXMAP_ASSERT_NOT_SCREEN (dst);
  
  if (tile_src1_p)
    {
      s1_width = RECT_WIDTH (&src1->bounds);
      s1_height = RECT_HEIGHT (&src1->bounds);
    }
  else
    {
      s1_width = RECT_WIDTH (r1);
      s1_height = RECT_HEIGHT (r1);
    }
  width  = RECT_WIDTH (r2);
  height = RECT_HEIGHT (r2);
  
  if (rgb_spec)
    {
      /* initalize to some sane values to shut gcc up */
      src1_ctab = src2_ctab = NULL;
      itab_res = itab_res_mask = 0;
      itab_array = NULL;
    }
  else
    {
      max_color = (1 << bits_per_pixel) - 1;
      
      /* Create a nice, sorted table for src1_ctab. */
      if (CTAB_FLAGS_X (src1_ctabh) & CTAB_GDEVICE_BIT_X)
	{
	  src1_ctab = STARH (src1_ctabh);
	}
      else
	{
	  src1_ctab = sort_table (STARH (src1_ctabh),
				  (CTabPtr) alloca (sizeof *src1_ctab
						    + (max_color
						       * sizeof (ColorSpec))),
				  max_color);
	}

      /* Create a nice, sorted table for src2_ctab. */
      if (CTAB_SEED_X (src1_ctabh) == CTAB_SEED_X (src2_ctabh))
	src2_ctab = src1_ctab;
      else
	{
	  if (CTAB_FLAGS_X (src2_ctabh) & CTAB_GDEVICE_BIT_X)
	    {
	      src2_ctab = STARH (src2_ctabh);
	    }
	  else
	    {
	      src2_ctab = sort_table (STARH (src2_ctabh),
				      (CTabPtr) alloca (sizeof *src2_ctab
							+ (max_color
							   * sizeof (ColorSpec))),
				      max_color);
	    }
	}

      /* Grab the inverse color table.  Note that we are to use this
       * table directly, and *not* call Color2Index.
       */
      itab_res = ITAB_RES (itabh);
      itab_res_mask = (1 << itab_res) - 1;
      itab_array = ITAB_TABLE (itabh);
    }
  
  /* Grab the rowbytes for the three bitmaps. */
  src1_rowbytes = BITMAP_ROWBYTES (src1);
  src2_rowbytes = BITMAP_ROWBYTES (src2);
  dst_rowbytes = (width * bits_per_pixel + 31) / 32 * 4;
  dst->rowBytes = CW (dst_rowbytes);
  
  /* We want to run x from 0 to width; adding these offsets gives us
   * the real x for the source bitmaps.
   */
  if (tile_src1_p)
    src1_deltax = pat_x_offset;
  else
    src1_deltax = CW (r1->left) - CW (src1->bounds.left);
  src2_deltax = CW (r2->left) - CW (src2->bounds.left);
  
  /* Compute a pointer to the base of the first row of each bitmap. */
  if (tile_src1_p)
    {
      src1_row_base = (unsigned char *) (MR (src1->baseAddr)
		       + (src1_rowbytes * (pat_y_offset & (s1_height - 1))));
    }
  else
    {
      src1_row_base = (unsigned char *) (MR (src1->baseAddr)
		       + ((CW (r1->top) - CW (src1->bounds.top))
			  * src1_rowbytes));
    }
  src2_row_base = (unsigned char *) (MR (src2->baseAddr)
		   + (CW (r2->top) - CW (src2->bounds.top)) * src2_rowbytes);
  dst_row_base = (unsigned char *) MR (dst->baseAddr);
  
  /* Fetch the "op color" fields, in case they are needed. */
  op_red   = CW (op_color->red);
  op_green = CW (op_color->green);
  op_blue  = CW (op_color->blue);
  
#define CONVERT_BITS(read1, read2, write, next1, transform, bpp)	\
{									\
  int x, y;								\
  for (y = 0; y < height; y++)						\
    {									\
      for (x = 0; x < width; x++)					\
	{								\
	  /* source 1, 2 rgb's */					\
	  uint32 r1, g1, b1;						\
	  uint32 r2, g2, b2;						\
	  /* result rgb's */						\
	  uint32 rr, gr, br;						\
	  /* final pixel value */					\
	  uint32 p1, p2, pr;						\
									\
	  p1 = read1 (src1_row_base, x + src1_deltax, bpp);		\
	  p2 = read2 (src2_row_base, x + src2_deltax, bpp);		\
									\
	  PIXEL_TO_RGB (bpp, p1, r1, g1, b1, src1_ctab);		\
	  PIXEL_TO_RGB (bpp, p2, r2, g2, b2, src2_ctab);		\
									\
	  transform (r1, g1, b1,					\
		     r2, g2, b2,					\
		     rr, gr, br);					\
									\
	  RGB_TO_PIXEL (bpp, rr, gr, br, pr);				\
									\
	  write (pr, dst_row_base, x, bpp);				\
	}								\
									\
      next1;								\
      src2_row_base += src2_rowbytes;					\
      dst_row_base  += dst_rowbytes;					\
    }									\
}

#define INDIRECT_PIXEL_TO_RGB(pixel, r, g, b, ctab)	\
  ((void)						\
   ({							\
     const RGBColor *color;				\
     color = &ctab->ctTable[pixel].rgb;			\
     (r)   = CW (color->red);				\
     (g) = CW (color->green);				\
     (b)  = CW (color->blue);				\
   }))
#define DIRECT_PIXEL_TO_RGB(bpp, pixel, red_out, green_out, blue_out,	\
			    dummy_ctab)					\
  ((void)								\
   ({									\
     RGBColor color;							\
									\
     (*rgb_spec->pixel_to_rgbcolor) (rgb_spec, (pixel), &color);	\
     (red_out) = CW (color.red);					\
     (green_out) = CW (color.green);					\
     (blue_out) = CW (color.blue);					\
   }))
#define PIXEL_TO_RGB(bpp, pixel, red, green, blue, ctab)	\
  ((void)							\
   ((bpp) == 32 || (bpp) == 16					\
    ? DIRECT_PIXEL_TO_RGB (bpp, pixel, red, green, blue, ctab)	\
    : INDIRECT_PIXEL_TO_RGB (pixel, red, green, blue, ctab)))

#define RGB_TO_INDIRECT_PIXEL(red, green, blue, pixel)	\
  ((pixel) = ITAB_LOOKUP (red, green, blue))
#define RGB_TO_DIRECT_PIXEL(bpp, red, green, blue, pixel)	\
  ((void)							\
   ({								\
     RGBColor color = { red, green, blue };			\
     								\
     (pixel) = ((*rgb_spec->rgbcolor_to_pixel)			\
		(rgb_spec, &color, FALSE));			\
   }))

#define RGB_TO_PIXEL(bpp, red, green, blue, pixel)		\
  ((void)							\
   ((bpp) == 32 || (bpp) == 16					\
    ? RGB_TO_DIRECT_PIXEL (bpp, red, green, blue, pixel)	\
    : RGB_TO_INDIRECT_PIXEL (red, green, blue, pixel)))

#define NONPAT_NEXT1 src1_row_base += src1_rowbytes
#define PAT_NEXT1 \
  src1_row_base = (unsigned char *) (MR (src1->baseAddr)		\
		   + (src1_rowbytes * ((y + pat_y_offset) & (s1_height - 1))))

#define SHIFT_COUNT(x, bpp)  (8 - (bpp) - (bpp) * ((x) & (7 / (bpp))))

#define READ_INDIRECT_PIXEL(b, x, bpp)						\
  ((bpp) == 8									\
   ? b[x]									\
   : ((b[(x) * (bpp) / 8] >> SHIFT_COUNT ((x), (bpp))) & ((1 << (bpp)) - 1)))
#define READ_DIRECT16_PIXEL(b, x, bpp)		\
  ((uint16 *) b)[x]
#define READ_DIRECT32_PIXEL(b, x, bpp)		\
  ((uint32 *) b)[x]

#define READ_PAT_INDIRECT_PIXEL(b, x, bpp)	\
  READ_INDIRECT_PIXEL (b, (x) & (s1_width - 1), bpp)
#define READ_PAT_DIRECT16_PIXEL(b, x, bpp)	\
  READ_DIRECT16_PIXEL (b, (x) & (s1_width - 1), bpp)
#define READ_PAT_DIRECT32_PIXEL(b, x, bpp)	\
  READ_DIRECT32_PIXEL (b, (x) & (s1_width - 1), bpp)

#define WRITE_INDIRECT_PIXEL(v, b, x, bpp)				\
  ((void)								\
   ((bpp) == 8 ? b[x] = (v)						\
  : ({									\
       uint8 *p;							\
       p = &b[(x) * (bpp) / 8];						\
       *p &= ~(((1 << (bpp)) - 1) << SHIFT_COUNT (x, bpp));		\
       *p |= (((v) & ((1 << (bpp)) - 1)) << SHIFT_COUNT (x, bpp));	\
    })))
#define WRITE_DIRECT16_PIXEL(v, b, x, bpp)	\
  ((void)					\
   (((uint16 *) (b))[(x)] = (v)))
#define WRITE_DIRECT32_PIXEL(v, b, x, bpp)	\
  ((void)					\
   (((uint32 *) (b))[(x)] = (v)))

#define ITAB_LOOKUP(r, g, b)			 			      \
  itab_array[((((r) >> (16 - itab_res)) & itab_res_mask) << (2 * itab_res))   \
	     | ((((g) >> (16 - itab_res)) & itab_res_mask) << (1 * itab_res)) \
	     | ((((b) >> (16 - itab_res)) & itab_res_mask) << (0 * itab_res))]

/* Define macros to actually munge together two RGB's in the right way. */
#define ADD_OVER_TRANSFORM(red1, green1, blue1,	\
			   red2, green2, blue2,	\
			   redr, greenr, bluer)	\
  ((void)					\
   (redr = red1 + red2,				\
    greenr = green1 + green2,			\
    bluer = blue1 + blue2))
#define ADD_PIN_TRANSFORM(red1, green1, blue1,	\
			  red2, green2, blue2,	\
			  redr, greenr, bluer)	\
  ((void)					\
   (redr = red1 + red2,				\
    greenr = green1 + green2,			\
    bluer = blue1 + blue2,			\
    redr = MIN (redr, op_red),			\
    greenr = MIN (greenr, op_green),		\
    bluer = MIN (bluer, op_blue)))
#define SUB_OVER_TRANSFORM(red1, green1, blue1,	\
			   red2, green2, blue2,	\
			   redr, greenr, bluer)	\
  ((void)					\
   (redr = red1 - red2,				\
    greenr = green1 - green2,			\
    bluer = blue1 - blue2))
#define SUB_PIN_TRANSFORM(red1, green1, blue1,	\
			  red2, green2, blue2,	\
			  redr, greenr, bluer)	\
  ((void)					\
   (redr = red1 - red2,				\
    greenr = green1 - green2,			\
    bluer = blue1 - blue2,			\
    redr = MAX (redr, op_red),			\
    greenr = MAX (greenr, op_green),		\
    bluer = MAX (bluer, op_blue)))
#define AD_MAX_TRANSFORM(red1, green1, blue1,	\
			 red2, green2, blue2,	\
			 redr, greenr, bluer)	\
  ((void)					\
   (redr = MAX (red1, red2),			\
    greenr = MAX (green1, green2),		\
    bluer = MAX (blue1, blue2)))
#define AD_MIN_TRANSFORM(red1, green1, blue1,	\
			 red2, green2, blue2,	\
			 redr, greenr, bluer)	\
  ((void)					\
   (redr = MIN (red1, red2),			\
    greenr = MIN (green1, green2),		\
    bluer = MIN (blue1, blue2)))
#define BLEND_TRANSFORM(red1, green1, blue1,				\
			red2, green2, blue2,				\
			redr, greenr, bluer)				\
  ((void)								\
   (redr = (red1 * op_red + red2 * (65535 - op_red)) / 65535,		\
    greenr = (green1 * op_green + green2 * (65535 - op_green)) / 65535,	\
    bluer = (blue1 * op_blue + blue2 * (65535 - op_blue)) / 65535))

/* Silly macro to let me switch on both mode, bpp, tile_src1_p. */
#define MUNGE(mode, bpp, ts1) ((mode & 0x3F) + (bpp) * 0x40 + (ts1) * 0x1000)

  switch (MUNGE (mode, bits_per_pixel, tile_src1_p))
    {
      TRANSFORM_CASE (addOver, ADD_OVER_TRANSFORM);
      TRANSFORM_CASE (addPin, ADD_PIN_TRANSFORM);
      TRANSFORM_CASE (subOver, SUB_OVER_TRANSFORM);
      TRANSFORM_CASE (subPin, SUB_PIN_TRANSFORM);
      TRANSFORM_CASE (adMin, AD_MIN_TRANSFORM);
      TRANSFORM_CASE (adMax, AD_MAX_TRANSFORM);
      TRANSFORM_CASE (blend, BLEND_TRANSFORM);
    default:
      gui_fatal ("invalid case");
    }
#undef CONVERT_BITS

  /* Set up the dst bitmap's bounds so that rectangle r2 identifies
   * the newly created bits.
   */
  dst->bounds = *r2;

  if (copy1_p)
    pixmap_free_copy (&write_back1.src_pm);
  if (copy2_p)
    pixmap_free_copy (&write_back2.src_pm);
}

