/* Copyright 1986 - 1996 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_qStdRgn[] =
	    "$Id: qStdRgn.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/quick.h"
#include "rsys/cquick.h"
#include "rsys/picture.h"
#include "rsys/prefs.h"
#include "rsys/notmac.h"
#include "rsys/mman.h"
#include "rsys/flags.h"
#include "rsys/host.h"
#include "rsys/vdriver.h"
#include "rsys/fatal.h"
#include "rsys/assert.h"
#include "rsys/myabort.h"
#include "rsys/tempalloc.h"

#include "rsys/xdblt.h"
#include "rsys/srcblt.h"
#include "rsys/dirtyrect.h"

PUBLIC WriteWhenType ROMlib_when = WriteInBltrgn;


void
ROMlib_WriteWhen (WriteWhenType when)
{
  ROMlib_when = when;
}


void
ROMlib_blt_rgn_update_dirty_rect
  (RgnHandle rh, 
   int16 mode, boolean_t tile_src_p, int bpp, 
   const PixMap *src_pm, PixMap *dst_pm,
   const Rect *src_rect, const Rect *dst_rect,
   uint32 fg_color, uint32 bk_color)
{
  boolean_t screen_dst_p, update_dirty_p;
  Rect bbox;
  TEMP_ALLOC_DECL (temp_alloc_space);
  
  if (mode == mask)
    mode = patCopy;
  else if (mode >= hilite
	   || ((mode == patXor
		|| mode == srcXor)
	       && !(HiliteMode & 128)))
    mode = hilite;

  if (mode == grayishTextOr)
    {
      if (bpp == 1)
	{
	  PixMap *new_src_pm = alloca (sizeof *new_src_pm);
	  int baseaddr_size, height, row_bytes;
	  uint8 *new_bits, *s, *d;
	  int y;

	  /* Make a copy of the source bitmap, so we can dither it. */
	  *new_src_pm = *src_pm;
	  height = RECT_HEIGHT (&new_src_pm->bounds);
	  row_bytes = BITMAP_ROWBYTES (new_src_pm);
	  baseaddr_size = row_bytes * height;
	  TEMP_ALLOC_ALLOCATE (new_bits, temp_alloc_space, baseaddr_size);
	  new_src_pm->baseAddr = (Ptr) RM (new_bits);

	  s = (uint8 *) MR (src_pm->baseAddr);
	  d = new_bits;
	  for (y = 0; y < height; y++)
	    {
	      int x, mask;
	      mask = (y & 1) ? 0xAA : 0x55;
	      for (x = row_bytes - 1; x >= 0; x--)
		d[x] = s[x] & mask;
	      s += row_bytes;
	      d += row_bytes;
	    }

	  src_pm = new_src_pm;
	}
      else
	{
	  RGBColor fg_rgb, bk_rgb;
	  const rgb_spec_t *dst_rgb_spec;

	  dst_rgb_spec = pixmap_rgb_spec (dst_pm);
	  ROMlib_fg_bk (NULL, NULL, &fg_rgb, &bk_rgb, dst_rgb_spec,
			active_screen_addr_p (dst_pm), bpp <= 8);

	  AVERAGE_COLOR (&fg_rgb, &bk_rgb, 0x8000, &fg_rgb);

	  if (dst_rgb_spec)
	    fg_color = ((*dst_rgb_spec->rgbcolor_to_pixel)
			(dst_rgb_spec, &fg_rgb, TRUE));
	  else
	    fg_color = Color2Index (&fg_rgb);
	}

      mode = srcOr;
    }
  else if ((IMV_XFER_MODE_P (mode)
	    || mode == hilite)
	   && bpp > 1)
    {
      PixMap *new_src_pm;
      int row_bytes;
      void *new_bits;
      
      Rect convert_src_rect;
      int top, left;
      int bbox_width, bbox_height;
      
      new_src_pm = (PixMap *) alloca (sizeof *new_src_pm);
      
      bbox        = RGN_BBOX (rh);
      bbox_width  = RECT_WIDTH (&bbox);
      bbox_height = RECT_HEIGHT (&bbox);
      
      row_bytes = ((bbox_width * bpp + 31) / 32) * 4;
      new_src_pm->rowBytes = CW (row_bytes);
      TEMP_ALLOC_ALLOCATE (new_bits, temp_alloc_space,
			   row_bytes * bbox_height);
      new_src_pm->baseAddr = (Ptr) RM (new_bits);
      
      pixmap_set_pixel_fields (new_src_pm, bpp);

      new_src_pm->pmTable = RM (ROMlib_dont_depthconv_ctab);
      
      top  = (CW (src_rect->top)
	      + (CW (bbox.top) - CW (dst_rect->top)));
      left = (CW (src_rect->left)
	      + (CW (bbox.left) - CW (dst_rect->left)));
      
      convert_src_rect.top = CW (top);
      convert_src_rect.left = CW (left);
      convert_src_rect.bottom = CW (top + bbox_height);
      convert_src_rect.right = CW (left + bbox_width);
      
      if (mode == transparent
	  || mode == hilite)
	{
	  convert_transparent (src_pm, dst_pm, new_src_pm,
			       &convert_src_rect, &bbox,
			       mode,
			       tile_src_p,
/* #warning "have mat figure out what the tiling offsets should be" */
			       0, 0);
	}
      else
	{
	  CTabHandle ctab;
	  ITabHandle itab;
	  
	  ctab = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
	  itab = GD_ITABLE (MR (TheGDevice));
	  
	  convert_pixmap_with_IMV_mode (src_pm, dst_pm, new_src_pm,
					ctab, ctab, itab,
					&convert_src_rect, &bbox,
					mode,
					&CPORT_OP_COLOR (theCPort),
					tile_src_p,
/* #warning "have mat figure out what the tiling offsets should be" */
					0, 0);
	}
      
      src_pm = new_src_pm;
      dst_rect = src_rect = &bbox;
      mode = srcCopy;
      pixmap_black_white (src_pm, &fg_color, &bk_color);
      tile_src_p = FALSE;
    }

