/**********************************************************************
 *
 * ibmcols.h - internal color definition
 *
 * NOTE: arch. independence header
 * for more information, please see the readme file
 *
 **********************************************************************/

#include "ibmcols.h"
#include "ibitmapm.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


/**********************************************************************
 * GLOBAL VARIABLES
 **********************************************************************/
long _ishift_endian = 0;
long _ishift_endmag = 0x11223344;

int _ishift_rgb_r_15 = 10;
int _ishift_rgb_g_15 = 5;
int _ishift_rgb_b_15 = 0;
int _ishift_rgb_r_16 = 11;
int _ishift_rgb_g_16 = 5;
int _ishift_rgb_b_16 = 0;
int _ishift_rgb_r_24 = 16;
int _ishift_rgb_g_24 = 8;
int _ishift_rgb_b_24 = 0;
int _ishift_rgb_r_32 = 16;
int _ishift_rgb_g_32 = 8;
int _ishift_rgb_b_32 = 0;
int _ishift_rgb_a_32 = 24;
int _ishift_rgb_r_4444 = 8;
int _ishift_rgb_g_4444 = 4;
int _ishift_rgb_b_4444 = 0;
int _ishift_rgb_a_4444 = 12;
int _ishift_rgb_r_1555 = 10;
int _ishift_rgb_g_1555 = 5;
int _ishift_rgb_b_1555 = 0;
int _ishift_rgb_a_1555 = 15;

int _ishift_data_b1 = 0;
int _ishift_data_b2 = 8;
int _ishift_data_b3 = 16;


/* lookup table for scaling 1 bit colors up to 8 bits */
int _iscale_rgb_1[2] = { 0, 255 };

/* lookup table for scaling 4 bit colors up to 8 bits */
int _iscale_rgb_4[16] = 
{
	0, 16, 32, 49, 65, 82, 98, 115, 
	139, 156, 172, 189, 205, 222, 238, 255
};

/* lookup table for scaling 5 bit colors up to 8 bits */
int _iscale_rgb_5[32] =
{
   0,   8,   16,  24,  32,  41,  49,  57,
   65,  74,  82,  90,  98,  106, 115, 123,
   131, 139, 148, 156, 164, 172, 180, 189,
   197, 205, 213, 222, 230, 238, 246, 255
};

/* lookup table for scaling 6 bit colors up to 8 bits */
int _iscale_rgb_6[64] =
{
   0,   4,   8,   12,  16,  20,  24,  28,
   32,  36,  40,  44,  48,  52,  56,  60,
   64,  68,  72,  76,  80,  85,  89,  93,
   97,  101, 105, 109, 113, 117, 121, 125,
   129, 133, 137, 141, 145, 149, 153, 157,
   161, 165, 170, 174, 178, 182, 186, 190,
   194, 198, 202, 206, 210, 214, 218, 222,
   226, 230, 234, 238, 242, 246, 250, 255
};

IRGB _ipaletted[256];
int _icolorconv = 0;

/* color converter 15/16 bits -> 32 bits lookup table */
ICOLORD _iconvert_rgb_16_32[1024];
ICOLORD _iconvert_rgb_15_32[1024];

#ifndef IDISABLE_CONVERT
/* color converter 15 bits <-> 16 bits lookup table */
ICOLORW _iconvert_rgb_15_16[1024];
ICOLORW _iconvert_rgb_16_15[1024];
ICOLORW _iconvert_rgb_15_15[1024];
ICOLORW _iconvert_rgb_16_16[1024];

/* color converter 1555 <-> 4444 <-> 8888 lookup table */
ICOLORW _iconvert_rgb_1555_4444[1024];
ICOLORW _iconvert_rgb_4444_1555[1024];
ICOLORD _iconvert_rgb_1555_8888[1024];
ICOLORD _iconvert_rgb_4444_8888[1024];
#endif

/* color difference lookup table */
long _ilookup_diff[512 * 3] = { 0 };

/* abs[n | n = (-255)->(+255)] = _ilookup_abs[256 + n] */
unsigned char _ilookup_abs[512] = { 0 };

#if defined(__BORLANDC__) && !defined(__MSDOS__)
#pragma warn -8004  
#pragma warn -8057
#endif


/**********************************************************************
 * GLOBAL FUNCTION
 **********************************************************************/
void _ishift_init(void)
{
	static long endian = 0x11223344;
	static long inited = 0;

	if (inited) return;	

	if (((unsigned char*)&endian)[0] == 0x44) {	
		/* for little endian */
		_ishift_data_b1 = 0;
		_ishift_data_b2 = 8;
		_ishift_data_b3 = 16;
		_ishift_endian = 0;
	}	else {
		/* for big endian */
		_ishift_data_b1 = 16;
		_ishift_data_b2 = 8;
		_ishift_data_b3 = 0;
		_ishift_endian = 1;
	}

	inited = 1;
}



/**********************************************************************
 * make raw-color value with rgb 
 **********************************************************************/
ICOLORD _im_color_raw(ICOLORV rgb, int bpp, int order, const IRGB *pal)
{
	unsigned long a, r, g, b, c;
	a = (rgb >> 24) & 0xff;
	r = (rgb >> 16) & 0xff;
	g = (rgb >>  8) & 0xff;
	b = (rgb >>  0) & 0xff;
	pal = (pal)? pal : _ipaletted;
	if (order && bpp != 8) c = r, r = b, b = c;
	if (bpp == 8) c = ((r & 0xE0) | ((g >> 3) & 0x1C) | (b >> 6));
	else if (bpp == 15) c = _imcolor15(r, g, b);
	else if (bpp == 16) c = _imcolor16(r, g, b);
	else if (bpp == 24) c = _imcolor24(r, g, b);
	else if (bpp == 32) c = _imcolora32(r, g, b, a);
	else c = 0;

	return c;
}


/**********************************************************************
 * make rgb-color value with raw 
 **********************************************************************/
