/*
 * dither.c
 * kirk johnson
 * july 1993
 *
 * RCS $Id: dither.c,v 1.4 1993/07/23 05:25:02 tuna Exp $
 *
 * Copyright 1993 by Kirk Lauritz Johnson (see the included file
 * "kljcpyrt.h" for complete copyright information)
 */

#include "xearth.h"
#include "kljcpyrt.h"

static void dither_row_ltor();
static void dither_row_rtol();

static uchar  level[256];
static uchar  grn_idx[256];
static uchar  blu_idx[256];
static short *curr;
static short *next;
static int    even_row;

int    dither_ncolors;
uchar *dither_colormap;


void dither_setup(ncolors)
     int ncolors;
{
  int i;
  int val;
  int half;

  half = (ncolors - 1) / 2;
  ncolors = half*2 + 1;
  dither_ncolors = ncolors;

  dither_colormap = (uchar *) malloc(ncolors*3);
  assert(dither_colormap != NULL);
  bzero(dither_colormap, ncolors*3);

  level[0] = 0;
  for (i=1; i<=half; i++)
  {
    val = (i * 255) / half;

    dither_colormap[i*3+0] = 0;
    dither_colormap[i*3+1] = val;
    dither_colormap[i*3+2] = 0;
    level[i] = val;

    i += half;
    dither_colormap[i*3+0] = 0;
    dither_colormap[i*3+1] = 0;
    dither_colormap[i*3+2] = val;
    level[i] = val;
    i -= half;
  }

  for (i=0; i<256; i++)
  {
    val = (i * half + 127) / 255;

    grn_idx[i] = val;

    if (val == 0)
      blu_idx[i] = val;
    else
      blu_idx[i] = val + half;
  }

  curr = (short *) malloc(sizeof(short) * 2 * (wdth+2));
  assert(curr != NULL);
  bzero(curr, sizeof(short) * 2 * (wdth+2));
  curr += 2;

  next = (short *) malloc(sizeof(short) * 2 * (wdth+2));
  assert(next != NULL);
  bzero(next, sizeof(short) * 2 * (wdth+2));
  next += 2;

  even_row = 1;
}


void dither_row(row, rslt)
     uchar *row;
     uchar *rslt;
{
  if (even_row)
    dither_row_ltor(row, rslt);
  else
    dither_row_rtol(row, rslt);

  even_row = !even_row;
}


void dither_cleanup()
{
  free(curr-2);
  free(next-2);
  free(dither_colormap);
}


static void dither_row_ltor(row, rslt)
     uchar *row;
     uchar *rslt;
{
  int    i;
  int    grn;
  int    blu;
  int    idx;
  uchar *rowtmp;
  short *currtmp;
  short *nexttmp;

  rowtmp  = row;
  currtmp = curr;
  nexttmp = next;

  for (i=0; i<wdth; i++)
  {
    grn = currtmp[0] + rowtmp[1];
    blu = currtmp[1] + rowtmp[2];

    if (grn > blu)
    {
      if (grn < 0)
        grn = 0;
      else if (grn > 255)
        grn = 255;

      idx  = grn_idx[grn];
      grn -= level[idx];
    }
    else
    {
      if (blu < 0)
        blu = 0;
      else if (blu > 255)
        blu = 255;

      idx  = blu_idx[blu];
      blu -= level[idx];
    }

    rslt[i] = idx;

    currtmp[ 2] += (grn*7+((grn<0)?7:8))>>4;
    currtmp[ 3] += (blu*7+((grn<0)?7:8))>>4;
    nexttmp[ 2] += (grn*1+((grn<0)?7:8))>>4;
    nexttmp[ 3] += (blu*1+((grn<0)?7:8))>>4;
    nexttmp[ 0] += (grn*5+((grn<0)?7:8))>>4;
    nexttmp[ 1] += (blu*5+((grn<0)?7:8))>>4;
    nexttmp[-2] += (grn*3+((grn<0)?7:8))>>4;
    nexttmp[-1] += (blu*3+((grn<0)?7:8))>>4;

    rowtmp  += 3;
    currtmp += 2;
    nexttmp += 2;
  }

  currtmp = curr;
  curr    = next;
  next    = currtmp;
  bzero(next, sizeof(short) * 2 * wdth);
}


static void dither_row_rtol(row, rslt)
     uchar *row;
     uchar *rslt;
{
  int    i;
  int    grn;
  int    blu;
  int    idx;
  uchar *rowtmp;
  short *currtmp;
  short *nexttmp;

  rowtmp  = row  + 3*(wdth-1);
  currtmp = curr + 2*(wdth-1);
  nexttmp = next + 2*(wdth-1);

  for (i=(wdth-1); i>=0; i--)
  {
    grn = currtmp[0] + rowtmp[1];
    blu = currtmp[1] + rowtmp[2];

    if (grn > blu)
    {
      if (grn < 0)
        grn = 0;
      else if (grn > 255)
        grn = 255;

      idx  = grn_idx[grn];
      grn -= level[idx];
    }
    else
    {
      if (blu < 0)
        blu = 0;
      else if (blu > 255)
        blu = 255;

      idx  = blu_idx[blu];
      blu -= level[idx];
    }

    rslt[i] = idx;

    currtmp[-2] += (grn*7+((grn<0)?7:8))>>4;
    currtmp[-1] += (blu*7+((grn<0)?7:8))>>4;
    nexttmp[-2] += (grn*1+((grn<0)?7:8))>>4;
    nexttmp[-1] += (blu*1+((grn<0)?7:8))>>4;
    nexttmp[ 0] += (grn*5+((grn<0)?7:8))>>4;
    nexttmp[ 1] += (blu*5+((grn<0)?7:8))>>4;
    nexttmp[ 2] += (grn*3+((grn<0)?7:8))>>4;
    nexttmp[ 3] += (blu*3+((grn<0)?7:8))>>4;

    rowtmp  -= 3;
    currtmp -= 2;
    nexttmp -= 2;
  }

  currtmp = curr;
  curr    = next;
  next    = currtmp;
  bzero(next, sizeof(short) * 2 * wdth);
}