/* #warning "don't ignore dither mode at some point in the future" */

  mode &= 0x3F;
  if (mode >= 0x20 && mode <= 0x2F)
    {
      static const INTEGER funny_mode_lookup[16] =
	{
	  srcCopy, srcBic, srcXor, srcOr, srcOr, srcBic, srcXor, srcOr,
	  patCopy, patBic, patXor, patOr, patOr, patBic, patXor, patOr,
	};
      mode = funny_mode_lookup[mode - 0x20];
    }
  else if (mode == hilite)
    mode = patXor;
  
  if (tile_src_p)
    mode |= 0x8;
  else
    mode &= 0x37;
  
  screen_dst_p = active_screen_addr_p (dst_pm);

  update_dirty_p = srcblt_rgn (rh, mode, ROMlib_log2[bpp],
			       (blt_bitmap_t *) src_pm,
			       (blt_bitmap_t *) dst_pm,
			       (Point *) src_rect, (Point *) dst_rect,
			       fg_color, bk_color);
  
  /* if drawing to the screen, update the dirty rect (or copy the
     image straight to screen) */
  if (screen_dst_p && update_dirty_p)
    {
      const Rect *r = &RGN_BBOX (rh);
      int dst_top = CW (dst_pm->bounds.top);
      int dst_left = CW (dst_pm->bounds.left);
      
      dirty_rect_accrue (CW (r->top) - dst_top, CW (r->left) - dst_left,
			 CW (r->bottom) - dst_top, CW (r->right) - dst_left);
      if (ROMlib_when == WriteInBltrgn)
	{
	  dirty_rect_update_screen ();
	  vdriver_flush_display ();
	}
    }
  
  TEMP_ALLOC_FREE (temp_alloc_space);
}


const int ROMlib_log2[] =
{
  -1,
  0,
  1,
  -1, 2,
  -1, -1, -1, 3,
  -1, -1, -1, -1, -1, -1, -1, 4,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5,
};


