/* Copyright 1986, 1988, 1989, 1990, 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

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

/* Forward declarations in QuickDraw.h (DO NOT DELETE THIS LINE) */

#include "rsys/common.h"

#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "MemoryMgr.h"
#include "ToolboxUtil.h"

#include "rsys/stdbits.h"
#include "rsys/quick.h"
#include "rsys/cquick.h"
#include "rsys/region.h"
#include "rsys/gworld.h"
#include "rsys/picture.h"
#include "rsys/mman.h"
#include "rsys/flags.h"
#include "rsys/host.h"
#include "rsys/tempalloc.h"

static void ROMlib_real_copy_bits (PixMap *src, PixMap *dst,
				   const Rect *src_rect, const Rect *dst_rect,
				   INTEGER mode, RgnHandle mask);

static boolean_t
src_dst_overlap_and_dst_below_src_p (const Rect *srcr, const Rect *dstr,
				     int dh, int dv)
{
  if (   (CW (dstr->top))       <  (CW (srcr->bottom) + dv)
      && (CW (srcr->top) + dv)  <= (CW (dstr->top))
      && (CW (srcr->left) + dh) <  (CW (dstr->right))
      && (CW (dstr->left))      <  (CW (srcr->right) + dh))
    return TRUE;
  else
    return FALSE;
}

static inline boolean_t
dy_zero_p (const Rect *srcr, const Rect *dstr,
	   int dh, int dv)
{
  return (CW (srcr->top) + dv) == CW (dstr->top);
}

void
canonicalize_bogo_map_cleanup (BitMap *bogo_map,
			       struct cleanup_info *info)
{
  switch (info->cleanup_type)
    {
    case cleanup_state:
      HSetState (info->data.handle, info->h_state);
      break;
    case cleanup_free:
      DisposHandle (info->data.handle);
      break;
    case cleanup_unlock_gworld_pixels:
      UnlockPixels (info->data.pixmap_handle);
      break;
    case cleanup_unlock_gworld_pixels_update:
      {
	gw_info_t *gw_info;

	gw_info = info->data.gw_info;
	
	UnlockPixels (gw_info->gw_pixmap);
	BITMAP_BASEADDR_X ((PixMap *) bogo_map)
	  = PIXMAP_BASEADDR_X (gw_info->gw_pixmap);
	break;
      }
    case cleanup_none:
      break;
    default:
      gui_fatal ("unknown cleanup type `%d'", info->cleanup_type);
    }
}

void
canonicalize_bogo_map (BitMap *bogo_map, PixMap **canonical_addr,
		       struct cleanup_info *info)
{
  int high_bits = ((unsigned short) CW (bogo_map->rowBytes)) >> 14;
  int low_bit = ((unsigned short) CW (bogo_map->rowBytes)) & 1;
  
