/***************************************************************************
    dither.c  -  floyd-steinberg & halftone dither routines 
                             -------------------
    begin                : Thu Jan 13 2000
    copyright            : (C) 1998-2000 by pnm2ppa project
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

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

#define __DITHER_C__

#include "global.h"
#include "debug.h"

#include "image.h"
#include "fscoeff.h"
#include "palette.h"
#include "hash_ink.h"

int
ink_lookup(int R, int G, int B, int MaxPass, 
	   float *d, int *bx, int *by, int *bz, int *br, int *bg, int *bb)
{ /* Try to find correct color value in hashed structure instead of 
   * exhaustive search in (MaxPass+1)^3 ink possibilities */

  struct hash_ink **arr = 0, *cur;
  int r, g, b, h;
  float d1;
  int shift = 0; /* Keep the compiler quiet! */
  
  /* *d = -1; */
  if (MaxPass == 4) /* standard mode */
    {
      arr = arr_max4;
      shift = shift4;
    }
  else if (MaxPass == 1) /* --eco mode */
    {
      arr = arr_max1;
      shift = shift1;
    }
  /* Add here other supported MaxPass arrays */

  if (arr == 0) /* not supported MaxPass value by int_lookup() */
    return 0;

  r = R >> shift;
  g = G >> shift;
  b = B >> shift;
  
  h = (r << (16 - shift * 2)) + (g << (8-shift)) + (b); 
  /* h e [0, 4096> for shift = 4 */
  
  cur = arr[h];

  while( cur != 0 )
  /* the code added below is an edited copy of the code found below
     in FS_Color_Dither */
  {
    r =	(int) R - (int) cur->data[0];
    g = (int) G - (int) cur->data[1];
    b = (int) B - (int) cur->data[2];
			      
    d1 = /* 0.5 * */ r * r + g * g + b * b;


    if (d1 <= *d)
      {
	*d = d1;
	*bx = gMaxPass + 1 - cur->x;
	*by = gMaxPass + 1 - cur->y;
	*bz = gMaxPass + 1 - cur->z;
	*br = r;
	*bg = g;
	*bb = b;
      }

    cur = cur->next;
  }
  
  return MaxPass;
}