const uint32 ROMlib_pixel_tile_scale[6] =
{
  0xFFFFFFFF,
  0x55555555,
  0x11111111,
  0x01010101,
  0x00010001,
  0x00000001,
};

const uint32 ROMlib_pixel_size_mask[6] =
{
  0x00000001,
  0x00000003,
  0x0000000F,
  0x000000FF,
  0x0000FFFF,
  0xFFFFFFFF,
};

#define BLT_PAT_SIMPLE(rh, mode, pixpat_accessor, pattern_accessor)	      \
do {									      \
  GrafPtr the_port = thePort;						      \
  if (CGrafPort_p (the_port))						      \
    blt_pixpat_to_pixmap_simple_mode (rh, mode,				      \
				      pixpat_accessor ((CGrafPtr) the_port),  \
				      CPORT_PIXMAP ((CGrafPtr) the_port));    \
  else									      \
    blt_pattern_to_bitmap_simple_mode (rh, mode, pattern_accessor (the_port), \
				       &the_port->portBits);		      \
} while (0)


#define BLT_PAT_FANCY(rh, mode, pixpat_accessor, pattern_accessor)	\
do {									\
  GrafPtr the_port = thePort;						\
  if (CGrafPort_p (the_port))						\
    {									\
      PixMapHandle cport_pmap = CPORT_PIXMAP ((CGrafPtr) the_port);	\
      LOCK_HANDLE_EXCURSION_1						\
	(cport_pmap,							\
	 {								\
	   blt_fancy_pat_mode_to_pixmap (rh, mode,			\
					 pixpat_accessor ((CGrafPtr)	\
							  the_port),	\
					 NULL, STARH (cport_pmap));	\
	 });								\
    }									\
  else if (active_screen_addr_p (&the_port->portBits))			\
    {									\
      PixMap copy_of_screen;						\
      copy_of_screen = *(STARH (GD_PMAP (MR (TheGDevice))));		\
      copy_of_screen.bounds = the_port->portBits.bounds;		\
      blt_fancy_pat_mode_to_pixmap (rh, mode, NULL,			\
				    pattern_accessor (the_port),	\
				    &copy_of_screen);			\
    }									\
  else									\
    {									\
      /* We shouldn't get here; we don't allow fancy blits to old-style	\
       * grafports not on the screen.					\
       */								\
      gui_abort ();							\
    }									\
} while (0)


static void
blt_pattern_to_bitmap_simple_mode (RgnHandle rh, INTEGER mode,
				   Pattern src, BitMap *dst)
{
  PixMapHandle main_gd_pmap;
  GDHandle main_gd;
  uint32 bk_pixel, fg_pixel;
  int bpp, dst_top, dst_left;
  boolean_t screen_dst_p;
  PixMap dst_pixmap;
  GrafPtr the_port;
  boolean_t update_dirty_p;
  
  main_gd = MR (MainDevice);
  main_gd_pmap = GD_PMAP (main_gd);
  screen_dst_p = active_screen_addr_p (dst);
  bpp = PIXMAP_PIXEL_SIZE (main_gd_pmap);
  
  the_port = thePort;
  
  /* ### is this if necessary? */
  if (screen_dst_p)
    {
      dst_pixmap = *STARH (main_gd_pmap);
      ROMlib_fg_bk (&fg_pixel, &bk_pixel, NULL, NULL,
		    pixmap_rgb_spec (STARH (main_gd_pmap)),
		    TRUE, FALSE);
    }
  else
    {
      dst_pixmap.pixelSize = CWC (1);
      dst_pixmap.baseAddr  = dst->baseAddr;
      dst_pixmap.rowBytes  = dst->rowBytes | PIXMAP_DEFAULT_ROWBYTES_X;
      dst_pixmap.pmTable   = RM (ROMlib_bw_ctab);
      
      ROMlib_fg_bk (&fg_pixel, &bk_pixel, NULL, NULL, NULL, FALSE, FALSE);
    }
  
  dst_pixmap.bounds = dst->bounds;
  dst_top  = CW (dst_pixmap.bounds.top);
  dst_left = CW (dst_pixmap.bounds.left);

  /* Actually do the blt. */
  update_dirty_p = xdblt_pattern (rh, mode, -dst_left, -dst_top, src,
				  &dst_pixmap, fg_pixel, bk_pixel);

  /* Update the real screen as appropriate. */
  if (screen_dst_p && update_dirty_p)
    {
      const Rect *r = &RGN_BBOX (rh);
      dirty_rect_accrue (CW (r->top) - dst_top, CW (r->left) - dst_left,
			 CW (r->bottom) - dst_top, CW (r->right) - dst_left);
      if (ROMlib_when == WriteInBltrgn)
	{
	  dirty_rect_update_screen ();
	  vdriver_flush_display ();
	}
    }
}