  switch (high_bits)
    {
      /* (BitMap *) */
    case 0:
      {
	PixMapHandle gd_pmap;
	PixMap *canonical = *canonical_addr;
	
	canonical->baseAddr = bogo_map->baseAddr;
	canonical->bounds = bogo_map->bounds;
	
	canonical->pmVersion = CWC (0);
	
	/* no packing currently supported */
	canonical->packType = CWC (0);
	canonical->packSize = CWC (0);
	
	canonical->vRes = canonical->hRes = CWC (72);
	
	gd_pmap = GD_PMAP (MR (TheGDevice));
	if (canonical->baseAddr == PIXMAP_BASEADDR_X (gd_pmap))
	  {
	    pixmap_set_pixel_fields (canonical, PIXMAP_PIXEL_SIZE (gd_pmap));
	    canonical->rowBytes = (PIXMAP_ROWBYTES_X (gd_pmap)
				   | PIXMAP_DEFAULT_ROW_BYTES_X);
	    canonical->pmTable = PIXMAP_TABLE_X (gd_pmap);

	    info->cleanup_type = cleanup_none;
	  }
	else
	  {
	    pixmap_set_pixel_fields (canonical, 1);
	    
	    canonical->rowBytes = (bogo_map->rowBytes
				   | PIXMAP_DEFAULT_ROW_BYTES_X);
	    info->cleanup_type = cleanup_none;
	    canonical->pmTable = RM (validate_relative_bw_ctab ());
	  }
	canonical->planeBytes = CWC (0);

	canonical->pmReserved = CWC (0);
	break;
      }

      /* (unknown) */
    case 1:
      gui_abort ();
      
      /* pointer to pixmap */
    case 2:
      {
	gw_info_t *gw_info;
	
	gw_info = (lookup_gw_info_by_gw_pixmap_baseaddr
		   (BITMAP_BASEADDR ((PixMap *) bogo_map)));
	if (gw_info)
	  {
	    LockPixels (gw_info->gw_pixmap);
	    BITMAP_BASEADDR_X ((PixMap *) bogo_map)
	      = PIXMAP_BASEADDR_X (gw_info->gw_pixmap);
	    
	    info->cleanup_type = cleanup_unlock_gworld_pixels_update;
	    info->data.gw_info = gw_info;
	  }
	else
	  info->cleanup_type = cleanup_none;
	
	*canonical_addr = (PixMap *) bogo_map;
	break;
      }
      
      /* (CGrafPtr spew) */
    case 3:
      if (low_bit)
	{
	  CGrafPtr gw;
	  PixMapHandle pixmap;
	  
	  gw = (CGrafPtr) ((char *) bogo_map - 2);
	  pixmap = CPORT_PIXMAP (gw);
	  
	  *canonical_addr = STARH (pixmap);
	  info->cleanup_type = cleanup_unlock_gworld_pixels;
	  info->data.pixmap_handle = pixmap;
	  
	  /* lock the pixels */
	  LockPixels (pixmap);
	}
      else
	{
	  PixMapHandle pixmap_handle = MR (*(PixMapHandle *) bogo_map);
	  
	  *canonical_addr = STARH (pixmap_handle);
	  
	  info->cleanup_type = cleanup_state;
	  info->data.pixmap_handle = pixmap_handle;
	  info->h_state = HGetState ((Handle) pixmap_handle);
	  
	  /* lock the pixmap handle */
	  HSetState ((Handle) pixmap_handle, info->h_state | LOCKBIT);
	  
	  break;
	}
    }
#if !defined (NDEBUG)
  if (  (RECT_WIDTH (&BITMAP_BOUNDS (*canonical_addr))
	 * CW ((*canonical_addr)->pixelSize)
	 / 8)
      > BITMAP_ROWBYTES (*canonical_addr))
    warning_unexpected ("unlikely map");
#endif
}