void
FS_Color_Dither (image_t *image, unsigned char *line)
{
  short dd;
  int i, i_max ;
  int x, bx, y, by, z, bz;
  float d, d1;
  int R, G, B, dr, dg, db;
  int br, bg, bb, r, g, b, oldr, oldb, oldg;
  static short *err_coeff_e = 0;
  static short *err_coeff_se = 0;
  static short *err_coeff_s = 0;
  static short *err_coeff_sw = 0;
  int MaxPass = 0;
  int *error;
  
  if (image->bufferCurLine % 2 == 1)
    {
      error = gErrVec_bw;
    }
  else
    {
      error = gErrVec;
      MaxPass = gMaxPass;
    }
  
  r = g = b = dr = dg = db = 0;
  br = r;
  bg = g;
  bb = b;
  
  if (err_coeff_e == NULL)
    {
      err_coeff_e = fs_err_coeff_e + 255;
      err_coeff_se = fs_err_coeff_se + 255;
      err_coeff_s = fs_err_coeff_s + 255;
      err_coeff_sw = fs_err_coeff_sw + 255;
      
      dd = 25;
      
      for (i = -255; i < 256; i++)
	{
	  err_coeff_e[i] = i * 8 / dd;	//8
	  err_coeff_se[i] = i * 5 / dd;	//5
	  err_coeff_s[i] = i * 8 / dd;	//8
	  err_coeff_sw[i] = i * 4 / dd;	//4
	}
    }
  
  /* Only even lines are treated for color ;
     pixels are treated in pairs, and
     reduced to 300dpi resolution */
   
      i_max = image->width * 3 ;

  if (image->bufferCurLine % 2 == 0)
    {
      /*treatment of even lines */
    for (i = 0; i < i_max ; i += 6)
      {
	
	oldr = br;
	oldb = bb;
	oldg = bg;
	
	br = bg = bb = 0;

	      /* avoid the (0,0,0) entry for black if 
	       * no match is found (initial d too small)
	       * use (1,1,1) instead (gMaxPass drops of each color)
	       */
        bx = by = bz = 1      ;

	/* if either of a pair of successive pixels is black,
	   make  both black (produces 300dpi black resolution) */
	if ( (i+3) != i_max)
	  {
	   if ( !line[i + 3] && !line[i + 4] && !line[i + 5] )
		 line[i]=  line[i + 1] = line[i + 2]  = 0 ;
	   }

	if ( !line[i] && !line[i + 1] && !line[i + 2] )
	  {
	    /* pixel is black */
	    error[i + 0] = 0;
	    error[i + 1] = 0;
	    error[i + 2] = 0;
	    dr = dg = db = 0;
	    /* make next pixel black, too */
	    if ( (i + 3) != i_max )
	      {
		 line[i + 3] =  line[i + 4] = line[i + 5]  = 0 ;
	       }
	  }
	else
	  if (((line[i] < 255) || (line[i + 1] < 255)
	       || (line[i + 2] < 255)))
	    {
	      /* pixel is colored */
	      
	      /* 
		 find the "requested color by considering previous 
		 errors
	      */
	      
	      
	      R =
		gEnh_curve_r[(int) (line[i] + line[i + 3]) / 2] +
		error[i] + dr;
	      R = (R < 0 ? 0 : R);
	      R = (R > 255 ? 255 : R);
	      G =
		gEnh_curve_g[(int) (line[(i + 1)] + line[i + 4]) / 2] +
		error[i + 1] + dg;
	      G = (G < 0 ? 0 : G);
	      G = (G > 255 ? 255 : G);
	      B =
		gEnh_curve_b[(int) (line[(i + 2)] + line[i + 5]) / 2] +
		error[i + 2] + db;
	      B = (B < 0 ? 0 : B);
	      B = (B > 255 ? 255 : B);
	      


	      /* 
	       * now convert this RGB value to drops of ink per pixel.
	       * The table is set up as a hashing table .  
	       */


	      if (!gGammaMode)
		{
		  // yet another fine mocho fix! :-)
		  br = R / 2;
		  bg = G / 2;
		  bb = B / 2;
		  //d = ((2.0 + (float)R / 256.0) * R * R + 4 * G * G + (2 + (255.0 - (float)R) / 256.0) * B * B)*0.7;

		  if (gMaxPass == 4)
		    {
		      d = (R * R + G * G + B * B) * 1.3;
		    }
		  else
		    {
		      /* does the "1.3"  need adjusting for gMaxPass = 1?
		       * This sets "very dark" pixels to black.
		       */
		      d = (R * R + G * G + B * B) * 1.3;
		    }
		}
	      else
		{
		  /* this is for the special case when gamma.ppm is
		   * being generated.
		   */
		  d = 2147483647; //just a large initial value
		}
	      
#ifdef CHECK_HASHING
	      {
		/* code for checking correctness of hash_ink (by ink_lookup)
		   Not sensible in production code! */
		int sbx, sby, sbz, sbr, sbg, sbb;
		float sd;
		int s_result;
		
		sd = d;
		sbx = bx;
		sby = by;
		sbz = bz;
		sbr = br;
		sbg = bg;
		sbb = bb;
		
		s_result = ink_lookup(R, G, B, MaxPass, 
				      &sd, &sbx, &sby, &sbz, &sbr, &sbg, &sbb);
#else
		if (! ink_lookup(R, G, B, MaxPass, 
				 &d, &bx, &by, &bz, &br, &bg, &bb))
		  {
		    /* ink_lookup failed -- so use old code as fallback */
		    static int unsupported_flag = 0;
		    
		    if (unsupported_flag != MaxPass)
		      {
			unsupported_flag = MaxPass;
			DPRINTF("WARNING:Unsupported MaxPass value %d by ink_lookup\n", MaxPass);
		      }
#endif
		    /* this code scans the 625 (gMaxPass=4) or
		     * 8 (gMaxPass=1) possible combinations of
		     * (z,y,x) drops of (Cyan,Magenta,Yellow) ink
		     * to best match the RGB value for the pixel, 
		     * using the tabulated values in hp_pal
		     * in pallette.h
		     */

		    for (z = 0; z < MaxPass + 1; z++)
		      for (x = 0; x < MaxPass + 1; x++)
			for (y = 0; y < MaxPass + 1; y++)
			  if (!
			      (((x == 4) && (y > 2) && (z > 2)) ||
			       ((y == 4) && (x > 2) && (z > 2)) ||
			       ((z == 4) && (y > 2) && (x > 2))))
			    
			    {
			      
				//rmean = ((int) R + (int) hp_pal[4-x][4-y][4-z][0]) / 2;
			      
			      r =
				(int) R - (int) hp_pal[4 - x][4 - y][4 -
								    z]
				[0];
			      g =
				(int) G - (int) hp_pal[4 - x][4 - y][4 -
								    z]
				[1];
			      b =
				(int) B - (int) hp_pal[4 - x][4 - y][4 -
								    z]
				[2];
			      
				//d1 = (2.0 + (float)rmean / 256.0) * r * r + 
				//     4 * g * g +
				//    (2 + (255.0 - (float)rmean) / 256.0) * b * b;
			      
			      d1 = /* 0.5 * */ r * r + g * g + b * b;
			      
			      /* the next test also excludes "too dark"
			       * pixels with a small initial d value.
			       */

			      if (d1 <= d)   
				{
				  d = d1;
				  bx = gMaxPass + 1 - x;
				  by = gMaxPass + 1 - y;
				  bz = gMaxPass + 1 - z;
				  br = r;
				  bg = g;
				  bb = b;
				}
			    }
#ifdef CHECK_HASHING
		    if (s_result != 0)
		      {
			/* if (d != sd)
			   debug( R, G, B, d, gMaxPass + 1 - bx, 
			   gMaxPass + 1 - by, gMaxPass + 1 - bz ); */
			assert(d == sd);
			/*  The assertions below are deleted; there might
			    be equivalent solutions (w.r.t. distance d)
			    with different values for bx, by, bz 
			    assert(sbx == bx);
			    assert(sby == by);
			    assert(sbz == bz);
			    assert(br == sbr);
			    assert(bg == sbg);
			    assert(bb == sbb); */
		      }
		  }
#else
	      }
#endif
	      
	      
	      if ( true  /* || (bx==0 && by==0 && bz==0) */ )
		{
		  // careful! floats cannot be compared easily
		  if (!(d == 0.0) && ((i + 8) < image->width * 3))
		    {
		      
		      dr = err_coeff_e[br];
		      dg = err_coeff_e[bg];
		      db = err_coeff_e[bb];
		      
			error[i + 0] = err_coeff_s[br];
			error[i + 1] = err_coeff_s[bg];
			error[i + 2] = err_coeff_s[bb];
			
			if (i > 3)
			  {
			    error[i - 6] += err_coeff_sw[br];
			    error[i - 5] += err_coeff_sw[bg];
			    error[i - 4] += err_coeff_sw[bb];
			  }
			
		    }
		  else
		    error[i + 0] = error[i + 1] =
		      error[i + 2] = dr = dg = db = 0;
		}
	      
	      /* 
	       *Put in line[...] the times each color component 
	       *have to be printed
	       * gMaxPass + 1 - bz = number of drops of Cyan
	       * gMaxPass + 1 - by = number of drops of Magenta
	       * gMaxPass + 1 - bx = number of drops of Yellow
	       */

	      line[i] = (unsigned char) bz;
	      line[i + 1] = (unsigned char) by;
	      line[i + 2] = (unsigned char) bx;
	      if ((i + 3)  != i_max)
		{
		  line[i + 3] = line[i];
		  line[i + 4] = line[i + 1];
		  line[i + 5] = line[i + 2];
		}
	    }
	
	  else
	    {
	      /* pixel is white */
	      error[i + 0] = error[i + 1] = error[i + 2] = dr = dg = db = 0;
	      line[i] = line[i + 1] = line[i + 2] = 255;
	      if ( (i + 3)  != i_max)
		{
		  /*  white out next pixel, */
		    line[i + 3] = line[i + 4] = line[i + 5] = 255;
		}
	    }
	if (i < image->width * 3 - 8)
	  {
	    error[i] += err_coeff_se[oldr];
	    error[i + 1] += err_coeff_se[oldg];
	    error[i + 2] += err_coeff_se[oldb];
	  }
	
      }
    }
  else
    {
      /* treatment of odd lines (black only) */
      for (i = 0; i < i_max ; i += 6)
	{
	      /* if either of two successive pixels are black, 
		 make both black */
	  if ( (i+3) != i_max)
	    {
	      if ( !line[i + 3] && !line[i + 4] && !line[i + 5] )
		line[i] = line[i + 1] = line[i + 2] = 0;
	      else if ( !line[i] && !line[i + 1] && !line[i + 2] )
		line[i + 3] = line[i + 4] = line[i + 5] = 0;
	    }
	  /* white out non-black pixels */
	  
	  if ( line[i] || line[i + 1] || line[i + 2] )
	    {
	      line[i] = line[i + 1] = line [i + 2] = 255 ;
	      if ( (i+3) != i_max )
		line[i + 3] = line[i + 4] = line [i + 5] = 255 ;
	    }
	  
	}
    }
}