static void
blt_pixpat_to_pixmap_simple_mode (RgnHandle rh, INTEGER mode,
				  PixPatHandle srch, PixMapHandle dsth)
{
  boolean_t screen_dst_p;
  int dst_top, dst_left;
  boolean_t update_dirty_p;

  LOCK_HANDLE_EXCURSION_2
    (srch, dsth,
     {
       PixPat *src = STARH (srch);
       PixMap *dst = STARH (dsth);
       GrafPtr the_port = thePort;

       screen_dst_p = active_screen_addr_p (dst);
       
       dst_top  = CW (dst->bounds.top);
       dst_left = CW (dst->bounds.left);

       if (src->patType == CWC (pixpat_old_style_pattern))
	 {
	   const rgb_spec_t *dst_rgb_spec;
	   uint32 fg_color;
	   uint32 bk_color;
	   
	   dst_rgb_spec = pixmap_rgb_spec (dst);
	   canonical_from_bogo_color (PORT_FG_COLOR (the_port), dst_rgb_spec,
				      &fg_color, NULL);
	   canonical_from_bogo_color (PORT_BK_COLOR (the_port), dst_rgb_spec,
				      &bk_color, NULL);
	   
	   update_dirty_p = xdblt_pattern (rh, mode, -dst_left, -dst_top,
					   src->pat1Data, dst, fg_color,
					   bk_color);
	 }
       else
	 {
	   boolean_t xdata_valid_p;
	   boolean_t handle_size_wrong_p;
	   xdata_handle_t xh;

	   xh = (xdata_handle_t) MR (src->patXData);
	   if (!xh)
	     {
	       warning_unexpected ("xdata handle NULL_STRING");
	       xh = (xdata_handle_t) NewHandle (sizeof (xdata_t));
	       HxX (xh, raw_pat_bits_mem) = RM (NULL);
	       src->patXData = (Handle) RM (xh);
	       xdata_valid_p = FALSE;
	       handle_size_wrong_p = FALSE;
	     }
	   else
	     {
	       handle_size_wrong_p = (GetHandleSize ((Handle) xh)
				      != sizeof (xdata_t));
	       xdata_valid_p = (!handle_size_wrong_p
				&& (HxX (xh, magic_cookie)
				    == XDATA_MAGIC_COOKIE));
	     }
	   
	   if (src->patXValid == CWC (-1)
	       || !xdata_valid_p)
	     {
	       if (xdata_valid_p)
		 {
		   Ptr raw = HxX (xh, raw_pat_bits_mem);
		   if (raw)
		     {
		       DisposPtr (raw);
		       HxX (xh, raw_pat_bits_mem) = NULL;
		     }
		 }
	       else if (handle_size_wrong_p)
		 {
		   SetHandleSize ((Handle) xh, sizeof (xdata_t));
		   warning_unexpected ("invalid xdata size; appl juked xdata maybe");
		 }
	       
	       xdata_for_pixpat_with_space (src, dst, xh);
	       
	       src->patXValid = CWC (0);
	     }
	   else
	     {
	       update_xdata_if_needed (xh, src, dst);
	     }

	   LOCK_HANDLE_EXCURSION_1
	     (xh,
	      {
		xdata_t *x = STARH (xh);
		update_dirty_p = (*x->blt_func) (rh, mode, -dst_left,
						 -dst_top, x, dst);
	      });
	 }
     });

  /* Update the real screen as appropriate. */
  if (screen_dst_p && update_dirty_p)
    {
      const Rect *r = &RGN_BBOX (rh);
      dirty_rect_accrue (CW (r->top) - dst_top, CW (r->left) - dst_left,
			 CW (r->bottom) - dst_top, CW (r->right) - dst_left);
      if (ROMlib_when == WriteInBltrgn)
	{
	  dirty_rect_update_screen ();
	  vdriver_flush_display ();
	}
    }
}


