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

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

#include "rsys/common.h"

#include "MemoryMgr.h"

#include "rsys/xdata.h"
#include "rsys/xdblt.h"
#include "rsys/tempalloc.h"
#include "rsys/mman.h"

#include "rsys/vdriver.h"

boolean_t
update_xdata_if_needed (xdata_handle_t xh, PixPat *pixpat,
			PixMap *dest)
{
  xdata_t *x;

  x = STARH (xh);

  if (x->ctab_seed_x != CTAB_SEED_X (MR (dest->pmTable))
      || (1 << x->log2_bpp) != CW (dest->pixelSize)
      || (x->log2_bpp >= 4 && x->rgb_spec != pixmap_rgb_spec (dest)))
    {
      if (x->raw_pat_bits_mem)
	DisposPtr (x->raw_pat_bits_mem);
      xdata_for_pixpat_with_space (pixpat, dest, xh);
      return TRUE;
    }

  return FALSE;
}


static void
raw_bits_for_pattern (const Pattern pattern, PixMap *target,
		      uint32 *bits, int *row_bytes)
{
  /* this is a template pattern to be used as the source
     when performing the conversion from the old style
     pattern to the destination */
  static PixMap pattern_pixmap_tmpl =
    {
      /* baseAddr; to be filled in later */
      NULL,
      PIXMAP_DEFAULT_ROWBYTES_X | CWC (1),
      { CWC (0), CWC (0), CWC (8), CWC (8) },
      /* version */
      CWC (0),
      CWC (0),
      CWC (0),
		
      CWC (72), CWC (72),
		
      CWC (0),
      /* 1bpp */
      CWC (1),
      /* 1 component */
      CWC (1), CWC (1),
      CWC (0),
      /* color table; to be filled in later */
      NULL,
		
      CLC (0),
    };
  /* this is a template pattern to be used as the dest
     when performing the conversion from the old style
     pattern to the destination */
  static PixMap dst_pixmap_tmpl =
    {
      /* baseAddr; to be filled in later */
      NULL,
      0,
      { CWC (0), CWC (0), CWC (8), CWC (8) },
      /* version */
      CWC (0),
      CWC (0),
      CWC (0),
		
      CWC (72), CWC (72),
		
      CWC (0),
      CWC (0),
      CWC (0), CWC (0),
      CWC (0),
      /* color table; to be filled in later */
      NULL,
		
      CLC (0),
    };
  CTabPtr conv_table;
  CTabHandle fg_bk_ctab;
  int dst_row_bytes, target_depth;
  
  pattern_pixmap_tmpl.baseAddr = RM ((Ptr) &pattern[0]);
  fg_bk_ctab = validate_fg_bk_ctab ();
  pattern_pixmap_tmpl.pmTable = RM (ROMlib_bw_ctab);
  conv_table = alloca (CTAB_STORAGE_FOR_SIZE (1));
  conv_table->ctSeed  = CTAB_SEED_X (fg_bk_ctab);
  conv_table->ctFlags = CWC (0);
  conv_table->ctSize  = CWC (1);

  conv_table->ctTable[0].value = CWC (0);
  conv_table->ctTable[1].value = CWC (~0);
  
  target_depth = CW (target->pixelSize);
  dst_row_bytes = target_depth;  /* old-style Patterns always 8 pixels wide. */
  *row_bytes = dst_row_bytes;
  dst_pixmap_tmpl.rowBytes = (CW (dst_row_bytes)
			      | (target->rowBytes & ROWBYTES_FLAG_BITS_X)
			      | PIXMAP_DEFAULT_ROWBYTES_X);
  pixmap_set_pixel_fields (&dst_pixmap_tmpl, target_depth);
  if (target_depth > 8
      && active_screen_addr_p (target))
    dst_pixmap_tmpl.pixelType = CWC (vdriver_rgb_pixel_type);
  
  dst_pixmap_tmpl.pmTable = PIXMAP_TABLE_X (GD_PMAP (MR (TheGDevice)));
  dst_pixmap_tmpl.baseAddr = (Ptr) RM (bits);
  
  convert_pixmap (&pattern_pixmap_tmpl, &dst_pixmap_tmpl,
		  &ROMlib_pattern_bounds, conv_table);
}