ICOLORV _im_color_rgb(ICOLORD raw, int bpp, int order, const IRGB *pal)
{
	unsigned long a, r, g, b;

	pal = (pal)? pal : _ipaletted;

	if (bpp == 8) {
		r = _iscale_rgb_6[pal[raw].r];
		g = _iscale_rgb_6[pal[raw].g];
		b = _iscale_rgb_6[pal[raw].b];
		a = (raw == 0)? 0 : 255;
	}	
	else if (bpp == 15) {
		r = (order == 0)? _im_getr15(raw) : _im_getr15i(raw);
		g = (order == 0)? _im_getg15(raw) : _im_getg15i(raw);
		b = (order == 0)? _im_getb15(raw) : _im_getb15i(raw);
		a = 255;
	}
	else if (bpp == 16) {
		r = (order == 0)? _im_getr16(raw) : _im_getr16i(raw);
		g = (order == 0)? _im_getg16(raw) : _im_getg16i(raw);
		b = (order == 0)? _im_getb16(raw) : _im_getb16i(raw);
		a = 255;
	}
	else if (bpp == 24) {
		r = (order == 0)? _im_getr24(raw) : _im_getr24i(raw);
		g = (order == 0)? _im_getg24(raw) : _im_getg24i(raw);
		b = (order == 0)? _im_getb24(raw) : _im_getb24i(raw);
		a = 255;
	}
	else if (bpp == 32) {
		r = (order == 0)? _im_getr32(raw) : _im_getr32i(raw);
		g = (order == 0)? _im_getg32(raw) : _im_getg32i(raw);
		b = (order == 0)? _im_getb32(raw) : _im_getb32i(raw);
		a = (order == 0)? _im_geta32(raw) : _im_geta32i(raw);
	}	else {
		r = g = b = a = 0;
	}

	return IRGBA(r, g, b, a);
}


/**********************************************************************
 * COLOR DIFF TABLE
 * table1: _ilookup_diff[i | i = 256->511, n=0->(+255)] = (n * 30) ^ 2
 * table2: _ilookup_diff[i | i = 256->1,   n=0->(-255)] = (n * 30) ^ 2
 * result: f(n) = (n * 30) ^ 2 = _ilookup_diff[256 + n]
 **********************************************************************/
void _imake_color_diff(void)
{
	int i, k;
	if (_ilookup_diff[0] != 0) return;
	for (i = 0; i < 256; i++) {
		k = i * i;
		_ilookup_diff[ 256 + i] = _ilookup_diff[ 256 - i] = k * 30 * 30;
		_ilookup_diff[ 768 + i] = _ilookup_diff[ 768 - i] = k * 59 * 59;
		_ilookup_diff[1280 + i] = _ilookup_diff[1280 - i] = k * 11 * 11;
	}
	for (i = 0; i < 256; i++) {
		_ilookup_abs[256 + i] = _ilookup_abs[256 - i] = (unsigned char)i;
	}
	_ilookup_abs[0] = 1;
	_ilookup_diff[0] = 1;
}

/**********************************************************************
 * 256 PALETTE INTERFACE
 **********************************************************************/
int _ibestfit_color(IRGB *pal, int r, int g, int b, int palsize)
{ 
	long lowest = 0x7FFFFFFF, bestfit = 0, coldiff, i;
	IRGB *rgb;

	/* calculate color difference lookup table */
	if (_ilookup_diff[0] == 0) 
		_imake_color_diff();

	/* range correction */
	r = r & 255;
	g = g & 255;
	b = b & 255;

	/*
	 * vector:   c1 = [r1, g1, b1], c2 = [r2, g2, b2]
	 * distance: dc = length(c1 - c2)
	 * coldiff:  dc^2 = (r1 - r2)^2 + (g1 - g2)^2 + (b1 - b2)^2
	 */
	for (i = palsize, rgb = pal; i > 0; rgb++, i--) {
		coldiff  = _ilookup_diff[ 768 + rgb->g - g];
		if (coldiff >= lowest) continue;

		coldiff += _ilookup_diff[ 256 + rgb->r - r];
		if (coldiff >= lowest) continue;

		coldiff += _ilookup_diff[1280 + rgb->b - b];
		if (coldiff >= lowest) continue;

		bestfit = rgb - pal; /* faster than `bestfit = i;' */
		if (coldiff == 0) return bestfit;
		lowest = coldiff;
	}

	return bestfit;
} 

int _igenerate_332_palette(IRGB *pal)
{
	unsigned long c;

	pal = (pal)? pal : _ipaletted;
	for (c = 0; c < 256; c++) {
		pal[c].r = (unsigned char)(((c >> 5) & 7) * 255 / 7);
		pal[c].g = (unsigned char)(((c >> 2) & 7) * 255 / 7);
		pal[c].b = (unsigned char)((c & 3) * 255 / 3);
	}

	pal[0].r = 255;
	pal[0].g = 0;
	pal[0].b = 255;

	pal[255].r = pal[255].g = pal[255].b = 0;

	return 0;
}


/**********************************************************************
 * make rgb-color value with raw 
 **********************************************************************/
void _igenerate_convert_table(void)
{
	ICOLORD c1, c2, r1, g1, b1, r2, g2, b2;
	unsigned long checkendian = 0x11223344;
	int be, i;

	#define ICONVERT_SAVE(sbits, dbits, dtype) do { \
		_im_scale_rgb(c1, sbits, r1, g1, b1); \
		_im_scale_rgb(c2, sbits, r2, g2, b2); \
		_iconvert_rgb_##sbits##_##dbits[i] = (dtype) \
			_im_pack_c##dbits(r1, g1, b1); \
		_iconvert_rgb_##sbits##_##dbits[i + 256] = (dtype) \
			_im_pack_c##dbits(r2, g2, b2); \
		_iconvert_rgb_##sbits##_##dbits[i + 512] = (dtype) \
			_im_pack_c##dbits(b1, g1, r1); \
		_iconvert_rgb_##sbits##_##dbits[i + 768] = (dtype) \
			_im_pack_c##dbits(b2, g2, r2); \
	}	while (0)

	be = (*(unsigned char*)(&checkendian) == 0x44)? 0 : 1;
	for (i = 0; i < 256; i++) {
		if (be == 0) c1 = (ICOLORD)i, c2 = (ICOLORD)(i << 8);
		else c2 = (ICOLORD)i, c1 = (ICOLORD)(i << 8);
		ICONVERT_SAVE(15, 32, ICOLORD);
		ICONVERT_SAVE(16, 32, ICOLORD);
		#ifndef IDISABLE_CONVERT
		ICONVERT_SAVE(15, 16, ICOLORW);
		ICONVERT_SAVE(16, 15, ICOLORW);
		ICONVERT_SAVE(15, 15, ICOLORW);
		ICONVERT_SAVE(16, 16, ICOLORW);
		#endif
	}

	#undef ICONVERT_SAVE

	#ifndef IDISABLE_CONVERT
	#define ICONVERT_SAVE(sbits, dbits, dtype) do { \
		_im_scale_argb(c1, sbits, a1, r1, g1, b1); \
		_im_scale_argb(c2, sbits, a2, r2, g2, b2); \
		_iconvert_rgb_##sbits##_##dbits[i] = (dtype) \
			_im_pack_a##dbits(a1, r1, g1, b1); \
		_iconvert_rgb_##sbits##_##dbits[i + 256] = (dtype) \
			_im_pack_a##dbits(a2, r2, g2, b2); \
		_iconvert_rgb_##sbits##_##dbits[i + 512] = (dtype) \
			_im_pack_a##dbits(a1, b1, g1, r1); \
		_iconvert_rgb_##sbits##_##dbits[i + 768] = (dtype) \
			_im_pack_a##dbits(a2, b2, g2, r2); \
	}	while (0)

	for (i = 0; i < 256; i++) {
		ICOLORD a1, a2;
		if (be == 0) c1 = (ICOLORD)i, c2 = (ICOLORD)(i << 8);
		else c2 = (ICOLORD)i, c1 = (ICOLORD)(i << 8);
		ICONVERT_SAVE(1555, 4444, ICOLORW);
		ICONVERT_SAVE(4444, 1555, ICOLORW);
		ICONVERT_SAVE(1555, 8888, ICOLORD);
		ICONVERT_SAVE(4444, 8888, ICOLORD);
	}
	#undef ICONVERT_SAVE
	#endif
}


