/* BeOS driver for Ghostscript using a BBitmap for buffering.
 * This driver has been heavily rewritten from the previous version.
 *
 * Copyright 1998, Jake Hamby
 * Send all comments, bug fixes, etc. to:  jehamby@lightside.com
 *
 * Anti-Aliasing drivers: bealpha2, bealpht2, bealpha4 and bealpht4 
 * Copyright 2000, Michael Pfeiffer
 * Email: michael.pfeiffer@utanet.at
 *
 * Description: bealpha2 and bealpha4 anti-alias text and graphic. 
 * bealpht2 and bealpht4 anti-alias text only.
 * 
 * To do: 
 * -  Find optimal values for (see gdevbe.h):
 *    - BE_ALPHA_HASHTABLE_SIZE
 *    - BE_ALPHA_MAX_BITMAPS
 *    - BE_ALPHA_REMOVE_BITMAPS
 *    - BE_ALPHA_MAX_SIZE
 *    and an optimal hash function HASH_FUNC in this file.
 * -  Set the bits per pixel for the internal bitmap with a 
 *    parameter (e.g. gs -dBitsPerPixel={8,16,32}).
 * -  Limit the number of bitmaps in the mono bitmap cache.
 * -  alpha_tmp_list exists because it is assumed that DrawBitmapAsync
 *    scales on smp systems, a tmp_list for mono bitmaps should
 *    also be implemented.
 * -  Let the GS interpreter be interruptible (return a negative number
 *    in the device-callback functions).
 */

// #define PROFILE 1

#include <View.h>
#include <Window.h>
#include <Bitmap.h>

typedef void* HWND;          /* For old DLL interface */

extern "C" void gsdll_draw(unsigned char *device, BView *view, BRect dest, BRect src);

extern "C" {
#define __PROTOTYPES__		/* C++ needs prototypes, of course */
#include "gx.h"			/* for gx_bitmap; includes std.h */
#include "math_.h"
#include "memory_.h"
#include "gserrors.h"
#include "gsparam.h"
#include "gsstruct.h"
#include "gxdevice.h"
#include "gsdevice.h"
#include "gdevbe.h"
#include "gsdll.h"
extern gx_device_be gs_bealpha2_device;	
extern gx_device_be gs_bealpha4_device;	
extern gx_device_be gs_bealpht2_device;	
extern gx_device_be gs_bealpht4_device;	
extern gx_device_be gs_beos_device;	/* Intel does name mangling on */
extern HWND hwndtext;		/* on variables, so define these here. */
}

HWND hwndtext;               /* currently unused */

/* Procedures */

private dev_proc_open_device(be_open);
private dev_proc_sync_output(be_sync);
private dev_proc_output_page(be_output_page);
private dev_proc_close_device(be_close);
private dev_proc_map_rgb_color(be_map_rgb_color);
private dev_proc_map_color_rgb(be_map_color_rgb);
private dev_proc_fill_rectangle(be_fill_rectangle);
private dev_proc_copy_mono(be_copy_mono);
private dev_proc_copy_color(be_copy_color);
private dev_proc_draw_line(be_draw_line);
private dev_proc_strip_tile_rectangle(be_strip_tile_rectangle);

private dev_proc_get_params(be_get_params);
private dev_proc_put_params(be_put_params);

/* The device descriptor */
private gx_device_procs be_procs = {
	be_open,
	gx_default_get_initial_matrix,
	be_sync,
	be_output_page,
	be_close,
	be_map_rgb_color,
	be_map_color_rgb,
	be_fill_rectangle,
	NULL,			/* tile_rectangle */
	be_copy_mono,
	NULL,			/* copy_color */
	be_draw_line,
	NULL,			/* get_bits */
	NULL, // be_get_params,			/* get_params */
	NULL, // be_put_params,			/* put_params */
	NULL,			/* map_cmyk_color */
	NULL,			/* get_xfont_procs */
	NULL,			/* get_xfont_device */
	NULL,			/* map_rgb_alpha_color */
	gx_page_device_get_page_device,
	NULL,			/* get_alpha_bits */
	NULL,			/* copy_alpha */
	NULL,			/* get_band */
	NULL,			/* copy_rop */
	NULL,			/* fill_path */
	NULL,			/* stroke_path */
	NULL,			/* fill_mask */
	NULL,			/* fill_trapezoid */
	NULL,			/* fill_parallelogram */
	NULL,			/* fill_triangle */
	NULL,			/* draw_thin_line */
	NULL,			/* begin_image */
	NULL,			/* image_data */
	NULL,			/* end_image */
	NULL			/* be_strip_tile_rectangle */
};

/* The instance is public. */
gx_device_be gs_beos_device = {
	std_device_color_body(gx_device_be, &be_procs, "beos",
	  INITIAL_WIDTH, INITIAL_HEIGHT,
	  INITIAL_RESOLUTION, INITIAL_RESOLUTION,
	  /*dci_color(*/24, 255, 256/*)*/),
	{ 0 },			/* std_procs */
	-1,				/* mutex */
	NULL,			/* main bitmap */
	NULL,			/* offscreen view */
	NULL,			/* bmapcache ptr */
	0,				/* next bitmap to use */
};