static void
write_copybits_picdata (PixMap *src, PixMap *dst,
			const Rect *src_rect, const Rect *dst_rect,
			int16 mode, RgnHandle mask)
{
  int32 zero = 0;
  int16 opcode;
  int16 swapped_mode;
  int16 row_bytes;
  int16 temp_pixmap_row_bytes;
  int16 pack_type;
  int16 pixel_size;
  int height;
  int i;
  boolean_t direct_bits_p;
  
  {
    /* always copy the src pixmap since the code below, as it is
       written, will always record the _entire_ src, which is very bad
       if the src is the screen, or some other large image */
    /* it is also necessary to copy when we are the src is the actual
       screen */
    PixMap *_src;
    Rect *_src_rect;
    
    _src = alloca (sizeof *_src);
    _src_rect = alloca (sizeof *_src_rect);
    
    ZONE_SAVE_EXCURSION
      (ApplZone,
       {
	 if (FreeMemSys () >= FreeMem ())
	   TheZone = SysZone;
	 
	 pixmap_copy (src, src_rect,
		      _src, _src_rect);
       });
    
    src      = _src;
    src_rect = _src_rect;
  }

  row_bytes = BITMAP_ROWBYTES (src);
  pixel_size = CW (src->pixelSize);
  
  direct_bits_p = (pixel_size == 16 || pixel_size == 32);
  if (pixel_size == 32)
    pack_type = CWC (2);
  else
    pack_type = CWC (0);
  
  if (direct_bits_p)
    opcode = mask ? OP_DirectBitsRgn : OP_DirectBitsRect;
  else if (row_bytes < 8)
    opcode = mask ? OP_BitsRgn : OP_BitsRect;
  else
    opcode = mask ? OP_PackBitsRgn : OP_PackBitsRect;
  
  ROMlib_drawingpicupdate ();
  PICOP (opcode);
  
  if (direct_bits_p)
    {
      int32 swapped_bogo_baseaddr = CLC (0xFF);
      
      PICWRITE (&swapped_bogo_baseaddr, sizeof swapped_bogo_baseaddr);
    }
  
  temp_pixmap_row_bytes = src->rowBytes | PIXMAP_DEFAULT_ROWBYTES_X;
  PICWRITE (&temp_pixmap_row_bytes, sizeof temp_pixmap_row_bytes);
  
  PICWRITE (&src->bounds, sizeof src->bounds);
  PICWRITE (&src->pmVersion, sizeof src->pmVersion);
  
  /* pack type of bits stored in the picture may not necessarily match
     the pack type of the actual pixmap */
  PICWRITE (&pack_type, sizeof pack_type);
  
  PICWRITE (&src->packSize, sizeof src->packSize);
  PICWRITE (&src->hRes, sizeof src->hRes);
  PICWRITE (&src->vRes, sizeof src->vRes);
  PICWRITE (&src->pixelType, sizeof src->pixelType);
  PICWRITE (&src->pixelSize, sizeof src->pixelSize);
  PICWRITE (&src->cmpCount, sizeof src->cmpCount);
  PICWRITE (&src->cmpSize, sizeof src->cmpSize);
  PICWRITE (&src->planeBytes, sizeof src->planeBytes);
  PICWRITE (&zero, sizeof zero);
  PICWRITE (&src->pmReserved, sizeof src->pmReserved);
  
  if (! direct_bits_p)
    {
      LOCK_HANDLE_EXCURSION_1
	(MR (src->pmTable),
	 {
	   CTabPtr ctab;
	   
	   ctab = STARH (MR (src->pmTable));
	   
	   /* write out the src color table */
	   PICWRITE (&zero, sizeof zero);
	   PICWRITE (&ctab->ctFlags, sizeof ctab->ctFlags);
	   PICWRITE (&ctab->ctSize, sizeof ctab->ctSize);
	   for (i = 0; i <= CW (ctab->ctSize); i ++)
	     {
	       ColorSpec *elt;

	       elt = &ctab->ctTable[i];
	       PICWRITE (&elt->value, sizeof elt->value);
	       PICWRITE (&elt->rgb, sizeof elt->rgb);
	     }
	 });
    }
  
  PICWRITE (src_rect, sizeof *src_rect);
  PICWRITE (dst_rect, sizeof *dst_rect);
  swapped_mode = CW (mode);
  PICWRITE (&swapped_mode, sizeof swapped_mode);
  if (mask)
    {
      LOCK_HANDLE_EXCURSION_1
	(mask,
	 {
	   PICWRITE (STARH (mask), Hx (mask, rgnSize));
	 });
    }
  height = RECT_HEIGHT (&src->bounds);
  if (row_bytes < 8 || pack_type == CWC (2))
    {
      if (pack_type == CWC (2))
	{
	  uint8 *current, *end;
	  
	  current = (uint8 *) MR (src->baseAddr) + 1;
	  end = current + row_bytes * height;
	  
	  while (current < end)
	    {
	      PICWRITE (current, 3 * sizeof *current);
	      current += 4;
	    }
	  if ((row_bytes / 4 * height) & 1)
	    PICWRITE ("", 1);
	}
      else
	PICWRITE (MR (src->baseAddr), row_bytes * height);
    }
  else
    {
      HIDDEN_Ptr ip, op;
      int parity;
      uint8 *packed_line;
      int8 *countloc;
      int16 count, swappedcount, countsize;
      uint8 *baseaddr;

      /* i copied the code below from the executor 1.2 implementation
         of StdBits pic recording.  i can't say i fully understand it */
      
      /* #### why the extra 5 bytes? */
      packed_line = alloca (row_bytes + 5);
      baseaddr = (uint8 *) MR (src->baseAddr);
      ip.p = (Ptr) baseaddr;
      parity = 0;
      
      if (row_bytes > 250)
	{
	  countloc = (int8 *) &swappedcount;
	  countsize = 2;
	}
      else
	{
	  countloc = (int8 *) &swappedcount + 1;
	  countsize = 1;
	}

      for (i = 0; i < height; i ++)
	{
	  op.p = (Ptr) RM (packed_line);
	  gui_assert ((uint8 *) ip.p == &baseaddr[row_bytes * i]);
	  ip.p = RM (ip.p);
	  PackBits (&ip, &op, row_bytes);
	  op.p = MR (op.p);
	  ip.p = MR (ip.p);
	  count = op.p - (Ptr) packed_line;
	  parity += count + countsize;
	  swappedcount = CW (count);
	  PICWRITE (countloc, countsize);
	  PICWRITE (packed_line, count);
	}
      /* even things up */
      if (parity & 1)
	PICWRITE ("", 1);
    }
  
  pixmap_free_copy (src);
}

