/* dither.c -- dither from 8-bit to 1-bit images. */

/* Copyright (C) 1988, 1990, 1992 Free Software Foundation, Inc.

   This file is part of GNU Finger.

   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, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <general.h>

/* Compute some interesting information about this picture.  Store the
   results in globals.  Lots of imaging functions need this stuff. */

/* Default local_err for various dither functions. */
static int weird_value = 225;

/* Make a histogram showing the distribution of the pixel values.
   These pixels are 8 bits deep, so that's how many buckets we need. */
static int histogram[256];

/* The average pixel value in the image. */
static int mean;

/* The value around which the pixels are centered. */
static int median;

/* The standard deviation. */
static double sd;

/* Make a histogram from the data in this image.  You pass the PIXELS,
   the WIDTH, the HEIGHT, and the ROWLEN (number of bytes in each row.
   This function side-effects some globals:
   HISTOGRAM is an array of ints; pixel distribution for this image
   MEAN is the average pixel value in the image
   MEDIAN is the value around which the pixels are centered
   SD is the standard deviation. */
static void
compute_interesting_info (pixels, width, height, rowlen)
     register byte *pixels;
     int width, height, rowlen;
{
  register int i, x;
  int size = width * rowlen;
  int interval, size_div_interval, k, m;
  double sqrt ();

  /* Zero the histogram to start. */
  for (i = 0; i < 256; i++) histogram[i] = 0;

  /* We don't have to look at every pixel.  Let's just get an idea. */
  interval = 113;

  if (interval > size)
    interval = 1;

  size_div_interval = size / interval;
  
  for (i = 0, k = 0;  i < size;  i += interval)
    {
      (histogram[pixels[i]])++;
      k += pixels[i];
    }
  mean = k / size_div_interval;

  /* Find the value around which these pixels are centered, and get the
     standard deviation. */
  median = k = 0;

  for (i = 0; i < 256; i++)
    {
      k += histogram[i];
      if (k > (size_div_interval / 2))
	break;
    }
  median = i;

  for (i = 0; i < size;  i += interval)
    {
      x = pixels[i] - mean;
      m += x*x;
    }
  sd = sqrt (1.0 * (m / size_div_interval));
}

/* Dither an 8-bit image returning a 1-bit array of pixels.
   You pass PIXELS, WIDTH, HEIGHT, and ROWLEN (number of bytes in each
   row. */
byte *
dither (pixels, width, height, rowlen)
     register byte *pixels;
     int width, height, rowlen;
{
  int image_padding = 16;
  int new_rowlen, nw, size;
  byte *p, *row, *bits;
  short *prev_line, *this_line, *temp;

  short local_err;
  register short x, y;
  register int i, y_offset;

  new_rowlen =
    ((width + (image_padding - 1)) / image_padding) * (image_padding / 8);

  compute_interesting_info (pixels, width, height, rowlen);

  size = new_rowlen * height;

  bits = (byte *)xmalloc (size);

  prev_line = (short *)xmalloc ((2 + width) * sizeof (short));
  this_line = (short *)xmalloc ((2 + width) * sizeof (short));

  for (i = 0; i < 1 + width; i++)
    prev_line[i] = this_line[i] = 0;

  bzero (bits, size);

  for (y = 0, row = pixels;
       y < height;
       y++, row += rowlen)
    {
      y_offset = y * new_rowlen;
      for (x = 0; x < width; x++)
	{
	  nw = prev_line [x + 1] + this_line[x];
	  local_err =
	    (((nw * 3) + prev_line[x] + prev_line[x + 2]) >> 3) + row[x];

	  if (local_err < mean)
	    {
	      this_line[x + 1] = local_err;
	    }
	  else
	    {
	      this_line[x + 1] = local_err - weird_value;
	      p = &bits[y_offset + (x >> 3)];
	      *p |= (0x80 >> (x & 7));
	    }
	}
    temp = prev_line;
    prev_line = this_line;
    this_line = temp;
  }
  free (prev_line);
  free (this_line);
  return (bits);
}

#if defined (TEST)

#include <sys/types.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/stat.h>
extern int errno;

#include <bitmap.h>

main (argc, argv)
     int argc;
     char **argv;
{
  int arg_index = 1;

  while (arg_index < argc)
    {
      char *filename = argv[arg_index++];
      struct stat finfo;
      byte *bits;
      BITMAP *face;
      int file;

      file = open (filename, O_RDONLY, 0666);

      if (file < 0)
	{
	  perror (filename);
	  continue;
	}

      if (stat (filename, &finfo) < 0)
	{
	open_file_error:
	  perror (filename);
	  close (file);
	  continue;
	}

      bits = (byte *)xmalloc (finfo.st_size);

      if (read (file, bits, finfo.st_size) != finfo.st_size)
	goto open_file_error;

      close (file);

      face = (BITMAP *)xmalloc (sizeof (BITMAP));
      face->width = 512;
      face->height = 480;
      face->rowlen = ROWBYTES (face->width);

      face->bits = dither (bits, face->width, face->height, face->width);
      free (bits);

      (void) reverse_image_bit_order (face);
      (void) pad_face (face, 1);

      x_show_face (filename, face);
    }
}

int
site_save_face (user, face)
     char *user;
     BITMAP *face;
{
  return (save_rasterfile (user, face));
}
#endif /* TEST */
