The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*-
 * Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id$
 */
/* Created by Dmitry Karasik <dk@plab.ku.dk> */
#include "img_conv.h"

#ifdef __cplusplus
extern "C" {
#endif


#define var (( PImage) self)

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

#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 j, 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;
      for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
         for ( j = 0; j < ws; j++) dstData[j] =~ srcData[j];
         dstData[ws] = (~srcData[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))) goto FAIL;
   
   EDIFF_INIT;
   if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_mono_byte( srcData, buf, width); 
      bc_byte_op( buf, buf, width, tree, var-> palette, dstPal, EDIFF_CONV);
      bc_byte_mono_cr( buf, dstData, 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);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_mono_nibble_cr( BCCONV, colorref);
}

BC( mono, byte, None)
{
   dBCARGS;
   BCWARN;
   FILL_PALETTE( stdmono_palette, 2, 256, colorref);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_mono_byte_cr( BCCONV, colorref);
}

BC( mono, graybyte, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_mono_graybyte( BCCONV, var->palette);
}

BC( mono, rgb, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_mono_rgb( BCCONV, 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);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_mono_cr( BCCONV, colorref);
}

BC( nibble, mono, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_mono_ht( BCCONV, var->palette, i);
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( nibble, mono, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_mono_ed( BCCONV, 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))) goto FAIL;
   EDIFF_INIT;
   if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_nibble_byte( srcData, buf, width);
      bc_byte_op( buf, buf, width, tree, var-> palette, dstPal, EDIFF_CONV);
      bc_byte_mono_cr( buf, dstData, width, map_stdcolorref);
   }
   free( tree);
   free( buf);
   EDIFF_DONE;
   return;
   
FAIL:  
   ic_nibble_mono_ictErrorDiffusion(BCPARMS);
} 

BC( nibble, nibble, None)
{
   dBCARGS;
   int j, w = (width >> 1) + (width & 1);
   BCWARN;
   FILL_PALETTE( cubic_palette16, 16, 16, colorref);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      for ( j = 0; j < w; j++)
         dstData[j] = (colorref[srcData[j] >> 4] << 4) | colorref[srcData[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))) goto FAIL;
   
   EDIFF_INIT;
   if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_nibble_byte( srcData, buf, width); 
      bc_byte_op( buf, buf, width, tree, var-> palette, dstPal, EDIFF_CONV);
      bc_byte_nibble_cr( buf, dstData, 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);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_byte_cr( BCCONV, colorref);
}

BC( nibble, graybyte, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_graybyte( BCCONV, var->palette);
}

BC( nibble, rgb, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_nibble_rgb( BCCONV, 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);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_mono_cr( BCCONV, colorref);
}

BC( byte, mono, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_mono_ht( BCCONV, var->palette, i);
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( byte, mono, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_mono_ed( BCCONV, 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))) goto FAIL;
   EDIFF_INIT;
   if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_byte_op( srcData, buf, width, tree, var-> palette, dstPal, EDIFF_CONV);
      bc_byte_mono_cr( buf, dstData, 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);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_nibble_cr( BCCONV, colorref);
}

BC( byte, nibble, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_nibble_ht( BCCONV, var->palette, i);
   memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( byte, nibble, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_nibble_ed( BCCONV, 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))) goto FAIL;
   EDIFF_INIT;
   if (!( tree = cm_study_palette( new_palette, new_pal_size))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_byte_op( srcData, buf, width, tree, var-> palette, new_palette, EDIFF_CONV);
      bc_byte_nibble_cr( buf, dstData, 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;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) 
      bc_byte_op( srcData, dstData, width, tree, var-> palette, dstPal, EDIFF_CONV);
   free( tree);
   EDIFF_DONE;
   return;
   
FAIL:  
   ic_byte_byte_ictNone(BCPARMS);
}

BC( byte, graybyte, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_graybyte( BCCONV, var->palette);
}

BC( byte, rgb, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_rgb( BCCONV, var->palette);
}

/* Graybyte */
BC( graybyte, mono, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_graybyte_mono_ht( BCCONV, i);
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( graybyte, mono, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_byte_mono_ed( BCCONV, std256gray_palette, EDIFF_CONV);
   EDIFF_DONE;
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( graybyte, nibble, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_graybyte_nibble_ht( BCCONV, i);
   memcpy( dstPal, std16gray_palette, sizeof( std16gray_palette));
   *dstPalSize = 16;
}

BC( graybyte, nibble, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_graybyte_nibble_ed( BCCONV, EDIFF_CONV);
   EDIFF_DONE;
   memcpy( dstPal, std16gray_palette, sizeof( std16gray_palette));
   *dstPalSize = 16;
}

BC( graybyte, rgb, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_graybyte_rgb( BCCONV);
}

/* RGB */
BC( rgb, mono, None)
{
   dBCARGS;
   Byte * convBuf = allocb( width);
   BCWARN;
   if ( !convBuf) return;
   cm_fill_colorref(( PRGBColor) map_RGB_gray, 256, stdmono_palette, 2, colorref);
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
   {
      bc_rgb_graybyte( srcData, convBuf, width);
      bc_byte_mono_cr( convBuf, dstData, width, colorref);
   }
   free( convBuf);
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( rgb, mono, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_mono_ht( BCCONV, i);
   memcpy( dstPal, stdmono_palette, (*dstPalSize = 2) * sizeof(RGBColor));
}

BC( rgb, mono, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_mono_ed( BCCONV, 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))) goto FAIL;
   EDIFF_INIT;
   if (!( tree = cm_study_palette( dstPal, *dstPalSize))) {
      EDIFF_DONE;
      free( buf);
      goto FAIL;
   }
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_rgb_byte_op(( RGBColor *) srcData, buf, width, tree, dstPal, EDIFF_CONV);
      bc_byte_mono_cr( buf, dstData, 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));
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_nibble(BCCONV);
   *dstPalSize = 16;
}

BC( rgb, nibble, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_nibble_ht( BCCONV, i);
   memcpy( dstPal, cubic_palette8, (*dstPalSize = 8) * sizeof(RGBColor));
}

BC( rgb, nibble, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_nibble_ed( BCCONV, 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))) 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;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine) {
      bc_rgb_byte_op(( RGBColor *) srcData, buf, width, tree, dstPal, EDIFF_CONV);
      bc_byte_nibble_cr( buf, dstData, width, map_stdcolorref);
   }
   free( tree);
   free( buf);
   EDIFF_DONE;
   return;
   
FAIL:  
   ic_rgb_nibble_ictErrorDiffusion(BCPARMS);
} 

BC( rgb, byte, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_byte( BCCONV);
   memcpy( dstPal, cubic_palette, (*dstPalSize = 216) * sizeof(RGBColor));
}

BC( rgb, byte, Ordered)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_byte_ht( BCCONV, i);
   memcpy( dstPal, cubic_palette, (*dstPalSize = 216) * sizeof(RGBColor));
}

BC( rgb, byte, ErrorDiffusion)
{
   dBCARGS;
   dEDIFF_ARGS;
   BCWARN;
   EDIFF_INIT;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_byte_ed( BCCONV, 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;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_byte_op(( RGBColor *) srcData, dstData, width, tree, dstPal, EDIFF_CONV);
   free( tree);
   EDIFF_DONE;
   return;
   
FAIL:  
   ic_rgb_byte_ictErrorDiffusion(BCPARMS);
} 

BC( rgb, graybyte, None)
{
   dBCARGS;
   BCWARN;
   for ( i = 0; i < height; i++, srcData += srcLine, dstData += dstLine)
      bc_rgb_graybyte( BCCONV);
}

#ifdef __cplusplus
}
#endif