/**********************************************************************
 * _ireplacement_mask
 **********************************************************************/
static unsigned long _ireplacement_mask(IBITMAP *bmp, IRGB *pal)
{
	unsigned long mask;
	mask = bmp->mask;
	pal = pal;
	return mask;
}

/**********************************************************************
 * _iconvert_blit
 **********************************************************************/
#define _ICONVERT_BLIT_EX(dbits, dsize, sbits, ssize, MAKECOL) \
{	\
	unsigned long sdelta = ssize * s_x;		\
	unsigned long ddelta = dsize * d_x;		\
	unsigned char *s, *d;	\
	unsigned long smask, c;	\
	long incs = ssize; \
	int x, y, k, v, r, g, b;	\
	IRGB *_ipaletted = pal;		\
	if (flags & ICONV_VFLIP) { \
		sdelta = ssize * (s_x + w - 1); \
		incs = -ssize; \
		_ipaletted = NULL; \
	} \
	if (flags & ICONV_HFLIP) k = h - 1, v = -1; \
	else k = 0, v = 1; \
	if (_ipaletted != pal) _ipaletted = pal; \
	if (flags & ICONV_MASK) { \
		smask = src->mask;		\
		for (y = 0; y < h; y++, k += v) { \
			s = _ilineptr(src, s_y + k) + sdelta;	\
			d = _ilineptr(dst, d_y + y) + ddelta;	\
			if ((flags & ICONV_RGB2BGR) == 0) { \
				for (x = w; x > 0; x--) {	\
					c = _im_get##ssize##b(s);	\
					if (c != smask) {	\
						r = _im_getr##sbits(c);	\
						g = _im_getg##sbits(c);	\
						b = _im_getb##sbits(c);	\
						c = MAKECOL;		\
						_im_put##dsize##b(d, c);	\
					}	\
					s += incs;		\
					d += dsize;		\
				} \
			}	else {	\
				for (x = w; x > 0; x--) {	\
					c = _im_get##ssize##b(s);	\
					if (c != smask) {	\
						b = _im_getr##sbits(c);	\
						g = _im_getg##sbits(c);	\
						r = _im_getb##sbits(c);	\
						c = MAKECOL;		\
						_im_put##dsize##b(d, c);	\
					}	\
					s += incs;		\
					d += dsize;		\
				} \
			}	\
		}	\
	}	else {		\
		for (y = 0; y < h; y++, k += v) { \
			s = _ilineptr(src, s_y + k) + sdelta;	\
			d = _ilineptr(dst, d_y + y) + ddelta;	\
			if ((flags & ICONV_RGB2BGR) == 0) { \
				for (x = w; x > 0; x--) {	\
					c = _im_get##ssize##b(s);	\
					r = _im_getr##sbits(c);	\
					g = _im_getg##sbits(c);	\
					b = _im_getb##sbits(c);	\
					c = MAKECOL;		\
					_im_put##dsize##b(d, c);	\
					s += incs;		\
					d += dsize;		\
				}	\
			}	else { \
				for (x = w; x > 0; x--) {	\
					c = _im_get##ssize##b(s);	\
					b = _im_getr##sbits(c);	\
					g = _im_getg##sbits(c);	\
					r = _im_getb##sbits(c);	\
					c = MAKECOL;		\
					_im_put##dsize##b(d, c);	\
					s += incs;		\
					d += dsize;		\
				}	\
			}	\
		}	\
	}	\
}

#define _ICONVERT_BLIT(dbits, dsize, sbits, ssize) \
  _ICONVERT_BLIT_EX(dbits, dsize, sbits, ssize, _imcolor##dbits(r, g, b))


#define _ICONVERT_BLIT_TO_8(sbits, ssize)  \
  _ICONVERT_BLIT_EX(8, 1, sbits, ssize, _ibestfit_color(pal, r, g, b, 256))


#define _ICONVERT_DITHER_BLIT(dbits, dsize, sbits, ssize)	\
	_ICONVERT_BLIT_EX(dbits, dsize, sbits, ssize, _imcolor##dbits(r, g, b))


static void _iconvert_blit_to_15(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int flags)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) 
	{
	case  8: _ICONVERT_BLIT(15, 2,  8, 1); break;
	case 15: _ICONVERT_BLIT(15, 2, 15, 2); break;
	case 16: _ICONVERT_BLIT(15, 2, 16, 2); break;
	case 24: _ICONVERT_BLIT(15, 2, 24, 3); break;
	case 32: _ICONVERT_BLIT(15, 2, 32, 4); break;
	}
}

static void _iconvert_blit_to_16(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int flags)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) 
	{
	case  8: _ICONVERT_BLIT(16, 2,  8, 1); break;
	case 15: _ICONVERT_BLIT(16, 2, 15, 2); break;
	case 16: _ICONVERT_BLIT(16, 2, 16, 2); break;
	case 24: _ICONVERT_BLIT(16, 2, 24, 3); break;
	case 32: _ICONVERT_BLIT(16, 2, 32, 4); break;
	}
}

static void _iconvert_blit_to_24(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int flags)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) 
	{
	case  8: _ICONVERT_BLIT(24, 3,  8, 1); break;
	case 15: _ICONVERT_BLIT(24, 3, 15, 2); break;
	case 16: _ICONVERT_BLIT(24, 3, 16, 2); break;
	case 24: _ICONVERT_BLIT(24, 3, 24, 3); break;
	case 32: _ICONVERT_BLIT(24, 3, 32, 4); break;
	}
}

