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

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


#include "rsys/common.h"

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

#include "rsys/depthconv.h"
#include "rsys/cquick.h"
#include "rsys/rgbutil.h"
#include "rsys/prefs.h"
#include "rsys/tempalloc.h"
#include "rsys/picture.h"
#include "rsys/mman.h"
#include "rsys/vdriver.h"

static uint32 depth_table_space[DEPTHCONV_MAX_UINT32_TABLE_SIZE];

static depthconv_func_t conversion_func = NULL;

static int32 cached_src_bpp = -1, cached_dst_bpp = -1;
static int32 cached_src_seed_x = CLC (-1), cached_dst_seed_x = CLC (-1);

static ITabHandle target_itab;

rgb_spec_t mac_16bpp_rgb_spec, mac_32bpp_rgb_spec;

void
pixmap_black_white (const PixMap *pixmap,
		    uint32 *black_return, uint32 *white_return)
{
  if (pixmap->pixelType == CWC (RGBDirect))
    {
      const rgb_spec_t *rgb_spec;

      rgb_spec = pixmap_rgb_spec (pixmap);
      *black_return = rgb_spec->black_pixel;
      *white_return = rgb_spec->white_pixel;
    }
  else
    {
      int bpp;
      
      bpp = CW (pixmap->pixelSize);
      
      *black_return = (1 << bpp) - 1;
      *white_return = 0;
    }
}

void
gd_black_white (GDHandle gdh,
		uint32 *black_return, uint32 *white_return)
{
  GDPtr gd;
  PixMapPtr gd_pmap;
  
  gd = STARH (gdh);
  gd_pmap = STARH (MR (gd->gdPMap));
  
  pixmap_black_white (gd_pmap, black_return, white_return);
}

void
pixmap_free_copy (PixMap *pm)
{
  DisposPtr (MR (pm->baseAddr));
}
 
void
pixmap_copy (const PixMap *src_pm, const Rect *src_rect,
 	     PixMap *return_pm, Rect *return_rect)
{
  /* alas, we must copy to a scratch bitmap */
  /* src is the bitmap in the active screen which cannot be
      accessed dirctly */
  int row_bytes;
  int width, height;
   
  width  = RECT_WIDTH (src_rect);
  height = RECT_HEIGHT (src_rect);
  
  *return_rect = *src_rect;
  
  /* default pixmap field values are the same for src and return
      pixmaps */
  *return_pm = *src_pm;
  
  row_bytes = ((width * CW (src_pm->pixelSize) + 31) / 32) * 4;
  
  return_pm->baseAddr = RM (NewPtr (height * row_bytes));
  return_pm->rowBytes = CW (  row_bytes
			    | PIXMAP_DEFAULT_ROW_BYTES);
  return_pm->bounds = *return_rect;
  
  {
    uint32 black_pixel, white_pixel;
    RgnHandle rgn;
    Rect tmp_rect;
    
    rgn = NewRgn ();
    SectRect (return_rect, &return_pm->bounds, &tmp_rect);
    RectRgn (rgn, &tmp_rect);
    
    pixmap_black_white (src_pm, &black_pixel, &white_pixel);
    
    ROMlib_blt_rgn_update_dirty_rect (rgn, srcCopy,
				      FALSE, CW (src_pm->pixelSize),
				      src_pm, return_pm,
				      src_rect, return_rect,
				      black_pixel, white_pixel);
    DisposeRgn (rgn);
  }
}
 
boolean_t
pixmap_copy_if_screen (const PixMap *src_pm, const Rect *src_rect,
 		       write_back_data_t *write_back_data)
{
#if defined (VDRIVER_SUPPORTS_REAL_SCREEN_BLITS)
  if (VDRIVER_BYPASS_INTERNAL_FBUF_P ()
      && active_screen_addr_p (src_pm))
    {
      write_back_data->dst_pm   = *src_pm;
      write_back_data->dst_rect = *src_rect;

      pixmap_copy (src_pm, src_rect,
 		   &write_back_data->src_pm, &write_back_data->src_rect);

      return TRUE;
    }
#endif

  return FALSE;
}