#ifdef BE_ALPHA_BITMAP_CACHE
/* The BBitmap alpha cache struct */
typedef struct bmap_alpha_cache_s {
	gx_bitmap_id id;
	rgb_color color;
	long size;
	struct bmap_alpha_cache_s *of_next, *of_prev, // overflow list
		*mru_next, *mru_prev; // most recently used list
	struct bmap_alpha_cache_s **ht_entry; // the hashtable entry with this bitmap 	
	BBitmap bitmap;
	
	bmap_alpha_cache_s(struct bmap_alpha_cache_s **ht_entry, gx_bitmap_id id, rgb_color color, 
		int w, int h, color_space mode) :

		id(id), color(color), size(w*h), 
		of_next(NULL), of_prev(NULL), mru_next(NULL), mru_prev(NULL), 
		ht_entry(ht_entry),
		bitmap(BRect(0, 0, w-1, h-1), mode) { }
} bmap_alpha_cache;

#define BE_ALPHA_HASH(key) ((key) % BE_ALPHA_HASHTABLE_SIZE)
#define SAME_COLOR(c1, c2) (*((uint32*)&(c1)) == *((uint32*)&(c2)))

// Find bmapc in hashtable
// Returns entry in hashtable and bmapc if it is in overflow list
static inline bmap_alpha_cache **be_alpha_find_bmap(gx_device_be *bdev, gx_bitmap_id id, rgb_color color, bmap_alpha_cache **bmapc) {
	bmap_alpha_cache **ht_entry = &bdev->alpha_hashtable[BE_ALPHA_HASH(id)];
	bmap_alpha_cache *current = *ht_entry;
	while (current) {
	 	if ((current->id == id) && SAME_COLOR(current->color, color)) {
	 		break; // found
	 	}
		current = current->of_next;
	}
	*bmapc = current;
	return ht_entry;
}

// Insert into hashtable
static inline void be_alpha_ht_insert(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	bmapc->of_next = *ht_entry;
	bmapc->of_prev = NULL;
	if (*ht_entry) {
		(*ht_entry)->of_prev = bmapc;
	}
	*ht_entry = bmapc;
}

// Remove from hashtable
static inline void be_alpha_ht_remove(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	if (*ht_entry == bmapc) {
		*ht_entry = bmapc->of_next;
	}
	if (bmapc->of_next) {
		bmapc->of_next->of_prev = bmapc->of_prev;
	}
	if (bmapc->of_prev) {
		bmapc->of_prev->of_next = bmapc->of_next;
	}
}

// Insert at top of mru list
static inline void be_alpha_mru_insert(gx_device_be *bdev, bmap_alpha_cache *bmapc) {
	bmapc->mru_next = bdev->alpha_mru_first;
	bmapc->mru_prev = NULL;
	if (bdev->alpha_mru_first) {
		bdev->alpha_mru_first->mru_prev = bmapc;
	} else { // first element in mru list
		bdev->alpha_mru_last = bmapc; 
	}
	bdev->alpha_mru_first = bmapc;
}

// Remove from mru list
static inline void be_alpha_mru_remove(gx_device_be *bdev, bmap_alpha_cache *bmapc) {
	if (bdev->alpha_mru_first == bmapc) { 
		bdev->alpha_mru_first = bmapc->mru_next;
	}
	if (bdev->alpha_mru_last == bmapc) {
		bdev->alpha_mru_last = bmapc->mru_prev;
	}
	if (bmapc->mru_next) {
		bmapc->mru_next->mru_prev = bmapc->mru_prev;
	}
	if (bmapc->mru_prev) {
		bmapc->mru_prev->mru_next = bmapc->mru_next;
	}
}

// Move bmapc in front of overflow list of hashtable
static inline void be_alpha_update_of_list(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	if (*ht_entry != bmapc) {
		be_alpha_ht_remove(bdev, ht_entry, bmapc);
		be_alpha_ht_insert(bdev, ht_entry, bmapc);
	}
}

// Move bmapc in front of mru list
static inline void be_alpha_update_mru_list(gx_device_be *bdev, bmap_alpha_cache *bmapc) {
	if (bdev->alpha_mru_first != bmapc) {
		be_alpha_mru_remove(bdev, bmapc);
		be_alpha_mru_insert(bdev, bmapc);
	}
}

static inline void be_alpha_update_bmap(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	be_alpha_update_of_list(bdev, ht_entry, bmapc);
	be_alpha_update_mru_list(bdev, bmapc);
}

static inline void be_alpha_insert_bmap(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	be_alpha_ht_insert(bdev, ht_entry, bmapc);
	be_alpha_mru_insert(bdev, bmapc);
	bdev->alpha_num_bmaps ++;
	bdev->alpha_size += bmapc->size;
}

static inline void be_alpha_remove_bmap(gx_device_be *bdev, bmap_alpha_cache **ht_entry, bmap_alpha_cache *bmapc) {
	be_alpha_ht_remove(bdev, ht_entry, bmapc);
	be_alpha_mru_remove(bdev, bmapc);
	bdev->alpha_num_bmaps --;
	bdev->alpha_size -= bmapc->size;
}

static inline void be_alpha_add_tmp(gx_device_be *bdev, bmap_alpha_cache *bmapc) {
	bmapc->of_next = bdev->alpha_tmp_list;
	bdev->alpha_tmp_list = bmapc;
	bdev->alpha_num_bmaps ++;
	bdev->alpha_size += bmapc->size;	
}