static void
raw_bits_for_color_pattern (PixPatPtr pixpat, PixMap *target,
			    uint32 *bits, int *row_bytesp)
{
  PixMapHandle patmap;

  patmap = MR (pixpat->patMap);
  LOCK_HANDLE_EXCURSION_1
    (patmap,
     {
       int row_bytes;
       int target_depth;
       PixMap *src;
       PixMap dst;
       Handle data;
       const Rect *bounds;

       src = STARH (patmap);
       bounds = &src->bounds;
       target_depth = CW (target->pixelSize);
       row_bytes = ((RECT_WIDTH (bounds) * target_depth) + 7) / 8;
       *row_bytesp = row_bytes;

       dst = *target;
       dst.bounds   = *bounds;
       dst.rowBytes = (CW (row_bytes)
		       | (target->rowBytes & ROWBYTES_FLAG_BITS_X)
		       | PIXMAP_DEFAULT_ROWBYTES_X);
       dst.baseAddr = (Ptr) RM (bits);
  
       data = MR (pixpat->patData);

       LOCK_HANDLE_EXCURSION_1
	 (data,
	  {
	    src->baseAddr = data->p;
	    convert_pixmap (src, &dst, bounds, NULL);
	  });
     });
}


static void
raw_bits_for_rgb_pattern (PixPatPtr pixpat, PixMap *target,
			  uint32 *bits, int *row_bytes)
{
  uint32 actual_value;
  RGBColor desired_color;
  int target_depth;

  /* The color desired for this pixpat is stored in the 5th color
   * table entry for the pixpat.  We copy the color in case any of the
   * relevant handles move when we call Color2Index.
   */
  desired_color = CTAB_TABLE (PIXMAP_TABLE (MR (pixpat->patMap)))[4].rgb;
  
  target_depth = CW (target->pixelSize);

  if (target_depth <= 8)
    {
      actual_value = Color2Index (&desired_color);
      
      switch (target_depth)
	{
	case 1:
	  actual_value = ((int32) (actual_value << 31)) >> 31;
	  break;
	case 2:
	  actual_value = (actual_value & 3) * 0x55555555U;
	  break;
	case 4:
	  actual_value = (actual_value & 0xF) * 0x11111111U;
	  break;
	case 8:
	  actual_value = (actual_value & 0xFF) * 0x01010101U;
	  break;
	default:
	  gui_abort ();
	}
    }
  else  /* RGB pixel. */
    {
      const rgb_spec_t *rgb_spec;

      rgb_spec = pixmap_rgb_spec (target);
      gui_assert (rgb_spec != NULL);

      actual_value = (*rgb_spec->rgbcolor_to_pixel) (rgb_spec, &desired_color,
						     TRUE);
      if (target_depth == 16)
	actual_value |= (actual_value << 16);
    }

  bits[0] = actual_value;
  *row_bytes = 4;
}


static void
raw_bits_for_pixpat (PixPat *pixpat, PixMap *target,
		     uint32 *bits, int *row_bytes, int *height_override)
{
  switch (pixpat->patType)
    {
    case CWC (pixpat_old_style_pattern):
      raw_bits_for_pattern (pixpat->pat1Data, target, bits, row_bytes);
      break;
    case CWC (pixpat_color_pattern):
      raw_bits_for_color_pattern (pixpat, target, bits, row_bytes);
      break;
    case CWC (pixpat_rgb_pattern):
      raw_bits_for_rgb_pattern (pixpat, target, bits, row_bytes);
      *height_override = 1;
      break;
    default:
      /* Error!  Bogus pixpat type. */
      *row_bytes = 4;
      *bits = 0;
      break;
    }
}


/* Returns TRUE iff the specified pattern can be compressed to be only
 * four bytes wide with no loss of information.
 */
static inline boolean_t
narrow_p (const uint32 *bits, int row_longs, int height)
{
  const uint32 *p, *end;

  end = &bits[row_longs * height];

  for (p = bits; p != end; p += row_longs)
    {
      uint32 v = p[0];
      int i;

      for (i = row_longs - 1; i > 0; i--)
	if (p[i] != v)
	  return FALSE;
    }

  return TRUE;
}