static void _iconvert_blit_to_32(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int flags)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) 
	{
	case  8: _ICONVERT_BLIT(32, 4,  8, 1); break;
	case 15: _ICONVERT_BLIT(32, 4, 15, 2); break;
	case 16: _ICONVERT_BLIT(32, 4, 16, 2); break;
	case 24: _ICONVERT_BLIT(32, 4, 24, 3); break;
	case 32: _ICONVERT_BLIT(32, 4, 32, 4); break;
	}
}

static void _iconvert_blit_to_8(IBITMAP *dst, int d_x, int d_y,
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int flags)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp)
	{
	case 15: _ICONVERT_BLIT_TO_8(15, 2); break;
	case 16: _ICONVERT_BLIT_TO_8(16, 2); break;
	case 24: _ICONVERT_BLIT_TO_8(24, 3); break;
	case 32: _ICONVERT_BLIT_TO_8(32, 4); break;
	}
}


/**********************************************************************
 * _iconvert_blit
 **********************************************************************/
void _iconvert_blit(IBITMAP *dst, int dx, int dy, IBITMAP *src,
	int sx, int sy, int w, int h, IRGB *pal, int flags)
{
    int m = sx + w;
    int n = sy + h;
	int r;

	assert(dst && src);
	if (flags & ICONV_CLIP) {
		r = ibitmap_clip(dst->w, dst->h, src->w, src->h, 
					&dx, &dy, &sx, &sy, &m, &n);
		if (r) return;
	}

	w = m - sx;
	h = n - sy;

	if (flags & ICONV_KEEPTRANS) {
		flags |= ICONV_MASK;
		ibitmap_fill(dst, dx, dy, w, h, dst->mask, 0);
	}

	if (dst->bpp == src->bpp && (flags & ICONV_RGB2BGR) == 0) {
		ibitmap_blit(dst, dx, dy, src, sx, sy, w, h, src->mask, 
			flags & (IBLIT_MASK | IBLIT_HFLIP | IBLIT_VFLIP));
		return;
	}

	switch (dst->bpp)
	{
	case 8:
		_iconvert_blit_to_8(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		break;
	case 15:
		_iconvert_blit_to_15(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		break;
	case 16:
		_iconvert_blit_to_16(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		break;
	case 24:
		_iconvert_blit_to_24(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		break;
	case 32:
		_iconvert_blit_to_32(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		break;
	}
}



#ifndef IDISABLE_CONVERT
/**********************************************************************
 * _ICONVERT_BLIT_APPROXIMATE
 **********************************************************************/
#define _ICONVERT_BLIT_APPROXIMATE(dbits, dsize, sbits, ssize) \
{	\
	unsigned long sdelta = ssize * s_x;		\
	unsigned long ddelta = dsize * d_x;		\
	unsigned char *s, *d;	\
	unsigned long c1, r1, g1, b1;	\
	unsigned long c2, r2, g2, b2; \
	unsigned long smask = src->mask; \
	long y;	\
	IRGB *_ipaletted = pal;		\
	if (ismask == 0) { \
		if ((inverse) == 0) { \
			for (y = 0; y < h; y++) { \
				s = _ilineptr(src, s_y + y) + sdelta;	\
				d = _ilineptr(dst, d_y + y) + ddelta;	\
				ILINS_LOOP_DOUBLE( \
		_iconvert_copy_increment(dbits, dsize, sbits, ssize, rgb), \
		_iconvert_double_copy_increment(dbits, dsize, sbits, ssize, rgb), \
				w); \
			}	\
		}	else { \
			for (y = 0; y < h; y++) { \
				s = _ilineptr(src, s_y + y) + sdelta;	\
				d = _ilineptr(dst, d_y + y) + ddelta;	\
				ILINS_LOOP_DOUBLE( \
		_iconvert_copy_increment(dbits, dsize, sbits, ssize, bgr), \
		_iconvert_double_copy_increment(dbits, dsize, sbits, ssize, bgr), \
				w); \
			}	\
		} \
	}	else { \
		for (y = 0; y < h; y++) { \
			s = _ilineptr(src, s_y + y) + sdelta;	\
			d = _ilineptr(dst, d_y + y) + ddelta;	\
			if ((inverse) == 0) { \
				ILINS_LOOP_ONCE(_iconvert_copy_mask_increment(dbits, \
				dsize, sbits, ssize, rgb), w); \
			}	else { \
				ILINS_LOOP_ONCE(_iconvert_copy_mask_increment(dbits, \
				dsize, sbits, ssize, bgr), w); \
			} \
		}	\
	}	\
	pal = _ipaletted; \
}

#define _iconvert_copy_increment(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	_im_unpack_rgb(c1, sbits, r1, g1, b1); \
	c1 = _im_pack_##mode(dbits, r1, g1, b1); \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
}

#define _iconvert_double_copy_increment(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	c2 = _im_get##ssize##b(s); \
	s += ssize; \
	_im_unpack_rgb(c1, sbits, r1, g1, b1); \
	_im_unpack_rgb(c2, sbits, r2, g2, b2); \
	c1 = _im_pack_##mode(dbits, r1, g1, b1); \
	c2 = _im_pack_##mode(dbits, r2, g2, b2); \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
	_im_put##dsize##b(d, c2); \
	d += dsize; \
}

#define _iconvert_copy_mask_increment(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	if (c1 != smask) { \
		_im_unpack_rgb(c1, sbits, r1, g1, b1); \
		c1 = _im_pack_##mode(dbits, r1, g1, b1); \
		_im_put##dsize##b(d, c1); \
	} \
	d += dsize; \
}


static void _iconvert_approxy_to_15(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse,
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) {
	case 15: _ICONVERT_BLIT_APPROXIMATE(15, 2, 15, 2); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(15, 2, 16, 2); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(15, 2, 24, 3); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(15, 2, 32, 4); break;
	}
}

static void _iconvert_approxy_to_16(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse,
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) {
	case 15: _ICONVERT_BLIT_APPROXIMATE(16, 2, 15, 2); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(16, 2, 16, 2); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(16, 2, 24, 3); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(16, 2, 32, 4); break;
	}
}

static void _iconvert_approxy_to_24(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse,
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) {
	case 15: _ICONVERT_BLIT_APPROXIMATE(24, 3, 15, 2); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(24, 3, 16, 2); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(24, 3, 24, 3); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(24, 3, 32, 4); break;
	}
}

static void _iconvert_approxy_to_32(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse, 
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) {
	case 15: _ICONVERT_BLIT_APPROXIMATE(32, 4, 15, 2); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(32, 4, 16, 2); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(32, 4, 24, 3); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(32, 4, 32, 4); break;
	}
}