static inline void be_alpha_remove_all_tmp(gx_device_be *bdev) {
	bmap_alpha_cache *bmapc = bdev->alpha_tmp_list, *old;
	while (bmapc) {
		bdev->alpha_num_bmaps --;
		bdev->alpha_size -= bmapc->size;
		old = bmapc; bmapc = bmapc->of_next;
		delete old;
	}
	bdev->alpha_tmp_list = NULL;
}

#endif

/* The BBitmap cache struct */
struct bmapcache_s {
	gx_bitmap_id id;
	struct bmapcache_s *next;
	BBitmap bitmap;
	
	bmapcache_s(gx_bitmap_id id, BRect bounds, color_space mode) :
		id(id), next(NULL), bitmap(bounds, mode) { }
};

/* Open the device. */
private int
be_open(gx_device *dev)
{
    gx_device_be *bdev = (gx_device_be *)dev;
	bdev->bmapcache_current = &(bdev->bmapcache);
	bdev->mutex = create_sem(1, "gsdll sem");
	
	if (dev->width == INITIAL_WIDTH)
		dev->width = (int)(8.5 * dev->x_pixels_per_inch);
	if (dev->height == INITIAL_HEIGHT)
		dev->height = (int)(11.0 * dev->y_pixels_per_inch);

	BRect frame(0.0, 0.0, dev->width-1, dev->height-1);
	bdev->m_bitmap = new BBitmap(frame, B_RGB_32_BIT, true);
	bdev->m_view = new BView(frame, "bitmap view", B_FOLLOW_NONE, B_WILL_DRAW);
	bdev->m_bitmap->AddChild(bdev->m_view);
	bdev->m_view->Window()->Lock();
	bdev->m_view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);

#ifdef BE_ALPHA_BITMAP_CACHE
	for (int i = 0; i < BE_ALPHA_HASHTABLE_SIZE; i++) {
		bdev->alpha_hashtable[i] = NULL;
	}
	bdev->alpha_mru_first = bdev->alpha_mru_last = bdev->alpha_tmp_list = NULL;
	bdev->alpha_num_bmaps = 0;
	bdev->alpha_size = 0;
#endif 

#ifdef PROFILE
	bdev->fillrect = bdev->copymono = bdev->drawline = bdev->copyalpha = 0;
	bdev->size = 0; bdev->bitmaps = 0;
#endif

	/* notify caller about new device */
	(*pgsdll_callback)(GSDLL_DEVICE, (char *)dev, 1);
	(*pgsdll_callback)(GSDLL_SIZE, (char *)dev,
			(dev->width & 0xffff) +
			((ulong)(dev->height & 0xffff) << 16));
    return 0;
}

/* Close the device. */
private int
be_close(gx_device *dev)
{
    gx_device_be *bdev = (gx_device_be *)dev;
    /* wait until bitmap is not being used by caller */
    gsdll_lock_device((unsigned char*)dev, 1);
	(*pgsdll_callback)(GSDLL_DEVICE, (char *)dev, 0);
	gsdll_lock_device((unsigned char*)dev, 0);

#ifdef PROFILE
    fprintf(stderr, "fillrect=%d copymono=%d drawline=%d copyalpha=%d "
    	"bitmaps=%ld size=%ld\n",
    	bdev->fillrect, bdev->copymono, bdev->drawline, bdev->copyalpha,
   		bdev->bitmaps, bdev->size);
  #ifdef BE_ALPHA_BITMAP_CACHE
    fprintf(stderr, "alpha_num_bmaps=%d alpha_size=%ld\n",
   		bdev->alpha_num_bmaps, bdev->alpha_size);
  #endif
#endif
	bdev->m_view->Sync();
	delete bdev->m_bitmap;	/* this also deletes m_view */
	bmapcache_s *cur2 = bdev->bmapcache;
	while(cur2) {
		bmapcache_s *next = cur2->next;
		delete cur2;
		cur2 = next;
	}

#ifdef BE_ALPHA_BITMAP_CACHE
	bmap_alpha_cache *current, *old;
	for (int i = 0; i < BE_ALPHA_HASHTABLE_SIZE; i++) {
		current = bdev->alpha_hashtable[i];
		while (current) {
			old = current;
			current = current->of_next;
			delete old;
		}
		bdev->alpha_hashtable[i] = NULL;
	}
	be_alpha_remove_all_tmp(bdev);
	bdev->alpha_mru_first = bdev->alpha_mru_last = NULL;
	bdev->alpha_num_bmaps = 0;
	bdev->alpha_size = 0;
#endif 

	delete_sem(bdev->mutex);
    return 0;
}

/* Map a color.  The "device colors" are rgb_color structs. */
private gx_color_index
be_map_rgb_color(register gx_device *dev,
		gx_color_value r, gx_color_value g, gx_color_value b)
{
//    gx_device_be *bdev = (gx_device_be *)dev;
    union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.r.red = r >> 8;
	colorcast.r.green = g >> 8;
	colorcast.r.blue = b >> 8;
	colorcast.r.alpha = 0;
	return colorcast.g;
}


/* Map a "device color" back to r-g-b. */
/* Foreground and background may be mapped to other colors, so */
/* they are handled specially. */
private int
be_map_color_rgb(register gx_device *dev, gx_color_index color,
		gx_color_value prgb[3])
{
//    gx_device_be *bdev = (gx_device_be *)dev;
    union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.g = color;
	prgb[0] = colorcast.r.red << 8;
	prgb[1] = colorcast.r.green << 8;
	prgb[2] = colorcast.r.blue << 8;
	return 0;
}