void
ROMlib_bogo_stdbits (BitMap *src_bogo_map, BitMap *dst_bogo_map,
		     const Rect *src_rect, const Rect *dst_rect,
		     short mode, RgnHandle mask)
{
  Rect dummy_rect;
  
  PixMap dummy_space[2];
  PixMap *src = &dummy_space[0], *dst = &dummy_space[1];
  
  struct cleanup_info cleanup_info[2];
  
  if (CW (dst_rect->bottom) <= CW (dst_rect->top)
      || CW (dst_rect->right) <= CW (dst_rect->left)
      || (mask && !SectRect (dst_rect, &HxX (mask, rgnBBox), &dummy_rect)))
    return;
  
  /* the incoming source and destinations can be any one of the
     following;

     a `BitMap *', the high two bits of rowBytes must be zero,
     or CGrafPort coerced to a GrafPort with the PortBits
     passed in; in this case the high two bits of rowBytes
     must be `11'; and the baseAddr field is a `PixMapHandle'

     each of these types are canonicalized into a `PixMap *', which
     is what we work with here */
  /* these functions lock the PixMapHandle if src or dst
     are from a coerced CGrafPort; we must make sure
     to unlock those handles on the way out */
  canonicalize_bogo_map (src_bogo_map, &src, &cleanup_info[0]);
  canonicalize_bogo_map (dst_bogo_map, &dst, &cleanup_info[1]);
  
  PIC_SAVE_EXCURSION
    ({
      write_copybits_picdata (src, dst, src_rect, dst_rect, mode, mask);
    });
  
  if (PORT_PEN_VIS (thePort) < 0)
    return;
  
  ROMlib_real_copy_bits (src, dst, src_rect, dst_rect, mode, mask);
  
  canonicalize_bogo_map_cleanup (src_bogo_map, &cleanup_info[0]);
  canonicalize_bogo_map_cleanup (dst_bogo_map, &cleanup_info[1]);
}