static void
blt_fancy_pat_mode_to_pixmap (RgnHandle rh, int mode,
			      PixPatHandle pixpat_handle,
			      Pattern pattern,  /* valid iff !pixpat_handle */
			      PixMap *pixmap)
{
  PixMap converted_pm;
  PixMap pattern_pm;
  Rect bbox;
  int row_bytes;
  void *new_bits;
  xdata_handle_t xh;
  xdata_t *x;
  boolean_t apply_fg_bk_p;
  int bpp, log2_bpp;
  TEMP_ALLOC_DECL (temp_alloc_space);

  /* Set up xdata for the thing being blitted. */
  bpp = CW (pixmap->pixelSize);
  log2_bpp = ROMlib_log2[bpp];

  if (!pixpat_handle)
    {
      xh = xdata_for_pattern (pattern, pixmap);
      apply_fg_bk_p = TRUE;
    }
  else
    {
#if 0
      LOCK_HANDLE_EXCURSION_1
	(pixpat_handle,
#endif
	 {
	   PixPat *pixpat = STARH (pixpat_handle);
	   if (pixpat->patType == CWC (pixpat_type_orig))
	     {
	       xh = xdata_for_pattern (pixpat->pat1Data, pixmap);
	       apply_fg_bk_p = TRUE;
	     }
	   else			/* newer-style pixpat */
	     {
	       xh = xdata_for_pixpat (pixpat, pixmap);
	       apply_fg_bk_p = FALSE;
	     }
#if 0
	 });
#else
    }
#endif
    }

  /* Set up the pattern bitmap. */
  HLock ((Handle) xh);
  x = STARH (xh);
  pattern_pm.bounds.top    = CWC (0);
  pattern_pm.bounds.left   = CWC (0);
  pattern_pm.bounds.bottom = CW (x->height_minus_1 + 1);
  pattern_pm.bounds.right  = CW ((x->row_bytes << (5 - x->log2_bpp)) >> 2);
  pattern_pm.rowBytes = CW (x->row_bytes);
  if (x->pat_bits)
    pattern_pm.baseAddr = (Ptr) RM (x->pat_bits);
  else
    pattern_pm.baseAddr = (Ptr) RM (&x->pat_value);
  
  pixmap_set_pixel_fields (&pattern_pm, 1 << x->log2_bpp);
  pattern_pm.pmTable = RM (ROMlib_dont_depthconv_ctab);
  
  /* When dealing with an old-style pattern, we need to apply fg/bk colors. */
  if (apply_fg_bk_p)
    {
      uint32 *p, *end, fg_pixel, bk_pixel, tiled_fg_pixel, tiled_bk_pixel;
      
      ROMlib_fg_bk (&fg_pixel, &bk_pixel, NULL, NULL,
		    pixmap_rgb_spec (pixmap),
		    active_screen_addr_p (pixmap), FALSE);
      
      /* Tile the pixel values out to 32 bpp. */
      tiled_fg_pixel = ((fg_pixel & ROMlib_pixel_size_mask[log2_bpp])
			* ROMlib_pixel_tile_scale[log2_bpp]);
      tiled_bk_pixel =  ((bk_pixel & ROMlib_pixel_size_mask[log2_bpp])
			 * ROMlib_pixel_tile_scale[log2_bpp]);

      /* Note that we don't care if we clobber the bits of this temp xdata. */
      p = (uint32 *) MR (pattern_pm.baseAddr);
      end = (uint32 *) ((char *) p + x->byte_size);
      for (; p != end; p++)
	{
	  uint32 v = *p;
/* #warning "not sure how this interacts w/RGB" */
	  *p = (v & tiled_fg_pixel) | ((~v) & tiled_bk_pixel);
	}
    }

  /* Set up the converted bitmap. */
  bbox = RGN_BBOX (rh);
  converted_pm.bounds = bbox;
  row_bytes = (((RECT_WIDTH (&bbox) << log2_bpp) + 31U) / 32) * 4;
  converted_pm.rowBytes = CW (row_bytes);
  TEMP_ALLOC_ALLOCATE (new_bits, temp_alloc_space,
		       row_bytes * RECT_HEIGHT (&bbox));
  converted_pm.baseAddr = (Ptr) RM (new_bits);

  pixmap_set_pixel_fields (&converted_pm, 1 << log2_bpp);
  converted_pm.pmTable = RM (ROMlib_dont_depthconv_ctab);

  if (mode == transparent || mode == hilite)
    {
      convert_transparent (&pattern_pm, pixmap, &converted_pm,
			   &bbox, &bbox,
			   mode,
			   TRUE,
/* #warning "have mat figure out what the tiling offsets should be" */
			   0, 0);
    }
  else
    {
      RGBColor op_color;
      CTabHandle ctab;
      ITabHandle itab;
      
      ctab = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
      itab = GD_ITABLE (MR (TheGDevice));
      
      if (CGrafPort_p (thePort))
	op_color = CPORT_OP_COLOR (theCPort);
      else /* I have no idea what I'm supposed to do in this case */
	op_color = ROMlib_black_rgb_color;
      
      convert_pixmap_with_IMV_mode (&pattern_pm, pixmap, &converted_pm,
				    ctab, ctab, itab,
				    &bbox, &bbox,
				    mode, &op_color, TRUE,
/* #warning "have mat figure out what the tiling offsets should be" */
				    0, 0);
    }

  {
    uint32 fg, bk;

    pixmap_black_white (pixmap, &fg, &bk);
    srcblt_rgn (rh, srcCopy, log2_bpp,
		(blt_bitmap_t *) &converted_pm, (blt_bitmap_t *) pixmap,
		(Point *) &bbox, (Point *) &bbox,
		fg, bk);
  }

  if (active_screen_addr_p (pixmap))
    {
      const Rect *r = &RGN_BBOX (rh);
      int dst_top = CW (pixmap->bounds.top);
      int dst_left = CW (pixmap->bounds.left);

      dirty_rect_accrue (CW (r->top) - dst_top, CW (r->left) - dst_left,
			 CW (r->bottom) - dst_top, CW (r->right) - dst_left);
      if (ROMlib_when == WriteInBltrgn)
	{
	  dirty_rect_update_screen ();
	  vdriver_flush_display ();
	}
    }

  HUnlock ((Handle) xh);
  xdata_free (xh);

  TEMP_ALLOC_FREE (temp_alloc_space);
}