void
HT_Black_Dither (image_t *image, unsigned char *line)
{
    int i, j, k, i_max;

    unsigned int halftone[5][5] = {
	{120, 180, 450, 270, 30},
	{240, 510, 720, 600, 330},
	{420, 630, 750, 660, 390},
	{360, 570, 690, 540, 210},
	{60, 300, 480, 150, 90}
    };

    i_max = image->width * 3;
    j = 0;
    k = image->bufferCurLine % 5;
    for (i = 0; i < i_max; i += 3)
    {

      /* make  black pixels come in pairs */
      if ( !(i % 6) && (i + 3) != i_max  )
	{
	  if (!line[i] &&  !line[i + 1] && !line[i + 2] )
	    line[i + 3] = line [i + 4] = line[i + 5] = 0;
	  else
	    if (!line[i + 3] &&  !line[i + 4] && !line[i + 5] )
	      line[i] = line [i + 1] = line[i + 2] = 0;
	}
    

	if ( line[i] + line[i + 1] + line[i + 2]  < halftone[j][k])
	{
	    line[i] = line[i + 1] = line[i + 2] = 0;
	}

	else
	{
	    line[i] = line[i + 1] = line[i + 2] = 255;
	}
	if (j == 4)
	    j = 0;
	else
	    j++;
    }
}