uint32
pixel_from_rgb (RGBColor *color, 
		const rgb_spec_t *rgb_spec)
{
  if (rgb_spec)
    return ((rgb_spec->rgbcolor_to_pixel)
	    (rgb_spec, color, TRUE));
  else
    return Color2Index (color);
}

void
canonical_from_bogo_color (uint32 index,
			   const rgb_spec_t *rgb_spec,
			   uint32 *pixel_out,
			   RGBColor *rgb_out)
{
  if (rgb_spec)
    {
      RGBColor t_color;
      rgb_spec_t *mac_rgb_spec;

      if (rgb_spec->bpp == 16)
	{
	  mac_rgb_spec = &mac_16bpp_rgb_spec;
	  if (rgb_spec == mac_rgb_spec)
	    {
	      if (pixel_out)
		*pixel_out = CW (index);
	      if (rgb_out)
		(rgb_spec->pixel_to_rgbcolor) (rgb_spec, index, rgb_out);
	      return;
	    }
	}
      else if (rgb_spec->bpp == 32)
	{
	  mac_rgb_spec = &mac_32bpp_rgb_spec;
	  if (rgb_spec == mac_rgb_spec)
	    {
	      if (pixel_out)
		*pixel_out = CL (index);
	      if (rgb_out)
		(rgb_spec->pixel_to_rgbcolor) (rgb_spec, index, rgb_out);
	      return;
	    }
	}
      else
	gui_fatal ("unknown bpp");

      (mac_rgb_spec->pixel_to_rgbcolor) (mac_rgb_spec, index, &t_color);
      if (pixel_out)
	*pixel_out = (rgb_spec->rgbcolor_to_pixel) (rgb_spec, &t_color, TRUE);
      if (rgb_out)
	*rgb_out = t_color;
    }
  else
    {
      if (pixel_out)
	*pixel_out = index;
      if (rgb_out)
	Index2Color (index, rgb_out);
    }
}

void
ROMlib_fg_bk (uint32 *fg_pixel_out, uint32 *bk_pixel_out,
	      RGBColor *fg_rgb_out, RGBColor *bk_rgb_out,
	      const rgb_spec_t *rgb_spec,
	      boolean_t active_screen_addr_p,
	      boolean_t indirect_p)
{
  GrafPtr current_port;
  uint32 fg_pixel, bk_pixel;
  RGBColor fg_rgb, bk_rgb;
  
  current_port = thePort;
  if (CGrafPort_p (current_port))
    {
      if (indirect_p)
	{
	  fg_rgb = CPORT_RGB_FG_COLOR (current_port);
	  fg_pixel = pixel_from_rgb (&fg_rgb, rgb_spec);
	  bk_rgb = CPORT_RGB_BK_COLOR (current_port);
	  bk_pixel = pixel_from_rgb (&bk_rgb, rgb_spec);
	}
      else
	{
	  canonical_from_bogo_color (PORT_FG_COLOR (current_port), rgb_spec,
				     &fg_pixel, &fg_rgb);
	  canonical_from_bogo_color (PORT_BK_COLOR (current_port), rgb_spec,
				     &bk_pixel, &bk_rgb);
	}
    }
  else
    {
      if (active_screen_addr_p)
	{
	  uint32 fg_color, bk_color;
	  
	  fg_color = PORT_FG_COLOR (current_port);
	  fg_rgb = *ROMlib_qd_color_to_rgb (fg_color);
	  fg_pixel = pixel_from_rgb (&fg_rgb, rgb_spec);
	  
	  bk_color = PORT_BK_COLOR (current_port);
	  bk_rgb = *ROMlib_qd_color_to_rgb (bk_color);
	  bk_pixel = pixel_from_rgb (&bk_rgb, rgb_spec);
	}
      else
	{
	  uint32 black_pixel, white_pixel;
	  
	  black_pixel = pixel_from_rgb (&ROMlib_black_rgb_color,
					rgb_spec);
	  white_pixel = pixel_from_rgb (&ROMlib_white_rgb_color,
					rgb_spec);
	  
	  if (PORT_FG_COLOR (current_port) == whiteColor)
	    {
	      fg_pixel = white_pixel;
	      fg_rgb = ROMlib_white_rgb_color;
	    }
	  else
	    {
	      fg_pixel = black_pixel;
	      fg_rgb = ROMlib_black_rgb_color;
	    }

	  if (PORT_BK_COLOR (current_port) == whiteColor)
	    {
	      bk_pixel = white_pixel;
	      bk_rgb = ROMlib_white_rgb_color;
	    }
	  else
	    {
	      bk_pixel = black_pixel;
	      bk_rgb = ROMlib_black_rgb_color;
	    }
	}
    }
  if (fg_pixel_out)
    *fg_pixel_out = fg_pixel;
  if (bk_pixel_out)
    *bk_pixel_out = bk_pixel;

  if (fg_rgb_out)
    *fg_rgb_out = fg_rgb;
  if (bk_rgb_out)
    *bk_rgb_out = bk_rgb;
}