/* Returns the actual bpp to be used for the destination. */
static inline int
theport_bpp (void)
{
  GrafPtr the_port = thePort;
  int bpp;

  if (CGrafPort_p (the_port))
    bpp = PIXMAP_PIXEL_SIZE (CPORT_PIXMAP ((CGrafPtr) the_port));
  else if (active_screen_addr_p (&the_port->portBits))
    bpp = PIXMAP_PIXEL_SIZE (GD_PMAP (MR (MainDevice)));
  else
    bpp = 1;

  return bpp;
}


inline void
ROMlib_blt_pn (RgnHandle rh, INTEGER mode)
{
  int bpp = theport_bpp ();

  mode &= 0x3F;
  if (mode < 8)
    mode |= 8;
  if (mode >= hilite || (mode == patXor && !(HiliteMode & 128)))
    mode = hilite;

  if (bpp == 1 || (!IMV_XFER_MODE_P (mode) && mode != hilite))
    {
      /* I have no idea where this table came from.  It's just been
       * passed down from the old black and white blitter.
       */
      static const uint8 imvxfer_pat_mode_lookup[8] =
	{ patCopy, patBic, patXor, patOr, patOr, patBic, patXor, patOr };

      if (mode >= 0x20 && mode <= 0x2F)
	mode = imvxfer_pat_mode_lookup[mode & 7];
      else if (mode == hilite)
	mode = patXor;
      BLT_PAT_SIMPLE (rh, mode, CPORT_PEN_PIXPAT, PORT_PEN_PAT);
    }
  else
    {
      BLT_PAT_FANCY (rh, mode, CPORT_PEN_PIXPAT, PORT_PEN_PAT);
    }
}