void
HT_Color_Dither (image_t *image, unsigned char *line)
{
    int i = 0, i_max;

    unsigned char halftoner[5][5] = {
	{40, 60, 150, 90, 10},
	{80, 170, 240, 200, 110},
	{140, 210, 250, 220, 130},
	{120, 190, 230, 180, 70},
	{20, 100, 160, 50, 30}
    };
    unsigned char halftoneg[5][5] = {
	{40, 80, 140, 120, 20},
	{60, 170, 210, 190, 100},
	{150, 240, 250, 230, 160},
	{90, 200, 220, 180, 50},
	{10, 110, 130, 70, 30}
    };
    unsigned char halftoneb[5][5] = {
	{100, 230, 220, 110, 40},
	{60, 160, 180, 130, 80},
	{150, 50, 70, 140, 170},
	{240, 90, 30, 120, 210},
	{200, 10, 20, 190, 250}
    };

  /* Only even lines are evaluated ;
     pixels are treated in pairs, and
     reduced to 300dpi resolution */
   

    i_max = image->width * 3 ;
    
    if (image->bufferCurLine % 2 == 0)
      {
	/* treatment of even lines */
      for (i = 0; i < i_max ; i += 6)
	{

	  /* if either of two successive pixels are black, make both black */
	  if ( (i+3) != i_max)
	    {
	      if ( !line[i + 3] && !line[i + 4] && !line[i + 5] )
		line[i] = line[i + 1] = line[i + 2] = 0;
	    }


	  if (line[i] || line[i + 1] || line[i + 2])
	    if (image->bufferCurLine % 2 == 0)
	      {
		if (line[i] < halftoner[i % 5][image->bufferCurLine % 5])
		  line[i] = 1;
		else
		  line[i] = 255;
		if (line[i + 1] < halftoneg[i % 5][image->bufferCurLine % 5])
		  line[i + 1] = 1;
		else
		  line[i + 1] = 255;
		if (line[i + 2] < halftoneb[i % 5][image->bufferCurLine % 5])
		  line[i + 2] = 1;
		else
		  line[i + 2] = 255;
	      }
	      if ( (i+3) != i_max)
		{
		  line[i + 3] = line[i];
		  line[i + 4] = line[i + 1];
		  line[i + 5] = line[i + 2];
		}
	}
      }
      else
	{
	  /* treatment of odd lines (black only) */
	  for (i = 0; i < i_max ; i += 6)
	    {
	      /* if either of two successive pixels are black, 
		 make both black */
	      if ( (i+3) != i_max)
		{
		  if ( !line[i + 3] && !line[i + 4] && !line[i + 5] )
		    line[i] = line[i + 1] = line[i + 2] = 0;
		  else if ( !line[i] && !line[i + 1] && !line[i + 2] )
		    line[i + 3] = line[i + 4] = line[i + 5] = 0;
		}
	      
	      /* white out non-black pixels */
	      if ( line[i] || line[i + 1] || line[i + 2] )
		{
		  line[i] = line[i + 1] = line [i + 2] = 255 ;
		  if ( (i+3) != i_max )
		    line[i + 3] = line[i + 4] = line [i + 5] = 255 ;
		}
	      
	    }
	}
}