/* Returns TRUE iff the specified pattern can be compressed to be only
 * one row tall with no loss of information.
 */
static inline boolean_t
short_p (const uint32 *bits, int row_longs, int height)
{
  const uint8 *p, *end;
  int row_bytes;

  row_bytes = row_longs * sizeof (uint32);
  end = ((const uint8 *) bits) + row_bytes * height;
  for (p = ((const uint8 *) bits) + row_bytes; p != end; p += row_bytes)
    {
      if (memcmp (bits, p, row_bytes))
	return FALSE;
    }

  return TRUE;
}


/* Don't call this directly, use the `xdata_for_pixpat' macro. */
static void
xdata_for_raw_data (PixMap *target, xdata_t *x, uint32 *raw_bits,
		    int row_bytes, int height)
{
  uint32 *p, v;
  unsigned row_longs;
  
  memset (x, 0, sizeof *x);
  
  x->magic_cookie = XDATA_MAGIC_COOKIE;
  x->log2_bpp = ROMlib_log2[CW (target->pixelSize)];
  x->ctab_seed_x = CTAB_SEED_X (MR (target->pmTable));
  x->rgb_spec = pixmap_rgb_spec (target);
  
  /* See if it's not a full long wide. */
  if (row_bytes < 4)
    {
      int y;

      /* Tile the raw bits out to four bytes wide.  We do this
       * by converting rows in backwards order, so we don't smash
       * anything before we use it.
       */
      if (row_bytes == 1)
	{
	  for (y = height - 1; y >= 0; y--)
	    raw_bits[y] = (((const uint8 *) raw_bits)[y]) * 0x01010101U;
	}
      else if (row_bytes == 2)
	{
	  for (y = height - 1; y >= 0; y--)
	    raw_bits[y] = (((const uint16 *) raw_bits)[y]) * 0x00010001U;
	}
      else
	abort ();

      row_bytes = 4;
    }
  
  row_longs = row_bytes >> 2;
  
  /* Check for a short & narrow pattern (i.e. one that can be
   * compressed to only one row tall and 4 bytes wide with no
   * loss of information).  This is a common case because solid
   * colors are so common.
   */
  v = raw_bits[0];
  for (p = &raw_bits[row_longs * height - 1]; p != raw_bits; p--)
    if (*p != v)
      break;
  
  if (p == raw_bits)  /* is every uint32 in the pattern the same? */
    {
      int shift;

      /* short & narrow. */
      x->pat_value        = v;
      x->row_bytes        = 4;
      x->log2_row_bytes   = 2;
      x->byte_size        = 4;
      x->row_bits_minus_1 = 31;
      x->height_minus_1   = 0;

      /* Choose the fastest blitting function we can use. */
      if (x->rgb_spec
	  || ((shift = 1 << x->log2_bpp), /* non-rgb ==> shift < 32 */
	      (v != ((v >> shift) | (v << (32 - shift))))))
	{
	  x->blt_func = xdblt_xdata_short_narrow;
	  x->stub_table_for_mode = xdblt_short_narrow_stubs;
	}
      else
	{
	  x->blt_func = xdblt_xdata_norgb_norotate;
	  if (v == 0)
	    x->stub_table_for_mode = xdblt_zeros_stubs;
	  else if (v == (uint32) ~0)
	    x->stub_table_for_mode = xdblt_ones_stubs;
	  else
	    x->stub_table_for_mode = xdblt_short_narrow_stubs;
	}
    }
  else
    {
      if (narrow_p (raw_bits, row_longs, height))
	{
	  int i;

	  /* narrow. */

	  /* Allocate some memory, and compute a long-aligned
	   * pointer for it.  We can't use a Handle here because
	   * the Handle might move in such a way as to become no
	   * longer long-aligned.
	   */
	  x->raw_pat_bits_mem = NewPtr (height * sizeof (uint32) + 3);
	  x->pat_bits = (uint32 *) (((unsigned long) x->raw_pat_bits_mem
				     + 3) & ~3);
	  for (i = 0; i < height; i++)
	    x->pat_bits[i] = raw_bits[i * row_longs];
	  x->stub_table_for_mode = xdblt_tall_narrow_stubs;

	  x->row_bytes        = sizeof (uint32);
	  x->log2_row_bytes   = 2;
	  x->row_bits_minus_1 = 31;
	  x->byte_size        = height * sizeof (uint32);
	}
      else			/* wide */
	{
	  int log2_row_bytes, c;

	  if (short_p (raw_bits, row_longs, height))
	    height = 1;

	  for (log2_row_bytes = 0, c = row_bytes; c >>= 1; log2_row_bytes++)
	    ;

	  x->log2_row_bytes   = log2_row_bytes;
	  x->row_bytes        = row_bytes;
	  x->row_bits_minus_1 = (row_bytes * 8) - 1;
	  x->byte_size        = height << log2_row_bytes;

	  /* Allocate long-aligned memory, as described above. */
	  x->raw_pat_bits_mem = NewPtr (x->byte_size + 3);
	  x->pat_bits = (uint32 *) (((unsigned long) x->raw_pat_bits_mem
				     + 3) & ~3);
	  memcpy (x->pat_bits, raw_bits, x->byte_size);

	  if (height == 1)
	    x->stub_table_for_mode = xdblt_short_wide_stubs;
	  else
	    x->stub_table_for_mode = xdblt_tall_wide_stubs;
	}

      x->blt_func = xdblt_xdata_complex;
      x->height_minus_1 = height - 1;
    }
}