static void _iconvert_approxy_to_8(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse,
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (src->bpp) {
	case  8: _ICONVERT_BLIT_APPROXIMATE(8, 1, 8, 1); break;
	case 15: _ICONVERT_BLIT_APPROXIMATE(8, 1, 15, 2); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(8, 1, 16, 2); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(8, 1, 24, 3); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(8, 1, 32, 4); break;
	}
}

static void _iconvert_approxy_from_8(IBITMAP *dst, int d_x, int d_y, 
	IBITMAP *src, int s_x, int s_y, int w, int h, IRGB *pal, int inverse,
	int ismask)
{
	pal = pal? pal : _ipaletted;
	switch (dst->bpp) {
	case  8: _ICONVERT_BLIT_APPROXIMATE(8, 1, 8, 1); break;
	case 15: _ICONVERT_BLIT_APPROXIMATE(15, 1, 8, 1); break;
	case 16: _ICONVERT_BLIT_APPROXIMATE(16, 1, 8, 1); break;
	case 24: _ICONVERT_BLIT_APPROXIMATE(24, 1, 8, 1); break;
	case 32: _ICONVERT_BLIT_APPROXIMATE(32, 1, 8, 1); break;
	}
}

#undef _ICONVERT_BLIT_APPROXIMATE

/**********************************************************************
 * _iconvert_approxy
 **********************************************************************/
static void _iconvert_approxy(IBITMAP *dst, int dx, int dy, IBITMAP *src,
	int sx, int sy, int w, int h, IRGB *pal, int inverse, int ismask)
{
    int m = sx + w;
    int n = sy + h;
	int r;

	assert(dst && src);
	r = ibitmap_clip(dst->w, dst->h, src->w, src->h, 
						&dx, &dy, &sx, &sy, &m, &n);
	if (r) return;
	w = m - sx;
	h = n - sy;

	if (dst->bpp == src->bpp && inverse == 0) {
		ibitmap_blit(dst, dx, dy, src, sx, sy, w, h, 0, 0);
		return;
	}
	if (src->bpp == 8) {
		_iconvert_approxy_from_8(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		return;
	}
	switch (dst->bpp)
	{
	case 8:
		_iconvert_approxy_to_8(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		break;
	case 15:
		_iconvert_approxy_to_15(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		break;
	case 16:
		_iconvert_approxy_to_16(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		break;
	case 24:
		_iconvert_approxy_to_24(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		break;
	case 32:
		_iconvert_approxy_to_32(dst, dx, dy, src, sx, sy, w, h, 
			pal, inverse, ismask);
		break;
	}
}


#endif /* #ifndef IDISABLE_CONVERT */


/**********************************************************************
 * _ICONVERT_BLIT_2BYTES_X
 **********************************************************************/
#define _ICONVERT_BLIT_2BYTES_X(sbits, dbits, dsize, inverse) do { \
	unsigned long sdelta = 2 * sx; \
	unsigned long ddelta = dsize * dx; \
	unsigned long c1, c2, c3, c4; \
	unsigned char *s, *d; \
	int y; \
	if (inverse == 0) { \
		for (y = 0; y < h; y++) { \
			s = _ilineptr(src, sy + y) + sdelta;	\
			d = _ilineptr(dst, dy + y) + ddelta;	\
			ILINS_LOOP_DOUBLE( \
			__iconvert_scale_copy(sbits, dbits, dsize, 0, 256), \
			__iconvert_scale_copy_x2(sbits, dbits, dsize, 0, 256), w); \
		} \
	}	else { \
		for (y = 0; y < h; y++) { \
			s = _ilineptr(src, sy + y) + sdelta;	\
			d = _ilineptr(dst, dy + y) + ddelta;	\
			ILINS_LOOP_DOUBLE( \
			__iconvert_scale_copy(sbits, dbits, dsize, 512, 768), \
			__iconvert_scale_copy_x2(sbits, dbits, dsize, 512, 768), w); \
		} \
	} \
}	while (0)

#define __iconvert_scale_copy_x2(sbits, dbits, dsize, off1, off2) { \
	c1 = _iconvert_rgb_##sbits##_##dbits[*s++ + off1]; \
	c2 = _iconvert_rgb_##sbits##_##dbits[*s++ + off2]; \
	c3 = _iconvert_rgb_##sbits##_##dbits[*s++ + off1]; \
	c4 = _iconvert_rgb_##sbits##_##dbits[*s++ + off2]; \
	c1 |= c2; \
	c3 |= c4; \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
	_im_put##dsize##b(d, c3); \
	d += dsize; \
}

#define __iconvert_scale_copy(sbits, dbits, dsize, off1, off2) { \
	c1 = _iconvert_rgb_##sbits##_##dbits[*s++ + off1]; \
	c2 = _iconvert_rgb_##sbits##_##dbits[*s++ + off2]; \
	c1 |= c2; \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
}

#ifndef IDISABLE_CONVERT

static void _iconvert_blit_scale(IBITMAP *dst, int dx, int dy, IBITMAP 
		*src, int sx, int sy, int w, int h, IRGB *pal, int IsBGR)
{
	static ICOLORD *_iconvert_rgb_15_24 = _iconvert_rgb_15_32;
	static ICOLORD *_iconvert_rgb_16_24 = _iconvert_rgb_16_32;
	static int inited = 0;
    int m = sx + w;
	int n = sy + h;
	int r;

	assert(dst && src);
	r = ibitmap_clip(dst->w, dst->h, src->w, src->h, 
						&dx, &dy, &sx, &sy, &m, &n);
	if (r) return;
	w = m - sx;
	h = n - sy;

	if (dst->bpp == src->bpp || dst->bpp == 8) r++;
	if (src->bpp != 15 && src->bpp != 16) r++;
	
	if (r) {
		_iconvert_approxy(dst, dx, dy, src, sx, sy, w, h, pal, IsBGR, 0);
		return;
	}

	if (inited == 0) {
		_igenerate_convert_table();
		inited++;
	}

	if (src->bpp == 15) {
		switch (dst->bpp) {
		case 15: _ICONVERT_BLIT_2BYTES_X(15, 15, 2, IsBGR); break;
		case 16: _ICONVERT_BLIT_2BYTES_X(15, 16, 2, IsBGR); break;
		case 24: _ICONVERT_BLIT_2BYTES_X(15, 24, 3, IsBGR); break;
		case 32: _ICONVERT_BLIT_2BYTES_X(15, 32, 4, IsBGR); break;
		} 
	}	else 
	if (src->bpp == 16) {
		switch (dst->bpp) {
		case 15: _ICONVERT_BLIT_2BYTES_X(16, 15, 2, IsBGR); break;
		case 16: _ICONVERT_BLIT_2BYTES_X(16, 16, 2, IsBGR); break;
		case 24: _ICONVERT_BLIT_2BYTES_X(16, 24, 3, IsBGR); break;
		case 32: _ICONVERT_BLIT_2BYTES_X(16, 32, 4, IsBGR); break;
		} 
	}
}


/**
 * _iconvert_fast:
 *
 * color format converting routine, supports endian indepedance, 
 * mask color converting, lookup-table color scaling and color order
 * swapping. EACH converting is compiled into DIFFERENT-C-FUNCTION
 * to get the high performance.
 * 
 * parameters:
 * dst, dx, dy - destination bitmap and position
 * src, sx, sy - source bitmap and position
 * w, h        - size
 * pal         - palette (can be used when srcbpp/dstbpp is 8 bits
 * flags       - enable ICONV_RGB2BGR or ICONV_SHIFT or ICONV_MASK
*/

void _iconvert_fast(IBITMAP *dst, int dx, int dy, IBITMAP *src,
	int sx, int sy, int w, int h, IRGB *pal, int flags)
{
	int IsBGR = (flags & ICONV_RGB2BGR)? 1 : 0;
	int IsMask = (flags & ICONV_MASK)? 1 : 0;

	if ((src->bpp == 15 || src->bpp == 16) && dst->bpp != 8) {
		if ((flags & (ICONV_SHIFT | ICONV_MASK)) == 0) {
			_iconvert_blit_scale(dst, dx, dy, src, sx, sy, w, h,
				pal, IsBGR);
			return;
		}
	}

	_iconvert_approxy(dst, dx, dy, src, sx, sy, w, h, pal, IsBGR, IsMask);
}


#endif


#ifndef IDISABLE_CONVERT
/**********************************************************************
 * _ICONVERT_BLIT_ARGB
 **********************************************************************/
#define _ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, mode) \
{	\
	unsigned long sdelta = ssize * s_x;		\
	unsigned long ddelta = dsize * d_x;		\
	unsigned char *s, *d;	\
	unsigned long c1, a1, r1, g1, b1; \
	unsigned long c2, a2, r2, g2, b2; \
	unsigned long smask;	\
	long y;	\
	IRGB *_ipaletted = pal;		\
	if ((ismask) == 0) { \
		for (y = 0; y < h; y++) { \
			s = _ilineptr(src, s_y + y) + sdelta;	\
			d = _ilineptr(dst, d_y + y) + ddelta;	\
			ILINS_LOOP_DOUBLE( \
		_iconvert_copy_argb(dbits, dsize, sbits, ssize, mode), \
		_iconvert_double_copy_argb(dbits, dsize, sbits, ssize, mode), \
			w); \
		}	\
	}	else { \
		smask = src->mask;		\
		for (y = 0; y < h; y++) { \
			s = _ilineptr(src, s_y + y) + sdelta;	\
			d = _ilineptr(dst, d_y + y) + ddelta;	\
			ILINS_LOOP_DOUBLE( \
		_iconvert_copy_argb_mask(dbits, dsize, sbits, ssize, mode), \
		_iconvert_double_copy_argb_mask(dbits, dsize, sbits, ssize, mode), \
			w); \
		}	\
	} \
	pal = _ipaletted; \
}

#define _iconvert_copy_argb(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	_im_unpack_argb(c1, sbits, a1, r1, g1, b1); \
	c1 = _im_pack_##mode(dbits, a1, r1, g1, b1); \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
}

#define _iconvert_double_copy_argb(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	c2 = _im_get##ssize##b(s); \
	s += ssize; \
	_im_unpack_argb(c1, sbits, a1, r1, g1, b1); \
	_im_unpack_argb(c2, sbits, a2, r2, g2, b2); \
	c1 = _im_pack_##mode(dbits, a1, r1, g1, b1); \
	c2 = _im_pack_##mode(dbits, a2, r2, g2, b2); \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
	_im_put##dsize##b(d, c2); \
	d += dsize; \
}


