#include "img_conv.h"

#ifdef __cplusplus
extern "C" {
#endif


#define var (( PImage) self)

#define dEDIFF_ARGS \
	int * err_buf, err_stride = (width + 2) * 3
#define EDIFF_INIT  \
	if (!(err_buf = malloc(err_stride * sizeof( int) * OMP_MAX_THREADS))) return;\
	memset( err_buf, 0, err_stride * sizeof( int) * OMP_MAX_THREADS);
#define EDIFF_DONE free(err_buf)
#define EDIFF_CONV err_buf + err_stride * OMP_THREAD_NUM

#define BCPARMS      self, dstData, dstPal, dstType, dstPalSize, palSize_only
#define FILL_PALETTE(_pal,_palsize,_maxpalsize,_colorref)\
	fill_palette(self,palSize_only,dstPal,dstPalSize,_pal,_palsize,_maxpalsize,_colorref)

/* Mono */

static void
fill_palette( Handle self, Bool palSize_only, RGBColor * dstPal, int * dstPalSize, 
				RGBColor * fillPalette, int fillPalSize, int maxPalSize, Byte * colorref)
{
	Bool do_colormap = 1;
	if ( palSize_only) { 
		if ( var-> palSize > *dstPalSize)
			cm_squeeze_palette( var-> palette, var-> palSize, dstPal, *dstPalSize);
		else if ( *dstPalSize > fillPalSize + var-> palSize) {
			memcpy( dstPal, var-> palette, var-> palSize * sizeof(RGBColor));
			memcpy( dstPal + var-> palSize, fillPalette, fillPalSize * sizeof(RGBColor));
			memset( dstPal + var-> palSize + fillPalSize, 0, (*dstPalSize - fillPalSize - var-> palSize) * sizeof(RGBColor));
			do_colormap = 0;
		} else {
			memcpy( dstPal, var-> palette, var-> palSize * sizeof(RGBColor));
			cm_squeeze_palette( fillPalette, fillPalSize, dstPal + var-> palSize, *dstPalSize - var-> palSize);
			do_colormap = 0;
		}
	} else if ( *dstPalSize != 0) {
		if ( *dstPalSize > maxPalSize) 
			cm_squeeze_palette( dstPal, *dstPalSize, dstPal, *dstPalSize = maxPalSize);
	} else if ( var-> palSize > maxPalSize) {
		cm_squeeze_palette( var-> palette, var-> palSize, dstPal, *dstPalSize = maxPalSize);
	} else {
		memcpy( dstPal, var-> palette, (*dstPalSize = var-> palSize) * sizeof(RGBColor));
		do_colormap = 0;
	}
	if ( colorref) {
		if ( do_colormap)
			cm_fill_colorref( var->palette, var-> palSize, dstPal, *dstPalSize, colorref);
		else
			memcpy( colorref, map_stdcolorref, 256);
	}
}

BC( mono, mono, None)
{
	int ws, mask;
	dBCARGS;
	BCWARN;

	if ( palSize_only || *dstPalSize == 0) 
		memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof( RGBColor));
	
	if ((
		(var->palette[0].r + var->palette[0].g + var->palette[0].b) >
		(var->palette[1].r + var->palette[1].g + var->palette[1].b)
	) == (
		(dstPal[0].r + dstPal[0].g + dstPal[0].b) >
		(dstPal[1].r + dstPal[1].g + dstPal[1].b) 
	)) {
		if ( dstData != var-> data)
			memcpy( dstData, var-> data, var-> dataSize);
	} else {
		/* preserve off-width zeros */
		ws = width >> 3;
		if ((width & 7) == 0) {
			ws--;
			mask = 0xff;
		} else
			mask = (0xff00 >> (width & 7)) & 0xff;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
		for ( i = 0; i < height; i++) {
			register int j;
			dBCLOOP;
			for ( j = 0; j < ws; j++) dstDataLoop[j] =~ srcDataLoop[j];
			dstDataLoop[ws] = (~srcDataLoop[j]) & mask;
		}
	}
}

BC( mono, mono, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf;
	dEDIFF_ARGS;
	BCWARN;

	FILL_PALETTE( stdmono_palette, 2, 2, nil);

	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	
	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_mono_byte( srcDataLoop, buf_t, width); 
		bc_byte_op( buf_t, buf_t, width, tree, var-> palette, dstPal, EDIFF_CONV);
		bc_byte_mono_cr( buf_t, dstDataLoop, width, map_stdcolorref); 
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_mono_mono_ictNone(BCPARMS);
}