void
StdBitsPicSaveFlag (BitMap *src_bogo_map,
		    const Rect *src_rect, const Rect *dst_rect,
		    INTEGER mode, RgnHandle mask,
		    BOOLEAN savepic)
{
  Rect dummy_rect;

  /* we want the actual port bits, no fooling; so don't use the
     accessor macros */
  BitMap *dst_bogo_map = &thePort->portBits;
  
  PixMap dummy_space[2];
  PixMap *src = &dummy_space[0], *dst = &dummy_space[1];

  struct cleanup_info cleanup_info[2];

  if (CW (dst_rect->bottom) <= CW (dst_rect->top)
      || CW (dst_rect->right) <= CW (dst_rect->left)
      || (mask && !SectRect (dst_rect, &HxX (mask, rgnBBox), &dummy_rect)))
    return;

  /* the incoming source and destinations can be any one of the
     following;

     a `BitMap *', the high two bits of rowBytes must be zero,
     or CGrafPort coerced to a GrafPort with the PortBits
     passed in; in this case the high two bits of rowBytes
     must be `11'; and the baseAddr field is a `PixMapHandle'

     each of these types are canonicalized into a `PixMap *', which
     is what we work with here */
  /* these functions lock the PixMapHandle if src or dst
     are from a coerced CGrafPort; we must make sure
     to unlock those handles on the way out */
  canonicalize_bogo_map (src_bogo_map, &src, &cleanup_info[0]);
  canonicalize_bogo_map (dst_bogo_map, &dst, &cleanup_info[1]);
  
  if (savepic)
    {
      PIC_SAVE_EXCURSION
	({
	  write_copybits_picdata (src, dst, src_rect, dst_rect, mode, mask);
	});
    }
  
  if (PORT_PEN_VIS (thePort) < 0)
    return;
  
  ROMlib_real_copy_bits (src, dst, src_rect, dst_rect, mode, mask);
  
  canonicalize_bogo_map_cleanup (src_bogo_map, &cleanup_info[0]);
  canonicalize_bogo_map_cleanup (dst_bogo_map, &cleanup_info[1]);
}

P5 (PUBLIC pascal trap, void, StdBits,
    /* destination is alawys the current port */
    BitMap *, src_bogo_map,
    const Rect *, src_rect, const Rect *, dst_rect,
    INTEGER, mode, RgnHandle, mask)
{
  StdBitsPicSaveFlag (src_bogo_map, src_rect, dst_rect, mode, mask, TRUE);
}
  