#define _iconvert_copy_argb_mask(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	_im_unpack_rgb(c1, sbits, r1, g1, b1); \
	a1 = (c1 != smask)? 255 : 0; \
	c1 = _im_pack_##mode(dbits, a1, r1, g1, b1); \
	_im_put##dsize##b(d, c1); \
	s += ssize; \
	d += dsize; \
}

#define _iconvert_double_copy_argb_mask(dbits, dsize, sbits, ssize, mode) { \
	c1 = _im_get##ssize##b(s); \
	s += ssize; \
	c2 = _im_get##ssize##b(s); \
	s += ssize; \
	_im_unpack_rgb(c1, sbits, r1, g1, b1); \
	_im_unpack_rgb(c2, sbits, r2, g2, b2); \
	a1 = (c1 != smask)? 255 : 0; \
	a2 = (c2 != smask)? 255 : 0; \
	c1 = _im_pack_##mode(dbits, a1, r1, g1, b1); \
	c2 = _im_pack_##mode(dbits, a2, r2, g2, b2); \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
	_im_put##dsize##b(d, c2); \
	d += dsize; \
}

#define _ICONVERT_BLIT_ARGB(dbits, dsize, sbits, ssize, order) {\
	if (order == 4123) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, argb); \
	}	else \
	if (order == 4321) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, abgr); \
	}	\
}

#define _ICONVERT_BLIT_ARGB2(dbits, dsize, sbits, ssize, order) {\
	if (order == 4123) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, argb); \
	}	else \
	if (order == 4321) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, abgr); \
	}	else \
	if (order == 1234) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, rgba); \
	}	else \
	if (order == 3214) { \
		_ICONVERT_BLIT_ARGB_EX(dbits, dsize, sbits, ssize, bgra); \
	}	\
}

static void _iconvert_argb_to_8888(IBITMAP *dst, int d_x, int d_y, IBITMAP 
	*src, int s_x, int s_y, int w, int h, IRGB *pal, int ismask, int inverse)
{
	pal = pal? pal : _ipaletted;
	switch (_ibitmap_order(src)) {
	case 1555: _ICONVERT_BLIT_ARGB2(8888, 4, 1555, 2, inverse); break;
	case  555: _ICONVERT_BLIT_ARGB2(8888, 4,  555, 2, inverse); break;
	case 4444: _ICONVERT_BLIT_ARGB2(8888, 4, 4444, 2, inverse); break;
	case  565: _ICONVERT_BLIT_ARGB2(8888, 4,  565, 2, inverse); break;
	case  888: _ICONVERT_BLIT_ARGB2(8888, 4,  888, 3, inverse); break;
	}
}