/* Synchronize the display with the commands already given */
private int
be_sync(register gx_device *dev)
{
    gx_device_be *bdev = (gx_device_be *)dev;
    bdev->m_view->Sync();
    return (*pgsdll_callback)(GSDLL_SYNC, (char *)dev, 0);
}

/* Make the window visible, and display the output. */
int
be_output_page(gx_device *dev, int copies, int flush)
{
    gx_device_be *bdev = (gx_device_be *)dev;
    bdev->m_view->Sync();
	int ret = (*pgsdll_callback)(GSDLL_PAGE, (char *)dev, 0);
#ifdef PROFILE
    fprintf(stderr, "fillrect=%d copymono=%d drawline=%d copyalpha=%d "
    	"bitmaps=%ld size=%ld\n",
    	bdev->fillrect, bdev->copymono, bdev->drawline, bdev->copyalpha,
   		bdev->bitmaps, bdev->size);
  #ifdef BE_ALPHA_BITMAP_CACHE
    fprintf(stderr, "alpha_num_bmaps=%d alpha_size=%ld\n",
   		bdev->alpha_num_bmaps, bdev->alpha_size);
  #endif
#endif
    return ret;
}

/* Fill a rectangle with a color. */
private int
be_fill_rectangle(register gx_device *dev,
		 int x, int y, int w, int h, gx_color_index color)
{
    gx_device_be *bdev = (gx_device_be *)dev;
#ifdef PROFILE
    bigtime_t start = system_time();
#endif
	union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.g = color;
	bdev->m_view->SetDrawingMode(B_OP_COPY);
	bdev->m_view->SetHighColor(colorcast.r);
	bdev->m_view->FillRect(BRect(x, y, w+x-1, y+h-1));

#if 0
	if(x==0 && y==0 && w==bdev->width && h==bdev->height) {
		/* Just cleared the screen so clear the bitmap cache */
fprintf(stderr, "clear bitmap cache\n");
		bmapcache_s *cur2 = bdev->bmapcache;
		while(cur2) {
			bmapcache_s *next = cur2->next;
			delete cur2;
			cur2 = next;
		}
		bdev->bmapcache = NULL;
		bdev->bmapcache_current = &(bdev->bmapcache);
	}
#endif

#ifdef PROFILE
	bdev->fillrect += (int)(system_time() - start);
#endif
	return 0;
}