BC( mono, nibble, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( stdmono_palette, 2, 16, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_mono_nibble_cr( BCCONVLOOP, colorref);
	}
}

BC( mono, byte, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( stdmono_palette, 2, 256, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_mono_byte_cr( BCCONVLOOP, colorref);
	}
}

BC( mono, graybyte, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_mono_graybyte( BCCONVLOOP, var->palette);
	}
}

BC( mono, rgb, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_mono_rgb( BCCONVLOOP, var->palette);
	}
}

/* Nibble */

BC( nibble, mono, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( stdmono_palette, 2, 2, colorref);
	cm_fill_colorref( var->palette, var-> palSize, dstPal, *dstPalSize, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_mono_cr( BCCONVLOOP, colorref);
	}
}

BC( nibble, mono, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_mono_ht( BCCONVLOOP, var->palette, i);
	}
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( nibble, mono, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_mono_ed( BCCONVLOOP, var->palette, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( nibble, mono, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf;
	dEDIFF_ARGS;
	BCWARN;

	FILL_PALETTE( stdmono_palette, 2, 2, nil);
	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_nibble_byte( srcDataLoop, buf_t, width);
		bc_byte_op( buf_t, buf_t, width, tree, var-> palette, dstPal, EDIFF_CONV);
		bc_byte_mono_cr( buf_t, dstDataLoop, width, map_stdcolorref);
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_nibble_mono_ictErrorDiffusion(BCPARMS);
} 

BC( nibble, nibble, None)
{
	dBCARGS;
	int w = (width >> 1) + (width & 1);
	BCWARN;
	FILL_PALETTE( cubic_palette16, 16, 16, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		register int j;
		dBCLOOP;
		for ( j = 0; j < w; j++)
			dstDataLoop[j] = (colorref[srcDataLoop[j] >> 4] << 4) | colorref[srcDataLoop[j] & 0xf]; 
	}
}

BC( nibble, nibble, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf;
	dEDIFF_ARGS;
	BCWARN;

	FILL_PALETTE( cubic_palette16, 16, 16, nil);

	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	
	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_nibble_byte( srcDataLoop, buf_t, width); 
		bc_byte_op( buf_t, buf_t, width, tree, var-> palette, dstPal, EDIFF_CONV);
		bc_byte_nibble_cr( buf_t, dstDataLoop, width, map_stdcolorref); 
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_nibble_nibble_ictNone(BCPARMS);
}

BC( nibble, byte, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( cubic_palette, 216, 256, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_byte_cr( BCCONVLOOP, colorref);
	}
}

BC( nibble, graybyte, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_graybyte( BCCONVLOOP, var->palette);
	}
}

BC( nibble, rgb, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_nibble_rgb( BCCONVLOOP, var->palette);
	}
}

/* Byte */
BC( byte, mono, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( stdmono_palette, 2, 2, colorref);
	cm_fill_colorref( var->palette, var-> palSize, dstPal, *dstPalSize, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_mono_cr( BCCONVLOOP, colorref);
	}
}