static void _iconvert_argb_to_1555(IBITMAP *dst, int d_x, int d_y, IBITMAP 
	*src, int s_x, int s_y, int w, int h, IRGB *pal, int ismask, int inverse)
{
	pal = pal? pal : _ipaletted;
	switch (_ibitmap_order(src)) {
	case  555: _ICONVERT_BLIT_ARGB(1555, 2,  555, 2, inverse); break;
	case 4444: _ICONVERT_BLIT_ARGB(1555, 2, 4444, 2, inverse); break;
	case  565: _ICONVERT_BLIT_ARGB(1555, 2,  565, 2, inverse); break;
	case  888: _ICONVERT_BLIT_ARGB(1555, 2,  888, 3, inverse); break;
	case 8888: _ICONVERT_BLIT_ARGB(1555, 2, 8888, 4, inverse); break;
	}
}

static void _iconvert_argb_to_4444(IBITMAP *dst, int d_x, int d_y, IBITMAP 
	*src, int s_x, int s_y, int w, int h, IRGB *pal, int ismask, int inverse)
{
	pal = pal? pal : _ipaletted;
	switch (_ibitmap_order(src)) {
	case 1555: _ICONVERT_BLIT_ARGB(4444, 2, 1555, 2, inverse); break;
	case  555: _ICONVERT_BLIT_ARGB(4444, 2,  555, 2, inverse); break;
	case  565: _ICONVERT_BLIT_ARGB(4444, 2,  565, 2, inverse); break;
	case  888: _ICONVERT_BLIT_ARGB(4444, 2,  888, 3, inverse); break;
	case 8888: _ICONVERT_BLIT_ARGB(4444, 2, 8888, 4, inverse); break;
	}
}

static void _iconvert_argb_from_8(IBITMAP *dst, int d_x, int d_y, IBITMAP 
	*src, int s_x, int s_y, int w, int h, IRGB *pal, int ismask, int inverse)
{
	pal = pal? pal : _ipaletted;
	switch (_ibitmap_order(dst)) {
	case 1555: _ICONVERT_BLIT_ARGB(1555, 2, 8, 1, inverse); break;
	case 4444: _ICONVERT_BLIT_ARGB(4444, 2, 8, 1, inverse); break;
	case 8888: _ICONVERT_BLIT_ARGB(8888, 4, 8, 1, inverse); break;
	}
}

void _ibitmap_make_order(IBITMAP *bmp)
{
	if (_ibitmap_order(bmp) != 0) return;
	if (_ibitmap_flags(bmp, IFLAG_HAVEALPHA) == 0) {
		switch (bmp->bpp) 
		{
		case  8: _ibitmap_set_order(bmp,  332); break;
		case 15: _ibitmap_set_order(bmp,  555); break;
		case 16: _ibitmap_set_order(bmp,  565); break;
		case 24: _ibitmap_set_order(bmp,  888); break;
		case 32: _ibitmap_set_order(bmp, 8888); break;
		default: _ibitmap_set_order(bmp,    0); break;
		}
		return;
	}
	switch (bmp->bpp)
	{
	case  8: _ibitmap_flags_clr(bmp, IFLAG_HAVEALPHA); break;
	case 15: _ibitmap_set_order(bmp, 1555); break;
	case 16: _ibitmap_set_order(bmp, 4444); break;
	case 24: _ibitmap_flags_clr(bmp, IFLAG_HAVEALPHA); break;
	case 32: _ibitmap_set_order(bmp, 8888); break;
	}
}

int _ibitmap_check_inverse(IBITMAP *bmp) 
{
	int inverse = 0;
	if (_ibitmap_flags(bmp, IFLAG_HAVEALPHA) == 0) {
		inverse = _ibitmap_flags(bmp, IFLAG_USEBGR)? 4321 : 4123; 
		return inverse;
	}
	if (_ibitmap_flags(bmp, IFLAG_ALPHALOW) == 0) 
		inverse = _ibitmap_flags(bmp, IFLAG_USEBGR)? 4321 : 4123; 
	else
		inverse = _ibitmap_flags(bmp, IFLAG_USEBGR)? 3214 : 1234; 
	if (bmp->bpp != 32) {
		if (inverse == 3214) inverse = 4321;
		if (inverse == 1234) inverse = 4123;
	}
	return inverse? inverse : 4123;
}

#undef _ICONVERT_BLIT_ARGB_EX


/**********************************************************************
 * _iconvert_argb
 **********************************************************************/
void _iconvert_argb(IBITMAP *dst, int dx, int dy, IBITMAP *src,
	int sx, int sy, int w, int h, IRGB *pal, int ismask)
{
	int m = sx + w;
	int n = sy + h;
	int checksame;
	int inverse1;
	int inverse2;
	int flags, r;

	assert(dst && src);
	r = ibitmap_clip(dst->w, dst->h, src->w, src->h, 
						&dx, &dy, &sx, &sy, &m, &n);
	if (r) return;
	w = m - sx;
	h = n - sy;

	checksame = IFLAG_USEBGR | IFLAG_HAVEALPHA | IFLAG_ALPHALOW;
	if (dst->bpp == src->bpp && _ibitmap_flags(dst, checksame) == 
		_ibitmap_flags(src, checksame)) {
		ibitmap_blit(dst, dx, dy, src, sx, sy, w, h, src->mask, ismask);
		return;
	}

	if (_ibitmap_flags(dst, IFLAG_HAVEALPHA) == 0 &&
		_ibitmap_flags(src, IFLAG_HAVEALPHA) == 0) {
		checksame = (_ibitmap_flags(dst, IFLAG_USEBGR) ==
					 _ibitmap_flags(src, IFLAG_USEBGR))? 0 : 1;
		flags = (checksame? ICONV_RGB2BGR : 0) | (ismask? ICONV_MASK : 0);
		_iconvert_fast(dst, dx, dy, src, sx, sy, w, h, pal, flags);
		return;
	}

	_ibitmap_make_order(dst);
	_ibitmap_make_order(src);

	inverse1 = _ibitmap_check_inverse(dst);
	inverse2 = _ibitmap_check_inverse(src);

	if (inverse1 == inverse2) r = 4123;
	else r = inverse1;

	if (src->bpp == 8) {
		_iconvert_argb_from_8(dst, dx, dy, src, sx, sy, w, h, 
			pal, ismask, r);
		return;
	}

	switch (_ibitmap_order(dst)) 
	{
	case 8888:
		_iconvert_argb_to_8888(dst, dx, dy, src, sx, sy, w, h, 
			pal, ismask, r); 
		break;
	case 1555:
		_iconvert_argb_to_1555(dst, dx, dy, src, sx, sy, w, h, 
			pal, ismask, r); 
		break;
	case 4444:
		_iconvert_argb_to_4444(dst, dx, dy, src, sx, sy, w, h, 
			pal, ismask, r); 
		break;
	}
}