static void
ROMlib_real_copy_bits_helper (PixMap *src, PixMap *dst,
			      const Rect *src_rect, const Rect *dst_rect,
			      INTEGER mode, RgnHandle mask)
{		       
  GDHandle the_gd;
  PixMapHandle the_gd_pmap;
  GrafPtr current_port;
  
  Rect tmp_mask_rect;
  uint32 bk_color, fg_color;
  
  /* region used to compute the complete mask region */
  RgnHandle mask_region;
  
  /* bits of a pixel position in `src' that have a resolution
     below that of a byte */
  int dst_sub_byte_bits;
  
  /* final depth of the destination */
  int dst_depth;

  const rgb_spec_t *dst_rgb_spec;
  
#if defined (SAVE_CURSOR)
  int save_cursor_visible_p = FALSE;
  int screen_src_p;
#endif /* SAVE_CURSOR */
  
  TEMP_ALLOC_DECL (temp_depth_bits);
  TEMP_ALLOC_DECL (temp_scale_bits);
  TEMP_ALLOC_DECL (temp_overlap_bits);
  
  the_gd = MR (TheGDevice);
  the_gd_pmap = GD_PMAP (the_gd);
  current_port = thePort;
  
#if defined (SAVE_CURSOR)
  screen_src_p = active_screen_addr_p (src);
#endif
  
  dst_rgb_spec = pixmap_rgb_spec (dst);
  dst_depth = CW (dst->pixelSize);
  
  switch (dst_depth)
    {
    case 1:  dst_sub_byte_bits = 7;  break;
    case 2:  dst_sub_byte_bits = 3;  break;
    case 4:  dst_sub_byte_bits = 1;  break;
    default:
      dst_sub_byte_bits = 0;  break;
    }
  
  ROMlib_fg_bk (&fg_color, &bk_color, NULL, NULL, dst_rgb_spec,
		active_screen_addr_p (dst), dst_depth <= 8);
  
  /* if the source and dest differ in depths, perform a depth
     conversion on the src, so it matches that of the depth */
  if (src->pixelSize != dst->pixelSize
      || (src->pmTable != dst->pmTable &&
	  (CW (src->pixelSize) < 16
	   && (CTAB_SEED_X (MR (src->pmTable))
	       /* we assume the destination has the same color table as
		  the current graphics device */
	       != CTAB_SEED_X (PIXMAP_TABLE (GD_PMAP (the_gd)))
	       && CTAB_SEED_X (MR (src->pmTable)) != CLC (0)))))
    {
      PixMap *new_src = (PixMap *) alloca (sizeof (PixMap));
      /* convert_pixmap expects the src rect to be aligned to byte
	 boundaries; compute that in `widened_src_rect' */
      Rect *widened_src_rect = (Rect *) alloca (sizeof (Rect));
      int src_depth, src_sub_byte_bits;
      void *new_src_bits;
      int n_bytes_needed;
      
#if defined (SAVE_CURSOR)
      if (screen_src_p)
	{
	  save_cursor_visible_p = host_set_cursor_visible (FALSE);
	  screen_src_p = FALSE;
	}
#endif /* SAVE_CURSOR */
      
      src_depth = CW (src->pixelSize);
      
      switch (src_depth)
	{
	case 1:  src_sub_byte_bits = 7;  break;
	case 2:  src_sub_byte_bits = 3;  break;
	case 4:  src_sub_byte_bits = 1;  break;
	default:
	  src_sub_byte_bits = 0;  break;
	}

      widened_src_rect->top = src_rect->top;
      widened_src_rect->bottom = src_rect->bottom;

      /* translate the left (right) src_rect coords to absolute bitmap
	 coordinates; and round down (up) to the byte boundary, and
	 re-translate to boundary-relative bitmap coords */
      widened_src_rect->left
	= CW (((CW (src_rect->left) - CW (src->bounds.left))
	       & ~src_sub_byte_bits)
	      + CW (src->bounds.left));
      widened_src_rect->right
	= CW ((((CW (src_rect->right) - CW (src->bounds.left))
		+ src_sub_byte_bits)
	       & ~src_sub_byte_bits) + CW (src->bounds.left));
      
      /* the new_src should `be a pixmap' (have the pixmap bits set in
         the rowBytes) only if the dst is a pixmap; convert_pixmap
         does different things if the destination is a bitmap */
      new_src->rowBytes = (  PIXMAP_DEFAULT_ROW_BYTES_X
			   | CW ((((RECT_WIDTH (widened_src_rect)
				    * dst_depth) + 31) / 32) * 4));
      
      /* Allocate temporary storage for the new_src_bits bitmap. */

      n_bytes_needed = (BITMAP_ROWBYTES (new_src)
			    * (CW (src_rect->bottom) - CW (src_rect->top)));

      TEMP_ALLOC_ALLOCATE (new_src_bits, temp_depth_bits, n_bytes_needed);
      new_src->baseAddr = RM (new_src_bits);
      
      pixmap_set_pixel_fields (new_src, dst_depth);
      new_src->pmTable = PIXMAP_TABLE_X (the_gd_pmap);
      
      /* don't initialize the color table of new_src; we assume that
	 it has the same color space as the current graphics device */
      
      convert_pixmap (src, new_src, widened_src_rect, NULL);
      
      src = new_src;
      /* `convert_pixmap ()' jukes the coordinates of the
	 new_src bitmap so that `src_rect' is still correct */
    }

  /* if the source and dest rectangles are not equivalent, scale the
     source rectangle */
  if (RECT_WIDTH (src_rect) != RECT_WIDTH (dst_rect)
      || RECT_HEIGHT (src_rect) != RECT_HEIGHT (dst_rect))
    {
      PixMap *new_src = (PixMap *) alloca (sizeof (PixMap));
      int new_src_row_bytes;
      void *scale_base;
      
#if defined (SAVE_CURSOR)
      if (screen_src_p)
	{
	  save_cursor_visible_p = host_set_cursor_visible (FALSE);
	  screen_src_p = FALSE;
	}
#endif /* SAVE_CURSOR */
      
      new_src_row_bytes
	= (((RECT_WIDTH (dst_rect) * dst_depth
	     + /* dst_sub_byte_bits */ 7) / 8) + 3) & ~3;
      new_src->rowBytes = CW (new_src_row_bytes) | PIXMAP_DEFAULT_ROW_BYTES_X;
      
      TEMP_ALLOC_ALLOCATE (scale_base, temp_scale_bits,
			   new_src_row_bytes * RECT_HEIGHT (dst_rect));
      new_src->baseAddr = (Ptr) RM (scale_base);
      
      pixmap_set_pixel_fields (new_src, dst_depth);
      
      scale_blt_bitmap ((blt_bitmap_t *) src,
			(blt_bitmap_t *) new_src,
			src_rect, dst_rect, ROMlib_log2[dst_depth]);
      
      src = new_src;
      src_rect = dst_rect;
    }
  
  /* compute the mask region before checking if the source and dest
     overlap becuase we only double buffer if dy is nonzero */
  /* intersect the region mask with thePort bounds, thePort rect, the
     destination rect, and the port {clip, vis} regions */
  mask_region = NewRgn ();
  
  SectRect (dst_rect, &dst->bounds, &tmp_mask_rect);
  RectRgn (mask_region, &tmp_mask_rect);
  SectRgn (PORT_CLIP_REGION (current_port), mask_region, mask_region);
  SectRgn (PORT_VIS_REGION (current_port), mask_region, mask_region);
  if (mask)
    SectRgn (mask, mask_region, mask_region);
  
  if (src->baseAddr == dst->baseAddr
      && (src_dst_overlap_and_dst_below_src_p
	  (src_rect, dst_rect,
	   CW (dst->bounds.left) - CW (src->bounds.left),
	   CW (dst->bounds.top)  - CW (src->bounds.top)))
      && (! RGN_SMALL_P (mask_region)
	  || dy_zero_p (src_rect, dst_rect,
			CW (dst->bounds.left) - CW (src->bounds.left),
			CW (dst->bounds.top)  - CW (src->bounds.top))))
    {
      PixMap *new_src;
      void *overlap_bits;
      
      Rect copy_rect;
      Rect clipped_src_rect;
      int height, offset;
      
      /* the source and destination are overlapped;
	 create a new src bitmap */
      new_src = (PixMap *) alloca (sizeof *new_src);
      
#if defined (SAVE_CURSOR)
      if (screen_src_p)
	{
	  save_cursor_visible_p = host_set_cursor_visible (FALSE);
	  screen_src_p = FALSE;
	}
#endif /* SAVE_CURSOR */
      
      SectRect (&src->bounds, src_rect, &clipped_src_rect);
      
      height = RECT_HEIGHT (&clipped_src_rect);
      offset = CW (clipped_src_rect.top) - CW (src_rect->top);
      
      copy_rect.top    = CW (CW (src_rect->top) + offset);
      copy_rect.bottom = CW (CW (src_rect->top) + offset + height);
      copy_rect.left   = src->bounds.left;
      copy_rect.right  = src->bounds.right;
      
      new_src->rowBytes      = src->rowBytes;
      TEMP_ALLOC_ALLOCATE (overlap_bits, temp_overlap_bits,
			   height * BITMAP_ROWBYTES (src));
      new_src->baseAddr      = (Ptr) RM (overlap_bits);
      new_src->bounds        = copy_rect;
      
      pixmap_set_pixel_fields (new_src, dst_depth);
      new_src->pmTable = src->pmTable;
      
      /* pause picture recording since we don't want the recursive
	 call to copy the overlapping image to be recorded */
      
      {
	uint32 black_pixel, white_pixel;
	RgnHandle rgn;
	
	rgn = NewRgn ();
	RectRgn (rgn, &copy_rect);
	
	pixmap_black_white (new_src, &black_pixel, &white_pixel);
	
	ROMlib_blt_rgn_update_dirty_rect (rgn, srcCopy,
					  FALSE, dst_depth,
					  src, new_src,
					  &copy_rect, &copy_rect,
					  black_pixel, white_pixel);
	DisposeRgn (rgn);
      }
      
      src = new_src;
    }
  
  ROMlib_blt_rgn_update_dirty_rect
    (mask_region, mode, FALSE, dst_depth,
     src, dst, src_rect, dst_rect,
     fg_color, bk_color);

#if defined (SAVE_CURSOR)
  if (save_cursor_visible_p)
    host_set_cursor_visible (TRUE);
#endif
  
  DisposeRgn (mask_region);

  TEMP_ALLOC_FREE (temp_overlap_bits);
  TEMP_ALLOC_FREE (temp_scale_bits);
  TEMP_ALLOC_FREE (temp_depth_bits);
}  