BC( byte, mono, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_mono_ht( BCCONVLOOP, var->palette, i);
	}
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( byte, mono, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_mono_ed( BCCONVLOOP, var->palette, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( byte, mono, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf;
	dEDIFF_ARGS;
	BCWARN;

	FILL_PALETTE( stdmono_palette, 2, 2, nil);
	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_byte_op( srcDataLoop, buf_t, width, tree, var-> palette, dstPal, EDIFF_CONV);
		bc_byte_mono_cr( buf_t, dstDataLoop, width, map_stdcolorref);
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_byte_mono_ictErrorDiffusion(BCPARMS);
} 

BC( byte, nibble, None)
{
	dBCARGS;
	BCWARN;
	FILL_PALETTE( cubic_palette16, 16, 16, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_nibble_cr( BCCONVLOOP, colorref);
	}
}

BC( byte, nibble, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_nibble_ht( BCCONVLOOP, var->palette, i);
	}
	memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( byte, nibble, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_nibble_ed( BCCONVLOOP, var->palette, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( byte, nibble, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf, hist[256];
	RGBColor new_palette[256];
	int j, new_pal_size = 0;
	dEDIFF_ARGS;
	BCWARN;

	if ( *dstPalSize == 0 || palSize_only) {
		int lim = palSize_only ? *dstPalSize : 16;
		memset( hist, 0, sizeof( hist));
		for ( i = 0; i < height; i++) {
			Byte * d = srcData + srcLine * i;
			for ( j = 0; j < width; j++, d++) 
				if ( hist[*d] == 0) {
					hist[*d] = 1;
					new_palette[new_pal_size++] = var-> palette[*d];
					if ( new_pal_size == 256) goto END_HIST_LOOP;
				}
		}
	END_HIST_LOOP:
		if ( new_pal_size > lim) {
			cm_squeeze_palette( new_palette, new_pal_size, new_palette, lim);
			new_pal_size = lim;
		}
	} else
		memcpy( new_palette, dstPal, ( new_pal_size = *dstPalSize) * sizeof(RGBColor));

	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	EDIFF_INIT;
	if (!( tree = cm_study_palette( new_palette, new_pal_size))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_byte_op( srcDataLoop, buf_t, width, tree, var-> palette, new_palette, EDIFF_CONV);
		bc_byte_nibble_cr( buf_t, dstDataLoop, width, map_stdcolorref);
	}
	memcpy( dstPal, new_palette, new_pal_size * sizeof(RGBColor));
	*dstPalSize = new_pal_size;
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_byte_nibble_ictErrorDiffusion(BCPARMS);
} 

BC( byte, byte, None)
{
	dBCARGS;
	int j;
	BCWARN;
	FILL_PALETTE( cubic_palette, 216, 256, colorref);
	for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
		for ( j = 0; j < width; j++)
			dstData[j] = colorref[srcData[j]];
	}
}

BC( byte, byte, Optimized)
{
	dBCARGS;
	U16 * tree;
	dEDIFF_ARGS;
	BCWARN;

	FILL_PALETTE( cubic_palette, 216, 256, nil);

	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_op( srcDataLoop, dstDataLoop, width, tree, var-> palette, dstPal, EDIFF_CONV);
	}
	free( tree);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_byte_byte_ictNone(BCPARMS);
}

BC( byte, graybyte, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_graybyte( BCCONVLOOP, var->palette);
	}
}

BC( byte, rgb, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_rgb( BCCONVLOOP, var->palette);
	}
}

/* Graybyte */
BC( graybyte, mono, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_graybyte_mono_ht( BCCONVLOOP, i);
	}
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( graybyte, mono, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_byte_mono_ed( BCCONVLOOP, std256gray_palette, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( graybyte, nibble, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_graybyte_nibble_ht( BCCONVLOOP, i);
	}
	memcpy( dstPal, std16gray_palette, sizeof( std16gray_palette));
	*dstPalSize = 16;
}

BC( graybyte, nibble, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_graybyte_nibble_ed( BCCONVLOOP, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, std16gray_palette, sizeof( std16gray_palette));
	*dstPalSize = 16;
}

BC( graybyte, rgb, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_graybyte_rgb( BCCONVLOOP);
	}
}