/* Copy a monochrome bitmap. */
private int
be_copy_mono(register gx_device *dev,
	    const byte *base, int sourcex, int raster, gx_bitmap_id id,
	    int x, int y, int w, int h,
	    gx_color_index zero, gx_color_index one)
{
    gx_device_be *bdev = (gx_device_be *)dev;
#ifdef PROFILE
    bigtime_t start = system_time();
#endif

    if(raster) {
		union {
			rgb_color r;
			gx_color_index g;
		} colorcast;
		colorcast.g = one;
		bdev->m_view->SetHighColor(colorcast.r);
		if(zero == gx_no_color_index)
			bdev->m_view->SetDrawingMode(B_OP_OVER);
		else {
			colorcast.g = zero;
			bdev->m_view->SetLowColor(colorcast.r);
			bdev->m_view->SetDrawingMode(B_OP_COPY);
		}
	
		BBitmap *bitmap=0;
		if(id != gx_no_bitmap_id) {
			bmapcache_s *current = bdev->bmapcache;
			while(current) {
			    if(current->id == id) {
			    	bitmap = &(current->bitmap);
					break;
			    }
				current = current->next;
			}
		}
		
		if(!bitmap) {
			bmapcache_s *bmapc = new bmapcache_s(id, BRect(0.0, 0.0, (raster << 3) - 1, h-1), B_MONOCHROME_1_BIT);
		    bmapc->bitmap.SetBits(base, raster * h, 0, B_MONOCHROME_1_BIT);
			bitmap = &(bmapc->bitmap);
		    if (id != gx_no_bitmap_id) {
				*(bdev->bmapcache_current) = bmapc;
				bdev->bmapcache_current = &(bmapc->next);
				bdev->m_view->DrawBitmapAsync(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
			} else {
				bdev->m_view->DrawBitmap(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
				delete bmapc;
			}
		} else {
			bdev->m_view->DrawBitmapAsync(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
		}
    }
#ifdef PROFILE
	bdev->copymono += (int)(system_time() - start);
#endif
    return 0;
}

/* Copy a color bitmap. */
/* Note:  I couldn't find any PS file which uses this function, so I left
 * it unimplemented for now.  -jeh
 */
private int
be_copy_color(register gx_device *dev,
	     const byte *base, int sourcex, int raster, gx_bitmap_id id,
	     int x, int y, int w, int h)
{
//    gx_device_be *bdev = (gx_device_be *)dev;
    fprintf(stderr, "copy color!\n");
    return 0;
}

/* Draw a line */
private int
be_draw_line(register gx_device *dev,
  	     int x0, int y0, int x1, int y1, gx_color_index color)
{
    gx_device_be *bdev = (gx_device_be *)dev;
#ifdef PROFILE
    bigtime_t start = system_time();
#endif
	union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.g = color;
	bdev->m_view->SetDrawingMode(B_OP_COPY);
	bdev->m_view->SetHighColor(colorcast.r);
	bdev->m_view->StrokeLine(BPoint(x0, y0), BPoint(x1, y1));
#ifdef PROFILE
	bdev->drawline += (int)(system_time() - start);
#endif
    return 0;
}

/* Tile a rectangle. */
/* Note:  Because the Be GUI always has at least 256 colors, and because
 *   this function is only used for halftoning, I left it unimplemented for
 *   now.  -jeh
 */
private int
be_strip_tile_rectangle(register gx_device *dev, const gx_strip_bitmap *tiles,
		       int x, int y, int w, int h,
		       gx_color_index zero, gx_color_index one,
		       int px, int py)
{
//    gx_device_be *bdev = (gx_device_be *)dev;
    fprintf(stderr, "Striptile rectangle\n");
    return 0;
}

/* Lock to make DLL threadsafe */
#if defined(__POWERPC__)
__declspec(export)
#endif
int gsdll_lock_device(unsigned char *dev, int flag) {
	gx_device_be *bdev = (gx_device_be *)dev;
    if (flag) {
    	acquire_sem(bdev->mutex);
    	return 1;
    }
    release_sem(bdev->mutex);
    return 0;
}

/* Draw to on-screen BView */
#if defined(__POWERPC__)
__declspec(export)
#endif
void gsdll_draw(unsigned char *dev, BView *view, BRect dest, BRect src) {
	gx_device_be *bdev = (gx_device_be *)dev;
	view->SetDrawingMode(B_OP_COPY);
	view->DrawBitmapAsync(bdev->m_bitmap, src, dest);
}

#if 0
int be_get_params(gx_device *dev, gs_param_list *plist) {
	if (plist) {
		gs_param_enumerator_t iterator;
		gs_param_key_t key;
		param_init_enumerator(&iterator);
		while (0 == param_get_next_key(plist, &iterator, &key)) {
			if (key.data) {
				fprintf(stderr, "%*.*s %d\n", (int)key.size, (int)key.size, key.data);
			}
		}
	}
	return 0;
}

int be_put_params(gx_device *dev, gs_param_list *plist) {	
	if (plist) {
		gs_param_enumerator_t iterator;
		gs_param_key_t key;
		param_init_enumerator(&iterator);
		while (0 == param_get_next_key(plist, &iterator, &key)) {
			if (key.data) {
				fprintf(stderr, "%*.*s %d\n", (int)key.size, (int)key.size, key.data);
			}
		}
	
		int bpp;
		param_read_int(plist, "BitsPerPixel", &bpp);
		long l;
		param_read_long(plist, "BitsPerPixel", &l);
	}
	return 0;
}
#endif
/* Anti-aliasing Devices: bealpha2, bealpha4, bealpht2, bealpht4 */

/* Procedures */
private dev_proc_map_color_rgb(be_alpha_map_color_rgb);
private dev_proc_map_rgb_alpha_color(be_alpha_map_rgb_alpha_color);
// private dev_proc_get_alpha_bits(be_alpha2_get_alpha_bits);
// private dev_proc_get_alpha_bits(be_alpha4_get_alpha_bits);
private dev_proc_copy_alpha(be_alpha_copy_alpha);

/* ---------------------------------- */
/* ------------ bealpha2 ------------ */
/* ---------------------------------- */

/* The device descriptor */
private gx_device_procs be_alpha2_procs = {
	be_open,
	gx_default_get_initial_matrix,
	be_sync,
	be_output_page,
	be_close,
	be_map_rgb_color,
	be_alpha_map_color_rgb, // be_map_color_rgb,
	be_fill_rectangle,
	NULL,			/* tile_rectangle */
	be_copy_mono,
	NULL,			/* copy_color */
	be_draw_line,
	NULL,			/* get_bits */
	NULL,			/* get_params */
	NULL,			/* put_params */
	NULL,			/* map_cmyk_color */
	NULL,			/* get_xfont_procs */
	NULL,			/* get_xfont_device */
	be_alpha_map_rgb_alpha_color,			/* map_rgb_alpha_color */
	gx_page_device_get_page_device,
	NULL, // be_alpha2_get_alpha_bits,			/* get_alpha_bits */
	be_alpha_copy_alpha,			/* copy_alpha */
	NULL,			/* get_band */
	NULL,			/* copy_rop */
	NULL,			/* fill_path */
	NULL,			/* stroke_path */
	NULL,			/* fill_mask */
	NULL,			/* fill_trapezoid */
	NULL,			/* fill_parallelogram */
	NULL,			/* fill_triangle */
	NULL,			/* draw_thin_line */
	NULL,			/* begin_image */
	NULL,			/* image_data */
	NULL,			/* end_image */
	NULL			/* be_strip_tile_rectangle */
};

/* The instance is public. */
gx_device_be gs_bealpha2_device = {
    std_device_dci_alpha_type_body(gx_device_be, &be_alpha2_procs,
	"bealpha2", NULL,
	  INITIAL_WIDTH, INITIAL_HEIGHT,
	  INITIAL_RESOLUTION, INITIAL_RESOLUTION,
	3, 32, 255, 255, 256, 256, 2, 2),
	{ 0 },			/* std_procs */
	-1,				/* mutex */
	NULL,			/* main bitmap */
	NULL,			/* offscreen view */
	NULL,			/* bmapcache ptr */
	0,				/* next bitmap to use */
};


/* ---------------------------------- */
/* ------------ bealpha4 ------------ */
/* ---------------------------------- */

/* The device descriptor */
private gx_device_procs be_alpha4_procs = {
	be_open,
	gx_default_get_initial_matrix,
	be_sync,
	be_output_page,
	be_close,
	be_map_rgb_color,
	be_alpha_map_color_rgb, // be_map_color_rgb,
	be_fill_rectangle,
	NULL,			/* tile_rectangle */
	be_copy_mono,
	NULL,			/* copy_color */
	be_draw_line,
	NULL,			/* get_bits */
	NULL,			/* get_params */
	NULL,			/* put_params */
	NULL,			/* map_cmyk_color */
	NULL,			/* get_xfont_procs */
	NULL,			/* get_xfont_device */
	be_alpha_map_rgb_alpha_color,			/* map_rgb_alpha_color */
	gx_page_device_get_page_device,
	NULL, // be_alpha4_get_alpha_bits,			/* get_alpha_bits */
	be_alpha_copy_alpha,			/* copy_alpha */
	NULL,			/* get_band */
	NULL,			/* copy_rop */
	NULL,			/* fill_path */
	NULL,			/* stroke_path */
	NULL,			/* fill_mask */
	NULL,			/* fill_trapezoid */
	NULL,			/* fill_parallelogram */
	NULL,			/* fill_triangle */
	NULL,			/* draw_thin_line */
	NULL,			/* begin_image */
	NULL,			/* image_data */
	NULL,			/* end_image */
	NULL			/* be_strip_tile_rectangle */
};

/* The instance is public. */
gx_device_be gs_bealpha4_device = {
    std_device_dci_alpha_type_body(gx_device_be, &be_alpha4_procs,
	"bealpha4", NULL,
	  INITIAL_WIDTH, INITIAL_HEIGHT,
	  INITIAL_RESOLUTION, INITIAL_RESOLUTION,
	3, 32, 255, 255, 256, 256, 4, 4),
	{ 0 },			/* std_procs */
	-1,				/* mutex */
	NULL,			/* main bitmap */
	NULL,			/* offscreen view */
	NULL,			/* bmapcache ptr */
	0,				/* next bitmap to use */
};

/* ---------------------------------- */
/* ------------ bealpht2 ------------ */
/* ---------------------------------- */

/* The device descriptor */
private gx_device_procs be_alpht2_procs = {
	be_open,
	gx_default_get_initial_matrix,
	be_sync,
	be_output_page,
	be_close,
	be_map_rgb_color,
	be_alpha_map_color_rgb, // be_map_color_rgb,
	be_fill_rectangle,
	NULL,			/* tile_rectangle */
	be_copy_mono,
	NULL,			/* copy_color */
	be_draw_line,
	NULL,			/* get_bits */
	NULL,			/* get_params */
	NULL,			/* put_params */
	NULL,			/* map_cmyk_color */
	NULL,			/* get_xfont_procs */
	NULL,			/* get_xfont_device */
	be_alpha_map_rgb_alpha_color,			/* map_rgb_alpha_color */
	gx_page_device_get_page_device,
	NULL, // be_alpha2_get_alpha_bits,			/* get_alpha_bits */
	be_alpha_copy_alpha,			/* copy_alpha */
	NULL,			/* get_band */
	NULL,			/* copy_rop */
	NULL,			/* fill_path */
	NULL,			/* stroke_path */
	NULL,			/* fill_mask */
	NULL,			/* fill_trapezoid */
	NULL,			/* fill_parallelogram */
	NULL,			/* fill_triangle */
	NULL,			/* draw_thin_line */
	NULL,			/* begin_image */
	NULL,			/* image_data */
	NULL,			/* end_image */
	NULL			/* be_strip_tile_rectangle */
};

/* The instance is public. */
gx_device_be gs_bealpht2_device = {
    std_device_dci_alpha_type_body(gx_device_be, &be_alpht2_procs,
	"bealpht2", NULL,
	  INITIAL_WIDTH, INITIAL_HEIGHT,
	  INITIAL_RESOLUTION, INITIAL_RESOLUTION,
	3, 32, 255, 255, 256, 256, 2, 1),
	{ 0 },			/* std_procs */
	-1,				/* mutex */
	NULL,			/* main bitmap */
	NULL,			/* offscreen view */
	NULL,			/* bmapcache ptr */
	0,				/* next bitmap to use */
};

/* ---------------------------------- */
/* ------------ bealpht4 ------------ */
/* ---------------------------------- */

/* The device descriptor */
private gx_device_procs be_alpht4_procs = {
	be_open,
	gx_default_get_initial_matrix,
	be_sync,
	be_output_page,
	be_close,
	be_map_rgb_color,
	be_alpha_map_color_rgb, // be_map_color_rgb,
	be_fill_rectangle,
	NULL,			/* tile_rectangle */
	be_copy_mono,
	NULL,			/* copy_color */
	be_draw_line,
	NULL,			/* get_bits */
	NULL,			/* get_params */
	NULL,			/* put_params */
	NULL,			/* map_cmyk_color */
	NULL,			/* get_xfont_procs */
	NULL,			/* get_xfont_device */
	be_alpha_map_rgb_alpha_color,			/* map_rgb_alpha_color */
	gx_page_device_get_page_device,
	NULL, 			/* get_alpha_bits */
	be_alpha_copy_alpha,			/* copy_alpha */
	NULL,			/* get_band */
	NULL,			/* copy_rop */
	NULL,			/* fill_path */
	NULL,			/* stroke_path */
	NULL,			/* fill_mask */
	NULL,			/* fill_trapezoid */
	NULL,			/* fill_parallelogram */
	NULL,			/* fill_triangle */
	NULL,			/* draw_thin_line */
	NULL,			/* begin_image */
	NULL,			/* image_data */
	NULL,			/* end_image */
	NULL			/* be_strip_tile_rectangle */
};

/* The instance is public. */
gx_device_be gs_bealpht4_device = {
    std_device_dci_alpha_type_body(gx_device_be, &be_alpht4_procs,
	"bealpht4", NULL,
	  INITIAL_WIDTH, INITIAL_HEIGHT,
	  INITIAL_RESOLUTION, INITIAL_RESOLUTION,
	3, 32, 255, 255, 256, 256, 4, 1),
	{ 0 },			/* std_procs */
	-1,				/* mutex */
	NULL,			/* main bitmap */
	NULL,			/* offscreen view */
	NULL,			/* bmapcache ptr */
	0,				/* next bitmap to use */
};

/* Device procedures */

private int
be_alpha_map_color_rgb(gx_device * dev, gx_color_index color,
		      gx_color_value prgb[3])
{
    union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.g = color;
	prgb[0] = colorcast.r.red << 8;
	prgb[1] = colorcast.r.green << 8;
	prgb[2] = colorcast.r.blue << 8;
	return 0;	
}

private gx_color_index
be_alpha_map_rgb_alpha_color(gx_device * dev,
 gx_color_value r, gx_color_value g, gx_color_value b, gx_color_value alpha)
{
    union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.r.red = r >> 8;
	colorcast.r.green = g >> 8;
	colorcast.r.blue = b >> 8;
	colorcast.r.alpha = alpha >> 8;
	return colorcast.g;
}

private int
be_alpha2_get_alpha_bits(gx_device * dev, graphics_object_type type)
{
    return 2;
}

private int
be_alpha4_get_alpha_bits(gx_device * dev, graphics_object_type type)
{
    return 4;
}

// alpha conversion table
static unsigned char 
	// 2 bit alpha -> 8 bit alpha (= 255/((1 << 2) - 1) * alpha)
	alpha2[4] = {0, 85, 170,255},
	// 4 bit alpha -> 8 bit alpha (= 255/((1 << 4) - 1) * alpha)
 	alpha4[16] = {0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255};

private int
be_alpha_copy_alpha(gx_device * dev, const unsigned char *base, int sourcex,
		   int raster, gx_bitmap_id id, int x, int y, int w, int h,
		   gx_color_index color, int depth)
{
    gx_device_be *bdev = (gx_device_be *)dev;
#ifdef PROFILE
    bigtime_t start = system_time();
#endif

    if(raster) {
	union {
		rgb_color r;
		gx_color_index g;
	} colorcast;
	colorcast.g = color;
	
	// We have to swap red and blue values, so we can use colorcast.r to write directly to memory
	unsigned char c = colorcast.r.red; colorcast.r.red = colorcast.r.blue; colorcast.r.blue = c;

	BBitmap *bitmap=0;
#ifdef BE_ALPHA_BITMAP_CACHE
	bmap_alpha_cache **ht_entry, *bmapc;
	
	// Remove bitmaps from cache if required
	if ((bdev->alpha_num_bmaps >= BE_ALPHA_MAX_BITMAPS) || (bdev->alpha_size >= BE_ALPHA_MAX_SIZE)) {
		bdev->m_view->Sync();
		// Remove temporary bitmaps first
		be_alpha_remove_all_tmp(bdev);
		// Do we need to remove more bitmaps?
		bmapc = bdev->alpha_mru_last;
		if (bmapc && 
			((bdev->alpha_num_bmaps >= BE_ALPHA_MAX_BITMAPS) || (bdev->alpha_size >= BE_ALPHA_MAX_SIZE))) {
			// Remove the least recently used bitmaps
			do {
				be_alpha_remove_bmap(bdev, bmapc->ht_entry, bmapc);
				delete bmapc;
				bmapc = bdev->alpha_mru_last;
			} while (bmapc && 
				((bdev->alpha_num_bmaps >= (BE_ALPHA_MAX_BITMAPS - BE_ALPHA_REMOVE_BITMAPS)) || 
				(bdev->alpha_size >= BE_ALPHA_MAX_SIZE)));
		}
	}
	if (id != gx_no_bitmap_id) {
		ht_entry = be_alpha_find_bmap(bdev, id, colorcast.r, &bmapc);
		if (bmapc != NULL) { // found
			bitmap = &(bmapc->bitmap);
			be_alpha_update_bmap(bdev, ht_entry, bmapc);
		}
	} else {
		ht_entry = NULL; // this bitmap is a temporary bitmap
	}
#else
  #if BE_ALPHA_CACHE_ENABLED
 	bmapcache_s *bmapc;
	if(id != gx_no_bitmap_id) {
		bmapcache_s *current = bdev->bmapcache;
		while(current) {
		    if(current->id == id) {
		    	bitmap = &(current->bitmap);
				break;
		    }
			current = current->next;
		}
	}
  #endif	
#endif 
	
	if(!bitmap) {
		// Create a bitmap in the color 'color'; take alpha channel from 'base'
#ifdef BE_ALPHA_BITMAP_CACHE
		bmapc = new bmap_alpha_cache_s(ht_entry, id, colorcast.r, w, h, B_RGBA32);
		bitmap = &(bmapc->bitmap); 
		if (ht_entry) {
			be_alpha_insert_bmap(bdev, ht_entry, bmapc);
		} else {
			be_alpha_add_tmp(bdev, bmapc);
		}
#else
  #if BE_ALPHA_CACHE_ENABLED
		bmapc = new bmapcache_s(id, BRect(0.0, 0.0, w-1, h-1), B_RGBA32);
		bitmap = &(bmapc->bitmap);
  #else
		bitmap = new BBitmap(BRect(0.0, 0.0, w-1, h-1), B_RGBA32);
  #endif
#endif
		if (depth == 2) { // 2 alpha bits per pixel  (4 alpha values per byte in base)
			unsigned char *bmp = (unsigned char*)bitmap->Bits();
			int32 bpr = bitmap->BytesPerRow();
			unsigned char *data = (unsigned char*)base;
			int32 w4 = w/4;
			int32 rest = w & 3;
			for (int32 line = h; line; line --) {
				unsigned char *src = data; data += raster;
				rgb_color *dst = (rgb_color*)bmp; bmp += bpr;
				for (int32 column = w4; column; column --) {
					colorcast.r.alpha = alpha2[(*src) >> 6];
					*dst = colorcast.r;
					dst++;
					
					colorcast.r.alpha = alpha2[((*src) >> 4) & 0x03];
					*dst = colorcast.r;
					dst++;
					
					colorcast.r.alpha = alpha2[((*src) >> 2) & 0x03];
					*dst = colorcast.r;
					dst++;
					
					colorcast.r.alpha = alpha2[(*src) & 0x03];
					*dst = colorcast.r;
					dst++; src++;
				}
				switch (rest) {
					case 1:
						colorcast.r.alpha = alpha2[(*src) >> 6];
						*dst = colorcast.r;
						break;
					case 2:
						colorcast.r.alpha = alpha2[(*src) >> 6];
						*dst = colorcast.r;
						dst++;
						
						colorcast.r.alpha = alpha2[((*src) >> 4) & 0x03];
						*dst = colorcast.r;
						break;
					case 3:
						colorcast.r.alpha = alpha2[(*src) >> 6];
						*dst = colorcast.r;
						dst++;
						
						colorcast.r.alpha = alpha2[((*src) >> 4) & 0x03];
						*dst = colorcast.r;
						dst++;
						
						colorcast.r.alpha = alpha2[((*src) >> 2) & 0x03];
						*dst = colorcast.r;
						break;
				}
			}
		} else if (depth == 4) { // 4 alpha bits per pixel (2 alpha values per byte in base)
			unsigned char *bmp = (unsigned char*)bitmap->Bits();
			int32 bpr = bitmap->BytesPerRow();
			unsigned char *data = (unsigned char*)base;
			int32 w2 = w/2;
			bool rest = w & 1;
			for (int32 line = h; line; line --) {
				unsigned char *src = data; data += raster;
				rgb_color *dst = (rgb_color*)bmp; bmp += bpr;
				for (int32 column = w2; column; column --) {
					colorcast.r.alpha = alpha4[(*src) >> 4];
					*dst = colorcast.r;
					dst++;
					colorcast.r.alpha = alpha4[(*src) & 0x0F];
					*dst = colorcast.r;
					src++; dst++;
				}
				if (rest) {
					colorcast.r.alpha = alpha4[(*src) >> 4];
					*dst = colorcast.r;
				}
			}
		} else { // this must not happen
		}
#ifdef BE_ALPHA_BITMAP_CACHE
#else
  #if BE_ALPHA_CACHE_ENABLED
		*(bdev->bmapcache_current) = bmapc;
		bdev->bmapcache_current = &(bmapc->next);
	} else { // we have the bitmap, but has it the same color?
		unsigned char *bmp = (unsigned char*)bitmap->Bits();
		bdev->m_view->Sync(); // this bitmap was draw asynchronosly before
		if ((((rgb_color*)bmp)->red != colorcast.r.red) ||
			(((rgb_color*)bmp)->green != colorcast.r.green) ||
			(((rgb_color*)bmp)->blue != colorcast.r.blue)) {
			// now re-color the bitmap
			int32 bpr = bitmap->BytesPerRow();
			for (int32 line = h; line; line --) {
				rgb_color *dst = (rgb_color*)bmp; bmp += bpr;
				for (int32 column = w; column; column --) {
					dst->red = colorcast.r.red;
					dst->green = colorcast.r.green;
					dst->blue = colorcast.r.blue;
					dst++;
				}
			}
		}
  #endif
#endif
	}

	bdev->m_view->SetDrawingMode(B_OP_ALPHA);
#ifdef BE_ALPHA_BITMAP_CACHE
	bdev->m_view->DrawBitmapAsync(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
#else
  #if BE_ALPHA_CACHE_ENABLED
	bdev->m_view->DrawBitmapAsync(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
  #else
	bdev->m_view->DrawBitmap(bitmap, BRect(sourcex, 0, sourcex+w-1, h-1), BRect(x, y, x+w-1, y+h-1));
	delete bitmap;
  #endif
#endif
    }
#ifdef PROFILE
	bdev->copyalpha += (int)(system_time() - start);
	bdev->bitmaps++; bdev->size += w * h;
#endif
    return 0;
}