#if 1

/*
 * If we're copying a big 1-bpp bitmap to a small 8-bpp pixmap, we really
 * want to first copy to a temporary small 1-bpp bitmap and then copy that
 * to a small 8-bpp pixmap, otherwise we expand our large bitmap by a factor
 * of 8, which can be excessive.
 */

static void
ROMlib_real_copy_bits (PixMap *src, PixMap *dst,
			      const Rect *src_rect, const Rect *dst_rect,
			      INTEGER mode, RgnHandle mask)
{
  boolean_t shrink_first_p;

  if (src->pixelSize == dst->pixelSize)
    shrink_first_p = FALSE;
  else
    {
      int default_nbits, new_nbits;

      default_nbits = (RECT_WIDTH (src_rect) * CW (dst->pixelSize) *
		       RECT_HEIGHT (src_rect));
      
      new_nbits = ((RECT_WIDTH (dst_rect) * CW (src->pixelSize) *
		    RECT_HEIGHT (dst_rect)) +
		   (RECT_WIDTH (dst_rect) * CW (dst->pixelSize) *
		    RECT_HEIGHT (dst_rect)));
      shrink_first_p = (new_nbits < default_nbits);
    }

  if (!shrink_first_p)
    ROMlib_real_copy_bits_helper (src, dst, src_rect, dst_rect, mode, mask);
  else
    {
      PixMap *new_src;
      int temp_bytes_needed;
      void *temp_bits;
      INTEGER temp_row_bytes;
      int src_depth;
#if defined (SAVE_CURSOR)
      int save_cursor_visible_p = FALSE;
      int screen_src_p;
#endif /* SAVE_CURSOR */
      TEMP_ALLOC_DECL (temp_alloc_bits);

#if defined (SAVE_CURSOR)
      screen_src_p = active_screen_addr_p (src);
      if (screen_src_p)
	{
	  save_cursor_visible_p = host_set_cursor_visible (FALSE);
	  screen_src_p = FALSE;
	}
#endif /* SAVE_CURSOR */

      new_src = (PixMap *) alloca (sizeof *new_src);
      src_depth = CW (src->pixelSize);
      temp_row_bytes = (RECT_WIDTH (dst_rect) * src_depth + 31) / 32 * 4;
      temp_bytes_needed = temp_row_bytes * RECT_HEIGHT (dst_rect);
      TEMP_ALLOC_ALLOCATE (temp_bits, temp_alloc_bits, temp_bytes_needed);

      *new_src = *src;
      new_src->baseAddr = RM (temp_bits);
      new_src->rowBytes = CW (temp_row_bytes | PIXMAP_DEFAULT_ROWBYTES);
      new_src->bounds = *dst_rect;

      scale_blt_bitmap ((blt_bitmap_t *) src, (blt_bitmap_t *) new_src,
			src_rect, dst_rect, ROMlib_log2[src_depth]);
      ROMlib_real_copy_bits_helper (new_src, dst, dst_rect, dst_rect, mode,
				    mask);
      
      TEMP_ALLOC_FREE (temp_alloc_bits);
    }
}
#endif