const rgb_spec_t *
pixmap_rgb_spec (const PixMap *pixmap)
{
  if (pixmap->pixelType == CWC (RGBDirect)
      || pixmap->pixelType == CWC (vdriver_rgb_pixel_type))
    {
      if (vdriver_rgb_spec
	  && (active_screen_addr_p (pixmap)
	      || pixmap->pixelType == CWC (vdriver_rgb_pixel_type)))
	return vdriver_rgb_spec;
      else if (pixmap->pixelSize == CWC (16))
	return &mac_16bpp_rgb_spec;
      else if (pixmap->pixelSize == CWC (32))
	return &mac_32bpp_rgb_spec;
      else
	gui_fatal ("unknown pixel size");
    }
  else
    return NULL;
}

void
pixmap_set_pixel_fields (PixMap *pixmap, int bpp)
{
  pixmap->packType = pixmap->packSize = CWC (0);
  
  if (bpp <= 8)
    {
      pixmap->pixelType = CWC (Indirect);
      pixmap->cmpSize = pixmap->pixelSize = CW (bpp);
      pixmap->cmpCount = CWC (1);
    }
  else
    {
      pixmap->pixelType = CWC (RGBDirect);
      pixmap->pixelSize = CW (bpp);
      pixmap->cmpCount = CWC (3);
      switch (bpp)
	{
	case 16:
	  pixmap->cmpSize = CWC (5);
	  break;
	case 32:
	  pixmap->cmpSize = CWC (8);
	  break;
	}
    }
}

static void
sort_color_table (CTabHandle dsth, const CTabHandle srch)
{
  CTabPtr src, dst;
  INTEGER src_ct_size;
  
  src = STARH (srch);
  dst = STARH (dsth);

  /* Claris Home Page has some PICTs with color tables that are too large,
     so we make sure we don't try to copy too much. */
  
  src_ct_size = MIN (CW (src->ctSize), CW (dst->ctSize));
  
  if (src->ctFlags & CTAB_GDEVICE_BIT_X)
    {
      dst->ctSeed = src->ctSeed;
      memcpy (dst->ctTable, src->ctTable,
	      sizeof src->ctTable[0] * (src_ct_size + 1));
    }
  else
    {
      ColorSpec *src_table, *dst_table;
      int max_ctab_elt;
      int i;
      
      src_table = src->ctTable;
      dst_table = dst->ctTable;
      max_ctab_elt = CW (dst->ctSize);
      dst->ctSeed = src->ctSeed;
      for (i = src_ct_size; i >= 0; i--)
	dst_table[CW (src_table[i].value) & max_ctab_elt].rgb
	  = src_table[i].rgb;
    }
}