P2 (PUBLIC pascal trap, void, StdRgn,
    GrafVerb, verb, RgnHandle, rgn)
{
  RgnHandle rh;
  Rect r;
  
  if (!rgn || EmptyRgn (rgn))
    return;
  
  if (HxX (rgn, rgnSize) & CWC (0x8000))
    {
      warning_unexpected ("negative rgnSize = 0x%x\n", HxX (rgn, rgnSize));
      return;
    }

  PIC_SAVE_EXCURSION
    ({
      ROMlib_drawingverbpicupdate (verb);
      PICOP (OP_frameRgn + (int) verb);
      LOCK_HANDLE_EXCURSION_1
	(rgn,
	 {
	   RgnPtr rp = STARH (rgn);
	   PICWRITE (rp, CW (rp->rgnSize));
	 });
    });
  
  /* intersect the region to be drawn with the
     port bounds and port rect */
  rh = NewRgn ();
  r = PORT_BOUNDS (thePort);

  RectRgn (rh, &r);
  SectRgn (rgn, rh, rh);
  
  if (verb == frame)
    {
      RgnHandle rsave;
      Point pen_size;
      
      /* remove the current region from rgnSave */
/* #warning "How does XOR remove it?  e.g. two framerects in a row." */
      pen_size = PORT_PEN_SIZE (thePort);
      rsave = (RgnHandle) PORT_REGION_SAVE (thePort);
      if (rsave)
	XorRgn (rgn, rsave, rsave);

      /* construct the frame */
/* #warning "We inset the region AFTER we've clipped it to the port bounds???" */
      InsetRgn (rh, CW (pen_size.h), CW (pen_size.v));
      XorRgn (rgn, rh, rh);
      
      /* now `paint' the frame */
      verb = paint;
    }

  /* can this check be moved up? */
  /* if verb == frame, we juke region_save above, so this check
   * probably can't be moved up.  on the other hand, are we supposed
   * to juke region_save if the pen isn't visible? -Mat */
  if (PORT_PEN_VIS (thePort) < 0)
    {
      DisposeRgn (rh);
      return;
    }

  SectRgn (rh, PORT_VIS_REGION (thePort), rh);
  SectRgn (rh, PORT_CLIP_REGION (thePort), rh);
  
  if (GWorld_p (thePort))
    LockPixels (CPORT_PIXMAP (thePort));
  
  switch (verb)
    {
    case paint:
      ROMlib_blt_pn (rh, PORT_PEN_MODE (thePort));
      break;
    case erase:
      BLT_PAT_SIMPLE (rh, patCopy, CPORT_BK_PIXPAT, PORT_BK_PAT);
      break;
    case invert:
      {
	int bpp = theport_bpp ();
	/* FIXME: is it ok to thrash the fill pattern here? */
	ROMlib_fill_pat (black);
	if (bpp == 1 || (HiliteMode & 128))
	  BLT_PAT_SIMPLE (rh, patXor, CPORT_FILL_PIXPAT, PORT_FILL_PAT);
	else
	  BLT_PAT_FANCY (rh, hilite, CPORT_FILL_PIXPAT, PORT_FILL_PAT);
      }
      break;
    case fill:
      BLT_PAT_SIMPLE (rh, patCopy, CPORT_FILL_PIXPAT, PORT_FILL_PAT);
      break;
    default:
      gui_fatal ("unknown GrafVerb");
      break;
    }

  if (GWorld_p (thePort))
    UnlockPixels (CPORT_PIXMAP (thePort));
  
  SET_HILITE_BIT ();
  DisposeRgn (rh);
}