#endif


/**********************************************************************
 * _IBLIT_FILL_EX
 **********************************************************************/
#ifndef IDISABLE_BLEND

#define _IBLIT_FILL_BLEND_EX(dbits, dsize, blend) { \
	unsigned long ddelta = dsize * dx; \
	unsigned long twice = dsize * 2; \
	unsigned char *s, *d;	\
	unsigned long c1, c2; \
	long y; \
	if (dsize == 2) { \
		rawcol = (rawcol << 16) | (rawcol & 0xffff); \
		rawcol = _iblend_fast(dbits, blend, rawcol, 0); \
		twice = dsize << 2; \
		for (y = 0; y < h; y++) { \
			d = _ilineptr(dst, dy + y) + ddelta; \
			s = d + (dsize << 1); \
			ILINS_LOOP_QUATRO( \
				_iblit_fill_blend(2, dbits, blend), \
				_iblit_fill_blend(4, dbits, blend), \
				_iblit_fill_blend_x2(4, dbits, blend), w); \
		} \
	}	else { \
		rawcol = _iblend_fast(dbits, blend, rawcol, 0); \
		for (y = 0; y < h; y++) { \
			d = _ilineptr(dst, dy + y) + ddelta; \
			s = d + dsize; \
			ILINS_LOOP_DOUBLE( \
					_iblit_fill_blend(dsize, dbits, blend), \
					_iblit_fill_blend_x2(dsize, dbits, blend), w ); \
		} \
	} \
}

#define _iblit_fill_blend(dsize, dbits, blend) { \
	c1 = _im_get##dsize##b(d); \
	c1 = _iblend_fast(dbits, blend, 0, c1) + rawcol; \
	_im_put##dsize##b(d, c1); \
	d += dsize; \
}

#define _iblit_fill_blend_x2(dsize, dbits, blend) { \
	c1 = _im_get##dsize##b(d); \
	c2 = _im_get##dsize##b(s); \
	c1 = _iblend_fast(dbits, blend, 0, c1) + rawcol; \
	c2 = _iblend_fast(dbits, blend, 0, c2) + rawcol; \
	_im_put##dsize##b(d, c1); \
	_im_put##dsize##b(s, c2); \
	d += twice; \
	s += twice; \
}

#if 0
#define _IBLIT_FILL_BLEND(dbits, dsize, blend) { \
	switch (blend) { \
	case  1: _IBLIT_FILL_BLEND_EX(dbits, dsize,  1); break; \
	case  2: _IBLIT_FILL_BLEND_EX(dbits, dsize,  2); break; \
	case  3: _IBLIT_FILL_BLEND_EX(dbits, dsize,  3); break; \
	case  4: _IBLIT_FILL_BLEND_EX(dbits, dsize,  4); break; \
	case  5: _IBLIT_FILL_BLEND_EX(dbits, dsize,  5); break; \
	case  6: _IBLIT_FILL_BLEND_EX(dbits, dsize,  6); break; \
	case  7: _IBLIT_FILL_BLEND_EX(dbits, dsize,  7); break; \
	case  8: _IBLIT_FILL_BLEND_EX(dbits, dsize,  8); break; \
	case  9: _IBLIT_FILL_BLEND_EX(dbits, dsize,  9); break; \
	case 10: _IBLIT_FILL_BLEND_EX(dbits, dsize, 10); break; \
	case 11: _IBLIT_FILL_BLEND_EX(dbits, dsize, 11); break; \
	case 12: _IBLIT_FILL_BLEND_EX(dbits, dsize, 12); break; \
	case 13: _IBLIT_FILL_BLEND_EX(dbits, dsize, 13); break; \
	case 14: _IBLIT_FILL_BLEND_EX(dbits, dsize, 14); break; \
	case 15: _IBLIT_FILL_BLEND_EX(dbits, dsize, 15); break; \
	} \
}
#else
#define _IBLIT_FILL_BLEND(dbits, dsize, blend) {}
#endif

static void _iblit_fill_15(IBITMAP *dst, int dx, int dy, int w, int h, 
	unsigned long rawcol, int alpha)
{
	_IBLIT_FILL_BLEND(15, 2, alpha);
}

static void _iblit_fill_16(IBITMAP *dst, int dx, int dy, int w, int h, 
	unsigned long rawcol, int alpha)
{
	_IBLIT_FILL_BLEND(16, 2, alpha);
}

static void _iblit_fill_24(IBITMAP *dst, int dx, int dy, int w, int h, 
	unsigned long rawcol, int alpha)
{
	_IBLIT_FILL_BLEND(24, 3, alpha);
}

static void _iblit_fill_32(IBITMAP *dst, int dx, int dy, int w, int h, 
	unsigned long rawcol, int alpha)
{
	_IBLIT_FILL_BLEND(32, 4, alpha);
}
#endif


/**********************************************************************
 * _iblit_flip
 **********************************************************************/
void _iblit_fill(IBITMAP *dst, int dx, int dy, int w, int h, 
	unsigned long rawcol, int alpha)
{
	if (dx + w <= 0 || dy + h <= 0 || w < 0 || h < 0) return;
	if (dx >= (long)dst->w || dy >= (long)dst->h) return;

	if (dx < 0) w += dx, dx = 0;
	if (dy < 0) h += dy, dy = 0;
	if (dx + w >= (long)dst->w) w = (long)dst->w - dx;
	if (dy + h >= (long)dst->h) h = (long)dst->h - dy;

	alpha = _iblend_alpha(alpha);

	if (alpha == 0) return;
	if (alpha >= 1 && alpha < 16) {
		#ifndef IDISABLE_BLEND
		switch (dst->bpp) 
		{
		case 15: _iblit_fill_15(dst, dx, dy, w, h, rawcol, alpha); break;
		case 16: _iblit_fill_16(dst, dx, dy, w, h, rawcol, alpha); break;
		case 24: _iblit_fill_24(dst, dx, dy, w, h, rawcol, alpha); break;
		case 32: _iblit_fill_32(dst, dx, dy, w, h, rawcol, alpha); break;
		}
		#endif
		return;
	}

	ibitmap_fill(dst, dx, dy, w, h, rawcol, 1);
}

#undef _IBLIT_FILL_EX