xdata_handle_t
xdata_for_pixpat_with_space (PixPat *pixpat, PixMap *target,
			     xdata_handle_t xh)
{
  uint32 *raw_bits;
  int height;
  unsigned width;
  int max_byte_size, row_bytes;
  TEMP_ALLOC_DECL (raw_bits_temp_storage);

  LOCK_HANDLE_EXCURSION_1
    (xh,
     {
       xdata_t *x = STARH (xh);
  
       /* Compute the dimensions of the PixPat, and allocate scratch
	* space for the biggest possible bitmap that might be required.
	*/
       {
	 const Rect *bounds = &PIXMAP_BOUNDS (MR (pixpat->patMap));
	 height = RECT_HEIGHT (bounds);
	 width = RECT_WIDTH (bounds);
       }
       max_byte_size = width * sizeof (uint32) * height;
       TEMP_ALLOC_ALLOCATE (raw_bits, raw_bits_temp_storage, max_byte_size);
       
       /* Compute the raw bits for this pixpat. */
       raw_bits_for_pixpat (pixpat, target, raw_bits, &row_bytes, &height);
 
       /* Crank out the xdata. */
       xdata_for_raw_data (target, x, raw_bits, row_bytes, height);
       
       TEMP_ALLOC_FREE (raw_bits_temp_storage);
     });

  return xh;
}


xdata_handle_t
xdata_for_pattern (const Pattern pattern, PixMap *target)
{
  xdata_handle_t xh;

  xh = (xdata_handle_t) NewHandle (sizeof (xdata_t));

  LOCK_HANDLE_EXCURSION_1
    (xh,
     {
       uint32 raw_bits[8 * 8];  /* Maximum possible resultant pattern size. */
       int row_bytes;
       xdata_t *x = STARH (xh);
       
       /* Compute the raw bits for this pixpat and crank out the xdata. */
       raw_bits_for_pattern (pattern, target, raw_bits, &row_bytes);
       xdata_for_raw_data (target, x, raw_bits, row_bytes, 8);
     });

  return xh;
}


void
xdata_free (xdata_handle_t xh)
{
  boolean_t xdata_valid_p;
  
  xdata_valid_p = xh && (GetHandleSize ((Handle) xh) == sizeof (xdata_t));
  
  if (xdata_valid_p)
    {
      Ptr p = HxX (xh, raw_pat_bits_mem);
      if (p)
	DisposPtr (p);
    }
  else
    {
      warning_unexpected ("invalid xdata size; appl juked xdata maybe");
    }
  DisposHandle ((Handle) xh);
}