void
convert_pixmap (const PixMap *src, PixMap *dst,
		const Rect *rect,
		CTabPtr conv_table)
{
  GDHandle the_gd;
  int src_bpp, dst_bpp;
  int width, height;
  int32 dst_seed_x, src_seed_x;
  const rgb_spec_t *src_rgb_spec, *dst_rgb_spec;
  
  uint8 *src_base, *dst_base;
  int16 src_row_bytes, dst_row_bytes;
  
  write_back_data_t write_back;
  boolean_t copy_p;
  
  TEMP_ALLOC_DECL (temp_scratch_pm_bits);
  
  /* Grab some useful information about the PixMaps. */
  src_bpp      = CW (src->pixelSize);
  dst_bpp      = CW (dst->pixelSize);
  
  width  = RECT_WIDTH (rect);
  height = RECT_HEIGHT (rect);
  
  /* Make sure both bits per pixel are powers of 2 less than or equal
     to 32. */
  gui_assert (src_bpp <= 32 && dst_bpp <= 32
	      && (src_bpp & (src_bpp - 1)) == 0
	      && (dst_bpp & (dst_bpp - 1)) == 0);

  the_gd = MR (TheGDevice);
  
#define BPP_PIXEL_TYPE(bpp) ((bpp) > 8 ? RGBDirect : Indirect)
#define MUNGE(a, b) ((a) + (b) * 0x100)
  switch (MUNGE (BPP_PIXEL_TYPE (src_bpp),
		 BPP_PIXEL_TYPE (dst_bpp)))
    {
    case MUNGE (Indirect, Indirect):
      {
	src_seed_x   = CTAB_SEED_X (MR (src->pmTable)); /* big endian */
	dst_seed_x   = CTAB_SEED_X (PIXMAP_TABLE (GD_PMAP (the_gd)));

	if (   src_bpp != cached_src_bpp       || dst_bpp != cached_dst_bpp
	    || src_seed_x != cached_src_seed_x || dst_seed_x != cached_dst_seed_x
	    || conv_table)
	  {
	    if (conv_table)
	      {
		conversion_func
		  = depthconv_make_ind_to_ind_table (depth_table_space,
						     src_bpp, dst_bpp, NULL,
						     conv_table->ctTable);
		/* Remember the specs for the table we just created. */
		cached_src_seed_x = CLC (-1);
		cached_dst_seed_x = CLC (-1);
	      }
	    else
	      {
 		CTabHandle target_table, src_table;
 		CTabHandle mapping;
 		int max_mapping_elt, max_src_elt;
 		
 		src_table = MR (src->pmTable);
 		max_src_elt = CTAB_SIZE (src_table);
 		max_mapping_elt = (1 << src_bpp) - 1;
 		mapping
 		  = (CTabHandle) (NewHandle
 				  (CTAB_STORAGE_FOR_SIZE (max_mapping_elt)));
 		CTAB_SIZE_X (mapping) = CW (max_mapping_elt);
 		CTAB_FLAGS_X (mapping) = CWC (0);
 		/* the source color table may not specify all  possible
 		   colors, so set unspecified colors to some sane value
 		   
 		   really, we only need to set the `value' field
 		   of the color specs */
 		if (max_mapping_elt > max_src_elt)
 		  memset (&CTAB_TABLE (mapping)[max_src_elt + 1], 0,
 			  (max_mapping_elt - max_src_elt) * sizeof (ColorSpec));
		
 		sort_color_table (mapping, src_table);
 		
 		if (dst_bpp == 1 && BITMAP_P (dst))
 		  target_table = ROMlib_bw_ctab;
 		else
 		  target_table = NULL;
 		
 		GetSubTable (mapping, DEFAULT_ITABLE_RESOLUTION,
 			     target_table);
 		
 		conversion_func
 		  = depthconv_make_ind_to_ind_table (depth_table_space,
 						     src_bpp, dst_bpp, NULL,
 						     CTAB_TABLE (mapping));
 		
 		/* free up the mapping table */
 		DisposHandle ((Handle) mapping);
 		
 		/* Remember the specs for the table we just created. */
 		cached_src_seed_x = src_seed_x;
		cached_dst_seed_x = dst_seed_x;
	      }
	  }
	break;
      }
    case MUNGE (Indirect, RGBDirect):
      {
	CTabHandle src_table;

	src_table = MR (src->pmTable);
	src_seed_x = CTAB_SEED_X (src_table);
	
	dst_rgb_spec = pixmap_rgb_spec (dst);
	dst_seed_x = dst_rgb_spec->seed_x;
	
	if (   src_bpp != cached_src_bpp       || dst_bpp != cached_dst_bpp
	    || src_seed_x != cached_src_seed_x || dst_seed_x != cached_dst_seed_x)
	  {
	    conversion_func
	      = depthconv_make_ind_to_rgb_table (depth_table_space, src_bpp,
						 NULL, CTAB_TABLE (src_table),
						 dst_rgb_spec);
	  }
	break;
      }
    case MUNGE (RGBDirect, Indirect):
      {
	CTabHandle target_table;
	static CTabHandle cached_target_table;
	
	src_rgb_spec = pixmap_rgb_spec (src);
	src_seed_x = src_rgb_spec->seed_x;
	
	if (dst_bpp == 1 && BITMAP_P (dst))
	  target_table = ROMlib_bw_ctab;
	else
	  target_table = PIXMAP_TABLE (GD_PMAP (the_gd));
	
	if (!target_itab)
	  {
	    ZONE_SAVE_EXCURSION
	      (SysZone,
	       {
		 target_itab = (ITabHandle) NewHandle (sizeof (ITab));
		 ITAB_SEED_X (target_itab) = -1;
	       });
	  }
#warning ctm added questionable caching here
	if (target_table != cached_target_table ||
	    ITAB_SEED_X (target_itab) != CTAB_SEED_X (target_table))
	  {
	    MakeITable (target_table, target_itab, DEFAULT_ITABLE_RESOLUTION);
	    cached_target_table = target_table;
	  }
	dst_seed_x = CTAB_SEED_X (target_table);
	
	if (   src_bpp != cached_src_bpp       || dst_bpp != cached_dst_bpp
	    || src_seed_x != cached_src_seed_x || dst_seed_x != cached_dst_seed_x)
	  {
	    conversion_func
	      = depthconv_make_rgb_to_ind_table (depth_table_space, dst_bpp,
						 NULL,
						 target_table, target_itab,
						 src_rgb_spec);
	  }
	break;
      }
    case MUNGE (RGBDirect, RGBDirect):
      {
	src_rgb_spec = pixmap_rgb_spec (src);
	dst_rgb_spec = pixmap_rgb_spec (dst);
	
	src_seed_x = src_rgb_spec->seed_x;
	dst_seed_x = dst_rgb_spec->seed_x;

	if (   src_bpp != cached_src_bpp       || dst_bpp != cached_dst_bpp
	    || src_seed_x != cached_src_seed_x || dst_seed_x != cached_dst_seed_x)
	  {
	    conversion_func
	      = depthconv_make_rgb_to_rgb_table (depth_table_space, NULL,
						 src_rgb_spec, dst_rgb_spec);
	  }
	break;
      }
    default:
      gui_fatal ("unknown pixel types");
    }
  
  /* Make sure we're doing something useful. */
  gui_assert ((src_seed_x != dst_seed_x
	       || conv_table)
	      || src_bpp != dst_bpp);
  
  cached_src_bpp = src_bpp;
  cached_dst_bpp = dst_bpp;
  
  copy_p = pixmap_copy_if_screen (src, rect, &write_back);
  if (copy_p)
    {
      src  = &write_back.src_pm;
      rect = &write_back.src_rect;
    }
  
  PIXMAP_ASSERT_NOT_SCREEN (dst);
  
  /* using BITMAP_... on a PixMap * is slimy */
  src_row_bytes = BITMAP_ROWBYTES (src);
  src_base = (uint8 *) (MR (src->baseAddr)
	      + (CW (rect->top) - CW (src->bounds.top)) * src_row_bytes
	      + (CW (rect->left) - CW (src->bounds.left)) * src_bpp / 8);
  
  dst_row_bytes = BITMAP_ROWBYTES (dst);
  dst_base = (uint8 *) MR (dst->baseAddr);
  
  (*conversion_func) (depth_table_space,
		      src_base, src_row_bytes, dst_base, dst_row_bytes,
		      0, 0, height, width);
  
  dst->bounds = *rect;

  if (copy_p)
    pixmap_free_copy (&write_back.src_pm);
  TEMP_ALLOC_FREE (temp_scratch_pm_bits);
}
