The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#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