/* RGB */
BC( rgb, mono, None)
{
	dBCARGS;
	Byte * convBuf = allocb( width * OMP_MAX_THREADS);
	BCWARN;
	if ( !convBuf) return;
	cm_fill_colorref(( PRGBColor) map_RGB_gray, 256, stdmono_palette, 2, colorref);
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++)
	{
		dBCLOOP;
		Byte * buf_t = convBuf + width * OMP_THREAD_NUM;
		bc_rgb_graybyte( srcDataLoop, buf_t, width);
		bc_byte_mono_cr( buf_t, dstDataLoop, width, colorref);
	}
	free( convBuf);
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( rgb, mono, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_mono_ht( BCCONVLOOP, i);
	}
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( rgb, mono, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_mono_ed( BCCONVLOOP, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( rgb, mono, Optimized)
{
	dBCARGS;
	Byte * buf;
	U16 * tree;
	dEDIFF_ARGS;
	BCWARN;

	if ( palSize_only) goto FAIL;
	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	EDIFF_INIT;
	if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_rgb_byte_op(( RGBColor *) srcDataLoop, buf_t, width, tree, dstPal, EDIFF_CONV);
		bc_byte_mono_cr( buf_t, dstDataLoop, width, map_stdcolorref);
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;

FAIL:   
	ic_rgb_mono_ictErrorDiffusion(BCPARMS);
}

BC( rgb, nibble, None)
{
	dBCARGS;
	BCWARN;
	memcpy( dstPal, cubic_palette16, sizeof( cubic_palette16));
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_nibble(BCCONVLOOP);
	}
	*dstPalSize = 16;
}

BC( rgb, nibble, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_nibble_ht( BCCONVLOOP, i);
	}
	memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( rgb, nibble, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_nibble_ed( BCCONVLOOP, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( rgb, nibble, Optimized)
{
	dBCARGS;
	U16 * tree;
	Byte * buf;
	RGBColor new_palette[16];
	int new_pal_size = 16;
	dEDIFF_ARGS;
	BCWARN;

	if ( *dstPalSize == 0 || palSize_only) {
		if ( palSize_only) new_pal_size = *dstPalSize;
		if ( !cm_optimized_palette( srcData, srcLine, width, height, new_palette, &new_pal_size)) 
			goto FAIL;
	} else
		memcpy( new_palette, dstPal, ( new_pal_size = *dstPalSize) * sizeof(RGBColor));

	if ( !( buf = malloc( width * OMP_MAX_THREADS))) goto FAIL;
	EDIFF_INIT;
	if (!( tree = cm_study_palette( new_palette, new_pal_size))) {
		EDIFF_DONE;
		free( buf);
		goto FAIL;
	}
	memcpy( dstPal, new_palette, new_pal_size * 3);
	*dstPalSize = new_pal_size;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		Byte * buf_t = buf + width * OMP_THREAD_NUM;
		bc_rgb_byte_op(( RGBColor *) srcDataLoop, buf_t, width, tree, dstPal, EDIFF_CONV);
		bc_byte_nibble_cr( buf_t, dstDataLoop, width, map_stdcolorref);
	}
	free( tree);
	free( buf);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_rgb_nibble_ictErrorDiffusion(BCPARMS);
} 

BC( rgb, byte, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_byte( BCCONVLOOP);
	}
	memcpy( dstPal, cubic_palette, (*dstPalSize = 216) * sizeof(RGBColor));
}

BC( rgb, byte, Ordered)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_byte_ht( BCCONVLOOP, i);
	}
	memcpy( dstPal, cubic_palette, (*dstPalSize = 216) * sizeof(RGBColor));
}

BC( rgb, byte, ErrorDiffusion)
{
	dBCARGS;
	dEDIFF_ARGS;
	BCWARN;
	EDIFF_INIT;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_byte_ed( BCCONVLOOP, EDIFF_CONV);
	}
	EDIFF_DONE;
	memcpy( dstPal, cubic_palette, (*dstPalSize = 216) * sizeof(RGBColor));
}   

BC( rgb, byte, Optimized)
{
	dBCARGS;
	U16 * tree;
	RGBColor new_palette[768];
	int new_pal_size = 256;
	dEDIFF_ARGS;
	BCWARN;
	if ( *dstPalSize == 0 || palSize_only) {
		if ( palSize_only) new_pal_size = *dstPalSize;
		if ( !cm_optimized_palette( srcData, srcLine, width, height, new_palette, &new_pal_size)) 
			goto FAIL;
	} else
		memcpy( new_palette, dstPal, ( new_pal_size = *dstPalSize) * sizeof(RGBColor));

	EDIFF_INIT;
	if (!( tree = cm_study_palette( new_palette, new_pal_size))) {
		EDIFF_DONE;
		goto FAIL;
	}
	memcpy( dstPal, new_palette, new_pal_size * 3);
	*dstPalSize = new_pal_size;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_byte_op(( RGBColor *) srcDataLoop, dstDataLoop, width, tree, dstPal, EDIFF_CONV);
	}      
	free( tree);
	EDIFF_DONE;
	return;
	
FAIL:  
	ic_rgb_byte_ictErrorDiffusion(BCPARMS);
} 

BC( rgb, graybyte, None)
{
	dBCARGS;
	BCWARN;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
	for ( i = 0; i < height; i++) {
		dBCLOOP;
		bc_rgb_graybyte( BCCONVLOOP);
	}
}

#ifdef __cplusplus
}
#endif
